Moneybag

Subscription API

Create and manage recurring payment subscriptions for your customers with flexible billing plans, trial periods, and automatic payment processing

Base URLs

  • Sandbox: https://sandbox.api.moneybag.com.bd/api/v2
  • Production: https://api.moneybag.com.bd/api/v2

Overview

Moneybag Subscriptions enable you to offer recurring billing to your customers with flexible plans, trial periods, and automatic payment processing.

Key Features

  • Flexible billing intervals (daily, weekly, monthly, yearly)
  • Free or paid trial periods
  • Automatic recurring payments
  • Invoice generation for each billing cycle
  • Pause/resume subscription capability
  • Payment failure handling with retry logic
  • Real-time webhook notifications
  • Billing history tracking

How Subscriptions Work


Quick Start

1. Create a Subscription Plan

curl -X POST "https://api.moneybag.com.bd/api/v2/payments/subscription-plans" \
  -H "X-Merchant-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "plan_name": "Premium Monthly",
    "amount": 499.00,
    "interval_count": 1,
    "interval_unit": "MONTH",
    "trial_period_days": 14
  }'

2. Create a Subscription for a Customer

curl -X POST "https://api.moneybag.com.bd/api/v2/payments/subscriptions" \
  -H "X-Merchant-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "subscription_plan_id": 1,
    "customer": {
      "name": "John Doe",
      "email": "john@example.com",
      "phone": "+880123456789"
    }
  }'

3. Set Up Webhooks

Register a webhook to receive subscription events:

curl -X POST "https://api.moneybag.com.bd/api/v2/merchants/webhooks" \
  -H "X-Merchant-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "endpoint_url": "https://your-domain.com/webhooks",
    "event_types": [
      "subscription.created",
      "subscription.payment_succeeded",
      "subscription.payment_failed",
      "subscription.cancelled"
    ]
  }'

Authentication

All subscription API endpoints require authentication using your merchant API key in the header:

X-Merchant-API-Key: YOUR_API_KEY
Content-Type: application/json

Note: Merchants should use their API keys for all subscription-related operations. The subscription API is designed for system-to-system integration.


Core Concepts

Subscription Lifecycle

Status Descriptions:

  • PENDING: Just created, awaiting first payment
  • TRIALING: Customer in free/paid trial period
  • ACTIVE: Subscription active, billing normally
  • PAST_DUE: Payment failed, retrying automatically
  • PAUSED: Temporarily paused, no billing
  • CANCELLED: Cancelled, will expire at period end
  • EXPIRED: Fully ended, no access

Subscription Checkout Flow

Any valid subscription (ACTIVE, PAST_DUE, PAUSED, or TRIALING ending soon) can have a checkout session created for direct payment collection:

Subscription Statuses

StatusDescription
PENDINGCreated, awaiting first payment
TRIALINGIn trial period
ACTIVEActive, recurring payments enabled
PAST_DUEPayment failed, awaiting retry
PAUSEDTemporarily paused by request
CANCELLEDCancelled, won't renew
EXPIREDCancelled and billing period ended

Billing Intervals

IntervalDescriptionExample
DAYDaily billingEvery day, every 3 days
WEEKWeekly billingEvery week, every 2 weeks
MONTHMonthly billingMonthly, quarterly
YEARAnnual billingYearly

Trial Periods

  • Free Trial: trial_period_days set, trial_amount is null
  • Paid Trial: Both trial_period_days and trial_amount set
  • No Trial: trial_period_days is null or 0

Subscription Plans

Plans define the template for subscriptions.

Create Plan

POST /api/v2/payments/subscription-plans

Headers:

X-Merchant-API-Key: YOUR_API_KEY
Content-Type: application/json

Request:

{
  "plan_name": "Premium Monthly",
  "plan_description": "Full access to all premium features",
  "amount": 499.00,
  "currency": "BDT",
  "interval_count": 1,
  "interval_unit": "MONTH",
  "trial_period_days": 14,
  "trial_amount": null,
  "meta_data": {
    "features": ["unlimited_storage", "priority_support"]
  }
}

Parameters:

