Overview
This guide documents the exact implementation used in our platform for one-time credits purchase using Razorpay. It covers the user journey, server APIs, client checkout integration, verification, schema, and operational best practices.
- User selects a package or custom credits.
- We open a usage sheet explaining what those credits unlock.
- On "Proceed to Pay", client calls `/api/payments/create-order`.
- Server calculates price, creates Razorpay order, stores a PENDING `Payment`, returns `orderId`.
- Client opens Razorpay Checkout with that `orderId`.
- On success, client posts identifiers + signature to `/api/payments/verify`.
- Server verifies signature, checks capture from Razorpay, marks COMPLETED, credits user, records `CreditTransaction`.
Purchase UI (Client)
The purchase page opens a usage sheet, then initializes payment with a processing dialog indicating states (initializing → processing → verifying → redirecting). On success, we redirect to a success page with receipt details.
Create Order API
Server receives credits and currency, validates range, calculates price strictly on the server, creates a Razorpay order, stores a PENDING `Payment`, and returns `orderId`. Do not trust client amounts.
Open Razorpay Checkout
On success, the handler posts `razorpay_payment_id`, `razorpay_order_id`, and `razorpay_signature` to the verify API. For failures, Razorpay suggests retry flows in the widget.
Verify Payment API
Server verifies signature, fetches Razorpay payment, ensures captured status, safely marks `Payment` as COMPLETED, credits the user, and records a `CreditTransaction`—idempotently.
Example Schema
A simplified example showing `Payment` and `CreditTransaction` with useful indices and statuses.
Advanced: Webhooks & Reconciliation
Implement Razorpay webhooks for `payment.captured`/`order.paid`. On capture, verify signature, reconcile by `orderId` and idempotently mark COMPLETED. Add a periodic job to re-check old PENDING payments.
Also consider a callback URL fallback and a resume-on-return banner for robustness against client interruptions.
Edge Cases & Playbook
Below are detailed scenarios and how to handle them in production. Code samples for these will be added soon.
- Client disconnected after pay: Use webhooks to finalize capture; on next visit, show a banner to resume/confirm status. Callback URL can also help.
- Duplicate clicks/requests: Use idempotency keys (receipt, orderId) and check existing PENDING/COMPLETED records before creating new ones.
- Amount tampering: Never trust client amount; compute server-side from SKU/config and re-validate against Razorpay amount in verify step.
- Order expiry: Detect on verify; recreate order and prompt retry with a fresh orderId.
- Webhook security: Verify webhook signature; whitelist IPs if applicable; handle only expected events.
- Partial captures/refunds: Reflect status transitions (FAILED/REFUNDED/CANCELLED) and adjust credits accordingly.
- Observability: Log state transitions; add metrics and alerts for stuck PENDING or frequent failures.
Coming soon: production-ready webhook handler examples and reconciliation jobs.