Klarna
Offer BoomPay as a pay-now crypto option in the same checkout session as Klarna's BNPL: one session, two payment categories, each resolved through its own flow.
Klarna is primarily a Buy Now Pay Later provider, but its checkout session accepts multiple payment_method_categories — including custom ones you define. Adding BoomPay as a boompay category in the Klarna session means your checkout presents Klarna’s own BNPL options and a Boomcoin option in the same flow, without requiring a separate checkout page or a second session for crypto. Klarna handles its side; BoomPay handles its side; your platform reconciles both through a shared return handler.
Klarna’s payment_method_categories field lets you list custom options alongside Klarna’s own BNPL products, but Klarna does not process the BoomPay transaction itself. When the customer selects Boomcoin, your platform routes them out of the Klarna flow entirely to BoomPay’s hosted page, and the return comes to your handler — not Klarna’s. Klarna sees this as a session that wasn’t completed through its own rails, which is expected and correct for this pattern.
Flow
Session created
Your server creates a Klarna session including a custom 'boompay' payment category, and pre-generates a BoomPay intent link for the order.
Customer chooses
Klarna's checkout widget renders both BNPL options and the Boomcoin option. If the customer selects Boomcoin, your page redirects them to BoomPay's hosted link.
Customer pays
The customer approves from their Boom wallet on BoomPay's hosted page.
Signed return
BoomPay redirects to your successUrl with paymentIntentId and X-Boom-Signature.
Confirm
Your handler verifies the signature, confirms via getPayment(), marks the order paid, and notifies the merchant.
Code
const BoomPay = require('boom-pay-sdk');
// When creating a Klarna Payments session, include BoomPay as an additional
// payment category. Klarna handles its own BNPL categories (pay_later,
// pay_over_time); you add boompay as a parallel pay_now option.
async function createCheckoutSession(merchant, order) {
const boomPay = new BoomPay({
apiKey: merchant.boompayApiKey,
sandbox: process.env.NODE_ENV !== 'production',
});
// Pre-create the BoomPay intent so the link is ready if the customer
// selects Boomcoin. Store the intent id with the session.
const intent = await boomPay.payments.createIntent({
amount: toBmc(order.amount, order.currency),
successUrl: `${process.env.PSP_BASE}/boompay/return?status=success&session=${order.sessionId}&mid=${merchant.id}`,
failureUrl: `${process.env.PSP_BASE}/boompay/return?status=failure&session=${order.sessionId}&mid=${merchant.id}`,
label: `${merchant.name} Order ${order.reference}`,
metadata: { sessionId: order.sessionId, merchantId: merchant.id },
});
await db.setSessionMeta(order.sessionId, 'boompay_intent_id', intent.id);
await db.setSessionMeta(order.sessionId, 'boompay_link', intent.link);
// Klarna session creation body: include your standard order lines plus
// a custom payment_method_categories entry for BoomPay.
const klarnaSessionBody = {
purchase_country: order.country,
purchase_currency: order.currency,
locale: order.locale,
order_amount: order.amount,
order_lines: order.lines,
payment_method_categories: [
...merchant.klarnaCategories, // e.g. pay_later, pay_over_time
{
identifier: 'boompay',
name: 'Boomcoin (BoomPay)',
asset_urls: {
descriptive: 'https://your-cdn.example.com/boompay-logo.png',
standard: 'https://your-cdn.example.com/boompay-icon.png',
},
},
],
};
return { klarnaSessionBody, boompayLink: intent.link };
}
function toBmc(amount, currency) {
return amount; // placeholder -- replace with real conversion
}const crypto = require('crypto');
// BoomPay returns the customer here after they pay via the Boomcoin option.
async function handleBoompayReturn(req, res) {
const { session: sessionId, mid, status } = req.query;
const intentId = req.query.paymentIntentId;
const signature = (req.query['X-Boom-Signature'] || '').replace(/ /g, '+');
const merchant = await db.getMerchant(mid);
const expected = crypto
.createHmac('sha1', merchant.boompayApiKey)
.update(intentId)
.digest('base64');
if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature))) {
return res.status(403).send('Invalid signature');
}
const boomPay = new BoomPay({ apiKey: merchant.boompayApiKey, sandbox: process.env.NODE_ENV !== 'production' });
const payment = await boomPay.payments.getPayment(intentId);
if (status === 'success' && payment.paidAt) {
await db.markSessionPaid(sessionId, { provider: 'boompay', reference: intentId });
await notifyMerchant(merchant, 'PAYMENT_COMPLETE', sessionId, payment);
return res.redirect(merchant.successUrl);
}
await db.markSessionFailed(sessionId);
res.redirect(merchant.failureUrl);
}Go-live checklist
- Confirm Klarna’s terms of service for your region allow custom payment method categories in sessions — policies vary by market.
- Implement
toBmc()with a real conversion before going live. - Handle the case where the customer returns from BoomPay but the Klarna session has expired — Klarna sessions have a short TTL.
- Test that Klarna’s BNPL path and BoomPay’s path can’t both resolve the same order.