> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bafanglaicai88.com/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Describe only the public 123hub merchant API documented on this site. The primary endpoint is POST /public/api/multihub/v1.
> Preserve API method names, field names, header names, and error codes exactly as documented. Do not invent endpoints or parameters.
> Treat every amount as an integer in minor units unless a page explicitly states otherwise.
> Never expose, request, or fabricate a merchant secret key. The SDK pages contain reference implementations, not official SDK packages.

# Payments Guide

> Learn how to create deposits, withdrawals, and check payment statuses with the 123hub API

# Payments Guide

This guide covers everything you need to know about creating and managing payments through the 123hub API. All payment operations use a single endpoint with method-based routing.

## Overview

The 123hub API provides three payment methods through a single endpoint:

| Method           | Direction | Description                                   |
| ---------------- | --------- | --------------------------------------------- |
| `payment.in`     | Inbound   | Customer pays to merchant (deposit)           |
| `payment.out`    | Outbound  | Merchant pays to customer/vendor (withdrawal) |
| `payment.status` | Query     | Check the current status of a payment         |

### Endpoint

All requests are sent to a single endpoint:

```
POST /public/api/multihub/v1
```

Successful responses return **HTTP 200**. Error responses return **HTTP 400** (or **HTTP 404** for unknown methods). Always check the `success` field in the response body to determine the outcome.

### Authentication

Every request requires the application ID and exact-body hash. The timestamp
and nonce are optional and must be supplied together when used:

| Header                  | Description                                                        | Example       |
| ----------------------- | ------------------------------------------------------------------ | ------------- |
| `X-Data-Application-Id` | Your application ID (integer)                                      | `42`          |
| `X-Data-Hash`           | SHA-512 hash of the request body concatenated with your secret key | `a1b2c3d4...` |
| `X-Data-Timestamp`      | Optional current Unix timestamp in seconds                         | `1716123456`  |
| `X-Data-Nonce`          | Optional unique nonce for this request                             | `req_01HX...` |

The hash is computed as:

```
sha512(requestBody + secretKey)
```

Where `requestBody` is the raw JSON string of the request body, and `secretKey` is your merchant secret key.

<Warning>
  Keep your secret key secure. Never expose it in client-side code, public
  repositories, or browser requests. The hash must be computed server-side.
</Warning>

### Request Format

All requests follow the same envelope:

```json theme={null}
{
  "method": "payment.in | payment.out | payment.status",
  "service_id": 14701,
  "params": {
    "payment": { ... }
  }
}
```

| Field        | Type    | Required | Description                                                                                                                                             |
| ------------ | ------- | :------: | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `method`     | string  |    Yes   | The operation to perform                                                                                                                                |
| `service_id` | integer |   Yes\*  | Routing identifier assigned per merchant. Determines the provider, payment method, currency, and direction. Required for `payment.in` and `payment.out` |
| `params`     | object  |    Yes   | Contains the `payment` object with operation-specific fields                                                                                            |

### Response Format

All responses follow a consistent envelope:

```json theme={null}
{
  "success": true,
  "result": {
    "payment": { ... }
  },
  "request_id": "req_abc123",
  "processing_time": 42
}
```

| Field             | Type           | Description                                                     |
| ----------------- | -------------- | --------------------------------------------------------------- |
| `success`         | boolean        | Whether the operation succeeded                                 |
| `result`          | object or null | Contains the `payment` object on success                        |
| `error`           | object or null | Contains `code`, `message`, `details`, and `context` on failure |
| `request_id`      | string         | Unique identifier for this request (useful for debugging)       |
| `processing_time` | number         | Server processing time in milliseconds                          |

***

## Identifiers

Every payment uses three identifiers that appear in the `identifiers` object:

| Identifier | Type   | Description                                                                                                                                                                                   |
| ---------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `c_id`     | string | **Client ID** -- your merchant reference. You provide this when creating a payment. Must be unique per payment.                                                                               |
| `h_id`     | string | **Hub ID** -- system-assigned payment ID. Assigned automatically when the payment is created.                                                                                                 |
| `p_id`     | string | **Provider ID** -- the payment provider's transaction reference. Assigned when the payment reaches the provider. Returned for reconciliation only; it is not accepted as a status lookup key. |

<Info>
  Use `c_id` as your primary reference for tracking payments. It is the key you
  control and should map to your internal order or transaction ID.
</Info>

***

## Creating a Deposit (payment.in)

Use `payment.in` to create a deposit where a customer pays you.

<Steps>
  <Step title="Build the request body">
    Include the payment amount, currency, customer (payer) details, and your
    unique `c_id`.
  </Step>

  <Step title="Compute the authentication hash">
    Compute `sha512(requestBody + secretKey)` and set it in the `X-Data-Hash`
    header.
  </Step>

  <Step title="Send the request">
    POST to `/public/api/multihub/v1` and handle the response.
  </Step>

  <Step title="Redirect the customer">
    If the response includes a `redirect.to` URL, redirect the customer to
    complete payment.
  </Step>
</Steps>

### Request

