Skip to main content

Subscriptions

Subscription model (X402FlexSubscriptions)

Subscriptions are onchain agreements for recurring charges with typed-data authorization and deterministic subscription IDs.

Create subscription authorization

What this does: builds signer-ready EIP-712 typed data for subscription creation.

import { createClient } from '@pepay/x402flex';
import { ethers } from 'ethers';

const sdk = createClient({
mode: 'contracts',
preset: 'bnbpay-testnets',
contracts: { defaultNetwork: 'eip155:97' },
});

const createRequest = {
payer: '0x2222222222222222222222222222222222222222',
merchant: '0x1111111111111111111111111111111111111111',
token: '0x55d398326f99059fF775485246999027B3197955',
amount: ethers.parseUnits('25', 18),
startAt: Math.floor(Date.now() / 1000) + 300,
cadenceKind: 0,
cadence: 30 * 24 * 60 * 60,
cancelWindow: 24 * 60 * 60,
maxPayments: 12,
pullMode: 1,
termsHash: ethers.ZeroHash,
salt: ethers.hexlify(ethers.randomBytes(32)),
deadline: Math.floor(Date.now() / 1000) + 3600,
};

const typed = sdk.subscriptions.buildCreateTypedData(createRequest, {
chainId: 97,
verifyingContract: '0x3333333333333333333333333333333333333333',
});

Expected result: typed.domain/types/message for wallet signing.

Create subscription on-chain

What this does: submits subscribeAndChargeWithSig through the SDK wrapper.

const payerSignature = await payerWallet.signTypedData(
typed.domain,
typed.types,
typed.message,
);

await sdk.subscriptions.createWithSig(
{
request: createRequest,
payerSignature,
},
'bnbTestnet',
);

Expected result: subscription exists and initial charge can be executed per contract logic.

Charge due subscription

What this does: charges an existing subscription ID when due.

await sdk.subscriptions.charge('0xsubIdBytes32', 'bnbTestnet');

Expected result: charge transaction submitted if cadence/eligibility checks pass.

Cancel subscription

What this does: cancels directly or via signature depending on parameters.

await sdk.subscriptions.cancel(
'0xsubIdBytes32',
Math.floor(Date.now() / 1000) + 3600,
undefined,
'bnbTestnet',
);

Expected result: subscription status becomes cancelled and future charges stop.

Query status and due state

What this does: reads current subscription and next charge conditions.

const sub = await sdk.subscriptions.get('0xsubIdBytes32', 'bnbTestnet');
const due = await sdk.subscriptions.isDue('0xsubIdBytes32', 'bnbTestnet');

console.log(sub, due);

Expected result: clear subscription state and due decision.

Failure modes

  • Invalid typed-data domain: wrong chain ID or verifying contract.
  • Not due: charge fails if cadence window is not reached.
  • Signature mismatch: payer signature does not match subscription request fields.