Execution Flows

Step-by-step traces of payment operations with Gateway commands

Flow 1: Payment Authorization

When order is placed with payment_action = "authorize"

1

Order Placement Triggers Payment

Sales\Model\Order\Payment::place() is called during order save.

// Order\Payment::place()
$this->_authorize(true, $baseTotalDue);
// Calls MethodInterface::authorize()
2

Method Adapter Routes to Gateway

Method\Adapter::authorize() resolves "authorize" command from CommandPool.

// Method\Adapter::authorize()
$this->executeCommand('authorize', [
    'payment' => $paymentDataObject,
    'amount' => $amount
]);
3

Request Builder Constructs Payload

BuilderComposite chains multiple builders to create the API request.

// Example builder output
[
    'amount' => 99.99,
    'currency' => 'USD',
    'card' => ['number' => '4111...', 'exp' => '12/25'],
    'billing_address' => [...],
    'merchant_id' => 'abc123'
]
4

HTTP Client Sends to Gateway

TransferFactory creates transfer object, ClientInterface sends request.

// Gateway\Http\Client\Zend or Curl
POST https://api.gateway.com/authorize
Content-Type: application/json
Authorization: Bearer {token}

{"amount": 99.99, "currency": "USD", ...}
5

Response Handler Updates Payment

HandlerInterface extracts transaction ID and stores in payment.

// Response handler
$payment->setTransactionId($response['transaction_id']);
$payment->setIsTransactionClosed(false);
$payment->setAdditionalInformation('auth_code', $response['auth_code']);
6

Validator Confirms Success

ValidatorInterface checks response codes and throws exception on failure.

// Validator checks
if ($response['status'] !== 'approved') {
    throw new CommandException(__('Payment authorization failed'));
}
return $this->createResult(true, []);

Flow 2: Payment Capture

When invoice is created with "Capture Online" option

1

Invoice Service Initiates Capture

Sales\Model\Order\Payment::capture() called from invoice creation.

// InvoiceService triggers
$payment->capture($invoice);
// For previously authorized payments:
$this->_capture($invoice);
2

Gateway Capture Command Executes

Request builder includes original transaction ID for reference.

// Capture request builder
[
    'transaction_id' => $payment->getParentTransactionId(),
    'amount' => $invoice->getBaseGrandTotal(),
    'currency' => $order->getBaseCurrencyCode()
]
3

Transaction Created

Capture transaction linked to authorization as parent.

// Response handler creates transaction
$payment->setTransactionId($response['capture_id']);
$payment->setParentTransactionId($response['auth_id']);
// Transaction type: capture

Flow 3: Authorization Void

When order is cancelled before capture

1

Order Cancel Triggers Void

Order\Payment::cancel() checks if void is possible.

// Order\Payment::cancel()
if ($this->canVoid()) {
    $this->void(new \Magento\Framework\DataObject());
}
2

Gateway Void Command

Sends void request with original authorization transaction ID.

// Void request
[
    'transaction_id' => $payment->getLastTransId(),
    'reason' => 'Order cancelled'
]
3

Authorization Closed

Original auth transaction marked as closed.

// After void
$authTransaction->setIsClosed(true);
// New void transaction created
$payment->setTransactionId($response['void_id'] . '-void');

Flow 4: Online Refund

When credit memo is created with "Refund" option

1

Credit Memo Initiates Refund

Order\Payment::refund() called from creditmemo service.

// Creditmemo service
$payment->refund($creditmemo);
// Triggers MethodInterface::refund()
2

Gateway Refund Command

References capture transaction for partial or full refund.

// Refund request builder
[
    'transaction_id' => $payment->getRefundTransactionId(),
    'amount' => $creditmemo->getBaseGrandTotal(),
    'invoice_id' => $invoice->getIncrementId()
]
3

Refund Transaction Created

New refund transaction linked to capture as parent.

// Response handler
$payment->setTransactionId($response['refund_id']);
$creditmemo->setTransactionId($response['refund_id']);
// If full refund, capture transaction closed

Flow 5: Checkout Payment Method Loading

When checkout page loads available payment methods

1

Checkout Config Provider Invoked

CompositeConfigProvider collects all payment config.

// checkout/index/index renders
window.checkoutConfig = {
    payment: {
        // Each method adds its config here
    }
}
2

Payment Method List Filtered

PaymentMethodList::getActiveList() filters by availability rules.

// Filtering criteria:
// - isActive() for current store
// - isAvailable($quote) checks:
//   - min/max order total
//   - allowed countries
//   - specific customer groups
//   - SpecificationInterface rules
3

JS Component Renders Methods

RequireJS components render each payment method in checkout.

// Magento_Checkout/js/view/payment/list
// Iterates over available methods
// Each method has its own component:
// Magento_Payment/js/view/payment/cc-form

Key Event Dispatch Points

sales_order_save_before

Observed by SalesOrderBeforeSaveObserver to validate payment data before order save.

sales_order_status_unassign

UpdateOrderStatusForPaymentMethodsObserver updates status mapping when status unassigned.

payment_method_is_active

Dispatched during method availability check. Allows plugins to enable/disable methods dynamically.

sales_order_payment_*

Various events for payment operations: _place_start, _place_end, _cancel, _void, etc.

Error Handling Flow

Gateway Response → Validator → ErrorMessageMapper → Exception

// 1. Validator checks response
$result = $validator->validate($response);
if (!$result->isValid()) {
    $errorCodes = $result->getFailsDescription();
}

// 2. ErrorMessageMapper translates codes
// Configured in error_mapping.xml:
<error_messages>
    <message code="1001" translate="true">Card declined</message>
    <message code="1002" translate="true">Insufficient funds</message>
</error_messages>

// 3. CommandException thrown with customer-friendly message
throw new CommandException(__($mappedMessage));

Best Practice: Always implement error_mapping.xml for your payment gateway. Raw gateway error codes should never be shown to customers. Map them to localized, user-friendly messages.