Payments
Razorpay payments for GoCommerce
A Go package that creates a Razorpay order at checkout and marks the GoCommerce order paid when Razorpay’s signed webhook says the payment was captured. Three keys to set, one webhook URL to register — and, unlike every other payment module in the repository, no tests yet.
- Payments module
- 6 settings
- no tests
- ext/payments-razorpay
What it does
The module registers “razorpay” as a payment method. At checkout it creates a Razorpay order for the order’s total, in minor units and the store’s currency, and hands the storefront what Razorpay’s checkout needs: the Razorpay order id, the key id, the amount and the currency.
Razorpay then calls POST /api/checkout/razorpay/webhook, a route the engine owns. The module checks the signature, and on payment.captured asks the engine to mark the order paid; on payment.failed, to mark the payment failed. It never writes to an order itself — the engine makes the transition and records the event, as it does for every provider.
In the admin it describes itself as “Cards, UPI and netbanking through Razorpay, India, with its signed webhook marking orders paid.” It speaks Razorpay’s REST API over Go’s standard library: in the package’s own words, an order is one JSON POST and a webhook is one HMAC, so the SDK would add a dependency without adding capability.
Configuration
Three settings are required: the API key pair and the webhook secret. Set them in Config from your own main(), or leave Config empty and type them under Settings › Payment methods in the admin.
| Setting | Environment variable | Required | What it does |
|---|---|---|---|
KeyIDKey ID | RAZORPAY_KEY_ID | Yes | The API key id. It is also handed to the storefront at checkout, because Razorpay’s checkout needs it. |
KeySecretKey secret | RAZORPAY_KEY_SECRET | Yes | The API key secret, sent with the key id as HTTP basic auth. Not sent to the storefront. |
WebhookSecretWebhook secret | RAZORPAY_WEBHOOK_SECRET | Yes | Verifies the X-Razorpay-Signature header. Required because, as the code puts it, without it any caller could mark orders paid. |
HostedUse the hosted checkout page | — | No | Answers checkout with a redirect intent for Razorpay’s hosted page instead of data for the in-page widget. Off by default. |
BaseURLAPI base URL | — | No | Overrides https://api.razorpay.com, for tests. Empty for production. |
Client | — | No | Replaces the HTTP client, which otherwise times out after 20 seconds. Go only; not in the panel. |
The module reads its Config struct, not the environment. The variable names are the ones the package’s own example or the reference binary uses; in your own main() you choose where each value comes from. Where a setting has a panel label, the admin’s settings drawer can hold it too, and a value typed there wins over Config.
Setting it up
- Register the webhookIn Razorpay, point a webhook at https://your-api-host/api/checkout/razorpay/webhook for the payment.captured and payment.failed events, and keep its secret.
- Install the moduleImport it and pass razorpay.New to gocommerce.New, as below — or run the reference binary with -gateways, which installs every payment module idle.
- Give it the keysKey ID, key secret and webhook secret, in Config or under Settings › Payment methods. A key typed into the panel counts on the next request, with no restart.
- Take a paymentPOST /api/checkout/razorpay answers with the order and a client_action intent carrying razorpay_order_id and key_id for Razorpay’s checkout. The webhook marks it paid.
main.go
import (
"os"
"github.com/itswadesh/gocommerce/core"
razorpay "github.com/itswadesh/gocommerce/ext/payments-razorpay"
)
app, err := gocommerce.New(cfg,
razorpay.New(razorpay.Config{
KeyID: os.Getenv("RAZORPAY_KEY_ID"),
KeySecret: os.Getenv("RAZORPAY_KEY_SECRET"),
WebhookSecret: os.Getenv("RAZORPAY_WEBHOOK_SECRET"),
}),
) The package doc’s own example, with its imports; cfg is your gocommerce.Config. With an empty razorpay.Config the module installs idle and waits for the panel. Import path github.com/itswadesh/gocommerce/ext/payments-razorpay.
How it works
-
One route, owned by the engine
Razorpay calls POST /api/checkout/razorpay/webhook. The engine hands the module the request body untouched, because the signature is over the exact bytes Razorpay sent.
-
Signed, or refused
The body’s HMAC-SHA256 is compared with X-Razorpay-Signature in constant time. A missing or wrong signature is refused with a 400; until the module is switched on and configured, every webhook is refused with a 503.
-
Each fact once
Razorpay sends no per-delivery event id, so the module keys each event on its name plus the payment id and claims it with INSERT … ON CONFLICT DO NOTHING in its own table. A repeated capture is answered 200 and changes nothing.
-
A failed update is retried
If marking the order paid fails, the module releases its claim on the event and answers 500, so Razorpay’s retry finds work to do rather than a record saying it was handled.
-
Refunds keep Razorpay’s id
A refund from the admin posts to Razorpay against the captured payment, for the amount asked, and the refund id Razorpay answers with is recorded on the refund row — the id somebody reconciles against a bank statement.
-
Settings without a restart
Before each use the panel’s values are laid over Config. A store with nothing in Config installs the module idle; one with all three keys in Config starts with it switched on.
What it does not do
Read these before an order depends on it. No store is known to run GoCommerce in production yet, so these come from the code, not from anyone’s experience.
- No testsThe package has no _test.go file. Every other payment module in the repository ships with one, so read razorpay.go before an order depends on it.
- Captured payments onlyOnly payment.captured marks an order paid. A payment that is authorised but never captured leaves the order unpaid; when Razorpay captures is a setting on Razorpay’s side.
- Refunds need the webhook firstA refund looks up the payment id the payment.captured webhook recorded. If that webhook never arrived, the refund is refused with “no captured payment recorded for this order”.
- Dashboard refunds are not readEvents other than payment.captured and payment.failed are acknowledged and ignored, so a refund made in Razorpay’s dashboard does not appear on the GoCommerce order.
- Hosted mode returns no URLWith Hosted on, the intent is a redirect carrying the Razorpay order id, the key id and your return URL as callback_url. There is no page URL in it; the storefront still sends the shopper to Razorpay itself.
- The store’s one currencyThe total goes to Razorpay in the store’s single settlement currency, unconverted. Which currencies Razorpay accepts is Razorpay’s decision, not the module’s.
FAQ
Questions about the Razorpay module
Does it take UPI?
The module creates a Razorpay order and leaves the choice of method to Razorpay’s checkout, so the shopper is offered what your Razorpay account offers — the module’s own description names cards, UPI and netbanking. There is no per-method code in the module.
What is the webhook URL?
It is fixed by the engine: POST /api/checkout/razorpay/webhook on the host that serves your GoCommerce API. Subscribe it to payment.captured and payment.failed; any other event is acknowledged and ignored.
Can I use it without writing Go?
Yes. The reference binary’s -gateways flag installs every payment module with an empty Config. Switch Razorpay on under Settings › Payment methods and type the three keys there. Your own main() is the route when you want the keys to come from the environment instead.
Why no Razorpay SDK?
The package doc answers it: an order is one JSON POST and a webhook is one HMAC, so the SDK would add a dependency without adding capability. GoCommerce has one production dependency, and its modules add none.
Is this a Razorpay partnership?
No. The module calls Razorpay’s public API with keys you supply. Razorpay does not endorse GoCommerce, and no store is known to run this module in production yet.
Source
Everything on this page is read from ext/payments-razorpay in the GoCommerce repository, MIT licensed. When this page and the code disagree, the code is right and this page is out of date.Razorpay is a trademark of its owner; this module talks to its public API and implies no endorsement. See trademarks.
Try it against a store of your own
The one-command stack gives you GoCommerce’s API and admin on your own machine in minutes. Add this module to it and try it on test orders before a real one depends on it.