# Cashfree integration in Go (GoCommerce module)

> Take UPI, cards, netbanking and wallets through Cashfree in a GoCommerce store: two keys, the webhook URL, the return check, refunds, and rupees only.

- Canonical: https://kitcommerce.store/integrations/payments-cashfree/
- Last updated: 2026-10-28

---

Payments

## Cashfree integration in Go

A Go package that opens a Cashfree order at checkout, hands the storefront its payment session for Cashfree’s own checkout, and marks the GoCommerce order paid when Cashfree’s signed webhook says the payment succeeded. Two keys to set, one webhook URL to register, and rupees only.

- **Payments** module
- **5** settings
- **28** tests
- **1** API operation
- **ext/payments-cashfree**

### What it does

The module registers “cashfree” as a payment method. At checkout it creates a Cashfree order for the order’s total and answers with the order’s payment_session_id, which Cashfree’s JavaScript checkout opens. The shopper pays on Cashfree’s page, so card numbers never reach the store. In the admin it describes itself as “UPI, cards, netbanking and wallets on Cashfree’s checkout, India, with its signed webhook marking orders paid. Rupees only.”

Coming back to the storefront proves nothing; the signed webhook settles the order. The storefront’s return page can ask POST /x/payments-cashfree/orders/{cf_order_id}/verify to have Cashfree asked at once rather than wait, and the order screen’s “Check with Cashfree” asks the same question. Either way the order is marked paid through the engine’s own check, the same path the webhook takes.

Cashfree takes and reports amounts as rupees with two decimals, and the engine holds paise. The two are converted as strings of digits, never through a float. Each checkout gets a Cashfree order id of its own, the order number with a random suffix, because two stores can share an account and a checkout can be retried; the module keeps its own table from that id to the store’s order, with the amount it asked for.

### Configuration

Two settings are required: the App ID and the secret key. There is no separate webhook secret, because Cashfree signs its webhooks with the secret key. Set them in Config from your own main(), or leave Config empty and type them under Settings › Payment methods in the admin.

*Cashfree module settings — 5 settings, 2 required*

| Setting | Environment variable | Required | What it does |
| --- | --- | --- | --- |
| `AppID` App ID | `CASHFREE_APP_ID` | Yes | The API key’s client id, sent as the x-client-id header. The panel’s hint: Developers, API Keys, in the Cashfree dashboard. |
| `SecretKey` Secret key | `CASHFREE_SECRET_KEY` | Yes | The API secret, sent as x-client-secret. It also verifies the webhooks, so, as the code puts it, without it any caller could mark orders paid. Not sent to the storefront. |
| `Environment` Environment | — | No | production, the default, or sandbox, Cashfree’s test mode with its own keys. It picks Cashfree’s host, and is handed to the storefront at checkout, because a session opened in the wrong environment fails. |
| `BaseURL` API base URL | — | No | Overrides the environment’s host, https://api.cashfree.com/pg or https://sandbox.cashfree.com/pg, for tests. It must be a public address: a private or loopback one is refused unless the install sets GOCOMMERCE_ALLOW_PRIVATE_URLS. |
| `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

1. **Set the webhook in Cashfree** — In the Cashfree dashboard, under Developers, Webhooks, point the payment events at https://your-api-host/api/checkout/cashfree/webhook, webhook version 2023-08-01.
2. **Install the module** — Pass cashfree.New to gocommerce.New, as below, or run the reference binary with -gateways, which installs every payment module idle.
3. **Give it the keys** — App ID, secret key and environment, in Config or under Settings › Payment methods. A key typed into the panel counts on the next request, with no restart.
4. **Take a payment** — POST /api/checkout/cashfree answers with payment_session_id, the Cashfree order id and the environment for Cashfree’s checkout. The webhook marks the order paid.

main.go

```
import (
	"os"

	"github.com/itswadesh/gocommerce/core"
	cashfree "github.com/itswadesh/gocommerce/ext/payments-cashfree"
)