<CodeGroup>
  ```bash cURL theme={null}
  BODY='{"method":"payment.in","service_id":14701,"params":{"payment":{"description":"Order #12345","identifiers":{"c_id":"12345"},"amount":{"value":10000,"currency":"INR"},"payer":{"email":"customer@example.com","phone":"9876543210","person":{"first_name":"John","last_name":"Doe"},"customer_account":{"id":"ACC123"}},"client":{"language":"EN","country":"IN"}}}}'
  HASH=$(printf '%s%s' "$BODY" "YOUR_SECRET_KEY" | sha512sum | awk '{print $1}')
  TIMESTAMP=$(date +%s)
  NONCE=$(uuidgen | tr '[:upper:]' '[:lower:]')

  curl -X POST "https://api.bafanglaicai88.com/public/api/multihub/v1" \
    -H "Content-Type: application/json" \
    -H "X-Data-Application-Id: 42" \
    -H "X-Data-Hash: ${HASH}" \
    -H "X-Data-Timestamp: ${TIMESTAMP}" \
    -H "X-Data-Nonce: ${NONCE}" \
    --data-binary "${BODY}"
  ```

  ```python Python theme={null}
  import hashlib
  import json
  import time
  import uuid
  import requests

  secret_key = "YOUR_SECRET_KEY"
  app_id = 42

  body = {
      "method": "payment.in",
      "service_id": 14701,
      "params": {
          "payment": {
              "description": "Order #12345",
              "identifiers": {"c_id": "12345"},
              "amount": {"value": 10000, "currency": "INR"},
              "payer": {
                  "email": "customer@example.com",
                  "phone": "9876543210",
                  "person": {"first_name": "John", "last_name": "Doe"},
                  "customer_account": {"id": "ACC123"},
              },
              "client": {"language": "EN", "country": "IN"},
          }
      },
  }

  body_str = json.dumps(body, separators=(",", ":"))
  hash_value = hashlib.sha512((body_str + secret_key).encode()).hexdigest()

  response = requests.post(
      "https://api.bafanglaicai88.com/public/api/multihub/v1",
      headers={
          "Content-Type": "application/json",
          "X-Data-Application-Id": str(app_id),
          "X-Data-Hash": hash_value,
          "X-Data-Timestamp": str(int(time.time())),
          "X-Data-Nonce": str(uuid.uuid4()),
      },
      data=body_str,
      timeout=(3.05, 30),
  )

  result = response.json()
  print(result)
  ```

  ```javascript Node.js theme={null}
  const crypto = require("crypto");
  const axios = require("axios");

  const secretKey = "YOUR_SECRET_KEY";
  const appId = 42;

  const body = {
    method: "payment.in",
    service_id: 14701,
    params: {
      payment: {
        description: "Order #12345",
        identifiers: { c_id: "12345" },
        amount: { value: 10000, currency: "INR" },
        payer: {
          email: "customer@example.com",
          phone: "9876543210",
          person: { first_name: "John", last_name: "Doe" },
          customer_account: { id: "ACC123" },
        },
        client: { language: "EN", country: "IN" },
      },
    },
  };

  const bodyStr = JSON.stringify(body);
  const hash = crypto
    .createHash("sha512")
    .update(bodyStr + secretKey)
    .digest("hex");
  const timestamp = Math.floor(Date.now() / 1000).toString();
  const nonce = crypto.randomUUID();

  const response = await axios.post(
    "https://api.bafanglaicai88.com/public/api/multihub/v1",
    bodyStr,
    {
      headers: {
        "Content-Type": "application/json",
        "X-Data-Application-Id": String(appId),
        "X-Data-Hash": hash,
        "X-Data-Timestamp": timestamp,
        "X-Data-Nonce": nonce,
      },
      timeout: 30000,
    },
  );

  console.log(response.data);
  ```
</CodeGroup>

### Deposit Request Fields

For `payment.in`, `payer` is required and must be included under `params.payment.payer`.

| Field                       | Type    |   Required  | Description                                                |
| --------------------------- | ------- | :---------: | ---------------------------------------------------------- |
| `description`               | string  |      No     | Human-readable description for the payment                 |
| `identifiers.c_id`          | string  |     Yes     | Your unique client reference for this payment              |
| `amount.value`              | integer |     Yes     | Payment amount in minor units (e.g., paise for INR)        |
| `amount.currency`           | string  |     Yes     | ISO 4217 currency code                                     |
| `payer`                     | object  |     Yes     | Customer who sends funds for `payment.in`                  |
| `payer.email`               | string  | Recommended | Customer email address (may be required by some providers) |
| `payer.phone`               | string  |      No     | Customer phone number                                      |
| `payer.person.first_name`   | string  |      No     | Customer first name                                        |
| `payer.person.last_name`    | string  |      No     | Customer last name                                         |
| `payer.customer_account.id` | string  |      No     | Customer account identifier in your system                 |
| `client.language`           | string  |      No     | ISO 639-1 language code for the payment page               |
| `client.country`            | string  |      No     | ISO 3166-1 alpha-2 country code                            |

### Response

```json theme={null}
{
  "success": true,
  "result": {
    "payment": {
      "payer": {
        "email": "customer@example.com",
        "phone": "9876543210",
        "person": { "first_name": "John", "last_name": "Doe" }
      },
      "amount": { "value": 10000, "currency": "INR" },
      "description": "Order #12345",
      "identifiers": { "c_id": "12345", "h_id": "1001", "p_id": "txn_abc123" },
      "status": {
        "status": "created",
        "final": false,
        "success": null,
        "error": null,
        "history": [
          {
            "status": "created",
            "final": false,
            "success": null,
            "created": "2026-01-15T10:30:00Z",
            "reason": null,
            "amount": 10000
          }
        ]
      },
      "timestamps": {
        "created": "2026-01-15T10:30:00Z",
        "updated": "2026-01-15T10:30:00Z"
      },
      "destination": "in",
      "service_id": 14701
    }
  },
  "request_id": "req_abc123",
  "processing_time": 42
}
```

<Info>
  For UPI payments (India), the response may include a `redirect` object with
  UPI deep-link URLs in the `redirect.to` field. For other payment methods,
  redirect may not be present in the initial response.
</Info>

### Full Deposit Response Examples

