Skip to main content
fn create_plan(
    env: Env,
    merchant: Address,
    token: Address,
    amount: i128,
    period: u64,
    trial_periods: u32,
    max_periods: u32,
    grace_period: u64,
    price_ceiling: i128,
) -> u64
Creates a new billing plan on-chain. The plan defines the terms of recurring payments - token, amount, billing period, trial length, and price protection ceiling. Returns the auto-incremented plan_id.

Parameters

NameTypeDescription
merchantAddressThe merchant’s Stellar address. Must sign the transaction.
tokenAddressThe SEP-41 token contract address (e.g., USDC).
amounti128Amount charged per billing period, in stroops (7 decimal places).
periodu64Billing period duration in seconds (e.g., 2592000 for 30 days).
trial_periodsu32Number of free trial periods before billing begins. Use 0 for no trial.
max_periodsu32Maximum number of billing periods. Use 0 for unlimited.
grace_periodu64Grace window in seconds after a failed charge before the subscription pauses.
price_ceilingi128Maximum amount the plan can ever charge per period, in stroops. Protects subscribers from price increases.

Authorization

merchant.require_auth();
The merchant address must sign the transaction.

Return value

u64 - the newly created plan ID.

Events emitted

EventTopicsData
plan_createdmerchant, plan_idPlan struct

Error cases

CodeNameDescription
3InvalidAmountamount is zero or negative.
4InvalidPeriodperiod is zero.
5CeilingBelowAmountprice_ceiling is less than amount.

Examples

import { VowenaClient, NETWORKS, toStroops } from "vowena";

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

const tx = await client.buildCreatePlan({
  merchant: "GMERCHANT...ADDR",
  token: NETWORKS.testnet.usdcAddress,
  amount: toStroops("9.99"),          // 99900000n stroops
  period: 2_592_000,                  // 30 days
  trialPeriods: 1,                    // 1 free period
  maxPeriods: 0,                      // unlimited
  gracePeriod: 259_200,               // 3 days
  priceCeiling: toStroops("14.99"),   // max 14.99 USDC
});

const signedXdr = await signTransaction(tx);
const result = await client.submitTransaction(signedXdr);
console.log("Plan ID:", result.planId); // e.g., 1
Stellar tokens use 7 decimal places. The SDK’s toStroops() helper converts human-readable amounts:
toStroops("9.99")   // → 99900000n
toStroops("14.99")  // → 149900000n
toStroops("1.00")   // → 10000000n
When using the CLI, pass the raw stroop value directly.
Set price_ceiling thoughtfully. It protects subscribers from unexpected price hikes - the merchant can call update_plan_amount to raise the price, but never above the ceiling. If you need to exceed the ceiling, create a new plan and use the migration flow.