One2Pays

Create Payment

Creates a new payment transaction. The payment will be processed asynchronously, and you'll receive a nextAction in the response indicating what action to take next.

Endpoint

POST /api/v1/payments

Authentication

This endpoint requires HMAC Integration Authentication.

Request Parameters

ParameterTypeRequiredDescription
amountstringYesAmount as decimal string (e.g., "1000.00" = 10.00 THB)
currencystringNoCurrency code. Defaults to "THB"
referenceIdstringYesYour unique reference ID (alphanumeric, underscores, hyphens, dots, 1-255 chars)
paymentMethodstringYesPayment method: "promptpay" or "bank_transfer"
responseModestringNoInstruction fields to return: "both" (default), "payment_link", or "qr_code"
customerBankAccountNamestringYesCustomer's bank account name
customerBankAccountNumberstringYesCustomer's bank account number
customerBankCodestringYes3-digit Thai bank code (e.g., "004" for KBANK)
descriptionstringNoPayment description (max 255 characters)
merchantReferencestringNoYour invoice, order, or internal transaction reference
invoiceNostringNoAlias for merchantReference if your system already uses this field name
metadataobjectNoAdditional metadata (key-value pairs)
idempotencyKeystringNoUnique key to prevent duplicate payments (recommended)
expiresInMinutesnumberNoPayment expiration time in minutes (5-1440, default based on payment method)

Integration Context

Your merchantId and integrationId are automatically provided from your integration credentials. You don't need to include them in the request.

Example Request

import crypto from 'crypto';

const API_KEY = process.env.PAYMENT_API_KEY!;
const API_SECRET = process.env.PAYMENT_API_SECRET!;

async function createPayment() {
  const method = 'POST';
  const path = '/api/v1/payments';
  const body = JSON.stringify({
    amount: '1000.00',
    currency: 'THB',
    referenceId: `order-${Date.now()}`,
    paymentMethod: 'promptpay',
    // Return a payment URL without QR code data.
    responseMode: 'payment_link',
    customerBankAccountName: 'John Doe',
    customerBankAccountNumber: '1234567890',
    customerBankCode: '004',
    description: 'Payment for order #12345',
    merchantReference: 'order-12345',
    metadata: {
      order_id: '12345',
    },
    idempotencyKey: `order-12345-${Date.now()}`,
  });

  const timestamp = Date.now().toString();
  const message = `${timestamp}.${body}`;
  const signature = crypto
    .createHmac('sha256', API_SECRET)
    .update(message)
    .digest('hex');

  const response = await fetch(`https://api.example.com${path}`, {
    method,
    headers: {
      'X-API-Key': API_KEY,
      'X-Timestamp': timestamp,
      'X-Signature': `sha256=${signature}`,
      'Content-Type': 'application/json',
    },
    body,
  });

  const result = await response.json();

  if (result.success) {
    console.log('Payment created:', result.data.id);
    console.log('Status:', result.data.status);
    console.log('Next action:', result.data.nextAction);

    // Handle nextAction
    if (result.data.nextAction?.type === 'use_payment_app') {
      // Redirect customer to payment app
      window.location.href = result.data.nextAction.paymentAppUrl;
    } else if (result.data.nextAction?.type === 'redirect') {
      // Redirect to custom URL
      window.location.href = result.data.nextAction.redirectUrl;
    }
  } else {
    console.error('Error:', result.error);
  }
}

createPayment();

Response

Response modes

responseMode only changes the payment instructions inside nextAction. All other payment fields remain in the response. It does not change provider processing or the QR data stored by BroPay.

ValuenextAction projection
bothDefault, backward-compatible response. Returns paymentAppUrl and QR data when available.
payment_linkReturns nextAction.paymentAppUrl without nextAction.qrCode. QR copies are also omitted from response metadata. Use this when your system only redirects to a URL.
qr_codeReturns only nextAction.qrCode when QR generation has completed.

To receive a payment link without QR code data, include this field in the create request:

{
  "responseMode": "payment_link"
}

The returned payment object will contain:

