Go from zero to a working on-chain subscription in 5 minutes. Install the Vowena SDK, create a billing plan, subscribe a wallet, charge, and cancel.
Get a complete subscription lifecycle running on Stellar in under 5 minutes.
This guide uses the Vowena TypeScript SDK. You’ll need Node.js 18+ and a Stellar account with USDC.
1
Install the SDK
Add the Vowena SDK to your project.
npm install vowena
The SDK is a lightweight wrapper around the Vowena Soroban contract. It builds transactions locally - you sign and submit them with your own wallet or keypair.
2
Create a Client
Initialize the VowenaClient with your network configuration.
Mainnet
Testnet
Custom RPC
import { VowenaClient, NETWORKS } from "vowena";const client = new VowenaClient({ contractId: NETWORKS.mainnet.contractId, rpcUrl: NETWORKS.mainnet.rpcUrl, networkPassphrase: NETWORKS.mainnet.networkPassphrase,});
import { VowenaClient, NETWORKS } from "vowena";const client = new VowenaClient({ contractId: NETWORKS.testnet.contractId, rpcUrl: NETWORKS.testnet.rpcUrl, networkPassphrase: NETWORKS.testnet.networkPassphrase,});
import { VowenaClient } from "vowena";const client = new VowenaClient({ contractId: "CABC...XYZ", rpcUrl: "https://your-rpc.example.com", networkPassphrase: "Test SDF Network ; September 2015",});
NETWORKS.mainnet provides pre-configured values for contractId, rpcUrl, networkPassphrase, and usdcAddress so you don’t need to look them up.
3
Create a Plan (Merchant)
Define a subscription plan. This is an on-chain transaction that stores the plan parameters permanently.
import { toStroops, NETWORKS } from "vowena";// Build the create_plan transactionconst tx = await client.buildCreatePlan({ merchant: "GMERCHANT...ADDR", // Merchant's Stellar address token: NETWORKS.mainnet.usdcAddress, // USDC token contract amount: toStroops("9.99"), // 9.99 USDC per period → 99900000n period: 2_592_000, // 30 days in seconds priceCeiling: toStroops("14.99"), // Max price before re-auth needed trialPeriods: 1, // 1 free billing period maxPeriods: 0, // 0 = unlimited gracePeriod: 259_200, // 3 days grace on failed charges});// Sign with your wallet and submitconst signedXdr = await signTransaction(tx); // Your wallet integrationconst result = await client.submitTransaction(signedXdr);console.log("Plan created! ID:", result.planId);
Plans are immutable once created. If you need to change pricing, create a new plan and use the migration flow to move existing subscribers.
What does toStroops do?
toStroops("9.99") converts a human-readable amount to the 7-decimal-place integer format used by Stellar tokens. It returns a BigInt:
USDC on Stellar uses 7 decimal places (stroops), matching the native XLM precision.
4
Subscribe (Subscriber)
A subscriber authorizes the plan. This single transaction calls subscribe() on the Vowena contract and sets the SEP-41 token allowance - one signature covers both.
// Build the subscribe transactionconst tx = await client.buildSubscribe( "GSUBSCRIBER...ADDR", // Subscriber's Stellar address planId // Plan ID from step 3);// Subscriber signs and submitsconst signedXdr = await signTransaction(tx);const result = await client.submitTransaction(signedXdr);console.log("Subscribed! Subscription ID:", result.subscriptionId);
The subscriber’s wallet will display exactly what is being approved: the token (USDC), the spending ceiling (price_ceiling from the plan), and the contract authorized to pull funds. Full transparency - no hidden permissions.
5
Charge (Keeper / Anyone)
Once a billing period has elapsed, anyone can trigger the charge. The contract validates all conditions on-chain.
// Build the charge transactionconst tx = await client.buildCharge( "GCALLER...ADDR", // Address of whoever is calling (can be anyone) subscriptionId // Subscription ID from step 4);// Sign and submitconst signedXdr = await signTransaction(tx);const result = await client.submitTransaction(signedXdr);console.log("Charged! Period:", result.periodNumber);
The contract checks:
Is the subscription active?
Has enough time passed since the last charge?
Is the trial period over?
Does the subscriber have sufficient balance and allowance?
Has the max period limit been reached?
If all checks pass, USDC is transferred from the subscriber directly to the merchant via transfer_from().
You don’t need to run your own keeper. The Vowena Dashboard includes a built-in keeper service, or you can use third-party keeper networks.
6
Cancel
Either the subscriber or the merchant can cancel a subscription at any time.
// Build the cancel transactionconst tx = await client.buildCancel( "GSUBSCRIBER...ADDR", // Subscriber or merchant address subscriptionId // Subscription ID);// Sign and submitconst signedXdr = await signTransaction(tx);const result = await client.submitTransaction(signedXdr);console.log("Cancelled! Subscription:", subscriptionId);
Cancellation is immediate and on-chain. No approval from the merchant is needed. The subscriber can also cancel directly through any Stellar wallet or block explorer that supports Soroban contract invocations - the Vowena frontend is not required.