{
  "info": {
    "_postman_id": "b3e1d0a7-2c4f-4c9a-9f2e-one2p-merchant-api",
    "name": "One2P Merchant API (auto-sign)",
    "description": "Postman collection for the One2P (P2P) Merchant API — with automatic MD5 request signing.\n\n## Setup\n1. Set the collection variables `apiKey`, `secretKey` (and pick the right `key` per request body — DEPOSIT key for deposit-side, WITHDRAW key for withdraw-side, either for balance/lists).\n2. `baseUrl` defaults to the dev gateway.\n3. Fill in the request body fields and hit **Send** — the signature is computed automatically by each request's pre-request script.\n\n## How auto-signing works\nEvery signed endpoint has a pre-request script that:\n- Reads the current request body JSON.\n- Joins the documented field values in the exact per-endpoint order, appending `secretKey`, colon-separated: `[...values, secretKey].join(':')`.\n- Computes `CryptoJS.MD5(input).toString()` (lowercase hex — CryptoJS is built into Postman).\n- Writes it back into the body's `signature` field and re-serialises the body.\n- Also sets the `X-API-KEY` header from the `apiKey` collection variable.\n\nSo you never type a signature by hand.\n\n## Signature field order per endpoint\n| Endpoint | Signature input (before md5) |\n|---|---|\n| POST /v1/p2p/deposit | key:amount:fromAccount:fromBankCode:callbackUrl:secretKey |\n| POST /v1/p2p/upload-slip | key:ref:secretKey |\n| POST /v1/p2p/cancel | key:ref:secretKey |\n| POST /v1/p2p/withdraw | key:amount:toAccount:toBankCode:callbackUrl:secretKey |\n| POST /v1/p2p/withdraw-add-timeout | key:ref:secretKey |\n| POST /v1/p2p/balance | key:secretKey |\n| POST /v1/p2p/deposit-list | key:secretKey |\n| POST /v1/p2p/withdraw-list | key:secretKey |\n\nPublic slip-page endpoints (`/v1/p2p-public/*`) use no API key and no signature — only the `(ref, token)` pair carried in the URL.",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "variable": [
    {
      "key": "baseUrl",
      "value": "https://gateway-oneday-api-dev.onedaypayment.com",
      "type": "string"
    },
    {
      "key": "apiKey",
      "value": "",
      "type": "string",
      "description": "Your merchant X-API-KEY header. The server derives customerId from it."
    },
    {
      "key": "secretKey",
      "value": "",
      "type": "string",
      "description": "The secret paired with the body `key` (issued at onboarding). Never sent on the wire — used only to compute the MD5 signature."
    },
    {
      "key": "ref",
      "value": "ON2P-D-20260424-102000-DEP456",
      "type": "string",
      "description": "A deposit ref used by the public slip-page requests."
    },
    {
      "key": "token",
      "value": "a3f8e7c2b1d4e5f6789a",
      "type": "string",
      "description": "The uploadToken returned when a deposit is created (public slip-page `t` query param)."
    }
  ],
  "item": [
    {
      "name": "Merchant · Deposit",
      "description": "Create deposits, upload transfer slips, and cancel.",
      "item": [
        {
          "name": "Create deposit",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "// Signature: md5(key:amount:fromAccount:fromBankCode:callbackUrl:secretKey)",
                  "var secretKey = pm.collectionVariables.get('secretKey') || pm.variables.get('secretKey') || '';",
                  "var body = JSON.parse(pm.request.body.raw || '{}');",
                  "var input = [body.key, body.amount, body.fromAccount, body.fromBankCode, body.callbackUrl, secretKey].join(':');",
                  "body.signature = CryptoJS.MD5(input).toString();",
                  "pm.request.body.raw = JSON.stringify(body, null, 2);",
                  "pm.request.headers.upsert({ key: 'X-API-KEY', value: pm.collectionVariables.get('apiKey') || '' });"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "X-API-KEY", "value": "{{apiKey}}" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"key\": \"dep-key-xyz\",\n  \"amount\": 1000,\n  \"fromAccount\": \"4545454545\",\n  \"fromBankCode\": \"006\",\n  \"fullName\": \"น.ส. อนุตา ธาธา\",\n  \"fullNameEn\": \"ANUTA TATA\",\n  \"callbackUrl\": \"https://merchant.com/api/deposit-callback\",\n  \"returnUrl\": \"https://merchant.com/thank-you\",\n  \"ref\": \"ON2P-W-20260424-100000-ABC\",\n  \"signature\": \"auto-computed\"\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": {
              "raw": "{{baseUrl}}/v1/p2p/deposit",
              "host": ["{{baseUrl}}"],
              "path": ["v1", "p2p", "deposit"]
            },
            "description": "Create a deposit. Mode A: supply a withdraw `ref` to match explicitly. Mode B: omit `ref` for auto-match. Returns `ref`, `uploadToken`, `uploadUrl`.\n\n**Signature:** md5(key:amount:fromAccount:fromBankCode:callbackUrl:secretKey) — auto-computed by the pre-request script."
          }
        },
        {
          "name": "Upload slip (JSON base64)",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "// Signature: md5(key:ref:secretKey)",
                  "var secretKey = pm.collectionVariables.get('secretKey') || pm.variables.get('secretKey') || '';",
                  "var body = JSON.parse(pm.request.body.raw || '{}');",
                  "var input = [body.key, body.ref, secretKey].join(':');",
                  "body.signature = CryptoJS.MD5(input).toString();",
                  "pm.request.body.raw = JSON.stringify(body, null, 2);",
                  "pm.request.headers.upsert({ key: 'X-API-KEY', value: pm.collectionVariables.get('apiKey') || '' });"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "X-API-KEY", "value": "{{apiKey}}" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"key\": \"dep-key-xyz\",\n  \"ref\": \"ON2P-D-20260424-102000-DEP456\",\n  \"imgSlip\": \"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEASABIAAD...\",\n  \"signature\": \"auto-computed\"\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": {
              "raw": "{{baseUrl}}/v1/p2p/upload-slip",
              "host": ["{{baseUrl}}"],
              "path": ["v1", "p2p", "upload-slip"]
            },
            "description": "Upload the end-player's transfer slip for a deposit. Accepts JSON with a base64 data-URI (`imgSlip`) or multipart form-data (file field `imgSlip`). Max 10 MB.\n\n**Signature:** md5(key:ref:secretKey) — auto-computed by the pre-request script.\n\n_Note: for the multipart variant, switch the body to form-data, add the `imgSlip` file field plus `key`, `ref`, `signature` text fields — the pre-request script here signs the raw JSON path; use the JSON path or precompute for multipart._"
          }
        },
        {
          "name": "Cancel deposit",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "// Signature: md5(key:ref:secretKey)",
                  "var secretKey = pm.collectionVariables.get('secretKey') || pm.variables.get('secretKey') || '';",
                  "var body = JSON.parse(pm.request.body.raw || '{}');",
                  "var input = [body.key, body.ref, secretKey].join(':');",
                  "body.signature = CryptoJS.MD5(input).toString();",
                  "pm.request.body.raw = JSON.stringify(body, null, 2);",
                  "pm.request.headers.upsert({ key: 'X-API-KEY', value: pm.collectionVariables.get('apiKey') || '' });"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "X-API-KEY", "value": "{{apiKey}}" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"key\": \"dep-key-xyz\",\n  \"ref\": \"ON2P-D-20260424-102000-DEP456\",\n  \"signature\": \"auto-computed\"\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": {
              "raw": "{{baseUrl}}/v1/p2p/cancel",
              "host": ["{{baseUrl}}"],
              "path": ["v1", "p2p", "cancel"]
            },
            "description": "Cancel a still-pending deposit. Releases the matched withdraw's held amount and fires a `status:\"cancelled\"` callback.\n\n**Signature:** md5(key:ref:secretKey) — auto-computed by the pre-request script."
          }
        }
      ]
    },
    {
      "name": "Merchant · Withdraw",
      "description": "Create withdraws and extend their timeout.",
      "item": [
        {
          "name": "Create withdraw",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "// Signature: md5(key:amount:toAccount:toBankCode:callbackUrl:secretKey)",
                  "var secretKey = pm.collectionVariables.get('secretKey') || pm.variables.get('secretKey') || '';",
                  "var body = JSON.parse(pm.request.body.raw || '{}');",
                  "var input = [body.key, body.amount, body.toAccount, body.toBankCode, body.callbackUrl, secretKey].join(':');",
                  "body.signature = CryptoJS.MD5(input).toString();",
                  "pm.request.body.raw = JSON.stringify(body, null, 2);",
                  "pm.request.headers.upsert({ key: 'X-API-KEY', value: pm.collectionVariables.get('apiKey') || '' });"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "X-API-KEY", "value": "{{apiKey}}" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"key\": \"wd-key-abc123\",\n  \"amount\": 1000,\n  \"toAccount\": \"1234567890\",\n  \"toBankCode\": \"011\",\n  \"toAccountName\": \"เนรมิตร ม้านิล\",\n  \"toAccountNameEn\": \"\",\n  \"callbackUrl\": \"https://merchant.com/api/withdraw-callback\",\n  \"signature\": \"auto-computed\"\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": {
              "raw": "{{baseUrl}}/v1/p2p/withdraw",
              "host": ["{{baseUrl}}"],
              "path": ["v1", "p2p", "withdraw"]
            },
            "description": "Create a withdraw. Locks the amount from your P2P balance and matches it against incoming deposits. Requires a merchant account of type `P2P`.\n\n**Signature:** md5(key:amount:toAccount:toBankCode:callbackUrl:secretKey) — auto-computed by the pre-request script."
          }
        },
        {
          "name": "Extend withdraw timeout",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "// Signature: md5(key:ref:secretKey)",
                  "var secretKey = pm.collectionVariables.get('secretKey') || pm.variables.get('secretKey') || '';",
                  "var body = JSON.parse(pm.request.body.raw || '{}');",
                  "var input = [body.key, body.ref, secretKey].join(':');",
                  "body.signature = CryptoJS.MD5(input).toString();",
                  "pm.request.body.raw = JSON.stringify(body, null, 2);",
                  "pm.request.headers.upsert({ key: 'X-API-KEY', value: pm.collectionVariables.get('apiKey') || '' });"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "X-API-KEY", "value": "{{apiKey}}" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"key\": \"wd-key-abc123\",\n  \"ref\": \"ON2P-W-20260424-100000-ABC\",\n  \"signature\": \"auto-computed\"\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": {
              "raw": "{{baseUrl}}/v1/p2p/withdraw-add-timeout",
              "host": ["{{baseUrl}}"],
              "path": ["v1", "p2p", "withdraw-add-timeout"]
            },
            "description": "Extend a still-pending withdraw's timeout by +15 minutes (maximum 2 extensions).\n\n**Signature:** md5(key:ref:secretKey) — auto-computed by the pre-request script."
          }
        }
      ]
    },
    {
      "name": "Merchant · Balance",
      "description": "Read your P2P balance (read-only).",
      "item": [
        {
          "name": "Read P2P balance",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "// Signature: md5(key:secretKey) — DEPOSIT or WITHDRAW key both work",
                  "var secretKey = pm.collectionVariables.get('secretKey') || pm.variables.get('secretKey') || '';",
                  "var body = JSON.parse(pm.request.body.raw || '{}');",
                  "var input = [body.key, secretKey].join(':');",
                  "body.signature = CryptoJS.MD5(input).toString();",
                  "pm.request.body.raw = JSON.stringify(body, null, 2);",
                  "pm.request.headers.upsert({ key: 'X-API-KEY', value: pm.collectionVariables.get('apiKey') || '' });"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "X-API-KEY", "value": "{{apiKey}}" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"key\": \"dep-key-abc123\",\n  \"signature\": \"auto-computed\"\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": {
              "raw": "{{baseUrl}}/v1/p2p/balance",
              "host": ["{{baseUrl}}"],
              "path": ["v1", "p2p", "balance"]
            },
            "description": "Read your P2P balance: `balance` (available), `withdrawPending` (locked in pending withdraws), `total`.\n\n**Signature:** md5(key:secretKey) — a DEPOSIT or WITHDRAW key both work. Auto-computed by the pre-request script."
          }
        }
      ]
    },
    {
      "name": "Back Office (BO)",
      "description": "Pull and reconcile your own deposit / withdraw history.",
      "item": [
        {
          "name": "List deposits",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "// Signature: md5(key:secretKey) — DEPOSIT key",
                  "var secretKey = pm.collectionVariables.get('secretKey') || pm.variables.get('secretKey') || '';",
                  "var body = JSON.parse(pm.request.body.raw || '{}');",
                  "var input = [body.key, secretKey].join(':');",
                  "body.signature = CryptoJS.MD5(input).toString();",
                  "pm.request.body.raw = JSON.stringify(body, null, 2);",
                  "pm.request.headers.upsert({ key: 'X-API-KEY', value: pm.collectionVariables.get('apiKey') || '' });"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "X-API-KEY", "value": "{{apiKey}}" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"key\": \"dep-key-abc123\",\n  \"status\": \"complete\",\n  \"sDate\": \"2026-05-01T00:00:00.000Z\",\n  \"eDate\": \"2026-05-23T23:59:59.999Z\",\n  \"minAmount\": 500,\n  \"limit\": 50,\n  \"skip\": 0,\n  \"signature\": \"auto-computed\"\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": {
              "raw": "{{baseUrl}}/v1/p2p/deposit-list",
              "host": ["{{baseUrl}}"],
              "path": ["v1", "p2p", "deposit-list"]
            },
            "description": "Pull your own deposit history. Filter by date/status/amount/ref; `limit` 1–100 (default 50), `skip` (default 0, max 10,000). Newest-first. Uses a DEPOSIT key. Hourly rate limit applies (P2P_016).\n\n**Signature:** md5(key:secretKey) — auto-computed by the pre-request script."
          }
        },
        {
          "name": "List withdraws",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "// Signature: md5(key:secretKey) — WITHDRAW key",
                  "var secretKey = pm.collectionVariables.get('secretKey') || pm.variables.get('secretKey') || '';",
                  "var body = JSON.parse(pm.request.body.raw || '{}');",
                  "var input = [body.key, secretKey].join(':');",
                  "body.signature = CryptoJS.MD5(input).toString();",
                  "pm.request.body.raw = JSON.stringify(body, null, 2);",
                  "pm.request.headers.upsert({ key: 'X-API-KEY', value: pm.collectionVariables.get('apiKey') || '' });"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "X-API-KEY", "value": "{{apiKey}}" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"key\": \"wd-key-xyz789\",\n  \"status\": \"pending\",\n  \"sDate\": \"2026-05-23T00:00:00.000Z\",\n  \"eDate\": \"2026-05-23T23:59:59.999Z\",\n  \"limit\": 50,\n  \"skip\": 0,\n  \"signature\": \"auto-computed\"\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": {
              "raw": "{{baseUrl}}/v1/p2p/withdraw-list",
              "host": ["{{baseUrl}}"],
              "path": ["v1", "p2p", "withdraw-list"]
            },
            "description": "Pull your own withdraw history. Same filters/pagination as deposit-list but uses a WITHDRAW key; the amount filter applies to the withdraw amount. Per row: completedAmount + matchingAmount + remainingAmount = amount.\n\n**Signature:** md5(key:secretKey) — auto-computed by the pre-request script."
          }
        }
      ]
    },
    {
      "name": "Public Slip Page",
      "description": "Public endpoints backing the player's slip-upload page — no API key, no signature. Authenticated only by the (ref, token) pair in the URL.",
      "item": [
        {
          "name": "Get slip-page display data",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/v1/p2p-public/upload-info/{{ref}}?t={{token}}",
              "host": ["{{baseUrl}}"],
              "path": ["v1", "p2p-public", "upload-info", "{{ref}}"],
              "query": [
                { "key": "t", "value": "{{token}}", "description": "The uploadToken issued at deposit creation." }
              ]
            },
            "description": "Fetch the deposit's display data (amount, sender/destination account, PromptPay QR, expiry, status) for the public slip-upload page. No login; authenticated only by the (ref, token) pair."
          }
        },
        {
          "name": "Upload slip via public page",
          "request": {
            "method": "POST",
            "header": [],
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "slip",
                  "type": "file",
                  "src": [],
                  "description": "The slip image file. Accepted: image/jpeg,png,webp,heic,bmp,tiff. Max 20 MB."
                }
              ]
            },
            "url": {
              "raw": "{{baseUrl}}/v1/p2p-public/upload-slip/{{ref}}?t={{token}}",
              "host": ["{{baseUrl}}"],
              "path": ["v1", "p2p-public", "upload-slip", "{{ref}}"],
              "query": [
                { "key": "t", "value": "{{token}}", "description": "The upload token." }
              ]
            },
            "description": "Upload the transfer slip via the public page (no signature / API key). Multipart file field is `slip`."
          }
        },
        {
          "name": "Cancel deposit via public page",
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/v1/p2p-public/cancel/{{ref}}?t={{token}}",
              "host": ["{{baseUrl}}"],
              "path": ["v1", "p2p-public", "cancel", "{{ref}}"],
              "query": [
                { "key": "t", "value": "{{token}}", "description": "The upload token." }
              ]
            },
            "description": "Cancel a still-pending deposit from the public page. Triggers the standard deposit-cancel flow (destination rollback + a status:\"cancelled\" callback to the merchant)."
          }
        },
        {
          "name": "Realtime system-status snapshot",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/v1/p2p-public/bank-status",
              "host": ["{{baseUrl}}"],
              "path": ["v1", "p2p-public", "bank-status"]
            },
            "description": "Realtime system-status snapshot (banks open for deposit/withdraw, no-QR bank pairs, open/close schedule, limits, fees, tiers). No ref, token, or API key. Cached ~20–60s server-side."
          }
        }
      ]
    }
  ]
}
