Receiving payments

Enable UPI & bank transfer in Settings → Payment providers. Add the seller’s bank-issued UPI ID, bank account details, or both. Checkout shows a UPI QR and app link, or copyable bank instructions for IMPS and NEFT. RTGS is also shown for payments of at least ₹200,000. The seller receives INR directly; OurPay does not receive or hold that money. The buyer-facing UPI ID masks the middle of the account identifier, showing its first two and last three characters plus the bank handle, for example 83*****779@ybl. Short identifiers are fully masked. There is no UPI ID copy or reveal control. The QR and app link still contain the full receiving ID to route the payment; display masking does not hide it from the payment app or QR decoding. New domestic payment requests include an OurPay order reference before payment. The same reference appears in the QR’s transaction reference, the buyer’s payment instructions, and Finance → Collections. The UPI payment note contains only the product name at the time the request is created, without an added reference or prefix. OurPay’s 64-character product-name limit fits within Google Pay’s 80-character transaction-note limit. Existing requests retain their original note, reference, and receiving instructions, including after a product is renamed. The QR screen identifies the seller; the UPI app displays the recipient identity registered with their bank. OurPay branding does not replace that bank identity. The OurPay order reference identifies the payment request. It is separate from the bank reference / UTR issued after payment and cannot be submitted as proof of a bank receipt. Add an INR price to the product. Multi-currency checkouts include a Payment currency selector and refresh the available payment methods when the buyer changes currency. Append ?currency=inr to a checkout link to open it in INR directly. The product must have an INR price; unsupported currencies are rejected rather than converted. The buyer can submit a bank reference after paying. This is a request for verification, not proof of payment. In Finance → Collections, the seller checks their statement, selects the actual payment method, and confirms the reference and full amount. This completes the native OurPay order. Repeating the same confirmation is safe; a reference cannot fund another order for the same receiving account. Owners and admins can configure receiving details and confirm receipts. Finance-role members can review the inbox; ordinary members cannot access collection financial details. UPI QR/intent generation needs no commercial gateway integration. Automatic bank confirmation requires an authenticated connection to the seller’s bank. This implementation supplies the receipt interface below; it does not claim access to a public NPCI transaction-status API. Bank and network charges, where applicable, are separate from OurPay’s integration. NPCI’s merchant FAQ describes the acquiring-bank relationship. RBI’s RTGS FAQ specifies its minimum.

Native USDC across networks

