Custom integration
Build your entire checkout experience from the ground up using our powerful APIs and SDK. Perfect for complete control over the whole user experience.
Account setup and configuration
Before you can integrate, you must set up your merchant environment. You’ll need the following:
Create a merchant account: Sign up and gain access to your merchant dashboard
Create an SDK installation: In your dashboard, under developer settings, create a new SDK installation for each website or mobile application you plan to deploy on. This process involves:
- Choosing your platform (e.g., Web, iOS, Android).
- Providing application details like your website domain or app bundle ID.
- Selecting payment methods you want to offer on that platform (e.g., Card, PromptPay).
- Configuring customer data to require (e.g., email address, mobile number).
- Choosing your platform (e.g., Web, iOS, Android).
Create a new API key: In your dashboard, under developer settings, create a new API key. Keep your API key secret. Your API key is a secret credential. It must only be used on your backend server. Never expose this key in your frontend (client-side) JavaScript, mobile app code, or any public repository.
Connect a webhook endpoint (optional): In your dashboard, under developer settings, create a new webhook endpoint and listen to the
payment_completeevent.
Integration flow overview
A complete transaction involves both your frontend (handling user interaction) and your backend (handling secure API calls).
On the frontend (aka client-side): Your customer interacts with your UI. You use the Reservepay SDK to capture all payment details and create a
payment_session_id.On the backend (aka server-side): Your frontend sends the
payment_session_idto your server. Your server then securely uses its secret API key to call the Reservepay Merchant API and initiate the actual payment flow.Back on the frontend: Your frontend uses the SDK to poll for the next action (like redirecting the user to another page or displaying a QR code) and, ultimately, the final payment status
About payment session IDs
The payment_session_id is a unique identifier that represents a single payment attempt from start to finish. It is the essential "link" between your customer's actions on the frontend (using the SDK) with the secure payment initiation request made by your backend (using your API key). You can think of it as a unique tracking number for the entire transaction lifecycle.
Integration steps
Install the SDK
Include the SDK script tag in your HTML file. This loads the reservepay object into your application.
Example
<script src="https://sdk.reservepay.com/js/v1/reservepay.js"></script>
// Initialize the sdk
//
// Note that the Reservepay sdk is available in window.Reservepay
const reservepay = window.Reservepay.initReservepay({
merchantId: "123456789100",
installationId: "ins_123",
})
Tokenize card details (card payments only)
If the user selects "Card" as their payment method, you must first tokenize their card information. Call /sdk/tokenize-card with the user's card details which returns a token.
Create a payment session
Next, create a payment session. This tells our system you're about to start a payment. Call /sdk/select-payment-method with the amount, currency, payment method, and any required customer info. If the payment_method is CARD, you must include the token from the previous step. This will return a payment_session_id. Send this ID to your backend.
Example
const paymentSessionId = await reservepay.endpoints.sdk.selectPaymentMethod({
amount: "100000",
currency: "THB",
payment_method: "PROMPTPAY",
})
Initiate the payment flow
After your frontend sends the payment_session_id, your backend must call the /merchants/initiate-payment-flow endpoint which will return a payment_id. This call must be authenticated using your secret API key as a bearer token in the Authorization header. This "authorizes" the payment session and kicks off the payment flow.
Example
// IMPORTANT: This must be called from your server-side code with your API key
// Never expose your API key (reservepay_xxx) in client-side code
// Make a request from your backend to ReservePay API:
//
await fetch('https://api.reservepay.com/merchants/initiate-payment-flow', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY', // Use your API key here (server-side only!)
'Api-Version': '2025-04-01',
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
amount: "100000",
currency: "THB",
payment_session_id: paymentSessionId,
capture: true,
return_url: "https://your-site.com/return-url"
})
})
Handle next actions
After your backend has initiated the payment, your frontend must poll to discover what to do next. Call /sdk/discover-next-action in a loop (or use the polling helper provided by the SDK, see example below), passing your merchant_id, installation_id, and payment_session_id and handle the action returned:
WAIT: The payment is processing. Wait 1 second and call/sdk/discover-next-actionagain.REDIRECT: The user needs to be redirected (e.g., for 3D Secure or to a mobile banking application). Call/sdk/get-redirect-urlto get the destination URL and redirect the user to this URL. After the user returns to your site, resume polling on the frontend or fetch the payment details on your backend by using the/merchants/find-paymentendpoint.DISPLAY_QR: The user needs to be shown a QR code. Call/sdk/retrieve-qr-datato get the base64-encoded QR data. Base64-decode the response and render it as a QR code in your UI. Continue polling/sdk/discover-next-actionwhile the QR code is displayed.CHECK_STATUS: The payment flow is complete. The loop can stop. Proceed to the next step.
Example
// Discover the next action (polling until DISPLAY_QR)
const discoverNextActionPolling =
reservepay.endpoints.sdk.createDiscoverNextActionPolling()
const action = await discoverNextActionPolling.execute({
payment_session_id: paymentSessionId,
pollingUntilAction: "DISPLAY_QR"
})
// Then retrieve the QR data
const qrData = await reservepay.endpoints.sdk.retrieveQrData({
payment_session_id: paymentSessionId,
})
// Display QR code to user (qrData is base64 encoded)
const qrCodeString = atob(qrData)
// And finally render the QR code in your UI
Check the status
Once /sdk/discover-next-action returns CHECK_STATUS, make one final call to get the definitive outcome. Call /sdk/check-status using the same merchant_id, installation_id, and payment_session_id. This will return a final status (e.g., SUCCESSFUL, FAILED). Update your UI accordingly
Example
const checkStatusPolling = reservepay.endpoints.sdk.createCheckStatusPolling()
const status = await checkStatusPolling.execute({
payment_session_id: paymentSessionId,
})
if (status === "SUCCESSFUL") {
// Show success message with payment details
console.log("Payment successful")
}
if (status === "FAILED") {
// Handle payment failure
console.log("Payment failed")
}
Confirming the payment status
To confirm the payment status from your backend call /merchants/find-payment using the payment_id returned by the /merchants/initiate-payment-flow endpoint. This returns the full payment object, including the status field.
Webhooks (optional)
For a more robust integration, we highly recommend using webhooks. Instead of relying only on polling, your server can receive asynchronous notifications. To achieve the same result as the previous step, listen for the payment_complete event.
To verify the signature of incoming events our requests include an X-Webhook-Signature header. You must verify this signature using your webhook endpoint verify key (available in your dashboard) and a libsodium compatible library. This verification is critical to make sure that the request genuinely came from Reservepay and was not forged.
Polling considerations
You must cancel polling to stop background requests when your UI closes. Without abort(), polling continues making API calls every few seconds for up to 10 minutes (hundreds of requests!)
You should call abort() when:
- Component unmounts (payment dialog closes)
- User clicks cancel or navigates away
- Payment completes (success or failure)
This prevents memory leaks, unnecessary server load, and resource exhaustion
Example
discoverNextActionPolling.abort()
checkStatusPolling.abort()
No account yet?
Start integrating all these amazing features into your app or website by creating your own merchant account today. It's free to sign up and only takes a few minutes to get started.
