Payments
Clover integration in Go
A Go package that sends the shopper to Clover’s Hosted Checkout page for the order’s total and marks the GoCommerce order paid when Clover’s signed webhook says the payment was approved. Card numbers never reach the store, and there is no Clover SDK in your build.
- Payments module
- 8 settings
- 16 tests
- ext/payments-clover
What it does
The module registers “clover” as a payment method. At checkout it creates a Hosted Checkout session for the order’s total and answers with a redirect intent carrying the session’s URL. The shopper types their card on Clover’s page and comes back to the return URL — which, in the package’s words, proves nothing: the signed webhook is what settles the order.
The order goes to Clover as one line for its total, with tips switched off. The engine has already worked out tax, shipping and discounts, and Clover would otherwise apply its own; Clover also refunds part of a payment only when the payment was one line with no tax or tip, so this is what makes a partial refund possible.
A Hosted Checkout session carries no currency and no reference of the store’s. So the module is told the merchant account’s currency, USD or CAD, and refuses an order in any other rather than charge 2500 Canadian cents for 2500 US ones; and it keeps its own table from session to order, with the amount each session asked for. The package notes that Hosted Checkout is offered in the United States and Canada only.
Configuration
Three settings are required: the merchant ID, the Ecommerce private token and the webhook signing 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 |
|---|---|---|---|
MerchantIDMerchant ID | CLOVER_MERCHANT_ID | Yes | The Clover merchant the payments go to, sent as the X-Clover-Merchant-Id header. The panel’s hint: from the Clover dashboard’s address bar or Account & Setup. |
PrivateTokenEcommerce private token | CLOVER_PRIVATE_TOKEN | Yes | The merchant’s Ecommerce API private token, from Settings › Ecommerce › Ecommerce API Tokens in the Clover dashboard. Sent as a bearer token; not sent to the storefront. |
WebhookSecretWebhook signing secret | CLOVER_WEBHOOK_SECRET | Yes | The Hosted Checkout signing secret. Required because, as the code puts it, without it any caller could mark orders paid. |
CurrencyAccount currency | — | No | USD or CAD; USD by default. Clover charges in the account’s own currency, so an order in any other is refused before a session is made. |
ReturnURLReturn URL | — | No | Where the shopper lands after paying, when the checkout sends no return_url of its own. It must be https: Clover sends the shopper back to nothing else, and the module refuses the checkout otherwise. |
SandboxSandbox | — | No | Points the module at apisandbox.dev.clover.com, with a test merchant’s own token and secret. |
BaseURLAPI base URL | — | No | Overrides https://api.clover.com, and the sandbox switch, for tests. Empty for production. 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
- Set the webhook in CloverIn the Clover dashboard, under Settings › Ecommerce › Hosted Checkout, set the webhook URL to https://your-api-host/api/checkout/clover/webhook and generate the signing secret.
- Install the modulePass clover.New to gocommerce.New, as below, or run the reference binary with -gateways, which installs every payment module idle.
- Give it the keysMerchant ID, private token, signing secret and the account’s currency, in Config or under Settings › Payment methods, with a return URL if the storefront sends none.
- Take a paymentPOST /api/checkout/clover answers with a redirect intent whose url is Clover’s Hosted Checkout page. The PAYMENT webhook marks the order paid.
main.go
import (
"os"
"github.com/itswadesh/gocommerce/core"
clover "github.com/itswadesh/gocommerce/ext/payments-clover"
)
app, err := gocommerce.New(cfg,
clover.New(clover.Config{
MerchantID: os.Getenv("CLOVER_MERCHANT_ID"),
PrivateToken: os.Getenv("CLOVER_PRIVATE_TOKEN"),
WebhookSecret: os.Getenv("CLOVER_WEBHOOK_SECRET"),
}),
) The package doc’s own example, with its imports; cfg is your gocommerce.Config. With an empty clover.Config the module installs idle and waits for the panel, which is how examples/store/main.go and the reference binary install it. Import path github.com/itswadesh/gocommerce/ext/payments-clover.
How it works
-
One route, owned by the engine
Clover calls POST /api/checkout/clover/webhook. The engine hands the module the request body untouched, because the signature is over the exact bytes Clover sent.
-
Signed and timed
Clover-Signature’s t=…,v1=… is checked as an HMAC-SHA256 over the timestamp, a dot and the body, in constant time; any v1 that matches is a match. A timestamp more than five minutes from now is refused, as is a missing or wrong signature (400).
-
The session’s amount is the check
The webhook carries no amount, so the module compares the amount and currency its session asked for with the order’s. An order whose total changed since is logged as an error and not settled, for an operator who can see both sides.
-
Each outcome once
A decline and then an approval can arrive for one session, so the claim is on the payment and its outcome, in the module’s own table with ON CONFLICT DO NOTHING. A redelivery is answered 200 and changes nothing; a decline arriving after the approval leaves the order paid.
-
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 Clover’s retry finds work to do.
-
Refunds, whole or in part
A refund from the admin posts to Clover’s refunds endpoint against the payment id the webhook recorded, for the amount asked. Clover’s refund id is kept; a refund Clover marks failed or cancelled comes back as an error with its reason.
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.
- USD and CAD onlyHosted Checkout is offered in the United States and Canada, and charges in the merchant account’s currency. The store’s settlement currency has to be that one; any other order is refused at checkout.
- The return proves nothingThe shopper’s return to the storefront does not settle the order. Until the webhook arrives the order reads unpaid, and an order with no working webhook stays that way.
- A changed total waits for a personAn approved payment whose session asked for a different amount or currency than the order now holds is acknowledged and not applied. The order stays unpaid until an operator reconciles it.
- Refunds need the webhook firstUntil the webhook records Clover’s payment id, the order holds only the checkout session, which Clover cannot refund; the module says so rather than try.
- Dashboard refunds and disputesThe module acts on PAYMENT notifications only. A refund made in Clover’s dashboard, or a dispute, does not change the GoCommerce order.
- Nothing travels with the sessionExtra data the storefront sends at checkout is not passed to Clover: a Hosted Checkout session has nowhere to carry it. The order’s email goes as the customer’s.
FAQ
Questions about the Clover module
What is the webhook URL?
POST /api/checkout/clover/webhook on the host that serves your GoCommerce API — a route the engine owns, so the module mounts nothing itself. Set it in the Clover dashboard under Settings › Ecommerce › Hosted Checkout, where the signing secret is generated.
Does the shopper leave the storefront?
Yes. Checkout answers with a redirect to Clover’s Hosted Checkout page, where the card is typed, so card numbers never reach the store. Clover then sends the shopper back to the return URL, which has to be https.
How do I test it?
Switch on Sandbox, which points the module at apisandbox.dev.clover.com, and give it a test merchant’s ID, token and signing secret — Clover issues those separately. The module’s own tests answer in Clover’s place with a local test server; none calls Clover.
Can I sell in euros or pounds?
No. Hosted Checkout is offered in the United States and Canada, and the module accepts USD or CAD as the account’s currency, refusing an order in any other.
Is this a Clover partnership?
No. The module calls Clover’s public API with credentials you supply. Clover 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-clover in the GoCommerce repository, MIT licensed. When this page and the code disagree, the code is right and this page is out of date.Clover 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 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.