FieldTypeRequiredDescription
plan_namestringYesPlan display name (max 200 chars)
plan_descriptionstringNoPlan description
amountdecimalYesSubscription amount (min: 10.00)
currencystringNoCurrency code (default: BDT)
interval_countintegerYesNumber of intervals (1-365)
interval_unitstringYesDAY, WEEK, MONTH, or YEAR
trial_period_daysintegerNoTrial period length
trial_amountdecimalNoPaid trial amount (null = free)
meta_dataobjectNoCustom metadata

Response (201 Created):

{
  "id": 1,
  "uuid": "c82f5b83-feb7-4fcd-81a2-621f3a2e3187",
  "merchant_id": 123,
  "plan_name": "Premium Monthly",
  "plan_description": "Full access to all premium features",
  "amount": "499.00",
  "currency": "BDT",
  "interval_count": 1,
  "interval_unit": "MONTH",
  "trial_period_days": 14,
  "trial_amount": null,
  "meta_data": {
    "features": ["unlimited_storage", "priority_support"]
  },
  "is_active": true,
  "created_at": "2025-11-30T10:00:00Z"
}

List Plans

GET /api/v2/payments/subscription-plans

Query Parameters:

ParameterTypeDefaultDescription
include_inactivebooleanfalseInclude archived/inactive plans
pageinteger1Page number for pagination
per_pageinteger20Items per page (max: 100)

cURL Example:

curl -X GET "https://api.moneybag.com.bd/api/v2/payments/subscription-plans?include_inactive=false&page=1&per_page=20" \
  -H "X-Merchant-API-Key: YOUR_API_KEY"

Response (200 OK):

{
  "success": true,
  "message": "Subscription plans retrieved successfully",
  "data": [
    {
      "id": 1,
      "uuid": "c82f5b83-feb7-4fcd-81a2-621f3a2e3187",
      "merchant_id": 123,
      "plan_name": "Premium Monthly",
      "amount": "499.00",
      "currency": "BDT",
      "interval_count": 1,
      "interval_unit": "MONTH",
      "trial_period_days": 14,
      "trial_amount": null,
      "is_active": true,
      "created_at": "2025-11-30T10:00:00Z"
    }
  ],
  "meta": {
    "total": 1,
    "page": 1,
    "per_page": 20
  }
}

Get Plan

GET /api/v2/payments/subscription-plans/{plan_id}

cURL Example:

curl -X GET "https://api.moneybag.com.bd/api/v2/payments/subscription-plans/1" \
  -H "X-Merchant-API-Key: YOUR_API_KEY"

Response (200 OK):

{
  "id": 1,
  "uuid": "c82f5b83-feb7-4fcd-81a2-621f3a2e3187",
  "merchant_id": 123,
  "plan_name": "Premium Monthly",
  "plan_description": "Full access to all premium features",
  "amount": "499.00",
  "currency": "BDT",
  "interval_count": 1,
  "interval_unit": "MONTH",
  "trial_period_days": 14,
  "trial_amount": null,
  "meta_data": {
    "features": ["unlimited_storage", "priority_support"]
  },
  "is_active": true,
  "created_at": "2025-11-30T10:00:00Z"
}

Update Plan

PATCH /api/v2/payments/subscription-plans/{plan_id}

Request Body:

All fields are optional. Only include fields you want to update.

{
  "plan_name": "Premium Plus Monthly",
  "amount": 599.00,
  "plan_description": "Enhanced premium features"
}

Archive Plan

DELETE /api/v2/payments/subscription-plans/{plan_id}

cURL Example:

curl -X DELETE "https://api.moneybag.com.bd/api/v2/payments/subscription-plans/1" \
  -H "X-Merchant-API-Key: YOUR_API_KEY"

Response (200 OK):

{
  "id": 1,
  "uuid": "c82f5b83-feb7-4fcd-81a2-621f3a2e3187",
  "merchant_id": 123,
  "plan_name": "Premium Monthly",
  "amount": "499.00",
  "currency": "BDT",
  "is_active": false,
  "created_at": "2025-11-30T10:00:00Z"
}

Note: This sets is_active to false. Existing subscriptions continue but no new subscriptions can use this plan.

Restore Archived Plan

POST /api/v2/payments/subscription-plans/{plan_id}/restore

cURL Example:

curl -X POST "https://api.moneybag.com.bd/api/v2/payments/subscription-plans/1/restore" \
  -H "X-Merchant-API-Key: YOUR_API_KEY"

Response (200 OK):

