Documentation

Subscriptions

Subscriptions

PerfectPay handles recurring billing through the subscriptions API, mandate management for saved payment authorization, and cross-processor retry logic that recovers failed charges by attempting them on alternate connectors.

Recurring Billing Setup

Create a subscription to bill a customer on a recurring schedule.

POST /subscriptions

GET //sandbox.perfectpay.ai/subscriptions
curl https://sandbox.perfectpay.ai/subscriptions \
  -X POST \
  -H "api-key: YOUR_SECRET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "customer_id": "cus_abc123",
    "amount": 2999,
    "currency": "USD",
    "recurring_details": {
      "type": "plan",
      "interval": "monthly",
      "start_date": "2026-04-01"
    },
    "payment_method_id": "pm_saved_card_001",
    "description": "Pro Plan - Monthly"
  }'

The subscription creates payment intents automatically at each billing cycle. Each generated payment follows your routing rules, so if your primary processor declines, the retry logic kicks in.

See the subscriptions API reference for the full field set.

Usage-Based and Metered Billing

For usage-based billing, report usage during the billing period and let PerfectPay calculate the charge at cycle end.

The pattern:

  1. Create a subscription with "type": "metered" and a unit price
  2. Report usage events throughout the billing period via the API
  3. At the billing cycle boundary, PerfectPay sums the reported usage and creates a payment for the calculated amount
GET //sandbox.perfectpay.ai/subscriptions/{subscription_id}/usage
curl https://sandbox.perfectpay.ai/subscriptions/{subscription_id}/usage \
  -X POST \
  -H "api-key: YOUR_SECRET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "quantity": 150,
    "timestamp": "2026-03-15T14:30:00Z",
    "action": "increment",
    "metadata": {
      "feature": "api_calls"
    }
  }'

Hybrid Billing Models

Combine a fixed base fee with usage-based charges for plans like "$29/month plus $0.01 per API call."

Configure hybrid billing by setting both a base amount and a metered component on the subscription:

JSON
{
  "amount": 2900,
  "recurring_details": {
    "type": "hybrid",
    "interval": "monthly",
    "metered_component": {
      "unit_amount": 1,
      "unit_label": "api_call"
    }
  }
}

At each billing cycle, PerfectPay charges the base amount plus the accumulated metered usage.

Dunning and Retry Logic

When a recurring charge fails, PerfectPay does not give up after one attempt. The retry logic works at two levels:

Same-Connector Retry

If a charge fails with a soft decline (insufficient funds, temporary issuer error), PerfectPay retries on the same connector after a configurable delay.

Cross-Connector Retry

If the decline is hard or the connector is down, PerfectPay routes the retry to the next eligible connector in your routing configuration. This is where having multiple connectors pays off -- a decline on one processor does not mean the card is actually bad.

Retry Schedule

Configure the retry schedule per subscription or use the account-level default:

AttemptTimingBehavior
1Billing datePrimary connector
2+1 daySame connector, soft retry
3+3 daysNext eligible connector
4+7 daysNext eligible connector

After all retries are exhausted, the subscription moves to past_due. You can configure whether to cancel automatically or hold for manual recovery.

Dunning Communication

Subscribe to these webhook events to trigger customer-facing dunning emails:

  • payment_failed -- charge attempt failed, retry scheduled
  • subscription_past_due -- all retries exhausted
  • subscription_canceled -- subscription canceled after dunning period

See Events & Webhooks for webhook configuration.

Mandate Management

Mandates authorize PerfectPay to charge a saved payment method for recurring transactions. They are required for:

  • Card-on-file -- storing a card and charging it later without the customer present
  • Bank debit -- ACH, SEPA Direct Debit, and other pull-based bank methods
  • Wallet recurring -- recurring charges through wallet providers that require pre-authorization

Create a mandate alongside the first payment:

GET //sandbox.perfectpay.ai/payments
curl https://sandbox.perfectpay.ai/payments \
  -X POST \
  -H "api-key: YOUR_SECRET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 2999,
    "currency": "USD",
    "customer_id": "cus_abc123",
    "setup_future_usage": "off_session",
    "mandate_data": {
      "customer_acceptance": {
        "acceptance_type": "online",
        "online": {
          "ip_address": "198.51.100.42",
          "user_agent": "Mozilla/5.0..."
        }
      },
      "mandate_type": {
        "single_use": null,
        "multi_use": {
          "amount": 2999,
          "currency": "USD"
        }
      }
    },
    "confirm": true,
    "payment_method": "card",
    "payment_method_type": "credit",
    "payment_method_data": {
      "card": {
        "card_number": "4242424242424242",
        "card_exp_month": "12",
        "card_exp_year": "27",
        "card_cvc": "123",
        "card_holder_name": "Jane Doe"
      }
    },
    "return_url": "https://example.com/subscribe/complete"
  }'

The mandate is linked to the saved payment method. Subsequent subscription charges reference the mandate automatically.

See the mandates guide for multi-use vs single-use mandates and bank debit specifics.

Plan Management

Manage subscription tiers through the API:

  • Upgrade -- change the subscription amount and interval mid-cycle with prorated billing
  • Downgrade -- schedule a plan change to take effect at the next billing cycle
  • Pause -- suspend billing without canceling the subscription
  • Cancel -- stop future charges, optionally at period end
GET //sandbox.perfectpay.ai/subscriptions/{subscription_id}
curl https://sandbox.perfectpay.ai/subscriptions/{subscription_id} \
  -X PUT \
  -H "api-key: YOUR_SECRET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 4999,
    "metadata": {
      "plan": "enterprise",
      "change_type": "upgrade",
      "prorate": true
    }
  }'

Next Steps