Selling through a provider
Sell products and plans through any provider's webhooks, and app-store purchases.
A product's "How it's sold" can name a relay instead of a Stripe Payment Link:
GoCardless, Lemon Squeezy, Paddle, bank transfer, or any provider whose webhooks follow a
supported signing standard and whose events you can map. fulfil_product sells it and refund_product takes it back.
This example sells a product through any provider, with no server of your own:
{
"name": "gocardless-paid",
"trigger": {
"kind": "receiver",
"verify": { "scheme": "signature", "header": "Webhook-Signature" },
"map": {
"event_id": "events.0.id",
"event_type": "events.0.action",
"person_email": "events.0.details.customer_email",
"payment_id": "events.0.links.payment"
},
"when": { "event_type": "confirmed" }
},
"actions": [
{ "type": "fulfil_product", "product": "Starter pack", "ref": "{{mapped.payment_id}}" }
]
}
Mapped fields are available in
templates as {{mapped.<name>}}, and {{event.<path>}}
reads the raw payload.
For refunds, add a second relay with when set to
{ "event_type": "refunded" } and the action refund_product.
g.payments.buy on a product sold this way answers 409
product_not_sellable, because there is no Payment Link to open.
Subscription plans
Plans are sold the same three ways. A plan's "How it's sold" can name a relay, and
two actions run its lifecycle: grant_plan starts or renews it and
revoke_plan ends it. A relay-sold plan is active until revoked, with no lapse
timer, so the revoke goes on the provider's end-of-access event, on its own relay: the
subscription expired, its period ended, or the payment was refunded. A cancellation that
only stops the next renewal is not that event; the person keeps the period they paid
for.
g.subscriptions.checkout on a plan sold this way answers 409
plan_not_sellable.
Set the plan's "How it's sold" on the Payments page to the relay that grants it. The
Webhooks page offers the pair as starter text under A payment provider (shared
token), for a sender that posts a token in a header rather than a signature (a
bank-transfer bridge of your own, for one). Both templates set
trustUnsignedEvents, with the warning in their note: saving one is your choice
to accept it (see Unsigned events):
- Payment → grant the plan: a
shared_tokenreceiver mapping the event's id, email and payment reference, andgrant_planwithrefset to that reference, so a resent event grants once. - Access ended → revoke the plan: its own webhook and secret, a
whenthat you fill in with the provider's event for the end of access (expiry, end of period or a refund), andrevoke_plan.
Change the plan name in the text to the plan's exact name. A shared token is the whole of the check on these requests. Where the provider signs its webhooks, use its signature recipe instead, and the opt-in isn't needed.
App stores
An App Store or Play purchase reaches Gemmein as a RevenueCat webhook into a receiver
whose map names person_token. The app gives RevenueCat the person's
storeAccountToken as its app user id, so neither the person id nor the
address enters a third party's ledger. The Webhooks page offers three templates:
revenuecat-purchase → fulfil_product on the three purchase
events (its when holds them as a set, { "in": [...] });
revenuecat-expiration → revoke_access on
EXPIRATION, when access ends; and revenuecat-refund →
refund_product on a refund, a CANCELLATION with
cancel_reason CUSTOMER_SUPPORT. The purchase and the refund
both use RevenueCat's transaction_id as the ref, so a refund
undoes that one purchase: its access and its receipt, and it takes back its credits,
first from that purchase's own grant, then from other credits the person holds, never
below zero. Access held through another purchase stays. A cancellation keeps the access until the period ends,
because RevenueCat sends EXPIRATION then. RevenueCat sends a shared token, not a
signature, so each template sets trustUnsignedEvents (see
Unsigned events). Mobile has the full
setup.
Thanks for telling us.
Thanks for telling us.
Last updated