bjb-qrismpm-sdk-node
v1.0.7
Published
BJB QRIS MPM SDK for Node.js — SNAP BI standard integration via IBM API Connect
Downloads
74
Readme
BJB QRIS MPM SDK — Node.js
Backend SDK for integrating with Bank BJB Open API (SNAP BI standard) for QRIS Merchant Presented Mode (MPM) payments via IBM API Connect.
Installation
npm install bjb-qrismpm-sdk-nodeQuick Start
const { QrisMpmService, CallbackVerifier, parseCallback } = require('bjb-qrismpm-sdk-node');
const fs = require('fs');
const qrisService = new QrisMpmService({
baseUrl: process.env.BJB_BASE_URL,
clientId: process.env.BJB_CLIENT_ID,
clientSecret: process.env.BJB_CLIENT_SECRET,
channelId: process.env.BJB_CHANNEL_ID,
privateKey: fs.readFileSync('./keys/private-key.pem', 'utf8'),
logger: console.log, // optional
});Generate QR Code
const result = await qrisService.generateQr({
partnerReferenceNo: 'ORDER-001',
amount: { value: '12000', currency: 'IDR' },
feeAmount: { value: '0.00', currency: 'IDR' },
merchantId: '825670157598976',
subMerchantId: '-',
storeId: '-',
terminalId: '825670157598976',
validityPeriod: '2026-12-31T23:59:59+07:00',
additionalInfo: {
paymentId: 1,
merchantCode: '6282120596296',
backendUrl: 'https://your-domain.com/api/v1/callback/qris-mpm',
channelId: '3',
transactionPurpose: '3rd party merchant payment',
},
});
console.log(result.qrContent); // QR string
console.log(result.referenceNo); // BJB reference (use for check status)
console.log(result.partnerReferenceNo); // your referenceResponse
{
"responseCode": "2004700",
"responseMessage": "Success",
"referenceNo": "523925829312",
"partnerReferenceNo": "ORDER-001",
"qrContent": "00020101021226690017ID.CO.BANKBJB.WWW...",
"merchantName": "SAMBARA PROV JABAR",
"terminalId": "825670157598976"
}Check Transaction Status
const status = await qrisService.checkStatus({
originalReferenceNo: '523925829312', // from generateQr response
originalPartnerReferenceNo: 'ORDER-001',
serviceCode: '50',
merchantId: '825670157598976',
additionalInfo: {
PaymentId: 1,
MerchantCode: '6282120596296',
Currency: 'IDR',
},
});
console.log(status.latestTransactionStatus); // "00" = paid
console.log(status.paidTime);Transaction Status Codes
| Status | Description |
|--------|-------------|
| 00 | Success / Paid |
| 01 | Initiated |
| 03 | Pending |
| 05 | Cancelled |
| 06 | Failed |
Callback Verification
BJB sends a POST to your server when payment is completed. Use CallbackVerifier to verify the signature.
const { CallbackVerifier, parseCallback } = require('bjb-qrismpm-sdk-node');
const verifier = new CallbackVerifier({
clientSecret: process.env.BJB_CLIENT_SECRET,
});
app.post('/api/v1/callback/qris-mpm', (req, res) => {
// 1. Verify signature
const isValid = verifier.verify({
method: 'POST',
path: '/api/v1/callback/qris-mpm',
accessToken: (req.headers['authorization'] || '').replace('Bearer ', ''),
body: JSON.stringify(req.body),
timestamp: req.headers['x-timestamp'],
signature: req.headers['x-signature'],
});
if (!isValid) {
return res.status(401).json({
ResponseCode: '4015200',
ResponseMessage: 'Unauthorized',
});
}
// 2. Parse payment data
const payment = parseCallback(req.body);
console.log(payment.originalPartnerReferenceNo); // your order reference
console.log(payment.latestTransactionStatus); // "00" = paid
console.log(payment.amount); // { value, currency }
console.log(payment.additionalInfo.Rrn); // bank reference
// 3. Your business logic (update DB, notify user, etc.)
// 4. Return success to BJB
res.json({
ResponseCode: '2005200',
ResponseMessage: 'Request has been processed successfully',
});
});Callback Flow
Customer pays → BJB processes → BJB POSTs to your backendUrl
│
├─ Authorization: Bearer {bjb_token}
├─ X-TIMESTAMP: {timestamp}
├─ X-SIGNATURE: {HMAC-SHA512, signed with YOUR clientSecret}
└─ Body: { payment data }
│
Your server:
├─ Rebuild signature with same clientSecret
├─ Compare (timing-safe)
├─ If match → process payment
└─ Return 2005200Parsed Callback Fields
| Field | Description |
|-------|-------------|
| originalPartnerReferenceNo | Your order reference |
| originalReferenceNo | BJB reference |
| merchantId | Merchant identifier |
| amount | { value, currency } |
| latestTransactionStatus | "00" = success |
| additionalInfo.Rrn | Bank reference number |
| additionalInfo.PaidTime | Payment timestamp |
Full Payment Flow
1. Generate QR → qrisService.generateQr(payload)
Returns: qrContent, referenceNo
2. Display QR → Show QR to customer (use qrContent with any QR library)
3. Customer pays → Customer scans QR with banking app
4. BJB callback → BJB POSTs to your backendUrl
You verify signature + process payment
5. Check status → qrisService.checkStatus(payload) (optional polling)
Returns: latestTransactionStatusConstructor Parameters
new QrisMpmService({
baseUrl, // string — BJB API Connect URL (required)
clientId, // string — API Connect client key (required)
clientSecret, // string — HMAC signing key (required)
channelId, // string — Channel identifier (required)
privateKey, // string — RSA private key PEM (required)
logger, // function — e.g. console.log (optional)
});API Reference
| Export | Description |
|--------|-------------|
| QrisMpmService | Main service — generateQr(), checkStatus() |
| CallbackVerifier | Verify incoming BJB callback signatures |
| parseCallback | Extract key fields from callback payload |
| SnapHttpClient | Low-level SNAP API HTTP client |
| getAccessToken | Request B2B access token directly |
| createAsymmetricSignature | SHA256withRSA signing → lowercase hex |
| createSymmetricSignature | HMAC-SHA512 signing → lowercase hex |
| SnapError | Error class with responseCode / responseMessage |
Error Handling
const { SnapError } = require('bjb-qrismpm-sdk-node');
try {
const result = await qrisService.generateQr(payload);
} catch (err) {
if (err instanceof SnapError) {
console.log(err.responseCode); // "4014700"
console.log(err.responseMessage); // "Unauthorized. Signature mismatch"
console.log(err.toJSON()); // { responseCode, responseMessage }
}
}Error Codes
| Code | Description |
|------|-------------|
| 2007300 | Access token granted |
| 2004700 | QR generated successfully |
| 2005100 | Status check successful |
| 2005200 | Callback processed |
| 4004700 | Bad request |
| 4014700 | Unauthorized / Signature mismatch |
| 4044700 | Transaction not found |
| 4044708 | Invalid merchant |
| 5004700 | General error |
Logging
Pass a logger function to see full request/response details:
const qrisService = new QrisMpmService({
...config,
logger: console.log,
});Output:
[a1b2c3d4] service=QRIS_MPM action=GENERATE_QR
[a1b2c3d4] service=TOKEN action=REQUEST url=https://devapi.bankbjb.co.id/api/v1/access-token/b2b
[a1b2c3d4] service=TOKEN action=REQUEST_HEADERS {"Content-Type":"application/json","X-CLIENT-KEY":"..."}
[a1b2c3d4] service=TOKEN action=REQUEST_BODY {"grantType":"client_credentials"}
[a1b2c3d4] service=TOKEN action=RESPONSE responseCode=2007300
[a1b2c3d4] service=TOKEN action=RESPONSE_BODY {"accessToken":"AAIg...","expiresIn":3600}
[a1b2c3d4] service=SNAP_API action=REQUEST url=https://devapi.bankbjb.co.id/v1.0/qr/qr-mpm-generate
[a1b2c3d4] service=SNAP_API action=REQUEST_HEADERS {"Authorization":"Bearer AAIg...","X-SIGNATURE":"..."}
[a1b2c3d4] service=SNAP_API action=REQUEST_BODY {"partnerReferenceNo":"ORDER-001",...}
[a1b2c3d4] service=SNAP_API action=RESPONSE status=200 responseCode=2004700
[a1b2c3d4] service=SNAP_API action=RESPONSE_BODY {"qrContent":"0002010102..."}Environments
| Environment | Base URL |
|-------------|----------|
| Development | https://devapi.bankbjb.co.id |
Dev environment uses a self-signed certificate — the SDK automatically skips SSL verification when the base URL contains devapi.
License
ISC
