- WooCommerce
- Magento
- Shopify Soon
Pre-built forms (popup or embedded)
Embed our secure, pre-built payment forms directly into your existing checkout page. Perfect for customizing your user flow while we handle PCI compliance.
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_completedevent.
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 Reservepay pre-built form which captures all necessary payment details and creates a
payment_session_idvia theonPaymentSessionReadycallback.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: The Reservepay pre-built form automatically displays the payment status and hands back control to you via the callbacks.
Testing: On your test account, use the test card generator to create a card for the outcome you want to test: approval, decline, or 3D Secure challenge. Use any future expiry date, such as
12/29, and any 3-digit CVV (4 digits for AMEX).
Web integration
Install the SDK
Include the SDK script tag in your HTML file. This loads the reservepay object into your application.
To maintain strict PCI compliance and ensure the integrity of sensitive payment information, our library must be loaded directly from our secure servers via a script tag and must not be bundled or imported via a package manager.
<script src="https://sdk.reservepay.com/js/v1/reservepay.js"></script>
Attach the SDK
Once the SDK script is loaded, you can initialize the payment form using the window.Reservepay.initReservepayFormUI method. This function takes a configuration object.
The required top-level parameters are:
formType: Defines the UI mode. Use"popup"or"embedded".merchantId: Your unique merchant identifier.installationId: The specific installation ID for this integration.formOptions: An object containing configuration specific to the form, such asamountand the necessary event callbacks.
Popup form
The popup opens when the customer selects the trigger element.
<button id="reservepay-popup-trigger">Pay Now</button>
window.Reservepay.initReservepayFormUI({
formType: "popup",
merchantId: "123456789100",
installationId: "ins_123",
formOptions: {
popupTriggerSelector: "#reservepay-popup-trigger",
amount: "100000",
async onPaymentSessionReady(sessionId) {
await sendPaymentSessionToBackend(sessionId)
},
onPaymentSuccess(sessionId) {
console.log("Payment successful", sessionId)
},
onPaymentFailed(sessionId) {
console.log("Payment failed", sessionId)
},
},
})
Embedded form
The embedded form renders inside a container in your checkout.
<div id="reservepay-mount"></div>
window.Reservepay.initReservepayFormUI({
formType: "embedded",
merchantId: "123456789100",
installationId: "ins_123",
formOptions: {
containerSelector: "#reservepay-mount",
amount: "100000",
async onPaymentSessionReady(sessionId) {
await sendPaymentSessionToBackend(sessionId)
},
onPaymentSuccess(sessionId) {
console.log("Payment successful", sessionId)
},
onPaymentFailed(sessionId) {
console.log("Payment failed", sessionId)
},
},
})
sendPaymentSessionToBackend is a placeholder for your frontend-to-backend request. Implement it to send the sessionId to your server, then follow Initiate the payment flow.
Handle callbacks
| Callback | When it runs | What to do |
|---|---|---|
onPaymentSessionReady(sessionId) |
The SDK creates the payment session, before the customer submits payment details. | Send the sessionId to your backend and associate it with the order or cart. |
onPaymentSuccess(sessionId) |
The customer completes the payment flow. | Show a success state or redirect to a confirmation page. |
onPaymentFailed(sessionId) |
The payment fails, is declined, or is canceled. | Show the failure and let the customer retry when appropriate. |
Use frontend callbacks to update the UI. Fulfill the order only after a webhooks or backend status check confirms the payment.
Accept payments with a saved card
Saved cards let returning customers check out with a single CVV entry: collect a reusable token on the first payment, then pass it back to the form on later visits.
Saving a card
Set enableSaveCard: true in formOptions. The SDK shows a Save this card checkbox; when the customer opts in, onPaymentSessionReady receives a second argument: { saveCard: true, savedCardToken }. Send both to your backend and save the card to the customer, either with the payment or on its own. See Saving the card to a contact. After Reservepay saves the card, you can retrieve its opaque token instead of storing it separately.
The SDK acknowledges the session even if
onPaymentSessionReadythrows, so the payment continues. Handle token-storage failures on your backend.
window.Reservepay.initReservepayFormUI({
formType: "embedded",
merchantId: "123456789100",
installationId: "ins_123",
formOptions: {
containerSelector: "#reservepay-mount",
amount: "100000",
enableSaveCard: true,
async onPaymentSessionReady(sessionId, details) {
await sendPaymentSessionToBackend(sessionId)
if (details.saveCard) {
await saveCardForCustomer(details.savedCardToken) // your backend call
}
},
onPaymentSuccess(sessionId) {
console.log("Payment successful", sessionId)
},
onPaymentFailed(sessionId) {
console.log("Payment failed", sessionId)
},
},
})
Reusing a saved card
On later checkouts, retrieve the customer's saved-card tokens from your backend, then pass them through formOptions.savedCardTokens (string or array). The form displays the masked card and asks only for the CVV - with multiple tokens, it shows a card picker and a Use a different card option first. The form sends the CVV to rehydrate-card before it creates the payment session, so you don't call it yourself. See The CVV on saved cards.
If a token in
savedCardTokenscannot be decoded - for example, if it is malformed or stale - it's dropped silently. If none decode, the SDK falls back to the standard new-card form automatically.
window.Reservepay.initReservepayFormUI({
formType: "embedded",
merchantId: "123456789100",
installationId: "ins_123",
formOptions: {
containerSelector: "#reservepay-mount",
amount: "100000",
savedCardTokens: customerSavedCardTokens, // string or string[] from your backend
async onPaymentSessionReady(sessionId) {
await sendPaymentSessionToBackend(sessionId)
},
onPaymentSuccess(sessionId) {
console.log("Payment successful", sessionId)
},
onPaymentFailed(sessionId) {
console.log("Payment failed", sessionId)
},
},
})
Android integration
Install the SDK
Add the SDK to your app's build.gradle:
repositories {
mavenCentral()
}
dependencies {
implementation 'com.reservepay:reservepay-android:1.0.0'
}
Declare ReservepayActivity in AndroidManifest.xml:
<activity
android:name="com.reservepay.ReservepayActivity"
android:exported="false" />
Start a payment
Configure the SDK once, typically in Application.onCreate(), then call startPayment for each transaction.
import com.reservepay.ReservepayCallbacks
import com.reservepay.ReservepaySDK
import com.reservepay.configure
import com.reservepay.startPayment
// Create callback
private val reservepayCallbacks = object : ReservepayCallbacks {
override fun onPaymentSessionReady(sessionId: String, savedCardToken: String?) {
// Send sessionId to your backend. Store savedCardToken when present.
}
override fun onPaymentStatus(status: String) {
}
override fun onPaymentCancelled() {
}
override fun onPaymentError(error: String) {
}
}
// Configure the SDK once, before starting any payment
ReservepaySDK.getInstance().configure(
context = context,
isDebug = true,
merchantId = "123456789100",
installationId = "ins_123",
)
// Start a payment
ReservepaySDK.getInstance().startPayment(
context,
100000L, // THB 1,000.00 in satang
"THB", // Currency
tokens = customerSavedCardTokens, // Saved card tokens to offer for reuse, or null for none
callback = reservepayCallbacks
)
Mobile amounts are integers in the currency's smallest unit. Pass saved-card tokens through tokens, or null when the customer has none. See Mobile callback behavior before continuing to Backend integration.
iOS integration
Install the SDK
In Xcode, choose File > Add Package Dependencies. Enter the Reservepay package URL supplied with your SDK release, select that release's supported version, and add ReservepaySDK to your target.
Start a payment
Configure the SDK once at app launch, then call startPayment for each transaction.
import ReservepaySDK
// Create callback
let callback = ReservepayCallback(
onPaymentSessionReady: { sessionId, savedCardToken in
// Send sessionId to your backend. Store savedCardToken when present.
},
onPaymentCancelled: {
print("Payment cancelled")
// Dismiss the view controller
viewController.dismiss(animated: true)
},
onPaymentStatus: { status in
print("Payment status: \(status)")
},
onPaymentError: { error in
print("Payment error: \(error)")
}
)
// Configure the SDK once, before starting any payment
ReservepaySDK.companion.getInstance().configure(
isDebug: true,
merchantId: "123456789100",
installationId: "ins_123"
)
// Start a payment
ReservepaySDK.companion.getInstance().startPayment(
viewController: viewController,
amount: 100000, // THB 1,000.00 in satang
currency: "THB",
tokens: customerSavedCardTokens, // Saved card tokens to offer for reuse, or nil for none
callback: callback
)
Mobile amounts are integers in the currency's smallest unit. Pass saved-card tokens through tokens, or nil when the customer has none.
Mobile callback behavior
Android and iOS use the same callback roles:
| Callback | What to do |
|---|---|
onPaymentSessionReady |
Send the session ID to your backend. If the saved-card token is present, store it against the customer. |
onPaymentStatus |
Update the UI from the reported status. |
onPaymentError |
Show or log the error. |
onPaymentCancelled |
Dismiss the payment flow or return to checkout. |
Web forms report completion through onPaymentSuccess and onPaymentFailed; mobile uses the status, error, and cancellation callbacks above. Continue with the shared backend steps after onPaymentSessionReady.
Backend integration
These steps are identical regardless of which platform initiated the payment session.
Initiate the payment flow
After your frontend sends the payment_session_id, your backend must call the 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: "1000",
currency: "THB",
payment_session_id: paymentSessionId,
capture: true,
return_url: "https://your-site.com/return-url",
external_id: "order_12345"
})
})
Pass an external_id based on your order or invoice reference. If a request times out and you retry it with the same external_id, the original payment is returned instead of a duplicate, and you can look the payment up later with find-payment. Each payment attempt needs its own reference: if a payment fails or expires and the customer tries again, use a new one, such as order_12345_2. See Calling the API for the full rules.
Saving and reusing cards
To save the customer's card with this payment, add customer_id and save_card to your initiate-payment-flow call. See Saving cards for future use for saving, retrieving, and charging saved cards.
Confirming the payment status
To confirm the payment status from your backend call find-payment using the payment_id returned by the 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_completed event.
Incoming events are signed. Verify the signature against the raw JSON body, before you parse or re-encode it, using the verification key for that endpoint. The header name and algorithm depend on the signature type the endpoint was configured with. See Real-time notifications for the signature types, header names, and verification steps.
Choose the integration method that best fits your needs and start accepting payments seamlessly on the web and in mobile apps.
Redirect customers to our secure, optimized checkout page. Perfect for quick integration with minimal development effort.
Read the guideEmbed our secure, pre-built payment forms directly into your existing checkout page while we handle PCI compliance.
Read the guideBuild your entire checkout experience from the ground up using our powerful APIs and SDK for complete control.
Read the guideNo 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.
