### Python requests example to retrieve a cash receipt Source: https://docs.paysync.kr/cash-receipts/file Uses the requests library to GET a cash receipt. The API key is read from the PAYSYNC_API_KEY environment variable. ```python import os, requests cash_receipt_id = "crt_a1b2c3d4e5f6g7h8i9j0k1l2" res = requests.get( f"https://api.paysync.kr/v1/cash-receipts/{cash_receipt_id}", headers={"Authorization": f"Bearer {os.environ['PAYSYNC_API_KEY']}"}, ) print(res.json()) ``` -------------------------------- ### Webhook Event Handling Source: https://docs.paysync.kr/api-reference/webhooks/overview Guidance on handling webhook events, including idempotency checks and local development setup. ```APIDOC ## Webhook Event Handling ### Description This section provides guidance on how to handle webhook events, including idempotency checks and local development setup. ### Supported Events - `invoice.created` - A new order has been created. - `invoice.paid` - The order has been converted to a paid status. - `invoice.deleted` - The order has been deleted. ### Response Requirements - **HTTP 200** - Recorded as success. - **Other (3xx, 4xx, 5xx, timeout)** - Recorded as failure and a failure notification email is sent. - Only an exact **200** is treated as success; `201`, `204`, etc. are considered failures. - Response timeout is **10 seconds**. For heavy operations, send a 200 response first and process asynchronously. ### Failure Handling - **No automatic retries** - PaySync does not perform automatic retries. It only records the result after a single send. - **Failure notification email** - If the `webhookFailures` notification is enabled, a failure notification email is sent to the registered email. This can be toggled in the dashboard under `Settings → Notifications`. - **Manual resend** - Failed deliveries can be resent from the dashboard under `Webhook Management → Delivery History`. ### Idempotency - The `webhook-id` header is a unique identifier for each delivery. Consider idempotent processing based on the domain resource ID, as the same event may arrive twice due to network issues. #### Example javascript // Ignore if already processed if (await db.isEventProcessed(event.invoice.id, event.type)) { return res.sendStatus(200); } await processEvent(event); await db.markEventProcessed(event.invoice.id, event.type); ### Local Development - To test in an internal network, use a tunneling tool like ngrok to expose your local server to the outside and register that URL. #### Example bash ngrok http 3000 # Outputs a URL like https://abcd-1234.ngrok-free.app # Register this URL as your PaySync webhook endpoint. ### ID Prefixes - `whd_` - Webhook endpoint (Destination) - `whm_` - Webhook delivery (Delivery / Message) ``` -------------------------------- ### Create Invoice with Node.js Source: https://docs.paysync.kr/invoices/issue Uses fetch to POST invoice data. Reads API key from environment variable PAYSYNC_API_KEY. Parses response JSON to get code and data. ```javascript const res = await fetch("https://api.paysync.kr/v1/invoices", { method: "POST", headers: { Authorization: `Bearer ${process.env.PAYSYNC_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ customer: { name: "홍길동", email: "hong@example.com", phoneNumber: "01012345678", }, amount: 50000, cashReceipt: { type: "PERSONAL", identifier: "01012345678" }, expireAfter: "1d", metadata: { orderId: "ORDER-2026-0001" }, }), }); const { code, data } = await res.json(); ``` -------------------------------- ### Create order with bankAccountIds using Node.js fetch Source: https://docs.paysync.kr/multi-bank-account-routing Use this Node.js fetch example to create an order with a specific receiving account. The bankAccountIds field must contain the account ID(s) to route deposits to this order. ```javascript await fetch("https://api.paysync.kr/v1/invoices", { method: "POST", headers: { Authorization: `Bearer ${process.env.PAYSYNC_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ bankAccountIds: ["bac_xxxxxxxxxxxx"], customer: { name: "홍길동" }, amount: 10000, }), }); ``` -------------------------------- ### POST /v1/invoices Source: https://docs.paysync.kr/api-reference/introduction Example of creating a single invoice using the Paysync API. ```APIDOC ## POST /v1/invoices ### Description Creates a new invoice (order) in Paysync. This endpoint is used to generate a payment request for a customer. ### Method POST ### Endpoint https://api.paysync.kr/v1/invoices ### Parameters #### Request Body - **customer** (object) - Required - Customer information - **name** (string) - Required - Customer name - **email** (string) - Required - Customer email - **phoneNumber** (string) - Required - Customer phone number - **amount** (number) - Required - Payment amount in KRW - **expireAfter** (string) - Required - Expiration period (e.g., "1d") - **metadata** (object) - Optional - Additional metadata - **orderId** (string) - Optional - Order identifier ### Request Example { "customer": { "name": "홍길동", "email": "hong@example.com", "phoneNumber": "01012345678" }, "amount": 50000, "expireAfter": "1d", "metadata": { "orderId": "ORDER-2026-0001" } } ### Response #### Success Response (200) - Response body format follows the standard Paysync API response format. #### Response Example (Not provided in source) ### Authentication Bearer Token (Authorization: Bearer YOUR_API_KEY) ### Content-Type application/json ``` -------------------------------- ### Create order with bankAccountIds using Python requests Source: https://docs.paysync.kr/multi-bank-account-routing Use this Python requests example to create an order with a specific receiving account. The bankAccountIds field must contain the account ID(s) to route deposits to this order. ```python import os import requests requests.post( "https://api.paysync.kr/v1/invoices", headers={ "Authorization": f"Bearer {os.environ['PAYSYNC_API_KEY']}", "Content-Type": "application/json", }, json={ "bankAccountIds": ["bac_xxxxxxxxxxxx"], "customer": {"name": "홍길동"}, "amount": 10000, }, ) ``` -------------------------------- ### Node.js Handling Example for invoice.deleted Source: https://docs.paysync.kr/api-reference/webhooks/invoice-deleted Example Node.js code to handle the invoice.deleted event. It updates the internal order table to cancelled status using the orderId from metadata. ```javascript if (event.type === "invoice.deleted") { const orderId = event.invoice.metadata?.orderId; if (!orderId) return res.sendStatus(200); // 자체 주문도 취소 상태로 전환 await db.orders.update(orderId, { status: "CANCELLED", cancelledAt: new Date(), cancelReason: "INVOICE_DELETED", }); } ``` -------------------------------- ### Successful Invoice Creation Response Source: https://docs.paysync.kr/invoices/issue Example response body for a successful invoice creation, containing invoice details such as id, customer, amount, and timestamps. ```json { "code": "CREATED", "data": { "id": "ivc_a1b2c3d4e5f6g7h8i9j0k1l2", "issuerId": "acc_x9y8z7w6v5u4t3s2r1q0p9o8", "customer": { "name": "홍길동", "email": "hong@example.com", "phoneNumber": "01012345678" }, "cashReceipt": { "type": "PERSONAL", "identifier": "01012345678" }, "amount": 50000, "paid": false, "metadata": { "orderId": "ORDER-2026-0001" }, "issuedAt": "2026-04-28T03:14:15.926Z", "expiresAt": "2026-04-29T03:14:15.926Z" } } ``` -------------------------------- ### List transactions with cURL, Node.js, and Python Source: https://docs.paysync.kr/transactions/list List deposit transactions with pagination and filters (offset, limit, matched, dateAfter). The cURL example uses YOUR_API_KEY placeholder; Node.js reads PAYSYNC_API_KEY from environment; Python reads PAYSYNC_API_KEY from environment. Response includes code, totalItems, and data. ```bash curl "https://api.paysync.kr/v1/transactions?offset=0&limit=20&matched=true&dateAfter=2026-04-01" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ```javascript const params = new URLSearchParams({ offset: "0", limit: "20", matched: "true", dateAfter: "2026-04-01", }); const res = await fetch(`https://api.paysync.kr/v1/transactions?${params}`, { headers: { Authorization: `Bearer ${process.env.PAYSYNC_API_KEY}` }, }); const { code, totalItems, data } = await res.json(); ``` ```python import os, requests res = requests.get( "https://api.paysync.kr/v1/transactions", headers={"Authorization": f"Bearer {os.environ['PAYSYNC_API_KEY']}"}, params={ "offset": 0, "limit": 20, "matched": True, "dateAfter": "2026-04-01", }, ) print(res.json()) ``` -------------------------------- ### 200 OK response for list invoices Source: https://docs.paysync.kr/invoices/list Example 200 OK response for the list invoices endpoint. Shows the structure with code "OK", totalItems, and an array of invoice objects. ```json { "code": "OK", "totalItems": 142, "data": [ { "id": "ivc_a1b2c3d4e5f6g7h8i9j0k1l2", "issuerId": "acc_x9y8z7w6v5u4t3s2r1q0p9o8", "bankAccountIds": [], "customer": { "name": "홍길동", "email": "hong@example.com", "phoneNumber": "01012345678" }, "cashReceipt": null, "amount": 50000, "paid": true, "metadata": { "orderId": "ORDER-2026-0001" }, "issuedAt": "2026-04-28T03:14:15.926Z", "expiresAt": "2026-04-29T03:14:15.926Z" } ] } ``` -------------------------------- ### List invoices with cURL, Node.js, and Python Source: https://docs.paysync.kr/invoices/list Lists invoices with pagination and filters (offset, limit, paid, dateAfter). The cURL example uses YOUR_API_KEY placeholder; Node.js and Python read the key from environment variable PAYSYNC_API_KEY. The response is parsed as JSON with code, totalItems, and data fields. ```bash curl "https://api.paysync.kr/v1/invoices?offset=0&limit=20&paid=true&dateAfter=2026-04-01" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ```javascript const params = new URLSearchParams({ offset: "0", limit: "20", paid: "true", dateAfter: "2026-04-01", }); const res = await fetch(`https://api.paysync.kr/v1/invoices?${params}`, { headers: { Authorization: `Bearer ${process.env.PAYSYNC_API_KEY}` }, }); const { code, totalItems, data } = await res.json(); ``` ```python import os, requests res = requests.get( "https://api.paysync.kr/v1/invoices", headers={"Authorization": f"Bearer {os.environ['PAYSYNC_API_KEY']}"}, params={ "offset": 0, "limit": 20, "paid": True, "dateAfter": "2026-04-01", }, ) print(res.json()) ``` -------------------------------- ### Example response — successful order creation Source: https://docs.paysync.kr/api-reference/introduction Shows the JSON structure returned when an order (invoice) is created successfully. The code field is "CREATED" and data contains the new invoice object with its id, customer details, amount, and timestamps. ```json { "code": "CREATED", "data": { "id": "ivc_a1b2c3d4e5f6g7h8i9j0k1l2", "issuerId": "acc_...", "bankAccountIds": [], "customer": { "name": "홍길동", "email": "hong@example.com", "phoneNumber": "01012345678" }, "amount": 50000, "paid": false, "metadata": { "orderId": "ORDER-2026-0001" }, "issuedAt": "2026-04-28T03:14:15.926Z", "expiresAt": "2026-04-29T03:14:15.926Z" } } ``` -------------------------------- ### Example response — deposit transaction list Source: https://docs.paysync.kr/api-reference/introduction Shows the paginated response for a list of deposit transactions. The code field is "OK", totalItems is 142, and data is an array of transaction objects with matched invoice id, amount, and timestamps. ```json { "code": "OK", "totalItems": 142, "data": [ { "id": "trx_a1b2c3d4e5f6g7h8i9j0k1l2", "matchedInvoiceId": "ivc_...", "amount": 50000, "description": "홍길동", "hidden": false, "receivedAt": "2026-04-28T03:20:11.000Z", "matchedAt": "2026-04-28T03:20:12.412Z", "createdAt": "2026-04-28T03:20:12.000Z" } ] } ``` -------------------------------- ### List cash receipts with cURL, Node.js, and Python Source: https://docs.paysync.kr/cash-receipts/list List cash receipts with pagination and filters (offset, limit, status, dateAfter). The cURL example uses YOUR_API_KEY placeholder; Node.js and Python read the key from PAYSYNC_API_KEY environment variable. The response contains code, totalItems, and data fields. ```bash curl "https://api.paysync.kr/v1/cash-receipts?offset=0&limit=20&status=ISSUED&dateAfter=2026-04-01" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ```javascript const params = new URLSearchParams({ offset: "0", limit: "20", status: "ISSUED", dateAfter: "2026-04-01", }); const res = await fetch(`https://api.paysync.kr/v1/cash-receipts?${params}`, { headers: { Authorization: `Bearer ${process.env.PAYSYNC_API_KEY}` }, }); const { code, totalItems, data } = await res.json(); ``` ```python import os, requests res = requests.get( "https://api.paysync.kr/v1/cash-receipts", headers={"Authorization": f"Bearer {os.environ['PAYSYNC_API_KEY']}"}, params={ "offset": 0, "limit": 20, "status": "ISSUED", "dateAfter": "2026-04-01", }, ) print(res.json()) ``` -------------------------------- ### ngrok으로 로컬 서버 터널링 Source: https://docs.paysync.kr/api-reference/webhooks/overview 로컬 개발 시 내부망의 서버를 외부에 노출하기 위해 ngrok 터널링 도구를 사용하는 예시입니다. 출력된 URL을 페이싱크 웹훅 엔드포인트로 등록하면 됩니다. ```bash ngrok http 3000 # https://abcd-1234.ngrok-free.app 같은 URL 이 출력됩니다. # 이 URL 을 페이싱크 웹훅 엔드포인트로 등록하세요. ``` -------------------------------- ### GET /v1/transactions Source: https://docs.paysync.kr/transactions/list Retrieves a paginated list of deposit transactions with filtering options. Includes request examples in cURL, Node.js, and Python, along with success and error response examples. ```APIDOC ## GET /v1/transactions ### Description Retrieves a paginated list of deposit transactions linked to the API key's account. Supports filtering by bank account, matching status, and date range. ### Method GET ### Endpoint https://api.paysync.kr/v1/transactions ### Parameters #### Query Parameters - **offset** (integer) - Optional - Pagination offset. Default is 0. - **limit** (integer) - Optional - Maximum number of items to return. Default is 20. - **matched** (boolean) - Optional - Filter by matching status (true/false). - **dateAfter** (string, date) - Optional - Return transactions received after this date (format: YYYY-MM-DD). ### Request Example ``` curl "https://api.paysync.kr/v1/transactions?offset=0&limit=20&matched=true&dateAfter=2026-04-01" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ### Response #### Success Response (200 OK) - **code** (string) - Response code, e.g., "OK". - **totalItems** (integer) - Total number of matching transactions. - **data** (array) - List of transaction objects. - **id** (string) - Transaction ID. - **bankAccount** (object) - Bank account details. - **id** (string) - Bank account ID. - **alias** (string) - Account alias. - **provider** (string) - Bank provider code. - **type** (string) - Account type (e.g., PERSONAL). - **number** (string) - Account number. - **matchedInvoiceId** (string) - ID of the matched invoice, if any. - **amount** (number) - Transaction amount. - **description** (string) - Transaction description. - **hidden** (boolean) - Whether the transaction is hidden. - **receivedAt** (string, date-time) - When the transaction was received. - **matchedAt** (string, date-time) - When the transaction was matched. - **createdAt** (string, date-time) - When the transaction record was created. #### Response Example ``` { "code": "OK", "totalItems": 87, "data": [ { "id": "trx_a1b2c3d4e5f6g7h8i9j0k1l2", "bankAccount": { "id": "bac_x9y8z7w6v5u4t3s2r1q0p9o8", "alias": "사업용 계좌", "provider": "KB_KOOKMIN_BANK", "type": "PERSONAL", "number": "1234567890" }, "matchedInvoiceId": "ivc_n0o1p2q3r4s5t6u7v8w9x0y1", "amount": 50000, "description": "홍길동", "hidden": false, "receivedAt": "2026-04-28T03:20:11.000Z", "matchedAt": "2026-04-28T03:20:12.412Z", "createdAt": "2026-04-28T03:20:12.000Z" } ] } ``` ### Error Handling #### 401 NOT_AUTHORIZED ``` { "code": "NOT_AUTHORIZED", "data": null } ``` #### 403 LIMIT_EXCEEDS_MAXIMUM ``` { "code": "LIMIT_EXCEEDS_MAXIMUM", "data": null } ``` #### 422 INVALID_OFFSET ``` { "code": "INVALID_OFFSET", "data": null } ``` #### 422 INVALID_LIMIT ``` { "code": "INVALID_LIMIT", "data": null } ``` #### 422 INVALID_DATE_RANGE_FORMAT ``` { "code": "INVALID_DATE_RANGE_FORMAT", "data": null } ``` ``` -------------------------------- ### Get transaction by ID with cURL, Node.js, and Python Source: https://docs.paysync.kr/transactions/get Retrieve a single transaction by its ID using the Paysync API. The cURL example uses YOUR_API_KEY placeholder; the Node.js example reads the key from process.env.PAYSYNC_API_KEY; the Python example reads it from the PAYSYNC_API_KEY environment variable. The response is parsed as JSON and printed in the Python example. ```bash curl https://api.paysync.kr/v1/transactions/trx_a1b2c3d4e5f6g7h8i9j0k1l2 \ -H "Authorization: Bearer YOUR_API_KEY" ``` ```javascript const res = await fetch( `https://api.paysync.kr/v1/transactions/${transactionId}`, { headers: { Authorization: `Bearer ${process.env.PAYSYNC_API_KEY}` } } ); const { code, data } = await res.json(); ``` ```python import os, requests transaction_id = "trx_a1b2c3d4e5f6g7h8i9j0k1l2" res = requests.get( f"https://api.paysync.kr/v1/transactions/{transaction_id}", headers={"Authorization": f"Bearer {os.environ['PAYSYNC_API_KEY']}"}, ) print(res.json()) ``` -------------------------------- ### Create Invoice with Python Source: https://docs.paysync.kr/invoices/issue Uses requests library to POST invoice data. Reads API key from environment variable PAYSYNC_API_KEY. Prints the JSON response. ```python import os, requests res = requests.post( "https://api.paysync.kr/v1/invoices", headers={"Authorization": f"Bearer {os.environ['PAYSYNC_API_KEY']}"}, json={ "customer": { "name": "홍길동", "email": "hong@example.com", "phoneNumber": "01012345678", }, "amount": 50000, "cashReceipt": {"type": "PERSONAL", "identifier": "01012345678"}, "expireAfter": "1d", "metadata": {"orderId": "ORDER-2026-0001"}, }, ) print(res.json()) ``` -------------------------------- ### Payload Example for invoice.deleted Event Source: https://docs.paysync.kr/api-reference/webhooks/invoice-deleted Example JSON payload for the invoice.deleted event. The invoice object is a snapshot of the deleted invoice, with paid always false. ```json { "type": "invoice.deleted", "invoice": { "id": "ivc_a1b2c3d4e5f6g7h8i9j0k1l2", "issuerId": "acc_x9y8z7w6v5u4t3s2r1q0p9o8", "bankAccountIds": [], "customer": { "name": "홍길동", "email": null, "phoneNumber": null }, "cashReceipt": null, "amount": 50000, "paid": false, "metadata": { "orderId": "ORDER-2026-0001" }, "issuedAt": "2026-04-28T03:14:15.926Z", "expiresAt": "2026-04-29T03:14:15.926Z" } } ``` -------------------------------- ### Create Checkout Page in Next.js Source: https://docs.paysync.kr/online-shop Displays payment instructions including bank details and customer-specific deposit information. Ensure the deposit name and amount are clearly highlighted to facilitate automatic matching. ```tsx export default async function CheckoutPage({ params }) { const order = await db.orders.find(params.orderId); const account = await db.bankAccounts.findActive(); // 등록된 페이싱크 계좌 return (