Skip to main content
fn subscribe(env: Env, subscriber: Address, plan_id: u64) -> u64
Subscribes a user to an existing plan. This function creates the subscription record and sets the SEP-41 token allowance in a single transaction. The subscriber signs once and both operations are authorized. Returns the auto-incremented sub_id.

Parameters

NameTypeDescription
subscriberAddressThe subscriber’s Stellar address. Must sign the transaction.
plan_idu64The ID of the plan to subscribe to.

Authorization

subscriber.require_auth();
The subscriber’s single signature covers both the subscribe() contract call and the nested token.approve() call. Soroban’s auth tree bundles both operations into one authorization.
The subscriber signs once. Their wallet will display exactly what is being authorized: the Vowena contract call and the token allowance (amount = price_ceiling * periods, spender = Vowena contract). No hidden permissions. Full transparency.

Allowance calculation

The contract calculates the token allowance as:
allowance = price_ceiling * periods_for_approval
  • If max_periods > 0: periods_for_approval = max_periods
  • If max_periods = 0 (unlimited): periods_for_approval = 120
  • Allowance expiry is capped at 6,000,000 ledgers (~347 days)
The allowance is set against the plan’s price_ceiling, not the current amount. This means the merchant can adjust pricing within the ceiling without requiring re-authorization.

Return value

u64 - the newly created subscription ID.

Events emitted

EventTopicsData
sub_createdsubscriber, sub_id, plan_idSubscription struct

Error cases

CodeNameDescription
6PlanNotFoundNo plan exists with the given plan_id.
7PlanInactiveThe plan is not accepting new subscribers.

Examples

import { VowenaClient, NETWORKS } from "vowena";

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

const tx = await client.buildSubscribe(
  "GSUBSCRIBER...ADDR",  // Subscriber's address
  1                       // Plan ID
);

const signedXdr = await signTransaction(tx);
const result = await client.submitTransaction(signedXdr);
console.log("Subscription ID:", result.subscriptionId);
If the plan has trial periods, the subscription starts immediately but billing begins after the trial ends. The next_billing_time is set to now + (period * (trial_periods + 1)) so the first real charge happens after the trial.