Skip to content

Commit e0942b8

Browse files
committed
BTI-122-Issue refunding GroupTransactions using Returnless
1 parent dd68745 commit e0942b8

1 file changed

Lines changed: 70 additions & 0 deletions

File tree

Service/RefundGroupTransactionService.php

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,9 +330,16 @@ public function refundGroupTransactions(array &$buildSubject)
330330

331331
$requestParams = $this->request->getParams();
332332
if (!empty($requestParams['creditmemo']['buckaroo_already_paid'])) {
333+
// Admin backend flow: giftcard amounts are explicitly specified in the creditmemo form
333334
foreach ($requestParams['creditmemo']['buckaroo_already_paid'] as $transaction => $giftCardValue) {
334335
$this->createRefundGroupRequest($buildSubject, $transaction, $giftCardValue);
335336
}
337+
} else {
338+
// API / headless flow (e.g. Returnless): no form params are present.
339+
// Automatically refund giftcard group transactions proportionally so that
340+
// the remaining amount passed to the primary payment method never exceeds
341+
// what was actually charged on that method.
342+
$this->refundGiftcardTransactionsAutomatically($buildSubject, $order);
336343
}
337344

338345
if ($this->amountLeftToRefund >= 0.01 && $originalRefundAmount > $this->amountLeftToRefund) {
@@ -506,6 +513,69 @@ public function setAmountLeftToRefund(float $amountLeftToRefund): void
506513
$this->amountLeftToRefund = $amountLeftToRefund;
507514
}
508515

516+
/**
517+
* Automatically refund giftcard group transactions for API/headless flows.
518+
*
519+
* When a refund is triggered via the REST API (e.g. Returnless), the
520+
* creditmemo form POST params are not available. This method reads the
521+
* group_transaction table directly, identifies giftcard transactions and
522+
* refunds each one proportionally so that only the remainder is sent to
523+
* the primary payment method (e.g. iDEAL).
524+
*
525+
* Example: order paid €10 VVV giftcard + €22 iDEAL = €32 total.
526+
* Full refund of €32 via API → refund €10 on VVV first → €22 left → refund €22 on iDEAL.
527+
*
528+
* @param array $buildSubject
529+
* @param Order $order
530+
* @return void
531+
* @throws ClientException
532+
* @throws ConverterException
533+
*/
534+
private function refundGiftcardTransactionsAutomatically(array $buildSubject, Order $order): void
535+
{
536+
$groupTransactions = $this->paymentGroupTransaction->getAnyGroupTransactionItems($order->getIncrementId());
537+
538+
foreach ($groupTransactions as $transaction) {
539+
$servicecode = $transaction->getData('servicecode');
540+
541+
if (!$this->isGiftcardService($servicecode)) {
542+
// Skip non-giftcard transactions; they are handled by the primary refund flow
543+
continue;
544+
}
545+
546+
if ($this->amountLeftToRefund < 0.01) {
547+
break;
548+
}
549+
550+
$availableOnTransaction = (float)$transaction->getData('amount')
551+
- (float)$transaction->getData('refunded_amount');
552+
553+
if ($availableOnTransaction < 0.01) {
554+
continue;
555+
}
556+
557+
// Refund as much as needed from this giftcard, capped at what was charged on it
558+
$refundAmount = min($this->amountLeftToRefund, $availableOnTransaction);
559+
560+
$this->buckarooLog->addDebug(sprintf(
561+
'[REFUND_AUTO_GIFTCARD] | Automatically refunding giftcard | Service: %s | Amount: €%.2f | Transaction: %s',
562+
$servicecode,
563+
$refundAmount,
564+
$transaction->getData('transaction_id')
565+
));
566+
567+
// Build the transaction key string in the same format used by createRefundGroupRequest:
568+
// transactionId|servicecode|amount
569+
$transactionKey = implode('|', [
570+
$transaction->getData('transaction_id'),
571+
$servicecode,
572+
$transaction->getData('amount'),
573+
]);
574+
575+
$this->createRefundGroupRequest($buildSubject, $transactionKey, $refundAmount);
576+
}
577+
}
578+
509579
/**
510580
* Get the non-giftcard payment transaction from group_transaction table
511581
*

0 commit comments

Comments
 (0)