UNPKG

swedbank-link-jwt

Version:

Swedbank API (JWT) payment integration helper for Node.js

157 lines (131 loc) 5.18 kB
// index.js const axios = require('axios'); const crypto = require('crypto'); const base64url = require('base64url'); const { v4: uuidv4 } = require('uuid'); const fs = require('fs'); class SwedbankLinkJWT { constructor() { this.url = 'https://pi.swedbank.com'; this.publicKeyURL = 'https://banklink.swedbank.com/public/resources/bank-certificates/009'; } base64urlEncode(data) { return base64url(Buffer.from(data)); } sign(url, country, sellerId, payload = '', privateKeyPem) { const header = { b64: false, crit: ['b64'], iat: Math.floor(Date.now() / 1000), alg: 'RS512', url: url, kid: `${country}:${sellerId}` }; const headerEncoded = this.base64urlEncode(JSON.stringify(header)); const dataToSign = `${headerEncoded}.${payload}`; const signer = crypto.createSign('RSA-SHA512'); signer.update(dataToSign); const signature = signer.sign(privateKeyPem); const signatureEncoded = base64url(signature); return `${headerEncoded}..${signatureEncoded}`; } async checkSignature(headers, body) { try { const res = await axios.get(this.publicKeyURL); const cert = res.data; const publicKey = crypto.createPublicKey(cert); const jwsParts = headers['x-jws-signature'].split('..'); if (jwsParts.length !== 2) return 0; const signature = Buffer.from(jwsParts[1], 'base64'); const dataToVerify = `${jwsParts[0]}.${JSON.stringify(body)}`; const verifier = crypto.createVerify('RSA-SHA512'); verifier.update(dataToVerify); return verifier.verify(publicKey, signature); } catch (err) { console.error('Signature check error:', err); return -1; } } async getProvidersList(country, sellerId, privateKeyPem) { const token = this.sign(`${this.url}/public/api/v2/agreement/providers`, country, sellerId, '', privateKeyPem); const res = await axios.get(`${this.url}/public/api/v2/agreement/providers`, { headers: { 'x-jws-signature': token, 'Accept': 'application/json' } }); return res.data; } generateUUID() { return uuidv4().replace(/-/g, '');; } getLang(locale = 'lv_LV') { const lang = locale.split('_')[1]?.toLowerCase() || 'en'; return ['lt', 'et', 'lv', 'ru'].includes(lang) ? lang : 'en'; } getDescriptionLang(code) { switch (code) { case 'et': case 'ee': return 'Tellimus nr.'; case 'lv': return 'Pasūtījuma Nr.'; case 'lt': return 'Užsakymo Nr.'; default: return 'Order no.'; } } async paymentRequest({ sellerId, country, bic, redirectUrl, notificationUrl, total, orderId, privateKeyPem }) { //use this for debugging //console.log("Seller ID:", sellerId); //console.log("Country:", country); //console.log("BIC:", bic); //console.log("Redirect URL:", redirectUrl); //console.log("Notification URL:", notificationUrl); //console.log("Total:", total); //console.log("Order ID:", orderId); //const lang = this.getLang(); //use this for switching languages //const note = this.getDescriptionLang(lang); //use this for switching languages const lang = 'lv'; const note = 'lv'; const payloadObj = { amount: total.toFixed(2), locale: lang, notificationUrl, redirectUrl, currency: 'EUR', description: `${note} ${orderId}`, endToEndIdentification: this.generateUUID() }; const payload = JSON.stringify(payloadObj); const token = this.sign(`${this.url}/public/api/v2/transactions/providers/${bic}`, country, sellerId, payload, privateKeyPem); const res = await axios.post(`${this.url}/public/api/v2/transactions/providers/${bic}`, payload, { headers: { 'x-jws-signature': token, 'Content-Type': 'application/json' } }); return res.data; } async paymentResponse({sellerId, country, packageId, privateKeyPem}) { const url = `${this.url}/public/api/v2/transactions/${packageId}/status`; const token = this.sign(url, country, sellerId, '', privateKeyPem); const res = await axios.get(url, { headers: { 'x-jws-signature': token, 'Accept': 'application/json' } }); return { headers: res.headers, body: res.data }; } getStatus(statusCode) { switch (statusCode) { case 'EXECUTED': return 'processed'; case 'EXPIRED': case 'FAILED': return 'failed'; case 'CANCELLED_BY_USER': return 'cancelled'; default: return 'pending'; } } } module.exports = new SwedbankLinkJWT();