Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Klarna is not working with Custom line totals #8

Open
fme-tahir opened this issue Apr 11, 2019 · 11 comments
Open

Klarna is not working with Custom line totals #8

fme-tahir opened this issue Apr 11, 2019 · 11 comments

Comments

@fme-tahir
Copy link

Hi,
we are using Klarna on site where it is failing to work with module that plays with order line totals, we recently faced an issue where a module adds a certain amount like extra charges and also subtracts a certain amount (based on some business logic) . The module works perfectly with Paypal, amazon and couple of other payment methods used on site.

Customers should see the checkout page and place order with Klarna payment method, like other payment methods do, with updated Order Grand total.

Customers see a blank Checkout page.
whereas in logs it throws an error as following:
Order line totals do not total order_amount - xxxx != xxx
as soon i disable the klarna payment method, site is back on track.

There should be a way like Paypal, to allow system to inject custom line totals, with Total Label, Total code and (+/-) amount Or atleast it should work with updated totals.

thanks,

@mauveine
Copy link

mauveine commented May 9, 2019

I have the same issue, but while using a coupon code.
The error appears when I go on checkout page and I get a blank.

@fme-tahir
Copy link
Author

I was able to fix the issue, it was matter of going through the logic used by Klarna, you have to use klarna.xml in youmodule/etc that should inject the custom total to payment and then place the model file of that injection at yourmodule/Model/..
The format of both files can be copied from core klarna files.

@bzneil
Copy link

bzneil commented Apr 8, 2020

I was able to fix the issue, it was matter of going through the logic used by Klarna, you have to use klarna.xml in youmodule/etc that should inject the custom total to payment and then place the model file of that injection at yourmodule/Model/..
The format of both files can be copied from core klarna files.

Hi - don't suppose you'd care to expand on this. I'm having the same issue but didn't quite follow your solution. Thanks

@fme-tahir
Copy link
Author

fme-tahir commented Apr 8, 2020

Hi - don't suppose you'd care to expand on this. I'm having the same issue but didn't quite follow your solution. Thanks

create klarna.xml in etc folder, with code as below:

<klarna xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:module:Klarna_Core:etc/klarna.xsd">
    <order_lines id="payments">
        <line id="custom_fee" class="YourVendor\CustomFee\Model\Klarna\Orderline\CustomFee"/>
    </order_lines>
</klarna>

Then create a Model Class mentioned above named CustomFee.php

namespace YourVendor\CustomFee\Model\Klarna\Orderline;

use Klarna\Core\Api\BuilderInterface;
use Klarna\Core\Helper\ConfigHelper;
use Klarna\Core\Helper\DataConverter;
use Klarna\Core\Helper\KlarnaConfig;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\DataObjectFactory;
use Magento\Tax\Model\Calculation;
use Klarna\Core\Model\Fpt\Rate;

/**
 * Class CustomFee
 *
 * @package Klarna\Core\Model\Checkout\Orderline
 */
class CustomFee extends \Klarna\Core\Model\Checkout\Orderline\AbstractLine
{

    const ITEM_TYPE_SURCHARGE = 'custom_fee';
    /** @var Rate $rate */
    private $rate;
    /** @var ConfigHelper $configHelper */
    private $configHelper;
    private $customHelper;

    /**
     * AbstractLine constructor.
     *
     * @param DataConverter        $helper
     * @param Calculation          $calculator
     * @param ScopeConfigInterface $config
     * @param DataObjectFactory    $dataObjectFactory
     * @param KlarnaConfig         $klarnaConfig
     * @param Rate                 $rate
     */
    public function __construct(
        DataConverter $helper,
        Calculation $calculator,
        ScopeConfigInterface $config,
        DataObjectFactory $dataObjectFactory,
        KlarnaConfig $klarnaConfig,
        ConfigHelper $configHelper,
        Rate $rate
    ) {
        parent::__construct(
            $helper,
            $calculator,
            $config,
            $dataObjectFactory,
            $klarnaConfig
        );
        $this->configHelper = $configHelper;
        $this->rate = $rate;
    }