{
  "nextAction": {
    "type": "use_payment_app",
    "paymentAppUrl": "https://pay.example.com/docs/pay/secure-token-here"
  }
}

For qr_code, QR generation is asynchronous, so the initial create response can contain nextAction: null. Retrieve the payment with GET /api/v1/payments/{id}?responseMode=qr_code after it reaches processing.

Omitting responseMode is identical to sending "both", so existing integrations are unaffected.

Success Response (201 Created)

{
  "success": true,
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "amount": "1000.00",
    "currency": "THB",
    "status": "processing",
    "paymentMethod": "promptpay",
    "referenceId": "order-12345",
    "description": "Payment for order #12345",
    "metadata": {
      "order_id": "12345"
    },
    "clientSecret": null,
    "nextAction": {
      "type": "use_payment_app",
      "paymentAppUrl": "https://pay.example.com/docs/pay/secure-token-here"
    },
    "confirmedAt": null,
    "capturedAt": null,
    "canceledAt": null,
    "expiresAt": "2024-01-01T01:00:00.000Z",
    "createdAt": "2024-01-01T00:00:00.000Z",
    "updatedAt": "2024-01-01T00:00:00.000Z"
  }
}

Error Response (400 Bad Request)

{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "amount must be a valid decimal number",
    "metadata": {
      "field": "amount"
    }
  }
}

Next Action Types

After creating a payment, the response includes a nextAction field that tells you what to do next:

use_payment_app

Redirect the customer to One2Pays's payment app to complete the payment:

{
  "type": "use_payment_app",
  "paymentAppUrl": "https://pay.example.com/docs/pay/secure-token-here"
}

Action: Redirect the customer to paymentAppUrl.

redirect

Redirect to a custom URL (if your integration has a custom redirect URL configured):

{
  "type": "redirect",
  "redirectUrl": "https://your-app.com/payment/{paymentId}"
}

Action: Redirect the customer to redirectUrl.

display_bank_transfer_instructions

Display bank transfer instructions to the customer:

{
  "type": "display_bank_transfer_instructions",
  "bankTransferInstructions": {
    "bankName": "Kasikorn Bank",
    "accountNumber": "1234567890",
    "accountName": "Example Company Ltd.",
    "amount": "1000.00",
    "reference": "ORDER-12345"
  }
}

Action: Display the bank transfer instructions to the customer.

Payment Creation Flow

Payment Status

After creation, payments typically start in one of these statuses:

  • processing - Payment is being processed
  • requires_payment_method - Payment method needs to be attached
  • requires_confirmation - Payment needs to be confirmed
  • requires_action - Additional action required from customer

Order References and Active Payment Instructions

Use a unique referenceId for every payment create request. For each new customer deposit or order, also send a unique business reference in merchantReference or invoiceNo, and keep that value in your system for reconciliation.

Do not rely only on free-form metadata when the value is your primary deposit, invoice, or order reference.

If a customer starts another PromptPay payment while a previous payment instruction is still valid, the API may return the existing valid payment instruction instead of creating a second payment. Always store the returned payment id, referenceId, and nextAction, and use webhooks to reconcile the final payment status.

Idempotency

Use Idempotency Keys

Always include an idempotencyKey in your requests to prevent duplicate payments if you need to retry a request.

If you make the same request twice with the same idempotencyKey, you'll receive the same payment object. This is useful for handling network errors and retries.

For a retry of the same create request, reuse the same idempotencyKey. For a new deposit or order, generate a new idempotencyKey.

If you don't provide an idempotencyKey, the system will auto-generate one, but you won't be able to retry safely.

Error Codes

CodeDescription
VALIDATION_ERRORInvalid request parameters
INVALID_AMOUNTAmount must be a valid decimal number
INVALID_CURRENCYCurrency must be 'THB'
INVALID_REFERENCE_IDReference ID format is invalid
INVALID_BANK_CODEBank code must be a valid 3-digit Thai bank code
DUPLICATE_REFERENCE_IDReference ID already exists for this merchant
INVALID_CREDENTIALSAuthentication failed

See Also

On this page