{
  "id": 1,
  "uuid": "c82f5b83-feb7-4fcd-81a2-621f3a2e3187",
  "merchant_id": 123,
  "plan_name": "Premium Monthly",
  "amount": "499.00",
  "currency": "BDT",
  "is_active": true,
  "created_at": "2025-11-30T10:00:00Z"
}

Note: This restores an archived plan, setting is_active back to true.


Creating Subscriptions

Create Subscription

POST /api/v2/payments/subscriptions

Request Body Options:

You can create subscriptions in multiple ways:

Option 1: Using Plan Reference

{
  "subscription_plan_id": 1,
  "customer_id": 456
}

Option 2: With Custom Overrides

{
  "subscription_plan_id": 1,
  "customer_id": 456,
  "amount": 399.00,
  "trial_period_days": 7
}

Option 3: Create New Customer Inline

{
  "subscription_plan_id": 1,
  "customer": {
    "name": "John Doe",
    "email": "john@example.com",
    "phone": "+880123456789"
  }
}

Option 4: Without Plan (Custom Subscription)

{
  "customer_id": 456,
  "amount": 299.00,
  "interval_count": 1,
  "interval_unit": "MONTH",
  "trial_period_days": 14,
  "meta_data": {
    "custom_field": "value"
  }
}

Parameters:

FieldTypeRequiredDescription
subscription_plan_idintegerNo*Plan ID to use (can be omitted if specifying custom values)
customer_idintegerNo*Existing customer ID
customerobjectNo*Customer info for get-or-create (name, email, phone)
amountdecimalNoOverride plan amount
currencystringNoCurrency code (default: BDT)
interval_countintegerNoOverride plan interval count
interval_unitstringNoOverride plan interval unit (DAY, WEEK, MONTH, YEAR)
trial_period_daysintegerNoOverride plan trial period
meta_dataobjectNoCustom metadata

*Either customer_id or customer object is required.


Managing Subscriptions

List Subscriptions

GET /api/v2/payments/subscriptions

Query Parameters:

ParameterTypeDefaultDescription
customer_idinteger-Filter by customer ID
subscription_statusstring-Filter by status (PENDING, TRIALING, ACTIVE, PAST_DUE, PAUSED, CANCELLED, EXPIRED)
pageinteger1Page number for pagination
per_pageinteger20Items per page (max: 100)

cURL Example:

curl -X GET "https://api.moneybag.com.bd/api/v2/payments/subscriptions?customer_id=456&subscription_status=ACTIVE&page=1&per_page=20" \
  -H "X-Merchant-API-Key: YOUR_API_KEY"

Response (200 OK):

{
  "success": true,
  "message": "Subscriptions retrieved successfully",
  "data": [
    {
      "id": 1,
      "uuid": "d93f6b94-gfc8-5ecd-92b3-732g4b3f4298",
      "merchant_id": 123,
      "customer_id": 456,
      "status": "ACTIVE",
      "amount": "499.00",
      "currency": "BDT",
      "next_billing_date": "2026-01-01T00:00:00Z",
      "billing_cycle_count": 2,
      "created_at": "2025-11-01T10:00:00Z"
    }
  ],
  "meta": {
    "total": 1,
    "page": 1,
    "per_page": 20
  }
}

Get Subscription

GET /api/v2/payments/subscriptions/{subscription_id}

cURL Example:

curl -X GET "https://api.moneybag.com.bd/api/v2/payments/subscriptions/d93f6b94-gfc8-5ecd-92b3-732g4b3f4298" \
  -H "X-Merchant-API-Key: YOUR_API_KEY"

Response (200 OK):

{
  "id": 1,
  "uuid": "d93f6b94-gfc8-5ecd-92b3-732g4b3f4298",
  "merchant_id": 123,
  "customer_id": 456,
  "subscription_plan_id": 1,
  "status": "ACTIVE",
  "amount": "499.00",
  "currency": "BDT",
  "interval_count": 1,
  "interval_unit": "MONTH",
  "next_billing_date": "2026-01-01T00:00:00Z",
  "billing_cycle_count": 2,
  "created_at": "2025-11-01T10:00:00Z",
  "customer": {
    "id": 456,
    "name": "John Doe",
    "email": "john@example.com",
    "phone": "+880123456789"
  }
}

