diff --git a/api/paymentmethods/klarna/klarna.php b/api/paymentmethods/klarna/klarna.php index dac52d9e3..5b9cd0d98 100644 --- a/api/paymentmethods/klarna/klarna.php +++ b/api/paymentmethods/klarna/klarna.php @@ -33,6 +33,13 @@ public function getPayload($data) return array_merge_recursive($this->payload, $data); } + public function reserve($customVars = []) + { + $this->payload = $this->getPayload($customVars); + + return $this->executeCustomPayAction('reserve'); + } + public function pay($customVars = []) { $this->payload = $this->getPayload($customVars); diff --git a/controllers/front/request.php b/controllers/front/request.php index a0b4ea67b..a8cedb1c2 100644 --- a/controllers/front/request.php +++ b/controllers/front/request.php @@ -400,6 +400,7 @@ private function handleSuccessfulRequest($cartId, $customer) $this->logger->logInfo('Request succeeded'); if ($this->checkout->isRedirectRequired()) { + $this->storeKlarnaDataRequestKey($response); $this->setCartCookie($cartId); $this->logger->logInfo('Redirecting ... '); $this->checkout->doRedirect(); @@ -550,6 +551,24 @@ private function handleFailedRequest($cartId) Tools::redirect($redirectUrl); } + private function storeKlarnaDataRequestKey($response): void + { + $method = Tools::strtolower(trim((string) Tools::getValue('method', ''))); + if ($method !== 'klarna') { + return; + } + + $responseData = $response->getResponse(); + $dataRequestKey = $responseData ? trim((string) $responseData->getTransactionKey()) : ''; + if ($dataRequestKey === '') { + return; + } + + $id_order = (int) $this->module->currentOrder; + $this->createTransactionMessage($id_order, 'Data Request Key: ' . $dataRequestKey); + $this->storeTransactionKeyOnOrderPayment($id_order, $dataRequestKey); + } + private function createTransactionMessage($orderId, $messageString) { $message = new Message(); diff --git a/controllers/front/return.php b/controllers/front/return.php index 2d83bf0ac..0dbef847f 100644 --- a/controllers/front/return.php +++ b/controllers/front/return.php @@ -15,6 +15,7 @@ * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) */ +use Buckaroo\PrestaShop\Src\Refund\KlarnaTransactionKey; use Buckaroo\PrestaShop\Src\Repository\RawBuckarooFeeRepository; use Buckaroo\PrestaShop\Src\Service\BuckarooGroupTransactionService; @@ -163,6 +164,10 @@ public function initContent() $new_status_code = (int) Buckaroo3::resolveStatusCode($response->status, $id_order); $order = new Order($id_order); + if (KlarnaTransactionKey::isCapturePush($response)) { + KlarnaTransactionKey::storeCaptureKey($order, (string) $response->transactions); + } + // Validate that the resolved order state actually exists in this shop. if (!$this->isValidOrderStateId($new_status_code)) { $this->logger->logError( diff --git a/library/checkout/checkout.php b/library/checkout/checkout.php index d5f939ff0..5568cb05f 100644 --- a/library/checkout/checkout.php +++ b/library/checkout/checkout.php @@ -418,7 +418,7 @@ protected function prepareProductArticles() } /** - * Get product image URL if method is "afterpay" + * Get product image URL if method is "afterpay" or "klarna" * * @param array $product * @@ -426,7 +426,7 @@ protected function prepareProductArticles() */ private function getProductImgUrl($product) { - if (Tools::getValue('method') !== "afterpay") { + if (!in_array(Tools::getValue('method'), ['afterpay', 'klarna'], true)) { return null; } diff --git a/library/checkout/klarnacheckout.php b/library/checkout/klarnacheckout.php index 814511e88..dcf67ccda 100644 --- a/library/checkout/klarnacheckout.php +++ b/library/checkout/klarnacheckout.php @@ -38,7 +38,7 @@ final public function setCheckout() $this->customVars = [ 'operatingCountry' => Tools::strtoupper($country->iso_code), 'billing' => $this->getBillingAddress(), - 'articles' => $this->getArticles(), + 'articles' => $this->prepareKlarnaArticles($this->getArticles()), 'shipping' => $this->getShippingAddress(), ]; } @@ -57,12 +57,12 @@ protected function getAddress(array $address): array $address_components = $this->getAddressComponents($address['address1']); // phpcs:ignore $address = array_merge($address, $address_components); - return [ + $phone = !empty($address['phone_mobile']) ? $address['phone_mobile'] : ($address['phone'] ?? ''); + + $payload = [ 'recipient' => [ 'firstName' => $address['firstname'], 'lastName' => $address['lastname'], - 'gender' => Tools::getValue('bpe_klarna_invoice_person_gender') === '1' ? 'male' : 'female', - 'category' => 'B2C', ], 'address' => [ 'street' => $address['street'], @@ -74,6 +74,14 @@ protected function getAddress(array $address): array ], 'email' => $this->customer->email, ]; + + if (!empty($phone)) { + $payload['phone'] = [ + 'mobile' => $phone, + ]; + } + + return $payload; } public function getShippingAddress() @@ -96,11 +104,43 @@ public function isVerifyRequired() public function startPayment() { - $this->payment_response = $this->payment_request->pay($this->customVars); + $this->payment_response = $this->payment_request->reserve($this->customVars); } protected function initialize() { $this->payment_request = PaymentRequestFactory::create(PaymentRequestFactory::REQUEST_TYPE_KLARNA); } + + protected function prepareKlarnaArticles(array $articles): array + { + foreach ($articles as $key => $article) { + if (empty($article['type'])) { + $articles[$key]['type'] = $this->resolveKlarnaArticleType($article); + } + } + + return $articles; + } + + protected function resolveKlarnaArticleType(array $article): string + { + $identifier = isset($article['identifier']) ? (string) $article['identifier'] : ''; + $price = isset($article['price']) ? (float) $article['price'] : 0.0; + $description = isset($article['description']) ? Tools::strtolower((string) $article['description']) : ''; + + if ($identifier === 'shipping' || $description === 'shipping costs') { + return 'shipping_fee'; + } + + if ($price < 0 || $description === 'discount') { + return 'discount'; + } + + if (in_array($identifier, ['0', 'buckaroo_fee'], true) || in_array($description, ['wrapping', 'buckaroo_fee'], true)) { + return 'surcharge'; + } + + return 'physical'; + } } diff --git a/src/Refund/KlarnaTransactionKey.php b/src/Refund/KlarnaTransactionKey.php new file mode 100644 index 000000000..2d4901e52 --- /dev/null +++ b/src/Refund/KlarnaTransactionKey.php @@ -0,0 +1,69 @@ + + * @copyright Copyright (c) Buckaroo B.V. + * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) + */ + +namespace Buckaroo\PrestaShop\Src\Refund; + +if (!defined('_PS_VERSION_')) { + exit; +} + +class KlarnaTransactionKey +{ + /** + * Klarna Pay (capture) transaction type. Refunds must use this key, not the Reserve data request key. + */ + public static function isCapturePush($response): bool + { + $method = \Tools::strtolower((string) ($response->payment_method ?? '')); + if (strpos($method, 'klarna') === false) { + return false; + } + + if (!empty($response->brq_relatedtransaction_refund)) { + return false; + } + + return strtoupper((string) ($response->brq_transaction_type ?? '')) === 'C339' + && $response->hasSucceeded() + && trim((string) ($response->transactions ?? '')) !== ''; + } + + public static function storeCaptureKey(\Order $order, string $transactionKey): void + { + $transactionKey = trim($transactionKey); + if ($transactionKey === '' || !\Validate::isLoadedObject($order)) { + return; + } + + $payments = \OrderPayment::getByOrderReference($order->reference); + if (!is_array($payments)) { + $payments = []; + } + + foreach ($payments as $payment) { + if ((float) $payment->amount <= 0) { + continue; + } + + if ((string) $payment->transaction_id !== $transactionKey) { + $payment->transaction_id = $transactionKey; + $payment->update(); + } + break; + } + } +} diff --git a/views/templates/hook/payment_klarna.tpl b/views/templates/hook/payment_klarna.tpl index b8ff385f9..228de905c 100644 --- a/views/templates/hook/payment_klarna.tpl +++ b/views/templates/hook/payment_klarna.tpl @@ -14,19 +14,4 @@ *}
-
-
-
- -
-
- -
-
-