Skip to main content

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:
MethodDirectionDescription
payment.inInboundCustomer pays to merchant (deposit)
payment.outOutboundMerchant pays to customer/vendor (withdrawal)
payment.statusQueryCheck 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 two headers:
HeaderDescriptionExample
X-Data-Application-IdYour application ID (integer)42
X-Data-HashSHA-512 hash of the request body concatenated with your secret keya1b2c3d4...
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.
Keep your secret key secure. Never expose it in client-side code, public repositories, or browser requests. The hash must be computed server-side.

Request Format

All requests follow the same envelope:
{
  "method": "payment.in | payment.out | payment.status",
  "service_id": 14701,
  "params": {
    "payment": { ... }
  }
}
FieldTypeRequiredDescription
methodstringYesThe operation to perform
service_idintegerYes*Routing identifier assigned per merchant. Determines the provider, payment method, currency, and direction. Required for payment.in and payment.out
paramsobjectYesContains the payment object with operation-specific fields

Response Format

All responses follow a consistent envelope:
{
  "success": true,
  "result": {
    "payment": { ... }
  },
  "request_id": "req_abc123",
  "processing_time": 42
}
FieldTypeDescription
successbooleanWhether the operation succeeded
resultobject or nullContains the payment object on success
errorobject or nullContains code, message, details, and context on failure
request_idstringUnique identifier for this request (useful for debugging)
processing_timenumberServer processing time in milliseconds

Identifiers

Every payment uses three identifiers that appear in the identifiers object:
IdentifierTypeDescription
c_idstringClient ID — your merchant reference. You provide this when creating a payment. Must be unique per payment.
h_idstringHub ID — system-assigned payment ID. Assigned automatically when the payment is created.
p_idstringProvider 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)

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

Build the request body

Include the payment amount, currency, customer (payer) details, and your unique c_id.
2

Compute the authentication hash

Compute sha512(requestBody + secretKey) and set it in the X-Data-Hash header.
3

Send the request

POST to /public/api/multihub/v1 and handle the response.
4

Redirect the customer

If the response includes a redirect.to URL, redirect the customer to complete payment.

Request

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: $(echo -n '{"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"}}}}YOUR_SECRET_KEY' | sha512sum | awk '{print $1}')" \
  -d '{
    "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" }
      }
    }
  }'

Deposit Request Fields

FieldTypeRequiredDescription
descriptionstringNoHuman-readable description for the payment
identifiers.c_idstringYesYour unique client reference for this payment
amount.valueintegerYesPayment amount in minor units (e.g., paise for INR)
amount.currencystringYesISO 4217 currency code
payer.emailstringRecommendedCustomer email address (may be required by some providers)
payer.phonestringNoCustomer phone number
payer.person.first_namestringNoCustomer first name
payer.person.last_namestringNoCustomer last name
payer.customer_account.idstringNoCustomer account identifier in your system
client.languagestringNoISO 639-1 language code for the payment page
client.countrystringNoISO 3166-1 alpha-2 country code

Response

{
  "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
}
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

{
  "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
}

Creating a Withdrawal (payment.out)

Use payment.out to send money to a recipient (payout).
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.
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

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" \
  -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" }
        }
      }
    }
  }'

Region-Specific Withdrawal Examples

The receiver.bank object varies by region. Below are the key differences:
{
  "receiver": {
    "bank": {
      "account": { "id": "1234567890" },
      "ifsc": "SBIN0001234"
    },
    "person": { "first_name": "Jane", "last_name": "Doe" }
  }
}

Withdrawal Request Fields