<Tabs>
  <Tab title="INR (UPI)">
    ```json theme={null}
    {
      "success": true,
      "result": {
        "payment": {
          "payer": {
            "customer_account": { "id": "123567890" },
            "email": "customer@example.com",
            "phone": "9013679859",
            "person": { "first_name": "John", "last_name": "Doe" }
          },
          "receiver": {},
          "amount": { "value": 30000, "currency": "INR" },
          "description": "Deposit",
          "identifiers": {
            "c_id": "your-unique-id-123",
            "h_id": "1774892151645",
            "p_id": "SLW17748921516820Q58KA"
          },
          "redirect": {
            "to": [
              "upi://pay?pa=merchant@bank&pn=Merchant&cu=INR&am=300.00",
              "paytmmp://pay?pa=merchant@bank&pn=Merchant&cu=INR&am=300.00",
              "gpay://upi/pay?pa=merchant@bank&pn=Merchant&cu=INR&am=300.00",
              "phonepe://pay?pa=merchant@bank&pn=Merchant&cu=INR&am=300.00"
            ]
          },
          "status": {
            "status": "processing",
            "final": false,
            "success": null,
            "error": null,
            "history": [
              { "status": "created", "final": false, "success": null, "created": "2026-03-30T17:35:51.645Z", "reason": null, "amount": 30000 },
              { "status": "processing", "final": false, "success": null, "created": "2026-03-30T17:35:51.645Z", "reason": null, "amount": 30000 }
            ]
          },
          "timestamps": { "created": "2026-03-30T17:35:51.645Z", "updated": "2026-03-30T17:35:51.645Z" },
          "destination": "in",
          "operations": [{
            "id": "1774892151645",
            "operation_type": "payment.in",
            "amount": { "value": 30000, "currency": "INR" },
            "timestamps": { "created": "2026-03-30T17:35:51.645Z" },
            "status": { "status": "processing", "final": false, "success": null, "error": null }
          }],
          "service_id": 18101
        },
        "operation": {
          "id": "1774892151645",
          "operation_type": "payment.in",
          "amount": { "value": 30000, "currency": "INR" },
          "timestamps": { "created": "2026-03-30T17:35:51.645Z" },
          "status": { "status": "processing", "final": false, "success": null, "error": null }
        }
      },
      "request_id": "4b65a669-19c6-4f2c-9370-ce65d272e7dd",
      "processing_time": 2139
    }
    ```
  </Tab>

  <Tab title="TRY (Havale)">
    ```json theme={null}
    {
      "success": true,
      "result": {
        "payment": {
          "payer": {
            "customer_account": { "id": "483554399" },
            "email": "orkun@test.com",
            "phone": "905412897534",
            "person": { "first_name": "Orkun", "last_name": "Hirlas" }
          },
          "receiver": {
            "bank_account": {
              "id": "TR850021000000145137400001",
              "name": "Mertcan Artun"
            }
          },
          "amount": { "value": 100000, "currency": "TRY" },
          "description": "Turkey deposit 1000 TRY",
          "identifiers": {
            "c_id": "your-unique-id",
            "h_id": "6a8a2eab-7f0a-493f-a1c2-563837e1003a",
            "p_id": "79fef702-a597-4126-8376-a02cae4c6e62"
          },
          "redirect": {
            "to": "https://example.com/Payment/Havale?trackingID=79fef702-..."
          },
          "status": {
            "status": "processing",
            "final": false,
            "success": null,
            "error": null,
            "history": [
              { "status": "created", "final": false, "success": null, "created": "2026-04-02T08:37:50.431Z", "reason": null, "amount": 100000 },
              { "status": "processing", "final": false, "success": null, "created": "2026-04-02T08:37:50.431Z", "reason": null, "amount": 100000 }
            ]
          },
          "timestamps": { "created": "2026-04-02T08:37:50.431Z", "updated": "2026-04-02T08:37:50.431Z" },
          "destination": "in",
          "operations": [{
            "id": "6a8a2eab-7f0a-493f-a1c2-563837e1003a",
            "operation_type": "payment.in",
            "amount": { "value": 100000, "currency": "TRY" },
            "timestamps": { "created": "2026-04-02T08:37:50.431Z" },
            "status": { "status": "processing", "final": false, "success": null, "error": null }
          }],
          "service_id": 13001
        },
        "operation": {
          "id": "6a8a2eab-7f0a-493f-a1c2-563837e1003a",
          "operation_type": "payment.in",
          "amount": { "value": 100000, "currency": "TRY" },
          "timestamps": { "created": "2026-04-02T08:37:50.431Z" },
          "status": { "status": "processing", "final": false, "success": null, "error": null }
        }
      },
      "request_id": "d19d5015-ee74-4187-a5c3-202ee3a48d38",
      "processing_time": 526
    }
    ```
  </Tab>

  <Tab title="ARS (Bank transfer)">
    ```json theme={null}
    {
      "success": true,
      "result": {
        "payment": {
          "payer": {
            "email": "juan@example.com",
            "phone": "+5491155551234",
            "person": { "first_name": "Juan", "last_name": "Perez" }
          },
          "receiver": {},
          "amount": { "value": 200000, "currency": "ARS" },
          "description": "Deposit ARS",
          "identifiers": {
            "c_id": "your-unique-id",
            "h_id": "b8166e5e-2ec8-4c9c-b5a0-157a54e6ee65",
            "p_id": "pay_1775118854277_08ac84a5"
          },
          "status": {
            "status": "processing",
            "final": false,
            "success": null,
            "error": null,
            "history": [
              { "status": "created", "final": false, "success": null, "created": "2026-04-02T08:34:14.001Z", "reason": null, "amount": 200000 },
              { "status": "processing", "final": false, "success": null, "created": "2026-04-02T08:34:14.001Z", "reason": null, "amount": 200000 }
            ]
          },
          "timestamps": { "created": "2026-04-02T08:34:14.001Z", "updated": "2026-04-02T08:34:14.001Z" },
          "destination": "in",
          "operations": [{
            "id": "b8166e5e-2ec8-4c9c-b5a0-157a54e6ee65",
            "operation_type": "payment.in",
            "amount": { "value": 200000, "currency": "ARS" },
            "timestamps": { "created": "2026-04-02T08:34:14.001Z" },
            "status": { "status": "processing", "final": false, "success": null, "error": null }
          }],
          "service_id": 12401
        },
        "operation": {
          "id": "b8166e5e-2ec8-4c9c-b5a0-157a54e6ee65",
          "operation_type": "payment.in",
          "amount": { "value": 200000, "currency": "ARS" },
          "timestamps": { "created": "2026-04-02T08:34:14.001Z" },
          "status": { "status": "processing", "final": false, "success": null, "error": null }
        }
      },
      "request_id": "0c87254a-4bb4-411c-93b8-b7e3c3e0bf54",
      "processing_time": 379
    }
    ```
  </Tab>
</Tabs>

***

## Creating a Withdrawal (payment.out)

Use `payment.out` to send money to a recipient (payout).

<Warning>
  Withdrawals require sufficient available balance and must fit the merchant
  payout limits configured for the requested currency. The API returns `6004`
  when balance is too low, `6006` for daily limit breaches, `6005` for monthly
  limit breaches, and `6035` for other payout-limit rules such as
  since-last-settlement limits.
</Warning>

<Info>
  Use `params.payment.identifiers.c_id` as your own payout identifier and
  idempotency key. The same `c_id` is returned in the create response,
  `payment.status`, and payout webhooks under
  `data.result.payment.identifiers.c_id`.
</Info>

