Klarna JavaScript Integration
Overview
Radial's foundational JavaScript library can be used to quickly integrate with the Radial Payments Framework in order to take advantage of Klarna Payments Support. Klarna is a global payments provider that works with retailers to give customers the smoothest online shopping experience by providing unique payment options and superior customer experience. This can be done by loading Radial's radial_payments.js and invoking the appropriate methods as needed.
When using this JavaScript integration, Klarna checkout includes two main steps:
-
Klarna initialization, detailed below.
-
An API call for payment authorization. For details, see Klarna Get Payment Authorization .
Including radial_payments.js
When using radial_payments.js, you begin by including the library. Add this script tag to your page:
- Test Environment
<script src=https://tst.payments.radial.com/hosted-payments/v2.0/radial_payments.js></script>
- Production Environment
<script src=https://hostedpayments.radial.com/hosted-payments/v2.0/radial_payments.js></script>
Klarna Initialization
Step #1: Nonce Generation
Nonce generation is a server-to-server call. For security purpose, Radial must authenticate the webstore prior to exchanging any data. To facilitate authentication, Radial exposes a HTTPS REST endpoint where a webstore can submit its credentials in the request body.
During Radial Integration, each webstore will be provided with authentication credentials. The webstore must submit these credentials in the body of the nonce generation request.
Use these environment-specific URLs to generate a nonce:
Environment | Endpoint |
---|---|
Test | https://tst.payments.radial.com/hosted-payments/auth/nonce |
Production | https://hostedpayments.radial.com/hosted-payments/auth/nonce |
The following is an example of a nonce generation request body.
{
"username" : "username",
"password" : "password",
}
The nonce generation call provides a response similar to the following.
{
"nonce": "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJUUlVTMiIsImF1ZCI6IndlYiIsImV4cCI6MTU4MjE5NzcxOCwiaWF0IjoxNTgyMTk3NDE4fQ.aaXjnv9SPcKKoOt4k1jzM_tTisgNSZ6OoaNy43SRTI9LPo9APHV-BT1zej7vSCl694qzhJDuqvWyoqYC9ggOow" ,
"expires_in_seconds": 300
}
Note: The nonce obtained has an expiration period associated with it. Currently, the expiration duration is set as 300 seconds. But this expiration period can vary from time to time based on security requirements. If the nonce times out, you will receive an InvalidLoginError(50002) as a response for subsequent calls. If that occurs, the webstore can make a new server to server authentication call to get a new nonce.
The webstore must capture the nonce generated during the /auth/nonce API response and send that in the subsequent calls to the Radial JavaScript as this required for the JavaScript library. The webstore must ensure these nonce value is available in the nonce response.
In case of error, the payload below is returned:
{
"error_code" : "50002",
"error_message" : "InvalidLoginError",
}
Below is a list of possible errors that can be returned.
Error Code | Error Message | Description |
---|---|---|
50002 | InvalidLoginError | The nonce used to connect to Radial is invalid or timed out. Renew the nonce and try again. |
50003 | RadialInternalError | There was an internal error in Radial's system. |
The following code snippet generates the nonce.
public class RestCallClient {
public static void main(String[] args) {
try {
URL url = new URL("https://tst.payments.radial.com/hosted-payments/auth/nonce");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setDoOutput(true);
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/json");
String input = "{\n" +
"\t\"username\": \"username\",\n" +
"\t\"password\" : \"password\",\n" +
"}";
OutputStream os = conn.getOutputStream();
os.write(input.getBytes());
os.flush();
System.out.println(conn.getResponseCode());
BufferedReader br = new BufferedReader(new InputStreamReader(
(conn.getInputStream())));
StringBuilder builder = new StringBuilder();
String output;
System.out.println("Output from Server .... \n");
while ((output = br.readLine()) != null) {
System.out.println(output);
builder.append(output);
}
System.out.println(builder);
JSONObject jsonObject = new JSONObject(builder.toString());
System.out.println(jsonObject.getString("nonce"));
conn.disconnect();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Step #2: Download radial_payments.js and Initialize JavaScript
Note: The JavaScript integration makes a cross-site call from the browser to Radial's server instead of the originating webstore server. This is a CORS request. The webstore should give Radial the domain names of servers for Javascript integration, so that Radial can whitelist the servers to enable the cross-site call. The domain name should be passed in the format of https://website.com
Radial hosts a JavaScript file, radial_payments.js, which provides the Klarna functionality. The nonce received in Step #1 is necessary for subsequent JavaScript calls from the browser. The webstore should download the radial_payments.js file using the script tag when the customer navigates to the checkout page. After downloading the radial_payments.js file, the webstore should invoke the Radial.setup() method by passing the nonce as illustrated in the following example. In other words, Radial.setup() should be invoked in the checkout page onload event.
<head>
<script src="http://tst.payments.radial.com/hosted-payments/v2.0/radial_payments.js"></script>
<script language="JavaScript">
function loadNonce() {
Radial.setup('<%=request.getAttribute("nonce")%>');
}
</script>
</head>
<body onload="loadNonce()">
Integration Flow
The sequence diagram below shows the series of system interactions in Klarna payment processing.
Steps invloved in klarna processing (redirect)
- The browser requests the payment page from the webstore server.
- The webstore server sends a request to Radial with the username and password provided during Radial Integration.
- The webstore server receives nonce and expires_in_seconds in Radial's response.
- The webstore server provides the payment page with nonce, which is used to initialize the Radial JavaScript (with the Radial.setup() method). Webstore server should invoke the Radial.setup() JavaScript method during the checkout page onload().
- The user submits the payment on choosing the Klarna payment method and the browser calls Radial.tokenizeAndAuthorize() with the order data.
- Radial makes a call to the Gateway Provider.
- Radial receives a response with the url from Gateway Processing provider where authentication of the payment needs to be done.
- Customer will be redirected to the issuer page, where authentication is done. The webstore needs to provide return url in request object, see the section below on the Klarna Json Object Format for furter details. Webstore needs to be able to handle the redirect on the server side. Webstore can append query parameters it needs to be in the returnUrl (shopper ids, order ids, etc. Payments will support the query parameters, so the webstore can append what it needs in that url without worry.)
- After customer is redirected to Klarna authentication page, they need to authenticate the payment by choosing the payment option provided by Klarna.
- After authenticating the Klarna payment, customer will be redirected back to redirectUrl which is provided in the order data JSON in step 5 and authenticate data is appended to the redirectUrl after authorizing the payment from Klarna authentication page
- Receive authentication result and paymentId that will be appended in the returnUrl.
- Submit the GetPaymentAuthorizationRequest to Radial using the paymentId and AuthenticationResult.
- Radial sends the authorization request to the Gateway provider.
- Radial receives the authorization response from the Gateway provider.
- The response from the Gateway provider will be sent to the webstore as GetPaymentAuthorizationReply.
- Based on the responseCode from the GetPaymentAuthorizationReply, the webstore shows the result of the transaction in the browser to the shopper.
Step #3: Accept Order Data and Invoke Authentication by Klarna
The webstore client code should call Radial's javascript method 'Radial.tokenizeAndAuthorize()' as described above, passing the first argument as empty string('') and the order data as a jsonObject, as shown in the examples below:
Radial.tokenizeAndAuthorize('', jsonObject, tokenizeAndAuthorizeCallbackHandler);
The webstore must implement the tokenizeAndAuthorizeCallbackHandler and invoke the authorization request with the results of the Klarna authentication.
The table below describes the fields in the json object to be passed in to the Radial.tokenizeAndAuthorize() js method. It is recommended that all of the listed fields are passed in, when possible.
Note regarding shipping address: In certain orders for electronic delivery items, shipping address may not be available. In such cases, it is recommended to send the billing address as the shipping address. In scenarios where the order is being shipped to multiple destinations, send the shipping address of the destination that receives the maximum order value.
Klarna Json Object Format
Field Name | Description | Required | Field Length |
---|---|---|---|
amount.currencyCode | 3 character alpha code ISO-4217 currency code for the sale amount | Y | AN(3) |
amount.value |
|
Y | N(20) |
shippingAmount.currencyCode | 3 character alpha code ISO-4217 currency code for the amount | Y | AN(3) |
shippingAmount.value | Unformatted total shipping amount without any decimals. Eg: $100.00 = 10000, $123.67 = 12367 | Y | N(20) |
taxAmount.currencyCode | 3 character alpha code ISO-4217 currency code for the sale amount | Y | AN(3) |
taxAmount.value | Unformatted total tax amount without any decimals. Eg: $100.00 = 10000, $123.67 = 12367 | Y | N(20) |
ipAddress | The IP Address of the consumer. Note: IPv4 and IPv6 are supported | Y | AN(50) |
orderNumber | Order identifier. Please ensure that the same order number is used in all subsequent transactions for the same order, such as auth request, risk assessment, and order create. If the order number in subsequent calls does not match this order number, it nullifies the effect of the fraud check. | Y | AN(20) |
billingInformation.person.name.firstName | Consumer's first name | Y | AN(50) |
billingInformation.person.name.middleName | Consumer's middle name | N | AN(50) |
billingInformation.person.name.lastName | Consumer's last name | Y | AN(50) |
billingInformation.person.gender | Consumer's gender | N | AN(1) |
billingInformation.person.dateOfBirth | Consumer's Birthdate. Should be sent in this format 01/01/2001 | N | AN(10) |
billingInformation.contactInformation.phoneNumber | Consumer's phone number for billing address. This should be unformatted without hyphens. Example: 222-234-5678 = 2222345678 | Y | AN(20) |
billingInformation.contactInformation.emailAddress | Consumer's email address | Y | AN(255) |
billingInformation.line1 | Consumer's billing address line 1 | Y | AN(50) |
billingInformation.line2 | Consumer's billing address line 2 | N | AN(50) |
billingInformation.city | Consumer's city of billing address | Y | AN(50) |
billingInformation.state | Consumer's billing state | N | AN(50) |
billingInformation.countryCode | Consumer's alpha 2 digit ISO 3166 country code. Example: United States = US | Y | AN(3) |
billingInformation.postalCode | Consumer's billing postal code | Y | AN(10) |
shippingInformation.person.name.firstName | Recepient's first name | Y | AN(50) |
shippingInformation.person.name.middleName | Recepient's middle name | N | AN(50) |
shippingInformation.person.name.lastName | Recepient's last name | N | AN(50) |
shippingInformation.person.gender | Consumer's gender | N | AN(1) |
shippingInformation.person.dateOfBirth | Consumer's Birthdate. Should be sent in this format 01/01/2001 | N | AN(10) |
shippingInformation.contactInformation.phoneNumber | Recepient’s phone number for shipping address. This should be unformatted without hyphens. Example: 222-234-5678 = 2222345678 | Y | AN(20) |
shippingInformation.contactInformation.emailAddress | Recepient’s Consumer's email address | Y | AN(20) |
shippingInformation.line1 | Recepient's address line1 | Y | AN(50) |
shippingInformation.line2 | Recepient's address line2 | N | AN(50) |
shippingInformation.city | Recepient's address city | Y | AN(50) |
shippingInformation.state | Recepient's address state | N | AN(50) |
shippingInformation.countryCode | Recepient's alpha 2 digit ISO 3166 country code. Example: United States = US | Y | AN(3) |
shippingInformation.postalCode | Recepient's postal code | Y | AN(10) |
paymentMethod.tenderType | 2 digit the payment method type used, Klarna (KL), Alipay (AL) | N | AN(2) |
lineItems.type | The type of the line items | Y | AN(15) |
lineItems.reference | The reference of the line items | N | AN(50) |
lineItems.name | The name of the line items | Y | AN(250) |
lineItems.imageUrl | The url of the image | N | AN(250) |
lineItems.productUrl | The url of the product | N | AN(15) |
lineItems.quantity | The number of the products | Y | AN(15) |
lineItems.unitPrice | The amount of the product. Unformatted without any decimals. Eg: $100.00 = 10000, $123.67 = 12367 | Y | AN(15) |
lineItems.totalAmount | The sum of amount of all quantities * unitprices with tax and discount for the individual line item. Unformatted without any decimals. totalAmount = (unitPrice * quantity) + totalTaxAmount - Discount. Eg: $100.00 = 10000, $123.67 = 12367 | Y | AN(500) |
lineItems.taxAmount | The taxAmount of the product. Unformatted without any decimals. Eg: $100.00 = 10000, $123.67 = 12367 | Y | AN(500) |
lineItems.taxRate | Tax rate of the line item. Unformatted without any decimals. Eg: $21.00 = 21000, $12.67 = 1267 | Y | AN(500) |
returnUrl | The return URL in (https://www.radial-demo-store.com/checkout?shopperId=123&orderId=345)to which the customer will be redirected after completing the authentication in a 3rd party website.This URL have requestParameter appended into. | Y | AN(500) |
merchantCode | The radial identifier of the merchant code. | N | AN(500) |
originUrl | The domain of the URL that will be white listed. This URL is required for authentication integrations. Otherwise, the authentication components may not work. | Y | AN(500) |
shopperLocale | RFC 1766 customer's locale. Example: "en-US" | Y | AN(10) |
Sample jsonObject Request with all elements
{
"amount": {
"value": 12500,
"currencyCode": "USD"
},
"shippingAmount": {
"value": 500,
"currencyCode": "USD"
},
"taxAmount": {
"value": 2000,
"currencyCode": "USD"
},
"ipAddress": "IpAddress",
"orderNumber": "OrderNumber",
"shopperLocale" : "en-US",
"billingInformation": {
"person": {
"name": {
"firstName": "BillingFirstName",
"middleName": "BillingMiddleName",
"lastName": "BillingLastName"
},
"gender": "M",
"dateOfBirth": "01/01/2001"
},
"contactInformation": {
"phoneNumber": "BillingPhone",
"emailAddress": "Email"
},
"line1": "Address1",
"line2": "Address2",
"city": "BillingCity",
"state": "BillingState",
"postalCode": "BillingPostalCode",
"countryCode": "BillingCountryCode"
},
"shippingInformation": {
"person": {
"name": {
"firstName": "ShippingFirstName",
"middleName": "ShippingMiddleName",
"lastName": "ShippingLastName"
},
"gender": "M",
"dateOfBirth": "01/01/2001"
},
"contactInformation": {
"phoneNumber": "ShippingPhone",
"emailAddress": "Email"
},
"line1": "ShippingAddress1",
"line2": "ShippingAddress2",
"city": "ShippingCity",
"state": "ShippingState",
"postalCode": "ShippingPostalCode",
"countryCode": "ShippingCountryCode"
},
"paymentMethod": {
"tenderType": "TenderType"
},
"lineItems": [
{
"type": "physical",
"reference": "coolthing1",
"name": "cool thing 1",
"imageUrl": "http://www.estore.com/image1.png",
"productUrl": "http://lwww.estore.com/product1/",
"quantity": 2,
"unitPrice": 5000,
"totalAmount": 12000, ## (5000 * 2) + 2000 - 0(discount)
"taxRate": 2000,
"totalTaxAmount": 2000
}
],
"merchantCode": "MerchantCode",
"returnUrl": "ReturnUrl",
"originUrl" : "localhost:8080",
}
Sample jsonObject Request with discount
{
"amount": {
"value": 10000,
"currencyCode": "USD"
},
"shippingAmount": {
"value": 500,
"currencyCode": "USD"
},
"taxAmount": {
"value": 2000,
"currencyCode": "USD"
},
"ipAddress": "IpAddress",
"orderNumber": "OrderNumber",
"shopperLocale" : "en-US",
"billingInformation": {
"person": {
"name": {
"firstName": "BillingFirstName",
"middleName": "BillingMiddleName",
"lastName": "BillingLastName"
},
"gender": "M",
"dateOfBirth": "01/01/2001"
},
"contactInformation": {
"phoneNumber": "BillingPhone",
"emailAddress": "Email"
},
"line1": "Address1",
"line2": "Address2",
"city": "BillingCity",
"state": "BillingState",
"postalCode": "BillingPostalCode",
"countryCode": "BillingCountryCode"
},
"shippingInformation": {
"person": {
"name": {
"firstName": "ShippingFirstName",
"middleName": "ShippingMiddleName",
"lastName": "ShippingLastName"
},
"gender": "M",
"dateOfBirth": "01/01/2001"
},
"contactInformation": {
"phoneNumber": "ShippingPhone",
"emailAddress": "Email"
},
"line1": "ShippingAddress1",
"line2": "ShippingAddress2",
"city": "ShippingCity",
"state": "ShippingState",
"postalCode": "ShippingPostalCode",
"countryCode": "ShippingCountryCode"
},
"paymentMethod": {
"tenderType": "TenderType"
},
"lineItems": [
{
"type": "physical",
"reference": "coolthing1",
"name": "cool thing 1",
"imageUrl": "http://www.estore.com/image1.png",
"productUrl": "http://lwww.estore.com/product1/",
"quantity": 2,
"unitPrice": 5000,
"totalAmount": 9500, ## (5000 * 2) + 2000 - 2500(discount)
"taxRate": 2000,
"totalTaxAmount": 2000
}
],
"merchantCode": "MerchantCode",
"returnUrl": "ReturnUrl",
"originUrl" : "localhost:8080",
}
So, in the above json, the total amount (amount.value) is less than the sum of totalAmount of all lineItems, total tax and total shipping amount. So, the difference is the total discount applied on the order. (Here, discount = 12500 (sum) - 10000 (amount.value)) = 2500
The webstore needs to redirect the browser to a Klarna authentication window that looks like the example below:
The user is prompted for authentication information.
Upon completion of the process, Radial calls the callback function provided during the authentication call. The webstore must implement a tokenizeAndAuthorizeCallbackHandler() JavaScript function. 'Radial.tokenizeAndAuthorize()' call sends a response with the authentication result and other details as mentioned below in the Response Format section. The webstore can handle the response as shown below:
<script language="javascript">
tokenizeAndAuthorizeCallbackHandler (data) {
switch(data.ActionCode){
// Handle successful authentication scenario
case "SUCCESS":
alert(data.account_token);
// Use these values when creating the GetPaymentAuthorizationRequest
console.log(data.paymentReferenceId);
console.log(data.authenticationResult);
break;
// Handle authentication failed or error encounter scenario
case "FAILURE":
alert(data.failure_code);
alert(data.failure_message);
break;
// Handle service level error
case "ERROR":
alert(data.error_code);
alert(data.error_message);
break;
}
</script>
Response Format
The response object returned in the callback handler is in json format and in general has the format shown below.
{
"account_token": "",
"paymentReferenceId" : "paymentId",
"orderId" : "orderId",
"authenticationResult" : "placeholder",
"responseCode":"SUCCESS"
}
Below is a list of possible failures that can be returned:
Error Code | Error Message | Description |
---|---|---|
40005 | The request sent is not in the excepted format | The json object passed in does not have all the required fields. |
40007 | Authentication failure/Signature verification failure. | When user authentication fails or there is a signature verification failure at Klarna Processing Provider end. |
40008 | BasicParamSetupException | Before calling this method, some basic parameters need to be set |
Some errors can be caused by an issue on the network or with one of Radial's internal services. If one of these errors is returned, the webstore can implicitly retry the request without user intervention. The following errors fall into this category.
Error Code | Error Message | Description |
---|---|---|
50001 | TimeoutError | There was a time out attempting to contact Radial's payment service |
50002 | InvalidLoginError | The nonce used to connect to Radial is invalid or timed out. Renew the nonce and try again. |
50003 | RadialInternalError | There was an internal error in Radial's system. |