Payments Guide
This guide covers everything you need to know about creating and managing payments through the 123hub MultiHub API. All payment operations use a single endpoint with method-based routing.Overview
The MultiHub 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:success field in the response body to determine the outcome.
Authentication
Every request requires two headers:| 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... |
requestBody is the raw JSON string of the request body, and secretKey is your merchant secret key.
Request Format
All requests follow the same envelope:| 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:| 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 theidentifiers 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. |
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.Creating a Deposit (payment.in)
Usepayment.in to create a deposit where a customer pays you.
Build the request body
Include the payment amount, currency, customer (payer) details, and your unique
c_id.Compute the authentication hash
Compute
sha512(requestBody + secretKey) and set it in the X-Data-Hash header.Request
Deposit Request Fields
| 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.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
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.Full Deposit Response Examples
- INR (UPI)
- TRY (Havale)
- ARS (Bank transfer)
Creating a Withdrawal (payment.out)
Usepayment.out to send money to a recipient (payout).
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.Request
Region-Specific Withdrawal Examples
Thereceiver.bank object varies by region. Below are the key differences:
- India (INR)
- Mexico (MXN)
- Argentina (ARS)
- Turkey (TRY)
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 |
Turkey (TRY) withdrawals use
bank_transfer with IBAN format in receiver.bank.account.id (e.g., TR330006100519786457841326). No ifsc or code is required.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.Tracking Withdrawal Completion
The initialpayment.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 |
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
- ARS Payout
- TRY Payout
Checking Payment Status (payment.status)
Usepayment.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
Query by h_id
You can also look up a payment using the system-assignedh_id. When using h_id, the service_id field is not required:
Response
The response contains the full payment object with its current status:Re-sending Webhook (payment.notification)
Usepayment.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:
| Field | Type | Required | Description |
|---|---|---|---|
method | string | Yes | "payment.notification" |
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 |
Provide either
c_id or h_id. This method triggers a new webhook delivery to your active webhook endpoints with the current payment state. The response body is identical to payment.status. Use this when your webhook handler missed or failed to process a notification.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 |
Terminal statuses are indicated by
final: true. Once a payment reaches a terminal status, it will not transition to any other status.Status Object Structure
Every payment includes astatus object with the following fields:
history array contains every status transition the payment has gone through, in chronological order. This is useful for debugging and auditing.
Handling Status Changes
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:
| 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) |
The
redirect.to field contains UPI deep-link URLs for mobile payment apps (when applicable). Provider HTTPS payment page URLs are not included in the redirect. The redirect field may be absent if no redirect URLs are available for the payment method.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 |
receiver.bank_account object contains the bank details the customer must transfer to:
| 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 |
Customer Data
Deposits: the payer Object
For payment.in requests, the payer object describes the customer making the payment:
Withdrawals: the receiver Object
For payment.out requests, the receiver object describes who receives the payout, including their bank details:
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 anoperations array and an operation shortcut:
| Field | Type | Description |
|---|---|---|
result.payment.operations | array | Array of operations (typically 1 element) |
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.payment.operations[].fees | array | Fee entries (may be empty []) |
result.operation | object | Shortcut — same as operations[0] |
Identifier Formats
Theh_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 errorinteger— error code (e.g.,7001)string— error message from provider (e.g.,"Sum 150000 is lower than 200000")
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).Bank Details in payment.status
Idempotency
Thec_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.
Error Handling
When a request fails, the response will havesuccess: false and include an error object. Error responses return HTTP 400:
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.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
Best Practices
Use Unique c_id Values
Always assign a unique
c_id to each payment. This serves as your idempotency key and prevents duplicate payments.Compute Hashes Server-Side
Never expose your secret key to the client. Always compute the
X-Data-Hash on your server.Check success Field
Always check the
success boolean in the response body to determine the outcome. Success returns HTTP 200, errors return HTTP 400.Store All Identifiers
Save
c_id, h_id, and p_id from the response for reconciliation, debugging, and support inquiries.Handle Redirects
For deposits, always check for a
redirect.to URL and redirect the customer to complete their payment.Use Status History
The
status.history array provides a complete audit trail. Use it for debugging and tracking payment lifecycle.