    /**
     * Collect totals process.
     *
     * @param BuilderInterface $checkout
     *
     * @return $this
     * @throws \Klarna\Core\Exception
     */
    public function collect(BuilderInterface $checkout)
    {
        /** @var \Magento\Sales\Model\AbstractModel|\Magento\Quote\Model\Quote $object */
        $object = $checkout->getObject();
       
        $address = $this->getAddress($object);
        $store = $this->getStore($object, $address);
        $totals = $address->getTotals();
        
        if (is_array($totals) && isset($totals['custom_fee'])) {
            $CustomFee = $this->processCustomFeeFromTotals($checkout, $totals, $object, $store);
            $checkout->addData($CustomFee);
        } elseif ($object->getCustomFee() > 0) {
            $CustomFee = $this->processCustomFeeWithoutTotals($checkout, $object, $store);
            $checkout->addData($CustomFee);
        }

    }
    private function processCustomFeeWithoutTotals(BuilderInterface $checkout, $object, $store)
    {
        $CustomFeeLabel = "Custom Fee";

        $amount = $object->getCustomFee();

        /** @noinspection IsEmptyFunctionUsageInspection */
        if (empty($amount) && !empty($object->getCustomFee())) {
            $amount = $object->getBaseSubtotal() - $object->getCustomFee();
        }

        $taxRate = $this->getCustomFeeTaxRate($checkout, $object->getAllVisibleItems());

        if ($taxRate > 100) {
            $taxRate = $taxRate / 100;
        }

        if ($taxRate > 1) {
            $taxRate = $taxRate / 100;
        }

        $taxAmount = 0;

        $unitPrice = $amount;
        $totalAmount = $amount;
        if ($this->klarnaConfig->isSeparateTaxLine($store)) {
            $taxRate = 0;
            $taxAmount = 0;
        } else {
            if ($this->isPriceExcludesVat($store)) {
                $unitPrice += $taxAmount;
                $totalAmount += $taxAmount;
            }
        }
        return [
            'custom_fee_unit_price'   => -abs($this->helper->toApiFloat($unitPrice)),
            'custom_fee_tax_rate'     => $this->helper->toApiFloat($taxRate * 100),
            'custom_fee_total_amount' => -abs($this->helper->toApiFloat($totalAmount)),
            'custom_fee_tax_amount'   => $this->helper->toApiFloat($taxAmount),
            'custom_fee_title'        => $CustomFeeLabel,
            'custom_fee_reference'    => 'custom_fee'

        ];
    }
    private function processCustomFeeFromTotals(BuilderInterface $checkout, $totals, $object, $store)
    {
        $total = $totals['custom_fee'];

        $taxAmount = 0;

        $amount = $total->getValue();
        $taxRate = 0; 

        $unitPrice = $amount;
        $totalAmount = $amount;
        
        
        $unitPrice += $taxAmount;
        $totalAmount += $taxAmount;

        return [
            'custom_fee_unit_price'   => -$this->helper->toApiFloat($unitPrice),
            'custom_fee_tax_rate'     => $this->helper->toApiFloat($taxRate),
            'custom_fee_total_amount' => -$this->helper->toApiFloat($totalAmount),
            'custom_fee_tax_amount'   => -$this->helper->toApiFloat($taxAmount),
            'custom_fee_title'        => (string)$total->getTitle(),
            'custom_fee_reference'    => $total->getCode()

        ];
    }
    private function isPriceExcludesVat($store = null)
    {
        $scope = ($store === null ? ScopeConfigInterface::SCOPE_TYPE_DEFAULT : ScopeInterface::SCOPE_STORES);
        return !$this->config->isSetFlag('tax/calculation/price_includes_tax', $scope, $store);
    }
    /**
     * @param $object
     * @return mixed
     */
    private function getAddress($object)
    {
        $address = $object->getShippingAddress();
        if ($address) {
            return $address;
        }
        return $object->getBillingAddress();
    }

    /**
     * @param $object
     * @param $address
     * @return mixed
     */
    private function getStore($object, $address)
    {
        $store = $object->getStore();
        if (!$store && $address->getQuote()) {
            $store = $address->getQuote()->getStore();
        }
        return $store;
    }