One-time checkout can accept native Circle-issued USDC on Polygon, Base, Arbitrum, Ethereum, OP Mainnet, Avalanche, Linea, Monad, HyperEVM, Unichain, World Chain, Sei, and Solana. Enable USDC in the seller’s payment settings. The buyer connects a wallet and checkout checks supported networks for enough native USDC and estimated gas. It selects a funded network, preferring the wallet’s current EVM network when that network can pay. Buyers can review the chosen network and change it before signing. When the connected wallet also supports Solana, checkout can request its Solana account if no checked EVM network can pay. Balance discovery uses the invoice’s native-token allowlist through the backend, with bounded RPC timeouts. An unavailable RPC is shown as an unknown balance, not a zero balance. Gas checks are estimates: EVM uses a buffered execution estimate, and Solana includes a fee reserve and the receiving token-account deposit when needed. Wallet fees, including rollup data fees, can change. The wallet shows the final transaction before approval. A submitted transaction keeps its original network after reload, and discovery never authorizes or submits a payment. This flow neither bridges nor swaps assets. Receipts remain in the operator’s wallet on the network the buyer used. The current network and token allowlist is in server/ourpay/integrations/usdc/networks.py, based on Circle’s native USDC registry. Other tokens, bridged USDC, BNB Chain, Tron, and Bitcoin are not accepted by this adapter. The operator configures public receiving addresses in the API, worker, and scheduler:
This setting defaults to disabled. Supply only the networks and addresses the operator owns. The same EVM address can receive on each configured EVM chain, while Solana needs its own address. No seed phrase, private key, token allowance, or signing service is needed for one-time collection. HTTPS RPC overrides can be provided by network key; credentials in overrides stay on the server. Default public RPC services have rate limits and no promised availability. Production operators can substitute a capacity-appropriate RPC provider. The buyer signs a checkout-specific message proving control of the sending wallet, then confirms the token transfer. The message grants no spending permission. Solana uses Wallet Standard wallets that support message signing and transaction submission. EVM checkout uses an injected wallet and can request switching to the chosen network. Exchange withdrawals, manual transfers, and contract-wallet signatures are not supported in this flow. Every transfer includes the checkout’s unique reference: appended ERC20 transfer data on EVM, or a signed memo on Solana. The browser submits the transaction ID automatically and saves it locally for submission recovery after a reload. The server checks chain identity, native token, exact quoted amount, receiving address, proven payer, reference, transaction success, finality, and the payment window. A submitted hash or wallet success response alone cannot complete an order. Receipt claims are serialized so one transaction cannot complete two collections. Existing reconciliation workers poll pending receipts every minute. The buyer pays network gas separately, using POL, ETH, AVAX, MON, HYPE, SEI, or SOL depending on the chain. The first Solana receipt can also require a SOL deposit for the destination USDC token account. Wallets display those costs before approval. The operator receives the quoted USDC amount and needs gas later to move it. Fiat-priced checkouts use a fresh USDC exchange quote rounded up to six decimals, so a $0.50 invoice need not equal exactly 0.500000 USDC. Quote failure stops instruction creation. Orders complete only after finalized chain evidence. This can take 20 minutes or more on Ethereum and some rollups, and hours on Linea. The transfer must be included before its payment window closes; later finality does not invalidate an in-window transfer. A reverted transfer can be retried while the window remains open. Mismatched receipts require review. Transactions still unresolved one day after expiry also require operator review. Hosted checkout uses two client-secret endpoints:
  • POST /v1/payment-collections/client/{client_secret}/usdc/prepare verifies the sender signature and returns a recent Solana blockhash when needed.
  • POST /v1/payment-collections/client/{client_secret}/usdc/transaction records the hash for reconciliation. Repeated submission of the same hash is idempotent.
Deploy the new API and workers with this setting disabled, deploy the compatible frontend, and run uv run python -m scripts.usdc_preflight with the proposed public-address config. Only then enable new invoices across API, worker, and scheduler. Disabling the flag stops new native-USDC invoices; keep this adapter deployed to reconcile existing ones. Preserve any existing Bitcart configuration and services for outstanding legacy invoices. There is no database migration specific to this adapter. Automatic subscriptions use separately configured EVM deployments.

Legacy Bitcart invoices

Production uses direct native-USDC verification and subscription contracts. OURPAY_BITCART_ENABLED=false; a Bitcart server is not required. The old deployment was retired after checking legacy invoices and archiving its database. The internal bitcart processor identifier and existing receipt records remain compatible with older API clients. This identifier does not mean a new payment uses the Bitcart gateway. Operators with a separate legacy deployment must reconcile outstanding invoices and save their records before retiring its services.

What appears in OurPay

Verified receipts use the existing checkout-success flow, including:
  • Paid orders and payment attempts in Sales and customer order history.
  • Organization-issued invoice numbers, invoices, and downloadable receipts.
  • Discounts, quantities, metadata, customer details, and existing tax calculation when configured.
  • Normal order/payment reporting, events, webhooks, emails, and product benefit grants.
  • A Collections inbox with pending receipts, customer references, asset/network details, verification source, and links to completed orders.
Direct receipts identify the seller as the payment recipient. Crypto orders retain their quoted fiat amount for order reporting and their actual coin amount and transaction IDs as receipt data. Neither rail credits the existing withdrawable fiat balance: INR already reached the seller, and a coin receipt is not a fiat settlement. Crypto payouts and conversion accounting are not implemented here. The dashboard and customer context show revenue grouped by payment currency. Existing USD settlement charts exclude non-USD collection amounts without a USD settlement, while still counting their orders. A ₹199 receipt is never treated as $199. The grouped view does not convert currencies or value cryptocurrency holdings. Direct UPI/bank transfers, native multi-network USDC, and Bitcart invoices support one-time purchases. Ordinary UPI QR payments cannot debit a buyer automatically; recurring bank debits need a separately onboarded UPI AutoPay mandate integration. Automatic refunds and processor dispute handling are not advertised as capabilities. Asking for UPI without a configured eligible INR receiving account returns HTTP 409. New output fields and processor enum values are included in the regenerated TypeScript client.

