BoomPay/partners

Integration Guide

The complete technical contract between your PSP platform and BoomPay: authentication, the payment lifecycle, signature verification, error handling, and going live.

npm: boom-pay-sdkv2.0.2Node.js / TypeScript

Integration model

BoomPay sits inside your platform as a hosted redirect APM. Your backend calls BoomPay’s API to create a payment session, hands the customer a hosted URL to approve payment from their Boom wallet, then handles a signed browser redirect on return. From the merchant’s perspective, they configure one API key in your dashboard and toggle Boomcoin on — identical to enabling any other payment method you offer.

Authentication and environments

BoomPay authenticates with a single x-api-key header. Each merchant has their own key, tied to their own Boom wallet. Your platform stores these per-merchant in your secrets store and instantiates a client per request — never share keys across merchants, and never expose them in client-side code or API responses.

EnvironmentBase URLWhen to use
Sandboxhttps://sapi.boom.marketAll development and certification testing. Keys from sandbox and production are different.
Productionhttps://api.boom.marketLive merchant traffic. Default when sandbox is omitted or false.

Install and initialize

bash
npm install boom-pay-sdk
psp/boompay-client.js
const BoomPay = require('boom-pay-sdk');

// Your platform instantiates one BoomPay client per merchant, using the API
// key they entered in your dashboard. Keep keys in your secrets store --
// they must never reach client-side code.
function boompayClientFor(merchant) {
  return new BoomPay({
    apiKey: merchant.boompayApiKey,
    sandbox: process.env.NODE_ENV !== 'production',
  });
}
i

The SDK constructor throws synchronously if apiKey is missing or options are malformed — wrap instantiation in a try/catch when building the client from untrusted merchant config.

Payment lifecycle

01

Create intent

Your backend calls payments.createIntent() with the BMC amount, success/failure URLs pointing at your own callback route, a label, and optional metadata.

02

Redirect customer

You redirect the customer's browser to intent.link — BoomPay's hosted page. Store the intent id in your order record.

03

Customer approves

The customer pays from their Boom wallet on BoomPay's hosted page.

04

Signed return

BoomPay redirects to your successUrl or failureUrl with paymentIntentId and X-Boom-Signature as query parameters.

05

Verify and confirm

Your callback handler verifies the HMAC-SHA1 signature, re-fetches the payment via getPayment(), and updates the order.

06

Notify merchant

Your platform fires its own webhook to the merchant — same as any other payment method in your system.

psp/create-session.js
// When a customer selects Boomcoin at your checkout, your platform creates
// an intent using that merchant's BoomPay client.
async function createBoompaySession(merchant, order) {
  const boomPay = boompayClientFor(merchant);

  const intent = await boomPay.payments.createIntent({
    amount: order.totalBmc,          // BMC amount -- convert from fiat first
    successUrl: `${PSP_BASE}/boompay/return?status=success&order=${order.id}&mid=${merchant.id}`,
    failureUrl: `${PSP_BASE}/boompay/return?status=failure&order=${order.id}&mid=${merchant.id}`,
    label: `${merchant.name} — Order ${order.reference}`,
    metadata: { orderId: order.id, merchantId: merchant.id },
  });

  // Store the intent id so your return handler can verify and re-fetch it.
  await db.setOrderMeta(order.id, 'boompay_intent_id', intent.id);

  // Redirect the customer's browser to this URL.
  return intent.link;
}

Signature verification

BoomPay signs its return redirect with an HMAC-SHA1 of the paymentIntentId, keyed with the merchant’s API key, base64-encoded. The signature arrives as the X-Boom-Signature query parameter. URL-decoding by intermediate layers can turn + into a space — restore it before comparing. Always use a timing-safe comparison to prevent timing oracle attacks.

!

The SDK’s boomPay.webhooks() is Express middleware that performs this verification automatically, but it’s bound to the single API key passed at construction time. For multi-merchant PSP use cases you need to select the right merchant’s key first, then verify — the explicit implementation below makes that easier.

psp/verify-signature.js
// Manual HMAC-SHA1 signature verification (equivalent to boomPay.webhooks()
// but written out explicitly for multi-merchant PSP use cases where you need
// to select the right API key before verifying).
const crypto = require('crypto');

function verifyBoompaySignature(paymentIntentId, rawSignature, apiKey) {
  const expected = crypto
    .createHmac('sha1', apiKey)
    .update(paymentIntentId)
    .digest('base64');
  // URL-decoding can turn '+' into a space; restore it before comparing.
  const received = rawSignature.replace(/ /g, '+');
  if (Buffer.byteLength(expected) !== Buffer.byteLength(received)) return false;
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(received)
  );
}
psp/boompay-return.js
const express = require('express');
const router = express.Router();

