Flowgate Recurring

Crypto subscriptions on your site

The customer approves a spending cap once. Later charges run on their own, and Flowgate’s operator pays the gas. Your site never holds a private key.

Overview

A merchant has a Recurring POS. It holds a collection of plans: title, fiat price, interval, and cycle count. The POS has a storefront id. Pass that id to the widget as id.

Embed

A button on your site opens the storefront. The same script can also place the storefront inline on the page.

Redirect

Your site sends the person to a link. No script is required. The link already names the plan and the user.

EnvironmentScriptStorefront
Production https://recurring.flowgate.tech/widget.js https://recurring.flowgate.tech/s/{id}
Staging https://staging.recurring.flowgate.tech/widget.js https://staging.recurring.flowgate.tech/s/{id}

Customer flow

This is a live merchant page. Each plan button opens the widget with that planId already selected. The customer does not pick the plan again.

Membership page with two plans and Join buttons
The merchant site. Each button is bound to one plan and to the current user’s reference.
Storefront popup over the merchant site
Embed as a popup. The close button, the backdrop, and Escape dismiss it. The site stays underneath.
Plan summary: 10 EUR every month
1. Summary of the selected plan.
Name and email form
2. Name and email. The manage link is emailed there later.
Choice of USDC or USDT
3. Token. The list is the networks that have a pull contract.
Choice of Sepolia or Amoy
4. Network. Next, the wallet opens and asks for approve.
  1. The customer approves a spending cap: one approve for an amount with room for the exchange rate, not a transfer.
  2. The storefront shows the first charge in token units at the current rate, and a Charge now button.
  3. Later charges run on the timer. The token amount can differ because the rate moves.
  4. Opening the flow again with the same reference returns this subscription, not a new form.

How charges work

The customer lets the contract pull tokens up to the cap. The operator calls pull and pays the gas. The transfer goes to the Flowgate invoice address. If the allowance or the balance is short, the transaction is not sent.

CheckWhat happens
Not enough tokens in the walletThe charge is not sent on-chain. Retries continue.
Allowance too lowAutomatic retries stop. The customer raises the spending cap.
First chargeA quote is created first, then Charge now. The subscription is not active until that payment lands.
CancelIf the period is already paid, access lasts until the next charge date. No further charges are pulled.

Embed

Put the script on your page. mount binds the click on a button. open opens the window from your own code.

<script src="https://recurring.flowgate.tech/widget.js"></script>
<button id="subscribe">Subscribe</button>
<script>
  FlowgateRecurring.mount("#subscribe", {
    id: "YOUR_STOREFRONT_ID",
    planId: "YOUR_PLAN_ID",
    reference: "YOUR_USER_ID"
  });
</script>

Popup or inline

mode defaults to popup. embed replaces the element with an iframe. height sets the minimum height, 720px by default.

FlowgateRecurring.mount("#subscribe", {
  id: "YOUR_STOREFRONT_ID",
  planId: "YOUR_PLAN_ID",
  reference: "YOUR_USER_ID",
  mode: "embed",
  height: "720px"
});
ParameterMeaning
id or slugPOS storefront id. Required when there is no offerId.
offerIdAn existing subscription, the /subscribe?id= page.
planIdA collection plan. Skips the plan picker.
referenceYour id for the person. One person, one value.
labelButton text, only when mount creates the button itself.

Redirect

Your server or frontend builds the link for the signed-in user and sends them there. In the cabinet, the Installation tab’s Redirect mode shows the finished URL for the selected plan card.

https://recurring.flowgate.tech/s/YOUR_STOREFRONT_ID?plan_id=YOUR_PLAN_ID&reference=YOUR_USER_ID

Without a real plan_id the storefront refuses checkout and says the link needs a plan id from the collection. Without reference there is no way to know whose subscription it is when the person comes back.

reference binds the person

reference=YOUR_USER_ID is the unique id of a person in your system. Use one value per user. The same value opens that subscription again. A different value starts a new one.

An open subscription on a storefront is found by the pair POS + reference. Open statuses are: waiting for approve, waiting for the first charge, confirming on-chain, active, and past due.

If the person opens a link for another plan with the same reference, the storefront does not create a second subscription and does not silently keep the old plan. It shows the change quote and asks them to confirm. Switching requires the manage link from the email. Without it, the plan stays as it is.

Plans

A plan is set in the cabinet: title, fiat amount, interval, and cycle count. Zero cycles means “until cancelled”. The price and interval are copied onto the subscription when it is created. Editing the plan card later does not rewrite subscriptions that already exist.

