1

I'm creating an integration for a Payment Service Provider / Payment Gateway, but I'm a bit stuck in the way how Magento handles stuff.

The payment method in question returns a "pending state" with a redirect URL to send the customer to. When the customer returns from that URL, a status update or asynchronous webhook can be send that can either result in a successful payment or in a failure. If the latter is the case, the order should be cancelled.

I'm sticking as much as possible to the default way on how to do this in Magento. This means that if I get the "pending state" from the gateway, I mark my payment transaction as "pending" meaning that some approval needs to take place at the payment gateway:

$redirect = $response->getBody()->getRedirect()->getUrl();

$paymentModel = $order->getPayment();
$paymentModel->setAdditionalInformation('redirect', $redirect);
$paymentModel->setAdditionalInformation('transaction_id', $response->getBody()->getTransaction()->getId());
$paymentModel->setIsTransactionPending(true);
$paymentModel->setTransactionId($response->getBody()->getTransaction()->getId());

By setting the "pending"-flag, the \Magento\Sales\Model\Order\Payment\Operations\CaptureOperation::capture()-operation will not mark the invoice as paid:

$message = $this->stateCommand->execute($payment, $amountToCapture, $order);
if ($payment->getIsTransactionPending()) {
    $invoice->setIsPaid(false);
} else {
    $invoice->setIsPaid(true);
    $this->updateTotals($payment, ['base_amount_paid_online' => $amountToCapture]);
}

The $message that is set also shows me I'm in the right direction:

\Magento\Sales\Model\Order\Payment\State\CaptureCommand::execute():

if ($payment->getIsTransactionPending()) {
    $state = Order::STATE_PAYMENT_REVIEW;
    $message = 'An amount of %1 will be captured after being approved at the payment gateway.';
}

So if I understand correctly, if some action is required by the gateway, the "payment review"-state is used.

Now later on, if the asynchronous event is received, I can simply get my invoice and mark it as being paid:

$invoice = $order->getInvoiceCollection()->getLastItem();
$invoice->pay();

So far, everything works perfectly. But: if my payment fails I want to cancel my order. I thought this would be as simple as just doing $order->cancel(), but the \Magento\Sales\Model\Order::canCancel()-method that is triggered, checks if the order is in the payment review-state, and if so, it does not allow cancellation. (please correct me if I'm wrong).

So now the question is: what to do? Should the order state be "pending payment" instead of "payment review"? If so: what is than the most friendly way to set this property using default Magento mechanics? Because if I look for usages of the "pending payment" state in the code, it doesn't look like this state is set by Magento itself (at least not using it's constant). The weird part is that if I tell Magento that a transaction is pending, the status becomes payment review. :-/

Is it even accepted in general to cancel an order that has the "payment review" state?

0

In my opinion you have to cancel the invoice you have created earlier if you are in the "payment review" state and the payment fails. After that you should be able to cancel the order too because at the end of invoices cancel() method the order state is set to "processing".

This shoud do it:

$invoice = $order->getInvoiceCollection()->getLastItem();
$invoice->cancel();
$order->cancel();

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.