How to use P2P

The short version: verify the slip, withdraw via PromptPay, and get your webhook right

This page is the quick tour, not the full spec. It covers the three things you actually have to build; each section links to the page with the full detail.

1. The flow in four steps

  1. 1

    Get the account ready

    Register the webhook URL and top up THB_P2P credit — fees and central-account transfers are deducted from it.

  2. 2

    Deposit: verify the slip

    The depositor transfers within the window and attaches a slip. Call the slip API to check it, then wait for PAYMENT_PAID.

  3. 3

    Withdrawal: PromptPay only

    Create the withdrawal with receiver_bank "PromptPay". The system splits it into legs and fills them from deposits.

  4. 4

    Settle from the webhook

    Close the order only when the terminal event arrives, and poll the stretches that stay silent.

2. Verify the payment slip

When a depositor attaches a slip, send it to the slip API — this is what decides whether the money counts. Pass the payment uuid and a URL to the slip image; the API reads the slip and matches it against the order.

POSTVerify the payment slip
Endpoint URL
https://crmwpayz.trustsig.xyz/api/slip/process
REQUESTuuid = the payment id of the deposit
POST https://crmwpayz.trustsig.xyz/api/slip/process
Content-Type: application/json
x-api-key: <API_KEY>

{
    "uuid": "61dffa9d-4e2c-4a4a-9d1c-c65f46a3dcfe",
    "slipImageUrl": "https://example.com/slip-image.jpg"
}
  • success: true means the slip passed. A body with errorCode 9008 or 9014 means the slip was already used.
  • Store transRef with a unique index — it is the cheapest guard against the same slip being submitted twice.
  • Do not credit the customer off this response alone. PAYMENT_PAID from the webhook is what actually closes the deposit.
  • A rejected slip does not kill the order — the depositor can attach another one until the window closes.

Process Slip — full reference

Every field, the full response, and the error-code table

3. Withdraw via PromptPay only

P2P withdrawals go out over PromptPay and nothing else. Use the same createRequest/fiat endpoint as a normal THB withdrawal, but set receiver_bank to "PromptPay" and put the phone number or national ID in withdrawal_address.

REQUESTPOST /v1/withdrawal/createRequest/fiat
{
    "amount": 10000,
    "currency": "THB",
    "receiver_bank": "PromptPay",
    "withdrawal_address": "0812345678",
    "receiver_name": "MR. John Snow",
    "order_id": "ACME-W-2026-000456",
    "order_user_reference": "USER900"
}
  • Bank accounts are not accepted on the P2P rail — a request with any other receiver_bank will not go through as P2P.
  • PromptPay is required because the depositor has to scan and pay within the window, and the destination can be matched more precisely.
  • Keep the returned idGET /v1/withdrawal/detail/:id is the only way to read the legs back.
  • A withdrawal that is still short after 30 minutes is topped up from the central account — and that amount comes out of your THB_P2P credit.

4. Set up the webhook

Webhooks are how you find out anything finished. Register a URL, turn on HMAC, and verify the signature against JSON.stringify(body.data) — not the raw body. Answer 2xx within 10 seconds and do the real work asynchronously.

Two traps worth knowing up front: with no webhook URL registered you get no events and no error either, and changing the URL blocks new withdrawals for 24 hours.