Payments
Creem payments for GoCommerce
A Go package that creates a Creem checkout at the order’s price and marks the GoCommerce order paid when a signed checkout.completed event says it was paid. Creem refunds in full only, and the module refuses a partial refund rather than send one.
- Payments module
- 6 settings
- 12 tests
- ext/payments-creem
What it does
The module registers “creem” as a payment method. At checkout it creates a Creem checkout against a placeholder product you name, overrides its price with the order’s total in minor units, and answers with a redirect intent carrying the checkout URL. The order id also goes as Creem’s request_id, so a row in Creem’s dashboard can be tied to the store without opening the payload.
Creem then calls POST /api/checkout/creem/webhook. The module verifies the creem-signature header and, when checkout.completed arrives with the order paid, asks the engine to mark the GoCommerce order paid. It records Creem’s transaction id at the same moment, because a refund needs it and it cannot be known any earlier.
In the admin it describes itself as “Checkouts through Creem as merchant of record, each created against one placeholder one-time product. Creem refunds in full only, so a partial refund has to be made in its dashboard.” As with Paddle and Lemon Squeezy, Creem works out the tax, so GoCommerce’s tax rates should stay empty.
Configuration
Three settings are required: the API key, the webhook’s signing secret, and the placeholder product every checkout is created against. Set them in Config from your own main(), or under Settings › Payment methods.
| Setting | Environment variable | Required | What it does |
|---|---|---|---|
APIKeyAPI key | CREEM_API_KEY | Yes | From the developers section of Creem’s dashboard, sent as the x-api-key header. Not sent to the storefront. |
WebhookSecretWebhook signing secret | CREEM_WEBHOOK_SECRET | Yes | Verifies the creem-signature header. Required because, as the code puts it, without it any caller could mark orders paid. |
ProductIDPlaceholder product ID | CREEM_PRODUCT_ID | Yes | A one-time product every checkout is created against; its price is overridden per order. Creem refuses a custom price on a subscription product. |
SuccessURLWhere to send the shopper afterwards | — | No | Where Creem sends the shopper after paying. Empty shows Creem’s own confirmation. |
BaseURLAPI base URL | — | No | Empty for production, https://api.creem.io; https://test-api.creem.io for the sandbox. |
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
- Create the placeholderIn Creem, create one one-time product — any name, any price — and note its id. A subscription product will not take a custom price.
- Add the webhookPoint a webhook at https://your-api-host/api/checkout/creem/webhook and keep its signing secret. checkout.completed is the event that settles an order.
- Install and configurePass creem.New to gocommerce.New as below, or run the reference binary with -gateways and fill in Settings › Payment methods. For the sandbox, set BaseURL to https://test-api.creem.io.
- Take a paymentPOST /api/checkout/creem answers with a redirect intent whose url is Creem’s checkout. A paid checkout.completed marks the order paid.
main.go
import (
"os"
"github.com/itswadesh/gocommerce/core"
creem "github.com/itswadesh/gocommerce/ext/payments-creem"
)
app, err := gocommerce.New(cfg,
creem.New(creem.Config{
APIKey: os.Getenv("CREEM_API_KEY"),
WebhookSecret: os.Getenv("CREEM_WEBHOOK_SECRET"),
ProductID: os.Getenv("CREEM_PRODUCT_ID"),
}),
) The package doc’s own example, with its imports; cfg is your gocommerce.Config. The sandbox host is exported as creem.TestBaseURL for BaseURL. Import path github.com/itswadesh/gocommerce/ext/payments-creem.
How it works
-
Amounts checked first
Creem takes a custom price from 100 to 99,999,999 minor units. The module checks the order total against that before calling, so an operator reads what is wrong with the order rather than an error about a field nobody filled in.
-
Signed, without a timestamp
creem-signature is a hex HMAC-SHA256 of the raw body, compared in constant time. With no timestamp in it, the claim table is what bounds a replay.
-
Each event once
Creem sends its own event id and the claim is that id. An event without one is refused with a 400 rather than guessed at; a redelivery is answered 200 and changes nothing.
-
Only paid settles
checkout.completed with an order status other than paid is acknowledged and changes nothing. If marking the order paid fails, the claim is released and the webhook answers 500 for Creem to retry.
-
Whole refunds, from the admin
A refund from the admin posts the recorded transaction id to Creem’s refunds endpoint, which returns the full remaining amount. A pending answer is accepted: the engine has recorded the refund, and Creem settles it later.
-
Dashboard events reach the log
refund.created and dispute.created from Creem’s side are logged with the order they concern, so the store is not the last to know.
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.
- All or nothing refundsCreem’s refund endpoint takes no amount, so a request for less than the whole order is refused, with a message pointing at Creem’s dashboard. Partial refunds are made there.
- Refunds need the webhook firstThe transaction id arrives with checkout.completed. Before it, a refund is refused: no transaction recorded for this order yet.
- A one-time placeholder productOne product in Creem is required, and it must be a one-time product; Creem refuses a custom price on a subscription.
- Price limitsOrders under 100 or over 99,999,999 in minor units are refused before Creem is called.
- No currency is sentThe checkout carries custom_price in minor units and no currency code. Check that Creem charges in the currency your store settles in.
- No failure pathThe module never marks a payment failed, and dashboard refunds and disputes are only logged. An abandoned checkout leaves the order unpaid.
FAQ
Questions about the Creem module
Can I make a partial refund?
Not from GoCommerce. Creem’s refund endpoint refunds the full remaining amount and takes no amount from the caller, so the module refuses anything less than the whole order rather than send it. The package’s reason: an operator asking for a small goodwill refund must not see Creem return the whole order. Make partial refunds in Creem’s dashboard.
Why does it need a placeholder product?
Creem sells items from its own catalogue, so a checkout must name a product that already exists there. The module overrides that product’s price with the order total, so one one-time product serves every order.
How do I use Creem’s sandbox?
Set BaseURL to https://test-api.creem.io and use a test key. The package made the sandbox a separate host rather than a flag, so a test key cannot be pointed at production by forgetting to unset something.
Is this a Creem partnership?
No. The module calls Creem’s public API with keys you supply. Creem 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-creem in the GoCommerce repository, MIT licensed. When this page and the code disagree, the code is right and this page is out of date.Creem 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.