# Lemon Squeezy Payments for GoCommerce — Open-Source Module

> Sell through Lemon Squeezy as merchant of record from a GoCommerce store: the placeholder variant, four settings, webhook URL, refunds and limits.

- Canonical: https://kitcommerce.store/integrations/payments-lemonsqueezy/
- Last updated: 2026-09-25

---

Payments

## Lemon Squeezy payments for GoCommerce

A Go package that creates a Lemon Squeezy checkout at the order’s price and marks the GoCommerce order paid when a signed order_created webhook says it was paid. It needs one placeholder product in Lemon Squeezy — the one setup step Paddle does not.

- **Payments** module
- **6** settings
- **7** tests
- **ext/payments-lemonsqueezy**

### What it does

The module registers “lemonsqueezy” as a payment method. At checkout it creates a Lemon Squeezy checkout against a store and a variant you name, replaces the variant’s price with the order’s total in minor units, and answers with a redirect intent carrying the checkout URL.

Lemon Squeezy then calls POST /api/checkout/lemonsqueezy/webhook. The module verifies X-Signature and, when order_created arrives with the status paid, asks the engine to mark the order paid, recording Lemon Squeezy’s own order id — the one a refund is later issued against.

Why the placeholder: Lemon Squeezy sells items from its own catalogue, so a checkout must name a variant that already exists there. You create one product — any name, any price, never shown to a shopper — and every order becomes that variant at the order’s own price. Like Paddle, Lemon Squeezy is the merchant of record and works out the tax itself.

### Configuration

Four settings are required: the API key, the webhook’s signing secret, and the store and placeholder variant every checkout is created against. Set them in Config from your own main(), or under Settings › Payment methods.

*Lemon Squeezy module settings — 6 settings, 4 required*

| Setting | Environment variable | Required | What it does |
| --- | --- | --- | --- |
| `APIKey` API key | `LEMONSQUEEZY_API_KEY` | Yes | Sent as a bearer token. Not sent to the storefront. |
| `WebhookSecret` Webhook signing secret | `LEMONSQUEEZY_WEBHOOK_SECRET` | Yes | Verifies the X-Signature header. Required because, as the code puts it, without it any caller could mark orders paid. |
| `StoreID` Store ID | `LEMONSQUEEZY_STORE_ID` | Yes | The Lemon Squeezy store every checkout is created in. |
| `VariantID` Placeholder variant ID | `LEMONSQUEEZY_VARIANT_ID` | Yes | The catalogue item every checkout is created against; its price is replaced by the order total. |
| `BaseURL` API base URL | — | No | Overrides https://api.lemonsqueezy.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

1. **Create the placeholder** — In Lemon Squeezy, create one product with any name and price, and note the store id and its variant id. Shoppers never see it.
2. **Add the webhook** — Point a webhook at https://your-api-host/api/checkout/lemonsqueezy/webhook for order_created, and keep its signing secret.
3. **Install and configure** — Pass lemonsqueezy.New to gocommerce.New as below, or run the reference binary with -gateways and fill in Settings › Payment methods. Leave GoCommerce’s tax rates empty.
4. **Take a payment** — POST /api/checkout/lemonsqueezy answers with a redirect intent whose url is the hosted checkout. A paid order_created marks the order paid.

main.go

```
import (
	"os"

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

app, err := gocommerce.New(cfg,
	lemonsqueezy.New(lemonsqueezy.Config{
		APIKey:        os.Getenv("LEMONSQUEEZY_API_KEY"),
		WebhookSecret: os.Getenv("LEMONSQUEEZY_WEBHOOK_SECRET"),
		StoreID:       os.Getenv("LEMONSQUEEZY_STORE_ID"),
		VariantID:     os.Getenv("LEMONSQUEEZY_VARIANT_ID"),
	}),
)
```

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

### How it works

- **JSON:API, as required**

  Requests go as application/vnd.api+json, which Lemon Squeezy insists on — the package notes that plain JSON is refused with a confusing 415.

- **Signed, without a timestamp**

  X-Signature is a hex HMAC-SHA256 of the raw body, compared in constant time. With no timestamp in it, the idempotency claim is what stops a captured delivery applying twice.

- **Each event once**

  Lemon Squeezy sends no event id, so claims are keyed on the event name plus the object id. A retry is a duplicate; two different events about one order stay distinct.

- **Only paid settles**

  order_created with any status other than paid is acknowledged and changes nothing, so a sale still being collected, or refused, is not marked paid.

- **Refunds against the order**

  A refund posts to Lemon Squeezy’s refund endpoint for its order id, for the amount asked, and keeps the id it answers with on the refund row.

- **A failed update is retried**

  If marking the order paid fails, the claim is released and the webhook answers 500, so the retry finds work to do.

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

- **A placeholder is required** — Without StoreID and VariantID the module stays unconfigured, and a wrong variant surfaces as an error from Lemon Squeezy at the first checkout.
- **No currency is sent** — The checkout carries the total as custom_price in minor units and no currency code. Check that the Lemon Squeezy store charges in the currency your store settles in.
- **Tax is Lemon Squeezy’s** — Leave GoCommerce’s tax rates empty. The module does not check, and configuring both charges tax twice.
- **No failure path** — The module never marks a payment failed. A checkout that is abandoned or declined leaves the order unpaid.
- **Refunds follow the webhook** — A refund goes against the Lemon Squeezy order id that only the paid order_created webhook records.
- **Dashboard refunds are only logged** — order_refunded is logged, not applied. A refund made in Lemon Squeezy does not change the GoCommerce order.

FAQ

### Questions about the Lemon Squeezy module

**Why does it need a placeholder product?**

Lemon Squeezy sells items from its own catalogue, so a checkout must name a store and a variant that already exist there. The module overrides that variant’s price with the order total, so one placeholder serves every order. The package states it up front because the alternative is discovering it from an error at the first real checkout.

**Should I keep GoCommerce’s tax rates?**

No. Lemon Squeezy is the merchant of record and works out the tax on the sale itself. Leave GoCommerce’s rates empty so the total it sends is what the shopper pays.

**What is the webhook URL?**

POST /api/checkout/lemonsqueezy/webhook on the host that serves your GoCommerce API. order_created settles a paid order; order_refunded is logged; anything else is acknowledged.

**Is this a Lemon Squeezy partnership?**

No. The module calls Lemon Squeezy’s public API with keys you supply. Lemon Squeezy 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-lemonsqueezy`](https://github.com/itswadesh/gocommerce/tree/main/ext/payments-lemonsqueezy) in the GoCommerce repository, MIT licensed. When this page and the code disagree, the code is right and this page is out of date.Lemon Squeezy 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-lemonsqueezy on GitHub](https://github.com/itswadesh/gocommerce/tree/main/ext/payments-lemonsqueezy)
- [Lemon Squeezy](https://www.lemonsqueezy.com)
- [All GoCommerce modules](https://kitcommerce.store/integrations/)

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

[Deploy in minutes](https://kitcommerce.store/#one-command) · [Read the module](https://github.com/itswadesh/gocommerce/tree/main/ext/payments-lemonsqueezy) · [All integrations](https://kitcommerce.store/integrations/)
