Skip to main content

Why Migrations Exist

In traditional payment systems, a merchant can silently change your subscription price. Your credit card gets charged a new amount, and you may not notice until you check your statement. Some services bury price-change notices in emails. Others change terms retroactively. Vowena makes this structurally impossible.
A merchant cannot raise prices above the ceiling without explicit subscriber consent. The migration system enforces this at the smart contract level - not through a policy, not through a terms of service, but through code that cannot be bypassed.
Plans are immutable (except for the amount within the ceiling). If a merchant needs to change the billing period, raise the price above the ceiling, switch tokens, or alter any other structural parameter, they must:
  1. Create a new plan with the new terms
  2. Request that subscribers migrate
  3. Wait for each subscriber to accept or reject

Migration Flow

1

Merchant creates a new plan

The merchant calls create_plan with the new pricing, period, or other terms. This is a completely independent plan with its own ID.
const newPlanId = await client.createPlan({
  merchant: merchantKeypair.publicKey(),
  token: USDC_CONTRACT_ID,
  amount: 15_0000000n,        // New price: 15 USDC
  period: 2592000n,
  trial_periods: 0,
  max_periods: 12,
  grace_period: 259200n,
  price_ceiling: 20_0000000n, // New ceiling
});
2

Merchant requests migration

The merchant calls request_migration(old_plan_id, new_plan_id). The contract:
  • Verifies merchant.require_auth()
  • Confirms both plans belong to the same merchant
  • Confirms the new plan is active
  • Iterates through all active subscriptions on the old plan
  • Sets migration_target = new_plan_id on each subscription
  • Emits MigrationRequested with the count of affected subscriptions
await client.requestMigration({
  merchant: merchantKeypair.publicKey(),
  old_plan_id: 1n,
  new_plan_id: 2n,
});
This step does not change the subscription status or stop billing. Subscriptions on the old plan continue to be charged at the old price until the subscriber acts.
3

Subscribers see the migration

Each affected subscriber will see migration_target != 0 on their subscription. The Vowena Dashboard shows a banner prompting action. Keeper bots and SDK event listeners can trigger notifications.The subscriber can view the new plan’s terms - including the new price, ceiling, and period - before deciding.
4

Subscriber accepts or rejects

The subscriber has two choices:
The subscriber calls accept_migration(sub_id). The contract:
  1. Cancels the old subscription (sets status to Cancelled, records cancelled_at)
  2. Creates a new subscription on the new plan
  3. Calls token.approve() with the new plan’s ceiling and periods (subscriber signs for this)
  4. Emits MigrationAccepted with old and new subscription IDs
The subscriber’s wallet shows the new approval amount, making the price change fully transparent.
await client.acceptMigration({
  subscriber: subscriberKeypair.publicKey(),
  sub_id: 42n,
});

Consumer Protection Guarantees

No silent changes

Every price change requires a new plan, a migration request, and explicit subscriber acceptance. The subscriber sees the new price in their wallet before signing.

Right to refuse

Rejecting a migration has zero consequences. The subscription continues at the original price on the original plan. The merchant cannot force the change.

No disruption during migration

The old subscription keeps billing normally while the migration is pending. There is no gap in service, no “pending” state that blocks charges.

Transparent on-chain

Both the old and new plan are visible on-chain. Anyone can compare the terms. The MigrationRequested event links the two plans publicly.

Comparison with Traditional Systems

AspectTraditional (Credit Card)Vowena
Price increase noticeEmail (often buried)On-chain migration request, wallet prompt
Subscriber action neededOften opt-out (charged by default)Opt-in (must explicitly accept)
Time pressure”Changes take effect in 30 days”No deadline - old plan continues indefinitely
AuditabilityStatement after the factAll terms visible on-chain before accepting
EnforcementConsumer protection law (varies by country)Smart contract code (global, deterministic)
Merchants should communicate migrations through their own channels (email, in-app notifications) in addition to the on-chain mechanism. While the protocol ensures subscribers can always check on-chain, good UX means proactively informing users about upcoming changes.

What Happens if No One Accepts?

If every subscriber rejects the migration, they all stay on the old plan. The merchant can:
  • Continue operating the old plan at the old price
  • Deactivate the old plan (stops new sign-ups but existing subscriptions continue)
  • Let subscriptions naturally expire via max_periods
The merchant cannot force-cancel subscriptions or stop billing to coerce migration.
The protocol is structurally designed so that the power asymmetry between merchant and subscriber is inverted compared to traditional systems. The subscriber always has the final say.

What’s Next

Refunds

How merchants issue partial or full refunds on-chain.

Security

The full consumer protection model and security architecture.