### Request

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://api.bafanglaicai88.com/public/api/multihub/v1" \
    -H "Content-Type: application/json" \
    -H "X-Data-Application-Id: 42" \
    -H "X-Data-Hash: YOUR_COMPUTED_HASH" \
    -H "X-Data-Timestamp: CURRENT_UNIX_TIMESTAMP" \
    -H "X-Data-Nonce: UNIQUE_REQUEST_NONCE" \
    -d '{
      "method": "payment.out",
      "service_id": 14701,
      "params": {
        "payment": {
          "description": "Payout #67890",
          "identifiers": { "c_id": "67890" },
          "amount": { "value": 50000, "currency": "INR" },
          "receiver": {
            "bank": {
              "account": { "id": "1234567890" },
              "ifsc": "SBIN0001234"
            },
            "email": "recipient@example.com",
            "phone": "9876543210",
            "person": { "first_name": "Jane", "last_name": "Doe" }
          }
        }
      }
    }'
  ```

  ```python Python theme={null}
  import hashlib
  import json
  import requests

  secret_key = "YOUR_SECRET_KEY"
  app_id = 42

  body = {
      "method": "payment.out",
      "service_id": 14701,
      "params": {
          "payment": {
              "description": "Payout #67890",
              "identifiers": {"c_id": "67890"},
              "amount": {"value": 50000, "currency": "INR"},
              "receiver": {
                  "bank": {
                      "account": {"id": "1234567890"},
                      "ifsc": "SBIN0001234",
                  },
                  "email": "recipient@example.com",
                  "phone": "9876543210",
                  "person": {"first_name": "Jane", "last_name": "Doe"},
              },
          }
      },
  }

  body_str = json.dumps(body, separators=(",", ":"))
  hash_value = hashlib.sha512((body_str + secret_key).encode()).hexdigest()

  response = requests.post(
      "https://api.bafanglaicai88.com/public/api/multihub/v1",
      headers={
          "Content-Type": "application/json",
          "X-Data-Application-Id": str(app_id),
          "X-Data-Hash": hash_value,
      },
      data=body_str,
  )

  result = response.json()
  print(result)
  ```

  ```javascript Node.js theme={null}
  const crypto = require("crypto");
  const axios = require("axios");

  const secretKey = "YOUR_SECRET_KEY";
  const appId = 42;

  const body = {
    method: "payment.out",
    service_id: 14701,
    params: {
      payment: {
        description: "Payout #67890",
        identifiers: { c_id: "67890" },
        amount: { value: 50000, currency: "INR" },
        receiver: {
          bank: {
            account: { id: "1234567890" },
            ifsc: "SBIN0001234",
          },
          email: "recipient@example.com",
          phone: "9876543210",
          person: { first_name: "Jane", last_name: "Doe" },
        },
      },
    },
  };

  const bodyStr = JSON.stringify(body);
  const hash = crypto
    .createHash("sha512")
    .update(bodyStr + secretKey)
    .digest("hex");

  const response = await axios.post(
    "https://api.bafanglaicai88.com/public/api/multihub/v1",
    bodyStr,
    {
      headers: {
        "Content-Type": "application/json",
        "X-Data-Application-Id": String(appId),
        "X-Data-Hash": hash,
      },
    },
  );

  console.log(response.data);
  ```
</CodeGroup>

### Region-Specific Withdrawal Examples

The `receiver.bank` object varies by region. Below are the key differences:

<Tabs>
  <Tab title="India (INR)">
    ```json theme={null}
    {
      "receiver": {
        "bank": {
          "account": { "id": "1234567890" },
          "ifsc": "SBIN0001234"
        },
        "person": { "first_name": "Jane", "last_name": "Doe" }
      }
    }
    ```
  </Tab>

  <Tab title="Mexico (MXN)">
    ```json theme={null}
    {
      "receiver": {
        "bank": {
          "account": { "id": "012345678901234567" }
        },
        "person": { "first_name": "Juan", "last_name": "Perez" }
      }
    }
    ```
  </Tab>

  <Tab title="Argentina (ARS)">
    ```json theme={null}
    {
      "receiver": {
        "bank": {
          "account": { "id": "0110599940000041227728" },
          "code": "BBVA"
        },
        "person": { "first_name": "Juan", "last_name": "Perez" }
      }
    }
    ```
  </Tab>

  <Tab title="Turkey (TRY)">
    ```json theme={null}
    {
      "receiver": {
        "bank": {
          "account": { "id": "TR330006100519786457841326" }
        },
        "person": { "first_name": "Mehmet", "last_name": "Yilmaz" }
      }
    }
    ```
  </Tab>
</Tabs>

### Withdrawal Request Fields

| Field                        | Type    |       Required       | Description                                                                   |
| ---------------------------- | ------- | :------------------: | ----------------------------------------------------------------------------- |
| `description`                | string  |          No          | Human-readable description for the payout                                     |
| `identifiers.c_id`           | string  |          Yes         | Your unique client reference and idempotency key for this payout              |
| `amount.value`               | integer |          Yes         | Payout amount in minor units                                                  |
| `amount.currency`            | string  |          Yes         | ISO 4217 currency code                                                        |
| `receiver.bank.account.id`   | string  |          Yes         | Recipient bank account number, IBAN, CBU/CVU, or CLABE depending on the route |
| `receiver.bank.ifsc`         | string  |      India only      | IFSC routing code (e.g., `SBIN0001234`)                                       |
| `receiver.bank.code`         | string  | Argentina (optional) | Bank code (e.g., `BBVA`)                                                      |
| `receiver.email`             | string  |          No          | Recipient email address                                                       |
| `receiver.phone`             | string  |          No          | Recipient phone number                                                        |
| `receiver.person.first_name` | string  |          No          | Recipient first name                                                          |
| `receiver.person.last_name`  | string  |          No          | Recipient last name                                                           |

<Info>
  **Turkey (TRY) withdrawals** use `bank_transfer` with IBAN format in
  `receiver.bank.account.id` (e.g., `TR330006100519786457841326`). No `ifsc` or
  `code` is required.
</Info>

<Info>
  **Deposits use `payer`, withdrawals use `receiver`.** The `payer` object
  describes who is sending the money (deposit), while the `receiver` object
  describes who is receiving the money (withdrawal). Both contain contact
  information and, for `receiver`, bank account details.
</Info>

### Tracking Withdrawal Completion

The initial `payment.out` response usually has `status.status: "processing"`. Wait for a webhook or poll `payment.status` with your `c_id` or the returned `h_id`.

| Identifier | Meaning                                | Where to use it                            |
| ---------- | -------------------------------------- | ------------------------------------------ |
| `c_id`     | Your payout reference from the request | Primary lookup and reconciliation key      |
| `h_id`     | 123hub payment/operation identifier    | Alternative lookup key returned by the API |
| `p_id`     | Provider reference, when available     | Reconciliation only; treat as opaque       |

Payout lifecycle webhooks use the same payment envelope as deposits. Read the business identifier from `data.result.payment.identifiers.c_id`; do not depend on provider-only callback fields such as `order_id`, `transaction_id`, `session_token`, or `user_id`.

### Full Withdrawal Response Examples

<Tabs>
  <Tab title="ARS Payout">
    ```json theme={null}
    {
      "success": true,
      "result": {
        "payment": {
          "receiver": {
            "email": "carlos@example.com",
            "phone": "1155551234",
            "person": { "first_name": "Carlos", "last_name": "Garcia" },
            "bank": {
              "account": { "id": "0110599940000041227728" },
              "code": "BBVA"
            }
          },
          "amount": { "value": 1000000, "currency": "ARS" },
          "identifiers": {
            "c_id": "your-payout-id",
            "h_id": "38322de4-25ad-4619-8960-46d29cab8e20",
            "p_id": "pay_1775118858499_75a26526"
          },
          "status": {
            "status": "processing",
            "final": false,
            "success": null,
            "error": null,
            "history": [
              { "status": "created", "final": false, "success": null, "created": "2026-04-02T08:34:18.297Z", "reason": null, "amount": 1000000 }
            ]
          },
          "timestamps": { "created": "2026-04-02T08:34:18.297Z" },
          "destination": "out",
          "operations": [{
            "id": "38322de4-25ad-4619-8960-46d29cab8e20",
            "operation_type": "payment.out",
            "amount": { "value": 1000000, "currency": "ARS" },
            "timestamps": { "created": "2026-04-02T08:34:18.297Z" },
            "status": { "status": "processing", "final": false, "success": null, "error": null }
          }],
          "service_id": 12501
        },
        "operation": {
          "id": "38322de4-25ad-4619-8960-46d29cab8e20",
          "operation_type": "payment.out",
          "amount": { "value": 1000000, "currency": "ARS" },
          "timestamps": { "created": "2026-04-02T08:34:18.297Z" },
          "status": { "status": "processing", "final": false, "success": null, "error": null }
        }
      },
      "request_id": "5a92838a-3644-4fb1-9a1e-ff79d24a3bb1",
      "processing_time": 327
    }
    ```
  </Tab>

  <Tab title="TRY Payout">
    ```json theme={null}
    {
      "success": true,
      "result": {
        "payment": {
          "receiver": {
            "email": "mehmet@test.com",
            "phone": "905551234567",
            "person": { "first_name": "Mehmet", "last_name": "Yilmaz" },
            "bank": {
              "account": { "id": "TR330006100519786457841326" }
            }
          },
          "amount": { "value": 10000, "currency": "TRY" },
          "identifiers": {
            "c_id": "your-payout-id",
            "h_id": "7fdfc6f1-a302-4b7b-9585-b3fd677415ce",
            "p_id": "e77e2693-d9ee-4ce5-ad33-ba07ea9c0801"
          },
          "status": {
            "status": "processing",
            "final": false,
            "success": null,
            "error": null,
            "history": [
              { "status": "created", "final": false, "success": null, "created": "2026-04-02T08:37:54.885Z", "reason": null, "amount": 10000 }
            ]
          },
          "timestamps": { "created": "2026-04-02T08:37:54.885Z" },
          "destination": "out",
          "operations": [{
            "id": "7fdfc6f1-a302-4b7b-9585-b3fd677415ce",
            "operation_type": "payment.out",
            "amount": { "value": 10000, "currency": "TRY" },
            "timestamps": { "created": "2026-04-02T08:37:54.885Z" },
            "status": { "status": "processing", "final": false, "success": null, "error": null }
          }],
          "service_id": 13101
        },
        "operation": {
          "id": "7fdfc6f1-a302-4b7b-9585-b3fd677415ce",
          "operation_type": "payment.out",
          "amount": { "value": 10000, "currency": "TRY" },
          "timestamps": { "created": "2026-04-02T08:37:54.885Z" },
          "status": { "status": "processing", "final": false, "success": null, "error": null }
        }
      },
      "request_id": "2da38a4f-d9d4-4487-9ce1-950abbd001d1",
      "processing_time": 5687
    }
    ```
  </Tab>
</Tabs>

***

## Checking Payment Status (payment.status)

Use `payment.status` to query the current state of a payment. You can look up a payment by either `c_id` (your reference) or `h_id` (system-assigned ID).

### Query by c\_id

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://api.bafanglaicai88.com/public/api/multihub/v1" \
    -H "Content-Type: application/json" \
    -H "X-Data-Application-Id: 42" \
    -H "X-Data-Hash: YOUR_COMPUTED_HASH" \
    -H "X-Data-Timestamp: CURRENT_UNIX_TIMESTAMP" \
    -H "X-Data-Nonce: UNIQUE_REQUEST_NONCE" \
    -d '{
      "method": "payment.status",
      "params": {
        "payment": {
          "identifiers": { "c_id": "12345" }
        }
      }
    }'
  ```

  ```python Python theme={null}
  import hashlib
  import json
  import requests

  secret_key = "YOUR_SECRET_KEY"
  app_id = 42

  body = {
      "method": "payment.status",
      "params": {
          "payment": {
              "identifiers": {"c_id": "12345"}
          }
      },
  }

  body_str = json.dumps(body, separators=(",", ":"))
  hash_value = hashlib.sha512((body_str + secret_key).encode()).hexdigest()

  response = requests.post(
      "https://api.bafanglaicai88.com/public/api/multihub/v1",
      headers={
          "Content-Type": "application/json",
          "X-Data-Application-Id": str(app_id),
          "X-Data-Hash": hash_value,
      },
      data=body_str,
  )

  result = response.json()
  print(result)
  ```

  ```javascript Node.js theme={null}
  const crypto = require("crypto");
  const axios = require("axios");

  const secretKey = "YOUR_SECRET_KEY";
  const appId = 42;

  const body = {
    method: "payment.status",
    params: {
      payment: {
        identifiers: { c_id: "12345" },
      },
    },
  };

  const bodyStr = JSON.stringify(body);
  const hash = crypto
    .createHash("sha512")
    .update(bodyStr + secretKey)
    .digest("hex");

  const response = await axios.post(
    "https://api.bafanglaicai88.com/public/api/multihub/v1",
    bodyStr,
    {
      headers: {
        "Content-Type": "application/json",
        "X-Data-Application-Id": String(appId),
        "X-Data-Hash": hash,
      },
    },
  );

  console.log(response.data);
  ```
