## 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).

* **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_complete` event.

## Integration flow overview

A complete transaction involves both your frontend (handling user interaction) and your backend (handling secure API calls).

1. 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_id` via the `onPaymentSessionCreated` callback.

2. On the backend (aka server-side): Your frontend sends the `payment_session_id` to your server. Your server then securely uses its secret API key to call the Reservepay Merchant API and initiate the actual payment flow.

3. Back on the frontend: The Reservepay pre-built form automatically displays the payment status and hands back control to you via the `onComplete` callback.

### 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

#### Example (JS)

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.

```html
<script src="https://sdk.reservepay.com/js/v1/reservepay.js"></script>
```

```ts
// Initialize the sdk
//
// Note that the Reservepay sdk is available in window.Reservepay
const reservepay = window.Reservepay.initReservepay({
  merchantId: "123456789100",
  installationId: "ins_123",
})
```

#### Example (Android)

 Add the following to your project's `build.gradle`:

```
repositories {
    mavenCentral()
}

dependencies {
    implementation 'com.reservepay:reservepay-android:1.0.0'
}
```

 

Add ReservepayActivity into `AndroidManifest.xml`

```xml
<activity
    android:name="com.reservepay.ReservepayActivity"
    android:exported="false" />
```

```kotlin
import com.reservepay.ReservepayCallbacks
import com.reservepay.ReservepaySDKHelper

// Create callback
private val reservepayCallbacks = object : ReservepayCallbacks {
    override fun onPaymentCancelled() {
    }
    override fun onPaymentError(error: String) {
    }
    override fun onPaymentSessionReady(sessionId: String) {
    }
    override fun onPaymentStatus(status: String) {
    }
}

// Initialize the sdk
ReservepaySDK.getInstance().startPayment(
    context,
    "123456789100", // Merchant Id
    "ins_123", // Installation Id
    "1500.50", // Amount
    "THB", // Currency
    reservepayCallbacks // Callbacks
)
```

#### Example (iOS)

Using Swift Package Manager

1. In Xcode, go to File \> Add Packages...  
2. Enter the repository URL  
3. Select the version you want to use  
4. Click Add Package

```swift
import ReservepaySDK

// Create callback
let callback = ReservepayCallback(
    onPaymentSessionReady: { sessionId in
        print("Payment session ready with ID: \(sessionId)")
    },
    onPaymentCancelled: {
        print("Payment cancelled")
        // Dismiss the view controller
        viewController.dismiss(animated: true)
    }
)
        
// Initialize the SDK
ReservepaySDKHelper().getInstance().startPayment(
    viewController: viewController,
    merchantId: "123456789100",
    installationId: "ins_123",
    amount: "1000.25",
    currency: "THB",
    callback: callback
)
```

### 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.

> **Testing your integration:** Use the [test card generator](/tools/test-card-generator) to create Luhn-compliant test card numbers to enter into the payment form.

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 as `amount` and the necessary event callbacks.

#### Option A: Popup Form

The **popup** mode displays the payment form in a modal window that opens when a user clicks a trigger element (like a button).

**Step 1: Create the HTML Trigger Element** Add an element, such as a `<button>`, with a unique ID or class to your HTML.

```html
<button id="reservepay-popup-trigger">Pay Now</button>
```

**Step 2: Initialize the SDK for Popup Mode** In your JavaScript, call `initReservepayFormUI` with `formType: "popup"` and provide the CSS selector for your trigger element in `formOptions.popupTriggerSelector`.

```ts
window.Reservepay.initReservepayFormUI({
  formType: "popup",
  merchantId: "123456789100",
  installationId: "ins_123",
  formOptions: {
    popupTriggerSelector: "#reservepay-popup-trigger", // Points to your button
    amount: "100000",
    // ... event handlers (see section 3)
  },
});
```

#### Option B: Embedded Form

The **embedded** mode renders the payment form directly inside a container element on your page. This is useful for seamless integration within your checkout flow.

**Step 1: Create the HTML Container Element** Add a container element, such as a `<div>`, with a unique ID or class where you want the form to appear.

```html
<div id="reservepay-mount"></div>
```

**Step 2: Initialize the SDK for Embedded Mode** In your JavaScript, call `initReservepayFormUI` with `formType: "embedded"` and provide the CSS selector for your container element in `formOptions.containerSelector`.

```ts
window.Reservepay.initReservepayFormUI({
  formType: "embedded",
  merchantId: "123456789100",
  installationId: "ins_123",
  formOptions: {
    containerSelector: "#reservepay-mount", // Points to your div
    amount: "100000",
    // ... event handlers (see section 3)
  },
});
```

#### Handle payment callbacks

The `formOptions` object must include callback functions to handle the different stages of the payment process. These callbacks receive the sessionId as an argument.

Handling the `payment_session_id`

* `onPaymentSessionReady(sessionId)`  
  * **When it fires:** As soon as the SDK successfully initializes and creates a payment session, *before* the user has submitted any payment details.  
  * **What to do:** This is the most critical callback for your backend. You **must** send this `sessionId` to your server and associate it with the user's order or cart in your database. This allows you to track the payment and verify its status later.

Handling completion status

* `onPaymentSuccess(sessionId)`
    * **When it fires:** After the user successfully completes the payment flow.
    * **What to do:** This is the primary way to update your frontend UI. You should:
        1. Show a success message to the user.
        2. Redirect them to a confirmation page.
        3. Trigger any frontend-specific success logic.
    * **Note:** While this confirms a successful UI flow, final order fulfillment should always be triggered by a secure, server-to-server confirmation (like a webhook) or by verifying the status of the `sessionId` you saved from `onPaymentSessionReady`.
* `onPaymentFailed(sessionId)`
    * **When it fires:** If the payment is declined, encounters an error, or is canceled by the user.
    * **What to do:**
        1. Show an appropriate error message to the user.
        2. Allow the user to try again (if applicable).

#### Full code examples

##### Popup example

```ts
// Create button element
<button id="reservepay-popup-trigger">Open</button>

// Attach popup to button
window.Reservepay.initReservepayFormUI({
  formType: "popup",
  merchantId: "123456789100",
  installationId: "ins_123",
  formOptions: {
    popupTriggerSelector: "#reservepay-popup-trigger", // Your element selector where you want to open the popup, this could be a button, etc.
    amount: "1234",
    onPaymentSessionReady(sessionId) {
      console.log("Payment session ready", sessionId)
    },
    onPaymentSuccess(sessionId) {
      console.log("Payment successful", sessionId)
    },
    onPaymentFailed(sessionId) {
      console.log("Payment failed", sessionId)
    },
  },
})
```

##### Embedded example

```ts
// Create div element
<div id="reservepay-mount"></div>

// Attach embedded to div
window.Reservepay.initReservepayFormUI({
  formType: "embedded",
  merchantId: "123456789100",
  installationId: "ins_123",
  formOptions: {
    containerSelector: "#reservepay-mount", // Your elementselector where you want to attach the embedded this could be a div.
    amount: "1234",
    onPaymentSessionReady(sessionId) {
      console.log("Payment session ready", sessionId)
    },
    onPaymentSuccess(sessionId) {
      console.log("Payment successful", sessionId)
    },
    onPaymentFailed(sessionId) {
      console.log("Payment failed", sessionId)
    },
  },
})
```

### 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

```ts
// 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"
  })
})
```

### 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.
