Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions Helper/Data.php
Original file line number Diff line number Diff line change
Expand Up @@ -1265,4 +1265,28 @@ public function getConfigValue($key, $store, $storeCode=null)
return $this->scopeConfig->getValue($path, $store, $storeCode);
}

/**
* Checks if a transaction has already been processed based on the transaction additional info (JSON blob).
*
* @param $payment
* @param string $resultField The key in the transaction's additional information to check (e.g., 'settle_result').
* @param string $expectedStatus The status value to check for (case-insensitive).
* @param bool $checkCaptured Optional. If true, also checks if the transaction is marked as captured.
* @return bool
*/
public function isTransactionProcessed($payment, $resultField, $expectedStatus, $checkCaptured = false){
$transactionAdditionalInfo = $payment->getTransactionAdditionalInfo();
$resultData = isset($transactionAdditionalInfo[$resultField])
? json_decode($transactionAdditionalInfo[$resultField], true)
: null;

// Check if the status matches the expected value (case-insensitive)
$isProcessed = $resultData && strcasecmp($resultData['status'], $expectedStatus) === 0;

if ($checkCaptured) {
$isProcessed = $isProcessed || !empty($transactionAdditionalInfo['captured']);
}

return $isProcessed;
}
}
55 changes: 54 additions & 1 deletion Model/Payment/Method/Bread.php
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,19 @@ public function void(\Magento\Payment\Model\InfoInterface $payment)
throw new \Magento\Framework\Exception\LocalizedException(__('Void action is not available.'));
}

$transactionAdditionalInfo = $payment->getTransactionAdditionalInfo();
$cancelResult = isset($transactionAdditionalInfo['cancel_result'])
? json_decode($transactionAdditionalInfo['cancel_result'], true)
: null;

if ($this->helper->isTransactionProcessed($payment, $cancelResult, 'CANCELLED', true)) {
$this->breadLogger->info(sprintf(
'Transaction ID %s has already cancelled. Skipping further processing.',
$payment->getTxnId()
));
return $this;
}

return $this->_place($payment, 0, self::ACTION_VOID);
}

Expand All @@ -275,6 +288,19 @@ public function authorize(\Magento\Payment\Model\InfoInterface $payment, $amount
throw new \Magento\Framework\Exception\LocalizedException(__('Authorize action is not available.'));
}

$transactionAdditionalInfo = $payment->getTransactionAdditionalInfo();
$authorizeResult = isset($transactionAdditionalInfo['authorize_result'])
? json_decode($transactionAdditionalInfo['authorize_result'], true)
: null;

if ($this->helper->isTransactionProcessed($payment, $authorizeResult, 'AUTHORIZED', true)) {
$this->breadLogger->info(sprintf(
'Transaction ID %s has already authorized. Skipping further processing.',
$payment->getTxnId()
));
return $this;
}

