Once again we delve into the world of confusing documentation with broken links, no clear explanation of what you need to do and a lack of cohesion between the different parts of the system so you’re left piecing it together yourself.
As always, the client is told by the company that integration is easy so they’re expecting a low quote, and on first inspection all looks good; plenty of documentation and a PHP SDK! Fantastic. Then you find out the SDK was deprecated a few years ago and the new documentation doesn’t tell you where to start or give any step-by-step instruction. I couldn’t find any examples on the web so, after working it out myself, have written this blog post so you don’t have to.
Our client has a fully fledged and bespoke checkout system so all we needed from Klarna was the option for customers to pay via them at the end of the checkout and for the result to be passed back. A pretty straightforward, and I would have thought common, scenario. One problem was that when contacting Klarna with an issue it felt like I was the first person to ever use their API; because I was integrating manually (rather than, I guess, plugging in to Shopify or whatever) the question had to be escalated to their technical team then I had to wait for them to email back so you end up not contacting them at all because it’s such a faff. Unrelated to the API I also experienced issues getting my account linked to both of my client’s; emails never arrived; Klarna said I shouldn’t have access even though I did to one of them; they couldn’t explain it. All of this takes so much time.
Before you start with the code you’ll need to get some API Credentials.
You can get test credentials by creating an account in the Playground (https://app.playground.klarna.com/login, then create credentials here https://portal.playground.klarna.com/settings/api-credentials).
You’ll find live credentials, or be able to generate new ones if needed, here https://portal.klarna.com/settings/api-credentials.
After a lot of reading I realised there were 3 main steps in the Klarna process:
I wrote a class with the functions as can be found below.
Set up the class, entering your own website URL and Klarna API credentials. Comment/uncomment the relevant lines when testing or going live.
class Klarna{
var $site_url = "https://www.example.com";
// Live
/*var $base_url = "https://api.klarna.com";
var $uid = "";
var $pass = "";*/
// Test
var $base_url = "https://api.playground.klarna.com";
var $uid = "";
var $pass = "";
function __construct(){
}
}
The following describes the three steps split into three methods/functions which need to be added to the class.
We pass the customer’s $order
details through to this function as we need to send them to Klarna. The way you store your data will be different but you just need to loop through the order’s products adding name, price, image etc to an array, which we then include in another array detailing the customer’s name and address.
Then cURL this data to Klarna which, when successful, returns a “session_id”.
Note: I tried entering various amounts in “total_tax_amount”, getting an error each time, so eventually gave up and set it to zero.
function createSession($order){
$url = "{$this->base_url}/payments/v1/sessions";
// Build order_lines
$contents = $order->cart;
foreach($contents as $line){
$product_price = $line->product->grossprice * 100;
$order_line = array(
"image_url" => "{$this->site_url}/images/250/250/{$line->product->main_image}",
"type" => "physical",
"reference" => $line->product->gtin,
"name" => $line->product->name,
"quantity" => $line->quantity,
"unit_price" => $product_price,
"tax_rate" => 20,
"total_amount" => $product_price * $line->quantity,
"total_tax_amount" => 0
);
$order_lines[] = $order_line;
}
$data = array(
"purchase_country" => "gb",
"purchase_currency" => "gbp",
"locale" => "en-GB",
"order_amount" => $order->totalgross * 100,
"order_lines" => $order_lines,
"billing_address" => array(
"given_name" => $order->firstname,
"family_name" => $order->lastname,
"email" => $order->email,
"street_address" => $order->addr1,
"street_address2" => $order->addr2,
"postal_code" => $order->postcode,
"city" => $order->city,
"region" => $order->county,
"phone" => $order->telephone,
"country" => $order->country
)
);
$payload = json_encode($data);
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
"Content-Type: application/json",
"Cache-Control: no-cache",
"Authorization: Basic ".base64_encode("{$this->uid}:{$this->pass}")
));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
$response = curl_exec($ch);
curl_close($ch);
// Process the response
if($response === false){
// Request failed
echo "cURL error: " . curl_error($ch) . "<br/>";
echo "cURL error code: " . curl_errno($ch);
}else{
// Request succeeded
$result = json_decode($response, true);
return $result['session_id'];
}
}
https://docs.klarna.com/hosted-payment-page/api-documentation/create-session/
Using the “session_id” we obtained from createSession() we now need to create a Hosted Payment Page session. This generates a URL to which we redirect the customer so they can pay on Klarna’s website.
We also pass our own $orderid
in to use in the return URLs.
Note: The documentation says “token=<random_uuid>” but I wasn’t able to find out what the random UUID was suppose to be for, so I used it to contain our system’s order ID so we can reconcile the order with the result on our return URL. The “{{session_id}}” is automatically filled by Klarna, so don’t change that. I’m not sure whether it’s the same “session_id” that’s generated by createSession(); if it is then you could save that with the order in your system and reconcile that way, but using the token worked fine for me. “{{order_id}}” is automatically filled with Klarna’s own order ID.
function createHPPSession($session_id, $orderid){
$url = "{$this->base_url}/hpp/v1/sessions";
$data = array(
"payment_session_url" => "{$this->base_url}/payments/v1/sessions/$session_id",
"merchant_urls" => array(
"success" => "{$this->site_url}/shop/thank-you.html?token={$orderid}&sid={{session_id}}&order_id={{order_id}}",
"cancel" => "{$this->site_url}/shop/display-basket.html",
"back" => "{$this->site_url}/shop/display-basket.html",
"failure" => "{$this->site_url}/shop/thank-you.html?token={$orderid}&sid={{session_id}}",
"error" => "{$this->site_url}/shop/thank-you.html?token={$orderid}&sid={{session_id}}"
),
"options" => array(
"place_order_mode" => "PLACE_ORDER"
)
);
$payload = json_encode($data);
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
"Content-Type: application/json",
"Cache-Control: no-cache",
"Authorization: Basic ".base64_encode("{$this->uid}:{$this->pass}")
));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
$response = curl_exec($ch);
curl_close($ch);
// Process the response
if($response === false){
// Request failed
echo "cURL error: " . curl_error($ch) . "<br/>";
echo "cURL error code: " . curl_errno($ch);
}else{
// Request succeeded
$result = json_decode($response, true);
if($result['redirect_url']){
header("Location: {$result['redirect_url']}");
die;
}
}
}
On the return URL (“/shop/thank-you.html” in our case), where the user is redirected after Klarna, we need to check the status of the order; we call the below function which passes Klarna’s order ID back to Klarna and returns information about the order, including its status.
function checkOrderStatus($orderid){
$url = "{$this->base_url}/ordermanagement/v1/orders/$orderid";
$ch = curl_init($url);
// Set options
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
"Content-Type: application/json",
"Cache-Control: no-cache",
"Authorization: Basic ".base64_encode("{$this->uid}:{$this->pass}")
));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPGET, true); // Use GET method
// Execute the request
$response = curl_exec($ch);
curl_close($ch);
// Check for errors
if($response === false){
// Error handling
echo "Error occurred while fetching the data: " . curl_error($ch);
}else{
// Process the response
return json_decode($response, true);
}
}
That’s all the code that deals with Klarna, neatly packaged up into a class, and this is how we use it: when the customer chooses to pay by Klarna the code we run is simple:
$klarna = new Klarna();
$session_id = $klarna->createSession($order);
$klarna->createHPPSession($session_id, $orderid); // Klarna's $session_id, our $orderid
This redirects the customer to Klarna.
On the return page of the website we check the order with this code:
$token = get("token"); // Our order ID
$order_id = get("order_id"); // Klarna's order ID
$klarna = new Klarna();
$info = $klarna->checkOrderStatus($order_id);
// Check status of response
if($info['status'] == "AUTHORIZED"){
// Success
}else{
// Fail
}
If $info['status'] == "AUTHORIZED"
then the order has been successful so we display a success message and carry out our usual process of fulfilling an order i.e. marking it as such in our database and sending the customer an email. Otherwise the order has not been successful so we display a relevant message to the customer.
You’ll probably want to add some error handling but that’s it! Simple when you know how.
This code is free to use at your own discretion. It comes without warranty. Please feel free to feedback any edits.
Like what you’ve read, then why not tell others about it... they might enjoy it too
If you think Bronco has the skills to take your business forward then what are you waiting for?
Get in Touch Today!
It works great. Thank you very much for sharing 🙂
Thanks for this, it helped a lot!
Thank U so much! This saved my day !
much appreciated, thanks mate!
got too much help brother from this article , i was trying different methods to integrate klarna for my client , and the same thing is that my client was saying that its easy but it was too much difficult due to confusing documentation , and when i found documentation then firstly i tried one method and after integrating for some issues and then got that it was old one , and then i tried new method from the updated klarna documentation but the docs were not clear and was not getting satisfaction , then i found your blog and it helped me a lot , thank you so much brother
just want to add one thing that may be you forget to add , when we create $order its like this :
$order = new stdClass();
$order->cart = [
(object)[
‘product’ => (object)[
‘grossprice’ => 100.00,
‘gtin’ => ‘1234567890123’,
‘name’ => ‘Product 1’
],
‘quantity’ => 2
],
(object)[
‘product’ => (object)[
‘grossprice’ => 50.00,
‘gtin’ => ‘9876543210987’,
‘name’ => ‘Product 2’
],
‘quantity’ => 1
]
];
$order->totalgross = 250.00;
$order->firstname = “John”;
$order->lastname = “Doe”;
$order->email = “john.doe@example.com”;
$order->addr1 = “123 Main St”;
$order->addr2 = “Apt 4B”;
$order->postcode = “12345”;
$order->city = “Anytown”;
$order->county = “Anycounty”;
$order->telephone = “123-456-7890”;
$order->country = “GB”;
so you can put the dummy $order data as well , users will get help with it , thanks
You’re welcome; I’m glad it helped.
Hopefully your extra code will help some people.