</CodeGroup>

### Query by h\_id

You can also look up a payment using the system-assigned `h_id`. When using `h_id`, the `service_id` field is not required:

```json theme={null}
{
  "method": "payment.status",
  "params": {
    "payment": {
      "identifiers": { "h_id": "1001" }
    }
  }
}
```

### Response

The response contains the full payment object with its current status:

```json theme={null}
{
  "success": true,
  "result": {
    "payment": {
      "payer": {
        "email": "customer@example.com",
        "phone": "9876543210",
        "person": { "first_name": "John", "last_name": "Doe" }
      },
      "amount": { "value": 10000, "currency": "INR" },
      "description": "Order #12345",
      "identifiers": { "c_id": "12345", "h_id": "1001", "p_id": "txn_abc123" },
      "status": {
        "status": "success",
        "final": true,
        "success": true,
        "error": null,
        "history": [
          {
            "status": "created",
            "final": false,
            "success": null,
            "created": "2026-01-15T10:30:00Z",
            "reason": null,
            "amount": 10000
          },
          {
            "status": "processing",
            "final": false,
            "success": null,
            "created": "2026-01-15T10:30:05Z",
            "reason": null,
            "amount": 10000
          },
          {
            "status": "success",
            "final": true,
            "success": true,
            "created": "2026-01-15T10:35:12Z",
            "reason": null,
            "amount": 10000
          }
        ]
      },
      "timestamps": {
        "created": "2026-01-15T10:30:00Z",
        "updated": "2026-01-15T10:35:12Z",
        "finished": "2026-01-15T10:35:12Z"
      },
      "destination": "in",
      "service_id": 14701
    }
  },
  "request_id": "req_xyz789",
  "processing_time": 12
}
```