IntervalSeconds
10 minutes, demo only600
Hourly3600
Daily86400
Weekly604800
Monthly, 30 days2592000
Yearly, 365 days31536000

Changing plans

Access to the new plan starts when the customer asks for it. The date the current period is paid through does not move. On that date the full new price is charged, and the new interval starts from there.

Days already paid are repriced. They are not discarded. Example: 18 of 30 days remain, the plan was 10 EUR and becomes 15 EUR. The old plan still covers 6.00 EUR, the new plan for those days is 9.00 EUR, so the top-up due now is 3.00 EUR. Amounts in the history are rounded to two decimal places.

If the new plan is cheaper, nothing is pulled now and nothing is refunded on-chain. The difference becomes credit and reduces the next invoice.

If the current period has not been paid, the full new price is what gets charged now. The old invoice is not retried.

Balance and allowance are both checked before the switch. If the wallet cannot cover the amount due now, the plan and the status stay as they are. The refusal is written to the history with the same calculation. A downgrade that pulls nothing is not blocked by a low balance, but the allowance must still cover the new plan’s full future charge.

History has two lists. Plans is which plan was in effect and from when. A new row is added only after a successful switch. Activity is the request, the refusal, the successful change with the calculation, plus a wallet change, a higher spending cap, and a cancellation.

Granting access

A message from the browser is only a signal for the interface. Turn access on and off from the webhook to your server, or from a status request. postMessage is not signed.

window.addEventListener("message", (event) => {
  const frame = document.querySelector("#subscribe iframe");
  if (frame && event.source !== frame.contentWindow) return;
  const data = event.data;
  if (!data || data.source !== "flowgate-recurring") return;
  if (data.event === "subscription.active") {
    // refresh the screen. the server grants access.
  }
});
Browser eventWhen
subscription.createdThe subscription exists. The first charge is not confirmed yet.
subscription.confirmingThe first charge was sent on-chain.
subscription.activeThe first charge succeeded.
subscription.failedThe charge failed.

Webhook

Set a public HTTPS URL on the POS. The response includes a secret. Each event is a JSON POST with the header X-Flowgate-Signature: sha256=<hex>. The signature is HMAC-SHA256 of the raw body using that secret. The same id on a retry is not a second event: store the ids you have already handled.

eventWhen
subscription.activeA charge succeeded and the subscription is active.
subscription.past_dueA charge failed.
subscription.cancelledThe subscription was cancelled.
subscription.plan_changedThe plan changed. The body includes the new plan_id. Switch access immediately.
charge.paidThat charge was paid. The body includes charge_id.
charge.failedThat charge failed. If the allowance is short, error_code is allowance_low.

Status by reference

GET https://recurring.api.flowgate.tech/v1/admin/subscriptions/by-reference?reference=YOUR_USER_ID (staging: https://staging.recurring.api.flowgate.tech, same path). Send the POS API key as the X-Api-Key header. This is not the storefront URL. In the cabinet, open this POS and the API key / Webhook tab: the key, the URL, and an example response are there. The response is { "success": true, "data": { "status": "active", "reference": "YOUR_USER_ID", ... } }. Grant access when status is active. The card includes closed subscriptions, without the email and without the manage token. It is the latest subscription for that reference. An unknown reference is 404.

Customer manage page

The link in the email works for 24 hours. Opening it does not extend that window. From the page the customer sees charges, raises the spending cap, changes the wallet, and cancels. A new link can be requested from the subscription screen. The email goes to the address used at signup, and not more than once a minute.

Raising the cap requires an approve from the same wallet that pays. The new cap cannot be lower than the current one. Changing the wallet is a new approve for the current cap from the new address. Older charges stay in the history.

Merchant cabinet

A recurring POS has these tabs: Installation, Plans, Subscribers, Payments, API key / Webhook, Settings. When there are no subscribers yet, Installation opens first. When there are subscribers, Subscribers stays selected, and a later refresh does not leave a tab the merchant already clicked.

  • Plans — plan cards. Add plan sits at the top. Clicking a card opens the editor. Saving writes the whole POS.
  • Installation — the Embed and Redirect switch, the highlighted reference, and a ready snippet or link.
  • Subscribers — the person card: wallet, schedule, Plans and Activity, and the last charge error.
  • API key / Webhook — the POS API key (X-Api-Key), the webhook URL, and the signing secret.