Update Subscription

PATCH /api/v2/payments/subscriptions/{subscription_id}

Request Body:

All fields are optional. Only include fields you want to update.

{
  "amount": 599.00,
  "interval_count": 1,
  "interval_unit": "MONTH",
  "meta_data": {
    "upgraded": true,
    "upgrade_date": "2025-11-30"
  }
}

Pause Subscription

POST /api/v2/payments/subscriptions/{subscription_id}/pause

cURL Example:

curl -X POST "https://api.moneybag.com.bd/api/v2/payments/subscriptions/d93f6b94-gfc8-5ecd-92b3-732g4b3f4298/pause" \
  -H "X-Merchant-API-Key: YOUR_API_KEY"

Response (200 OK):

Returns the updated subscription with status: "PAUSED".

Note: Temporarily pauses billing. The subscription status changes to PAUSED.

Resume Subscription

POST /api/v2/payments/subscriptions/{subscription_id}/resume

cURL Example:

curl -X POST "https://api.moneybag.com.bd/api/v2/payments/subscriptions/d93f6b94-gfc8-5ecd-92b3-732g4b3f4298/resume" \
  -H "X-Merchant-API-Key: YOUR_API_KEY"

Response (200 OK):

Returns the updated subscription with status: "ACTIVE".

Note: Resumes a paused subscription. Billing continues from where it left off.

Cancel Subscription

POST /api/v2/payments/subscriptions/{subscription_id}/cancel

Request Body:

{
  "cancellation_reason": "Customer requested downgrade",
  "cancel_at_period_end": true
}

Parameters:

FieldTypeDefaultDescription
cancellation_reasonstring-Optional reason for cancellation
cancel_at_period_endbooleanfalsetrue = cancel at end of current period, false = cancel immediately

Billing & Invoices

Billing History

GET /api/v2/payments/subscriptions/{subscription_id}/billing-history

Query Parameters:

ParameterTypeDefaultDescription
pageinteger1Page number for pagination
per_pageinteger20Items per page (max: 100)

cURL Example:

curl -X GET "https://api.moneybag.com.bd/api/v2/payments/subscriptions/d93f6b94-gfc8-5ecd-92b3-732g4b3f4298/billing-history?page=1&per_page=20" \
  -H "X-Merchant-API-Key: YOUR_API_KEY"

Response (200 OK):

{
  "success": true,
  "message": "Billing history retrieved successfully",
  "data": [
    {
      "id": 1,
      "uuid": "e04g7c05-hgd9-6fed-03c4-843h5c4g5309",
      "subscription_id": 1,
      "billing_date": "2025-11-30T10:00:00Z",
      "amount": "499.00",
      "currency": "BDT",
      "status": "SUCCESS",
      "payment_session_id": "ps_abc123",
      "transaction_id": "TXN987654321",
      "created_at": "2025-11-30T10:00:00Z"
    }
  ],
  "meta": {
    "total": 1,
    "page": 1,
    "per_page": 20
  }
}

Generate Invoice

Manually generate an invoice for a subscription:

POST /api/v2/payments/subscriptions/{subscription_id}/generate-invoice

Use Cases:

  • Retry failed payments with a new invoice
  • Send payment reminders
  • Manual billing when automatic retry is disabled

cURL Example:

curl -X POST "https://api.moneybag.com.bd/api/v2/payments/subscriptions/d93f6b94-gfc8-5ecd-92b3-732g4b3f4298/generate-invoice" \
  -H "X-Merchant-API-Key: YOUR_API_KEY"

Response (200 OK):

{
  "invoice_id": "INV-2025-001",
  "uuid": "inv-uuid-here",
  "business_name": "Your Business",
  "invoice_type": "SUBSCRIPTION",
  "due_date": "2025-12-05T10:00:00Z",
  "payable_amount": 499.00,
  "invoice_status": "PENDING"
}

Note: The invoice will be created with the current subscription amount and billing period. An email will be automatically sent to the customer with the payment link.

Create Subscription Checkout

Create a checkout session for a subscription payment:

POST /api/v2/payments/subscriptions/{subscription_id}/checkout

Request Body:

{
  "success_url": "https://yoursite.com/success",
  "cancel_url": "https://yoursite.com/cancel",
  "fail_url": "https://yoursite.com/fail",
  "ipn_url": "https://yoursite.com/ipn",
  "metadata": {}
}