***

## Re-sending Webhook (payment.notification)

Use `payment.notification` to trigger a webhook re-delivery for a specific payment. The request format is identical to `payment.status`, and the response returns the full payment object:

```json theme={null}
{
  "method": "payment.notification",
  "operation_id": "notification-retry-1001",
  "params": {
    "payment": {
      "identifiers": { "c_id": "your-payment-id" }
    }
  }
}
```

| Field                             | Type    | Required | Description                                                                      |
| --------------------------------- | ------- | :------: | -------------------------------------------------------------------------------- |
| `method`                          | string  |    Yes   | `"payment.notification"`                                                         |
| `operation_id`                    | string  |    No    | Stable idempotency identifier; reuse only for an exact retry                     |
| `service_id`                      | integer |    No    | Optional fallback if the original payment metadata does not contain `service_id` |
| `params.payment.identifiers.c_id` | string  |   Yes\*  | Your client reference for the payment                                            |
| `params.payment.identifiers.h_id` | string  |   Yes\*  | Hub payment ID                                                                   |

<Info>
  Provide either `c_id` or `h_id`. This method triggers a new webhook delivery
  to your active webhook endpoints with the current payment state. Use it only
  as a recovery action when your webhook handler missed or failed to process a
  notification. For read-only checks or polling, use `payment.status` with
  backoff instead. The method is limited to 10 requests per minute. Without
  `operation_id`, compatibility idempotency is scoped to the canonical request
  and current minute.
</Info>

***

## Payment Statuses

Each payment has a status object with the current status name, and boolean flags indicating whether it is final and whether it represents a success.

| Status               | Description                             | `final` | `success` |
| -------------------- | --------------------------------------- | :-----: | :-------: |
| `created`            | Payment created, waiting for processing | `false` |   `null`  |
| `processing`         | Payment is being processed              | `false` |   `null`  |
| `success`            | Payment completed successfully          |  `true` |   `true`  |
| `error`              | Payment failed                          |  `true` |  `false`  |
| `canceled`           | Payment was cancelled                   |  `true` |  `false`  |
| `declined`           | Payment expired or was declined         |  `true` |  `false`  |
| `refunded`           | Full refund processed                   |  `true` |   `true`  |
| `partially_refunded` | Partial refund processed                |  `true` |   `true`  |

<Info>
  **Terminal statuses** are indicated by `final: true`. A terminal status is the
  current completed outcome for integration handling, but reconciliation,
  refunds, or provider corrections can publish a later status update. Always
  process webhooks idempotently by payment id and status timestamp.
</Info>

### Status Object Structure

Every payment includes a `status` object with the following fields:

```json theme={null}
{
  "status": {
    "status": "processing",
    "final": false,
    "success": null,
    "error": null,
    "history": [
      {
        "status": "created",
        "final": false,
        "success": null,
        "created": "2026-01-15T10:30:00Z",
        "reason": null,
        "amount": 10000
      },
      {
        "status": "processing",
        "final": false,
        "success": null,
        "created": "2026-01-15T10:30:05Z",
        "reason": null,
        "amount": 10000
      }
    ]
  }
}
```

The `history` array contains every status transition the payment has gone through, in chronological order. This is useful for debugging and auditing.

### Handling Status Changes

```javascript theme={null}
// Example: Checking payment status and acting on it
const { payment } = response.result;
const { status } = payment.status;

if (payment.status.final) {
  if (payment.status.status === "success") {
    await markOrderAsPaid(payment.identifiers.c_id);
  } else if (
    payment.status.status === "refunded" ||
    payment.status.status === "partially_refunded"
  ) {
    await updateRefundState(payment.identifiers.c_id, payment.status.status);
  } else {
    // Payment failed (error, canceled, declined)
    await notifyPaymentFailed(payment.identifiers.c_id, payment.status.error);
  }
} else {
  // Payment still in progress (created, processing)
  // Wait for webhook or poll again later
}
```

***

## Redirect URLs

For deposit payments (`payment.in`), the response may include a `redirect` object. For UPI payments (India), this contains deep-link URLs for mobile payment apps:

```json theme={null}
{
  "redirect": {
    "to": [
      "upi://pay?pa=merchant@bank&am=100",
      "paytmmp://pay?pa=merchant@bank&am=100",
      "gpay://upi/pay?pa=merchant@bank&am=100",
      "phonepe://pay?pa=merchant@bank&am=100"
    ]
  }
}
```

| Field        | Description                                                                                                                                                                             |
| ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `to`         | A URL string or array of URLs for completing the payment. Type varies: **array** of UPI deep-links for India, **string** HTTPS URL for Turkey (Havale). May be absent for other regions |
| `on_fail`    | The URL the customer is redirected to if the payment fails (when available)                                                                                                             |
| `on_success` | The URL the customer is redirected to after a successful payment (when available)                                                                                                       |

<Note>
  The `redirect.to` field can contain UPI deep-link URLs, a provider HTTPS
  payment page URL, or another provider-specific redirect target. The `redirect`
  field may be absent if no redirect URL is available for the payment method.
</Note>

### Deposit Response by Region

For bank transfer deposits, the response may include payment details the customer needs to complete the transfer. The exact fields depend on the region:

