Agent-readable docs index: /llms.txt. Full docs in one file: /llms-full.txt. Download /docs.zip to grep all markdown files locally.

How Mesub charges a wallet on a schedule

A customer approves a plan once. After that, Mesub pulls the plan's price from their wallet to yours at every period, and your server asks Mesub whether a given customer has access.

The flow

Customer's walletYour serverMesubSolanaSubscribing, oncecreate a subscriptionterms + transactionask to signtwo signaturessubmitsend, first period paidEvery period afterpull the pricewebhookOn each paid requesthas access?yes or no
Your pages never call Mesub and never hold a key. The wallet signs in the browser, and everything else goes through your server.

What runs where

WhereWhat it doesWhat it holds
Your pagesAsk the wallet for its two signaturesNothing secret
Your serverCreates subscriptions, checks access, receives webhooksThe API key
MesubSends each charge, retries a missed one, records attemptsNo funds
SolanaHolds the plan's terms and moves the tokensThe customer's delegation
Your customer has no Mesub account. Mesub finds them by the id you give them (external_id), by the wallet that pays, or by their email.

On chain

Mesub does not hold or route the money. Plans and subscriptions are accounts of the subscriptions program published by the Solana Foundation, open source (MIT) and audited by Cantina. Its address is De1egAFMkMWZSN5rYXRj9CAdheBamobVNubTsi9avR44.
Three accounts are involved, all PDAs of that program:
AccountOne perWhat it holds
Planplan, created by youThe price, the period, who receives
SubscriptionAuthoritywallet and token, the customer'sThe right to move that token
SubscriptionDelegationwallet and plan, the customer'sThe terms signed and the period state

The plan

Creating a plan in the dashboard sends one transaction, signed by your wallet, that creates the Plan account with its mint, amount, period_hours and end_ts. amount and period_hours are immutable. destinations lists the wallets that may receive the money, and pullers lists who may trigger a charge besides you: Mesub's puller is one of them, which is what lets Mesub charge on schedule without your signature. If you take it off the plan, charges are blocked until it is back, and your subscribers are not held to be late for it.
The token can be any SPL Token or Token-2022 mint. Creating the plan is refused for a token nobody could pay in, before you sign anything:
codeThe token
mint_pausedIs paused by its issuer
mint_frozen_by_defaultStarts every new account frozen
mint_non_transferableCan never be transferred
mint_transfer_hookRuns a program of its issuer's on every transfer
mint_confidential_onlyIs held in confidential balances only
mint_extension_unknownUses a Token-2022 extension Mesub does not support yet
mint_display_scaledShows in wallets scaled by an interest rate or a multiplier

An end date

A plan runs until you close it, unless you give it an end date when you create it. The end date is a moment, to the second, written on chain as end_ts in Unix seconds. At that moment access stops for everybody. The last charge is the one for the last period that starts before it.
RuleWhat it is
At the earliestOne billing period from now, plus five minutes
At the latest100 years from now
Once createdThe date cannot be removed or pushed later
A date too close is refused with end_date_too_soon, and one too far with end_date_too_far. A plan left unsigned until its date is too close is refused with end_date_stale: change the date, then sign.
For your subscribers, the end date is where everything stops:
  • Nobody has access past it, whatever period they paid for.
  • The last period is charged in full, even when the plan ends inside it.
  • Nothing is charged after it, and every subscription still running ends with plan_ended.
  • Nobody can subscribe from then on.
The terms a customer signs name the date and say the first two. See when the plan has an end date.

The subscription

Subscribing creates the customer's SubscriptionAuthority for that token if they have none, and a SubscriptionDelegation to your plan, which tracks amount_pulled_in_period, current_period_start_ts and expires_at_ts. The authority is approved as delegate on their token account; it can only move tokens when a delegation says so, and a delegation only allows this:
  • up to the plan's amount per period, counted in amount_pulled_in_period,
  • to one of the plan's destinations,
  • triggered by the plan's owner or one of its pullers,
  • while expires_at_ts is zero or in the future.
The delegation copies the plan's terms when it is created, so a customer keeps the price they signed for.
Both accounts cost rent, about 0.0036 SOL together. The customer pays it and gets it back when the subscription is closed. The customer's wallet is also the fee payer of that transaction, so it pays the network fee too. A wallet that holds less SOL than the rent, the fee and the small reserve Solana makes every wallet keep is refused with insufficient_sol, before anything is signed: the customer adds SOL and tries again.

A charge

Each period, Mesub's puller sends a transfer instruction. The program checks the four conditions above, moves the tokens from the customer's token account to your destination, and updates the delegation's period counters. If the wallet is short, nothing moves and Mesub retries according to the plan's retry policy.
Some Token-2022 tokens take a transfer fee for their issuer. The customer still pays the plan's amount, and your wallet receives it less that fee: amount_received, on the attempt and in the subscription.created and subscription.renewed events, says what arrived. PYUSD is such a token, with a fee of 0 today. When the issuer changes the fee, Mesub mails you once and keeps charging the same amount. When the issuer pauses the token or adds a transfer hook after the plan was made, Mesub holds the charges (blocked_reason mint_paused or mint_transfer_hook), mails you, and charges again once the issuer lifts the pause or removes the hook.

Cancelling

Cancelling sets expires_at_ts to the end of the period already paid: access runs until then, and no further charge can land. The customer signs it, through your site or directly against the program. Resuming before that date clears it. Closing the delegation afterwards returns its rent.

Why your server asks Mesub and not the chain

A delegation that reads active on chain is not proof that the next charge will land: the wallet may be empty, or the customer may have revoked the token approval outside the program. Mesub tracks every attempt and its outcome, and hasAccess answers from that record, not from the account alone.
Next: your first subscriber, with a plan, an API key and the code. The SDKs are @mesub/node on the server and, optionally, @mesub/react in your pages.