Parameters:

FieldTypeRequiredDescription
success_urlstringNoCallback URL after successful payment (uses merchant config default if not provided)
cancel_urlstringNoCallback URL if user cancels (uses merchant config default if not provided)
fail_urlstringNoCallback URL if payment fails (uses merchant config default if not provided)
ipn_urlstringNoInstant Payment Notification URL (uses merchant config default if not provided)
metadataobjectNoAdditional checkout metadata

Use Cases:

  • Collecting payment when trial period ends
  • Resuming paused subscriptions with payment
  • Retrying failed payments with new checkout
  • Manual billing with direct customer payment

Response:

{
  "success": true,
  "message": "Checkout session created successfully",
  "data": {
    "checkout_url": "https://checkout.moneybag.com.bd/...",
    "session_id": "sess_abc123",
    "expires_at": "2025-12-01T06:00:00Z"
  },
  "meta": {}
}

Billing Statuses

StatusDescription
PENDINGBilling scheduled, not yet processed
PROCESSINGPayment in progress
SUCCESSPayment successful
FAILEDPayment failed
REFUNDEDPayment was refunded

Webhooks

Set up webhooks to receive real-time notifications. See Webhook Integration Guide for full details.

Subscription Events

EventDescription
subscription.createdNew subscription created
subscription.trial_startedTrial period began
subscription.trial_endingTrial ending in 3 days
subscription.trial_endedTrial period ended
subscription.activatedSubscription now active
subscription.payment_succeededRecurring payment successful
subscription.payment_failedRecurring payment failed
subscription.cancelledSubscription cancelled
subscription.expiredSubscription expired
subscription.pausedSubscription paused
subscription.resumedSubscription resumed

Example Webhook Payload

{
  "event_id": "550e8400-e29b-41d4-a716-446655440000",
  "event_type": "subscription.payment_succeeded",
  "occurred_at": "2025-12-30T10:00:00Z",
  "merchant_id": 123,
  "data": {
    "subscription_uuid": "d93f6b94-gfc8-5ecd-92b3-732g4b3f4298",
    "subscription_id": 1,
    "customer_id": 456,
    "amount": "499.00",
    "currency": "BDT",
    "billing_cycle": 2,
    "payment_session_id": "ps_xyz789",
    "transaction_id": "TXN123456789",
    "next_billing_date": "2026-01-30T10:00:00Z"
  }
}

Implementation Examples

Python

import requests

BASE_URL = "https://api.moneybag.com.bd/api/v2/payments"
headers = {
    "X-Merchant-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json"
}

# Create a plan
plan_data = {
    "plan_name": "Pro Plan",
    "amount": 999.00,
    "interval_count": 1,
    "interval_unit": "MONTH",
    "trial_period_days": 7
}
response = requests.post(f"{BASE_URL}/subscription-plans", json=plan_data, headers=headers)
plan = response.json()

# Create a subscription
subscription_data = {
    "subscription_plan_id": plan["id"],
    "customer": {
        "name": "Jane Smith",
        "email": "jane@example.com",
        "phone": "+880198765432"
    }
}
response = requests.post(f"{BASE_URL}/subscriptions", json=subscription_data, headers=headers)
subscription = response.json()

print(f"Subscription created: {subscription['uuid']}")
print(f"Status: {subscription['status']}")
print(f"Trial ends: {subscription['trial_end_date']}")

Node.js

const axios = require('axios');

const BASE_URL = 'https://api.moneybag.com.bd/api/v2/payments';
const headers = {
  'X-Merchant-API-Key': 'YOUR_API_KEY',
  'Content-Type': 'application/json'
};

async function createSubscription() {
  // Create a plan
  const planResponse = await axios.post(`${BASE_URL}/subscription-plans`, {
    plan_name: 'Pro Plan',
    amount: 999.00,
    interval_count: 1,
    interval_unit: 'MONTH',
    trial_period_days: 7
  }, { headers });

  const plan = planResponse.data;

  // Create a subscription
  const subResponse = await axios.post(`${BASE_URL}/subscriptions`, {
    subscription_plan_id: plan.id,
    customer: {
      name: 'Jane Smith',
      email: 'jane@example.com',
      phone: '+880198765432'
    }
  }, { headers });

  const subscription = subResponse.data;

  console.log(`Subscription created: ${subscription.uuid}`);
  console.log(`Status: ${subscription.status}`);
  console.log(`Trial ends: ${subscription.trial_end_date}`);
}