| Region                    | `redirect.to`                   | `receiver.bank_account`                | Action                                                                |
| ------------------------- | ------------------------------- | -------------------------------------- | --------------------------------------------------------------------- |
| India (UPI)               | Array of UPI deep-link URLs     | Empty (`receiver: {}`)                 | Redirect customer to UPI app                                          |
| Turkey (Havale)           | HTTPS URL string (payment page) | `{id: "IBAN", name: "Holder"}`         | Show bank details AND/OR redirect                                     |
| Argentina (Bank transfer) | Not present                     | `{id: "CBU", name: "Holder"}`          | Show bank details to customer                                         |
| Venezuela (Pago Móvil)    | Optional provider redirect      | Optional account plus typed P2P fields | Show phone, document, bank and QR instructions returned in `receiver` |

For TRY and ARS, the `receiver.bank_account` object contains the bank details the customer must transfer to:

```json theme={null}
{
  "receiver": {
    "bank_account": {
      "id": "TR850021000000145137400001",
      "name": "Account Holder Name"
    }
  }
}
```

| Field                        | Type   | Description                                        |
| ---------------------------- | ------ | -------------------------------------------------- |
| `receiver.bank_account.id`   | string | Bank account number, IBAN, or CBU for the transfer |
| `receiver.bank_account.name` | string | Account holder name                                |

Pago Móvil and similar P2P routes may return additional typed instructions:

```json theme={null}
{
  "receiver": {
    "bank_account": {
      "id": "01040019860190162931",
      "name": "Jenny Dias Matias"
    },
    "bank": {
      "name": "Banco Venezolano de Credito",
      "code": "0104"
    },
    "phone": "+584121234567",
    "document": {
      "type": "cedula",
      "number": "V12345678"
    },
    "qr_payload": "000201010212..."
  }
}
```

Only normalized instruction fields are returned. Provider-specific metadata is never copied into
the public response. Existing payments are not retroactively enriched; integrations must map new
provider responses to the canonical Studio `bankDetails.*` targets.

***

## Customer Data

### Deposits: the `payer` Object

For `payment.in` requests, the `payer` object describes the customer making the payment:

```json theme={null}
{
  "payer": {
    "email": "customer@example.com",
    "phone": "9876543210",
    "person": {
      "first_name": "John",
      "last_name": "Doe"
    },
    "customer_account": {
      "id": "ACC123"
    }
  }
}
```

### Withdrawals: the `receiver` Object

For `payment.out` requests, the `receiver` object describes who receives the payout, including their bank details:

```json theme={null}
{
  "receiver": {
    "bank": {
      "account": { "id": "1234567890" },
      "ifsc": "SBIN0001234"
    },
    "email": "recipient@example.com",
    "phone": "9876543210",
    "person": {
      "first_name": "Jane",
      "last_name": "Doe"
    }
  }
}
```

The `receiver.bank` object contains the banking details required to execute the transfer. The specific fields depend on the region and payment method:

| Region          | `bank.account.id` | Additional Field  | Notes                              |
| --------------- | ----------------- | ----------------- | ---------------------------------- |
| India (INR)     | Account number    | `ifsc` (required) | IFSC routing code                  |
| Mexico (MXN)    | CLABE (18 digits) | None              | Put the CLABE in `bank.account.id` |
| Argentina (ARS) | CBU (22 digits)   | `code` (optional) | Bank code, e.g. `BBVA`             |
| Turkey (TRY)    | IBAN (`TR...`)    | --                | No additional fields               |

***

## Additional Response Fields

### Operations

Every payment response includes an `operations` array and an `operation` shortcut:

| Field                                        | Type   | Description                                                                                          |
| -------------------------------------------- | ------ | ---------------------------------------------------------------------------------------------------- |
| `result.payment.operations`                  | array  | Array of operations when operation metadata is available; when present it contains at least one item |
| `result.payment.operations[].id`             | string | Operation ID (same as `h_id` for the primary operation)                                              |
| `result.payment.operations[].operation_type` | string | `"payment.in"` or `"payment.out"`                                                                    |
| `result.payment.operations[].amount`         | object | `{value, currency}` -- operation amount                                                              |
| `result.payment.operations[].timestamps`     | object | `{created, updated?, finished?}`                                                                     |
| `result.payment.operations[].status`         | object | Status object (`status`, `final`, `success`, `error`)                                                |
| `result.operation`                           | object | Shortcut -- same as `operations[0]`                                                                  |

```json theme={null}
{
  "operations": [
    {
      "id": "b8166e5e-2ec8-4c9c-b5a0-157a54e6ee65",
      "operation_type": "payment.in",
      "amount": { "value": 200000, "currency": "ARS" },
      "timestamps": { "created": "2026-04-02T08:34:14.001Z" },
      "status": {
        "status": "processing",
        "final": false,
        "success": null,
        "error": null
      }
    }
  ],
  "operation": {
    "id": "b8166e5e-2ec8-4c9c-b5a0-157a54e6ee65",
    "operation_type": "payment.in",
    "amount": { "value": 200000, "currency": "ARS" },
    "timestamps": { "created": "2026-04-02T08:34:14.001Z" },
    "status": {
      "status": "processing",
      "final": false,
      "success": null,
      "error": null
    }
  }
}
```

### Identifier Formats

The `h_id` format varies by provider. Treat it as an opaque string:

* Some providers return numeric strings (e.g., `"1774892151645"`)
* Others return UUIDs (e.g., `"b8adedc9-c245-4fa2-8f26-095e8117d11a"`)

### `status.error` Field

The `status.error` field inside the payment status object can be:

* `null` -- no error
* `integer` -- error code (e.g., `7001`)
* `string` -- error message from provider (e.g., `"Sum 150000 is lower than 200000"`)

<Note>
  Do not confuse `result.payment.status.error` (integer/string/null inside the
  payment status) with the top-level `error` object in error API responses
  (which has `code`, `message`, `details`, `context` fields).
</Note>

### Bank Details in `payment.status`

<Warning>
  Bank details (`receiver.bank`) are only available in the response to
  `payment.out` creation. They are **not returned** in `payment.status` or
  webhook callbacks. Store them when you create the withdrawal.
</Warning>

***

## Idempotency

The `c_id` (Client ID) serves as your idempotency key. If you send a `payment.in` or `payment.out` request with a `c_id` that was already used, the API will return error code `6009` (Payment already exists) instead of creating a duplicate.

<Warning>
  Always use a unique `c_id` for each payment. If you receive a `6009` error, it
  means a payment with that `c_id` already exists. Use `payment.status` to
  retrieve its current state rather than creating a new one.
</Warning>

```javascript theme={null}
// Example: Handling duplicate payment attempts
const response = await createPayment(body);

if (!response.success && response.error?.code === 6009) {
  // Payment already exists -- fetch its current status instead
  const statusResponse = await checkPaymentStatus(
    body.params.payment.identifiers.c_id,
  );
  return statusResponse.result.payment;
}
```