FieldTypeRequiredDescription
descriptionstringNoHuman-readable description for the payout
identifiers.c_idstringYesYour unique client reference and idempotency key for this payout
amount.valueintegerYesPayout amount in minor units
amount.currencystringYesISO 4217 currency code
receiver.bank.account.idstringYesRecipient bank account number, IBAN, CBU/CVU, or CLABE depending on the route
receiver.bank.ifscstringIndia onlyIFSC routing code (e.g., SBIN0001234)
receiver.bank.codestringArgentina (optional)Bank code (e.g., BBVA)
receiver.emailstringNoRecipient email address
receiver.phonestringNoRecipient phone number
receiver.person.first_namestringNoRecipient first name
receiver.person.last_namestringNoRecipient 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 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.
IdentifierMeaningWhere to use it
c_idYour payout reference from the requestPrimary lookup and reconciliation key
h_id123hub payment/operation identifierAlternative lookup key returned by the API
p_idProvider reference, when availableReconciliation 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

{
  "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
}

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

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" \
  -d '{
    "method": "payment.status",
    "params": {
      "payment": {
        "identifiers": { "c_id": "12345" }
      }
    }
  }'

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:
{
  "method": "payment.status",
  "params": {
    "payment": {
      "identifiers": { "h_id": "1001" }
    }
  }
}

Response

The response contains the full payment object with its current status:
{
  "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:
{
  "method": "payment.notification",
  "params": {
    "payment": {
      "identifiers": { "c_id": "your-payment-id" }
    }
  }
}
FieldTypeRequiredDescription
methodstringYes"payment.notification"
service_idintegerNoOptional fallback if the original payment metadata does not contain service_id
params.payment.identifiers.c_idstringYes*Your client reference for the payment
params.payment.identifiers.h_idstringYes*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.
StatusDescriptionfinalsuccess
createdPayment created, waiting for processingfalsenull
processingPayment is being processedfalsenull
successPayment completed successfullytruetrue
errorPayment failedtruefalse
canceledPayment was cancelledtruefalse
declinedPayment expired or was declinedtruefalse
refundedFull refund processedtruetrue
partially_refundedPartial refund processedtruetrue
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 a status object with the following fields:
{
  "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

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

if (payment.status.final) {
  if (payment.status.success) {
    // Payment succeeded (success, refunded, partially_refunded)
    await markOrderAsPaid(payment.identifiers.c_id);
  } 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:
{
  "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"
    ]
  }
}
FieldDescription
toA 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_failThe URL the customer is redirected to if the payment fails (when available)
on_successThe 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:
Regionredirect.toreceiver.bank_accountAction
India (UPI)Array of UPI deep-link URLsEmpty (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
For TRY and ARS, the receiver.bank_account object contains the bank details the customer must transfer to:
{
  "receiver": {
    "bank_account": {
      "id": "TR850021000000145137400001",
      "name": "Account Holder Name"
    }
  }
}
FieldTypeDescription
receiver.bank_account.idstringBank account number, IBAN, or CBU for the transfer
receiver.bank_account.namestringAccount holder name

Customer Data

Deposits: the payer Object

For payment.in requests, the payer object describes the customer making the payment:
{
  "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:
{
  "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:
Regionbank.account.idAdditional FieldNotes
India (INR)Account numberifsc (required)IFSC routing code
Mexico (MXN)CLABE (18 digits)NonePut 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:
FieldTypeDescription
result.payment.operationsarrayArray of operations (typically 1 element)
result.payment.operations[].idstringOperation ID (same as h_id for the primary operation)
result.payment.operations[].operation_typestring"payment.in" or "payment.out"
result.payment.operations[].amountobject{value, currency} — operation amount
result.payment.operations[].timestampsobject{created, updated?, finished?}
result.payment.operations[].statusobjectStatus object (status, final, success, error)
result.payment.operations[].feesarrayFee entries (may be empty [])
result.operationobjectShortcut — same as operations[0]
{
  "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 },
    "fees": []
  }],
  "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")
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

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.

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.
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.
// 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:
{
  "success": false,
  "error": {
    "code": 6010,
    "message": "Payment does not exist",
    "details": null,
    "context": null
  },
  "request_id": "req_err789",
  "processing_time": 5
}
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

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

Error Handling Example

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.")

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.