Developing Crypto Subscription Payment Model
Traditional subscriptions work simply: card linked, bank deducts every month. In crypto there's no pull payment mechanism — nobody can withdraw tokens from your wallet without your signature. This is a fundamental architectural difference that makes crypto subscriptions a nontrivial task. Each solution is a compromise between UX and decentralization.
Three Architectures and Their Real Compromises
Prepaid with On-Chain Balance
User deposits funds into subscription contract. Contract deducts payment periodically — either on service request (chargeFee(address user)), or on every user access to protected function.
This is the simplest option. Problem: user must remember to top up balance. Balance runs out — subscription stops without warning. For services with auto-replenishment, need off-chain trigger (Chainlink Automation or Gelato) for timely notification.
Pull Payments via EIP-2612 (Permit) + Off-Chain Trigger
User signs permit — permission to deduct up to N tokens from their wallet without separate on-chain transaction. Permit is stored off-chain. Every billing period backend sends transferFrom transaction using saved permit.
This is closest to traditional pull payments. Compromise: backend must be trusted and not abuse deduction rights. For fully decentralized system — not suitable. For B2B SaaS with trusted operator — great option.
EIP-2612 permit has deadline — permission expires. Need to warn user early about updating permission. Infinite permit (deadline = type(uint256).max) reduces UX friction but increases risks if key is compromised.
Account Abstraction + Session Keys
Most user-friendly approach. User's smart wallet (EIP-4337) issues session key with right to deduct up to X tokens per month to subscription contract. Session key lives server-side with service. Every billing period — UserOperation via bundler, without user participation.
Requires user has smart wallet, not EOA. Currently — limitation. In 2-3 years, as AA adoption grows — probably standard.
Subscription Contract Structure
Minimally necessary components:
struct Subscription {
address subscriber;
uint256 planId;
uint256 paidUntil; // timestamp
uint256 depositBalance; // prepaid balance
bool active;
}
Key functions:
-
subscribe(uint256 planId)— subscription with initial deposit -
renewSubscription(address subscriber)— renewal (called by operator or Chainlink Automation) -
cancelSubscription()— cancellation with deposit refund -
isSubscribed(address user) returns (bool)— status check (view, no gas) -
withdraw()— withdrawal of accumulated payments by owner
Access Management
Protected resource checks subscription status via ISubscription.isSubscribed(msg.sender). If resource is on-chain contract, check happens in modifier. If resource is off-chain (API, content), backend checks isSubscribed via eth_call before answering request.
Second option has centralization point: backend can ignore on-chain status. Not a problem for business services, but important to understand when positioning as "decentralized" product.
Plan Pricing and Oracles
If subscription is denominated in USD but payment in ETH or volatile tokens — need price oracle. Chainlink Price Feed is standard solution: contract gets current rate and calculates token quantity equivalent to N USD.
Risk: oracle can be manipulated via flash loan in same block. For subscription contracts less critical than for AMM (attacker gets unfair price for one transaction), but TWAP (Time-Weighted Average Price) via Uniswap V3 is more robust for sensitive systems.
Stablecoins (USDC, USDT, DAI) remove rate problem — subscription in USDC priced in USD without oracles. This is preferred for most SaaS products.
Renewal Automation
Chainlink Automation (formerly Keepers) lets contract "serve itself": define checkUpkeep() — logic to check if renewal needed — and performUpkeep() — actual renewal. Chainlink node calls performUpkeep() automatically when checkUpkeep() returns true.
Alternative — Gelato Network, more flexible in condition setup. For simple time-based triggers — Chainlink Automation simpler and cheaper.
Process and Timeline
Basic subscription contract (prepaid model, one plan, USDC) — 3-4 days development + 1 day tests. Multi-plan system with EIP-2612 permit, price oracle, and Chainlink Automation — 2-3 weeks. Full integration with AA wallets and session keys — from 4 weeks.
Cost depends on chosen architecture and decentralization requirements.







