A comprehensive payment integration module for Magento 2 that provides card payments, Buy Now Pay Later (BNPL) installments, and subscription management through the Paypercut payment gateway.
- Features
- Requirements
- Installation
- Configuration
- Architecture Overview
- Payment Methods
- Checkout Integrations
- Subscription System
- API Integration
- Frontend Components
- Troubleshooting
- Security
- Authorize and capture payments
- Partial capture support
- Full and partial refunds
- Void transactions
- Saved cards (Vault) for returning customers
- Installment payment plans (3, 6, or 12 months)
- Configurable order limits (min/max)
- Automatic installment calculation at checkout
- Recurring billing for subscription products
- Configurable billing intervals (daily, weekly, monthly, yearly)
- Trial period support
- Customer self-service (pause, cancel, resume)
- Product-level subscription overrides
- Magento 2.4.x
- PHP 7.4 or higher
- Paypercut merchant account with API credentials
- Magento_Sales
- Magento_Payment
- Magento_Checkout
- Magento_Catalog
- Magento_Vault
- Magento_Eav
composer require paypercut/magento2
bin/magento module:enable Paypercut_Payment
bin/magento setup:upgrade
bin/magento setup:di:compile
bin/magento cache:flush- Create directory:
app/code/Paypercut/Payment - Copy module files to the directory
- Run Magento setup commands:
bin/magento module:enable Paypercut_Payment
bin/magento setup:upgrade
bin/magento setup:di:compile
bin/magento cache:flushNavigate to Stores > Configuration > Sales > Payment Methods in the Magento Admin.
| Setting | Description |
|---|---|
| Enabled | Enable/disable the payment method |
| Title | Payment method title shown at checkout |
| Description | Description shown to customers |
| Environment | Sandbox or Production |
| Secret Key | Your Paypercut secret key |
| Payment Action | authorize_capture (immediate charge) or authorize (manual capture) |
| New Order Status | Order status after successful payment |
| Enable Saved Cards | Allow customers to save cards for future purchases |
| Debug Mode | Enable detailed logging |
| Sort Order | Display order at checkout |
| Setting | Description |
|---|---|
| Enabled | Enable/disable BNPL payment |
| Title | Payment method title (default: "Paypercut - Pay in Installments") |
| BNPL Secret Key | Separate secret key for BNPL API |
| Minimum Order Total | Minimum cart total for BNPL (default: 100) |
| Maximum Order Total | Maximum cart total for BNPL (default: 10,000) |
| Available Installments | Comma-separated months (e.g., "3,6,12") |
| Sort Order | Display order at checkout |
| Setting | Description |
|---|---|
| Enabled | Enable subscription functionality |
| Subscription Label | Label shown on subscription products |
| Default Billing Interval | day, week, month, or year |
| Billing Interval Count | Number of intervals between charges |
| Billing Day of Month | Specific day for monthly billing |
| Collection Method | charge_automatically or send_invoice |
| Enable Trial | Allow trial periods |
| Trial Days | Number of trial days |
| Missing Payment Behavior | cancel or keep subscription on failed payment |
| Cancel at Period End | Cancel at billing period end vs immediately |
| Allow Customer Cancellation | Let customers cancel subscriptions |
| Allow Customer Pause | Let customers pause subscriptions |
| Renewal Reminder Days | Days before renewal to send reminder |
Configure your Paypercut merchant dashboard to send webhooks to:
https://your-store.com/paypercut/payment/ipn
Paypercut/Payment/
├── Api/ # Service contracts
│ └── SubscriptionManagementInterface.php
├── Block/ # Block classes
│ ├── Info.php # Payment info block
│ └── BnplInfo.php # BNPL info block
├── Controller/Payment/ # Frontend controllers
│ ├── Redirect.php # Gateway redirect
│ ├── Success.php # Success callback
│ ├── Cancel.php # Cancel callback
│ └── Ipn.php # Webhook handler
├── Gateway/ # Payment gateway layer
│ ├── Http/Client/ # HTTP clients
│ ├── Request/ # Request builders
│ ├── Response/ # Response handlers
│ └── Validator/ # Response validators
├── Model/ # Business logic
│ ├── PaymentMethod.php # Card payment method
│ ├── BnplPaymentMethod.php # BNPL payment method
│ ├── Api/Client.php # API client
│ ├── Ui/ # Checkout UI providers
│ └── Subscription/ # Subscription management
├── Observer/ # Event observers
├── Setup/Patch/Data/ # Data patches
├── etc/ # Configuration files
│ ├── config.xml # Default config
│ ├── di.xml # Dependency injection
│ ├── system.xml # Admin configuration
│ └── payment.xml # Payment definitions
└── view/frontend/ # Frontend assets
├── layout/ # Layout XML
├── templates/ # PHTML templates
└── web/ # JS, CSS, HTML templates
┌─────────────────────────────────────────────────────────────────┐
│ CHECKOUT │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Customer Selects Payment Method │
│ (Card / BNPL / Saved Card) │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Order Created with pending_payment │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Redirect Controller │
│ - Creates checkout/BNPL attempt via API │
│ - Stores transaction data │
│ - Redirects to Paypercut gateway │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Paypercut Gateway │
│ (Customer completes payment) │
└─────────────────────────────────────────────────────────────────┘
│
┌───────────────┼───────────────┐
▼ ▼ ▼
┌────────┐ ┌─────────┐ ┌──────────┐
│Success │ │ Cancel │ │ IPN │
│Callback│ │Callback │ │ Webhook │
└────────┘ └─────────┘ └──────────┘
│ │ │
▼ ▼ ▼
Order Success Restore Cart Update Order
Page Cancel Order Create Invoice
The module uses Magento's payment gateway framework with the following commands:
| Command | Description |
|---|---|
authorize |
Authorize payment without capture |
capture |
Capture authorized payment |
void |
Void authorized transaction |
refund |
Refund captured transaction |
vault_authorize |
Authorize with saved card |
vault_capture |
Capture with saved card |
| Class | Purpose |
|---|---|
Model\Api\Client |
HTTP client for Paypercut API |
Model\PaymentMethod |
Card payment method implementation |
Model\BnplPaymentMethod |
BNPL payment method implementation |
Model\Subscription\SubscriptionManager |
Subscription lifecycle management |
Gateway\Request\AuthorizationRequest |
Builds authorization requests |
Gateway\Response\TxnIdHandler |
Handles transaction responses |
Standard card payment with redirect to Paypercut's hosted payment page.
Capabilities:
- Authorize
- Capture (full and partial)
- Refund (full and partial)
- Void
- Vault (saved cards)
Code:
$payment->getMethod(); // Returns: paypercut_cardBuy Now Pay Later installment payments.
Capabilities:
- Authorize
- Capture (full only)
- Refund (full and partial)
- Void
Order Restrictions:
- Minimum order total: configurable (default 100)
- Maximum order total: configurable (default 10,000)
- Not available for admin-created orders
Code:
$payment->getMethod(); // Returns: paypercut_bnplSaved card payments using Magento's Vault framework.
Features:
- Tokenized card storage
- Quick checkout for returning customers
- Required for subscription payments
This module supports multiple checkout solutions. Each checkout type requires specific configuration and has different integration mechanisms.
The default Magento checkout uses Knockout.js components.
Files:
view/frontend/layout/checkout_index_index.xml- Layout configurationview/frontend/web/js/view/payment/method-renderer/paypercut.js- Card rendererview/frontend/web/js/view/payment/method-renderer/paypercut-bnpl.js- BNPL rendererview/frontend/web/template/payment/form.html- Card form templateview/frontend/web/template/payment/bnpl-form.html- BNPL form template
Payment Flow:
- Customer selects payment method
- Order is placed via Magento checkout
afterPlaceOrder()redirects topaypercut/payment/redirect- Redirect controller creates payment session with Paypercut API
- Customer is redirected to Paypercut hosted payment page
- After payment, customer returns to success/cancel URL
- IPN webhook updates order status
For standard Hyva Checkout (non-React), Alpine.js templates are used.
Files:
view/frontend/layout/hyva_checkout_components.xml- Layout configurationview/frontend/templates/checkout/payment/method/paypercut-card.phtml- Card templateview/frontend/templates/checkout/payment/method/paypercut-bnpl.phtml- BNPL templateBlock/Checkout/BnplConfig.php- Block providing BNPL configuration
BNPL Installments:
The BNPL template displays installment options calculated server-side by BnplConfigProvider. Options are stored in window.__paypercutBnplConfig and rendered via Alpine.js.
Payment Flow:
Same as Luma checkout - redirect to paypercut/payment/redirect after order placement.
IMPORTANT: Zento Checkout is a separate React-based checkout solution located at:
app/code/Hyva/ReactCheckout
This Paypercut module is designed to work with Zento Checkout when the payment methods need to be displayed on the checkout page. Zento Checkout uses GraphQL and React components for payment handling.
Payment Flow (different from Luma/Hyva):
- Customer selects payment method in React checkout
- Order is placed via GraphQL mutation
fetchPaymentFormGraphQL query is called with order IDZento\Payment\Model\Resolver\PaymentFormresolves the query- Resolver calls
Paypercut\Payment\Model\Form::getForm()to get redirect URL - React component redirects to Paypercut using
window.location.replace() - Customer completes payment on Paypercut
- IPN webhook updates order status
Key Files in Zento Checkout:
Hyva/ReactCheckout/
├── reactapp/src/paymentMethods/paymentForm/
│ ├── renderers.js # Maps payment codes to components
│ ├── src/components/
│ │ ├── PaymentForm.jsx # Main payment form component
│ │ └── BnplInstallmentsForm.jsx # BNPL installment selector
│ └── src/api/fetchPaymentForm/
│ ├── query.js # GraphQL query
│ └── fetchPaymentForm.js # API call
Key Files in Zento Payment Module:
Zento/Payment/
├── etc/schema.graphqls # GraphQL schema for paymentForm query
└── Model/Resolver/PaymentForm.php # GraphQL resolver
The Zento\Payment\Model\Resolver\PaymentForm must include Paypercut payment methods. Verify these cases exist in getPaymentObject():
case 'paypercut_bnpl':
if ($this->moduleManager->isEnabled('Paypercut_Payment')) {
if ($this->scopeConfig->getValue('payment/paypercut_bnpl/active', ScopeInterface::SCOPE_STORE, $storeId)) {
$object = $objectManager->create(\Paypercut\Payment\Model\Form::class);
}
}
break;
case 'paypercut_card':
if ($this->moduleManager->isEnabled('Paypercut_Payment')) {
if ($this->scopeConfig->getValue('payment/paypercut_card/active', ScopeInterface::SCOPE_STORE, $storeId)) {
$object = $objectManager->create(\Paypercut\Payment\Model\Form::class);
}
}
break;
case 'paypercut_vault':
if ($this->moduleManager->isEnabled('Paypercut_Payment')) {
if ($this->scopeConfig->getValue('payment/paypercut_vault/active', ScopeInterface::SCOPE_STORE, $storeId)) {
$object = $objectManager->create(\Paypercut\Payment\Model\Form::class);
}
}
break;In Zento Checkout, installment options come from window.checkoutConfig.payment.paypercut_bnpl.installmentOptions, which is populated by Paypercut\Payment\Model\Ui\BnplConfigProvider::getConfig().
The React component BnplInstallmentsForm.jsx displays the installment options when BNPL is selected.
Troubleshooting Installments Not Showing:
- Verify BNPL is enabled in admin
- Check cart total is within min/max limits
- Ensure
BnplConfigProvideris registered indi.xml - Check browser console for
window.checkoutConfig.payment.paypercut_bnpl
If redirection to Paypercut is not working:
-
Verify GraphQL resolver: Ensure
paypercut_card,paypercut_bnpl, andpaypercut_vaultcases exist inZento\Payment\Model\Resolver\PaymentForm::getPaymentObject() -
Check Form model: The
Paypercut\Payment\Model\Form::getForm()method must return valid JSON with anactionkey:{"action": "https://checkout.paypercut.io/...", "data": [], "method": "GET"} -
Verify payment renderers: In
ReactCheckout/reactapp/src/paymentMethods/paymentForm/renderers.js:export default { paypercut_card: PaymentForm, paypercut_vault: PaymentForm, paypercut_bnpl: PaymentForm, };
-
Check PaymentForm.jsx: Verify the payment action is registered:
registerPaymentAction('paypercut_card', paymentSubmitHandler); registerPaymentAction('paypercut_vault', paymentSubmitHandler); registerPaymentAction('paypercut_bnpl', paymentSubmitHandler);
- Navigate to Catalog > Products
- Edit the product
- Set the following attributes:
| Attribute | Description |
|---|---|
paypercut_subscription_enabled |
Enable subscription for this product |
paypercut_subscription_interval |
Override billing interval |
paypercut_subscription_interval_count |
Override interval count |
paypercut_subscription_price |
Override subscription price |
paypercut_subscription_cycles |
Maximum billing cycles (0 = unlimited) |
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Active │────▶│ Paused │────▶│ Active │
└─────────────┘ └─────────────┘ └─────────────┘
│ │
│ │
▼ ▼
┌─────────────┐ ┌─────────────┐
│ Cancelled │ │ Cancelled │
└─────────────┘ └─────────────┘
use Paypercut\Payment\Api\SubscriptionManagementInterface;
// Inject via constructor
public function __construct(
SubscriptionManagementInterface $subscriptionManager
) {
$this->subscriptionManager = $subscriptionManager;
}
// Get customer subscriptions
$subscriptions = $this->subscriptionManager->getCustomerSubscriptions($customerId);
// Cancel subscription
$this->subscriptionManager->cancelSubscription($subscriptionId);
// Pause subscription
$this->subscriptionManager->pauseSubscription($subscriptionId);
// Resume subscription
$this->subscriptionManager->resumeSubscription($subscriptionId);Standard Payment API:
- Base URL (Production):
https://api.paypercut.io/v1 - Base URL (Sandbox):
https://sandbox-api.paypercut.io/v1
| Endpoint | Method | Purpose |
|---|---|---|
/checkouts |
POST | Create checkout session |
/checkouts/{id} |
GET | Get checkout status |
/transactions/authorize |
POST | Authorize transaction |
/transactions/capture |
POST | Capture transaction |
/transactions/void |
POST | Void transaction |
/transactions/refund |
POST | Refund transaction |
BNPL API:
- Base URL (Production):
https://api.paypercut.io/bnpl/v1 - Base URL (Sandbox):
https://sandbox-api.paypercut.io/bnpl/v1
| Endpoint | Method | Purpose |
|---|---|---|
/orders |
POST | Create BNPL order |
/orders/capture |
POST | Capture BNPL order |
/orders/cancel |
POST | Cancel BNPL order |
/orders/refund |
POST | Refund BNPL order |
Subscription API:
| Endpoint | Method | Purpose |
|---|---|---|
/subscriptions |
POST | Create subscription |
/subscriptions/{id} |
GET | Get subscription |
/subscriptions/{id} |
PATCH | Update subscription |
/subscriptions/{id}/cancel |
POST | Cancel subscription |
/subscriptions/{id}/pause |
POST | Pause subscription |
/subscriptions/{id}/resume |
POST | Resume subscription |
All API requests use Bearer token authentication:
Authorization: Bearer {api_key}
The module supports:
- Standard Magento Luma checkout
- Hyva Checkout (via
hyva_checkout_components.xml)
| Component | File | Purpose |
|---|---|---|
| Card Payment | view/frontend/web/js/view/payment/method-renderer/paypercut.js |
Card payment form |
| BNPL Payment | view/frontend/web/js/view/payment/method-renderer/paypercut-bnpl.js |
BNPL with installment selector |
| Vault | view/frontend/web/js/view/payment/method-renderer/vault.js |
Saved card selector |
| Template | Purpose |
|---|---|
form.html |
Card payment checkout form |
bnpl-form.html |
BNPL checkout form with installment options |
vault.html |
Saved cards display |
- Go to Stores > Configuration > Sales > Payment Methods > Paypercut Card
- Set Debug Mode to Yes
- Check logs in
var/log/paypercut.log
Payment method not showing at checkout:
- Verify the payment method is enabled
- Check country restrictions
- For BNPL: verify cart total is within min/max limits
API connection errors:
- Verify API credentials are correct
- Check environment setting (Sandbox vs Production)
- Ensure server can reach Paypercut API endpoints
Subscription not created:
- Verify subscriptions are enabled globally
- Check product has subscription enabled
- Ensure customer saved their card (Vault required)
Webhook not processing:
- Verify webhook URL is correctly configured in Paypercut dashboard
- Check for firewall blocking incoming requests
- Review
var/log/paypercut.logfor errors
BNPL installments not showing:
-
Empty installment options array:
- The
BnplConfigProvidercalculates options based onCheckoutSession::getQuote()->getGrandTotal() - If the session is not properly initialized, grand total may be 0
- Check browser console:
console.log(window.checkoutConfig?.payment?.paypercut_bnpl) - For Hyva: also check
console.log(window.__paypercutBnplConfig)
- The
-
Cart total outside min/max range:
- Verify admin settings for min/max order totals
BnplConfigProvider::isAvailableForAmount()returns false if outside range
-
Config provider not registered:
- Ensure
BnplConfigProvideris inetc/frontend/di.xmlas a config provider
- Ensure
Redirect not working after order placement:
-
Luma Checkout:
- Check
afterPlaceOrder()inpaypercut-bnpl.jsorpaypercut.js - Should call
window.location.replace(url.build('paypercut/payment/redirect'))
- Check
-
Zento Checkout (ReactCheckout):
- Verify
PaymentForm.jsxhas the payment action registered - Check
fetchPaymentFormGraphQL query returns valid data - Verify
Zento\Payment\Model\Resolver\PaymentFormhas the payment method case - Check
Paypercut\Payment\Model\Form::getForm()returns valid redirect URL
- Verify
-
Standard Hyva Checkout:
- Hyva Checkout handles redirects differently based on the checkout implementation
- May require Magewire component or custom Alpine.js integration
GraphQL errors in Zento Checkout:
Check the GraphQL response for paymentForm query:
query {
paymentForm(order_id: "000000001", success: "https://...", cancel: "https://...") {
order_id
form_data
}
}If form_data is empty or error, check:
Zento\Payment\Model\Resolver\PaymentFormhas Paypercut casesPaypercut\Payment\Model\Formis creating valid API requests- API credentials are correct
| Log | Content |
|---|---|
var/log/paypercut.log |
Payment-specific debug logs |
var/log/system.log |
General Magento errors |
var/log/exception.log |
PHP exceptions |
var/log/graphql/ |
GraphQL debug logs (if enabled) |
Check if module is enabled:
bin/magento module:status Paypercut_PaymentCheck payment method configuration:
bin/magento config:show payment/paypercut_card/active
bin/magento config:show payment/paypercut_bnpl/activeClear caches after configuration changes:
bin/magento cache:flush
bin/magento setup:di:compileThe Paypercut module uses a redirect-based (hosted payment page) integration for all payment methods -- Card, BNPL, and Vault. When a customer proceeds to pay, they are redirected from the Magento storefront to Paypercut's secure hosted payment page, where all sensitive payment data is collected and processed. Upon completion, the customer is returned to the merchant's Magento site via a callback URL.
At no point does the Magento server receive, handle, or have access to raw cardholder data such as card numbers, CVV/CVC codes, expiration dates, or cardholder names.
Because payment data is entered exclusively on Paypercut's PCI-certified hosted payment page and never passes through the merchant's Magento environment, this integration qualifies for PCI SAQ A -- the simplest Self-Assessment Questionnaire level. SAQ A applies to merchants that have fully outsourced all cardholder data processing to a PCI DSS-validated third-party provider and do not electronically store, process, or transmit any cardholder data on their own systems.
| Concern | Status |
|---|---|
| Customer payment data entered on Magento | No. All payment data is entered on Paypercut's hosted page. |
| Sensitive payment data stored on Magento | No. No card numbers, CVV codes, expiration dates, or cardholder names are stored. |
| Data stored on Magento | Transaction reference IDs, payment intent IDs, and opaque payment method tokens only -- used for order management, refunds, and subscription lifecycle. |
| Saved cards (Vault) | Stored as tokenized references (public hashes) via the Magento Vault framework. No actual card data is retained. |
Inbound IPN (Instant Payment Notification) webhooks from Paypercut are validated using HMAC-SHA256 signature verification against a shared webhook secret configured in the Magento admin. This includes timestamp tolerance checks to prevent replay attacks. Merchants should ensure the webhook secret is configured to enable this protection.
Before going live, verify:
- API credentials configured (sandbox first, then production)
- Webhook URL configured in Paypercut dashboard
- Payment methods enabled in admin
- BNPL min/max order totals configured
- For Zento Checkout:
PaymentFormGraphQL resolver has Paypercut cases - For Zento Checkout:
renderers.jsincludes Paypercut payment methods - IPN endpoint accessible from Paypercut servers
- SSL certificate valid on success/cancel URLs
- Tested full payment flow in sandbox environment
For technical support or feature requests, contact your Paypercut account representative or visit the Paypercut merchant dashboard.
Commercial License - All Rights Reserved