# "signature" is computed as described in the Signature Generation Guide.
# This value is the guide's worked example (example signing secret).
curl --request POST \
--url https://api.c2cpayment.net/api/merchant/orders \
--header "C2C-API-Key: $C2C_API_KEY" \
--header "Content-Type: application/json" \
--header "Idempotency-Key: INV-10045" \
--data '{"orderType":"Deposit","amount":1500,"currency":"PKR","walletType":"JazzCash","merchantOrderRef":"INV-10045","customerReference":"player_12345","callbackUrl":"https://merchant.example.com/webhooks/c2c","signature":"56eff91b5ab0351471a2a8d457320230"}'
// signBody(): see the Signature Generation Guide
const payload = {
orderType: 'Deposit',
amount: 1500,
currency: 'PKR',
walletType: 'JazzCash',
merchantOrderRef: 'INV-10045',
customerReference: 'player_12345',
callbackUrl: 'https://merchant.example.com/webhooks/c2c',
};
const response = await fetch(
'https://api.c2cpayment.net/api/merchant/orders',
{
method: 'POST',
headers: {
'C2C-API-Key': process.env.C2C_API_KEY,
'Content-Type': 'application/json',
'Idempotency-Key': 'INV-10045',
},
body: JSON.stringify({ ...payload, signature: signBody(payload, process.env.C2C_SIGNING_SECRET) }),
}
);
const { data } = await response.json();
// Persist data.orderId, then redirect the customer to data.paymentPageUrl
import os, requests
# sign_body(): see the Signature Generation Guide
payload = {
"orderType": "Deposit",
"amount": 1500,
"currency": "PKR",
"walletType": "JazzCash",
"merchantOrderRef": "INV-10045",
"customerReference": "player_12345",
"callbackUrl": "https://merchant.example.com/webhooks/c2c",
}
response = requests.post(
"https://api.c2cpayment.net/api/merchant/orders",
json={**payload, "signature": sign_body(payload, os.environ["C2C_SIGNING_SECRET"])},
headers={
"C2C-API-Key": os.environ["C2C_API_KEY"],
"Idempotency-Key": "INV-10045",
},
)
data = response.json()["data"]
# Persist data["orderId"], then redirect the customer to data["paymentPageUrl"]
{
"data": {
"orderId": "788fcae0-5214-403c-baa2-f318c0eb1b5b",
"paymentPageUrl": "https://<payment-page-host>/pay?order_id=788fcae0-5214-403c-baa2-f318c0eb1b5b&expires=1791462080&signature=Xk3...&wallet=jazzcash",
"status": "Queued",
"orderReference": "ORD-20261008120620-3f6b",
"expiresAt": "2026-10-08T12:21:20.605646Z",
"stage": "Assigned",
"merchantOrderRef": "INV-10045"
}
}
{
"data": {
"orderId": "788fcae0-5214-403c-baa2-f318c0eb1b5b",
"paymentPageUrl": "https://<payment-page-host>/pay?order_id=788fcae0-5214-403c-baa2-f318c0eb1b5b&expires=1791462080&signature=Xk3...&wallet=jazzcash",
"status": "Awaiting",
"orderReference": "ORD-20261008120620-3f6b",
"expiresAt": "2026-10-08T12:21:20.605646Z",
"stage": "Awaiting",
"merchantOrderRef": "INV-10045"
}
}
{
"error": "'Amount' must be greater than '0'."
}
{
"success": false,
"error": "Request signature verification failed. Sign the request as described in the signature guide.",
"code": "SIGNATURE_INVALID",
"traceId": "5b269362496001a7ed102e2d8cc50329"
}
{
"error": "IP not in whitelist."
}
{
"error": "merchantOrderRef 'INV-10045' is already used by order ORD-20261008120620-3f6b. Reuse the same Idempotency-Key to retry a request, or use a new reference for a new payment."
}
{
"error": "This Idempotency-Key was already used for a different order request."
}
Deposit
Create Deposit Order
Create a deposit order and get a hosted payment page URL where your customer completes the payment.
POST
/
api
/
merchant
/
orders
# "signature" is computed as described in the Signature Generation Guide.
# This value is the guide's worked example (example signing secret).
curl --request POST \
--url https://api.c2cpayment.net/api/merchant/orders \
--header "C2C-API-Key: $C2C_API_KEY" \
--header "Content-Type: application/json" \
--header "Idempotency-Key: INV-10045" \
--data '{"orderType":"Deposit","amount":1500,"currency":"PKR","walletType":"JazzCash","merchantOrderRef":"INV-10045","customerReference":"player_12345","callbackUrl":"https://merchant.example.com/webhooks/c2c","signature":"56eff91b5ab0351471a2a8d457320230"}'
// signBody(): see the Signature Generation Guide
const payload = {
orderType: 'Deposit',
amount: 1500,
currency: 'PKR',
walletType: 'JazzCash',
merchantOrderRef: 'INV-10045',
customerReference: 'player_12345',
callbackUrl: 'https://merchant.example.com/webhooks/c2c',
};
const response = await fetch(
'https://api.c2cpayment.net/api/merchant/orders',
{
method: 'POST',
headers: {
'C2C-API-Key': process.env.C2C_API_KEY,
'Content-Type': 'application/json',
'Idempotency-Key': 'INV-10045',
},
body: JSON.stringify({ ...payload, signature: signBody(payload, process.env.C2C_SIGNING_SECRET) }),
}
);
const { data } = await response.json();
// Persist data.orderId, then redirect the customer to data.paymentPageUrl
import os, requests
# sign_body(): see the Signature Generation Guide
payload = {
"orderType": "Deposit",
"amount": 1500,
"currency": "PKR",
"walletType": "JazzCash",
"merchantOrderRef": "INV-10045",
"customerReference": "player_12345",
"callbackUrl": "https://merchant.example.com/webhooks/c2c",
}
response = requests.post(
"https://api.c2cpayment.net/api/merchant/orders",
json={**payload, "signature": sign_body(payload, os.environ["C2C_SIGNING_SECRET"])},
headers={
"C2C-API-Key": os.environ["C2C_API_KEY"],
"Idempotency-Key": "INV-10045",
},
)
data = response.json()["data"]
# Persist data["orderId"], then redirect the customer to data["paymentPageUrl"]
{
"data": {
"orderId": "788fcae0-5214-403c-baa2-f318c0eb1b5b",
"paymentPageUrl": "https://<payment-page-host>/pay?order_id=788fcae0-5214-403c-baa2-f318c0eb1b5b&expires=1791462080&signature=Xk3...&wallet=jazzcash",
"status": "Queued",
"orderReference": "ORD-20261008120620-3f6b",
"expiresAt": "2026-10-08T12:21:20.605646Z",
"stage": "Assigned",
"merchantOrderRef": "INV-10045"
}
}
{
"data": {
"orderId": "788fcae0-5214-403c-baa2-f318c0eb1b5b",
"paymentPageUrl": "https://<payment-page-host>/pay?order_id=788fcae0-5214-403c-baa2-f318c0eb1b5b&expires=1791462080&signature=Xk3...&wallet=jazzcash",
"status": "Awaiting",
"orderReference": "ORD-20261008120620-3f6b",
"expiresAt": "2026-10-08T12:21:20.605646Z",
"stage": "Awaiting",
"merchantOrderRef": "INV-10045"
}
}
{
"error": "'Amount' must be greater than '0'."
}
{
"success": false,
"error": "Request signature verification failed. Sign the request as described in the signature guide.",
"code": "SIGNATURE_INVALID",
"traceId": "5b269362496001a7ed102e2d8cc50329"
}
{
"error": "IP not in whitelist."
}
{
"error": "merchantOrderRef 'INV-10045' is already used by order ORD-20261008120620-3f6b. Reuse the same Idempotency-Key to retry a request, or use a new reference for a new payment."
}
{
"error": "This Idempotency-Key was already used for a different order request."
}
Overview
Creates a deposit: your customer pays money into an account held by a C2C member, and the amount, minus fees, is credited to your merchant wallet once the member confirms receipt. C2C assigns an available member when the order is created and returns apaymentPageUrl. Redirect your customer to that hosted page. It opens on the payment method you requested in walletType: the customer sees the account number, Till ID or QR code to pay, sends the money from their wallet app, and confirms the payment on the page. See the Deposit flow for the full lifecycle.
The order is always accepted. If no eligible member is available right now, it’s created with status Awaiting, and a member is assigned when the customer opens the payment page.
| API key | Required |
| Signature | Required, in the body’s signature field. See the Signature Generation Guide |
| Idempotency-Key | Required |
Deposits and withdrawals use the same endpoint. Set
orderType to "Deposit" to create a deposit.Headers
string
required
mk_live_..., your merchant API key. Authorization: Bearer mk_live_... is also accepted.string
required
Your unique key for this order attempt, up to 100 characters. Retrying with the same key returns the original order instead of creating a new one. See Idempotency.
string
required
application/jsonRequest body
string
required
Must be
"Deposit".number
required
Amount the customer pays, in
currency. Must be greater than 0, have at most 4 decimal places and be less than 100,000,000,000,000.Example: 1500string
required
The payment method the customer pays with. C2C assigns a member with a receiving account of exactly this method, and the payment page opens directly on it.One of
No method falls back to another.
JazzCash, JazzCashTillID, JazzCashQR, Easypaisa, TillID, EasypaisaQR, BankAccount.| Value | The customer pays to |
|---|---|
JazzCash | A JazzCash account number |
JazzCashTillID | A JazzCash Till ID |
JazzCashQR | A JazzCash QR code |
Easypaisa | An Easypaisa account number |
TillID | An Easypaisa Till ID |
EasypaisaQR | An Easypaisa QR code |
BankAccount | Accepted, but bank accounts aren’t offered on the payment page for now: the page opens on the list of available methods and the customer picks one of the six above. |
JazzCash and Easypaisa mean the account number only: request JazzCashTillID or TillID for a Till ID. If no account of the requested method is available, the order is accepted as Awaiting. On the payment page the customer can still switch to another method that is enabled for you.string
default:"PKR"
ISO currency code:
PKR, INR or BDT. Defaults to PKR.string
Your own reference for this order, such as an invoice or cart ID. Up to 200 characters. It is echoed in webhooks, and you can query the order by it.
One live order per reference: if an order that is in progress or completed already uses this
merchantOrderRef, the request is rejected with 409. A reference whose earlier order failed can be reused. To retry a request, reuse the same Idempotency-Key instead. See merchantOrderRef rules.string
Your identifier for the paying customer, for example a player ID. Up to 200 characters.
string
Absolute
http(s) URL that receives the order.completed / order.failed webhooks for this order. Up to 500 characters. It must resolve to a public internet address: loopback, private and link-local addresses are refused at delivery time, and redirects aren’t followed.If omitted, C2C uses the default callback URL set in your Merchant Portal → Settings. If neither is set, no webhook is sent and you must poll order status.string
Absolute
http(s) URL, up to 500 characters. When the order completes, the hosted payment page sends the customer here, with c2c_order_id, c2c_order_reference and c2c_status appended. See Returning the customer.string
Absolute
http(s) URL, up to 500 characters. Where the hosted payment page sends the customer when the order is rejected, expires or is cancelled. The same parameters are appended.string
Free-form string, for example JSON-encoded, stored with the order for reconciliation. Up to 1000 characters. It isn’t returned in API responses or webhooks.
integer
default:"4"
4 to 8. Applies to the account number and Till ID methods only (JazzCash, JazzCashTillID, Easypaisa, TillID): how many trailing digits of the wallet transaction ID the customer enters on the payment page.It has no effect on a payment made with JazzCashQR or EasypaisaQR. Those methods don’t ask for a transaction ID: the customer gives their JazzCash account number (JazzCashQR) or the last 4 digits of their Easypaisa IBAN (EasypaisaQR) instead. See What the customer submits.integer
default:"3"
0 to 10. Only used by withdrawals: it limits how often C2C reassigns an order to another member. It has no effect on deposits.string
required
32-character lowercase hex MD5 of the other top-level fields (lowercased keys in ASCII order,
key=value joined with &, null and empty values skipped) followed directly by your signing secret. See the Signature Generation Guide.Example: f2eb65603d36507621fdfd64aba78974Response
object
Hide properties
Hide properties
uuid
C2C’s unique order ID. Store it. You need it for status queries, and it is the
orderId in webhooks.string
Hosted payment page for this order. Redirect the customer here. The URL is pre-signed and stops working at
expiresAt. Don’t modify it, and don’t expose it to anyone other than the paying customer.string
Initial status:
Queued when a member was assigned, or Awaiting when no eligible member was available yet. See Order status.string
Lifecycle stage:
Assigned or Awaiting for a new deposit. See Lifecycle stages.string | null
Your
merchantOrderRef, or null if you didn’t send one.string
C2C’s human-readable reference, for example
ORD-20261008120620-3f6b. Quote it to support.string
ISO 8601 UTC time when the payment page link expires, 15 minutes after creation.
# "signature" is computed as described in the Signature Generation Guide.
# This value is the guide's worked example (example signing secret).
curl --request POST \
--url https://api.c2cpayment.net/api/merchant/orders \
--header "C2C-API-Key: $C2C_API_KEY" \
--header "Content-Type: application/json" \
--header "Idempotency-Key: INV-10045" \
--data '{"orderType":"Deposit","amount":1500,"currency":"PKR","walletType":"JazzCash","merchantOrderRef":"INV-10045","customerReference":"player_12345","callbackUrl":"https://merchant.example.com/webhooks/c2c","signature":"56eff91b5ab0351471a2a8d457320230"}'
// signBody(): see the Signature Generation Guide
const payload = {
orderType: 'Deposit',
amount: 1500,
currency: 'PKR',
walletType: 'JazzCash',
merchantOrderRef: 'INV-10045',
customerReference: 'player_12345',
callbackUrl: 'https://merchant.example.com/webhooks/c2c',
};
const response = await fetch(
'https://api.c2cpayment.net/api/merchant/orders',
{
method: 'POST',
headers: {
'C2C-API-Key': process.env.C2C_API_KEY,
'Content-Type': 'application/json',
'Idempotency-Key': 'INV-10045',
},
body: JSON.stringify({ ...payload, signature: signBody(payload, process.env.C2C_SIGNING_SECRET) }),
}
);
const { data } = await response.json();
// Persist data.orderId, then redirect the customer to data.paymentPageUrl
import os, requests
# sign_body(): see the Signature Generation Guide
payload = {
"orderType": "Deposit",
"amount": 1500,
"currency": "PKR",
"walletType": "JazzCash",
"merchantOrderRef": "INV-10045",
"customerReference": "player_12345",
"callbackUrl": "https://merchant.example.com/webhooks/c2c",
}
response = requests.post(
"https://api.c2cpayment.net/api/merchant/orders",
json={**payload, "signature": sign_body(payload, os.environ["C2C_SIGNING_SECRET"])},
headers={
"C2C-API-Key": os.environ["C2C_API_KEY"],
"Idempotency-Key": "INV-10045",
},
)
data = response.json()["data"]
# Persist data["orderId"], then redirect the customer to data["paymentPageUrl"]
{
"data": {
"orderId": "788fcae0-5214-403c-baa2-f318c0eb1b5b",
"paymentPageUrl": "https://<payment-page-host>/pay?order_id=788fcae0-5214-403c-baa2-f318c0eb1b5b&expires=1791462080&signature=Xk3...&wallet=jazzcash",
"status": "Queued",
"orderReference": "ORD-20261008120620-3f6b",
"expiresAt": "2026-10-08T12:21:20.605646Z",
"stage": "Assigned",
"merchantOrderRef": "INV-10045"
}
}
{
"data": {
"orderId": "788fcae0-5214-403c-baa2-f318c0eb1b5b",
"paymentPageUrl": "https://<payment-page-host>/pay?order_id=788fcae0-5214-403c-baa2-f318c0eb1b5b&expires=1791462080&signature=Xk3...&wallet=jazzcash",
"status": "Awaiting",
"orderReference": "ORD-20261008120620-3f6b",
"expiresAt": "2026-10-08T12:21:20.605646Z",
"stage": "Awaiting",
"merchantOrderRef": "INV-10045"
}
}
{
"error": "'Amount' must be greater than '0'."
}
{
"success": false,
"error": "Request signature verification failed. Sign the request as described in the signature guide.",
"code": "SIGNATURE_INVALID",
"traceId": "5b269362496001a7ed102e2d8cc50329"
}
{
"error": "IP not in whitelist."
}
{
"error": "merchantOrderRef 'INV-10045' is already used by order ORD-20261008120620-3f6b. Reuse the same Idempotency-Key to retry a request, or use a new reference for a new payment."
}
{
"error": "This Idempotency-Key was already used for a different order request."
}
Error responses
| Status | When | Body |
|---|---|---|
400 | A field fails validation, for example amount ≤ 0, an amount with more than 4 decimal places or too large, an unknown currency, an invalid URL or a value that is too long | {"error": "..."}. Multiple messages are joined with ; |
400 | Idempotency-Key missing, malformed JSON, or an unknown orderType/walletType value | application/problem+json with an errors object |
401 | API key missing, invalid or revoked | Empty body |
401 | signature missing, malformed, invalid, or replayed under a different Idempotency-Key | {"success": false, "error", "code", "traceId"} |
403 | Merchant account not active, or caller IP not whitelisted | {"error": "..."} |
413 | Body larger than 64 KB | {"success": false, "error", "code": "PAYLOAD_TOO_LARGE", "traceId"} |
409 | merchantOrderRef is already used by an order that is in progress or completed | {"error": "merchantOrderRef '...' is already used by order ORD-…. ..."} |
422 | The payment method (walletType) is disabled for deposits on your account, for example JazzCash deposits are disabled for this merchant. or JazzCashQR deposits are disabled for this merchant. | {"error": "..."} |
422 | The Idempotency-Key was already used with different order terms | {"error": "This Idempotency-Key was already used for a different order request."} |
If a request was rejected (
400, 409, 422 …), you can fix it and reuse the same Idempotency-Key, because rejected requests don’t consume the key.What happens next
- Redirect the customer to
paymentPageUrlimmediately. The link expires after 15 minutes. - If the order is
Awaiting, a member is assigned when the customer opens the payment page. Until one is online the page shows “Finding an available agent…” and re-checks every 10 seconds. - The customer pays and confirms on the page: with the last digits of the transaction ID, or on the QR methods by giving the account they pay from. The order moves to
AwaitingCustomerPaymentand thenAwaitingMemberConfirmation. - The member confirms (
Completed) or rejects (Rejected) the payment. - You receive a webhook, and you can query the status at any time. If you set
successUrl/failureUrl, the payment page sends the customer back to you.
Fulfil the purchase only when the order status is
Completed, never on the creation response.Next steps
Deposit flow
Full lifecycle, statuses and timeouts
Query order status
Track the order to completion
Payment methods
The six methods and how they are matched
Hosted payment page
What your customer sees
Webhooks
Get notified when the order completes