Skip to content
Open
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
58 changes: 56 additions & 2 deletions api/paymentmethods/giftcard/giftcard.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,16 @@
require_once dirname(__FILE__) . '/../paymentmethod.php';
class GiftCard extends PaymentMethod
{
/**
* Service codes that use FashionCheque-specific parameters.
*/
private const FASHION_CHEQUE_CODES = ['fashioncheque'];

/**
* Service codes that use TCS-specific parameters.
*/
private const TCS_CODES = ['tcs'];

public function __construct()
{
$this->type = 'giftcard';
Expand All @@ -35,13 +45,57 @@ public function pay($customVars = [])

public function payDirect($customVars = [])
{
$this->payload = $this->getPayload($customVars);
$this->payload = $this->getPayload($this->mapDirectPayParameters($customVars));
$action = !empty($this->OriginalTransactionKey) ? 'payRemainder' : 'pay';

return parent::executeCustomPayAction('pay');
return parent::executeCustomPayAction($action);
}

public function getPayload($data)
{
return array_merge_recursive($this->payload, $data);
}

/**
* Map checkout card number/PIN to the Buckaroo service parameters expected
* for the selected giftcard brand (mirrors Magento2 Giftcard request).
*/
private function mapDirectPayParameters(array $data): array
{
$cardNumber = (string) ($data['cardNumber'] ?? $data['intersolveCardnumber'] ?? '');
$pin = (string) ($data['pin'] ?? $data['intersolvePIN'] ?? '');
$cardCode = strtolower((string) ($data['name'] ?? ''));

unset($data['cardNumber'], $data['pin']);

if ($cardNumber === '' && $pin === '') {
return $data;
}

// Already mapped by caller.
if (isset($data['intersolveCardnumber'])
|| isset($data['fashionChequeCardNumber'])
|| isset($data['tcsCardnumber'])
) {
return $data;
}

if (in_array($cardCode, self::FASHION_CHEQUE_CODES, true)) {
$data['fashionChequeCardNumber'] = $cardNumber;
$data['fashionChequePin'] = $pin;
} elseif (in_array($cardCode, self::TCS_CODES, true)) {
$data['tcsCardnumber'] = $cardNumber;
$data['tcsValidationCode'] = $pin;
} elseif ($cardCode !== '' && strpos($cardCode, 'customgiftcard') === 0) {
// Custom giftcard services use the generic Cardnumber/PIN parameters.
$data['cardNumber'] = $cardNumber;
$data['pin'] = $pin;
} else {
// Default brands (boekenbon, yourgift, vvvgiftcard, …) are Intersolve.
$data['intersolveCardnumber'] = $cardNumber;
$data['intersolvePIN'] = $pin;
}

return $data;
}
}
10 changes: 10 additions & 0 deletions api/paymentmethods/paymentmethod.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ public function executeCustomPayAction($action)
public function pay($customVars = [])
{
// @codingStandardsIgnoreEnd
// After a partial giftcard payment, Buckaroo requires PayRemainder + originalTransactionKey.
if (!empty($this->OriginalTransactionKey)) {
return $this->payGlobal('payRemainder');
}

$this->data['services'][$this->type]['action'] = 'Pay';
$this->data['services'][$this->type]['version'] = $this->version;

Expand All @@ -84,6 +89,11 @@ public function payGlobal($customPayAction = null)
{
(!$customPayAction) ? $payAction = 'pay' : $payAction = $customPayAction;

// Partial giftcard flow: OriginalTransactionKey is only valid with PayRemainder.
if (!empty($this->OriginalTransactionKey) && $payAction === 'pay') {
$payAction = 'payRemainder';
}

$basePayload = [
'currency' => $this->currency,
'amountDebit' => $this->amountDebit,
Expand Down
26 changes: 18 additions & 8 deletions api/paymentmethods/response.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,11 @@ public function __construct(TransactionResponse $response = null)
} else {
$this->isPush = $this->isPushRequest();
$this->received = true;
$this->logger->logInfo('Response determined to be a push request');
$this->logger->logInfo(
$this->isPush
? 'Response determined to be a push/return request'
: 'Response has no brq_statuscode (not a push/return payload)'
);
$this->parsePushRequest();
}
}
Expand Down Expand Up @@ -206,11 +210,13 @@ public function isValid(): bool
if ($this->isPush) {
$buckaroo = new BuckarooClient(Configuration::get('BUCKAROO_MERCHANT_KEY'), Configuration::get('BUCKAROO_SECRET_KEY'));
try {
$reply_handler = new ReplyHandler($buckaroo->client()->config(), $_POST);
// Customer return may arrive as GET; push is usually POST.
$data = array_merge($_GET, $_POST);
$reply_handler = new ReplyHandler($buckaroo->client()->config(), $data);
$reply_handler->validate();
$this->validated = $reply_handler->isValid();
$this->logger->logInfo('Push request validated successfully');
} catch (Exception $e) {
} catch (\Throwable $e) {
$this->logger->logError('Push request validation failed', ['exception' => $e->getMessage()]);
}
} elseif ($this->response) {
Expand Down Expand Up @@ -266,14 +272,18 @@ private function setPostVariable($key)

public function getCartIdAndReferenceId($show = false)
{
$e = explode('_', urldecode($this->invoicenumber));
if (!empty($e[1])) {
list($reference, $cartId) = $e;
$invoice = urldecode((string) ($this->invoicenumber ?? ''));
$parts = $invoice !== '' ? explode('_', $invoice) : [];

if (count($parts) >= 2) {
$cartId = (int) end($parts);
$reference = implode('_', array_slice($parts, 0, -1));
} else {
$cartId = 0;
$reference = $this->invoicenumber;
$reference = $invoice;
}
return $show == 'cartId' ? (int)$cartId : $reference;

return $show == 'cartId' ? $cartId : $reference;
}

public function getCartId(): int
Expand Down
31 changes: 14 additions & 17 deletions buckaroo3.php
Original file line number Diff line number Diff line change
Expand Up @@ -675,18 +675,8 @@ public function hookPaymentOptions($params)
}

$buckarooConfigService = $this->getBuckarooConfigService();

$buckarooPaymentService = $this->get('buckaroo.config.api.payment.service');

$giftcardApplied = 0.0;
$giftcardRemainder = 0.0;
try {
$giftcardApplied = $this->getGiftcardAlreadyPaid($cart);
$giftcardRemainder = $this->getGiftcardRemainingAmount($cart);
} catch (Exception $e) {
$this->logger->logError('Buckaroo3::hookPaymentOptions giftcard amounts - ' . $e->getMessage());
}

try {
$this->context->smarty->assign(
[
Expand All @@ -711,8 +701,6 @@ public function hookPaymentOptions($params)
'creditcardIssuers' => $buckarooConfigService->getActiveCreditCards(),
'creditCardDisplayMode' => $buckarooConfigService->getConfigValue('creditcard', 'display_type'),
'giftCardDisplayMode' => $buckarooConfigService->getConfigValue('giftcard', 'display_in_checkout'),
'buckarooGiftcardApplied' => $giftcardApplied,
'buckarooGiftcardRemainder' => $giftcardRemainder,
'in3Method' => $this->get('buckaroo.classes.issuers.capayableIn3')->getMethod(),
'buckaroo_idin_test' => $buckarooConfigService->getConfigValue('idin', 'mode'),
'houseNumbersAreValid' => $buckarooPaymentService->areHouseNumberValidForCountryDE($cart)
Expand Down Expand Up @@ -740,11 +728,14 @@ private function ensureBuckarooJsLoaded()
'remainingAmount' => 0,
'giftcardItems' => [],
'currencySign' => $this->context->currency ? $this->context->currency->sign : '',
'cartTotal' => 0,
];
try {
$cart = $this->context->cart;
if ($cart) {
$alreadyPaidData['alreadyPaid'] = $this->getGiftcardAlreadyPaid($cart);
$cartTotal = (float) $cart->getOrderTotal(true, Cart::BOTH);
$alreadyPaidData['cartTotal'] = $cartTotal;
$alreadyPaidData['alreadyPaid'] = $this->getGiftcardAlreadyPaid($cart);
$alreadyPaidData['remainingAmount'] = $this->getGiftcardRemainingAmount($cart);
$alreadyPaidData['giftcardItems'] = $this->getGiftcardDisplayItems($cart);
}
Expand Down Expand Up @@ -865,11 +856,9 @@ public function hookDisplayHeader()
*/
public function hookDisplayPaymentTop()
{
// Ensure Buckaroo JavaScript is loaded
$this->ensureBuckarooJsLoaded();

// Return empty string (we just need to load the JS)
return '';

return $this->renderGiftcardAlreadyPaidBlock();
}

/**
Expand All @@ -878,6 +867,14 @@ public function hookDisplayPaymentTop()
* total segment rendered by hookDisplayShoppingCartFooter.
*/
public function hookDisplayShoppingCartFooter($params)
{
return $this->renderGiftcardAlreadyPaidBlock();
}

/**
* Shared giftcard already-paid / remaining-amount markup for checkout.
*/
private function renderGiftcardAlreadyPaidBlock(): string
{
$cart = $this->context->cart;
if (!$cart || !Validate::isLoadedObject($cart)) {
Expand Down
1 change: 1 addition & 0 deletions controllers/front/ajax.php
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ private function renderCartSummary(Cart $cart, $presentedCart = null)
'alreadyPaid' => $alreadyPaid,
'remainingAmount' => $groupTransactionService->getRemainingAmount((int) $cart->id, $cartTotal),
'giftcardItems' => $groupTransactionService->getDisplayItems((int) $cart->id),
'currencySign' => $this->context->currency ? $this->context->currency->sign : '',
];

$this->ajaxRender(json_encode($responseArray));
Expand Down
79 changes: 46 additions & 33 deletions controllers/front/applygiftcard.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,38 @@ public function postProcess()

$currency = $this->context->currency;
$cartTotal = (float) $cart->getOrderTotal(true, Cart::BOTH);
$groupTransactionService = new BuckarooGroupTransactionService();
$alreadyPaid = $groupTransactionService->getAlreadyPaid((int) $cart->id);
$amountToCharge = $groupTransactionService->getRemainingAmount((int) $cart->id, $cartTotal);

if ($amountToCharge <= 0) {
$this->redirectWithError(
$this->module->l('The order is already fully covered by giftcard(s).')
);
return;
}

// Continue an existing group transaction when applying another giftcard.
$existingGroupTx = '';
$cookieCartId = (int) ($this->context->cookie->buckaroo_giftcard_cart_id ?? 0);
if ($cookieCartId === (int) $cart->id) {
$existingGroupTx = (string) ($this->context->cookie->buckaroo_giftcard_group_tx ?? '');
}
if ($existingGroupTx === '') {
foreach ($groupTransactionService->getGroupTransactionItems((int) $cart->id) as $row) {
if (!empty($row['group_transaction_id'])) {
$existingGroupTx = (string) $row['group_transaction_id'];
break;
}
}
}

try {
/** @var \BuckarooGiftCard $giftcardApi */
$giftcardApi = PaymentRequestFactory::create(PaymentRequestFactory::REQUEST_TYPE_GIFTCARD);

$giftcardApi->currency = $currency->iso_code;
$giftcardApi->amountDebit = (string) round($cartTotal, 2);
$giftcardApi->amountDebit = (string) round($amountToCharge, 2);
$giftcardApi->invoiceId = 'gc_' . $cart->id . '_' . time();
$giftcardApi->orderId = 'gc_' . $cart->id;
$giftcardApi->description = 'Giftcard';
Expand All @@ -94,6 +119,10 @@ public function postProcess()
$giftcardApi->moduleSupplier = $this->module->author;
$giftcardApi->moduleName = $this->module->name;

if ($existingGroupTx !== '') {
$giftcardApi->OriginalTransactionKey = $existingGroupTx;
}

$customVars = [
'cardNumber' => $cardNumber,
'pin' => $securityCode,
Expand Down Expand Up @@ -125,18 +154,22 @@ public function postProcess()

$transactionKey = ($response->getResponse()) ? (string) $response->getResponse()->getTransactionKey() : '';
$groupTransaction = (string) ($response->getGroupTransaction() ?? '');
if ($groupTransaction === '' && $existingGroupTx !== '') {
$groupTransaction = $existingGroupTx;
}
$remainingAmount = (float) $response->getRemainderAmount();
$appliedAmount = round($cartTotal - $remainingAmount, 2);
$appliedAmount = round($amountToCharge - $remainingAmount, 2);
$totalApplied = round($alreadyPaid + $appliedAmount, 2);

$this->logger->logInfo('Giftcard applied', [
'applied' => $appliedAmount,
'total' => $totalApplied,
'remainder' => $remainingAmount,
'group_tx' => $groupTransaction,
]);

// Persist the partial payment to the DB so the checkout summary can reflect it
// across page reloads and after Buckaroo push confirmation.
$groupTransactionService = new BuckarooGroupTransactionService();
$groupTransactionService->saveGroupTransaction((int) $cart->id, [
'transaction_key' => $transactionKey,
'group_transaction_id' => $groupTransaction,
Expand All @@ -149,13 +182,14 @@ public function postProcess()
// Also store in session cookies as fast-access cache for this page-load
$this->context->cookie->buckaroo_giftcard_group_tx = $groupTransaction;
$this->context->cookie->buckaroo_giftcard_remainder = (string) $remainingAmount;
$this->context->cookie->buckaroo_giftcard_applied = (string) $appliedAmount;
$this->context->cookie->buckaroo_giftcard_applied = (string) $totalApplied;
$this->context->cookie->buckaroo_giftcard_tx_key = $transactionKey;
$this->context->cookie->buckaroo_giftcard_card_code = $cardCode;
$this->context->cookie->buckaroo_giftcard_cart_id = (string) (int) $cart->id;
$this->context->cookie->write();

if ($remainingAmount <= 0) {
$this->completeOrderWithGiftcard($cart, $cartTotal, $transactionKey, $cardCode);
$this->completeOrderWithGiftcard($cart, $cartTotal);
} else {
$this->redirectWithGiftcardApplied($appliedAmount, $remainingAmount);
}
Expand All @@ -164,16 +198,11 @@ public function postProcess()
/**
* Giftcard covers the full cart total: create the order now and mark it paid.
*/
private function completeOrderWithGiftcard(
Cart $cart,
float $total,
string $transactionKey,
string $cardCode
): void {
private function completeOrderWithGiftcard(Cart $cart, float $total): void
{
$customer = new Customer($cart->id_customer);
$currency = $this->context->currency;
$pendingState = (int) Configuration::get('BUCKAROO_ORDER_STATE_DEFAULT');
$paidState = (int) Configuration::get('PS_OS_PAYMENT');

try {
$this->module->validateOrder(
Expand All @@ -194,28 +223,12 @@ private function completeOrderWithGiftcard(
}

$orderId = $this->module->currentOrder;
$order = new Order($orderId);

// Link all group-transaction rows for this cart to the newly created order
$groupTransactionService = new BuckarooGroupTransactionService();
$groupTransactionService->linkOrderToCart((int) $cart->id, (int) $orderId);

$payment = new OrderPayment();
$payment->order_reference = $order->reference;
$payment->id_currency = $order->id_currency;
$payment->conversion_rate = 1;
$payment->amount = $total;
$payment->payment_method = $cardCode ?: 'giftcard';
$payment->transaction_id = $transactionKey;
$payment->save();

$order->total_paid_real = $total;
$order->save();

$history = new OrderHistory();
$history->id_order = $orderId;
$history->changeIdOrderState($paidState, $orderId);
$history->add(true);
$this->recordGroupTransactionPayments((int) $orderId);
$this->syncOrderTotalPaidReal((int) $orderId);

$paidState = (int) Buckaroo3::resolveStatusCode(BuckarooAbstract::BUCKAROO_SUCCESS, (int) $orderId);
$this->updatePendingOrderStatus((int) $orderId, $paidState, true);

$this->clearGiftcardCookies();

Expand Down
Loading
Loading