app, err := gocommerce.New(cfg,
	cashfree.New(cashfree.Config{
		AppID:     os.Getenv("CASHFREE_APP_ID"),
		SecretKey: os.Getenv("CASHFREE_SECRET_KEY"),
	}),
)
```

The package doc’s own example, with its imports; cfg is your gocommerce.Config. With an empty cashfree.Config the module installs idle and waits for the panel, which is how the reference binary’s -gateways flag installs it. Import path `github.com/itswadesh/gocommerce/ext/payments-cashfree`.

### How it works

- **One webhook route, owned by the engine**

  Cashfree calls POST /api/checkout/cashfree/webhook. The engine hands the module the body untouched, because the signature is over the exact bytes Cashfree sent. Until the module is switched on and configured, every webhook is refused with a 503.

- **Signed and timed**

  x-webhook-signature is checked as the base64 HMAC-SHA256, keyed on the secret key, of x-webhook-timestamp followed by the raw body, in constant time. A timestamp more than five minutes from now is refused, as is a missing or wrong signature (400).

- **Each outcome once**

  A failure and then a success can arrive for one Cashfree order, so the claim is on the event type and the payment id, in the module’s own table with ON CONFLICT DO NOTHING. A redelivery is answered 200 and changes nothing; a failure arriving after the order is paid leaves it paid.

- **The amount asked is the check**

  A success settles the order only when the Cashfree order’s amount matches what the module asked for, and that still matches the order’s total, in INR. An offer can make the payment itself smaller, so it is the Cashfree order’s amount that is compared.

- **A failed update is retried**

  If marking the order paid or failed does not go through, the module releases its claim and answers 500, so Cashfree’s retry finds work to do.

- **Refunds, whole or in part**

  A refund from the admin goes to Cashfree against the Cashfree order the money was taken on, never for more than Cashfree captured. Its refund id is built from the order, the refunds before it and the amount, so a retry after a lost answer is refused by Cashfree as a duplicate rather than paid twice.

- **The return check is rationed**

  The verify route answers a paid order from the store, and asks Cashfree about one Cashfree order at most once every 15 seconds, so a page reloaded in a loop cannot spend the merchant’s API allowance. It answers with the order number and payment status, nothing more.

### 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.

- **Rupees only** — An order in any currency but INR is refused before a Cashfree order is made. The store’s settlement currency has to be the rupee.
- **A phone number is required** — Cashfree needs the shopper’s phone. An order with none, on the order or its address, is refused at checkout.
- **Ten tries per order** — An order may collect at most 10 Cashfree orders through retried checkouts. After that the shopper is told to place a new order.
- **A changed total waits for a person** — A success for a Cashfree order whose amount no longer matches the order is logged as an error, acknowledged and not applied. The order stays unpaid until an operator reconciles it.
- **Money for a cancelled order** — A payment that arrives after the order was cancelled does not settle it. It goes on the store’s Errors screen, and the refund is made by hand in Cashfree’s dashboard.
- **Payment events only** — Only PAYMENT_SUCCESS_WEBHOOK and PAYMENT_FAILED_WEBHOOK are acted on. A refund made in Cashfree’s dashboard, a dropped-out shopper or any other event is acknowledged and does not change the GoCommerce order.
- **The rationing is per process** — The verify route’s 15-second spacing is kept in memory. After a restart, or on another instance, the first call asks Cashfree again.

FAQ

### Questions about the Cashfree module

**What is the webhook URL?**

POST /api/checkout/cashfree/webhook on the host that serves your GoCommerce API, a route the engine owns. Set it in the Cashfree dashboard under Developers, Webhooks, for the payment events, with webhook version 2023-08-01. There is no webhook secret to copy: the secret key verifies it.

**Does it take UPI?**

The module opens a Cashfree order and leaves the choice of method to Cashfree’s checkout, so the shopper is offered what your Cashfree account offers. The module’s own description names UPI, cards, netbanking and wallets. There is no per-method code in it.

**What if the webhook is late?**

The storefront’s return page can post the cf_order_id Cashfree adds to the return URL to /x/payments-cashfree/orders/{cf_order_id}/verify, and an operator can press Check with Cashfree on the order. Both ask Cashfree for the order’s payments and record a success the way the webhook would.

**How do I test it?**

Set Environment to sandbox, which points the module at sandbox.cashfree.com, and give it the sandbox keys Cashfree issues separately. The module’s own tests answer in Cashfree’s place with a local test server; none calls Cashfree.

**Is this a Cashfree partnership?**

No. The module calls Cashfree’s public API with keys you supply. Cashfree does not endorse GoCommerce, and no store is known to run this module in production yet.

### Related modules

Each is a directory under `ext/`, imported the same way. Where this one fits in the store: [Checkout and payments in GoCommerce](https://kitcommerce.store/features/checkout/).

- [Clover integration in Go Take card payments through Clover’s Hosted Checkout in a GoCommerce store: three keys, the webhook URL, refunds, USD or CAD, and what the module leaves out.](https://kitcommerce.store/integrations/payments-clover/)
- [Creem integration in Go Sell through Creem as merchant of record from a GoCommerce store: the placeholder product, keys, webhook URL, full-only refunds and the module’s limits.](https://kitcommerce.store/integrations/payments-creem/)
- [Helcim integration in Go Take card payments through Helcim’s HelcimPay.js in a GoCommerce store: API token, verifier token, webhook URL, refunds, and what the module leaves out.](https://kitcommerce.store/integrations/payments-helcim/)
- [Hyperswitch integration in Go Route payments through Hyperswitch, the open-source payments router, from a GoCommerce store: keys, payment links, webhook URL, refunds and limits.](https://kitcommerce.store/integrations/payments-hyperswitch/)

### Source

Everything on this page is read from [`ext/payments-cashfree`](https://github.com/itswadesh/gocommerce/tree/main/ext/payments-cashfree) in the GoCommerce repository, MIT licensed. When this page and the code disagree, the code is right and this page is out of date.Cashfree is a trademark of its owner; this module talks to its public API and implies no endorsement. See [trademarks](https://kitcommerce.store/about/#trademarks).

- [ext/payments-cashfree on GitHub](https://github.com/itswadesh/gocommerce/tree/main/ext/payments-cashfree)
- [Cashfree](https://www.cashfree.com)
- [All GoCommerce modules](https://kitcommerce.store/integrations/)

### Try it against a store of your own

The backend + admin quick start runs GoCommerce’s API and admin on your own machine, without a storefront. Add this module to it and try it on test orders before a real one depends on it.

[Backend + admin quick start](https://kitcommerce.store/#one-command) · [Read the module](https://github.com/itswadesh/gocommerce/tree/main/ext/payments-cashfree) · [All integrations](https://kitcommerce.store/integrations/)
