[AVAILABLE], test mode and live mode both proven end to end against a real, deployed
Razorpay account.
packages/connector-sdk/src/connectors/razorpay/,
packages/api/src/webhooks/, docs/CLAIMS.md §3.4 through §3.9. This is the only connector
in this codebase that talks to a real external system, everything else registered by default
(vendor-payment) or offered as a reference (SAP, Salesforce, Workday, Oracle) is a mock, see
The gateway.What this connector does
Refund creation against Razorpay, policy-gated, idempotent, and closed out by a signed settlement confirmation once Razorpay actually processes the refund. Nothing else. It does not create payments, does not handle payouts (RazorpayX is a distinct, unimplemented capability, [FUTURE]), and does not change anything about Policy Engine, Execution Gateway, Replay, Receipt Generation, Verification, or the REST API, those work exactly as documented elsewhere on this site.Why it’s different from the other four connectors on this site
packages/connector-sdk/src/connectors/{sap,oracle,workday,salesforce}/ exist as explicit,
self-documented mocks, deterministic and in-memory, standing in for a real integration that
hasn’t been built. Razorpay is not that. RazorpayConnector calls
https://api.razorpay.com/v1 over real HTTP with HTTP Basic auth
(Authorization: Basic base64(keyId:keySecret)), and the codebase’s live integration test
suite (gated, see below) has executed a real refund against it, in both Razorpay’s test mode
and, once, in live mode against real money.
The three pieces
A Razorpay-backed refund lifecycle has three moving parts, each independently real and tested:- Refund creation. Your server calls
POST /executewith arazorpay-refundpolicy reference. If the policy approves, the connector calls Razorpay’s refund-create API. - Webhook receipt. Razorpay calls back to
POST /webhooks/razorpaywhen the refund’s status changes. This milestone stops at verifying the signature and durably recording the event, it does not yet act on it. See Verify Razorpay webhooks. - Settlement confirmation. A background poll loop picks up the recorded event, fetches
the refund’s real status directly from Razorpay (never trusting the webhook’s own claimed
event type), and appends a signed
SettlementConfirmationto the Execution Trust Record. See Settlement.
Requesting a refund
RazorpayRefundOutcome.replayed is true when this exact businessTransactionId was seen
before, the service answers from a local outcome cache with zero policy evaluation and zero
HTTP calls to Razorpay, stronger than, and separate from, the connector’s own idempotency
check against Razorpay’s own refund list (matching on
notes["parmana_txn"] === businessTransactionId). key_secret never appears anywhere in the
receipt, keyIdRedacted shows only the first 8 characters.
The reference policy
policies/razorpay-refund/1.0.0/policy.json is a real, loaded policy, not a stub. It rejects
a refund unless every condition holds: the payment is captured, the currency is INR, the
requested amount is positive, the amount does not exceed the payment’s refundable remainder,
the amount does not exceed a 500000 paise (₹5000) per-refund cap, and the running daily
total does not exceed a 2000000 paise (₹20000) cumulative cap. Anything that fails any
condition is rejected by an explicit, matching rule, reject-default is the catch-all if
somehow none of the specific rules match.
Try it without touching Razorpay at all
MockRazorpayServer with fake test-mode credentials and runs the real
RazorpayRefundService against it, loading the real policy above from disk. Real output from
a run against this exact code:
Configuration
Once any Razorpay credential is set, Supabase becomes required too, the webhook and
settlement event stores have no in-memory fallback in production. See Production
deployment.
What’s proven where, precisely
This is the part worth reading in full before deciding how much to trust this connector.packages/api/tests/integration/razorpay-refund.integration.test.tsandrazorpay-settlement.integration.test.tshitMockRazorpayServer, not the real network. They run on everynpm test, no gate required.packages/api/tests/integration/razorpay-real-webhook-fixture.integration.test.tsalways runs too, it replays a real, captured Razorpay-delivered webhook payload (redacted for PII) through the live route code, no live network call, but real captured bytes, not a synthetic fixture.packages/api/tests/integration/razorpay-live.integration.test.tsis the only test in this codebase that calls a real Razorpay endpoint. It is gated behindALLOW_LIVE_RAZORPAY=1plus realRAZORPAY_TEST_KEY_ID/RAZORPAY_TEST_KEY_SECRETcredentials (which must start withrzp_test_, or the suite aborts before any network call), and does not run in a defaultnpm test. Even fully enabled, this suite only proves Razorpay test-mode reachability and money movement, one ₹1.00 (100 paise) refund against a manually captured test-mode payment, never live-mode.- True live-mode operation (real money, real bank settlement) is proven once, manually,
against a permanent production deployment, not by the automated test suite: one
authenticated, policy-gated 100-paise refund against a real, card-paid ₹10 Payment Link,
producing a genuine
refund.processedwebhook from Razorpay’s own infrastructure and a signedSETTLEDsettlement confirmation roughly 43 seconds later. This validates one real-money transaction end to end, not sustained live-mode operation, volume, or failover. Seedocs/CLAIMS.md§3.9 for the full trace.
[FUTURE]
- RazorpayX payouts. No implementation exists. This connector covers refund creation only.
- A general-purpose way to add a new external connector. Adding anything beyond Razorpay and the four reference mocks means editing bootstrap code today, see Add a connector.
Next
Verify Razorpay webhooks
The signature scheme and the fetch-verify pattern that closes the loop.
Settlement
The signed object this whole lifecycle produces.