# Get company balance Source: https://docs.chip-in.asia/chip-collect/api-reference/account/balance _openapi-chip-collect GET /account/json/balance/ Returns the company balance according to the provided query string filters. Multiple values can be provided for all filters except `from` and `to`, including all results matching any of these values. # Get company turnover Source: https://docs.chip-in.asia/chip-collect/api-reference/account/turnover _openapi-chip-collect GET /account/json/turnover/ Fetches the company turnover according to the provided query string filters. Must provide exactly one `currency` filter. Multiple values can be provided for all filters except `currency`, `from` and `to`, including all results matching any of these values. # Create a client Source: https://docs.chip-in.asia/chip-collect/api-reference/clients/create _openapi-chip-collect POST /clients/ Client is a record of a single customer of your business. Create one for each of your clients so you can issue invoices or subscriptions for them later. # Delete a client Source: https://docs.chip-in.asia/chip-collect/api-reference/clients/delete _openapi-chip-collect DELETE /clients/{id}/ # Delete a recurring token Source: https://docs.chip-in.asia/chip-collect/api-reference/clients/delete-recurring-tokens _openapi-chip-collect DELETE /clients/{client_id}/recurring_tokens/{purchase_id}/ If you create the Purchase with the respective Client's ID (in `.client_id`), your client won't see this token as available on the checkout page anymore. You also won't be able to use the ID of this object as a `recurring_token` in `POST /purchases/{id}/charge/`. The respective Purchase will have `is_recurring_token` set to `false` (as if `POST /purchases/{recurring_token}/delete_recurring_token/` was issued). # List all clients Source: https://docs.chip-in.asia/chip-collect/api-reference/clients/list _openapi-chip-collect GET /clients/ # List recurring tokens Source: https://docs.chip-in.asia/chip-collect/api-reference/clients/list-recurring-tokens _openapi-chip-collect GET /clients/{id}/recurring_tokens/ List recurring tokens saved for a client. All of these tokens will be available in a checkout (see `Purchase.checkout_url`) if you create a Purchase with this client's ID in `client_id` field. You can use one in `POST /purchases/{id}/charge/`, too. Note that you can use one client's `recurring_token` to pay a Purchase created for a different `client_id` or created with no `client_id` at all; it's not recommended to do this. # Partially update a client Source: https://docs.chip-in.asia/chip-collect/api-reference/clients/partial-update _openapi-chip-collect PATCH /clients/{id}/ # Retrieve a client Source: https://docs.chip-in.asia/chip-collect/api-reference/clients/retrieve _openapi-chip-collect GET /clients/{id}/ # Retrieve a recurring token Source: https://docs.chip-in.asia/chip-collect/api-reference/clients/retrieve-recurring-tokens _openapi-chip-collect GET /clients/{client_id}/recurring_tokens/{purchase_id}/ # Update a client Source: https://docs.chip-in.asia/chip-collect/api-reference/clients/update _openapi-chip-collect PUT /clients/{id}/ # Cancel a statement Source: https://docs.chip-in.asia/chip-collect/api-reference/company-statements/cancel _openapi-chip-collect POST /company_statements/{id}/cancel/ # List all statements Source: https://docs.chip-in.asia/chip-collect/api-reference/company-statements/list _openapi-chip-collect GET /company_statements/ # Retrieve a statement Source: https://docs.chip-in.asia/chip-collect/api-reference/company-statements/retrieve _openapi-chip-collect GET /company_statements/{id}/ # Schedule a statement Source: https://docs.chip-in.asia/chip-collect/api-reference/company-statements/schedule _openapi-chip-collect POST /company_statements/ Schedule a statement generation. # List of payment methods Source: https://docs.chip-in.asia/chip-collect/api-reference/payment-methods/list _openapi-chip-collect GET /payment_methods/ > Always send the `amount` query parameter, e.g. `&amount=1000` for RM 10. > Without it, only card methods (visa, mastercard, maestro) and a few > others are returned — most methods (FPX, DuitNow QR, Shopee Pay, etc.) > have a minimum amount and are hidden from the response. > > RM 10 (`1000`) is a safe value that covers every method's minimum. Send this request providing the `brand_id` and `currency` query parameters, with the same values you'd use to create your Purchase. Be sure to use the same API key you'll create your Purchase with; it will define the test_mode setting used in the lookup. In the response body you'll receive an object with `available_payment_methods` property containing the list of payment method names available to use with your Purchase (e.g. those codes can be used in `payment_method_whitelist` field or with `?preferred={payment_method}` option of `checkout_url`). Please note that all lookup arguments must be provided via query parameters after the endpoint, e.g. the minimal call would be similar to: `GET /api/v1/payment_methods/?brand_id=75a76529-91c7-4d98-90a9-8a641d70ee52¤cy=MYR&amount=1000` # Retrieve a public key Source: https://docs.chip-in.asia/chip-collect/api-reference/public-key/retrieve _openapi-chip-collect GET /public_key/ Returns public key for authenticating company callback payloads. The response body is a JSON-encoded PEM string (e.g. `"-----BEGIN PUBLIC KEY-----..."` with surrounding quotes), not a bare PEM body and not a `{"key": ...}` object. `json_decode` the response body before passing it to your crypto library. # Cancel a pending purchase Source: https://docs.chip-in.asia/chip-collect/api-reference/purchases/cancel _openapi-chip-collect POST /purchases/{id}/cancel/ If a Purchase is still payable, this request guarantees that it cannot be paid. # Capture a previously authorized payment Source: https://docs.chip-in.asia/chip-collect/api-reference/purchases/capture _openapi-chip-collect POST /purchases/{id}/capture/ Capture funds reserved for a Purchase (`status == hold`). You can place a `hold` (authenticate the payment) using `skip_capture == true` when creating the Purchase and ensuring your client submits the payment form. If this operation takes too long to be processed on the acquirer side - you will get a response with status code 200 and a Purchase object having `status` = `pending_capture` in body (you will receive a corresponding Webhook callback too for a `purchase.pending_capture` event). To be notified of a successful operation completion, please subscribe to `purchase.captured` callback event - it will deliver an updated Purchase with `status` = `paid`. If capture fails due to payment processing error, you will receive HTTP response code 400 with error code `purchase_capture_error`. In this case, to get more details about the error, you should perform a `GET /purchases/{id}/` request for the Purchase you tried to capture. In `transaction_data.attempts[]` array (newest element first) you'll find the corresponding attempt with error code and description in `.error` parameter. By default the full amount is captured, the `amount` body param is optional. # Charge a purchase using a saved token Source: https://docs.chip-in.asia/chip-collect/api-reference/purchases/charge _openapi-chip-collect POST /purchases/{id}/charge/ Charge a purchase using a `recurring_token` provided in the request body. Its value should be an `id` of a Purchase that has `is_recurring_token == true`. This purchase will be paid using the same method (e.g. same card) as the one used to pay the `recurring_token` purchase. If this operation takes too long to be processed on the acquirer side - you will get a response with status code 200 and a Purchase object having `status` = `pending_charge` in body (you will receive a corresponding Webhook callback too for a `purchase.pending_charge` event). To be notified of a successful operation completion, please subscribe to `purchase.paid` callback event - it will deliver an updated Purchase with `status` = `paid`. Alternatively, if charge fails, you will receive a `purchase.payment_failure` callback event. If recurring charge fails due to payment processing error, you will receive HTTP response code 400 with error code `purchase_charge_error`. In this case, to get more details about the error, you should perform a `GET /purchases/{id}/` request for the Purchase you tried to charge. In `transaction_data.attempts[]` array (newest element first) you'll find the corresponding attempt with error code and description in `.error` parameter. If the `recurring_token` you provide does not correspond to a valid, active token (e.g. it does not exist, refers to a Purchase that does not have `is_recurring_token == true`, or its token has been deleted), you will receive HTTP response code 400 with the following error body and **no webhook callback will be sent** (no charge was attempted on the acquirer side): ```json { "__all__": [ { "message": "Invalid or inactive recurring token!", "code": "invalid_recurring_token" } ] } ``` To resolve this, verify the token by [retrieving the Purchase](/chip-collect/api-reference/purchases/retrieve) referenced by `recurring_token` and checking that `is_recurring_token` is `true`, or list the active tokens for the client via the [List Recurring Tokens API](/chip-collect/api-reference/clients/list-recurring-tokens). # Create a purchase Source: https://docs.chip-in.asia/chip-collect/api-reference/purchases/create _openapi-chip-collect POST /purchases/ To run payments in your application use `POST /purchases/`, request to register payments and receive the checkout link (`checkout_url`). After the payment is processed, gateway will redirect the client back to your website (take note of `success_redirect`, `failure_redirect`). To set the price to the smallest unit possible, the value of the `price` field is expected to be specified in cents. For example, `price: 100` is equivalent to `RM 1.00` You have three options to check payment status: 1) use `success_callback` parameter of `Purchase` object. 2) use `GET /purchases//` request. 3) set up a Webhook using the UI or Webhook API to listen to `purchase.paid` or `purchase.payment_failure` event on your server. Using `skip_capture` flag allows you to separate the authentication and payment execution steps, allowing you to reserve funds on payer’s card account for some time. This flag can also enable preauthorization capability, allowing you to save the card without a financial transaction, if available. When the client agrees to store their card during a purchase, they can pay with a single click on subsequent purchases. Instead of a redirect you can also utilize Direct Post checkout: you can create an HTML `
` on your website with `method="POST"` and `action` pointing to `direct_post_url` of a created Purchase. You will also need to populate the form with `` elements for the card data fields. As a result, when a payer submits their card data, it will be posted straight to our system, allowing you to customize the checkout as you wish while your PCI DSS requirement is only raised to SAQ A-EP, as your system doesn't receive or process card data. For more details, see the documentation on Purchase's `direct_post_url` field. To pay for test Purchases, use `4444 3333 2222 1111` as the card number, `123` as CVC, any date/month greater than now as expiry and any (Latin) cardholder name. Any other card number, CVC, or expiry earlier than the current month will cause a test payment to fail. # Delete a recurring token associated with a purchase Source: https://docs.chip-in.asia/chip-collect/api-reference/purchases/delete-recurring-token _openapi-chip-collect POST /purchases/{id}/delete_recurring_token/ Sets `is_recurring_token` to `false`. You won't be able to use this Purchase's ID as a `recurring_token` anymore. The respective `ClientRecurringToken`, if any, will also be deleted. If this operation takes too long to be processed on the acquirer side, you will get a response with status code 200 and a corresponding Webhook callback for a `purchase.pending_recurring_token_delete` event. To be notified of a successful operation completion, please subscribe to the `purchase.recurring_token_deleted` callback event. # Mark a purchase as paid Source: https://docs.chip-in.asia/chip-collect/api-reference/purchases/mark-as-paid _openapi-chip-collect POST /purchases/{id}/mark_as_paid/ Sets the Purchase's status to `paid` and marks `purchase.marked_as_paid` as `true` to distinguish it. # Refund a paid purchase Source: https://docs.chip-in.asia/chip-collect/api-reference/purchases/refund _openapi-chip-collect POST /purchases/{id}/refund/ Will generate a Payment object and return it as a successful response. Optional `amount` argument can be included in the request body to request a partial refund. Consult `refund_availability` field on Purchase on details whether this Purchase can be refunded or not. If this operation takes too long to be processed on the acquirer side - you will get a response with status code 200 and a Purchase object having `status` = `pending_refund` in body (you will receive a corresponding Webhook callback too for a `purchase.pending_refund` event). To be notified of a successful operation completion, please subscribe to `payment.refunded` callback event - it will deliver a Payment generated by this refund. If refund fails due to payment processing error, you will receive HTTP response code 400 with error code `purchase_refund_error`. In this case, to get more details about the error, you should perform a `GET /purchases/{id}/` request for the Purchase you tried to refund. In `transaction_data.attempts[]` array (newest element first) you'll find the corresponding attempt with error code and description in `.error` parameter. # Release funds on hold Source: https://docs.chip-in.asia/chip-collect/api-reference/purchases/release _openapi-chip-collect POST /purchases/{id}/release/ Release funds reserved for a Purchase (`status == hold`). You can place a `hold` (authenticate the payment) using `skip_capture == true` when creating the Purchase and ensuring your client submits the payment form. If this operation takes too long to be processed on the acquirer side - you will get a response with status code 200 and a Purchase object having `status` = `pending_release` in body (you will receive a corresponding Webhook callback too for a `purchase.pending_release` event). To be notified of a successful operation completion, please subscribe to `purchase.released` callback event - it will deliver an updated Purchase with `status` = `released`. If fund release fails due to payment processing error, you will receive HTTP response code 400 with error code `purchase_release_error`. In this case, to get more details about the error, you should perform a `GET /purchases/{id}/` request for the Purchase you tried to release funds for. In `transaction_data.attempts[]` array (newest element first) you'll find the corresponding attempt with error code and description in `.error` parameter. # Resend an invoice Source: https://docs.chip-in.asia/chip-collect/api-reference/purchases/resend-invoice _openapi-chip-collect POST /purchases/{id}/resend_invoice/ Re-sends the invoice. # Retrieve a purchase Source: https://docs.chip-in.asia/chip-collect/api-reference/purchases/retrieve _openapi-chip-collect GET /purchases/{id}/ Retrieve a Purchase by its `id`. Use this endpoint to verify purchase status server-side (for example, after a return-URL redirect) rather than trusting client-side data. For card payments, the `transaction_data.extra` and `transaction_data.attempts[].extra` objects contain detailed card and authorization metadata (funding type, brand, masked PAN, expiry, authorization code, etc.) and per-attempt fee information. See [Card response metadata](/chip-collect/overview/direct-post/card-response-metadata) for the full field list and how to map a card purchase to a processing-cost tier. # Create a webhook Source: https://docs.chip-in.asia/chip-collect/api-reference/webhooks/create _openapi-chip-collect POST /webhooks/ Create a webhook using the values provided in the request body. The webhook enables the recipient to receive notifications via the callback URL when selected events occur. # Delete a webhook Source: https://docs.chip-in.asia/chip-collect/api-reference/webhooks/delete _openapi-chip-collect DELETE /webhooks/{id}/ # List all webhooks Source: https://docs.chip-in.asia/chip-collect/api-reference/webhooks/list _openapi-chip-collect GET /webhooks/ # Partially update a webhook Source: https://docs.chip-in.asia/chip-collect/api-reference/webhooks/partial-update _openapi-chip-collect PATCH /webhooks/{id}/ # Retrieve a webhook Source: https://docs.chip-in.asia/chip-collect/api-reference/webhooks/retrieve _openapi-chip-collect GET /webhooks/{id}/ # Update a webhook Source: https://docs.chip-in.asia/chip-collect/api-reference/webhooks/update _openapi-chip-collect PUT /webhooks/{id}/ # Authentication Source: https://docs.chip-in.asia/chip-collect/overview/authentication How CHIP Collect authenticates API requests and webhook callbacks. CHIP Collect uses **two** distinct authentication mechanisms, and they cover different concerns: | Concern | Mechanism | | ------------------------------------------------------------- | ----------------------------------------------------- | | Authenticating **outbound** API requests from your server | Bearer API key (`Authorization: Bearer `) | | Verifying **inbound** webhook and success callback deliveries | RSA signature in the `X-Signature` header | The two are deliberately separate so that webhook receivers don't need access to your secret API key. ## 1. Authenticating API requests Every request to the CHIP Collect API must include your **Secret Key** as a bearer token in the `Authorization` header: ``` Authorization: Bearer ``` You can generate and manage your Secret Keys in the [CHIP merchant portal](https://portal.chip-in.asia/collect/developers/api-keys). ### Example ```bash theme={null} curl "https://gate.chip-in.asia/api/v1/purchases/" \ -H "Authorization: Bearer " ``` ### Brand ID Some endpoints also require a **Brand ID** as part of the request body or as a query parameter. You can find your Brand ID in the [merchants portal](https://portal.chip-in.asia/collect/developers/brands). ### Test vs live keys The merchant portal issues separate keys for **test mode** and **live mode**. Test keys only work against test purchases and never move real money. Always use a test key while integrating. Never expose your Secret Key in client-side code, mobile apps, or public repositories. The Secret Key can create real purchases and issue refunds. ## 2. Verifying webhook signatures CHIP signs every webhook delivery and success callback with an **asymmetric (public-key) signature** so the receiver can verify the payload was sent by CHIP and not by an attacker. See [Webhook signatures](/chip-collect/overview/webhook-signatures) for the full algorithm, code samples, and how to obtain the public key. ## 3. CHIP Send (payouts) authentication CHIP Send uses a different scheme: every request is signed with an HMAC-SHA512 checksum derived from a per-request `epoch` and the API Key. See the [CHIP Send introduction](/chip-send/api-reference/introduction) for the full algorithm. # Callbacks Source: https://docs.chip-in.asia/chip-collect/overview/callbacks Two methods for defining asynchronous callbacks are supported - `Purchase` success callbacks and webhooks. ## Purchase success callbacks `Purchase` success callbacks are defined by providing a target URL in the `success_callback` field on `Purchase` creation (see [POST /purchases/](/chip-collect/api-reference/purchases/create)). The system will generate a callback when: * a `Purchase` with `skip_capture=false` is successfully paid * a `Purchase` with `skip_capture=true` is successfully captured (see [POST /purchases/\{id}/capture/](/chip-collect/api-reference/purchases/capture)) * a `Purchase` is successfully paid using a recurring token (see [POST /purchases/\{id}/charge/](/chip-collect/api-reference/purchases/charge)) These callbacks pass a JSON-encoded `Purchase` as their payload. The payload represents a snapshot of the state of the `Purchase` when the event was created. The payload will include an `event_type` field to indicate which specific event (see [Event schema](/chip-collect/api-reference/webhooks/create)) triggered the callback. The payload is signed using a company-wide key pair. You can obtain the public key with `GET /public_key/`. See the `Authentication` section below for more details. ## Webhooks For creating and modifying webhooks, see the Webhook [CRUD API specification](/chip-collect/api-reference/webhooks/create). `Webhook` callback payloads are signed using a dedicated key pair. You can obtain the public key from `Webhook.public_key`. See the [Authentication](/chip-collect/overview/authentication) section below for more details. ## Delivery protocol When a callback is not successfully delivered (received by the target server and responded to with a 200 series HTTP response code), the system will make up to 8 additional attempts at exponentially increasing intervals between attempts. No further delivery attempts will be made if the callback is not successfully delivered 36 hours after triggering. Please note that due to the asynchronous nature of network requests, it is possible for a callback delivery confirmation (HTTP response with a 200 series status code) to not properly arrive from the callback's target server. Therefore it is possible in case of severe network faults for the target server to receive a callback, respond to it with a 200 series HTTP status code and then receive the same callback after an interval. Callback deliveries are guaranteed to be sequential to events triggered on their source objects. For example, when registering webhooks for both the `purchase.created` and `purchase.paid` events, there will be no `purchase.paid` callbacks for this `Purchase` until all `purchase.created` callbacks for this `Purchase` are successfully delivered. # Changelog Source: https://docs.chip-in.asia/chip-collect/overview/changelog Notable changes to the CHIP Collect API and this documentation. * Documented Crypto Coin as a supported payment method in the [payment method whitelist](/chip-collect/overview/what-we-offer), the "common values" list on [Set a default payment method](/chip-collect/overview/direct-post/active-payment-method) (`crypto_coin`), and the Skip-Payment-Page flow (`?preferred=crypto_coin`). Crypto Coin is a redirect method: no customer data entry is needed on the payment page, so `?preferred=crypto_coin` takes the payer straight to the crypto checkout. * Raised the recommended safe `amount` query parameter for [`GET /payment_methods/`](/chip-collect/api-reference/payment-methods/list) from RM 2 (`200`) to RM 10 (`1000`). The previous value fell below Atome's minimum and silently filtered it out of the response. The new value covers every currently supported payment method (Atome RM 10, FPX B2C RM 1, FPX B2B1 RM 2, DuitNow QR RM 0.15). * Updated the ShopeePay Skip-Payment-Page value in the [E-Wallet reference](/chip-collect/overview/direct-post/e-wallet) from `razer_shopeepay` to `shopee_pay`. ShopeePay is the only e-wallet that does not take a `razer_bank_code` parameter, so the URL is now just `?preferred=shopee_pay`. * Simplified the Duitnow QR Skip-Payment-Page page and the "common values" list on [Set a default payment method](/chip-collect/overview/direct-post/active-payment-method) to use only `dnqr`. The `duitnow_qr` alias is no longer surfaced in the bypass-payment-page or default-payment-method docs (the API still accepts it). * Added a [Quickstart](/chip-collect/overview/quickstart) page. * Rewrote [Authentication](/chip-collect/overview/authentication) to cover Bearer tokens, webhook signatures, and the CHIP Send HMAC scheme in one place. * Added a dedicated [Webhook signatures](/chip-collect/overview/webhook-signatures) page with Node, PHP, and Python verification examples. * Added an [Errors](/chip-collect/overview/errors) reference. * Promoted the [AI agent integration guide](/chip-collect/overview/vibe-coding-guide) to a top-level sidebar group and linked the machine-readable [`/llms.txt`](/llms.txt) conventions from it. * Added MBSB Bank (code `MBSB001`) to the [FPX B2B1 bank code list](/chip-collect/overview/direct-post/fpx#fpx-b2b1). * Renamed the webhook `partially-update-a-webhook-by-id` endpoint page to `partial-update` to match the rest of the naming convention. * Added a masked [example response](/chip-collect/api-reference/purchases/create) to the OpenAPI `POST /purchases/` operation. * Added a new page documenting the `?active={payment_method}` query parameter on the checkout URL, which pre-selects a payment method on the default page without skipping it (different from `?preferred=`, which *skips* the page). Linked to it from [FPX](/chip-collect/overview/direct-post/fpx), [DuitNow QR](/chip-collect/overview/direct-post/duitnow-qr), and [E-Wallet](/chip-collect/overview/direct-post/e-wallet). * Documented the `?active=` checkout URL query parameter in the OpenAPI spec and added a "Set a default payment method" page that explains the difference between `?active=` and `?preferred=`. * Documented the `invalid_recurring_token` error returned by the charge API when a recurring token has been revoked or expired. * Removed `american_express` from the `card_methods` list in the OpenAPI spec. * Re-included `mastercard` and `maestro` in the `card_methods` example. * Rewrote the [CHIP Send introduction](/chip-send/api-reference/introduction) with a full request example and troubleshooting table. * Cleaned up example emails across the docs (all now use `example.com` placeholders). * Added the `purchase.settled` event and a few other missing webhook events to the OpenAPI schema. * Added the CHIP Send [Webhook validation](/chip-send/api-reference/webhooks/validation) and [Delivery protocol](/chip-send/api-reference/webhooks/delivery-protocol) pages. * Served raw OpenAPI specs at `/openapi/` for direct consumption by coding agents. * Published `/llms.txt` with the canonical URL conventions and authentication rules. * Added an auto-generated PR summary workflow. # Set a default payment method Source: https://docs.chip-in.asia/chip-collect/overview/direct-post/active-payment-method Pre-select a payment method on the checkout page while keeping all methods available ## Instruction Once a purchase is created, a `checkout_url` will be provided to redirect the buyer to the payment page. To pre-select a specific payment method on the default payment page — so the customer's chosen method is highlighted first while the other methods remain visible — append the following parameter to the URL: `?active={payment_method}` `{payment_method}` must be one of the method names returned by [`GET /payment_methods/`](/chip-collect/api-reference/payment-methods/list). Common values include `fpx`, `fpx_b2b1`, `dnqr`, `shopee_pay`, and `crypto_coin`. This is different from [`?preferred={payment_method}`](/chip-collect/overview/direct-post/intro), which **skips** the default payment page entirely and forces the customer straight into the chosen method's checkout. Use `?active=` when you want to suggest a method but still let the customer pick a different one. ## Example ```text theme={null} https://gate.chip-in.asia/p/{purchase_id}/?active=dnqr ``` `{purchase_id}` is the purchase ID from the `checkout_url` field returned in the [purchase response](/chip-collect/api-reference/purchases/create). Use your own purchase's ID — example URLs pointing at someone else's purchase will not work. # Card (VISA/Mastercard) Source: https://docs.chip-in.asia/chip-collect/overview/direct-post/card Bypass the payment page for an optimized user experience ## Instruction Once you create a purchase, you will receive a direct\_post\_url. This URL is used to submit an HTML form POST with the necessary card data. ## Example Code ```html theme={null} ``` ## Real-World scenario This is how it appears in a real-world scenario: See also: [Card response metadata](/chip-collect/overview/direct-post/card-response-metadata) — the card and fee fields exposed on a paid card Purchase. # Card response metadata Source: https://docs.chip-in.asia/chip-collect/overview/direct-post/card-response-metadata Card and fee fields exposed on a paid card Purchase: card_type, card_brand, masked_pan, and fee_amount. ## Overview For a `paid` card Purchase, the response includes card and authorization metadata under `transaction_data.extra` and `transaction_data.attempts[].extra`, plus fee information under `payment.fee_amount` and at the per-attempt level. This page documents those fields. The same `transaction_data` shape is delivered in the `purchase.paid` webhook payload, so everything on this page applies to webhook consumers as well. ## Card fields The following fields appear in `transaction_data.extra` (and again in `transaction_data.attempts[].extra` for each attempt) when a card payment is made. All fields are read-only and reported by the acquirer. | Field | Type | Example | Notes | | ----------------------------- | ------- | -------------------- | -------------------------------------------------------------------------------------------------------------- | | `card_type` | string | `"credit"` | Funding type. Values: `credit`, `debit`, `prepaid`. Reported by the acquirer; may be missing for some issuers. | | `card_brand` | string | `"mastercard"` | Card scheme. For card payments, this is the same value as `transaction_data.payment_method`. | | `masked_pan` | string | `"444433******1111"` | Last 4 digits of the PAN. **The first 6 (BIN) is not included** — only the last 4 are exposed. | | `expiry_month` | integer | `12` | 1–12. | | `expiry_year` | integer | `28` | 2-digit year. | | `cardholder_name` | string | `"Ahmad Razali"` | Name on the card as supplied during authorization. | | `card_issuer` | string | `"cimb bank berhad"` | Issuing bank name. | | `card_issuer_country` | string | `"MY"` | ISO 3166-1 alpha-2 code of the issuing country. | | `card_category` | string | `"PERSONAL"` | Typical values: `PERSONAL`, `CORPORATE`. | | `RRN` | string | `"123456789012"` | Retrieval Reference Number from the acquirer. | | `authorization_approval_code` | string | `"AB1234"` | Authorization approval code. | | `descriptor` | string | `"YOUR BRAND NAME"` | Merchant descriptor sent to the issuer (truncated to fit on the cardholder's statement). | | `three_d_secure` | boolean | `true` | Whether 3D Secure authentication was performed for this transaction. | ## Fee fields Fees are exposed at two levels: per-Purchase and per-attempt. All amounts are in the smallest currency unit (e.g. sen for MYR, cents for USD). | Field | Type | Notes | | -------------------------------------------- | ------- | ----------------------------------------------------------------------- | | `payment.fee_amount` | integer | Total fee for the Purchase. The canonical "what was charged" field. | | `transaction_data.attempts[].fee_amount` | integer | Fee for that specific attempt. Useful when an authorization is retried. | | `transaction_data.attempts[].markup_amount` | integer | Markup (if any) on top of the processor fee for that attempt. | | `transaction_data.attempts[].processing_fee` | integer | Raw processor fee for that attempt, before any markup. | The top-level `payment.fee_amount` equals the `fee_amount` of the most recent successful attempt. For a single-attempt `paid` Purchase, all four values describe the same transaction. # Crypto Coin Source: https://docs.chip-in.asia/chip-collect/overview/direct-post/crypto-coin Skip the payment page and send the payer straight to the crypto coin checkout ## Direct Post Append `?preferred=crypto_coin` to the `checkout_url` to **skip** the default payment page and take the payer straight to the crypto coin checkout: ```text theme={null} https://gate.chip-in.asia/p/{purchase_id}/?preferred=crypto_coin ``` `{purchase_id}` is the purchase ID from the `checkout_url` field returned in the [purchase response](/chip-collect/api-reference/purchases/create). Use your own purchase's ID — example URLs pointing at someone else's purchase will not work. Crypto Coin is a redirect method: no customer data entry is needed on the payment page, so `?preferred=crypto_coin` takes the payer straight to the crypto checkout. If you want to pre-select Crypto Coin on the default payment page while still showing the customer all available options, use [`?active=crypto_coin`](/chip-collect/overview/direct-post/active-payment-method) instead. # Duitnow-QR Source: https://docs.chip-in.asia/chip-collect/overview/direct-post/duitnow-qr Bypass the payment page for an optimized user experience ## Instruction Once a purchase is created, a checkout\_url will be provided to redirect the buyer to the payment page. To automatically navigate the customer to Duitnow QR, you should append the following parameter to the URL: `?preferred=dnqr` ## Example ```text theme={null} https://gate.chip-in.asia/p/{purchase_id}/?preferred=dnqr ``` `{purchase_id}` is the purchase ID from the `checkout_url` field returned in the [purchase response](/chip-collect/api-reference/purchases/create). Use your own purchase's ID — example URLs pointing at someone else's purchase will not work. ## See also * [Set a default payment method](/chip-collect/overview/direct-post/active-payment-method) — to pre-select this method on the default payment page without skipping it. # E-Wallet Source: https://docs.chip-in.asia/chip-collect/overview/direct-post/e-wallet Bypass the payment page for an optimized user experience ## Instruction Once a purchase is created, a checkout\_url will be provided to redirect the buyer to the payment page. To automatically navigate the customer to a specific e-wallet, you should append the following parameters to the URL: `?preferred=&razer_bank_code=` ## E-Wallet List For the `` the values are listed below. Most wallets also require a `` passed as `razer_bank_code`; ShopeePay is the only exception and does not take a `razer_bank_code` parameter. | E-Wallet Name | Value (Wallet) | Value (Wallet Code) | | ------------------- | ---------------- | ------------------- | | GrabPay | razer\_grabpay | GrabPay | | Touch ‘n Go eWallet | razer\_tng | TNG-EWALLET | | ShopeePay | shopee\_pay | — | | Maybank QR | razer\_maybankqr | MB2U\_QRPay-Push | | Atome | razer\_atome | Atome | The full URL form for ShopeePay is therefore just `?preferred=shopee_pay` (no `&razer_bank_code=...` suffix). ## Example ```text theme={null} https://gate.chip-in.asia/p/{purchase_id}/?preferred=razer_grabpay&razer_bank_code=GrabPay ``` `{purchase_id}` is the purchase ID from the `checkout_url` field returned in the [purchase response](/chip-collect/api-reference/purchases/create). Use your own purchase's ID — example URLs pointing at someone else's purchase will not work. ## See also * [Set a default payment method](/chip-collect/overview/direct-post/active-payment-method) — to pre-select this method on the default payment page without skipping it. # FPX Source: https://docs.chip-in.asia/chip-collect/overview/direct-post/fpx Bypass the payment page for an optimized user experience ## Instruction When you create a purchase, you’ll be given a checkout\_url to redirect the buyer to the payment page. To automatically redirect customers to a specific bank, you need to add the following parameters to the URL: `?preferred=&fpx_bank_code=` You will have to set two options. The first one is `` and the second one is ``. ## FPX Type List For the `` option, the available choices are: | Type | Value | | -------- | ---------------------------------------------- | | FPX B2C | (Standard FPX) fpx | | FPX B2B1 | (FPX for Business/Corporate Account) fpx\_b2b1 | ## Bank Code List As for the ``, the following options are available: | Bank Name | Value (Bank Code) | | ------------------------ | ----------------- | | Affin Bank | ABB0233 | | Alliance Bank (Personal) | ABMB0212 | | AGRONet | AGRO01 | | AmBank | AMBB0209 | | Bank Islam | BIMB0340 | | Bank Muamalat | BMMB0341 | | Bank Rakyat | BKRM0602 | | Bank Of China | BOCM01 | | BSN | BSN0601 | | CIMB Bank | BCBB0235 | | Hong Leong Bank | HLB0224 | | HSBC Bank | HSBC0223 | | KFH | KFH0346 | | Maybank2E | MBB0228 | | Maybank2u | MB2U0227 | | MBSB Bank | MBSB001 | | OCBC Bank | OCBC0229 | | Public Bank | PBB0233 | | RHB Bank | RHB0218 | | Standard Chartered | SCB0216 | | UOB Bank | UOB0226 | | Bank Name | Value (Bank Code) | | -------------------------- | ----------------- | | AFFINMAX | ABB0235 | | Alliance Bank (Business) | ABMB0213 | | AGRONetBIZ | AGRO02 | | AmBank | AMBB0208 | | Bank Islam | BIMB0340 | | Bank Muamalat | BMMB0342 | | BNP Paribas | BNP003 | | CIMB Bank | BCBB0235 | | Citibank Corporate Banking | CIT0218 | | Deutsche Bank | DBB0199 | | Hong Leong Bank | HLB0224 | | HSBC Bank | HSBC0223 | | Bank Rakyat | BKRM0602 | | KFH | KFH0346 | | Maybank2E | MBB0228 | | MBSB Bank | MBSB001 | | OCBC Bank | OCBC0229 | | Public Bank | PBB0233 | | Public Bank PB enterprise | PBB0234 | | RHB Bank | RHB0218 | | Standard Chartered | SCB0215 | | UOB Regional | UOB0228 | ## Examples Here is a comprehensive example of a URL for the FPX payment option: ```text theme={null} https://gate.chip-in.asia/p/{purchase_id}/?preferred=fpx&fpx_bank_code=MB2U0227 ``` FPX B2B1 uses the same format with `fpx_b2b1` and the B2B1 bank code: ```text theme={null} https://gate.chip-in.asia/p/{purchase_id}/?preferred=fpx_b2b1&fpx_bank_code=MB2U0228 ``` `{purchase_id}` is the purchase ID from the `checkout_url` field returned in the [purchase response](/chip-collect/api-reference/purchases/create). Use your own purchase's ID — example URLs pointing at someone else's purchase will not work. ## Real-World scenario This is how it appears in a real-world scenario: ## See also * [Set a default payment method](/chip-collect/overview/direct-post/active-payment-method) — to pre-select this method on the default payment page without skipping it. # Introduction Source: https://docs.chip-in.asia/chip-collect/overview/direct-post/intro Bypass the payment page for an optimized user experience Integrating with a payment gateway can often pose challenges for user experience due to discrepancies in interface design. Typically, a payment page provided by the gateway is offered as a pre-designed, non-customizable format. This guide will steer you through the process of skipping the CHIP payment page for both FPX and Card payments. ## Default Payment Page Default payment page By default, clicking the `checkout-url` redirects the user to the page with the bank list. However, CHIP provides the flexibility to let a `checkout-url` redirect the user directly to the bank page by appending query parameters to the URL. ## Direct Post This link redirects the user directly to the Maybank FPX page: ```text theme={null} https://gate.chip-in.asia/p/{purchase_id}/?preferred=fpx&fpx_bank_code=MB2U0227 ``` `{purchase_id}` is the purchase ID from the `checkout_url` field returned in the [purchase response](/chip-collect/api-reference/purchases/create). Use your own purchase's ID — example URLs pointing at someone else's purchase will not work. ## Differences between Checkout URL and Direct Post URL Both use the same URL, except direct post appends query parameters to convert the checkout URL into a direct post URL: ```text theme={null} Default Payment Page - https://gate.chip-in.asia/p/{purchase_id}/ Direct Post - https://gate.chip-in.asia/p/{purchase_id}/?preferred=fpx&fpx_bank_code=MB2U0227 ``` ## Set a default payment method The `?preferred={payment_method}` parameter above **skips** the default payment page. If you want to pre-select a method on the default payment page while still showing the customer all available options, use [`?active={payment_method}`](/chip-collect/overview/direct-post/active-payment-method) instead. ## Payment Method List Payment methods available for direct post: * [FPX](/chip-collect/overview/direct-post/fpx) * [Card](/chip-collect/overview/direct-post/card) * [E-Wallet](/chip-collect/overview/direct-post/e-wallet) * [Duitnow QR](/chip-collect/overview/direct-post/duitnow-qr) * [Crypto Coin](/chip-collect/overview/direct-post/crypto-coin) # Errors Source: https://docs.chip-in.asia/chip-collect/overview/errors Common CHIP Collect error codes and how to handle them. CHIP Collect returns errors in two places: * **HTTP responses** to your API requests. Errors come back as 4xx (client errors) or 5xx (server errors) with a JSON body containing a `code` and a human-readable `message`. * **Webhook events** for things that happen after the initial request, such as `purchase.payment_failure` with an `error_code` field in the payload. This page lists the most common codes and the recommended next step. Always log the full response so you can debug from the raw error. ## HTTP error responses | Status | `code` | Meaning | Recommended action | | ------ | ------------------------- | ------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------- | | 400 | `invalid_request` | One or more required fields are missing or malformed. | Inspect the `message` field for the offending field. Validate inputs client-side before sending. | | 400 | `invalid_recurring_token` | The recurring token is unknown, revoked, or expired. | Re-prompt the buyer for a new card. Do not retry the charge. | | 401 | `unauthorized` | API key is missing, malformed, or invalid. | Verify the `Authorization` header is `Bearer ` and the key is from the right brand. | | 403 | `forbidden` | The API key does not have access to this resource. | Confirm the key has the required scopes in the merchant portal. | | 404 | `not_found` | The purchase, client, or webhook does not exist. | Double-check the ID. Records in CHIP Collect are not deleted by default. | | 409 | `conflict` | The operation is not valid for the resource's current state. | Inspect the resource's `status` field. For example, you cannot capture an already-captured purchase. | | 422 | `unprocessable_entity` | The request is well-formed but semantically invalid. | Read the `message` field. Common cause: amount exceeds the remaining capturable balance. | | 429 | `rate_limited` | Too many requests in a short window. | Back off and retry with jitter. Honor any `Retry-After` header if present. | | 500 | `internal_error` | Something went wrong on CHIP's side. | Safe to retry with idempotency. If it persists, contact [support@chip-in.asia](mailto:support@chip-in.asia). | | 502 | `bad_gateway` | Upstream provider (bank, card network) returned an error. | Do not retry immediately. Wait for the corresponding webhook before acting. | | 503 | `service_unavailable` | Planned maintenance or temporary outage. | Retry with exponential backoff. Status page: [https://status.chip-in.asia](https://status.chip-in.asia) (if available). | ## Webhook `error_code` values These appear on `purchase.payment_failure` and `purchase.cancelled` events. | `error_code` | Meaning | Recommended action | | ------------------------- | ------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | | `insufficient_funds` | The buyer's account did not have enough funds at the time of payment. | Ask the buyer to use a different payment method. Do not auto-retry. | | `payment_declined` | The buyer's bank or card issuer declined the transaction. | Ask the buyer to contact their bank or use a different method. | | `payment_cancelled` | The buyer explicitly cancelled the payment on the bank or card page. | Treat as a soft decline. Allow the buyer to retry. | | `payment_expired` | The buyer did not complete payment within the expiry window. | Issue a new purchase if the buyer still wants to pay. | | `invalid_signature` | (Only when re-validating locally) The webhook signature could not verify. | Check that you are reading the **raw** request body and using the public key for the right webhook. | | `invalid_recurring_token` | The recurring token has been deleted or revoked. | Re-prompt the buyer to enrol a new card. | ## Idempotency For write operations (create, charge, capture, refund, etc.), the same request body sent twice with the same `Idempotency-Key` header will return the same result without performing the action twice. Always generate an `Idempotency-Key` server-side for non-idempotent operations so a network retry does not create duplicates. ## When to contact support Open a ticket to [support@chip-in.asia](mailto:support@chip-in.asia) if: * The same error code is returned for more than 1% of your traffic. * You receive an HTTP 5xx response that retries do not clear. * A webhook `event_id` is not delivered within 5 minutes of the corresponding `purchase.paid` event. Always include the `purchase_id`, the request timestamp, and the full response body. # CHIP Collect API Source: https://docs.chip-in.asia/chip-collect/overview/introduction Welcome to the CHIP Collect documentation All the endpoints below have a prefix of `https://gate.chip-in.asia/api/v1/`
(e.g. `POST https://gate.chip-in.asia/api/v1/purchases/`). You will need your API key and Brand ID that you can obtain in the Developers section in your account. Please use this key as a bearer token in the Authorization header included in every request: `Authorization: Bearer `. You can generate and manage your API keys here:
[https://portal.chip-in.asia/collect/developers/api-keys](https://portal.chip-in.asia/collect/developers/api-keys) Your Brand ID is required for certain endpoints. You can find it here:
[https://portal.chip-in.asia/collect/developers/brands](https://portal.chip-in.asia/collect/developers/brands) Before starting the development, we recommend checking out the list of ready-to-go connectors to the popular platforms we’ve already built for you. It might save you some precious time if you use one of these to develop your project. Plugins: [WooCommerce](https://gate.chip-in.asia/apis/plugins/WooCommerce%20v3.5+), [Gravity Forms](https://gate.chip-in.asia/apis/plugins/Gravity%20Forms), [OpenCart](https://gate.chip-in.asia/apis/plugins/OpenCart%20v3.0+), [Magento](https://gate.chip-in.asia/apis/plugins/Magento%20v2.0+), [PrestaShop](https://gate.chip-in.asia/apis/plugins/PrestaShop%20v1.7+) Libraries: [PHP](https://gate.chip-in.asia/apis/libraries/PHP), [Java](https://gate.chip-in.asia/apis/libraries/Java), [C#](https://gate.chip-in.asia/apis/libraries/C%23), [Node.js](https://gate.chip-in.asia/apis/libraries/Node.js) SDKs: [iOS](https://gate.chip-in.asia/apis/sdks/iOS), [Android](https://gate.chip-in.asia/apis/sdks/Android) # Custom Source: https://docs.chip-in.asia/chip-collect/overview/online-purchases/custom ## Custom Payment Flow While we've outlined our standard offerings, we also provide custom payment flows tailored to your needs. Please contact the CHIP team for more details and a free consultation. # Payment Link Source: https://docs.chip-in.asia/chip-collect/overview/online-purchases/payment-link ## Example Use Cases | | | --------------------------------------------- | | Selling online products through the web | | Charging for freelance or consulting services | | Accepting donations or one-time payments | ## API Required 1. [Create Purchases API](/chip-collect/api-reference/purchases/create) ## Example JSON Payload Alan buys a mug for RM 10.00 1. Create a purchase using the [Purchases API](/chip-collect/api-reference/purchases/create)
- get `checkout_url` from the response body to be used as a payment link ```js theme={null} { "client": { "email": "customer@example.com", "full_name": "Alan" }, "purchase": { "products": [ { "name": "Mug", "price": 1000 } ] }, "brand_id": "<>" } ``` ## Testing Integration It’s possible to test-drive all checkouts using a test Purchase. To test a successful payment, you can use the following card numbers: * 4444 3333 2222 1111 - non-3D Secure card * 5555 5555 5555 4444 - 3D Secure card For both cards, please use: * any cardholder name * any expiry no earlier than the current month/year * CVC = 123 To test a failed payment, please change the CVC or expiration date. When using a 3D Secure enrolled card in S2S checkout, an incorrect CVC will trigger an authorization failure on the S2S callback step (after the customer returns from test ACS). Using a wrong expiry date emulates data validation failure and results in immediate error before that step. # Pre-Authorization Source: https://docs.chip-in.asia/chip-collect/overview/online-purchases/pre-auth ## Example Use Cases | | | ------------------- | | Rooms Reservations | | Rental Services | | Pay-Per-Use Parking | ## API Required 1. [Create Purchase API](/chip-collect/api-reference/purchases/create) 2. [Capture Payment API](/chip-collect/api-reference/purchases/capture) ## Example JSON Payload Alan rents a hotel room that costs RM 200.00, with an additional security deposit of RM 100.00. 1. Create a reservation fee using the [Purchases API](/chip-collect/api-reference/purchases/create)
- get `checkout_url` from the response body to be used as a payment link
- get `id` from the response body to be used as a token
*Note: Alan's reservation fee must be paid in order for the token to be usable.* ```js theme={null} { "client": { "email": "customer@example.com", "full_name": "Alan" }, "purchase": { "products": [ { "name": "Room fee", "price": 20000 }, { "name": "Security deposit", "price": 10000 } ] }, "skip_capture" : true, "brand_id": "<>" } ``` 2. Charge the funds using [Capture Payment API](/chip-collect/api-reference/purchases/capture) * If the full security deposit is to be charged, capture the total amount: ```js theme={null} { "amount" : 30000 } ``` * If the security deposit is not being charged, capture only the room fee portion: ```js theme={null} { "amount" : 20000 } ```
*Note: Partial capture is possible as any remainder of uncharged funds will be released automatically* ## Skip Capture Parameter When set to true, the `skip_capture` parameter authorizes the payment without capturing the funds, effectively holding the amount in the customer's account without withdrawing it. ## Testing Integration It’s possible to test-drive all checkouts using a test Purchase. To test a successful payment, you can use the following card numbers: * 4444 3333 2222 1111 - non-3D Secure card * 5555 5555 5555 4444 - 3D Secure card For both cards, please use: * any cardholder name * any expiry no earlier than the current month/year * CVC = 123 To test a failed payment, please change the CVC or expiration date. When using a 3D Secure enrolled card in S2S checkout, an incorrect CVC will trigger an authorization failure on the S2S callback step (after the customer returns from test ACS). Using a wrong expiry date emulates data validation failure and results in immediate error before that step. # Subscriptions Source: https://docs.chip-in.asia/chip-collect/overview/online-purchases/subscription ## Example Use Cases | | | ------------------------------------------ | | Recurring Memberships | | SaaS (Software-as-a-Service) Products | | Educational Content & Courses Subscription | ## API Required 1. [Create Purchase API](/chip-collect/api-reference/purchases/create) 2. [Charge Token API](/chip-collect/api-reference/purchases/charge) ## Example Cases 1. [Subscription With Free Trial](/chip-collect/overview/online-purchases/subscription#1-subscription-with-free-trial) 2. [Subscription With Registration Fee](/chip-collect/overview/online-purchases/subscription#2-subscription-with-registration-fee) ## 1) Subscription With Free Trial Alan subscribes to a gym membership with a free trial and a monthly subscription of RM 5.00. 1. Create a registration fee using the [Purchases API](/chip-collect/api-reference/purchases/create)
- get `checkout_url` from the response body to be used as a payment link
- get `id` from the response body to be used as a token
*Note: Alan's card must be validated in order for the token to be usable.* ```js theme={null} { "client": { "email": "customer@example.com", "full_name": "Alan" }, "purchase": { "products": [ { "name": "Free Trial", "price": 0 } ] }, "skip_capture" : true, "brand_id": "<>" } ``` 2. Create a monthly subscription using the [Purchases API](/chip-collect/api-reference/purchases/create)
- get `id` from the response body to be used as a payment link ```js theme={null} { "client": { "email": "customer@example.com", "full_name": "Alan" }, "purchase": { "products": [ { "name": "Subscription fee", "price": 500 } ] }, "brand_id": "<>" } ``` 3. Charge subscription fee id with registration fee id using [Charge token API](/chip-collect/api-reference/purchases/charge)
Example API : .../purchases/`monthly_subscription_fee_id`/charge/ ```js theme={null} { "recurring_token" : "<>" } ``` ## 2) Subscription With Registration Fee Alan subscribes to a gym membership with a registration fee of RM 20.00 and a monthly subscription of RM 5.00. 1. Create a registration fee using the [Purchases API](/chip-collect/api-reference/purchases/create)
- get `checkout_url` from the response body to be used as a payment link
- get `id` from the response body to be used as a token
*Note: Alan's registration fee must be paid in order for the token to be usable.* ```js theme={null} { "client": { "email": "customer@example.com", "full_name": "Alan" }, "purchase": { "products": [ { "name": "Registration Fee", "price": 2000 } ] }, "payment_method_whitelist" : ["visa", "mastercard", "maestro"], "force_recurring" : true, "brand_id": "<>" } ``` 2. Create a monthly subscription using the [Purchases API](/chip-collect/api-reference/purchases/create)
- get `id` from the response body to be used as a payment link ```js theme={null} { "client": { "email": "customer@example.com", "full_name": "Alan" }, "purchase": { "products": [ { "name": "Subscription fee", "price": 500 } ] }, "brand_id": "<>" } ``` 3. Charge subscription fee id with registration fee id using [Charge token API](/chip-collect/api-reference/purchases/charge)
Example API : .../purchases/`monthly_subscription_fee_id`/charge/ ```js theme={null} { "recurring_token" : "<>" } ``` ## Testing Integration It’s possible to test-drive all checkouts using a test Purchase. To test a successful payment, you can use the following card numbers: * 4444 3333 2222 1111 - non-3D Secure card * 5555 5555 5555 4444 - 3D Secure card For both cards, please use: * any cardholder name * any expiry no earlier than the current month/year * CVC = 123 To test a failed payment, please change the CVC or expiration date. When using a 3D Secure enrolled card in S2S checkout, an incorrect CVC will trigger an authorization failure on the S2S callback step (after the customer returns from test ACS). Using a wrong expiry date emulates data validation failure and results in immediate error before that step. ## FAQ Frequently asked questions regarding subscriptions. 1. Does CHIP handle the automatic renewal of subscriptions?
No, CHIP does not handle automatic renewal. What CHIP offers is the ability to save and charge a customer's saved card. The automatic renewal logic must be implemented on the merchant's side, for example using a cron job or other scheduling mechanism. 2. What happens if I accidentally charge the customer's card twice?
Once the payment link is paid, any subsequent payment attempt will be blocked. As a result, the likelihood of a double charge issue is extremely low. 3. What is the token tied to?
The token is tied to `brand_id`. 4. How is the token referenced?
The token uses `customer_email` as a reference. 5. Where can I see my customer's tokens?
You can list the tokens for a customer using the [List Token API](/chip-collect/api-reference/clients/list-recurring-tokens). 6. How do I delete the token?
You can delete a token using the [Delete Token API](/chip-collect/api-reference/purchases/delete-recurring-token). # Quickstart Source: https://docs.chip-in.asia/chip-collect/overview/quickstart Create your first CHIP Collect test purchase in under 5 minutes. This walkthrough creates a real test purchase against the CHIP Collect sandbox and returns a working checkout URL. By the end you'll have a complete end-to-end payment flow you can copy into your own application. Use a **test mode** API key for the entire quickstart. Live keys are not required and should not be used here. You can generate a test key from the [CHIP merchant portal](https://portal.chip-in.asia/collect/developers/api-keys). ## Prerequisites * A CHIP merchant account * A test mode **Secret Key** and **Brand ID** from the merchant portal * `curl` (or any HTTP client) ## 1. Create a test purchase Send a `POST` request to the CHIP Collect API to create a purchase. The response includes a `checkout_url` that you can open in a browser to complete a fake payment. ```bash theme={null} curl -X POST "https://gate.chip-in.asia/api/v1/purchases/" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "brand_id": "", "client": { "email": "buyer@example.com" }, "purchase": { "products": [ { "name": "Test Product", "price": 100 } ] }, "success_redirect": "https://example.com/success", "failure_redirect": "https://example.com/failure" }' ``` A successful response looks like: ```json theme={null} { "id": "8c3a2f4e-6b1d-4a5e-9c8b-2d3f1e4a5b6c", "status": "created", "checkout_url": "https://gate.chip-in.asia/p/8c3a2f4e-6b1d-4a5e-9c8b-2d3f1e4a5b6c/", "company_id": "...", "brand_id": "...", "client": { "email": "buyer@example.com" }, "purchase": { "products": [{ "name": "Test Product", "price": 100 }] } } ``` The `price` field is in the smallest currency unit. `100` equals RM 1.00 (or MYR 1.00). ## 2. Open the checkout URL Copy the `checkout_url` from the response and open it in your browser. In test mode, the hosted payment page lets you simulate a successful payment without redirecting to a real bank. ## 3. Check the purchase status After the test payment completes, query the purchase to confirm its status: ```bash theme={null} curl "https://gate.chip-in.asia/api/v1/purchases/8c3a2f4e-6b1d-4a5e-9c8b-2d3f1e4a5b6c/" \ -H "Authorization: Bearer " ``` The `status` field will move from `created` to `paid` once the test payment succeeds. ## 4. Receive a server-side notification with `success_callback` `success_redirect` only fires when the buyer reaches your site in a browser. To know about payments that succeed without a redirect — for example, when the buyer closes the tab, pays through an e-wallet, or completes the payment asynchronously — pass a `success_callback` URL when creating the purchase. CHIP will `POST` the full purchase object to that URL the moment the payment is captured. ```json theme={null} { "client": { "email": "buyer@example.com" }, "purchase": { "products": [{ "name": "Test Product", "price": 100 }] }, "success_redirect": "https://example.com/success", "failure_redirect": "https://example.com/failure", "success_callback": "https://example.com/webhooks/chip" } ``` `success_callback` payloads are signed with an RSA signature in the `X-Signature` header — see [Verifying webhook signatures](/chip-collect/overview/authentication#2-verifying-webhook-signatures) for the verification algorithm and code samples. Use [Webhooks](/chip-collect/api-reference/webhooks/create) when you need a single endpoint that listens for **multiple event types** across many purchases (e.g. `purchase.paid`, `purchase.payment_failure`, `purchase.refunded`). For a single purchase that just needs a "payment is done" notification, `success_callback` is the simpler choice. ## Next steps * [Authentication](/chip-collect/overview/authentication) — Bearer token and webhook signature verification * [Online Purchases](/chip-collect/overview/online-purchases/payment-link) — payment links, subscriptions, and pre-auth * [Direct Post & Skip payment page](/chip-collect/overview/direct-post/intro) — embed the checkout on your own site * [Integrate with AI agents](/chip-collect/overview/vibe-coding-guide) — use the CHIP skill with Cursor, Codex, or any AI-powered IDE * [Errors](/chip-collect/overview/errors) — common error codes and what to do about them # Install the skill Source: https://docs.chip-in.asia/chip-collect/overview/vibe-coding-guide Easily integrate with CHIP Collect by using an AI agent. ## Description The command provided is for the user to install the `SKILL.md` to assist with the integration of CHIP payment links using an AI agent. The `SKILL.md` contains manual instructions for the agent to integrate CHIP payment gateway and generate checkout URLs so the user can verify the integration end-to-end. ## Conventions for AI agents CHIP exposes a machine-readable entry point at [`/llms.txt`](/llms.txt) that lists the canonical base URLs, path conventions, and authentication headers for both products. When generating integration code for CHIP Collect: * **CHIP Collect** resource paths are **plural with a trailing slash** (e.g. `/purchases/{id}/`, not `/purchase/{id}/`). Base URL: `https://gate.chip-in.asia/api/v1`. * All CHIP Collect endpoints require `Authorization: Bearer `. * **Never invent a path.** If the doc or OpenAPI spec doesn't show it, it doesn't exist. If a path you "remember" isn't in the spec, the spec wins. The raw OpenAPI spec is served at [`/openapi/chip-collect.yaml`](/openapi/chip-collect.yaml) for direct consumption by coding agents. ## Requirements * Cursor, Codex or any AI-powered IDE * Know how to execute terminal command and prompt AI agent ## Install Run this command in your project root to install the skill. ```bash theme={null} npx -y github:CHIPAsia/skill ``` Once executed, a `SKILL.md` file is generated inside the `chip-skill/` folder in your root repository. *** ## Example prompt Use this prompt after installing the skill to generate a dummy payment link. ``` Create a dummy payment link, The expected output is: - a chip-env file to store CHIP's Secret Key and Brand ID - executable code that will generate a checkout URL. - instructions on where to get the credentials - the command to execute the code Use chip-skill/SKILL.md as a reference. ``` *** ## What to do next Once the code is generated, follow these steps to test your integration. Add your CHIP Secret Key and Brand ID into the generated `chip-env` file. Execute the generated code using the command provided.
A checkout URL will be printed to your terminal.
Open the URL in your browser to make a dummy payment. Please use the test mode API key.
Always use a **test mode API key** when generating dummy payment links.
Do not use your live credentials for testing.
How to identify a dummy payment link?
Dummy payment link will execute fake payment instead of redirect to bank page when user click pay.
*** ## Get your credentials Retrieve your Brand ID and API keys from the CHIP merchant portal. Get your Brand ID from the CHIP merchant portal. Get your Secret Key from the CHIP merchant portal. *** ## Source documents The raw OpenAPI spec is the canonical machine-readable source of truth for CHIP Collect. Coding agents should consume it directly. Raw `chip-collect.yaml` OpenAPI 3.1 spec. Source for the CHIP Collect AI skill and the canonical `SKILL.md`. Looking for **CHIP Send (payouts)** instead? See [Install the CHIP Send skill](/chip-send/api-reference/vibe-coding-guide). The two skills are independent and can be installed in the same project. # Webhook signatures Source: https://docs.chip-in.asia/chip-collect/overview/webhook-signatures Verify the authenticity of CHIP Collect webhook and success callback deliveries. Payloads are signed using asymmetric (public-key) cryptography to guarantee the authenticity of delivered callbacks. Each callback delivery request includes an `X-Signature` header field. This field contains a base64-encoded RSA PKCS#1 v1.5 signature of the SHA-256 digest of the request body buffer. You can obtain the public key for Webhook authentication from `Webhook.public_key` of the corresponding Webhook. You can obtain the public key for success callback authentication from [GET /public\_key/](/chip-collect/api-reference/public-key/retrieve). `GET /public_key/` returns the PEM as a **JSON-encoded string** — the response body is `"-----BEGIN PUBLIC KEY-----..."` (with surrounding quotes), not a bare PEM body and not a `{"key": ...}` object. You must `json_decode` the response body before passing it to your crypto library. Passing the raw body (quotes included) to `openssl_verify` / `crypto.createVerify` fails with "Supplied key param cannot be coerced into a public key". Please note that CHIP is not responsible for any financial losses incurred as a result of failing to implement payload signature verification. ## How to verify The verification process is: 1. Read the **raw request body** (before JSON parsing). The signature is computed over the bytes as received. 2. Decode the `X-Signature` header from base64. 3. Verify it against the request body using the public key with RSA PKCS#1 v1.5 padding and a SHA-256 digest. 4. Reject the request if verification fails. Always verify the raw request body. Re-serializing the parsed JSON will change byte ordering or whitespace and break the signature. ## Fetching the public key `GET /public_key/` returns a JSON-encoded PEM string. Decode it before use: ```bash theme={null} # Raw response (note the surrounding quotes): curl -H "Authorization: Bearer " \ "https://gate.chip-in.asia/api/v1/public_key/" # => "-----BEGIN PUBLIC KEY-----\nMIIBojAN...\n-----END PUBLIC KEY-----\n" ``` ```javascript theme={null} const res = await fetch('https://gate.chip-in.asia/api/v1/public_key/', { headers: { Authorization: 'Bearer ' + secretKey } }); const publicKeyPem = await res.json(); // JSON string -> PEM, quotes stripped ``` ```php theme={null} $response = file_get_contents('https://gate.chip-in.asia/api/v1/public_key/', false, $context); $publicKeyPem = json_decode($response); // JSON string -> PEM ``` ```python theme={null} import json, urllib.request req = urllib.request.Request( 'https://gate.chip-in.asia/api/v1/public_key/', headers={'Authorization': 'Bearer ' + secret_key}, ) public_key_pem = json.loads(urllib.request.urlopen(req).read()) ``` ## Example (Node.js) ```javascript theme={null} const crypto = require('crypto'); const express = require('express'); const app = express(); // IMPORTANT: capture the raw body before any JSON parsing app.use(express.json({ verify: (req, _res, buf) => { req.rawBody = buf; } })); app.post('/webhook', async (req, res) => { const signature = req.header('X-Signature'); // base64-encoded const publicKeyPem = await getPublicKeyForThisWebhook(); const verifier = crypto.createVerify('RSA-SHA256'); verifier.update(req.rawBody); verifier.end(); const ok = verifier.verify(publicKeyPem, signature, 'base64'); if (!ok) { return res.status(401).send('Invalid signature'); } // Signature valid — process the event console.log('Verified event:', req.body); res.status(200).end(); }); ``` ## Example (PHP) ```php theme={null} $signature = base64_decode($_SERVER['HTTP_X_SIGNATURE']); $rawBody = file_get_contents('php://input'); $publicKeyPem = getPublicKeyForThisWebhook(); // PEM-encoded $ok = openssl_verify($rawBody, $signature, $publicKeyPem, OPENSSL_ALGO_SHA256); if ($ok !== 1) { http_response_code(401); exit('Invalid signature'); } // Signature valid — process the event ``` ## Example (Python) ```python theme={null} import base64 from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import padding def verify(raw_body: bytes, signature_b64: str, public_key_pem: str) -> bool: public_key = serialization.load_pem_public_key(public_key_pem.encode()) signature = base64.b64decode(signature_b64) try: public_key.verify( signature, raw_body, padding.PKCS1v15(), hashes.SHA256(), ) return True except Exception: return False ``` # What We Offer Source: https://docs.chip-in.asia/chip-collect/overview/what-we-offer Easily access these features using simple API parameters. ### [Purchase Notifications](/chip-collect/api-reference/purchases/create#body-success-callback) Receive real-time notifications via system callback or email when a transaction is successfully completed. ### [Send Receipts to Customers](/chip-collect/api-reference/purchases/create#body-send-receipt) Automatically send a standardized receipt to your customers on your behalf. ### [Redirect Customer After Purchase](/chip-collect/api-reference/purchases/create#body-success-redirect) Automatically redirect customers to a designated webpage based on the outcome of the transaction—success, failure, or cancellation. ### [Secure Customer Card Information](/chip-collect/api-reference/purchases/create#body-skip-capture) Tokenize and securely store your customers’ card details for future purchases or subscriptions by setting the parameter `skip_capture: true` and `price: 0` ### [Pre-Authorize Payment](/chip-collect/api-reference/purchases/create#body-skip-capture) Withhold the amount in the customer's account to be captured or released later by setting the parameter `skip_capture: true` and `price` to a value greater than 0. ### [Free Form API Field](/chip-collect/api-reference/purchases/create#body-reference) Lets you store any custom value, such as an invoice number or other reference data, for your business needs. ### [Payment Method Whitelist](/chip-collect/api-reference/purchases/create#body-payment-method-whitelist) Enable or disable specific payment methods to suit your business needs. Payment methods available: * FPX Online Banking * Local credit and debit cards * Foreign credit and debit cards * E-wallets * DuitNow QR (Online) * Buy Now, Pay Later * Crypto Coin * On-Premise POS Terminal # List all accounts Source: https://docs.chip-in.asia/chip-send/api-reference/accounts/list _openapi-chip-send GET /send/accounts Use this endpoint to know: - Your available settlement (collection) amount that can be converted to CHIP Send Limit; - Current balance; - Send Fee; - Account Verification Fee; - No. of approvals required to convert collection balance to CHIP Send Limit Please note that `convertible_balance_from_statement` will return different data depending on when you hit this endpoint. This adds flexibility and time for you to plan out your CHIP Send Budget Allocation requests, without having to send extra information. For example: - Trigger today at 11.59 PM MYT will return convertible balance from today's collection. - Trigger tomorrow at 12.00 AM MYT will return convertible balance from today's collection. - Trigger tomorrow at 11.59 AM MYT will return convertible balance from today's collection. - Trigger tomorrow at 12.00 PM MYT will return convertible balance from tomorrow's collection. # Add a bank account Source: https://docs.chip-in.asia/chip-send/api-reference/bank-accounts/create _openapi-chip-send POST /send/bank_accounts Adds a bank account using the values provided in the request body. Store the ID returned in the response, as it will be required for subsequent operations. # Delete a bank account Source: https://docs.chip-in.asia/chip-send/api-reference/bank-accounts/delete _openapi-chip-send DELETE /send/bank_accounts/{id} Deletes a bank account record, preventing future payments via the Create Send Instruction endpoint. # List all bank accounts Source: https://docs.chip-in.asia/chip-send/api-reference/bank-accounts/list _openapi-chip-send GET /send/bank_accounts Returns list of recipient bank accounts. # Resend bank account webhook event Source: https://docs.chip-in.asia/chip-send/api-reference/bank-accounts/resend-webhook _openapi-chip-send POST /send/bank_accounts/{id}/resend_webhook_event Resends the webhook event for a bank account record. # Retrieve a bank account Source: https://docs.chip-in.asia/chip-send/api-reference/bank-accounts/retrieve _openapi-chip-send GET /send/bank_accounts/{id} Retrieve details of a recipient bank account that was previously created. # Changelog Source: https://docs.chip-in.asia/chip-send/api-reference/changelog Notable changes to the CHIP Send API and this documentation. * Added a top-level [Overview](/chip-send/api-reference/overview) group so integrators new to CHIP Send can find product positioning, the typical payout flow, and a one-line summary of authentication without diving straight into the API reference. * Clarified in both the OpenAPI spec and the docs that the `RM 1,000` daily cap on budget allocation is **staging only**. In production, the allocation is limited only by the available settlement balance returned as `convertible_balance_from_statement`. * Fixed the Send base URL across the spec, the docs, and `llms.txt`: it is `https://api.chip-in.asia/api` (production) and `https://staging-api.chip-in.asia/api` (sandbox), **not** `/api/v1`. A handful of older examples were still writing `/api/v1`; this aligns everything with the real endpoint. * Rewrote the [Send introduction](/chip-send/api-reference/introduction) from scratch. The new version covers credentials, the `epoch + checksum` signing algorithm, full request examples in Node/Ruby/Python/PHP, and a troubleshooting table for the most common 401 responses. * Added `send_recipient_receipt` so merchants can request a receipt be sent to the recipient of a payout (default off). * Added a dedicated [Webhook validation](/chip-send/api-reference/webhooks/validation) page and a [Delivery protocol](/chip-send/api-reference/webhooks/delivery-protocol) page, with example payloads and a reference signing implementation. # Create a group Source: https://docs.chip-in.asia/chip-send/api-reference/groups/create _openapi-chip-send POST /send/groups Creates a group using the values provided in the request body. A group acts as a label for bank accounts — for example, `Vendor` or `Employee`. # Delete a group Source: https://docs.chip-in.asia/chip-send/api-reference/groups/delete _openapi-chip-send DELETE /send/groups/{id} Permanently delete a group. # List all groups Source: https://docs.chip-in.asia/chip-send/api-reference/groups/list _openapi-chip-send GET /send/groups Returns a list of previously created group records. *Please note that this request is cached and will only refresh every hour.* # Retrieve a group Source: https://docs.chip-in.asia/chip-send/api-reference/groups/retrieve _openapi-chip-send GET /send/groups/{id} Retrieve details of a group that was previously created. # Update a group Source: https://docs.chip-in.asia/chip-send/api-reference/groups/update _openapi-chip-send PATCH /send/groups/{id} Updates the specified group using the values provided in the parameters. # Introduction Source: https://docs.chip-in.asia/chip-send/api-reference/introduction How to authenticate and call the CHIP Send API. # CHIP Send API The CHIP Send API enables merchants to send funds programmatically via a REST API and to register recipient bank accounts for payouts. All endpoints share a single base URL and a single authentication scheme. This page covers everything required to make a successful request. ## Endpoints The base URL must match the merchant's environment: | Environment | Base URL | | ----------- | -------------------------------------- | | Staging | `https://staging-api.chip-in.asia/api` | | Production | `https://api.chip-in.asia/api` | The operation path from the [API reference](#) (for example `/send/accounts` or `/send/send_instructions`) is appended to the base URL. ## Prerequisites Before integration begins, a CHIP Send account must be created for the merchant by the CHIP admin team. To obtain credentials, the merchant's CHIP Account Manager must be contacted with the following information: 1. A primary email address. 2. The email addresses of every required approver. If two approvals are required, both email addresses must be provided. ## Credentials Two pieces of information are issued to the merchant: | Credential | Where it goes | What it does | | -------------- | --------------------------------------------------- | ----------------------------------------------------------------------------------- | | **API Key** | `Authorization: Bearer ` header | Identifies the merchant's account. Sent on every request. | | **API Secret** | Never sent over the network. Used only for signing. | Used to compute the per-request checksum. Must be stored securely, like a password. | The API Key and the `api_key` value used inside the checksum string are the **same value**. The API Secret is **never** transmitted in any request. It only ever lives on the merchant's server and is used to compute the checksum described below. Both the API Key and the API Secret are available to the merchant in the [CHIP Control → Settings → Applications](https://portal.chip-in.asia/control/settings/applications) page of the merchant portal. ## How authentication works Every request to the CHIP Send API must include three headers: | Header | Value | | --------------- | --------------------------------------------------------------------------- | | `Authorization` | `Bearer ` | | `epoch` | The current Unix timestamp in seconds (for example, `1689826456`) | | `checksum` | Hex-encoded HMAC-SHA512 of the signing string, computed with the API Secret | The `epoch` value must be within **30 seconds** of the server's clock. If it is too old or too far in the future, the request is rejected as `Unauthorized`. The merchant's server clock must therefore be synchronised (for example via NTP). ## How to compute the checksum The signing string is formed by **concatenating the `epoch` value and the API Key with no separator, in that order**: ``` signing_string = + ``` For example, with `epoch = 1689826456` and `API Key = e0645c9e-fcf2-4f29-a327-202f7ed3d969`: ``` 1689826456e0645c9e-fcf2-4f29-a327-202f7ed3d969 ``` The checksum is then computed as: ``` checksum = HEX( HMAC_SHA512( key = API Secret, message = signing_string ) ) ``` Given `API Secret = a118729e-4243-4145-83b3-0b8cb213fe8e`, the checksum for the example signing string above is: ``` 45bee62dba8087ab1e7e767d92f8d6e26f8bd19ee5fd2fef6386bb9425976498a86ffdbddb7a49919998e993c20626196ea652320f438a9528d2b8c9d19ec266 ``` This expected value can be used to verify the implementation before any real request is sent. ## A complete request The following `curl` example computes the epoch and checksum, then sends a request end-to-end: ```bash theme={null} # 1. Compute the epoch and checksum (must be recomputed for every request) EPOCH=$(date +%s) API_KEY="e0645c9e-fcf2-4f29-a327-202f7ed3d969" API_SECRET="a118729e-4243-4145-83b3-0b8cb213fe8e" CHECKSUM=$(printf '%s%s' "$EPOCH" "$API_KEY" | openssl dgst -sha512 -hmac "$API_SECRET" -hex | sed 's/^.*= //') # 2. Send the request curl -X POST "https://staging-api.chip-in.asia/api/send/bank_accounts" \ -H "Authorization: Bearer $API_KEY" \ -H "epoch: $EPOCH" \ -H "checksum: $CHECKSUM" \ -H "Content-Type: application/json" \ -d '{ "account_number": "157380112229", "bank_code": "MBBEMYKL", "name": "Ahmad Razali", "reference": "VENDOR-EMP-001" }' ``` A `200 OK` response confirms that authentication is working correctly. For other responses, see [Troubleshooting](#troubleshooting) below. ## Language examples The same computation in four common languages: ```javascript Node.js theme={null} const crypto = require('crypto'); const epoch = Math.floor(Date.now() / 1000).toString(); const apiKey = process.env.CHIP_API_KEY; const apiSecret = process.env.CHIP_API_SECRET; const signingString = epoch + apiKey; const checksum = crypto .createHmac('sha512', apiSecret) .update(signingString) .digest('hex'); // Then send the request with headers: // Authorization: Bearer // epoch: // checksum: ``` ```ruby Ruby theme={null} require 'openssl' epoch = Time.now.to_i.to_s api_key = ENV['CHIP_API_KEY'] api_secret = ENV['CHIP_API_SECRET'] signing_string = "#{epoch}#{api_key}" checksum = OpenSSL::HMAC.hexdigest('SHA512', api_secret, signing_string) # Then send the request with headers: # Authorization: Bearer # epoch: # checksum: ``` ```python Python theme={null} import hashlib, hmac, time epoch = str(int(time.time())) api_key = "YOUR_API_KEY" api_secret = "YOUR_API_SECRET" signing_string = (epoch + api_key).encode() checksum = hmac.new(api_secret.encode(), signing_string, hashlib.sha512).hexdigest() # Then send the request with headers: # Authorization: Bearer # epoch: # checksum: ``` ```php PHP theme={null} // epoch: // checksum: ``` ## Troubleshooting | Symptom | Most likely cause | | ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `401 Unauthorized` with no further detail | The `epoch` value is more than 30 seconds off the server clock, or the merchant's server clock is incorrect. NTP synchronisation should be verified. | | `401 Unauthorized` with a checksum-related error | The signing string is wrong. The following should be verified: (a) it is ` + ` with **no separator**, (b) the message is the *string* (not a JSON object), (c) the output is **hex-encoded** (not base64), (d) SHA-512 is used (not SHA-256). | | `401 Unauthorized` with an `Authorization`-related error | The `Authorization: Bearer ` header is missing, malformed, or the API Key is invalid. | | `400 Bad Request` on a syntactically valid payload | The `epoch` or `checksum` header is missing, or one of the values contains an unexpected character. | | A request works in Postman but fails from the merchant's code | The checksum is being computed once and reused across requests. The checksum must be **recomputed for every request** with a fresh `epoch`. | ## Basic integration flow A complete payout consists of four steps: 1. The [Accounts API](/chip-send/api-reference/accounts/list) is called to check the convertible balance. 2. The [Increase Send Limit API](/chip-send/api-reference/send-limits/create) is called to allocate balance for payouts. 3. The [Add Bank Account API](/chip-send/api-reference/bank-accounts/create) is called to register a recipient. 4. The [Create Send Instruction API](/chip-send/api-reference/send-instructions/create) is called to send funds. A walkthrough is provided in [Test Integration](/chip-send/api-reference/test-integration). ## Approving CHIP Send Budget Allocation requests Every approver receives an email when a budget-allocation request requires approval. Approval is performed by clicking the **Approve** button in the email. Once all required approvers have approved, the new balance is reflected in the [Accounts API](/chip-send/api-reference/accounts/list) response. The token used in the `Authorization` header is the **API Key** mentioned in the Credentials section above. # CHIP Send overview Source: https://docs.chip-in.asia/chip-send/api-reference/overview Programmatically pay out funds via the CHIP Send API. CHIP Send is CHIP's **payouts** product. It lets a registered merchant move funds from a CHIP Send account to recipient bank accounts via a REST API, with built-in approval workflows and per-recipient bank account registration. It is a **separate product** from [CHIP Collect](/chip-collect/overview/introduction): | | CHIP Collect | CHIP Send | | ---------------- | ---------------------------------- | ----------------------------------------- | | Purpose | Accept payments from buyers | Pay out funds to recipients | | Base URL | `https://gate.chip-in.asia/api/v1` | `https://api.chip-in.asia/api` | | Sandbox base URL | (same — test mode uses test keys) | `https://staging-api.chip-in.asia/api` | | Authentication | `Authorization: Bearer ` | Bearer + `epoch` + HMAC-SHA512 `checksum` | | Path convention | Plural with trailing slash | `snake_case` under `/send/...` | ## Typical payout flow A complete payout consists of four steps: 1. Call the [Accounts API](/chip-send/api-reference/accounts/list) to check the convertible balance. 2. Call the [Increase Send Limit API](/chip-send/api-reference/send-limits/create) to allocate balance for payouts. 3. Call the [Add Bank Account API](/chip-send/api-reference/bank-accounts/create) to register a recipient. 4. Call the [Create Send Instruction API](/chip-send/api-reference/send-instructions/create) to send funds. A walkthrough is provided in [Test Integration](/chip-send/api-reference/test-integration). ## Prerequisites Before integration begins, a CHIP Send account must be created for the merchant by the CHIP admin team. The merchant's CHIP Account Manager must be contacted with: 1. A primary email address. 2. The email addresses of every required approver. If two approvals are required, both email addresses must be provided. ## Authentication in one line Every request needs three headers: ``` Authorization: Bearer epoch: checksum: HEX( HMAC_SHA512(key=api_secret, msg=epoch + api_key) ) ``` Full algorithm, code samples in Node, Ruby, Python, and PHP, and a troubleshooting table are in the [CHIP Send introduction](/chip-send/api-reference/introduction). # Pre-Request Script Source: https://docs.chip-in.asia/chip-send/api-reference/script How to wire the auth headers in Postman. ## Using the pre-request script In Postman, the following code is pasted into the **Pre-request Script** tab of the collection. Postman runs this script before every request, so the `epoch` and `checksum` headers are always fresh. ```javascript theme={null} var epoch = Math.floor(Date.now() / 1000).toString(); pm.collectionVariables.set("epoch", epoch); var apiKey = pm.collectionVariables.get("api_key"); var apiSecret = pm.collectionVariables.get("api_secret"); var signingString = epoch + apiKey; var checksum = CryptoJS.enc.Hex.stringify( CryptoJS.HmacSHA512(signingString, apiSecret) ); pm.collectionVariables.set("checksum", checksum); ``` On the **Authorization** tab of the collection, **Type** is set to **Bearer Token** and the API Key is provided as the token. Postman adds the `Authorization: Bearer ` header automatically. On each request, two headers are added that read from the variables: | Header | Value | | ---------- | -------------- | | `epoch` | `{{epoch}}` | | `checksum` | `{{checksum}}` | > **Why is a pre-request script needed?** The signature is computed with HMAC-SHA512, which Postman cannot express declaratively. The pre-request script is the standard place to perform per-request crypto. ## FAQ **Where are the API Key and API Secret obtained?** Both values are available in the [CHIP Control → Settings → Applications](https://portal.chip-in.asia/control/settings/applications) page of the merchant portal. The same page can also be used to generate new credentials or revoke existing ones. **Why does a request fail even though the script ran?** The most common cause is reuse of a previously computed checksum. The script must run on every request (the default behaviour). If **Run pre-request script before each request** has been disabled in the collection settings, it should be re-enabled. # Create a send instruction Source: https://docs.chip-in.asia/chip-send/api-reference/send-instructions/create _openapi-chip-send POST /send/send_instructions Creates a send instruction using the values provided in the request body. Calling this endpoint will reduce your available account balance and credit the amount to the recipient's bank account. # Delete a send instruction Source: https://docs.chip-in.asia/chip-send/api-reference/send-instructions/delete _openapi-chip-send DELETE /send/send_instructions/{id} Deletes a send instruction that was previously created. *Note: Only unprocessed instructions can be deleted; any other attempts will fail.* # List all send instructions Source: https://docs.chip-in.asia/chip-send/api-reference/send-instructions/list _openapi-chip-send GET /send/send_instructions Returns a list of previously created send instructions. *Note: This request is cached and will only refresh every hour.* # Resend send instruction webhook event Source: https://docs.chip-in.asia/chip-send/api-reference/send-instructions/resend-webhook _openapi-chip-send POST /send/send_instructions/{id}/resend_webhook_event Resends the webhook event for a send instruction record. # Retrieve a send instruction Source: https://docs.chip-in.asia/chip-send/api-reference/send-instructions/retrieve _openapi-chip-send GET /send/send_instructions/{id} Retrieve details of a send instruction that was previously created. # Increase Budget Allocation Source: https://docs.chip-in.asia/chip-send/api-reference/send-limits/create _openapi-chip-send POST /send/send_limits Use this API to increase your CHIP Send Budget Allocation by converting an upcoming settlement amount. Upon initiation, all approvers will receive an email prompting them to approve the budget allocation. *Note: All CHIP Send Budget Allocation requests must be approved by 12 PM MYT the following day. Any requests still pending at 12 PM MYT on the next day will be marked as expired.* In production, there is no per-request or per-day cap on the allocation amount — the amount is limited only by the available settlement balance returned as `convertible_balance_from_statement`. The RM 1,000 daily limit applies only to the staging environment. # List all send limits Source: https://docs.chip-in.asia/chip-send/api-reference/send-limits/list _openapi-chip-send GET /send/send_limits Returns list of send limits. # Resend approval requests Source: https://docs.chip-in.asia/chip-send/api-reference/send-limits/resend-approval-requests _openapi-chip-send POST /send/send_limits/{id}/resend_approval_requests Resends pending approval emails to approvers. # Retrieve send limit Source: https://docs.chip-in.asia/chip-send/api-reference/send-limits/retrieve _openapi-chip-send GET /send/send_limits/{id} Retrieve the details of a previously created send limit / budget allocation. # Test Integration Source: https://docs.chip-in.asia/chip-send/api-reference/test-integration Test integration tutorial # Requirements Refer here for [bearer token](/chip-send/api-reference/introduction#credentials) Refer here for [pre-request script](/chip-send/api-reference/script) # Flows After the bearer token and pre-request script have been added, follow these steps to test the CHIP Send features. This flow walks you through checking the convertible balance and completing a payout to a recipient bank. Check the convertible balance using the [Accounts API](/chip-send/api-reference/accounts/list).
The convertible balance can be found in the response body under the parameter `convertible_balance_from_statement`. Virtual RM1,000 is available for conversion daily in staging mode. In production, there is no fixed cap; the allocatable amount is limited only by your available settlement balance.
Allocate balance using the [Create Send Limit API](/chip-send/api-reference/send-limits/create). ```js theme={null} { "amount" : 2000 } ``` Add a recipient bank using the [Add Bank Account API](/chip-send/api-reference/bank-accounts/create). ```js theme={null} { "name" : "Ahmad Razali", "account_number" : "157380112229", "bank_code" : "MBBEMYKL", "reference" : "VENDOR-EMP-001" } ``` Send funds to the recipient bank using the [Create Send Instruction API](/chip-send/api-reference/send-instructions/create). ```js theme={null} { "bank_account_id" : 1, "amount" : 1000, "email" : "recipient@example.com", "description" : "Vendor payout for invoice INV-2024-0892", "reference" : "INV-2024-0892", "send_recipient_receipt" : true } ``` Get the bank account id using the [Bank Accounts List API](/chip-send/api-reference/bank-accounts/list). Reference must be unique
# Install the skill Source: https://docs.chip-in.asia/chip-send/api-reference/vibe-coding-guide Easily integrate with CHIP Send by using an AI agent. ## Description The command below installs the `SKILL.md` that an AI agent uses to integrate with the CHIP Send API for payouts. Once installed, the agent can read recipient bank account creation, send instruction flow, approval routing, and the HMAC-SHA512 request signing scheme straight from the canonical skill file. ## Conventions for AI agents CHIP exposes a machine-readable entry point at [`/llms.txt`](/llms.txt) that lists the canonical base URLs, path conventions, and authentication headers for both products. When generating integration code for CHIP Send: * **CHIP Send** resource paths are prefixed with `/send/` and use `snake_case` (e.g. `/send/bank_accounts/{id}/`). Base URLs: `https://api.chip-in.asia/api` (production) and `https://staging-api.chip-in.asia/api` (sandbox). * All CHIP Send endpoints require three headers on every request: `Authorization: Bearer `, `epoch: `, and `checksum: ` (see the [Send introduction](/chip-send/api-reference/introduction) for the signing algorithm). * **Never invent a path.** If the doc or OpenAPI spec doesn't show it, it doesn't exist. If a path you "remember" isn't in the spec, the spec wins. The raw OpenAPI spec is served at [`/openapi/chip-send.yaml`](/openapi/chip-send.yaml) for direct consumption by coding agents. ## Requirements * Cursor, Codex or any AI-powered IDE * Know how to execute terminal commands and prompt an AI agent * A CHIP Send account provisioned by the CHIP admin team (see [Send overview](/chip-send/api-reference/overview#prerequisites)) ## Install Run this command in your project root to install the CHIP Send skill. ```bash theme={null} npx -y github:CHIPAsia/skill-send ``` Once executed, a `SKILL.md` file is generated inside the `chip-skill-send/` folder in your project. If you also need to accept payments, install the [CHIP Collect skill](/chip-collect/overview/vibe-coding-guide) in the same project — the two skills are independent and do not conflict. *** ## Example prompt Use this prompt after installing the skill to generate a dummy payout. ``` Create a dummy CHIP Send payout, The expected output is: - a chip-send-env file to store CHIP Send's API Key and API Secret - executable code that will register a recipient bank account and create a send instruction - instructions on where to get the credentials - the command to execute the code Use chip-skill-send/SKILL.md as a reference. ``` *** ## What to do next Once the code is generated, follow these steps to test your integration in the CHIP Send sandbox. Add your CHIP Send API Key and API Secret into the generated `chip-send-env` file. Execute the generated code using the command provided.
The code will register a recipient bank account and create a send instruction in the staging environment.
Call the [Accounts API](/chip-send/api-reference/accounts/list) to confirm the convertible balance reflects the new send instruction.
Always use **staging credentials** when generating dummy payouts.
Do not use production credentials for testing — staging does not move real money and will not consume your daily allocation cap.
Every CHIP Send request is signed with a fresh `epoch` + `checksum` pair, recomputed for each request. Reusing a checksum from a prior request will be rejected with `401 Unauthorized`. *** ## Get your credentials Retrieve your API Key and API Secret from the CHIP Control panel. Find your API Key and API Secret in CHIP Control → Settings → Applications. New to CHIP Send? Contact your account manager to provision a merchant account and approvers. *** ## Source documents Raw `chip-send.yaml` OpenAPI 3.1 spec. Source for the CHIP Send AI skill and the canonical `SKILL.md`. # Create a webhook Source: https://docs.chip-in.asia/chip-send/api-reference/webhooks/create _openapi-chip-send POST /webhooks Creates a webhook using the values provided in the request body. When a selected event occurs, the system delivers a notification to the configured callback URL. # Delete a webhook Source: https://docs.chip-in.asia/chip-send/api-reference/webhooks/delete _openapi-chip-send DELETE /webhooks/{id} Permanently delete a webhook. # Delivery Protocol Source: https://docs.chip-in.asia/chip-send/api-reference/webhooks/delivery-protocol Learn about CHIP Send's webhook delivery and retry policy. If your server is unavailable or returns an error, our system will automatically retry the delivery 25 times over a period of approximately 21 days. The retries follow an `exponential backoff` schedule, meaning the attempts happen frequently at first and then gradually spread out over the three-week period. If the delivery still fails after all 25 attempts, we will send an email notification to your registered address so you can investigate the issue. ## Retry Schedule Here is an example of how the retry schedule would look for a single failed webhook: * **1st Failure**: Retries almost immediately (within seconds). * **5th Failure**: Retries after about 12 minutes. * **10th Failure**: Retries after about 3 hours. * **15th Failure**: Retries after about 14 hours. * **20th Failure**: Retries after about 2 days. * **25th Failure (Final)**: Retries after about 4.5 days. **Total Duration**: If your server remains down, the system will keep trying for a total of 21 days before finally giving up and sending you the `Retries Exhausted` email notification. # List all webhooks Source: https://docs.chip-in.asia/chip-send/api-reference/webhooks/list _openapi-chip-send GET /webhooks Returns a list of previously created webhooks. *Please note that this request is cached and will refresh every hour.* # Retrieve a webhook Source: https://docs.chip-in.asia/chip-send/api-reference/webhooks/retrieve _openapi-chip-send GET /webhooks/{id} Retrieve details of a webhook that was previously created. # Update a webhook Source: https://docs.chip-in.asia/chip-send/api-reference/webhooks/update _openapi-chip-send PATCH /webhooks/{id} Updates the specified webhook using the values provided in the parameters. # Validating Webhooks Source: https://docs.chip-in.asia/chip-send/api-reference/webhooks/validation Learn how to verify the authenticity of CHIP Send webhook payloads. To ensure that the webhook notifications you receive are sent by CHIP and have not been tampered with, you should validate the signature included in each request. ## Signature Header Each webhook delivery request includes an `X-Signature` header field. This field contains a base64-encoded RSA PKCS#1 v1.5 signature of the SHA512 digest of the request body buffer. ## Obtaining the Public Key Unlike CHIP Collect, CHIP Send provides a dedicated public key for each webhook. You can obtain the public key by retrieving the webhook details via the [Retrieve a Webhook](/chip-send/api-reference/webhooks/retrieve) API. The `public_key` field in the response contains the PEM-encoded RSA public key. ## Verification Steps 1. **Retrieve the Payload**: Get the raw request body (buffer) of the webhook notification. 2. **Get the Signature**: Extract the value of the `X-Signature` header and base64-decode it. 3. **Verify**: Use the RSA public key to verify the signature against the SHA512 digest of the raw request body. ## Code Examples ### Ruby ```ruby theme={null} require 'openssl' require 'base64' # webhook_public_key: The PEM-encoded public key from the Webhook object # x_signature_header: The value of the X-Signature header # request_body: The raw JSON body of the request public_key = OpenSSL::PKey::RSA.new(webhook_public_key) signature = Base64.decode64(x_signature_header) digest = OpenSSL::Digest::SHA512.new is_valid = public_key.verify(digest, signature, request_body) if is_valid puts "Signature is valid" else puts "Signature is invalid" end ``` ### PHP ```php theme={null} CHIP is not responsible for any financial losses incurred as a result of failing to implement payload signature verification.