Payment requests
Create a dynamic VietQR per order. When the customer pays, Hensa matches it automatically and sends payment_request.paid.
Create a payment request
POST/v1/payment_requests
Creates a dynamic VietQR with its own amount and referenceCode. Render qrPayload as a QR code for the customer – or redirect them to the hosted Hensa Pay page at payUrl. Expires after 15 minutes.
Request body
amountintegerrequiredAmount to collect, integer VND, minimum 1,000.
externalOrderRefstringYour own order id (max 64 chars). Returned as-is in webhooks.
Returns
201 · PaymentRequest
Possible errors
validation_error400 Invalid request data – seemessage.idempotency_conflict409 SameIdempotency-Keywith a different body.no_active_bank_account422 The store has no active bank account.merchant_name_required422 The store has no name yet – set it in the Dashboard before creating QR codes (live mode only).plan_limit422 The current plan's limit is reached.
curl -X POST https://api.hensa.example/v1/payment_requests \ -H "Authorization: Bearer $HENSA_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{"amount":150000,"externalOrderRef":"DH-1024"}'const res = await fetch('https://api.hensa.example/v1/payment_requests', { method: 'POST', headers: { Authorization: `Bearer ${process.env.HENSA_API_KEY}`, 'Idempotency-Key': crypto.randomUUID(), 'Content-Type': 'application/json' }, body: JSON.stringify({ "amount": 150000, "externalOrderRef": "DH-1024" })})const data = await res.json()<?php$ch = curl_init('https://api.hensa.example/v1/payment_requests');curl_setopt_array($ch, [ CURLOPT_CUSTOMREQUEST => 'POST', CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer ' . getenv('HENSA_API_KEY'), 'Idempotency-Key: ' . bin2hex(random_bytes(16)), 'Content-Type: application/json', ], CURLOPT_POSTFIELDS => '{"amount":150000,"externalOrderRef":"DH-1024"}',]);$data = json_decode(curl_exec($ch), true);import os, uuid, requests
res = requests.post( "https://api.hensa.example/v1/payment_requests", headers={ "Authorization": f"Bearer {os.environ['HENSA_API_KEY']}", "Idempotency-Key": str(uuid.uuid4()), }, json={ "amount": 150000, "externalOrderRef": "DH-1024" }, timeout=10,)res.raise_for_status(){ "id": "prq_01JD7Q8M4K2X9V3T6B5N0R1W8Y", "amount": 150000, "amountReceived": 0, "currency": "VND", "referenceCode": "LK7F3K9", "status": "pending", "qrPayload": "00020101021238…", "receiver": { "name": "CA PHE MOC", "bankName": "MB Bank", "accountMasked": "•••• 6789" }, "externalOrderRef": "DH-1024", "payUrl": "https://pay.hensa.vn/r/pt_7Hq2mX9vKd4RtY1nB8cZ0w", "expiresAt": "2026-11-03T08:15:00Z", "createdAt": "2026-11-03T08:00:00Z"}/v1/payment_requestsRetrieve a payment request
GET/v1/payment_requests/{id}
Use it to re-check the status if a webhook has not arrived.
Parameters
idstring · pathrequiredPayment request ID.
Returns
200 · PaymentRequest
Possible errors
not_found404 Not found (or not owned by this key's store).
curl https://api.hensa.example/v1/payment_requests/prq_01JD7Q8M4K2X9V3T6B5N0R1W8Y \ -H "Authorization: Bearer $HENSA_API_KEY"const res = await fetch('https://api.hensa.example/v1/payment_requests/prq_01JD7Q8M4K2X9V3T6B5N0R1W8Y', { method: 'GET', headers: { Authorization: `Bearer ${process.env.HENSA_API_KEY}`, },})const data = await res.json()<?php$ch = curl_init('https://api.hensa.example/v1/payment_requests/prq_01JD7Q8M4K2X9V3T6B5N0R1W8Y');curl_setopt_array($ch, [ CURLOPT_CUSTOMREQUEST => 'GET', CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer ' . getenv('HENSA_API_KEY'), ],]);$data = json_decode(curl_exec($ch), true);import os, requests
res = requests.get( "https://api.hensa.example/v1/payment_requests/prq_01JD7Q8M4K2X9V3T6B5N0R1W8Y", headers={ "Authorization": f"Bearer {os.environ['HENSA_API_KEY']}", }, timeout=10,)res.raise_for_status(){ "id": "prq_01JD7Q8M4K2X9V3T6B5N0R1W8Y", "amount": 150000, "amountReceived": 0, "currency": "VND", "referenceCode": "LK7F3K9", "status": "pending", "qrPayload": "00020101021238…", "receiver": { "name": "CA PHE MOC", "bankName": "MB Bank", "accountMasked": "•••• 6789" }, "externalOrderRef": "DH-1024", "payUrl": "https://pay.hensa.vn/r/pt_7Hq2mX9vKd4RtY1nB8cZ0w", "expiresAt": "2026-11-03T08:15:00Z", "createdAt": "2026-11-03T08:00:00Z"}/v1/payment_requests/List payment requests
GET/v1/payment_requests
Newest first. Cursor-paginated.
Parameters
statusstring · queryFilter by status.
limitinteger · queryItems per page (1–100, default 25).
cursorstring · queryThe
nextCursorvalue from the previous page.
Returns
200 · A page of PaymentRequest plus nextCursor for the next page.
Possible errors
validation_error400 Invalid request data – seemessage.
curl https://api.hensa.example/v1/payment_requests?limit=25 \ -H "Authorization: Bearer $HENSA_API_KEY"const res = await fetch('https://api.hensa.example/v1/payment_requests?limit=25', { method: 'GET', headers: { Authorization: `Bearer ${process.env.HENSA_API_KEY}`, },})const data = await res.json()<?php$ch = curl_init('https://api.hensa.example/v1/payment_requests?limit=25');curl_setopt_array($ch, [ CURLOPT_CUSTOMREQUEST => 'GET', CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer ' . getenv('HENSA_API_KEY'), ],]);$data = json_decode(curl_exec($ch), true);import os, requests
res = requests.get( "https://api.hensa.example/v1/payment_requests?limit=25", headers={ "Authorization": f"Bearer {os.environ['HENSA_API_KEY']}", }, timeout=10,)res.raise_for_status(){ "data": [ { "id": "prq_01JD7Q8M4K2X9V3T6B5N0R1W8Y", "amount": 150000, "amountReceived": 0, "currency": "VND", "referenceCode": "LK7F3K9", "status": "pending", "qrPayload": "00020101021238…", "receiver": { "name": "CA PHE MOC", "bankName": "MB Bank", "accountMasked": "•••• 6789" }, "externalOrderRef": "DH-1024", "payUrl": "https://pay.hensa.vn/r/pt_7Hq2mX9vKd4RtY1nB8cZ0w", "expiresAt": "2026-11-03T08:15:00Z", "createdAt": "2026-11-03T08:00:00Z" } ], "nextCursor": "…"}/v1/payment_requestsObjects
PaymentRequest
idstringrequiredPayment request ID.
amountintegerrequiredAmount to collect, integer VND.
amountReceivedintegerrequiredTotal of the transfers matched to this request. Less than
amountwhilepartially_paid; can exceed it on overpayment.currency"VND"requiredAlways
VND.referenceCodestringrequiredReference the customer puts in the transfer description – used for auto-matching.
statusstringrequiredpending·paidin full ·partially_paidunderpaid ·expired·cancelled.qrPayloadstringrequiredVietQR (EMVCo) string – render it as a QR code.
receiverobjectrequiredReceiver shown to the customer before paying.
receiver.namestringrequiredreceiver.bankNamestringrequiredreceiver.accountMaskedstringrequiredexternalOrderRefstring | nullrequiredYour own order id (if sent at creation).
payUrlstring | nullrequiredThe hosted Hensa Pay page for this request (QR, open-banking-app buttons, receipt). Send it to the payer or redirect them here instead of rendering the QR yourself. The URL carries a secret – share it only with the payer.
nullonce the page is revoked.expiresAtstringrequiredExpires 15 minutes after creation.
createdAtstringrequiredCreation time.
{ "id": "prq_01JD7Q8M4K2X9V3T6B5N0R1W8Y", "amount": 150000, "amountReceived": 0, "currency": "VND", "referenceCode": "LK7F3K9", "status": "pending", "qrPayload": "00020101021238…", "receiver": { "name": "CA PHE MOC", "bankName": "MB Bank", "accountMasked": "•••• 6789" }, "externalOrderRef": "DH-1024", "payUrl": "https://pay.hensa.vn/r/pt_7Hq2mX9vKd4RtY1nB8cZ0w", "expiresAt": "2026-11-03T08:15:00Z", "createdAt": "2026-11-03T08:00:00Z"}