Write methods build and simulate a Soroban transaction, returning an assembled XDR string. You sign the XDR with your wallet and then call submitTransaction() to broadcast it.buildCreateProject
Creates a Project on chain. Every plan must belong to a project, so this is the first call a merchant makes.const xdr = await client.buildCreateProject({
merchant: "GMERCHANT...ADDR",
name: "Acme SaaS",
description: "Recurring billing for Acme's hosted product.",
});
| Parameter | Type | Required | Description |
|---|
merchant | string | Yes | Stellar address of the merchant |
name | string | Yes | Human-readable project name |
description | string | No | Longer description shown in the dashboard |
Returns: Promise<string> — assembled XDR.
buildCreatePlan
Creates a new subscription plan inside an existing project.const xdr = 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: 0,
gracePeriod: 259_200,
name: "Pro",
projectId: 1,
});
| Parameter | Type | Required | Description |
|---|
merchant | string | Yes | Stellar address of the merchant receiving payments |
token | string | Yes | SEP-41 token contract address (e.g. USDC) |
amount | bigint | Yes | Amount per billing period in stroops |
period | number | Yes | Billing period duration in seconds |
priceCeiling | bigint | Yes | Maximum amount the plan can ever charge per period |
name | string | Yes | Display name for the plan |
projectId | number | Yes | Parent project ID |
trialPeriods | number | No | Number of free billing periods (default: 0) |
maxPeriods | number | No | Maximum billing periods, 0 = unlimited (default: 0) |
gracePeriod | number | No | Grace period in seconds for failed charges (default: 2,592,000) |
Returns: Promise<string> — assembled XDR.
buildSubscribe
Subscribes a user to a plan and sets the SEP-41 token allowance in a single transaction.// Minimal — SDK picks safe defaults
const xdr = await client.buildSubscribe(
"GSUBSCRIBER...ADDR",
42,
);
// Explicit — caller-controlled allowance
const xdr = await client.buildSubscribe(
"GSUBSCRIBER...ADDR",
42,
{
expirationLedger: latest.sequence + 2_900_000,
allowancePeriods: 24,
},
);
| Parameter | Type | Required | Description |
|---|
subscriber | string | Yes | Stellar address of the subscriber |
planId | number | Yes | Plan to subscribe to |
opts.expirationLedger | number | No | Absolute ledger the allowance expires at. Defaults to current + 2,900,000 |
opts.allowancePeriods | number | No | Number of periods the allowance should cover. Default 120 |
The expirationLedger and allowancePeriods are passed to the contract so
the nested token.approve() auth entry is deterministic between simulation
and submission. See the subscribe API reference
for the full explanation. Returns: Promise<string> — assembled XDR.
buildCharge
Charges a subscription for the current billing period. Permissionless — anyone can call.const xdr = await client.buildCharge("GKEEPER...ADDR", 101);
| Parameter | Type | Required | Description |
|---|
callerAddress | string | Yes | Address that pays the tx fee |
subId | number | Yes | Subscription ID to charge |
buildCancel
Cancels a subscription. Either the subscriber or the merchant can call.const xdr = await client.buildCancel("GSUBSCRIBER...ADDR", 101);
buildRefund
Issues a refund from the merchant to the subscriber.const xdr = await client.buildRefund(
"GMERCHANT...ADDR",
101,
toStroops("9.99"),
);
buildUpdatePlanAmount
Updates the billing amount for an existing plan. The new amount must stay within the plan’s price ceiling.const xdr = await client.buildUpdatePlanAmount(
"GMERCHANT...ADDR",
42,
toStroops("12.99"),
);
The new amount cannot exceed priceCeiling. This protects subscribers from
unexpected price increases beyond what they originally authorized.
buildRequestMigration
Flags every active subscription on oldPlanId as “migration pending” to newPlanId. Subscribers must accept individually.const xdr = await client.buildRequestMigration(
"GMERCHANT...ADDR",
42, // old plan
43, // new plan
);
buildAcceptMigration
Subscriber accepts a pending migration. The contract cancels the old subscription, creates one on the new plan, and sets a new allowance — all in one tx.const xdr = await client.buildAcceptMigration(
"GSUBSCRIBER...ADDR",
101,
{
expirationLedger: latest.sequence + 2_900_000,
allowancePeriods: 24,
},
);
The allowance options match buildSubscribe. If omitted, the SDK uses the same safe defaults.
buildRejectMigration
Subscriber rejects a pending migration. They stay on the old plan.const xdr = await client.buildRejectMigration("GSUBSCRIBER...ADDR", 101);
buildReactivate
Subscriber reactivates a paused subscription. Re-signs a new allowance and attempts an immediate charge.const xdr = await client.buildReactivate(
"GSUBSCRIBER...ADDR",
101,
{
expirationLedger: latest.sequence + 2_900_000,
allowancePeriods: 24,
},
);
buildExtendTtl
Permissionless TTL bump for a plan + subscription entry. Useful for keepers that want to keep long-lived state alive alongside their charge() calls.const xdr = await client.buildExtendTtl("GKEEPER...ADDR", 42, 101);
submitTransaction
Submits a signed XDR transaction to the Stellar network and waits for confirmation.const xdr = await client.buildSubscribe("GSUB...ADDR", 42);
const signedXdr = await wallet.signTransaction(xdr);
const result = await client.submitTransaction(signedXdr);
console.log(result.hash); // Transaction hash
console.log(result.success); // true if successful
console.log(result.returnValue); // Contract return value (if any)
Returns:interface TransactionResult {
hash: string;
success: boolean;
returnValue?: unknown;
}
Flow: build (VowenaClient) → sign (your wallet) → submit
(VowenaClient). The SDK never touches private keys.