Guide

Collect payments

Your customer pays OroPocket directly over UPI, and we buy and credit their gold or silver. No float to pre-fund, no money passing through you.

Every other buy on the Raw API is funded from your own prepaid wallet, so you collect from your customer first on your own rails. Payment collection removes that step: you create a payment order, your customer pays us, and the buy completes itself. It needs the payments capability, which OroPocket switches on per account — it is not self-serve. Once it is on, a Payments section appears in your dashboard.

Checkout typeWhat you get back
intentA raw upi:// link plus ready-made links for Google Pay, PhonePe, Paytm and CRED. Open it from your own app or page.
hostedA checkout_url at pay.oropocket.com showing your name and logo, the amount, and the UPI apps that work on the customer's device (a QR code on desktop). Send the customer there.
v1 covers one-off buys, paid by UPI. Recurring SIP collection comes later. Cards and netbanking are not offered.

The gateway fee

UPI costs 0.118% (0.10% + GST). OroPocket decides, per account, who bears it — you can see which on Payments → Checkout.

fee.bearerYour customer pays ₹500.00
oropocket₹500.00 buys metal. We absorb the ₹0.59.
end_user₹0.59 is deducted and ₹499.41 buys metal. The hosted checkout shows this line before the customer pays; show it in your own UI too if you use intent.
partner₹500.00 buys metal and you bear the ₹0.59. Each month we invoice you for the fees (fee + GST, totalling the fees) and deduct that invoice from the commission we transfer — your own commission invoice is unchanged. If OroPocket allows it, you can pass the fee on to your customer from Payments → Checkout.

The fee is fixed when the order is created and never changes afterwards. funded_amount_inr is what buys metal and what the tax invoice shows. Your markup and commission are calculated on it exactly as on any other buy.

1. Create a payment order

POST/partner/payments/orderspayments
FieldTypeDescription
user_codestringrequiredThe customer, registered earlier with POST /partner/users/init. In sandbox, a usr_test_… code.
amount_inrnumberrequiredWhat the customer pays, up to 2 decimals. Your per-transaction limits apply, and a platform ceiling of ₹50,000.
checkout_typestringrequired"intent" or "hosted".
asset_typestringoptional"gold" (default) or "silver".
referencestringoptionalYour own id for this payment, up to 120 characters. Echoed back everywhere, and unique per mode — a second order with the same reference is refused with 409 DUPLICATE_REFERENCE.
return_urlstringoptionalHosted only: where the checkout sends the customer when it finishes, with ?payment_id=…&status=… added. Must be https on an origin you registered under Payments → Checkout.

Send an Idempotency-Key header so a retried request returns the same order instead of creating a second one. Keys are scoped per mode.

curl -X POST https://api.oropocket.com/partner/payments/orders \
  -H "Authorization: Bearer oro_live_xxx" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-10482" \
  -d '{
    "user_code": "usr_0490fb2ff6f2007a55d39628c3807f85",
    "amount_inr": 500,
    "asset_type": "gold",
    "checkout_type": "hosted",
    "reference": "order-10482",
    "return_url": "https://shop.example.com/orders/10482"
  }'

Response, 201:

{
  "success": true,
  "request_id": "req_…",
  "mode": "live",
  "idempotent_replay": false,
  "data": {
    "payment_id": "ppo_5f1c…",
    "status": "created",
    "checkout_type": "hosted",
    "asset_type": "gold",
    "user_code": "usr_0490fb2ff6f2007a55d39628c3807f85",
    "reference": "order-10482",
    "amount_inr": 500,
    "fee": { "bearer": "end_user", "amount_inr": 0.59, "deducted_from_amount": true },
    "funded_amount_inr": 499.41,
    "estimate": { "asset_quantity": 0.046154, "rate_per_gram_inr": 10506, "final": false },
    "checkout_url": "https://pay.oropocket.com/ppo_5f1c…",
    "expires_at": "2026-09-26T09:40:00.000Z",
    "created_at": "2026-09-26T09:20:00.000Z"
  }
}

estimate is indicative ("final": false): the order is priced when the money arrives, at that moment's rate. The order stays payable until expires_at (20 minutes).

