SDK JavaScript/TypeScript

Gói bankopenapi trên npm gói sẵn việc gọi API, verify webhook và xử lý lỗi. Chạy trên Node.js 20+, Next.js (cả Edge), Cloudflare Workers, Bun, Deno; không phụ thuộc gói nào, có kiểu TypeScript cho mọi đối tượng và sự kiện. Mã nguồn MIT: GitHub.

Cài đặt

npm install bankopenapi
# hoặc: pnpm add bankopenapi · yarn add bankopenapi · bun add bankopenapi

Lấy API key ở trang API key và signing secret ở trang Webhook, đặt vào biến môi trường của backend:

.env
BANKOPENAPI_API_KEY=boa_test_...         # Sandbox; khóa thật là boa_live_...
BANKOPENAPI_WEBHOOK_SECRET=whsec_...     # signing secret của webhook endpoint

Tạo yêu cầu thanh toán

import BankOpenAPI from 'bankopenapi';

const boa = new BankOpenAPI(); // đọc BANKOPENAPI_API_KEY

const pr = await boa.paymentRequests.create(
  { amount: 150000, orderId: 'DH1028', description: 'Đơn hàng DH1028' },
  { idempotencyKey: 'order-DH1028' }, // bấm hai lần cũng chỉ tạo một yêu cầu
);

pr.hostedUrl;     // trang thanh toán để chuyển người mua tới
pr.qr?.payload;   // chuỗi QR để tự vẽ mã (vd. npm install qrcode)
pr.transfer;      // ngân hàng, số tài khoản, số tiền, nội dung chuyển khoản (mã TT…)

Thư viện tự gửi lại khi lỗi mạng, 429 hoặc 5xx, và mỗi lệnh tạo luôn kèm Idempotency-Key (tự sinh nếu bạn không truyền), nên gửi lại không bao giờ tạo hai yêu cầu. Nên truyền khóa gắn với đơn của bạn như ví dụ để người mua bấm hai lần cũng chỉ có một yêu cầu. Các trường của yêu cầu: Yêu cầu thanh toán.

Nhận webhook

verifyWebhook kiểm chữ ký theo chuẩn Standard Webhooks trên body thô rồi trả sự kiện đã có kiểu: kiểm event.type là TypeScript biết kiểu của event.data. Hàm này không cần API key, chỉ cần signing secret.

POST /webhooks/bankopenapi
// app/api/webhooks/bankopenapi/route.ts
import { verifyWebhook, WebhookVerificationError } from 'bankopenapi';

export async function POST(req: Request) {
  const body = await req.text(); // body thô, đừng dùng req.json() trước khi verify
  let event;
  try {
    event = await verifyWebhook(body, req.headers, process.env.BANKOPENAPI_WEBHOOK_SECRET!);
  } catch (err) {
    if (err instanceof WebhookVerificationError) return new Response('Invalid signature', { status: 400 });
    throw err;
  }

  // Chống xử lý trùng: một sự kiện giữ nguyên event.id qua mọi lần gửi lại
  if (await alreadyProcessed(event.id)) return new Response(null, { status: 200 });

  if (event.type === 'payment_request.paid') {
    await markOrderPaid(event.data.orderId, event.data.amountReceived); // event.data có kiểu PaymentRequest
  }

  await markProcessed(event.id);
  return new Response(null, { status: 200 });
}

Viết test cho endpoint: signWebhook(body, secret) trả ba header như BankOpenAPI gửi. Các loại sự kiện và payload:Sự kiện và payload.

Sandbox

Với khóa boa_test_…, giả lập toàn bộ luồng mà không cần tài khoản ngân hàng thật, không tốn phí:

const boa = new BankOpenAPI({ apiKey: process.env.BANKOPENAPI_TEST_KEY }); // boa_test_...

// Tài khoản ngân hàng thử, liên kết xong ngay
await boa.sandbox.createBankAccount();

const pr = await boa.paymentRequests.create({ amount: 150000, orderId: 'DH1028' });

// Người mua "chuyển khoản": yêu cầu thành paid, webhook livemode: false được gửi
await boa.sandbox.payPaymentRequest(pr.id);

// Tiền vào không thuộc đơn nào
await boa.sandbox.createTransaction({ amount: 50000, content: 'chuyen tien' });

Chi tiết: Sandbox.

Danh sách và phân trang

// Một trang
const page = await boa.transactions.list({ type: 'credit', limit: 20 });
console.log(page.data, page.hasMore);

// Mọi trang: thư viện tự gọi trang tiếp theo
for await (const tx of boa.transactions.list({ type: 'credit', from: '2026-11-01' })) {
  console.log(tx.transactedAt, tx.amount, tx.content);
}

Xử lý lỗi

Mọi lỗi từ API là BankOpenAPIError, có code (vd. INVALID_AMOUNT), status, message tiếng Việt và requestId để gửi kèm khi cần hỗ trợ. Lớp con theo mã HTTP: InvalidRequestError, AuthenticationError,PermissionDeniedError, NotFoundError, ConflictError, RateLimitError, InternalServerError,APIConnectionError, APITimeoutError.

import { BankOpenAPIError, NotFoundError } from 'bankopenapi';

try {
  await boa.paymentRequests.retrieve(id);
} catch (err) {
  if (err instanceof NotFoundError) return null;
  if (err instanceof BankOpenAPIError) console.error(err.code, err.message, err.requestId);
  throw err;
}

Danh sách mã lỗi: Lỗi và mã lỗi.

Cấu hình

new BankOpenAPI({ apiKey, webhookSecret, timeout, maxRetries }): mặc định đọc BANKOPENAPI_API_KEY vàBANKOPENAPI_WEBHOOK_SECRET, chờ 30 giây mỗi lần gọi, thử lại tối đa 2 lần. Mỗi hàm nhận thêm tham số cuối{ idempotencyKey, signal, timeout, maxRetries }.

Cloudflare Workers
// Cloudflare Workers không có process.env: truyền khóa từ env của Worker
export default {
  async fetch(req: Request, env: Env) {
    const boa = new BankOpenAPI({
      apiKey: env.BANKOPENAPI_API_KEY,
      webhookSecret: env.BANKOPENAPI_WEBHOOK_SECRET,
    });
    // ...
  },
};

Các hàm

NhómHàm
boa.paymentRequestscreate, retrieve, list({ status, orderId }), cancel
boa.transactionslist({ bankAccountId, type, from, to }), retrieve
boa.bankAccountslist, retrieve
boa.webhookEndpointscreate, retrieve, update, delete, list, retrieveSecret, rotateSecret, sendTest, testUrl, listDeliveries, retrieveDelivery, retryDelivery, retryFailed
boa.eventslist({ type }), retrieve, resend
boa.accountretrieve
boa.sandboxcreateBankAccount, createTransaction, payPaymentRequest
boa.webhooksverify, sign

Liên kết tài khoản ngân hàng mới làm trong app vì cần OTP của ngân hàng.

Ngôn ngữ khác

PHP, Python, Go, Java, .NET… gọi REST trực tiếp: mỗi trang API Reference có ví dụ curl, Node.js, PHP, Python. Verify webhook dùng thư viện Standard Webhooks có sẵn cho hầu hết ngôn ngữ (Nhận và verify webhook). Cần SDK cho ngôn ngữ khác, gửi email tớiteams@bankopenapi.com.

Bắt đầu với BankOpenAPI

Đăng ký tài khoản, liên kết tài khoản ngân hàng và nhận webhook báo có đầu tiên. Cần tư vấn thì nhắn chúng tôi.