Quickstart
This guide takes you from nothing to a verified test payment. Everything here uses test money: testnet coins, no real funds.
1. Create a test API key
Section titled “1. Create a test API key”- Sign in to the merchant dashboard at merchant.dev.bitmomo.app.
- Open Settings → API keys and choose Create key.
- Pick Test mode and Read & write access, then copy the key. It starts with
bm_test_and is shown only once.
Keep the key on your server, for example in an environment variable. Never put it in a browser, mobile app or public repository.
export BITMOMO_API_KEY="bm_test_..."export BITMOMO_API_URL="https://api.dev.bitmomo.app"2. Install the SDK
Section titled “2. Install the SDK”The SDK runs on Node.js 18 or later.
npm install @bitmomo/merchant-sdkYou can also call the HTTP API directly; every step below shows both.
3. Create a payment
Section titled “3. Create a payment”Create the payment from your server, using the price from your own order record. Pass an idempotency_key that you store with the order before calling BitMomo, so a retry after a timeout returns the same payment instead of creating a second one.
import { CryptoPOS, newIdempotencyKey } from '@bitmomo/merchant-sdk';
const bitmomo = new CryptoPOS({ apiKey: process.env.BITMOMO_API_KEY!, baseURL: process.env.BITMOMO_API_URL!,});
const payment = await bitmomo.createPayment({ amount: '100.00', // Ghana cedis, as a decimal string crypto_type: 'USDT_TRC20', fiat_currency: 'GHS', order_id: 'order_1001', idempotency_key: newIdempotencyKey(), // store this with the order first});
console.log(payment.id, payment.crypto_amount, payment.deposit_address, payment.expires_at);The same call over HTTP:
curl -X POST "$BITMOMO_API_URL/api/v1/gateway/payments" \ -H "x-api-key: $BITMOMO_API_KEY" \ -H "content-type: application/json" \ -d '{"amount":"100.00","crypto_type":"USDT_TRC20","fiat_currency":"GHS","order_id":"order_1001","idempotency_key":"7f1c0a7e-5d9b-4a51-9c1e-2a6f0f3b8d11"}'The response includes the locked crypto amount, the deposit address, a qr_code_data payment URI and an expiry time.
4. Send your customer to checkout
Section titled “4. Send your customer to checkout”Send the customer to the hosted checkout page for the payment:
https://customer.dev.bitmomo.app/pay/{payment.id}It shows the QR code, amount and countdown, and updates live as the payment progresses. You can also build your own page from the fields in the create response.
5. Receive the webhook
Section titled “5. Receive the webhook”- In the dashboard, open Settings → Webhooks, enter your HTTPS endpoint and select at least
payment.completed. - Save, then copy the signing secret. It is shown only once.
- Verify every request before trusting it:
import express from 'express';import { CryptoPOS } from '@bitmomo/merchant-sdk';
const app = express();
// Use the raw body: the signature covers the exact bytes BitMomo sent.app.post('/webhooks/bitmomo', express.raw({ type: 'application/json' }), (req, res) => { const verified = CryptoPOS.verifyWebhookEvent(req.body, req.header('x-webhook-signature') ?? '', { // A new endpoint's secret is version 1; each rotation adds one. secrets: [{ version: 1, secret: process.env.BITMOMO_WEBHOOK_SECRET! }], timestamp: req.header('x-webhook-timestamp'), keyVersion: req.header('x-webhook-key-version'), }); if (!verified.valid) return res.status(400).send(verified.reason);
// Store the event durably (keyed by verified.envelope.deliveryId), then reply 2xx. res.sendStatus(200);});6. Confirm before you fulfil
Section titled “6. Confirm before you fulfil”A webhook tells you something changed; it is not proof by itself. Before you ship goods, fetch the payment and check it against your order:
// Exact decimal comparison: never convert money to floating point.const units = (value: string) => { const [whole, fraction = ''] = value.split('.'); return BigInt(whole) * 100_000_000n + BigInt(fraction.padEnd(8, '0'));};
const current = await bitmomo.getPayment(paymentId);const paidInFull = current.status === 'completed' && current.environment === 'test' && current.order_terms?.order_id === order.id && current.economics !== null && units(current.economics.recognized_gross_ghs) >= units(order.amountGhs);Payments explains each field, and Build a merchant server shows a complete, crash-safe integration.
Next steps
Section titled “Next steps”- Environments: base URL, test networks and supported coins.
- API keys and scopes: which key to use for what.
- Webhooks: retries, rotation and replay protection.