Skip to content
recurring-billingstablecoinusdc
Joshua C.

How Recurring Stablecoin Payments Settle Onchain

A developer's guide to onchain recurring stablecoin payments. Learn how USDC subscriptions work, from customer approval to settlement, with practical code examples.

How Recurring Stablecoin Payments Settle Onchain

Recurring payments are essential for SaaS, memberships, and any subscription model. In crypto, recurring stablecoin payments let users pay from their own wallet on a set schedule. This guide explains the mechanics: how a customer gives approval, how a merchant collects, and what happens when things fail. You will see a concrete example with USDC, on Base, Ethereum, or Polygon.

How recurring billing works onchain

Onchain recurring billing starts with a customer giving the merchant permission to pull a defined amount of USDC from their wallet. The merchant can only collect up to this limit and only as often as the schedule specifies. No one hands over a private key. No one gives unlimited approval.

Cap and schedule: what a customer signs

The approval is a smart contract signature. The customer signs a transaction to approve a maximum amount per billing cycle. For example, $30 USDC per month. This is not an open-ended transfer. The cap is visible to the customer before they sign.

Usually, this happens through a hosted checkout. The merchant presents the subscription details. The customer connects their wallet, reviews the cap, and signs the approval. The signed approval is onchain and can be read by the billing contract.

Settlement: what happens at collection

At each billing cycle, the merchant’s system or a billing contract triggers a collection. The contract checks:

  • Is the scheduled time elapsed since the last billing?
  • Is the amount within the approved cap?
  • Does the customer’s wallet have enough USDC?

If all checks pass, the contract transfers USDC from the customer’s wallet to an address the merchant controls. The transaction is final. There are no chargebacks. If the wallet has insufficient funds, the payment fails. The merchant can retry on the next cycle or notify the customer to top up.

Example: USDC subscription billed monthly

Imagine a newsletter that charges $10 USDC per month. The merchant wants to automate billing for 100 subscribers. Here’s how it works with a smart contract.

1. Customer signs approval

Each subscriber connects their wallet to the merchant’s checkout. They are prompted to sign a transaction like this (pseudo-Solidity):

IERC20(usdc).approve(subscriptionContract, 10e6); // USDC uses 6 decimals

Or, more securely, a contract using permit (EIP-2612) so they sign an off-chain message:

IERC20Permit(usdc).permit(
    owner, // subscriber address
    subscriptionContract,
    10e6, // $10 USDC
    deadline, // expiration of approval
    v, r, s // signature params
);

The approval is for $10, not unlimited. The contract cannot pull more than this per billing cycle.

2. Merchant collects payment

At the start of each month, the merchant calls the collection function for each subscriber. The contract checks the time and amount, then moves funds:

function collect(address subscriber) external {
    require(block.timestamp >= nextBilling[subscriber], "Too soon");
    require(amount <= allowance[subscriber], "Exceeds cap");
    require(IERC20(usdc).balanceOf(subscriber) >= amount, "Insufficient funds");

    IERC20(usdc).transferFrom(subscriber, merchant, amount);
    nextBilling[subscriber] += 30 days;
}

merchant is the merchant’s settlement address. The funds go there directly.

3. Handling failures

If the subscriber’s wallet is empty, transferFrom fails. No funds leave the wallet. The merchant can notify the user offchain or try again in the next billing window. No one can reverse a successful payment.

Security and approvals

The customer’s approval is always bounded. The merchant cannot collect more than the signed cap. The customer can revoke approval at any time from their wallet. Private keys remain private. Funds only move when the customer’s wallet signs or when the contract acts within the agreed terms.

Code: a minimal subscription contract

Below is a simplified Solidity contract for recurring USDC billing. In production, use a well-audited implementation.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IERC20 {
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
    function balanceOf(address account) external view returns (uint256);
}

contract RecurringBilling {
    IERC20 public usdc;
    address public merchant;
    uint256 public cap;
    mapping(address => uint256) public nextBilling;
    uint256 public billingInterval;

    constructor(address _usdc, address _merchant, uint256 _cap, uint256 _interval) {
        usdc = IERC20(_usdc);
        merchant = _merchant;
        cap = _cap;
        billingInterval = _interval;
    }

    function subscribe() external {
        require(nextBilling[msg.sender] == 0, "Already subscribed");
        nextBilling[msg.sender] = block.timestamp + billingInterval;
    }

    function collect(address subscriber) external {
        require(block.timestamp >= nextBilling[subscriber], "Too soon");
        require(usdc.balanceOf(subscriber) >= cap, "Insufficient funds");

        bool success = usdc.transferFrom(subscriber, merchant, cap);
        require(success, "Transfer failed");
        nextBilling[subscriber] = block.timestamp + billingInterval;
    }
}

This contract assumes the merchant collects the full cap each cycle. In practice, you can make the cap user-specific or allow for variable amounts, but each must be signed and stored onchain.

Revocation and customer controls

Customers can revoke approval at any time in their wallet UI. No funds move after revocation. Merchants must monitor for revocations to avoid failed collections. If a scheduled collection fails, it is safe to retry on the next cycle.

Wallet UX and the customer portal

A well-designed system shows the customer exactly what they are approving: the maximum per cycle, the schedule, and the merchant’s address. Hosted checkouts and customer portals make it easy to view, pause, or cancel subscriptions. All collections are visible in the wallet’s transaction history. There are no hidden transfers.

How we handle this at Reclack

We built Reclack to make recurring stablecoin payments predictable and transparent. Merchants set the cap and schedule. Customers sign a bounded approval. Our contracts collect only what the customer has signed for, from their own wallet, on Base, Ethereum, or Polygon, in USDC. We never ask for a seed phrase or a private key, and we never hold funds. Every collection is final. You can read more or try it yourself at reclack.com.

recurring-billingstablecoinusdc