cancel
Showing results for 
Search instead for 
Did you mean: 

The Magento Sale Payment Operation

isentiabov
Adobe Team

Overview

 

If you have tried to implement a payment integration in Magento, you might have had a trouble with the sale (authorize & capture) payment operation. This post describes how the Place an Order flow is implemented in Magento, the “bridge” between Magento and payment service providers, and how to implement the sale operation when using custom payment methods without breaking the Place an Order flow.

 

At first, let’s define a small glossary:

  • Payment integration (payment method) – integration that allows interaction between e-commerce applications and the Payment Service Provider (PSP).
  • Payment Service Provider (PSP) – a service that processes electronic payments.
  • Magento Sales Management – Magento interfaces that provide the ability to create orders, invoices, shipments, etc.
  • Settlement – the process when an issuing bank sends customer funds to the seller’s PSP.
  • Authorization transaction – a transaction to check if the card issuing bank approves the customer’s credit card.
  • Capture transaction – a transaction that allows a PSP to submit a customer’s money for settlement.
  • Authorize operation – a payment method sends an authorization transaction to a PSP, and the PSP confirms the customer’s ability to pay and reserves the amount on the customer’s credit card.
    Authorize & Capture (Sale) – this includes authorization and immediately charges costs to a credit card. This operation can be used when merchants want to charge customers immediately.
  • Capture operation – when a payment method sends a transaction to a PSP to charge funds from a customer account or credit card.

Let’s start with the Place an Order flow in Magento, how Magento interacts with PSPs such as PayPal, Braintree, and Authorize.net, etc., and why we cannot use a standard flow.

 

Magento payment integrations support payment operations to be performed in one of two ways:

 

  • Authorize
  • Authorize & Capture (Sale)

 

The following diagram describes the Magento Place an Order flow based on an Authorize payment operation:

 

Place order general flow.png

 

The steps in the flow are as follows.

 

  1. A customer adds products to a shopping cart and places an order.
  2. Magento Sales Management sets order details and initiates the payment method selected by the customer.
  3. The payment method creates a request for an authorization transaction and sends it to the selected PSP.
  4. The PSP processes all required interactions with the customer's card issuing bank and returns processing details back to the store.
  5. A store's administrator manually creates an invoice for the placed order.
  6. The payment method creates a capture transaction, which is based on an authorization transaction, and sends it to the PSP.
  7. The PSP submits the capture transaction for settlement.
  8. After the transaction is settled by the customer’s issuing bank, the merchant receives funds.

 

Also, the capture transaction can be performed on the PSP's side (the merchant can manually create a capture transaction via the PSP interface).

 

The following diagram describes the “place order” flow based on the Authorize & Capture (Sale) payment operation:
Place order general flow with Sale.png

An issue

 

The sale payment operation flow is similar to an authorize payment operation, but in this case, the authorization and capture transactions are combined into one transaction. It will be submitted for settlement automatically by the PSP, so the store's administrator does not need to create an invoice manually.

 

According to sale restrictions in the Sales Management module:

<?php
protected function processAction($action, Order $order) { $totalDue = $order->getTotalDue(); $baseTotalDue = $order->getBaseTotalDue();
switch ($action) { case \Magento\Payment\Model\Method\AbstractMethod::ACTION_ORDER: $this->_order($baseTotalDue);
break; case \Magento\Payment\Model\Method\AbstractMethod::ACTION_AUTHORIZE: $this->authorize(true, $baseTotalDue); // base amount will be set inside $this->setAmountAuthorized($totalDue); break; case \Magento\Payment\Model\Method\AbstractMethod::ACTION_AUTHORIZE_CAPTURE: $this->setAmountAuthorized($totalDue); $this->setBaseAmountAuthorized($baseTotalDue); $this->capture(null); break; default: break; } }

 

The capture operation will be called for both the sale and capture payment operations. In that case, payment integrations do not have an ability to process both the authorize & capture operations.

 

For example, if a customer wants to buy some items, and the merchant wants to charge the customer balance immediately, the payment integration can’t send the correct type of transaction to the Payment Service Provider. Each payment integration would have to solve this issue again and again.

 

The flow described in this post assumes that the payment integration is based on the Magento Payment Gateway.

 

sale & capture.png

  

As previously mentioned, we only have the capture action for the sale payment operation. To avoid breaking the Place an Order flow, we will use it as it is. To solve this issue, we need to answer three questions:

  1. What does the payment operation do?
  2. What transaction details do we need to have in order to process the operation?
  3. How can we use it?

 

In most cases, the sale operation is similar to authorization, and it also processes the capture operation, answering the first question.

 

If our payment provides transaction capturing, we have part of the capture operation (which includes customer details, billing information, order amount, etc.), the answer to the second question.

 

The third answer is based on two previous points. Our payment method should perform payment operations similar to capture. We do not have an authorization transaction, so we can use some strategy to decide what kind of payment operation (sale or capture) our payment integration should perform when Magento Sales Management calls the capture action.

 

Luckily, OOP has a suitable pattern, the Strategy pattern. In our case, this pattern allows us to switch between authorization and sale payment operations, and vice versa.

 

Let's assume we have a payment integration based on a Magento Payment Provider Gateway such as Braintree.

If we open DI configuration, we will see the command pool with a list of available commands:

 

<virtualType name="BraintreeCommandPool" type="Magento\Payment\Gateway\Command\CommandPool">
    <arguments>
        <argument name="commands" xsi:type="array">
            <item name="authorize" xsi:type="string">BraintreeAuthorizeCommand</item>
            <item name="sale" xsi:type="string">BraintreeSaleCommand</item>
            <item name="capture" xsi:type="string">BraintreeCaptureStrategyCommand</item>
            <item name="settlement" xsi:type="string">BraintreeCaptureCommand</item>
        </argument>
    </arguments>
</virtualType>

 

The three interesting operations for us are:

  • sale - the virtual type for a "real" sale command
  • capture - our strategy
  • settlement - the virtual type for a capture command

 

Solution

 

To solve our issue with the sale operation and make payment integration clearer (as previously mentioned, the sales order payment calls the capture operation for both sale and capture actions), the capture command is specified as the strategy. This approach allows us to recognize what operation to process.

 

One possible way to implement our strategy is to check transactions. If an authorization transaction already exists, then we have a capture payment operation. Otherwise, it is a sale operation. Our capture algorithm is as follows:

capture strategy.png

  

So, the capture is our strategy and that is how it can be implemented (based on the Braintree payment method):

<?php
class CaptureStrategyCommand implements CommandInterface
{
    const SALE = 'sale';
    const CAPTURE = 'settlement';
    
public function execute(array $commandSubject) { /** @var \Magento\Payment\Gateway\Data\PaymentDataObjectInterface $paymentDO */ $paymentDO = $this->subjectReader->readPayment($commandSubject);
/** @var \Magento\Sales\Api\Data\OrderPaymentInterface $paymentInfo */ $paymentInfo = $paymentDO->getPayment(); ContextHelper::assertOrderPayment($paymentInfo); $command = $this->getCommand($paymentInfo); $this->commandPool->get($command)->execute($commandSubject); } private function getCommand(OrderPaymentInterface $payment) { // if auth transaction does not exist execute authorize&capture command $existsCapture = $this->isExistsCaptureTransaction($payment);
if (!$payment->getAuthorizationTransaction() && !$existsCapture) { return self::SALE; } // do capture for authorization transaction if (!$existsCapture && !$this->isExpiredAuthorization($payment)) { return self::CAPTURE; } ... } }

 

I omitted code not important to our example. The full class listing can be found in the Magento Github repository.

 

This logic is pretty simple. We just check, and if there are no authorization or capture transactions, then it's a sale operation. On the other hand, if the authorization has not expired and a capturing transaction does not exist then it’s a capture operation to charge customer's funds.

 

We considered Braintree capture strategy checks capturing transaction, but we are not considering it in this topic because it is related to Braintree partial invoicing and is not interesting for us. Then we process the capture operation.

 

Depending on the payment integration, the strategy to decide which payment action to execute can be more complicated than described above. For example, a Payment Gateway can provide API entry points to retrieve additional transaction details, and your code might choose a needed payment operation according to the Payment Gateway API response.

 

Conclusion

 

In this post, we’ve described how to implement the sale payment operation for a custom payment method based on the Magento Payment Provider Gateway. Using this knowledge, you can more easily extend your payment integration and add the sale payment operation, if the Payment Gateway supports authorize & capture transactions.

8 Comments
erfanimani
M2 Certified

 Thanks for the long but great write-up. Really clarifies some hard things that aren't in the devdocs.

 

Something that's ambiguous for me:

 

"It will be submitted for settlement automatically by the PSP, so the store's administrator does not need to create an invoice manually."

 

Is the invoice automatically generated if you use the sale command? Or do you mean that you don't have to create an invoice to capture the payment because it is automatically captured on order placement?

 

 

isentiabov
Adobe Team

@erfanimani, thanks for your comment.

 

"Or do you mean that you don't have to create an invoice to capture the payment because it is automatically captured on order placement?"

 

Yes, exactly. That's the main idea of sale operation usage, the transaction will be authorized and submitted for settlement (captured) and at that moment the order will be created with an invoice.

KumarPradeep
Senior Member

Thanks for the great article on Payment Operation

kanduvisla
M2 Certified

How does \Magento\Sales\Api\Data\TransactionInterface::TYPE_ORDER fit into this picture? Because if I look at \Magento\Sales\Model\Order\Payment::canCapture(), I see that even if my payment method can do a capture, I still need to have a transaction of TYPE_ORDER. 

 

If I understand the code correctly, in order for a merchant to be able to do an online capture from the admin, a TYPE_ORDER transaction is required. When is this created? I've analyzed the Braintree module, but could not find it.

isentiabov
Adobe Team

@kanduvisla you don't need to create order transaction to make a capture. Order transaction it's a different thing, for example, look at PayPal Express Checkout Order https://developer.paypal.com/docs/classic/admin/auth-capture/#orders-and-order-authorizations.

 

In Magento, order operation can be executed the same as `authorize` or sale\Magento\Sales\Model\Order\Payment:Smiley TonguerocessAction decides which payment operation should be executed, your payment solution just should support order operation and it should be selected by default. As an example of how it looks like, you can check PayPal Express Checkout integration in Magento (it uses the deprecated AbstractMethod but the idea for Payment Gateway will be the same).

vsuddamalla
Senior Member

Hi,

We are trying to perform sale operation on new order total of authorize.net payment method from admin.

But we are getting error like "Opaque data does not exist".

When we checked in frontend we are seeing the following values for payment information.

array (
'opaque_data' => '{"dataDescriptor":"COMMON.ACCEPT.INAPP.PAYMENT","dataValue":"eyJjb2RlIjoiNTBfMl8wNjAwMDUzN0NENUE4NUQ3RjVCMjkwQjM5NzlCMEIwRENEM0U2RUREMDhGNTc0RUMxMUYyM0FBOTE3NjdDOEY2NzMxOTgxMkJGRkU4OTExMDcwQjkxODEwNDhFQzhDQ0FCNkJGMjc5IiwidG9rZW4iOiI5NTk4NTUyMjgxODc5NTA3ODAzNjAxIiwidiI6IjEuMSJ9"}',
'cardExpYear' => '2022',
'cardExpMonth' => '2',
'is_active_payment_token_enabler' => false,
'method_title' => 'Credit Card',
)

But in the admin we don't have that information regd opaque_data.So How can we generate the dataValue and perform sale operation for new order total.

Thanks!

jhschoen
Member

@isentiabov 

 

 

In this example is there a way to not have an invoice automatically created at the moment of the order when you are doing an authorize and capture?

 

 

------

"Or do you mean that you don't have to create an invoice to capture the payment because it is automatically captured on order placement?"

 

Yes, exactly. That's the main idea of sale operation usage, the transaction will be authorized and submitted for settlement (captured) and at that moment the order will be created with an invoice."

 

------

 

abhayshuklc900
New Contributor

Pay: This basically sets the invoice state is ‘paid’, this works for both when payment is online or offline.

Capture: This is when actual payment processing happens online, and the capture() method in our payment method is called. After capture, pay() is called.