Payments

Paddle payments for GoCommerce

A Go package that creates a Paddle Billing transaction at checkout, sends the shopper to Paddle’s checkout, and marks the GoCommerce order paid when a signed transaction.completed notification arrives. Paddle is the merchant of record, and that changes how you set up tax.

What it does

The module registers “paddle” as a payment method. At checkout it creates a Paddle transaction with one ad-hoc line — “Order” and the order number, at the order’s total — and answers with a redirect intent carrying the transaction’s checkout URL.

Paddle then calls POST /api/checkout/paddle/webhook. The module verifies the Paddle-Signature header; transaction.completed asks the engine to mark the order paid, and transaction.payment_failed to mark the payment failed. transaction.paid is deliberately not enough: the package notes that it fires first and can still be reversed.

The package’s central point is that Paddle sells to the shopper, not the store, and decides the tax on the sale itself. A store on Paddle should leave GoCommerce’s tax rates empty and let the total it sends be the price the shopper pays; configuring both charges tax twice. That is documented rather than enforced, because the engine cannot tell which of the two a store intends.

Configuration

Two settings are required: the API key and the notification destination’s signing secret. Set them in Config from your own main(), or leave Config empty and fill them in under Settings › Payment methods.

Paddle module settings — 6 settings, 2 required
SettingEnvironment variableRequiredWhat it does
APIKey
API key
PADDLE_API_KEY Yes Sent as a bearer token. It needs transaction.write to create a transaction and adjustment.write to refund one.
WebhookSecret
Notification signing secret
PADDLE_NOTIFICATION_SECRET Yes Verifies the Paddle-Signature header. Required because, as the code puts it, without it any caller could mark orders paid.
Sandbox
Sandbox
— No Points the module at sandbox-api.paddle.com. Paddle issues separate keys for the two, so this is a deliberate flag rather than something read from the key.
BaseURL
API base URL
— No Overrides both hosts, for tests.
WebhookTolerance — No How old a signed notification may be. Five minutes by default; Paddle’s own SDK helpers default to five seconds. Go only.
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. Add a notification destinationIn Paddle, send notifications to https://your-api-host/api/checkout/paddle/webhook — transaction.completed and transaction.payment_failed at least — and keep its secret.
  2. Empty the tax ratesPaddle works out the tax. Leave GoCommerce’s tax rates empty so the order total is the price the shopper pays.
  3. Install and configurePass paddle.New to gocommerce.New as below, or run the reference binary with -gateways and fill in Settings › Payment methods. Tick Sandbox while testing.
  4. Take a paymentPOST /api/checkout/paddle answers with a redirect intent whose url is Paddle’s checkout. transaction.completed marks the order paid.

main.go

import (
	"os"

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

app, err := gocommerce.New(cfg,
	paddle.New(paddle.Config{
		APIKey:        os.Getenv("PADDLE_API_KEY"),
		WebhookSecret: os.Getenv("PADDLE_NOTIFICATION_SECRET"),
		Sandbox:       true,
	}),
)

The package doc’s own example, with its imports; cfg is your gocommerce.Config. It points at the sandbox; drop Sandbox for live keys. Import path github.com/itswadesh/gocommerce/ext/payments-paddle.

How it works

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.

FAQ

Questions about the Paddle module

Why should GoCommerce’s tax rates be empty?

Because Paddle is the merchant of record: it sells to the shopper and adds the tax it decides on. If GoCommerce has already added tax to the total, the shopper pays it twice. The package documents this rather than zeroing tax itself, because it cannot know what a store intends.

How do I test against Paddle’s sandbox?

Switch on Sandbox and use sandbox keys. Paddle issues separate keys for sandbox and live, so the module takes an explicit flag instead of guessing from the key.

Why does it wait for transaction.completed?

The package explains that transaction.paid fires first and can still be reversed, so settling on it would mark orders paid that Paddle has not finished collecting. transaction.completed is the event that means the money arrived.

Can I use it without writing Go?

Yes. The reference binary’s -gateways flag installs every payment module with an empty Config. Switch Paddle on under Settings › Payment methods and fill in the key, the secret and the sandbox flag there.

Is this a Paddle partnership?

No. The module calls Paddle’s public API with keys you supply. Paddle 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-paddle in the GoCommerce repository, MIT licensed. When this page and the code disagree, the code is right and this page is out of date.Paddle 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.

Chat on WhatsApp