createSubscription();

PHP

<?php
$baseUrl = "https://api.moneybag.com.bd/api/v2/payments";
$headers = [
    "X-Merchant-API-Key: YOUR_API_KEY",
    "Content-Type: application/json"
];

// Create a plan
$planData = json_encode([
    "plan_name" => "Pro Plan",
    "amount" => 999.00,
    "interval_count" => 1,
    "interval_unit" => "MONTH",
    "trial_period_days" => 7
]);

$ch = curl_init("$baseUrl/subscription-plans");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $planData);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$plan = json_decode(curl_exec($ch), true);
curl_close($ch);

// Create a subscription
$subData = json_encode([
    "subscription_plan_id" => $plan["id"],
    "customer" => [
        "name" => "Jane Smith",
        "email" => "jane@example.com",
        "phone" => "+880198765432"
    ]
]);

$ch = curl_init("$baseUrl/subscriptions");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $subData);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$subscription = json_decode(curl_exec($ch), true);
curl_close($ch);

echo "Subscription created: " . $subscription["uuid"] . "\n";
echo "Status: " . $subscription["status"] . "\n";
echo "Trial ends: " . $subscription["trial_end_date"] . "\n";

Common Use Cases

Trial Ending - Collect Payment

When a trial is about to end, create a checkout session for the customer to pay:

curl -X POST "https://api.moneybag.com.bd/api/v2/payments/subscriptions/d93f6b94-gfc8-5ecd-92b3-732g4b3f4298/checkout" \
  -H "X-Merchant-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "success_url": "https://yoursite.com/subscription/success",
    "fail_url": "https://yoursite.com/subscription/failed"
  }'

Failed Payment Retry

Generate a new invoice and send to customer:

curl -X POST "https://api.moneybag.com.bd/api/v2/payments/subscriptions/d93f6b94-gfc8-5ecd-92b3-732g4b3f4298/generate-invoice" \
  -H "X-Merchant-API-Key: YOUR_API_KEY"

Resume Paused Subscription with Payment

# First resume the subscription
resume_response = requests.post(
    f"{BASE_URL}/subscriptions/{subscription_uuid}/resume",
    headers=headers
)

# Then create checkout for payment
checkout_response = requests.post(
    f"{BASE_URL}/subscriptions/{subscription_uuid}/checkout",
    json={
        "success_url": "https://yoursite.com/success",
        "fail_url": "https://yoursite.com/fail"
    },
    headers=headers
)

checkout_url = checkout_response.json()['data']['checkout_url']
# Redirect customer to checkout_url

Subscription Plan Examples

1. SaaS Monthly Billing

{
  "plan_name": "Professional",
  "amount": 1999.00,
  "interval_count": 1,
  "interval_unit": "MONTH",
  "trial_period_days": 14
}

2. Annual Subscription with Discount

{
  "plan_name": "Professional Annual",
  "amount": 19990.00,
  "interval_count": 1,
  "interval_unit": "YEAR",
  "trial_period_days": 30
}

3. Weekly Service

{
  "plan_name": "Weekly Delivery",
  "amount": 299.00,
  "interval_count": 1,
  "interval_unit": "WEEK"
}

4. Quarterly Billing

{
  "plan_name": "Quarterly Plan",
  "amount": 2499.00,
  "interval_count": 3,
  "interval_unit": "MONTH"
}

5. Paid Trial

{
  "plan_name": "Premium with Paid Trial",
  "amount": 999.00,
  "interval_count": 1,
  "interval_unit": "MONTH",
  "trial_period_days": 7,
  "trial_amount": 99.00
}

Best Practices

1. Use Webhooks for Status Updates

Don't poll the API for subscription status. Set up webhooks to receive real-time updates:

# Webhook handler
@app.route('/webhooks/moneybag', methods=['POST'])
def handle_webhook():
    data = request.json
    event_type = data['event_type']

    if event_type == 'subscription.payment_succeeded':
        # Extend user access
        extend_user_access(data['data']['customer_id'])

    elif event_type == 'subscription.payment_failed':
        # Notify user, show payment update UI
        notify_payment_failed(data['data']['customer_id'])

    elif event_type == 'subscription.cancelled':
        # Revoke access at period end
        schedule_access_revocation(data['data'])

    return '', 200

