# Adyen Payments for GoCommerce — Open-Source Module

> Take card and local payments through Adyen payment links in a GoCommerce store: API key, HMAC key, endpoint, refunds, and what the module leaves out.

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

---

Payments

## Adyen payments for GoCommerce

A Go package that sends the shopper to an Adyen payment link and marks the GoCommerce order paid when Adyen’s HMAC-signed AUTHORISATION notification arrives — not before. Four required settings, one of which, the endpoint, deliberately has no default.

- **Payments** module
- **6** settings
- **8** tests
- **ext/payments-adyen**

### What it does

The module registers “adyen” as a payment method. At checkout it creates an Adyen payment link for the order’s total, in minor units, with the order number as Adyen’s merchant reference, and answers with a redirect intent carrying the link’s URL.

Adyen then calls POST /api/checkout/adyen/webhook with a batch of notifications. Every item’s signature is checked before any of them is acted on; a successful AUTHORISATION marks the order paid and a refused one marks the payment failed. The package is plain about why it waits: Adyen answers a payment or a refund with “received” and says what happened in a later notification, so notifications are the source of truth and no API response is treated as settlement.

In the admin it describes itself as “Card and local payments through Adyen’s Checkout API, with the HMAC-signed notification marking orders paid.” It chooses payment links over Drop-in on purpose: Drop-in is a better checkout, the package says, but it needs Adyen’s JavaScript, a /sessions call and a client key on the storefront, and a module cannot dictate the storefront.

### Configuration

Four settings are required: the API key, the merchant account, the webhook’s HMAC key and the Checkout API endpoint. Set them in Config from your own main(), or leave Config empty and fill them in under Settings › Payment methods.

*Adyen module settings — 6 settings, 4 required*

| Setting | Environment variable | Required | What it does |
| --- | --- | --- | --- |
| `APIKey` API key | `ADYEN_API_KEY` | Yes | An API credential’s key, sent as the X-API-Key header. Not sent to the storefront. |
| `MerchantAccount` Merchant account | `ADYEN_MERCHANT_ACCOUNT` | Yes | The account every payment link and refund is created against. |
| `HMACKey` Webhook HMAC key | `ADYEN_HMAC_KEY` | Yes | The hex key generated for the webhook in Adyen’s Customer Area. Required because, as the code puts it, without it any caller could mark orders paid. |
| `BaseURL` Checkout API base URL | — | Yes | The endpoint including its version segment: https://checkout-test.adyen.com/v71 for test, your own merchant-prefixed host for live. No default. |
| `ReturnURL` Return URL | — | No | Where the shopper lands after paying, when the checkout request did not send a return_url of its own. |
| `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 up the webhook** — In Adyen’s Customer Area, point a webhook at https://your-api-host/api/checkout/adyen/webhook and generate its HMAC key.
2. **Choose the endpoint** — Test is https://checkout-test.adyen.com/v71. Live is your own prefixed host, https://{prefix}-checkout-live.adyenpayments.com/checkout/v71 — the module will not guess it.
3. **Install and configure** — Pass adyen.New to gocommerce.New as below, or run the reference binary with -gateways and fill in all four settings under Settings › Payment methods.
4. **Take a payment** — POST /api/checkout/adyen answers with a redirect intent whose url is the payment link. The AUTHORISATION notification marks the order paid.

main.go

```
import (
	"os"

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

app, err := gocommerce.New(cfg,
	adyen.New(adyen.Config{
		APIKey:          os.Getenv("ADYEN_API_KEY"),
		MerchantAccount: os.Getenv("ADYEN_MERCHANT_ACCOUNT"),
		HMACKey:         os.Getenv("ADYEN_HMAC_KEY"),
		BaseURL:         "https://checkout-test.adyen.com/v71",
	}),
)
```

The package doc’s own example, with its imports; cfg is your gocommerce.Config. The test endpoint is written in because Adyen’s live URL carries your own prefix. Import path `github.com/itswadesh/gocommerce/ext/payments-adyen`.

### How it works

- **Payment links, not Drop-in**

  Checkout creates a link with POST /paymentLinks: the amount in minor units, the order number as reference, and the shopper’s email and two-letter country when the order has them. The storefront only redirects.

- **Every item verified first**

  Adyen signs eight fields of each notification item — PSP reference, original reference, merchant account, merchant reference, amount, currency, event code and success — with the HMAC in additionalData. One forged item rejects the whole batch.

- **Only signed fields pick the order**

  The order is found from merchantReference, which the HMAC covers, not from the metadata Adyen echoes back, which it does not. A valid signature for one payment cannot settle somebody else’s order.

- **Each event once**

  Claims are keyed on event code plus PSP reference in the module’s own table, so a retry is a duplicate and two different events about one payment stay distinct. The handler answers \[accepted\], which is what stops Adyen retrying.

- **A failed update is retried**

  If the engine cannot apply a notification, the claim is released and the webhook answers 500, so Adyen’s retry finds work to do.

- **Refunds by PSP reference**

  A refund posts to Adyen against the PSP reference the AUTHORISATION recorded, for the amount asked, and keeps the reference Adyen returns. A later REFUND notification that failed is logged as an error for an operator to reconcile.

### 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 default endpoint** — Until BaseURL is set, the module does not count as configured. The package chose that over a test default that would turn a misconfigured live store into one taking pretend money.
- **Redirect only** — No Drop-in or Components. A store that wants Adyen’s in-page checkout builds it on its storefront; this module only creates payment links.
- **Paid on authorisation** — A successful AUTHORISATION marks the order paid. The module sends no capture and reads no CAPTURE event; if your account captures manually, that happens in Adyen.
- **Refunds wait for the notification** — The refund is issued against the PSP reference the AUTHORISATION notification wrote. Before that arrives, the order has nothing to refund against.
- **Chargebacks are only logged** — CHARGEBACK, NOTIFICATION_OF_CHARGEBACK and SECOND_CHARGEBACK are logged as warnings. The engine has no chargeback state, so the order does not change.
- **Failed refunds are not undone** — A REFUND notification with success=false, after the engine recorded the refund, is logged and nothing more. The two sets of books need a person to reconcile them.

FAQ

### Questions about the Adyen module

**Why does the module have no default endpoint?**

Adyen’s live Checkout endpoint is prefixed with your own merchant identifier, so there is no live URL the package could know. It chose no default over a test default: a store that has not said which environment it means does not come up taking pretend money.

**What is the webhook URL?**

POST /api/checkout/adyen/webhook on the host that serves your GoCommerce API. The module answers \[accepted\] once every item in a batch is verified and applied.

**Can the shopper pay without leaving the storefront?**

Not through this module. It creates payment links and the storefront redirects to them. Adyen’s Drop-in needs its JavaScript, a /sessions call and a client key on the storefront, which the package leaves for a store to build.

**Can I use it without writing Go?**

Yes. The reference binary’s -gateways flag installs every payment module with an empty Config. Switch Adyen on under Settings › Payment methods and fill in the four settings, the endpoint included.

**Is this an Adyen partnership?**

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