***

## Error Handling

When a request fails, the response will have `success: false` and include an `error` object. Error responses return **HTTP 400**:

```json theme={null}
{
  "success": false,
  "error": {
    "code": 6010,
    "message": "Payment does not exist",
    "details": null,
    "context": null
  },
  "request_id": "req_err789",
  "processing_time": 5
}
```

<Note>
  Successful responses return HTTP 200. Error responses return HTTP 400 (or HTTP
  404 for unknown methods). Always check the `success` field in the response
  body to determine the outcome.
</Note>

### Error Codes

| Code   | Description                                     | Recommended Action                                                                     |
| ------ | ----------------------------------------------- | -------------------------------------------------------------------------------------- |
| `1005` | Invalid request format (missing required field) | Check the `details` field for specifics on which field is missing                      |
| `6001` | Incorrect transaction amount                    | Verify amount is a positive integer in minor units                                     |
| `6002` | Incorrect currency code                         | Ensure currency matches the `service_id` configuration                                 |
| `6004` | Insufficient funds (withdrawals)                | Top up your merchant balance before retrying                                           |
| `6005` | Monthly payout limit exceeded                   | Wait until next merchant-timezone month or contact account manager                     |
| `6006` | Daily payout limit exceeded                     | Wait until next merchant-timezone day or contact account manager                       |
| `6009` | Payment already exists (duplicate `c_id`)       | Use `payment.status` to retrieve the existing payment                                  |
| `6010` | Payment does not exist                          | Verify the `c_id` or `h_id` is correct                                                 |
| `6035` | Exceeded payments                               | Contact account manager to review payout limits, including since-last-settlement rules |
| `7001` | Provider interaction error                      | Retry after a delay or contact support                                                 |
| `8600` | Invalid bank credentials                        | Verify bank account number and routing code format                                     |
| `8801` | Invalid status transition                       | Check current payment status before attempting operations                              |

### Error Handling Example

<CodeGroup>
  ```python Python theme={null}
  import hashlib
  import json
  import requests

  secret_key = "YOUR_SECRET_KEY"
  app_id = 42

  body = {
      "method": "payment.in",
      "service_id": 14701,
      "params": {
          "payment": {
              "description": "Order #12345",
              "identifiers": {"c_id": "12345"},
              "amount": {"value": 10000, "currency": "INR"},
              "payer": {
                  "email": "customer@example.com",
                  "phone": "9876543210",
                  "person": {"first_name": "John", "last_name": "Doe"},
              },
          }
      },
  }

  body_str = json.dumps(body, separators=(",", ":"))
  hash_value = hashlib.sha512((body_str + secret_key).encode()).hexdigest()

  response = requests.post(
      "https://api.bafanglaicai88.com/public/api/multihub/v1",
      headers={
          "Content-Type": "application/json",
          "X-Data-Application-Id": str(app_id),
          "X-Data-Hash": hash_value,
      },
      data=body_str,
  )

  result = response.json()

  if result["success"]:
      payment = result["result"]["payment"]
      print(f"Payment created: h_id={payment['identifiers']['h_id']}")
      if payment.get("redirect", {}).get("to"):
          print(f"Redirect customer to: {payment['redirect']['to']}")
  else:
      error = result["error"]
      print(f"Error {error['code']}: {error['message']}")

      if error["code"] == 6009:
          print("Payment already exists. Fetching status...")
      elif error["code"] == 6004:
          print("Insufficient funds. Please top up.")
      elif error["code"] in (6005, 6006, 6035):
          print("Payout limit exceeded. Contact account manager.")
      elif error["code"] == 1005:
          print("Missing required field. Check error details.")
  ```

  ```javascript Node.js theme={null}
  const crypto = require("crypto");
  const axios = require("axios");

  const secretKey = "YOUR_SECRET_KEY";
  const appId = 42;

  async function createDeposit(cId, amount, currency, payer) {
    const body = {
      method: "payment.in",
      service_id: 14701,
      params: {
        payment: {
          identifiers: { c_id: cId },
          amount: { value: amount, currency },
          payer,
        },
      },
    };

    const bodyStr = JSON.stringify(body);
    const hash = crypto
      .createHash("sha512")
      .update(bodyStr + secretKey)
      .digest("hex");

    const response = await axios.post(
      "https://api.bafanglaicai88.com/public/api/multihub/v1",
      bodyStr,
      {
        headers: {
          "Content-Type": "application/json",
          "X-Data-Application-Id": String(appId),
          "X-Data-Hash": hash,
        },
      },
    );

    const result = response.data;

    if (result.success) {
      const payment = result.result.payment;
      console.log(`Payment created: h_id=${payment.identifiers.h_id}`);
      return payment;
    }

    const { code, message } = result.error;
    console.error(`Error ${code}: ${message}`);

    switch (code) {
      case 6009:
        console.log("Payment already exists. Fetching status...");
        break;
      case 6004:
        console.log("Insufficient funds. Please top up.");
        break;
      case 6005:
      case 6006:
      case 6035:
        console.log("Payout limit exceeded. Contact account manager.");
        break;
      case 1005:
        console.log("Missing required field. Check error details.");
        break;
      default:
        console.log("Unexpected error. Contact support.");
    }

    throw new Error(`Payment API error ${code}: ${message}`);
  }
  ```
</CodeGroup>

***

## Best Practices

<CardGroup cols={2}>
  <Card title="Use Unique c_id Values" icon="fingerprint">
    Always assign a unique `c_id` to each payment. This serves as your
    idempotency key and prevents duplicate payments.
  </Card>

  <Card title="Compute Hashes Server-Side" icon="lock">
    Never expose your secret key to the client. Always compute the `X-Data-Hash`
    on your server.
  </Card>

  <Card title="Check success Field" icon="circle-check">
    Always check the `success` boolean in the response body to determine the
    outcome. Success returns HTTP 200, errors return HTTP 400.
  </Card>

  <Card title="Store All Identifiers" icon="database">
    Save `c_id`, `h_id`, and `p_id` from the response for reconciliation,
    debugging, and support inquiries.
  </Card>

  <Card title="Handle Redirects" icon="arrow-up-right-from-square">
    For deposits, always check for a `redirect.to` URL and redirect the customer
    to complete their payment.
  </Card>

  <Card title="Use Status History" icon="clock-rotate-left">
    The `status.history` array provides a complete audit trail. Use it for
    debugging and tracking payment lifecycle.
  </Card>
</CardGroup>