$order = $payment->getOrder();
if ($order->getOrderCurrencyCode() !== $order->getBaseCurrencyCode()) {
$amount = $order->getGrandTotal();
Expand Down Expand Up @@ -375,11 +401,25 @@ public function canVoid() {
public function capture(\Magento\Payment\Model\InfoInterface $payment, $amount)
{
if ($this->adminOrderHelper->isAdminOrder()) {
return; // Bypass logic for admin order creation
return $this; // Bypass logic for admin order creation
}
if (!$this->canCapture()) {
throw new \Magento\Framework\Exception\LocalizedException(__('Capture action is not available.'));
}

$transactionAdditionalInfo = $payment->getTransactionAdditionalInfo();
$settleResult = isset($transactionAdditionalInfo['settle_result'])

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shopify checks this value for all transaction updates. Settle/Refund/Void/Capture/Authorize. If the transaction is already in that state, we log and don't do anything. Might make sense to do that for the others as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dschoder-bread good call. I'll update it

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

? json_decode($transactionAdditionalInfo['settle_result'], true)
: null;

if ($this->helper->isTransactionProcessed($payment, $settleResult, 'SETTLED', true)) {
$this->breadLogger->info(sprintf(
'Transaction ID %s has already been captured and settled. Skipping further processing.',
$payment->getTxnId()
));
return $this;
}

$apiVersion = $this->helper->getApiVersion();

$order = $payment->getOrder();
Expand Down Expand Up @@ -459,6 +499,19 @@ public function refund(\Magento\Payment\Model\InfoInterface $payment, $amount)
throw new \Magento\Framework\Exception\LocalizedException(__('Refund action is not available.'));
}

$transactionAdditionalInfo = $payment->getTransactionAdditionalInfo();
$refundResult = isset($transactionAdditionalInfo['refund_result'])
? json_decode($transactionAdditionalInfo['refund_result'], true)
: null;

if ($this->helper->isTransactionProcessed($payment, $refundResult, 'REFUNDED', true)) {
$this->breadLogger->info(sprintf(
'Transaction ID %s has already refunded. Skipping further processing.',
$payment->getTxnId()
));
return $this;
}

$order = $payment->getOrder();
if ($order->getOrderCurrencyCode() !== $order->getBaseCurrencyCode()) {
$amount = $order->getGrandTotal();
Expand Down
53 changes: 53 additions & 0 deletions Model/Payment/Method/Rbc.php
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,19 @@ public function void(\Magento\Payment\Model\InfoInterface $payment)
throw new \Magento\Framework\Exception\LocalizedException(__('Void action is not available.'));
}

$transactionAdditionalInfo = $payment->getTransactionAdditionalInfo();
$cancelResult = isset($transactionAdditionalInfo['cancel_result'])
? json_decode($transactionAdditionalInfo['cancel_result'], true)
: null;

if ($this->helper->isTransactionProcessed($payment, $cancelResult, 'CANCELLED', true)) {
$this->breadLogger->info(sprintf(
'Transaction ID %s has already cancelled. Skipping further processing.',
$payment->getTxnId()
));
return $this;
}

return $this->_place($payment, 0, self::ACTION_VOID);
}

Expand All @@ -265,6 +278,19 @@ public function authorize(\Magento\Payment\Model\InfoInterface $payment, $amount
throw new \Magento\Framework\Exception\LocalizedException(__('Authorize action is not available.'));
}

$transactionAdditionalInfo = $payment->getTransactionAdditionalInfo();
$authorizeResult = isset($transactionAdditionalInfo['authorize_result'])
? json_decode($transactionAdditionalInfo['authorize_result'], true)
: null;

if ($this->helper->isTransactionProcessed($payment, $authorizeResult, 'AUTHORIZED', true)) {
$this->breadLogger->info(sprintf(
'Transaction ID %s has already authorized. Skipping further processing.',
$payment->getTxnId()
));
return $this;
}

$order = $payment->getOrder();
if ($order->getOrderCurrencyCode() !== $order->getBaseCurrencyCode()) {
$amount = $order->getGrandTotal();
Expand Down Expand Up @@ -367,6 +393,20 @@ public function capture(\Magento\Payment\Model\InfoInterface $payment, $amount)
if (!$this->canCapture()) {
throw new \Magento\Framework\Exception\LocalizedException(__('Capture action is not available.'));
}

$transactionAdditionalInfo = $payment->getTransactionAdditionalInfo();
$settleResult = isset($transactionAdditionalInfo['settle_result'])
? json_decode($transactionAdditionalInfo['settle_result'], true)
: null;

if ($this->helper->isTransactionProcessed($payment, $settleResult, 'SETTLED', true)) {
$this->breadLogger->info(sprintf(
'Transaction ID %s has already been captured and settled. Skipping further processing.',
$payment->getTxnId()
));
return $this;
}

$apiVersion = $this->helper->getApiVersion();

$order = $payment->getOrder();
Expand Down Expand Up @@ -443,6 +483,19 @@ public function refund(\Magento\Payment\Model\InfoInterface $payment, $amount)
throw new \Magento\Framework\Exception\LocalizedException(__('Refund action is not available.'));
}

$transactionAdditionalInfo = $payment->getTransactionAdditionalInfo();
$refundResult = isset($transactionAdditionalInfo['refund_result'])
? json_decode($transactionAdditionalInfo['refund_result'], true)
: null;

if ($this->helper->isTransactionProcessed($payment, $refundResult, 'REFUNDED', true)) {
$this->breadLogger->info(sprintf(
'Transaction ID %s has already refunded. Skipping further processing.',
$payment->getTxnId()
));
return $this;
}

$order = $payment->getOrder();
if ($order->getOrderCurrencyCode() !== $order->getBaseCurrencyCode()) {
$amount = $order->getGrandTotal();
Expand Down