    /**
     * Add order details to checkout request
     *
     * @param BuilderInterface $checkout
     *
     * @return $this
     */
    public function fetch(BuilderInterface $checkout)
    {

        if ($checkout->getCustomFeeUnitPrice() != 0) {
            $checkout->addOrderLine(
                [
                    'type'             => 'surchage',
                    'reference'        => $checkout->getCustomFeeReference(),
                    'name'             => $checkout->getCustomFeeTitle(),
                    'quantity'         => 1,
                    'unit_price'       => $checkout->getCustomFeeUnitPrice(),
                    'tax_rate'         => $checkout->getCustomFeeTaxRate(),
                    'total_amount'     => $checkout->getCustomFeeTotalAmount(),
                    'total_tax_amount' => $checkout->getCustomFeeTaxAmount(),
                ]
            );
        }

        return $this;
    }
}

@like-vyk
Copy link

I have the same issue, but while using a coupon code.
The error appears when I go on checkout page and I get a blank.

We have the same issue. Did you find a fix for it?

@opaque01
Copy link

opaque01 commented Nov 18, 2022

I have the same issue with configurable products.
Magento 2.4.3 p2
Any fixes?

@bzneil
Copy link

bzneil commented Nov 18, 2022 via email

@opaque01
Copy link

opaque01 commented Nov 18, 2022

> I have the same issue with configurable products. Magento 2.4.3 p2 Any fixes?

It is not a problem with configurable products!
I also use the extension "Add Free Product to Cart for Magento 2". If a free product is inside the cart the checkout page wont work.

@fme-tahir
Copy link
Author

fme-tahir commented Nov 18, 2022

> I have the same issue with configurable products. Magento 2.4.3 p2 Any fixes?

It is not a problem with configurable products! I also use the extension "Add Free Product to Cart for Magento 2". If a free product is inside the cart the checkout page wont work.

For any custom fee, you have to follow the steps I mentioned above, that's how it worked for me.

@hannes011
Copy link

hannes011 commented Feb 2, 2023

Hi everyone,

I have the same error, even though I'm not using a custom total line.

My error occurs when \Klarna\Base\Helper\KlarnaConfig::isSeparateTaxLine is set to true only and if a discount (normal cart rule is sufficient) is applied and prices are configured to include tax! It is caused by the fact, that base_discount_amount contains tax while base_row_total does not - this leads to a partial mix of tax and non-tax prices.

I put quite some effort and time into debugging this issue. The following fix would solve that matter for me (and hopefully also for everyone).

Patch for klarna/module-orderlines in version 1.0.11 (klarna version 1.1.9 / base module version 9.1.10):

diff --git a/Model/Calculator/Item.php b/Model/Calculator/Item.php
index d3ccd66..3c7c567 100644
--- a/Model/Calculator/Item.php
+++ b/Model/Calculator/Item.php
@@ -74,6 +74,11 @@ class Item
         $itemResult['total_amount'] = $this->helper->toApiFloat(
             $item['base_row_total'] - $item['base_discount_amount']
         );
+        if ($this->taxConfig->priceIncludesTax($item['store'])) {  // needed since "base_discount_amount" contains tax if priceIncludesTax == true
+            $itemResult['total_amount'] = $this->helper->toApiFloat(
+                $item['base_row_total_incl_tax'] - $item['base_discount_amount'] - $item['base_tax_amount']
+            );
+        }
         $itemResult['total_discount_amount'] = $this->helper->toApiFloat($item['base_discount_amount']);
 
         if (!$this->klarnaConfig->isSeparateTaxLine($store)) {

For previous versions of Klarna (e.g. 8.3.6 with core 6.2.4) a similar fix would have to be made to Model/Checkout/Orderline/Items.php in klarna/module-core

I hope this fixes also the custom total line issue... but at least it solves a core bug in Klarna

@itsbreadd
Copy link

Hi everyone,

Also come across this issue. Might be worth searching isSeparateTaxLine in your entire code base.
We had a free gift module setting this value to true unnecessarily, possibly similar to @opaque01

In our case, it only needs to be true when using the NA Klarna API Endpoint which the Klarna module handles anyway.

Also if you are using a free gift module and sending zero priced products to Klarna instead of products with a 100% discount, you will probably face issues with unit_price.

$_item['unit_price'] = $this->helper->toApiFloat($item->getBasePrice()) ?: $this->helper->toApiFloat($item->getBaseOriginalPrice());

If the getBasePrice is zero it resorts to getBaseOriginalPrice (which contains the full price) but the order line total in the request will still be zero, so Klevu rejects it and the payment method wont show on checkout.

Hope this helps someone.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

7 participants