| title | Quickstart |
|---|---|
| description | Go from zero to a working on-chain subscription in 5 minutes. Install the Vowena SDK, create a project and plan, subscribe a wallet, charge, and cancel. |
Get a complete subscription lifecycle running on Stellar testnet in under 5 minutes.
You'll need Node.js 20+, a funded Stellar testnet account, and a USDC trustline. The [dashboard checkout page](https://dashboard.vowena.xyz) can fund a fresh wallet via friendbot if you don't have one. ```bash npm npm install @vowena/sdk ``` ```bash pnpm
pnpm add @vowena/sdk
```
```bash yarn
yarn add @vowena/sdk
```
</CodeGroup>
<Tip>
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.
</Tip>
const client = new VowenaClient({
contractId: NETWORKS.testnet.contractId,
rpcUrl: NETWORKS.testnet.rpcUrl,
networkPassphrase: NETWORKS.testnet.networkPassphrase,
});
```
</Tab>
<Tab title="Mainnet">
```typescript
// Mainnet deployment coming after testnet pilots.
import { VowenaClient, NETWORKS } from "@vowena/sdk";
const client = new VowenaClient({
contractId: NETWORKS.mainnet.contractId,
rpcUrl: NETWORKS.mainnet.rpcUrl,
networkPassphrase: NETWORKS.mainnet.networkPassphrase,
});
```
</Tab>
<Tab title="Custom RPC">
```typescript
import { VowenaClient } from "@vowena/sdk";
const client = new VowenaClient({
contractId: "CABC...XYZ",
rpcUrl: "https://your-rpc.example.com",
networkPassphrase: "Test SDF Network ; September 2015",
});
```
</Tab>
</Tabs>
<Note>
`NETWORKS.testnet` ships pre-configured `contractId`, `rpcUrl`,
`networkPassphrase`, and `usdcAddress`.
</Note>
```typescript
const projectTx = await client.buildCreateProject({
merchant: "GMERCHANT...ADDR",
name: "Acme SaaS",
description: "Recurring billing for Acme's hosted product.",
});
const projectResult = await client.submitTransaction(await sign(projectTx));
// Fetch the newly created project_id from the merchant's project list
const projectIds = await client.getMerchantProjects(
"GMERCHANT...ADDR",
"GMERCHANT...ADDR",
);
const projectId = projectIds[projectIds.length - 1];
```
const planTx = await client.buildCreatePlan({
merchant: "GMERCHANT...ADDR",
token: NETWORKS.testnet.usdcAddress,
amount: toStroops("9.99"), // 9.99 USDC per period
period: 2_592_000, // 30 days
priceCeiling: toStroops("14.99"), // max price within this plan
trialPeriods: 1, // 1 free period
maxPeriods: 0, // 0 = unlimited
gracePeriod: 259_200, // 3 days grace on failed charges
name: "Pro", // display name
projectId, // parent project from step 3
});
const planResult = await client.submitTransaction(await sign(planTx));
```
<Accordion title="What does toStroops do?">
Converts a human-readable amount to the 7-decimal integer format used
by Stellar tokens:
```typescript
toStroops("9.99"); // → 99900000n
toStroops("1.00"); // → 10000000n
```
</Accordion>
```typescript
const subTx = await client.buildSubscribe(
"GSUBSCRIBER...ADDR",
planId,
// Optional: tune the allowance explicitly
{ allowancePeriods: 24 },
);
const subResult = await client.submitTransaction(await sign(subTx));
```
<Tip>
The subscriber's wallet displays exactly what's being approved: the
token (USDC), the spending ceiling (`price_ceiling × allowance_periods`),
and the Vowena contract as spender. Full transparency.
</Tip>
```typescript
const chargeTx = await client.buildCharge("GKEEPER...ADDR", subId);
await client.submitTransaction(await sign(chargeTx));
```
The contract checks:
- Is the subscription active?
- Has `period` seconds passed since the last charge?
- Is the trial over?
- Does the subscriber have sufficient balance and allowance?
- Has `max_periods` been reached?
If all checks pass, USDC moves from subscriber to merchant via the SAC's
`transfer_from`.
<Info>
You don't have to run your own keeper. The [Vowena
dashboard](/dashboard/keeper-setup) ships a managed keeper cron that
fires every minute.
</Info>
```typescript
const cancelTx = await client.buildCancel("GSUBSCRIBER...ADDR", subId);
await client.submitTransaction(await sign(cancelTx));
```
<Note>
Cancellation is immediate and on-chain. Subscribers can also cancel
directly via any Soroban-aware wallet or block explorer — the Vowena
frontend is not required.
</Note>
import { VowenaClient, NETWORKS, toStroops } from "@vowena/sdk";
const client = new VowenaClient({
contractId: NETWORKS.testnet.contractId,
rpcUrl: NETWORKS.testnet.rpcUrl,
networkPassphrase: NETWORKS.testnet.networkPassphrase,
});
// 1. Create a project
const projectTx = await client.buildCreateProject({
merchant: "GMERCHANT...ADDR",
name: "Acme SaaS",
description: "",
});
await client.submitTransaction(await sign(projectTx));
const [projectId] = (
await client.getMerchantProjects("GMERCHANT...ADDR", "GMERCHANT...ADDR")
).slice(-1);
// 2. Create a plan
const planTx = await client.buildCreatePlan({
merchant: "GMERCHANT...ADDR",
token: NETWORKS.testnet.usdcAddress,
amount: toStroops("9.99"),
period: 2_592_000,
priceCeiling: toStroops("14.99"),
trialPeriods: 0,
maxPeriods: 12,
gracePeriod: 259_200,
name: "Pro",
projectId,
});
await client.submitTransaction(await sign(planTx));
const [planId] = (
await client.getMerchantPlans("GMERCHANT...ADDR", "GMERCHANT...ADDR")
).slice(-1);
// 3. Subscriber subscribes (first period charges atomically since trial=0)
const subTx = await client.buildSubscribe("GSUB...ADDR", planId);
await client.submitTransaction(await sign(subTx));
const [subId] = (
await client.getSubscriberSubscriptions("GSUB...ADDR", "GSUB...ADDR")
).slice(-1);
// 4. After the next period elapses, anyone can charge
const chargeTx = await client.buildCharge("GKEEPER...ADDR", subId);
await client.submitTransaction(await sign(chargeTx));
// 5. Cancel
const cancelTx = await client.buildCancel("GSUB...ADDR", subId);
await client.submitTransaction(await sign(cancelTx));Projects, plans, subscriptions, allowances, and the billing lifecycle in depth. Visual walkthrough of the full protocol flow — plan creation to cancellation. Every method, type, and configuration option. Set up an automated keeper to charge subscriptions on schedule.