// BoomPay redirects the customer's browser here after they pay or cancel.
// boomPay.webhooks() verifies the HMAC-SHA1 signature before your handler
// runs -- it returns 400 (missing signature), 403 (invalid), or 500 (no
// API key). If it passes, req.query.paymentIntentId is trustworthy.
//
// Note: webhooks() is Express middleware bound to a specific merchant's
// API key. Since PSPs handle multiple merchants, mount it per-request.
router.get('/boompay/return', async (req, res) => {
  const { order: orderId, mid: merchantId, status } = req.query;
  const merchant = await db.getMerchant(merchantId);
  const boomPay = boompayClientFor(merchant);

  // Run BoomPay's signature check inline since we need a per-merchant client.
  const intentId = req.query.paymentIntentId;
  const sig = (req.query['X-Boom-Signature'] || '').replace(/ /g, '+');
  const expected = require('crypto')
    .createHmac('sha1', merchant.boompayApiKey)
    .update(intentId)
    .digest('base64');

  if (!require('crypto').timingSafeEqual(Buffer.from(expected), Buffer.from(sig))) {
    return res.status(403).send('Invalid BoomPay signature');
  }

  // Re-fetch the payment to confirm state -- don't trust query params alone.
  const payment = await boomPay.payments.getPayment(intentId);
  const order = await db.getOrder(orderId);

  if (status === 'success' && payment.paidAt) {
    await db.markOrderPaid(orderId, { provider: 'boompay', reference: intentId });
    await notifyMerchant(merchant, 'PAYMENT_COMPLETE', order, payment);
    return res.redirect(merchant.successReturnUrl);
  }

  await db.markOrderFailed(orderId);
  await notifyMerchant(merchant, 'PAYMENT_FAILED', order, payment);
  res.redirect(merchant.failureReturnUrl);
});

module.exports = router;

createIntent parameters

ParameterTypeRequiredNotes
amountnumberYesBMC (Boomcoin) amount. BoomPay has no FX endpoint — convert from fiat before calling.
successUrlstring (URL)YesWhere BoomPay redirects on successful payment. Must be a valid URL pointing at your PSP callback route.
failureUrlstring (URL)YesWhere BoomPay redirects on cancellation or failure. Can be the same route with a different status query param.
labelstringYesHuman-readable description shown on BoomPay's hosted page. Include merchant name and order reference.
metadataobjectNoRound-tripped back in the payment response. Useful for orderId, merchantId — but verify these from your own DB, not from metadata.

Merchant onboarding

When a merchant enters their BoomPay API key in your dashboard, validate it immediately by calling wallets.getDefaultWallet() — a lightweight read that confirms the key is valid and returns their Boom wallet address. Store the address for display in your merchant dashboard alongside the payment method. Merchants create their API key and Boom wallet from the Boom mobile app.

psp/onboard-merchant.js
// Example: merchant saves their BoomPay API key in your dashboard.
// Validate it by calling getDefaultWallet() -- a lightweight read that
// returns their wallet address and balance, confirming the key works.
async function validateBoompayKey(apiKey, sandbox) {
  const boomPay = new BoomPay({ apiKey, sandbox });
  try {
    await boomPay.wallets.getDefaultWallet();
    return { valid: true };
  } catch (err) {
    return { valid: false, message: err.message };
  }
}

Error handling

Failed API calls throw a RestException with .status (HTTP code) and .message. The SDK retries automatically on HTTP 429 (up to 3 times, up to 3000 ms delay) and times out at 30 seconds. Constructor validation is synchronous and throws before any network call if apiKey is missing or successUrl/failureUrl/amount/label are invalid.

Sandbox testing

Use separate sandbox API keys (obtainable from the Boom app in test mode) and point your BoomPay client at sandbox: true. Run at least two full end-to-end flows before certifying a PSP integration for production: one successful payment and one deliberately cancelled payment. Confirm that your merchant webhook fires correctly for both outcomes and that your order state machine handles them without getting stuck.

Go-live checklist

  • All merchant API keys stored server-side only, never in client-side responses or logs.
  • Signature verification uses crypto.timingSafeEqual — not ===.
  • Your callback handler re-fetches the payment via getPayment() before fulfilling — don’t trust query params alone.
  • BMC conversion logic is implemented and tested — BoomPay has no FX endpoint.
  • Both success and failure paths tested end-to-end in sandbox with merchant webhooks firing.
  • Production sandbox: false, live API keys active for a pilot merchant before broad rollout.