Protocol Architecture
On-chain design — PDAs, instructions, fee model, keeper, and token delegation.
Protocol Architecture
Recur is a non-custodial recurring payment protocol on Solana. Subscriber funds remain in their own wallet — the protocol uses SPL Token delegation to pull payments at defined intervals.
Token delegation model
- Subscriber approves (SPL Token
approve) a fixed USDC amount to the Subscription PDA - The PDA acts as a delegate — it can transfer up to the approved amount from the subscriber's token account
- Each payment pulls one interval's amount; the keeper cannot pull more or pull early
- When the approval is exhausted (after N cycles), the subscriber must re-approve
This is entirely non-custodial. The subscriber can revoke delegation at any time via a standard SPL Token revoke instruction.
Program Derived Addresses (PDAs)
Subscription PDA
Seeds: ["subscription", subscriber_pubkey, merchant_pubkey, plan_seed(8 bytes)]
Stores:
subscriber: PublicKeymerchant: PublicKeyplan_seed: [u8; 8]amount: u64 (USDC base units per payment)interval_seconds: u64last_payment_timestamp: i64cancel_pending: boolbump: u8
Treasury Vault PDA
Seeds: ["treasury_vault"]
Receives the protocol fee from every payment. Controlled by the program — no external authority.
Instructions
| Instruction | Signer | Description |
|---|---|---|
initialize_subscription | Subscriber | Creates Subscription PDA; first payment pulled immediately |
process_payment | Keeper (any) | Pulls one payment if interval has elapsed since last payment |
request_cancel | Subscriber or Merchant | Sets cancel_pending = true; no more payments pulled |
finalize_cancel | Anyone (permissionless) | Closes PDA after prepaid period; reclaims rent |
subscriber_cancel | Subscriber | Immediate close; forfeits remaining prepaid time |
force_cancel | Merchant | Immediate close by merchant (e.g., fraud, ToS violation) |
Fee model
Every process_payment execution splits the payment:
| Recipient | Amount |
|---|---|
| Merchant | payment - fee |
| Treasury vault | $0.05 + 0.25% of payment |
Minimum payment: $1.00 USDC.
Keeper
The keeper is an off-chain bot that:
- Monitors all active Subscription PDAs
- When
current_time >= last_payment_timestamp + interval_seconds, callsprocess_payment - Fires webhooks to merchant endpoints on payment success/failure
- Calls
finalize_cancelwhen cancel-pending subscriptions' periods elapse
The keeper is permissionless — anyone can run one. The program enforces timing constraints, so a malicious keeper cannot pull early or double-charge.
First payment
On initialize_subscription, the program sets last_payment_timestamp = now - interval_seconds. This means the keeper can immediately call process_payment for the first billing cycle — no free trial by default.
Cancel flows
After request_cancel:
- No further payments are pulled
- The subscriber retains access until their prepaid period expires
- Anyone can call
finalize_cancelonce the period ends