A decentralized subscription payment system built on Ethereum that enables merchants to create subscription plans and receive recurring payments in ERC20 tokens.
- Merchant Management: Merchants can register and configure accepted payment tokens
- Subscription Plans: Create flexible subscription plans with custom amounts and periods
- Recurring Payments: Automated recurring payment processing with commission handling
- Multi-Token Support: Accept multiple ERC20 tokens for subscriptions
- Commission System: Configurable commission rates with min/max limits
- Subscription Control: Users can start and cancel subscriptions at any time
- ZeroPaySubscription: Main contract handling subscription logic
- Merchant: Stores merchant information and accepted tokens
- Plan: Subscription plan with amount, period, and merchant info
- Subscription: Active subscription with payment details and next claim time
The contract implements a flexible commission system:
- Commission rate (percentage)
- Minimum commission amount
- Maximum commission amount
This project uses Foundry.
forge installRun the comprehensive test suite:
forge testRun tests with verbosity:
forge test -vvvRun specific test:
forge test --match-test testCreateSubscriptionCreate a .env file with the following variables:
PRIVATE_KEY=your_private_key
INITIAL_OWNER=0x... # Optional, defaults to deployer address
COMMISSION_RATE=5 # Optional, default 5%
COMMISSION_MIN=1000000000000000000 # Optional, default 1 token (18 decimals)
COMMISSION_MAX=100000000000000000000 # Optional, default 100 tokens (18 decimals)
RPC_URL=your_rpc_urlsource .env
forge script script/Subscription.s.sol:SubscriptionScript --rpc-url $RPC_URL --broadcast --verifyStart Anvil:
anvilDeploy:
forge script script/Subscription.s.sol:SubscriptionScript --rpc-url http://localhost:8545 --broadcast-
Register as Merchant
subscription.merchant(receiverAddress); -
Configure Accepted Tokens
address[] memory adds = [token1, token2]; address[] memory dels = []; subscription.tokens(adds, dels);
-
Create Subscription Plan
subscription.plan(amount, period); -
Cancel Plan
subscription.unplan(planId);
-
Subscribe to Plan
token.approve(subscriptionAddress, amount); subscription.subscribe(planId, customerAddress, tokenAddress);
-
Cancel Subscription
subscription.unsubscribe(subscriptionId);
Claim Subscription Payment (after period expires):
subscription.claim(subscriptionId);Claim Accumulated Fees:
address[] memory tokens = [token1, token2];
subscription.claimFee(tokens, payeeAddress);PlanStarted(uint256 indexed id, address merchant, uint256 amount, uint256 period)PlanCanceled(uint256 indexed id)SubscriptionStarted(uint256 indexed id, uint256 plan, address customer, address payer, address token, uint256 nextTime)SubscriptionCanceled(uint256 indexed id)SubscriptionClaimed(uint256 indexed id)
M01: Invalid receiver address (cannot be zero address) or merchant not registeredM02: Unauthorized plan modificationM03: Plan is not activeM04: Token not supported by merchantM05: Subscription is not activeM06: Too early to claim (period not elapsed)
forge buildforge fmtforge snapshot- Uses OpenZeppelin's SafeERC20 for secure token transfers
- Ownable pattern for admin functions
- Input validation on all public functions
- Careful handling of commission calculations to prevent overflow
MIT
For more information on Foundry: