What is a Subscription?
A subscription is the on-chain record linking a subscriber to a plan. It tracks billing state, lifecycle status, and timing. Each subscription is its own persistent ledger entry, enabling parallel processing across the network.State Machine
Every subscription moves through a defined set of states. Transitions are enforced by the contract - there is no way to skip a state or force an invalid transition.Paused is a recoverable state. If a subscriber tops up their wallet and
calls
reactivate(), the subscription returns to Active and billing resumes.
But if another full billing period passes while paused, the next charge()
call will transition it to Cancelled permanently.There is no transition from Cancelled or Expired back to any other
state. These are terminal. The subscriber must create a new subscription if
they want to resume.
Creating a Subscription
When a subscriber callssubscribe(), several things happen in a single atomic transaction:
1
Subscriber authorization
The subscriber signs the transaction. Soroban’s auth tree means the subscriber signs once - this single signature covers both the
subscribe() contract call and the token.approve() call nested inside it. No separate approval transaction needed.2
Plan validation
The contract loads the plan and verifies:
- The plan exists
- The plan is
active == true - The subscriber is not the merchant (cannot subscribe to your own plan)
3
Allowance calculation
The contract calculates how much token allowance to request:Where
effective_periods is:max_periodsif the plan has a limit, or120if the plan is unlimited (max_periods == 0)
4
Token approval
The contract calls
token.approve(subscriber, contract_address, allowance, expiry_ledger). This grants the Vowena contract permission to pull up to allowance tokens from the subscriber over the approval period.5
Subscription stored
The contract increments
NextSubId, creates the Subscription struct with status: Active, and writes it to persistent storage. The subscription ID is appended to both SubscriberSubs(subscriber) and PlanSubs(plan_id) indexes.6
Event emitted
A
SubscriptionCreated event fires with the full subscription struct.Allowance Calculation Examples
- 12-month plan
- Unlimited plan
- Plan with trial
Plan: 10 USDC/month, ceiling 15 USDC, max 12 periodsThe subscriber’s wallet will show: “Approve 180 USDC for Vowena contract”.
Cancellation
Cancellation is always available to the subscriber and is immediate:Who can cancel?
Who can cancel?
Only the subscriber can cancel their own subscription. The contract calls
subscriber.require_auth(). Merchants cannot cancel on behalf of
subscribers.What happens to the allowance?
What happens to the allowance?
The remaining token allowance stays until it expires naturally on the
ledger. The contract will never use it after cancellation because the
subscription status is
Cancelled and charge() checks status before
attempting any transfer.Is it refundable?
Is it refundable?
Cancellation does not trigger an automatic refund. The current period is
already paid. Merchants can issue voluntary refunds
separately.
Reactivation
If a subscription is Paused (charge failed, grace expired), the subscriber can reactivate it:1
Top up wallet
The subscriber ensures they have sufficient token balance and that the
contract’s allowance has not expired.
2
Call reactivate
The subscriber calls
reactivate(sub_id). The contract verifies the
subscription is in Paused status and transitions it back to Active.3
Billing resumes
The
next_billing_time is reset so that the next charge() call can
process immediately.What’s Next
Billing
The most important page - understand exactly what happens during a charge.
Migrations
How merchants move subscribers to new plans with explicit consent.