Gateway command pattern, method abstraction, and configuration structure
The Payment Gateway is Magento's modern approach to building payment integrations. It separates concerns into discrete, testable components: Commands, Request Builders, HTTP Clients, Response Handlers, and Validators.
┌─────────────────────────────────────────────────────────────────┐
│ CommandPool │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ authorize│ │ capture │ │ void │ │ refund │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
└───────┼────────────┼────────────┼────────────┼──────────────────┘
│ │ │ │
▼ ▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────┐
│ GatewayCommand │
│ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐ │
│ │ RequestBuilder │──│ HttpClient │──│ResponseHandler │ │
│ └────────────────┘ └────────────────┘ └────────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌────────────────┐ ┌────────────────┐ │
│ │TransferFactory │ │ Validator │ │
│ └────────────────┘ └────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
| Interface | Purpose | Location |
|---|---|---|
| CommandInterface | Execute payment operation | Gateway/CommandInterface.php |
| BuilderInterface | Build request data array | Gateway/Request/BuilderInterface.php |
| TransferFactoryInterface | Create transfer object for HTTP | Gateway/Http/TransferFactoryInterface.php |
| ClientInterface | Send HTTP request to gateway | Gateway/Http/ClientInterface.php |
| HandlerInterface | Process gateway response | Gateway/Response/HandlerInterface.php |
| ValidatorInterface | Validate response data | Gateway/Validator/ValidatorInterface.php |
The Model\Method\Adapter class bridges the legacy MethodInterface with the Gateway framework.
Payment methods are configured entirely through di.xml virtual types - no PHP class needed per method.
<!-- di.xml: Define a payment method without writing PHP -->
<virtualType name="MyPaymentMethodFacade" type="Magento\Payment\Model\Method\Adapter">
<arguments>
<argument name="code" xsi:type="const">MyPayment::CODE</argument>
<argument name="formBlockType" xsi:type="string">Magento\Payment\Block\Form\Cc</argument>
<argument name="infoBlockType" xsi:type="string">Magento\Payment\Block\Info</argument>
<argument name="valueHandlerPool" xsi:type="object">MyPaymentValueHandlerPool</argument>
<argument name="commandPool" xsi:type="object">MyPaymentCommandPool</argument>
</arguments>
</virtualType>
<!-- Command Pool: Map operations to commands -->
<virtualType name="MyPaymentCommandPool" type="Magento\Payment\Gateway\Command\CommandPool">
<arguments>
<argument name="commands" xsi:type="array">
<item name="authorize" xsi:type="string">MyPaymentAuthorizeCommand</item>
<item name="capture" xsi:type="string">MyPaymentCaptureCommand</item>
<item name="void" xsi:type="string">MyPaymentVoidCommand</item>
<item name="refund" xsi:type="string">MyPaymentRefundCommand</item>
</argument>
</arguments>
</virtualType>
code - Payment method codeformBlockType - Form block classinfoBlockType - Info block classcommandPool - Gateway commandsvalidatorPool - ValidatorsvalueHandlerPool - Config handlersMethodInterface (API contract)
├── AbstractMethod (Legacy base class - deprecated for new methods)
│ ├── Free (Zero-total orders)
│ └── Cc (Credit card abstract)
│ └── [Legacy CC methods extend this]
│
└── Adapter (Gateway-based methods)
└── [Configured via di.xml virtual types]
├── Braintree
├── PayPal Express
├── Stripe
└── [Custom Gateway methods]
Best Practice: New payment methods should use the Gateway framework with Adapter
virtual types. Extending AbstractMethod is considered legacy and makes testing difficult.
| File | Purpose | Schema |
|---|---|---|
| etc/payment.xml | Credit card types and payment groups | payment.xsd |
| etc/config.xml | Default payment method settings | config.xsd |
| etc/di.xml | Gateway commands and method adapters | config.xsd |
| etc/error_mapping.xml | Gateway error code to message mapping | error_mapping.xsd |
Payment methods are configured under payment/{method_code}/ in system configuration.
payment/
├── {method_code}/
│ ├── active # Enable/disable method
│ ├── title # Display name
│ ├── order_status # New order status
│ ├── payment_action # authorize, authorize_capture
│ ├── allowspecific # Limit to specific countries
│ ├── specificcountry # Allowed countries list
│ ├── min_order_total # Minimum order amount
│ ├── max_order_total # Maximum order amount
│ └── sort_order # Display order in checkout
└── ...
Value handlers retrieve configuration values for payment methods. The pool allows different handlers for different config keys, enabling dynamic configuration based on context (quote, store, etc.).
<virtualType name="MyPaymentValueHandlerPool"
type="Magento\Payment\Gateway\Config\ValueHandlerPool">
<arguments>
<argument name="handlers" xsi:type="array">
<item name="default" xsi:type="string">MyPaymentConfigValueHandler</item>
<item name="can_void" xsi:type="string">MyPaymentCanVoidHandler</item>
<item name="can_cancel" xsi:type="string">MyPaymentCanCancelHandler</item>
</argument>
</arguments>
</virtualType>
<virtualType name="MyPaymentConfigValueHandler"
type="Magento\Payment\Gateway\Config\ConfigValueHandler">
<arguments>
<argument name="configInterface" xsi:type="object">MyPaymentConfig</argument>
</arguments>
</virtualType>
can_authorize
Can perform authorization
can_capture
Can capture payment
can_refund
Can issue refunds
can_void
Can void transactions
can_use_checkout
Available at checkout
can_use_internal
Available in admin
Gateway commands receive a PaymentDataObject that wraps payment info, order, and quote data.
This provides a consistent interface for request builders to access transaction context.
// PaymentDataObjectInterface provides:
$paymentDataObject->getPayment(); // OrderPaymentInterface
$paymentDataObject->getOrder(); // OrderAdapterInterface
$paymentDataObject->getQuote(); // QuoteAdapterInterface (if available)
// In request builder:
class MyRequestBuilder implements BuilderInterface
{
public function build(array $buildSubject): array
{
$paymentDO = SubjectReader::readPayment($buildSubject);
$payment = $paymentDO->getPayment();
$order = $paymentDO->getOrder();
return [
'amount' => $order->getGrandTotalAmount(),
'currency' => $order->getCurrencyCode(),
'order_id' => $order->getOrderIncrementId(),
'cc_number' => $payment->getCcNumber(),
];
}
}
| Interface | Purpose | Implementation |
|---|---|---|
| PaymentMethodListInterface | Get available payment methods for store | PaymentMethodList |
| PaymentVerificationInterface | Verify payment method data | [per method] |
| Data\PaymentMethodInterface | Payment method data transfer object | PaymentMethod |
| Data\PaymentAdditionalInfoInterface | Additional payment data DTO | PaymentAdditionalInfo |