tripay-node-sdk
v1.0.0
Published
Intuitive zero-dependency SDK for the TriPay payment gateway (Closed & Open Payment). Works on Node.js 18+ and Bun.
Readme
tripay-node-sdk
Zero-dependency, fully typed SDK for the TriPay payment gateway. Covers Closed Payment and Open Payment transactions, merchant tools, payment instructions, e-wallet linking, and callback verification.
Works on Node.js 18+ and Bun (uses only fetch + node:crypto). Ships ESM + CommonJS + types.
Install
npm i tripay-node-sdk
# pnpm add tripay-node-sdk
# yarn add tripay-node-sdk
# bun add tripay-node-sdkQuick start
import { Tripay } from "tripay-node-sdk";
const tripay = new Tripay({
apiKey: process.env.TRIPAY_API_KEY!,
privateKey: process.env.TRIPAY_PRIVATE_KEY!,
merchantCode: process.env.TRIPAY_MERCHANT_CODE!,
mode: "sandbox", // or "production"
});
const tx = await tripay.createTransaction({
method: "BRIVA",
merchantRef: "INV-001",
amount: 100_000,
customerName: "Nama Pelanggan",
customerEmail: "[email protected]",
customerPhone: "081234567890",
orderItems: [{ name: "T-Shirt", price: 100_000, quantity: 1 }],
returnUrl: "https://yourshop.com/thanks",
expiresIn: 24 * 3600, // seconds
});
console.log(tx.reference, tx.pay_code, tx.checkout_url);// CommonJS
const { Tripay } = require("tripay-node-sdk");Everything it does
Closed payment (one invoice = one fixed amount)
// Create — amount MUST equal sum(orderItems price × qty)
const tx = await tripay.createTransaction({
method: "QRIS",
merchantRef: "INV-002",
amount: 50_000,
customerName: "Jane",
customerEmail: "[email protected]",
orderItems: [
{ sku: "A1", name: "Item A", price: 30_000, quantity: 1 },
{ sku: "B2", name: "Item B", price: 10_000, quantity: 2 },
],
});
// Expiry options (pick one): expiredTime (unix seconds) | expiresIn (seconds) | expiredAt (Date)
// Default when omitted: TriPay's own default (24h).
// Detail + status
const detail = await tripay.getTransaction("T0001000000000000006");
const { status } = await tripay.checkTransaction("T0001000000000000006"); // PAID | UNPAID | ...
// Paginated history (perPage max 50 — TriPay limit)
const page = await tripay.listTransactions({ page: 1, perPage: 25, status: "PAID" });Open payment (one reusable VA/QR, any amount, many payments)
// Production only (TriPay has no sandbox for open payment)
const prod = new Tripay({ apiKey, privateKey, merchantCode, mode: "production" });
const op = await prod.createOpenPayment({ method: "QRISOP", merchantRef: "STATIC-01" });
const same = await prod.getOpenPayment(op.uuid);
const incoming = await prod.listOpenPaymentTransactions(op.uuid, { perPage: 25 });Open channel codes look like BRIVAOP, QRISOP, QRISCOP, …
Channels, fees, instructions
const all = await tripay.getChannels();
const active = await tripay.getActiveChannels();
const fee = await tripay.calculateFee({ code: "BRIVA", amount: 100_000 });
const allFees = await tripay.calculateFee({ amount: 100_000 });
const steps = await tripay.getInstruction({ code: "BRIVA", payCode: "123", amount: 100_000 });E-wallet (production only)
const link = await prod.linkWallet({ mobilePhone: "08123456789" }); // DANA by default
// → send user to link.authorization_url
const info = await prod.getWallet({ mobilePhone: "08123456789" });
await prod.unlinkWallet({ mobilePhone: "08123456789" });Callbacks (verify signature first, always)
TriPay signs the raw JSON body with your private key (HMAC-SHA256) and sends it as X-Callback-Signature with event X-Callback-Event: payment_status.
// Manual (any framework)
import { Tripay } from "tripay-node-sdk";
app.post("/tripay/callback", express.raw({ type: "application/json" }), (req, res) => {
const signature = req.headers["x-callback-signature"] as string;
const event = req.headers["x-callback-event"] as string;
try {
const { data } = tripay.parseCallback((req.body as Buffer).toString("utf8"), signature, event);
if (data.status === "PAID") {
// data.reference, data.merchant_ref, data.amount_received, ...
}
res.json({ success: true });
} catch {
res.status(401).json({ success: false, message: "Invalid signature" });
}
});// Node http helper (a two-arg verify helper `expressCallback(req, opts)` is also exported)
import { callbackHandler } from "tripay-node-sdk";
import http from "node:http";
const handler = callbackHandler({
privateKey: process.env.TRIPAY_PRIVATE_KEY!,
onPayment: async ({ data, isPaid }) => {
if (isPaid) await markInvoicePaid(data.merchant_ref!, data.reference);
},
});
http.createServer(handler).listen(3000);Important: use the raw body string for verification. JSON.stringify(req.body) can reorder keys/whitespace and break the signature — with Express use express.raw(), with Next.js/Hono/Bun read the raw text first.
Respond { "success": true } — otherwise TriPay retries 3× every 2 minutes.
API reference
| Method | Notes |
|---|---|
| new Tripay({ apiKey, privateKey, merchantCode, mode?, timeoutMs? }) | mode: "sandbox" \| "production" (default "sandbox"), timeoutMs default 15000 |
| createTransaction(input) | Signature auto-computed as HMAC(merchantCode + merchantRef + amount); sends application/x-www-form-urlencoded like the official docs |
| getTransaction(reference) | Full detail, doubles as status check |
| checkTransaction(reference) | Lightweight; returns { status, message } |
| listTransactions({ page?, perPage?, sort?, reference?, merchantRef?, method?, status? }) | perPage 1–50 |
| createOpenPayment({ method, merchantRef?, customerName? }) | Production only; signature HMAC(merchantCode + method + merchantRef) |
| getOpenPayment(uuid) / listOpenPaymentTransactions(uuid, filters?) | Production only |
| getChannels() / getActiveChannels() | Includes fees + min/max amounts |
| calculateFee({ amount, code? }) | Omit code for all channels |
| getInstruction({ code, payCode?, amount?, allowHtml? }) | Payment steps per channel |
| linkWallet / unlinkWallet / getWallet | Production only, walletType default "DANA" |
| verifyCallback(rawBody, signature) | boolean, timing-safe compare |
| parseCallback(rawBody, signature, event?) | Throws TripaySignatureError on bad signature/event/JSON |
| Tripay.signCallback(rawBody, privateKey) | Test helper |
| Tripay.callbackResponse(success, message?) | { success: true } shape TriPay expects |
Errors: TripayError (base, has .status/.response), TripayValidationError (local checks, e.g. items total ≠ amount), TripayAuthError (401/invalid key), TripaySignatureError (callback). All exported.
Sandbox vs production
| Feature | Sandbox (api-sandbox) | Production (api) |
|---|---|---|
| Closed transactions | ✅ | ✅ |
| Channels / fees / instructions | ✅ | ✅ |
| Open payment | ❌ (not offered by TriPay) | ✅ |
| E-wallet link/unlink/detail | ❌ | ✅ |
Credentials
Sandbox: Member area → API & Integrasi > Simulator > Merchant > Detail. Production: Merchant > Opsi > Edit. Then activate the channels you use (Simulator → Channel Pembayaran / Merchant → Atur Channel Pembayaran).
TypeScript
Fully typed, strict clean. Helpers: ClosedPaymentMethod, OpenPaymentMethod, TransactionStatus, PaymentChannel, ClosedTransaction, OpenPayment, CallbackPayload, …
License
MIT