Automatic USDC subscriptions

An optional Polygon contract supports fixed USD subscriptions paid in native USDC. This feature stays disabled until the operator deploys and verifies the contract and configures a dedicated relayer with POL for gas. Existing one-time collections do not use the subscription contract.
  1. Checkout shows the first payment, maximum USDC per renewal, calendar interval, and finite payment count. The default authorization covers 12 payments, including the first.
  2. The buyer connects their wallet, grants a limited USDC allowance, and authorizes the subscription. Authorization also transfers the first payment to OurPay’s receiving wallet.
  3. OurPay verifies the finalized transfer before creating the subscription and paid order. The normal billing scheduler creates future invoices and the relayer submits each renewal. The buyer does not sign every month, provided the mandate, balance and allowance remain valid.
  4. Cancellation in the customer portal stops future billing and queues permanent on-chain cancellation. The buyer can also cancel directly through the contract. A transaction already being processed may still settle; access continues through the paid period.
The buyer pays POL gas for initial wallet transactions. OurPay pays renewal and portal-requested cancellation gas. One USD of invoice amount is charged as one USDC. Invoice amounts and actual USDC receipts remain separate, and crypto collection does not create a withdrawable fiat balance. Trials, metered or seat-based pricing, plan changes and schedule changes are excluded from this first version. Changing those terms or restarting a canceled/exhausted mandate requires a fresh checkout and wallet authorization. The portal hides unsupported plan, pause and restart actions. Failed buyer payments use the existing retry and past-due handling; a relayer funding or RPC issue leaves collection pending for operator recovery. A browser closing after authorization does not lose a payment: the worker also scans finalized authorization events. Deployment instructions and limitations are in contracts/README.md. The API exposes subscription wallet instructions through the existing payment-collection endpoint and accepts the submitted transaction hash at POST /v1/payment-collections/client/{client_secret}/crypto-subscription. This client-secret endpoint is used by hosted checkout; it does not replace on-chain verification.

Bank receipt adapter

This is OurPay’s internal adapter contract. A bank-specific connector must first authenticate the bank’s notification or status response using that bank’s issued integration specification. Indian Bank onboarding is being evaluated; no Indian Bank connector or API access is configured. Operator preparation is documented in infra/indian-bank/. Set OURPAY_DOMESTIC_BANK_WEBHOOK_SECRET only on the operator API and the trusted bank adapter. After independently verifying a credited transfer with the bank, the adapter posts:
The HMAC signs timestamp + "." + raw_request_body using SHA-256. Timestamps must be within five minutes. amount is integer paise. payee must match the snapshotted UPI ID, or the account number when method is bank_transfer. Mismatched amounts are rejected; repeat receipts are idempotent. Never issue signatures for buyer-supplied references or screenshots. An authenticated bank receipt can complete the order without buyer reference submission or seller confirmation. The bank supplies the transaction identifier. Without a connected bank adapter, payment continues to require the seller’s verification.

Deployment and verification

Apply the additive Alembic migration before starting the new API and worker, regenerate the client, and deploy the frontend with the API. The worker registers payment_collection.reconcile and the minute-based recovery task. Keep wallet credentials in deployment secrets and use TLS between deployments. No Bitcart secrets are returned by the checkout API. Gateway-dependent tests mock the provider transport and exercise real OurPay order creation. They do not demonstrate a live blockchain receipt. Before accepting live funds, configure the operator’s actual wallets and perform a controlled end-to-end receipt check for each enabled network. Domestic automatic confirmation also needs a real bank adapter; seller confirmation is available without it.

Implementation references

The integration follows OurPay’s existing architecture and design system. These references are not bundled forks or dependencies of the domestic bank flow.