Recur Docs

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

  1. Subscriber approves (SPL Token approve) a fixed USDC amount to the Subscription PDA
  2. The PDA acts as a delegate — it can transfer up to the approved amount from the subscriber's token account
  3. Each payment pulls one interval's amount; the keeper cannot pull more or pull early
  4. 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: PublicKey
  • merchant: PublicKey
  • plan_seed: [u8; 8]
  • amount: u64 (USDC base units per payment)
  • interval_seconds: u64
  • last_payment_timestamp: i64
  • cancel_pending: bool
  • bump: u8

Treasury Vault PDA

Seeds: ["treasury_vault"]

Receives the protocol fee from every payment. Controlled by the program — no external authority.

Instructions

InstructionSignerDescription
initialize_subscriptionSubscriberCreates Subscription PDA; first payment pulled immediately
process_paymentKeeper (any)Pulls one payment if interval has elapsed since last payment
request_cancelSubscriber or MerchantSets cancel_pending = true; no more payments pulled
finalize_cancelAnyone (permissionless)Closes PDA after prepaid period; reclaims rent
subscriber_cancelSubscriberImmediate close; forfeits remaining prepaid time
force_cancelMerchantImmediate close by merchant (e.g., fraud, ToS violation)

Fee model

Every process_payment execution splits the payment:

RecipientAmount
Merchantpayment - fee
Treasury vault$0.05 + 0.25% of payment

Minimum payment: $1.00 USDC.

Keeper

The keeper is an off-chain bot that:

  1. Monitors all active Subscription PDAs
  2. When current_time >= last_payment_timestamp + interval_seconds, calls process_payment
  3. Fires webhooks to merchant endpoints on payment success/failure
  4. Calls finalize_cancel when 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

Active → request_cancel → Cancel Pending → (period elapses) → finalize_cancel → Closed
Active → subscriber_cancel → Closed (immediate)
Active → force_cancel → Closed (immediate, merchant-initiated)

After request_cancel:

  • No further payments are pulled
  • The subscriber retains access until their prepaid period expires
  • Anyone can call finalize_cancel once the period ends

Re-exported constants

import {
  PROGRAM_ID,
  USDC_MINT_DEVNET,
  findSubscriptionPda,
  findTreasuryVaultPda,
  planSeedToBuffer,
  planSeedToArray,
} from "@recur/sdk";

On this page