2. Handle Payment Failures Gracefully

def handle_payment_failure(subscription_uuid):
    # Get subscription details
    subscription = get_subscription(subscription_uuid)

    if subscription['failed_payment_count'] < 3:
        # Send reminder email
        send_payment_reminder(subscription['customer'])
    else:
        # Consider downgrading or restricting access
        restrict_features(subscription['customer_id'])

3. Implement Idempotency

Use subscription_uuid or event_id to prevent duplicate processing:

def process_payment_success(event_data):
    event_id = event_data['event_id']

    if is_already_processed(event_id):
        return  # Skip duplicate

    # Process the event
    update_user_subscription(event_data)

    # Mark as processed
    mark_processed(event_id)

4. Store Plan References

Store the subscription_uuid in your database:

# In your user/account table
user = User(
    email="john@example.com",
    moneybag_subscription_uuid="d93f6b94-gfc8-5ecd-92b3-732g4b3f4298",
    subscription_status="ACTIVE"
)

5. Offer Trial Periods

Trials significantly increase conversion:

{
  "plan_name": "Premium",
  "amount": 999.00,
  "interval_count": 1,
  "interval_unit": "MONTH",
  "trial_period_days": 14
}

Error Handling

Common Errors

Error CodeDescriptionSolution
400Invalid requestCheck request body format
401UnauthorizedVerify API key/token
404Not foundCheck UUID/ID
409ConflictResource already exists
422Validation errorCheck field values

Example Error Response

{
  "detail": "Subscription plan not found",
  "code": "PLAN_NOT_FOUND",
  "status_code": 404
}

Handling Errors

try:
    response = create_subscription(data)
except requests.HTTPError as e:
    if e.response.status_code == 404:
        # Plan doesn't exist
        handle_missing_plan()
    elif e.response.status_code == 422:
        # Validation error
        errors = e.response.json()
        handle_validation_errors(errors)
    else:
        # Other error
        log_error(e)
        raise

FAQ

Q: Can I change a subscription's amount after creation?

Yes, use the PATCH endpoint:

PATCH /api/v2/payments/subscriptions/{subscription_id}
{"amount": 599.00}

Q: What happens when a subscription trial ends?

The system automatically charges the customer the full subscription amount. If successful, the status changes to ACTIVE. If failed, it changes to PAST_DUE and retries begin.

Q: Can a customer have multiple subscriptions?

Yes, a customer can have multiple subscriptions to different plans.

Q: How do I offer a discount?

Override the amount when creating the subscription:

{
  "subscription_plan_id": 1,
  "customer_id": 456,
  "amount": 399.00
}

Q: What happens if I deactivate a plan?

Existing subscriptions continue to work. Only new subscriptions cannot use the deactivated plan.

Q: Can I pause a subscription during trial?

No, pausing only works for ACTIVE subscriptions. During trial, you can cancel instead.


API Reference

Plans

EndpointMethodDescription
/payments/subscription-plansPOSTCreate plan
/payments/subscription-plansGETList plans
/payments/subscription-plans/{plan_id}GETGet plan
/payments/subscription-plans/{plan_id}PATCHUpdate plan
/payments/subscription-plans/{plan_id}DELETEArchive plan
/payments/subscription-plans/{plan_id}/restorePOSTRestore archived plan

Subscriptions

EndpointMethodDescription
/payments/subscriptionsPOSTCreate subscription
/payments/subscriptionsGETList subscriptions
/payments/subscriptions/{subscription_id}GETGet subscription
/payments/subscriptions/{subscription_id}PATCHUpdate subscription
/payments/subscriptions/{subscription_id}/pausePOSTPause subscription
/payments/subscriptions/{subscription_id}/resumePOSTResume subscription
/payments/subscriptions/{subscription_id}/cancelPOSTCancel subscription
/payments/subscriptions/{subscription_id}/billing-historyGETGet billing history
/payments/subscriptions/{subscription_id}/generate-invoicePOSTGenerate invoice manually
/payments/subscriptions/{subscription_id}/checkoutPOSTCreate checkout session

Support