The payment_id is also the key to the hosted checkout. Anyone with the link can open that page (it never shows the customer's details). Send it only to the customer who is paying.

2a. Intent: open the UPI app yourself

For "checkout_type": "intent", the response carries a upi block:

"upi": {
  "url": "upi://pay?pa=…&pn=…&am=500.00&cu=INR&tr=…",
  "apps": {
    "gpay":    { "android": "intent://pay?…#Intent;scheme=upi;package=com.google.android.apps.nbu.paisa.user;end",
                 "android_package": "com.google.android.apps.nbu.paisa.user",
                 "ios": "gpay://upi/pay?…" },
    "phonepe": "phonepe://pay?…",
    "paytm":   "paytmmp://pay?…",
    "cred":    { "android": "intent://pay?…#Intent;scheme=upi;package=com.dreamplug.androidapp;end",
                 "android_package": "com.dreamplug.androidapp",
                 "ios": "credpay://upi/pay?…" }
  }
}
AppHow to open it
Google Pay · AndroidNative app: the raw upi.url with setPackage(android_package). From a web page or WebView: navigate the SAME frame to apps.gpay.android (Chrome's intent:// URL). Do not add a browser fallback — it navigates away from your page.
Google Pay · iOSapps.gpay.ios (gpay://upi/pay?…). Not tez:// — Google Pay for iOS does not handle that scheme.
PhonePe · PaytmOpen apps.phonepe / apps.paytm as they are, on either platform.
CREDAndroid: apps.cred.android. iOS: apps.cred.ios (credpay://upi/pay). One-off payments only — CRED is not offered for SIP mandates.
Any UPI appupi.url on Android shows the system chooser. On desktop, render upi.url as a QR code.
// Android (Kotlin): open Google Pay directly, no chooser.
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(upi.url))   // the raw upi:// link
intent.setPackage("com.google.android.apps.nbu.paisa.user")     // upi.apps.gpay.android_package
startActivity(intent)
// Omit setPackage to show the system's UPI app chooser.
Never re-encode any of these links. A UPI link can be signed over the whole string, and a re-encoded link is rejected by the app as tampered. Pass them on exactly as you received them.

2b. Hosted: send the customer to the checkout

Redirect or link to checkout_url. The page shows your company name (always), your logo and accent if you set them, the amount and the fee line, and only the UPI apps that work on that device. When the order finishes it shows the outcome and, if you passed a return_url, sends the customer back to it. Never rely on that redirect alone to confirm payment — use the webhook or the status endpoint.

3. Know when it is paid

Payment orders fire webhooks to the endpoint on your Webhooks page, signed the same way as every other event.

EventWhen
payment.completedThe customer paid and the metal is credited. fulfilment carries the final grams and rate. This is the one to fulfil your order on.
payment.failedThe order closed without a purchase. If refund.status is "pending", the customer's money arrived but the purchase could not be completed — OroPocket retries or refunds it.
payment.expiredNo payment arrived before expires_at.
payment.refundedA refund_due order was refunded to the customer. refund.reference is the UTR or gateway refund id.
{
  "event": "payment.completed",
  "event_id": "evt_…",
  "created_at": "2026-09-26T09:22:41.000Z",
  "data": {
    "user_code": "usr_0490fb2ff6f2007a55d39628c3807f85",
    "payment_id": "ppo_5f1c…",
    "status": "completed",
    "reference": "order-10482",
    "amount_inr": 500,
    "fee": { "bearer": "end_user", "amount_inr": 0.59, "deducted_from_amount": true },
    "funded_amount_inr": 499.41,
    "fulfilment": { "asset_quantity": 0.046131, "rate_per_gram_inr": 10510.2, "final": true },
    "completed_at": "2026-09-26T09:22:40.000Z"
  }
}

A completed payment order is also a buy, so the ordinary buy.completed event fires for it as well. Match on payment.completed for payment orders so you don't count them twice.

GET/partner/payments/orders/:payment_idpayments

The same object as the create response, plus fulfilment once completed. Polling it also checks the gateway, so it is the reliable fallback if a webhook is missed.

GET/partner/payments/orderspayments

Newest first. Query: status (comma-separated), reference, page, limit (max 100).

Statuses

statusMeaning
createdWaiting for the customer to pay.
paidMoney arrived; the purchase is being completed (seconds).
completedPaid and credited. Final.
failedClosed without a purchase — the gateway closed it, or it could not be created.
expiredNobody paid in time. If money arrives late anyway, the order still completes.
refund_duePaid, but the purchase could not be completed. Being retried or refunded — it can still move to completed.
refundedThe customer's money went back to them. Final.

SIPs: recurring buys with UPI AutoPay

A SIP buys the same amount of gold or silver every day, week or month. Your customer approves a UPI AutoPay mandate once. The first installment is taken with that approval, and every later one is debited automatically.

POST/partner/payments/sipspayments
FieldTypeDescription
user_codestringrequiredThe customer.
installment_amount_inrnumberrequiredWhat each debit takes. Minimum ₹100.
frequencystringrequired"daily", "weekly" or "monthly".
asset_typestringoptional"gold" (default) or "silver".
namestringoptionalWhat the customer calls it, up to 60 characters. Default "Gold SIP".
referencestringoptionalYour id, unique per mode.
return_urlstringoptionalWhere the hosted page sends the customer once AutoPay is set up. Registered origins only.
curl -X POST https://api.oropocket.com/partner/payments/sips \
  -H "Authorization: Bearer oro_live_xxx" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: sip-10482" \
  -d '{
    "user_code": "usr_0490fb2ff6f2007a55d39628c3807f85",
    "installment_amount_inr": 500,
    "frequency": "monthly",
    "asset_type": "gold",
    "name": "Wedding gold",
    "reference": "sip-10482"
  }'
{
  "id": "ppo_8e21…",
  "sip_id": 1840,
  "status": "pending_authorisation",
  "installment_amount_inr": 500,
  "frequency": "monthly",
  "fee": { "bearer": "end_user", "per_installment_inr": 5.9, "deducted_from_installment": true },
  "funded_installment_inr": 494.1,
  "checkout_url": "https://pay.oropocket.com/ppo_8e21…",
  "mandate": { "upi": { "url": "upi://mandate?…", "apps": { "gpay": { "android": "intent://mandate?…", "ios": "gpay://upi/mandate?…" }, "phonepe": "phonepe://mandate?…", "paytm": "paytmmp://mandate?…" } } },
  "authorise_by": "2026-09-26T10:20:00.000Z"
}

Send the customer to checkout_url, or open mandate.upi yourself with the same per-app rules as a payment. The verb is mandate, so use Google Pay's gpay://upi/mandate on iOS, and never turn a mandate link into a pay link. The link must be approved before authorise_by (one hour).

When your customer bears the gateway fee, it is deducted from every installment, and it is a flat fee per debit rather than a percentage: ₹1.18 up to ₹250, ₹5.90 up to ₹1,000, ₹17.70 above (or ₹3 per debit, depending on the provider). On a ₹101 SIP that is a noticeable share. The create response states the exact figure, so show it to your customer.

sip_id is the id every sip.* webhook carries: sip.created, then sip.activated once the bank approves, one sip.installment per debit (with fee_inr and funded_amount_inr), or sip.mandate_failed if it is never approved.

GET/partner/payments/sips/:idpayments

Accepts either id or sip_id. It checks the provider live and adds progress: installments paid, total invested, grams and the next date. Statuses are pending_authorisation, active, paused, cancelled, completed and failed (not approved in time).

GET/partner/payments/sipspayments
POST/partner/payments/sips/:id/cancelpayments

Stops the mandate so no further amounts are debited. Gold already bought stays with the customer.

Sandbox

With an oro_test_ key, orders never touch a gateway: intent returns a sandbox UPI link that pays nobody, and the hosted page shows "Simulate" buttons instead of apps. Finish an order from the API with:

POST/partner/payments/orders/:payment_id/simulatepayments
POST/partner/payments/sips/:id/simulatepayments

For a SIP, the outcome is "activate", "installment" or "failure".

Body { "outcome": "success" } or { "outcome": "failure" }. The matching webhook fires with "mode": "sandbox" in its data.

Errors

CodeMeaning
403 PAYMENT_INFRA_NOT_ENABLEDPayment collection is not on for your account.
503 PAYMENT_INFRA_DISABLEDCollection is paused platform-wide. Retry later.
503 PAYMENT_GATEWAY_UNAVAILABLENo gateway is available right now. Retry later.
503 UPI_INTENT_UNAVAILABLEIntent checkout is temporarily unavailable. Create the order with checkout_type "hosted" instead — it always works. Nothing was charged.
400 INVALID_CHECKOUT_TYPE · INVALID_AMOUNT · INVALID_ASSET_TYPEFix the field named in the message.
400 AMOUNT_BELOW_MIN · AMOUNT_ABOVE_MAXOutside your per-transaction limits or the ₹50,000 ceiling.
400 RETURN_URL_NOT_ALLOWEDRegister the origin under Payments → Checkout first.
404 USER_CODE_NOT_FOUNDRegister the customer with POST /partner/users/init.
409 SIP_LIMIT_REACHEDThe customer already has the most SIPs allowed. They can stop one first.
409 DUPLICATE_REFERENCEAn order with this reference exists; details.payment_id names it.
502 GATEWAY_ERRORThe gateway could not create the order. Nothing was charged; retry with a new Idempotency-Key.