Documentation
Get from zero to your first payment.
This guide walks you through a complete integration: authenticate, create a payment, send your customer to checkout and confirm the result with a webhook. Most developers finish it in an afternoon.
◷ Sandbox opens to waitlist developers first. Live keys follow once TujuPay is licensed.
1. Before you start
You need three things. Everything in this guide runs in the sandbox, so no real money moves.
- A TujuPay account. Sign up and you get test keys straight away, no approval needed.
- A server that can make HTTPS requests. Any language works; examples here use curl and Node.js.
- A public HTTPS URL for webhooks. For local development, a tunnel such as ngrok is fine.
2. Authenticate your requests
Every request uses your secret key with HTTP Basic auth: the key is the username and the password is empty. Test keys start with sk_test_ and live keys with sk_live_. Keep secret keys on your server only; never put them in a mobile app or browser code.
- sk_test_… works only in the sandbox and never moves real money.
- sk_live_… is issued after your business is verified and TujuPay is licensed.
- Rotate a key at any time from the dashboard. The old key stops working after 24 hours.
curl https://api.tujupay.com/v1/payments \ -u sk_test_51HxQ2...:
3. Create your first payment
Create a payment on your server when the customer is ready to pay. Send the amount in sen (RM 189.00 is 18900), the methods you accept and your own order reference. Add an Idempotency-Key so a retried request never creates a second payment.
- amount is an integer in sen. We never use decimals for money.
- methods can include fpx and duitnow_qr. Leave it out to offer every method you have switched on.
- reference is yours: use your order ID so you can match payments in your system.
const res = await fetch("https://api.tujupay.com/v1/payments", {
method: "POST",
headers: {
Authorization: "Basic " + btoa(process.env.TUJUPAY_SECRET + ":"),
"Content-Type": "application/json",
"Idempotency-Key": "order-2214",
},
body: JSON.stringify({
amount: 18900, // RM 189.00 in sen
currency: "myr",
methods: ["fpx", "duitnow_qr"],
reference: "ORDER-2214",
return_url: "https://yourshop.my/orders/2214",
}),
});
const payment = await res.json();
// payment.checkout_url → send the customer here4. Send the customer to checkout
The response includes a checkout_url. Redirect the customer there. They pick their bank or scan the QR, approve in their banking app and come back to the return_url you set.
- Checkout works on any phone and shows your logo and order details.
- When the customer returns, show a 'processing' message until your webhook confirms the result.
- Do not mark an order as paid just because the customer came back. Wait for the webhook.
// Express example
app.post("/checkout", async (req, res) => {
const payment = await createPayment(req.body.orderId);
res.redirect(303, payment.checkout_url);
});5. Confirm the result with a webhook
When the payment succeeds or fails, we send a payment.succeeded or payment.failed event to your webhook URL. Verify the signature, then fulfil the order. The Webhooks page explains signatures, retries and every event in detail.
- Respond with any 2xx status within 10 seconds, then do slow work in the background.
- Handle each event once: store the event ID and ignore repeats.
6. Go live
When your integration works in the sandbox, these are the steps to take real payments. Live keys become available once TujuPay is licensed and your business is verified.
- Complete business verification in the dashboard: SSM documents, directors and payout bank account.
- Swap sk_test_ for sk_live_ and update your webhook URL to production.
- Make one small real payment and refund it, to check the whole flow end to end.
- Switch on payout alerts so you know when money reaches your bank.