Skip to main content

General

Vowena runs on Stellar via Soroban, Stellar’s smart contract platform. Stellar offers ~$0.00001 transaction fees, 5-second finality, and native USDC support - making on-chain subscriptions economically viable for the first time.
Vowena supports any SEP-41 compliant token on Stellar. This includes USDC (the primary target), EURC, and any Stellar Asset Contract (SAC) token. Each plan specifies its billing token at creation time.
Stellar transactions cost approximately $0.00001 USD. This makes it practical to charge subscriptions as frequently as daily or even hourly without fees eating into the payment amount.
Vowena is a single Soroban smart contract deployed once on Stellar. All merchants share the same contract instance - plans and subscriptions are differentiated by on-chain data, not by separate deployments. This means one deployment serves the entire ecosystem.
The contract has an admin address set during initialization that could be used for upgrades via Soroban’s standard upgrade mechanism. The admin cannot access subscriber funds - the contract holds permission to pull funds, not the funds themselves.
The Vowena dashboard supports Freighter as the primary wallet. The SDK works with any Stellar wallet that supports Soroban transaction signing, including Freighter, xBull, Albedo, and WalletConnect-compatible wallets.

For Subscribers

Your subscription enters a grace period (defined by the merchant, typically 30 days). During this window, billing retries on each charge() call. If you fund your wallet during the grace period, the next charge succeeds and billing resumes normally. If the grace period expires, your subscription transitions to Paused. If it remains paused for another billing period, it becomes Cancelled.
Yes. You can cancel any subscription instantly by calling cancel() on the contract. This can be done through:
  • The Vowena dashboard (one-click cancel)
  • The Universal Subscription Manager
  • Any Soroban block explorer
  • The Stellar CLI directly
Cancellation is immediate and irreversible. The remaining token allowance is not consumed.
StatusMeaningCan recover?
PausedBilling failed and grace period expired.Yes - call reactivate() to re-approve allowance and resume.
CancelledTerminated by you, the merchant, or auto-cancelled after extended pause.No - cancellation is irreversible. You’d need to create a new subscription.
Your money is never held by the contract. The contract holds a permission to pull funds, not the funds themselves. Four layers protect you:
1

Direct on-chain cancellation

The cancel() function is public. Call it from any block explorer or Soroban transaction builder. No frontend needed.
2

Universal Subscription Manager

A standalone page in the Vowena dashboard where any wallet holder can see and cancel all their Vowena subscriptions, regardless of which merchant created them.
3

Auto-expiry

If a plan has max_periods, the subscription expires automatically when that count is reached.
4

Allowance expiration

The token allowance itself has an expiration ledger on Soroban. Even if you do nothing, the allowance eventually expires and the contract can no longer pull funds.
Plans can include a trial_periods count. During trial periods, the charge() function advances the period counter but does not transfer any tokens. When the trial ends, normal billing begins. If you cancel during the trial, you are never charged.
When a merchant wants to change pricing:
  1. They create a new plan with the new price
  2. They call request_migration() - your subscription gets flagged as “migration pending”
  3. You see the migration in the dashboard with old vs new price compared
  4. You accept (signs new allowance at new price) or reject (stay on current plan at current price)
You are never silently moved to a higher price. Period.

For Merchants

The price ceiling is the maximum amount you can adjust a plan’s billing to without requiring subscriber re-authorization. For example, if you create a plan at 9.99/monthwitha9.99/month with a 14.99 ceiling, you can raise the price up to $14.99 using update_plan_amount() - subscribers continue billing at the new amount without needing to re-approve. Beyond the ceiling, you must create a new plan and migrate subscribers.
Set the ceiling thoughtfully. Too low limits your flexibility. Too high may deter subscribers who see the maximum possible charge in their wallet approval.
Yes. Call refund(sub_id, amount) to transfer tokens from your wallet back to the subscriber. The merchant authorizes the transfer (it’s your own funds). Partial refunds are supported - specify any amount. The refund is recorded on-chain as a RefundIssued event, creating a verifiable receipt.
A keeper is any script, bot, or person that calls the charge() function for due subscriptions. The function is permissionless - anyone can call it, but only the merchant receives the payment. Options:
  • Dashboard keeper: Built-in auto-billing in the Vowena dashboard
  • Standalone bot: Run the keeper from sdk/keeper/
  • Your own: Build custom billing logic using the SDK
  • Third-party: Anyone can run a keeper service
The keeper pays the transaction fee (~$0.00001) but the subscription payment goes entirely to the merchant.
Yes. The standalone keeper bot lives at sdk/keeper/src/index.ts. Configure it with your contract ID and a funded Stellar keypair, then run it on a schedule. See the Keeper Setup guide for details.
It doesn’t change anything about your billing. The Universal Subscription Manager simply gives subscribers a way to view and manage their subscriptions independently of your app. Since cancel() is a public contract function anyway, this just provides a better UX for it. Transparent cancellation builds subscriber trust.

Technical

This is a critical design decision. In Soroban, if transfer_from() panics (insufficient balance or allowance), the entire transaction reverts - including any state changes the contract made before the call. This means the contract couldn’t record failed_at or emit a ChargeFailed event.By checking token.balance() and token.allowance() before calling transfer_from(), the contract can:
  • Record the failure timestamp (failed_at)
  • Emit a ChargeFailed event
  • Return false gracefully
  • Trigger the grace period state machine
Without the pre-check, a failed charge would be invisible to the system.
When a subscriber calls subscribe(), the contract internally calls token.approve(). In Soroban’s authorization model, sub-contract calls that require the same caller’s auth are bundled into an auth tree. The subscriber’s wallet sees both authorizations (the contract call and the token approval) and signs them as a single transaction. One signature, two authorizations.
The contract uses:
  • Instance storage (shared, limited): admin address, plan/subscription ID counters
  • Persistent storage (per-entry, restorable): each plan, each subscription, and index vectors (merchant→plans, subscriber→subs, plan→subs)
Each plan and subscription is its own persistent storage entry, which works well with Soroban’s parallelism model. TTLs are extended to ~120 days on write, with a ~30 day renewal threshold.
Soroban’s restore mechanism allows archived entries to be brought back. Subscription and plan data is never permanently lost - it can always be restored by paying the restoration fee. The extend_ttl() function exists to proactively prevent archival.
The compiled WASM is 18.5 KB with 17 exported functions and 29 passing tests. It’s a single, self-contained contract with no external dependencies beyond the SEP-41 token interface.