Skip to main content
Get a complete subscription lifecycle running on Stellar testnet in under 5 minutes.
This guide uses the Vowena TypeScript SDK. You’ll need Node.js 18+ and a Stellar testnet account with USDC. You can get testnet USDC from the Stellar Laboratory.
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.
import { VowenaClient, NETWORKS } from "vowena";

const client = new VowenaClient({
  contractId: NETWORKS.testnet.contractId,
  rpcUrl: NETWORKS.testnet.rpcUrl,
  networkPassphrase: NETWORKS.testnet.networkPassphrase,
});
NETWORKS.testnet 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 transaction
const tx = await client.buildCreatePlan({
  merchant: "GMERCHANT...ADDR",        // Merchant's Stellar address
  token: NETWORKS.testnet.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 submit
const signedXdr = await signTransaction(tx); // Your wallet integration
const 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.
toStroops("9.99") converts a human-readable amount to the 7-decimal-place integer format used by Stellar tokens. It returns a BigInt:
toStroops("9.99")   // → 99900000n
toStroops("1.00")   // → 10000000n
toStroops("0.50")   // → 5000000n
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 transaction
const tx = await client.buildSubscribe(
  "GSUBSCRIBER...ADDR",  // Subscriber's Stellar address
  planId                  // Plan ID from step 3
);

// Subscriber signs and submits
const 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 transaction
const tx = await client.buildCharge(
  "GCALLER...ADDR",  // Address of whoever is calling (can be anyone)
  subscriptionId      // Subscription ID from step 4
);

// Sign and submit
const 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 transaction
const tx = await client.buildCancel(
  "GSUBSCRIBER...ADDR",  // Subscriber or merchant address
  subscriptionId          // Subscription ID
);

// Sign and submit
const 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.

Full Example

Here’s the complete flow in a single script:
import { VowenaClient, NETWORKS, toStroops } from "vowena";

const client = new VowenaClient({
  contractId: NETWORKS.testnet.contractId,
  rpcUrl: NETWORKS.testnet.rpcUrl,
  networkPassphrase: NETWORKS.testnet.networkPassphrase,
});

// 1. Merchant creates a plan
const createTx = await client.buildCreatePlan({
  merchant: "GMERCHANT...ADDR",
  token: NETWORKS.testnet.usdcAddress,
  amount: toStroops("9.99"),
  period: 2_592_000,
  priceCeiling: toStroops("14.99"),
  trialPeriods: 1,
  maxPeriods: 12,
  gracePeriod: 259_200,
});
const planResult = await client.submitTransaction(await sign(createTx));

// 2. Subscriber subscribes
const subTx = await client.buildSubscribe("GSUB...ADDR", planResult.planId);
const subResult = await client.submitTransaction(await sign(subTx));

// 3. After billing period elapses, anyone charges
const chargeTx = await client.buildCharge("GKEEPER...ADDR", subResult.subscriptionId);
await client.submitTransaction(await sign(chargeTx));

// 4. Subscriber cancels when they want
const cancelTx = await client.buildCancel("GSUB...ADDR", subResult.subscriptionId);
await client.submitTransaction(await sign(cancelTx));

Next Steps

Core Concepts

Understand plans, subscriptions, allowances, and the billing lifecycle in depth.

How It Works

Visual walkthrough of the full protocol flow - from plan creation to cancellation.

SDK Reference

Complete SDK documentation with all methods, types, and configuration options.

Run a Keeper

Set up an automated keeper bot to charge subscriptions on schedule.