klarna-checkout
Version:
Library for integrating Klarna Checkout in a nodejs environment
242 lines (203 loc) • 6.69 kB
text/coffeescript
# Import modules
crypto = require 'crypto'
request = require 'request'
Promise = require 'promise'
# Flags
flags =
live: false,
initalized: false
# Credentials
credentials =
eid: null
secret: null
# Configuration (default: Sweden, SEK, Swedish)
config =
purchase_country: 'SE'
purchase_currency: 'SEK'
locale: 'sv-se'
merchant:
id: null
terms_uri: null
checkout_uri: null
confirmation_uri: null
push_uri: null
gui:
layout: 'desktop'
# Klarna REST API constants
klarna =
url:
test: 'https://checkout.testdrive.klarna.com/checkout/orders'
live: 'https://checkout.klarna.com/checkout/orders'
headers:
contentType: 'application/vnd.klarna.checkout.aggregated-order-v2+json'
accept: 'application/vnd.klarna.checkout.aggregated-order-v2+json'
### PRIVATE ###
# Construct HTTP request according to Klarna specifications
httpRequest =
# Set HTTP request headers
headers: (payload) ->
# Create (digestive) biscuit from payload and hash from biscuit
biscuit = if payload? then JSON.stringify(payload) + credentials.secret else credentials.secret
hash = crypto.createHash('sha256').update(biscuit, 'utf-8').digest('base64')
# Return headers
'Accept': klarna.headers.accept
'Authorization': 'Klarna ' + hash,
'Content-Type': klarna.headers.contentType
# Set HTTP request options
options: (data, id) ->
# Set base url depending on live or test environment
url = if flags.live then klarna.url.live else klarna.url.test
# Return HTTP request options
url: if id? then url + '/' + id else url
headers: this.headers(data),
body: data
json: if data? then true else false
# Wrapper for all exported (order related) functions
wrapper = (f) ->
# Module initalized?
if not flags.initalized
throw 'Klarna module not initialized. Please use init() method.'
# All uris set?
for key, value of config.merchant
if not value?
throw "Config error: #{key} not set"
# If no problems -> return original function
return f
# Parse HTTP response for error
parseError = (error, response, body) ->
# If HTTP request error
if error?
return {
type: 'HTTP'
code: error.code
message: error.message
}
# If Klarna error
else if body
body = if typeof body is 'string' then JSON.parse(body) else body
return {
type: 'Klarna'
code: body.http_status_code + " - " + body.http_status_message
message: body.internal_message
}
### PUBLIC ###
publicMethods =
# Initialize
init: (input) ->
if not input?
throw "Missing init values"
# Set merchant ID
if input.eid?
credentials.eid = input.eid
config.merchant.id = input.eid
# Set shared secret
if input.secret?
credentials.secret = input.secret
# Set/unset test flag
if input.live? and typeof input.live is 'boolean'
flags.live = input.live
# If success -> set flag 'initialized'
if input.eid? and input.secret? then flags.initalized = true
# Set config
config: (input) ->
# Country, language and currency
if input.purchase_country?
config.purchase_country = input.purchase_country
if input.purchase_currency?
config.purchase_currency = input.purchase_currency
if input.locale?
config.locale = input.locale
# Layout
if input.layout?
if input.layout is 'desktop' or input.layout is 'mobile'
config.gui.layout = input.layout
# Uris
if input.terms_uri?
config.merchant.terms_uri = input.terms_uri
if input.cancellation_terms_uri?
config.merchant.cancellation_terms_uri = input.cancellation_terms_uri
if input.checkout_uri?
config.merchant.checkout_uri = input.checkout_uri
if input.confirmation_uri?
config.merchant.confirmation_uri = input.confirmation_uri
if input.push_uri?
config.merchant.push_uri = input.push_uri
# Place order
place: (cart) ->
f = () ->
new Promise (resolve, reject) ->
# New resource object based on config options
resource = config
# Add cart to resource
resource.cart = cart
# Construct HTTP request and send to Klarna
request.post httpRequest.options(resource), (error, response, body) ->
# Parse response for error.
err = parseError(error, response, body)
# If error -> reject with error
if err?
return reject err
# Else, if OK from Klarna -> resolve with order id
else if response.statusCode? and response.statusCode is 201
# Get order url
location = response.headers.location
# Get id from url and resolve promise
resolve location.slice(location.lastIndexOf('/') + 1)
# Apply wrapper
wrapper(f)()
# Fetch order
fetch: (id) ->
f = () ->
new Promise (resolve, reject) ->
# Construct HTTP request and send to Klarna
request.get httpRequest.options(null, id), (error, response, body) ->
# If OK -> resolve promise with order
if response?
if response.statusCode is 200
resolve JSON.parse(body)
else
reject parseError(error, response, body)
# Else -> reject with error
else
reject parseError(error, response, body)
# Apply wrapper
wrapper(f)()
# Update order
update: (id, data) ->
f = () ->
new Promise (resolve, reject) ->
# Construct HTTP request and send to Klarna
request.post httpRequest.options(data, id), (error, response, body) ->
# If OK -> resolve promise with order
if response?
if response.statusCode? and response.statusCode is 200
resolve body
else
reject parseError(error, response, body)
# Else -> reject promise with error
else
reject parseError(error, response, body)
# Apply wrapper
wrapper(f)()
# Confirm order (with or without merchant order ids)
confirm: (id, orderid1, orderid2) ->
f = () ->
new Promise (resolve, reject) ->
# New status
data = status: 'created'
# If merchant reference(s), add to data to send
if orderid1?
data.merchant_reference = orderid1: orderid1
if orderid2?
data.merchant_reference.orderid2 = orderid2
# Update order
publicMethods.update(id, data).then(
(order) ->
resolve order
(error) ->
reject error
)
# Apply wrapper
wrapper(f)()
# Export module
module.exports = publicMethods