Skip to main content

Overview

Subscription APIs enable you to manage recurring payment agreements with customers. Subscriptions automate billing cycles for products or services, creating predictable recurring revenue for your business.

What is a Subscription?

A subscription represents a recurring payment agreement between you and your customer for a product or service. Subscriptions are created automatically when a customer completes a payment for a product with a selling plan (subscription plan). The Subscription API allows you to manage subscription lifecycles, including pausing, resuming, and canceling subscriptions.

Each subscription:

  • 🔄 Automates recurring billing cycles (weekly, monthly, yearly)
  • 💰 Links to a product with a selling plan
  • 👤 Associates with a customer
  • 📅 Tracks billing periods and payment status
  • 🏷️ Supports metadata for custom tracking

🔗 Related APIs: Subscriptions are created from Products with selling plans, linked to Customers, and initiated through Payment Intents.

Key Features

FeatureDescription
Automatic BillingCharges customers automatically each billing cycle
Lifecycle ManagementPause, resume, cancel subscriptions
Flexible PlansSupport weekly, monthly, yearly billing intervals
Payment TrackingMonitor payment status and history
Grace PeriodsHandle failed payments with retry logic
ProrationHandle mid-cycle plan changes with automatic pro-rata calculation
Webhook NotificationsReal-time updates on subscription events

Use Cases

Common Scenarios:

  • 💼 SaaS Products: Monthly/annual software subscriptions
  • 📰 Content Platforms: News, media, streaming subscriptions
  • 🏋️ Memberships: Gym, club, community memberships
  • 📦 Subscription Boxes: Monthly product deliveries
  • 🎓 Online Courses: Recurring access to educational content
  • 🛡️ Protection Plans: Extended warranties, insurance

How Subscriptions Work

Create Product → Add Selling Plan → Customer Pays → Subscription Active → Auto-Billing
↓ ↓ ↓ ↓ ↓
Product Subscription Payment Active Recurring
(1-time) Plan Created Complete Status Charges

Workflow:

  1. Create Product with selling plan (subscription option)
  2. Customer Subscribes via Payment Link or Payment Intent
  3. Initial Payment processed to activate subscription
  4. Subscription Active begins billing cycle
  5. Automatic Billing at each cycle (weekly/monthly/yearly)
  6. Manage Lifecycle pause, resume, or cancel as needed

Important Notes

⚠️ Subscriptions Are Auto-Created: There is NO direct POST /subscriptions endpoint. Subscriptions are created automatically when a customer completes payment for a product with a selling plan.

Creation Methods:


Introduction to Subscriptions

Subscriptions in MartianPay enable recurring revenue through automated billing cycles. Key concepts include:

Subscription Lifecycle

Subscriptions go through several states during their lifecycle:

  1. incomplete: Subscription created but initial payment not yet completed

    • Has payment_required: true and a payment_url for customer payment
    • Automatically expires after a set period if not paid
    • Check payment_expires_at and hours_since_creation for urgency
  2. active: Subscription is active and billing normally

    • Customer has completed payment
    • Recurring charges happen automatically at each billing cycle
    • current_period_start and current_period_end define the billing period
  3. paused: Subscription temporarily paused

    • No charges generated during pause period
    • Can be configured to auto-resume at resumes_at timestamp
    • pause_collection_behavior determines how pending invoices are handled
  4. past_due: Payment failed but subscription not yet canceled

    • Recent payment attempt failed
    • System may retry payment automatically
    • Check latest_invoice_id for failed invoice details
  5. canceled: Subscription has been canceled

    • No future charges will be created
    • canceled_at shows when cancellation occurred
    • cancel_reason may contain customer-provided reason
    • If cancel_at_period_end: true, subscription remains active until period ends

How Subscriptions Are Created

Important: There is NO direct POST /subscriptions endpoint. Subscriptions are created automatically through one of the following methods:

Method 1: Payment Intent with Selling Plan (Backend API)

Create a payment intent that references a payment link with a selling plan product:

curl --location --request POST 'https://api.martianpay.com/v1/payment_intents' \
--header 'Content-Type: application/json' \
--header 'Authorization: Basic {BASE64_ENCODE(api_key + ":")}' \
--data '{
"payment_link_id": "plink_abc123",
"primary_variant": {
"quantity": 1,
"variant_id": "var_xyz789",
"selling_plan_id": "sp_plan123"
},
"addons": [],
"description": "Monthly subscription plan",
"receipt_email": "customer@example.com",
"return_url": "https://yoursite.com/thank-you",
"complete_on_first_payment": true,
"product_version": 12,
"shipping_address": {
"line1": "123 Main St",
"line2": "Apt 4B",
"city": "San Francisco",
"state": "CA",
"postal_code": "94102",
"country": "US"
},
"customer": "cus_existingCustomer123",
"metadata": {
"order_id": "order_123",
"source": "website"
},
"merchant_order_id": "order_123"
}'

Key Fields for Subscription Creation:

  • payment_link_id: ID of the payment link containing the subscription product
  • selling_plan_id: The specific subscription plan the customer selected
  • variant_id: The product variant being subscribed to
  • complete_on_first_payment: Set to true to create subscription immediately after first payment
  • product_version: Version of the product configuration
  • customer: Existing customer ID (or omit to create new customer)
  • metadata: Custom data for tracking (keys and values are flexible)
  • merchant_order_id: Your unique order identifier

Payment Processing After Creating Payment Intent:

After creating the payment intent with the request above, the payment processing flow is identical to the standard payment flow documented in Payment Intent APIs:

  1. Update Payment Method - Customer selects crypto or fiat payment method
  2. Payment Execution - Customer completes payment (blockchain transfer or card payment)
  3. Payment Confirmation - System confirms payment and updates payment intent status
  4. Subscription Creation - Once payment is confirmed, subscription is automatically created

For detailed payment processing steps, including:

  • Updating payment method (crypto/fiat selection)
  • Handling payment confirmation
  • Tracking payment status
  • Error handling

Please refer to the How to Use Payment APIs documentation, specifically:

  • Section 2: Update Payment Intent (for payment method selection)
  • Section 4: Get Payment Intent (for checking payment status)
  • Section 6: Cancel Payment Intent (if needed)

The only difference is that for subscription products, a subscription will be automatically created after successful payment completion.

Customers can directly visit and pay on a payment link URL that contains subscription products:

  1. Create a payment link with a subscription product (see Payment Link APIs)
  2. Share the payment link URL with your customer (e.g., https://buy.martianpay.com/{payment_link_id})
  3. Customer Authentication: Customer enters their email address and receives an OTP (One-Time Password) code
  4. Customer enters the OTP code to log in
  5. Customer selects a selling plan and completes payment
  6. Subscription is automatically created upon successful payment

Note:

  • When using payment links, the customer can choose from available selling plans if the product has multiple subscription options
  • This method requires customer email authentication via OTP for security
  • If you want to skip the email/OTP login step, use Method 3 (Ephemeral Token) below

For seamless integration with external authentication systems (Instagram, WhatsApp, your own platform), you can use ephemeral tokens to authenticate customers automatically without requiring them to log in again.

Step 1: Generate Ephemeral Token

curl --location --request POST 'https://api.martianpay.com/v1/customers/ephemeral_tokens' \
--header 'Content-Type: application/json' \
--header 'Authorization: Basic {BASE64_ENCODE(api_key + ":")}' \
--data '{
"allow_create": true,
"channel_metadata": {
"platform_user_id": "user_12345",
"source": "whatsapp_bot"
},
"idp_key": "email",
"idp_subject": "customer@example.com",
"issued_by": "whatsapp_commerce_bot",
"provider": "whatsapp",
"return_url": "https://example.com/subscription/success"
}'

Response:

{
"expires_at": 1735689600,
"token": "eph_abc123xyz789"
}

Step 2: Share Payment Link with Token

Append the ephemeral token to your payment link URL:

https://buy.martianpay.com/{payment_link_id}?ephemeral_token=eph_abc123xyz789

Step 3: Customer Completes Payment

When the customer clicks the link:

  1. They're automatically authenticated (no login required)
  2. They see the subscription product and select a selling plan
  3. They complete payment through the hosted checkout
  4. Subscription is automatically created upon successful payment

Step 4: Track Subscription Status

After successful payment, you have two ways to track the created subscription:

  1. Via Webhook (Recommended):

    • Listen for payment_intent.succeeded webhook event
    • Extract merchant_order_id from the payment intent payload
    • Use this merchant_order_id to map the payment to your order system
  2. Via List Subscription API:

    • Query subscriptions using the external_id parameter
    • Example:
    curl --location --request GET 'https://api.martianpay.com/v1/subscriptions?external_id={order_id}' \
    --header 'Authorization: Basic {BASE64_ENCODE(api_key + ":")}'
    • This returns all subscriptions associated with the specified order_id
    • Useful for reconciliation and status checking after payment

Benefits:

  • No additional login step - seamless customer experience
  • Works with any external identity provider (email, phone, or UUID-based)
  • Supports anonymous tokens for order-only tracking (without customer identity)
  • Secure with short-lived tokens (5-15 minutes)
  • Auto-creates new customers if they don't exist (allow_create: true)
  • Tracks platform-specific data through channel_metadata
  • Can associate tokens with specific orders via order_id parameter

Use Cases:

  • Instagram Commerce: Send payment links in DMs with auto-authentication
  • WhatsApp Business: Share subscription links without customer login
  • Telegram Bots: Offer subscriptions with one-click checkout
  • Custom Platforms: Integrate MartianPay subscriptions into your existing user system

Subscription Creation Flow

After successful payment:

  1. Payment intent status changes to Completed or Confirmed
  2. A new subscription is automatically created with status: active
  3. If payment fails or is incomplete, subscription status will be incomplete
  4. You'll receive a subscription.created webhook event
  5. The subscription begins its billing cycle based on the selling plan configuration

Subscription Components

Each subscription contains:

  • Customer Information: Customer ID, name, and email for the subscriber
  • Product Details: Product and variant information including images and descriptions
  • Selling Plan: The subscription plan defining billing frequency and pricing
  • Billing Cycle: Current cycle number, period start/end, and billing anchor
  • Pricing Information: Current and upcoming pricing tiers with discounts
  • Payment Method: Default payment method for recurring charges
  • Trial Period: Optional trial start and end dates
  • Metadata: Custom key-value pairs for additional information

Billing and Pricing

Subscriptions support complex pricing scenarios:

  • Trial Periods: Optional free or discounted trial before regular billing starts
  • Pricing Tiers: Different pricing for different billing cycles (e.g., first month discount)
  • Billing Cycles: Monthly, quarterly, yearly, or custom intervals
  • Discounts: Percentage or fixed amount discounts applied to base price
  • Current vs Upcoming Pricing: Track current cycle price and next cycle price

2. List Subscriptions

Retrieves a paginated list of all subscriptions for your merchant account. Supports filtering by customer and status.

For detailed API reference, see: List Subscriptions API

Request:

curl --location --request GET 'https://api.martianpay.com/v1/subscriptions?offset=0&limit=20' \
--header 'Authorization: Basic {BASE64_ENCODE(api_key + ":")}'

Request with Filters:

curl --location --request GET 'https://api.martianpay.com/v1/subscriptions?customer_id=cus_abc123&status=active&offset=0&limit=20' \
--header 'Authorization: Basic {BASE64_ENCODE(api_key + ":")}'

Query Parameters:

  • customer_id (optional): Filter subscriptions by specific customer ID
  • status (optional): Filter by status (incomplete, active, paused, past_due, canceled)
  • external_id (optional): Filter by external ID for linking to your system
  • offset (optional): Pagination offset (default: 0)
  • limit (optional): Number of items per page (default: 20, max: 100)

Response:

{
"error_code": "success",
"msg": "success",
"data": {
"offset": 0,
"limit": 20,
"total": 45,
"data": [
{
"id": "sub_abc123xyz",
"merchant_id": "mer_def456",
"merchant_name": "My Store",
"customer_id": "cus_789ghi",
"customer_name": "John Doe",
"customer_email": "john@example.com",
"product_id": "prod_jkl012",
"product_name": "Premium Monthly Box",
"product_description": "Curated monthly subscription box",
"product_image_url": "https://cdn.example.com/product.jpg",
"variant_id": "var_mno345",
"variant_title": "Standard / Monthly",
"variant_option_values": {
"Size": "Standard",
"Frequency": "Monthly"
},
"variant_price": "49.99",
"selling_plan_id": "sp_pqr678",
"selling_plan_name": "Monthly Subscription",
"selling_plan_description": "Billed monthly, cancel anytime",
"selling_plan_pricing": {
"selling_plan_id": "sp_pqr678",
"selling_plan_name": "Monthly Subscription",
"billing_cycle": "month",
"currency": "USD",
"trial_period_days": 7,
"pricing_tiers": [
{
"policy_type": "FIXED",
"after_cycle": 1,
"cycle_description": "Cycle 1",
"total_cycles": 1,
"base_price": "49.99",
"selling_plan_discount": "10.00",
"subtotal_before_policy": "49.99",
"subtotal_after_policy": "39.99"
},
{
"policy_type": "RECURRING",
"after_cycle": 2,
"cycle_description": "Cycle 2 onwards",
"total_cycles": 0,
"base_price": "49.99",
"selling_plan_discount": "5.00",
"subtotal_before_policy": "49.99",
"subtotal_after_policy": "44.99"
}
]
},
"status": "active",
"collection_method": "charge_automatically",
"default_payment_method_id": "pm_stu901",
"default_payment_method_type": "card",
"default_provider_type": "stripe",
"payment_method_brand": "visa",
"payment_method_last4": "4242",
"external_id": "ext_vwx234",
"metadata": {
"source": "website",
"campaign": "summer2024"
},
"created_at": 1704067200,
"updated_at": 1704067200,
"billing_cycle_anchor": 1704067200,
"current_period_start": 1704067200,
"current_period_end": 1706745600,
"trial_start": 1704067200,
"trial_end": 1704672000,
"canceled_at": null,
"cancel_reason": null,
"cancel_at_period_end": false,
"paused_at": null,
"pause_collection_behavior": null,
"resumes_at": null,
"latest_invoice_id": "inv_yza567",
"current_cycle_number": 1,
"current_pricing_tier": {
"policy_type": "FIXED",
"cycle_number": 1,
"cycle_description": "Cycle 1",
"billing_cycle": "month",
"billing_cycle_interval": 1,
"currency": "USD",
"base_price": "49.99",
"selling_plan_discount": "10.00",
"discount_percentage": "20",
"final_price": "39.99"
},
"upcoming_pricing_tier": {
"policy_type": "RECURRING",
"cycle_number": 2,
"cycle_description": "Cycle 2 onwards",
"billing_cycle": "month",
"billing_cycle_interval": 1,
"currency": "USD",
"base_price": "49.99",
"selling_plan_discount": "5.00",
"discount_percentage": "10",
"final_price": "44.99"
},
"next_charge_amount": "44.99",
"next_charge_amount_display": "$44.99",
"payment_required": false,
"payment_url": null,
"payment_expires_at": null,
"hours_since_creation": 240
}
]
}
}

Understanding the Response

How to Check if the Request Was Successful:

FieldValueMeaning
error_code"success"✅ Subscriptions retrieved successfully
error_codeOther value❌ Error occurred

Key Fields:

  • total: Total number of subscriptions matching your query
  • offset: Current offset position
  • limit: Number of items per page
  • data: Array of subscription objects

Subscription Status Summary:

StatusMeaningAction
incomplete⏳ Awaiting first paymentSend payment reminder to customer
active✅ Active and billing normallyNo action needed
paused⏸️ Temporarily pausedCan resume when ready
past_due⚠️ Payment failedUpdate payment method or retry
canceled❌ Canceled, no future chargesNo action possible

Pagination:

  • Use offset and limit to navigate through results
  • If total > limit, there are more pages available
  • Next page: increase offset by limit value

Filtering:

  • Filter by customer_id to see all subscriptions for a customer
  • Filter by status to find subscriptions in specific states
  • Filter by external_id to link to your order system
  • Combine filters to narrow results

Incomplete Subscriptions: If payment_required: true:

  • Customer needs to complete payment via payment_url
  • Check payment_expires_at for expiration time
  • Use hours_since_creation to prioritize follow-ups

Next Steps:

  • Loop through data array to process each subscription
  • Monitor status field to track subscription lifecycle
  • Use subscription id for cancel, pause, or resume operations
  • Display next_charge_amount to show upcoming billing

Important Notes:

  1. Pagination: Use offset and limit to navigate through large subscription lists. The total field shows the total number of subscriptions matching the filter.

  2. Filtering:

    • Filter by customer_id to see all subscriptions for a specific customer
    • Filter by status to find subscriptions in specific states (e.g., all paused subscriptions)
    • Combine filters to narrow results (e.g., active subscriptions for a customer)
  3. Display Information: The response includes denormalized data for easy display:

    • Customer name and email
    • Product name, description, and image URL
    • Variant title and option values
    • Payment method brand and last 4 digits
    • Merchant information
  4. Pricing Information:

    • current_pricing_tier shows pricing for the current billing cycle
    • upcoming_pricing_tier shows pricing that will apply in the next cycle
    • next_charge_amount shows the expected amount for the next payment
  5. Incomplete Subscriptions: Subscriptions with status: incomplete have:

    • payment_required: true
    • payment_url for customer to complete payment
    • payment_expires_at timestamp for expiration
    • hours_since_creation to track how long it's been pending

3. Get Subscription

Retrieves detailed information about a specific subscription, including complete billing history, pricing tiers, and payment method details.

For detailed API reference, see: Get Subscription API

Request:

curl --location --request GET 'https://api.martianpay.com/v1/subscriptions/sub_abc123xyz' \
--header 'Authorization: Basic {BASE64_ENCODE(api_key + ":")}'

Response:

{
"error_code": "success",
"msg": "success",
"data": {
"id": "sub_abc123xyz",
"merchant_id": "mer_def456",
"merchant_name": "My Store",
"customer_id": "cus_789ghi",
"customer_name": "John Doe",
"customer_email": "john@example.com",
"product_id": "prod_jkl012",
"product_name": "Premium Monthly Box",
"product_description": "Curated monthly subscription box",
"product_image_url": "https://cdn.example.com/product.jpg",
"variant_id": "var_mno345",
"variant_title": "Standard / Monthly",
"variant_option_values": {
"Size": "Standard",
"Frequency": "Monthly"
},
"variant_price": "49.99",
"selling_plan_id": "sp_pqr678",
"selling_plan_name": "Monthly Subscription",
"selling_plan_description": "Billed monthly, cancel anytime",
"selling_plan_pricing": {
"selling_plan_id": "sp_pqr678",
"selling_plan_name": "Monthly Subscription",
"billing_cycle": "month",
"currency": "USD",
"trial_period_days": 7,
"pricing_tiers": [
{
"policy_type": "FIXED",
"after_cycle": 1,
"cycle_description": "Cycle 1",
"total_cycles": 1,
"base_price": "49.99",
"selling_plan_discount": "10.00",
"subtotal_before_policy": "49.99",
"subtotal_after_policy": "39.99"
},
{
"policy_type": "RECURRING",
"after_cycle": 2,
"cycle_description": "Cycle 2 onwards",
"total_cycles": 0,
"base_price": "49.99",
"selling_plan_discount": "5.00",
"subtotal_before_policy": "49.99",
"subtotal_after_policy": "44.99"
}
]
},
"status": "active",
"collection_method": "charge_automatically",
"default_payment_method_id": "pm_stu901",
"default_payment_method_type": "card",
"default_provider_type": "stripe",
"payment_method_brand": "visa",
"payment_method_last4": "4242",
"external_id": "ext_vwx234",
"metadata": {
"source": "website",
"campaign": "summer2024"
},
"created_at": 1704067200,
"updated_at": 1704067200,
"billing_cycle_anchor": 1704067200,
"current_period_start": 1704067200,
"current_period_end": 1706745600,
"trial_start": 1704067200,
"trial_end": 1704672000,
"canceled_at": null,
"cancel_reason": null,
"cancel_at_period_end": false,
"paused_at": null,
"pause_collection_behavior": null,
"resumes_at": null,
"latest_invoice_id": "inv_yza567",
"current_cycle_number": 1,
"current_pricing_tier": {
"policy_type": "FIXED",
"cycle_number": 1,
"cycle_description": "Cycle 1",
"billing_cycle": "month",
"billing_cycle_interval": 1,
"currency": "USD",
"base_price": "49.99",
"selling_plan_discount": "10.00",
"discount_percentage": "20",
"final_price": "39.99"
},
"upcoming_pricing_tier": {
"policy_type": "RECURRING",
"cycle_number": 2,
"cycle_description": "Cycle 2 onwards",
"billing_cycle": "month",
"billing_cycle_interval": 1,
"currency": "USD",
"base_price": "49.99",
"selling_plan_discount": "5.00",
"discount_percentage": "10",
"final_price": "44.99"
},
"next_charge_amount": "44.99",
"next_charge_amount_display": "$44.99",
"payment_required": false,
"payment_url": null,
"payment_expires_at": null,
"hours_since_creation": 240
}
}

Understanding the Response

How to Check if the Request Was Successful:

FieldValueMeaning
error_code"success"✅ Subscription retrieved successfully
error_code"subscription_not_found"❌ No subscription exists with this ID
error_codeOther value❌ Other error occurred

Key Subscription Information:

FieldDescription
statusCurrent subscription status (incomplete, active, paused, past_due, canceled)
current_cycle_numberWhich billing cycle the subscription is in
current_period_start / current_period_endCurrent billing period dates
next_charge_amountExpected amount for next payment
current_pricing_tierPricing for current billing cycle
upcoming_pricing_tierPricing that will apply in next cycle

Pricing Information:

  • current_pricing_tier: Shows pricing for the current billing cycle with discounts applied
  • upcoming_pricing_tier: Preview of pricing for next cycle (may differ if tiered pricing changes)
  • selling_plan_pricing: Full pricing tier breakdown for all cycles
  • trial_period_days: If set, shows trial duration before first charge

Billing Cycle Tracking:

  • billing_cycle_anchor: Reference date for billing cycles
  • current_period_start / current_period_end: Current billing period
  • Use these dates to calculate when next charge will occur

Status Indicators:

StatusMeaningWhat to Do
incomplete⏳ Awaiting first paymentDirect customer to payment_url
active✅ Active and billingMonitor for upcoming charges
paused⏸️ Temporarily pausedCan resume anytime
past_due⚠️ Payment failedUpdate payment method
canceled❌ CanceledCannot be reactivated

Trial Period: If subscription has a trial:

  • Check trial_start and trial_end dates
  • First charge occurs after trial ends
  • trial_period_days shows trial length

Next Steps:

  • Display subscription details to customer in your dashboard
  • Show next_charge_amount and current_period_end for transparency
  • If status: incomplete, prompt customer to complete payment
  • Use cancel_at_period_end to show if subscription will end soon
  • Check paused_at and resumes_at for paused subscriptions

Important Notes:

  1. Complete Subscription Details: The response includes all subscription information including:

    • Complete customer, product, and variant details
    • Full selling plan pricing tier breakdown
    • Current and upcoming billing cycle information
    • Payment method details
    • Trial period dates if applicable
  2. Pricing Tiers: The selling_plan_pricing.pricing_tiers array shows:

    • FIXED tiers: Apply to specific cycles (e.g., first month discount)
    • RECURRING tiers: Apply to ongoing cycles (e.g., cycle 2 onwards)
    • Discount amounts and percentages
    • Subtotals before and after discounts
  3. Billing Cycle Information:

    • billing_cycle_anchor: The reference date for billing cycles
    • current_period_start and current_period_end: Current billing period
    • current_cycle_number: Which cycle the subscription is currently in
    • Use these to understand when the next charge will occur
  4. Trial Periods: If the subscription has a trial:

    • trial_start and trial_end show trial period dates
    • trial_period_days in selling_plan_pricing shows trial length
    • First charge occurs after trial ends
  5. Status Indicators: Check these fields to understand subscription state:

    • status: Current subscription status
    • cancel_at_period_end: If true, subscription ends at period end
    • paused_at: Timestamp when subscription was paused
    • resumes_at: Timestamp when paused subscription will auto-resume

4. Cancel Subscription

Cancels a subscription either immediately or at the end of the current billing period. Canceled subscriptions cannot be reactivated.

For detailed API reference, see: Cancel Subscription API

Request (Cancel at Period End - Default):

curl --location --request POST 'https://api.martianpay.com/v1/subscriptions/sub_abc123xyz/cancel' \
--header 'Content-Type: application/json' \
--header 'Authorization: Basic {BASE64_ENCODE(api_key + ":")}' \
--data-raw '{
"cancel_at_period_end": true,
"cancel_reason": "Customer requested cancellation"
}'

Request (Cancel Immediately):

curl --location --request POST 'https://api.martianpay.com/v1/subscriptions/sub_abc123xyz/cancel' \
--header 'Content-Type: application/json' \
--header 'Authorization: Basic {BASE64_ENCODE(api_key + ":")}' \
--data-raw '{
"cancel_at_period_end": false,
"cancel_reason": "Payment failure"
}'

Request Body Parameters:

  • cancel_at_period_end (optional):
    • true: Cancel at the end of current billing period (default)
    • false: Cancel immediately
  • cancel_reason (optional): Reason for cancellation (stored for analytics)

Response:

{
"error_code": "success",
"msg": "success",
"data": {
"id": "sub_abc123xyz",
"merchant_id": "mer_def456",
"merchant_name": "My Store",
"customer_id": "cus_789ghi",
"customer_name": "John Doe",
"customer_email": "john@example.com",
"product_id": "prod_jkl012",
"product_name": "Premium Monthly Box",
"product_description": "Curated monthly subscription box",
"product_image_url": "https://cdn.example.com/product.jpg",
"variant_id": "var_mno345",
"variant_title": "Standard / Monthly",
"status": "active",
"collection_method": "charge_automatically",
"default_payment_method_id": "pm_stu901",
"canceled_at": 1706745600,
"cancel_reason": "Customer requested cancellation",
"cancel_at_period_end": true,
"current_period_start": 1704067200,
"current_period_end": 1706745600,
"billing_cycle_anchor": 1704067200,
"metadata": {},
"created_at": 1704067200,
"updated_at": 1706745600
}
}

Understanding the Response

How to Check if the Request Was Successful:

FieldValueMeaning
error_code"success"✅ Subscription canceled successfully
error_code"subscription_not_found"❌ No subscription exists with this ID
error_code"subscription_already_canceled"❌ Subscription is already canceled
error_codeOther value❌ Other error occurred

Key Cancellation Fields:

FieldDescription
statusRemains active if cancel_at_period_end=true, changes to canceled if false
canceled_atTimestamp when cancellation was initiated
cancel_reasonReason provided for cancellation (stored for analytics)
cancel_at_period_endIf true, subscription ends at period end; if false, ends immediately
current_period_endWhen subscription will actually end (if cancel_at_period_end=true)

Cancellation Behavior:

cancel_at_period_endImmediate EffectEnd Result
true (recommended)Stays active until period endCustomer keeps access, no future charges after period ends
falseChanges to canceled immediatelyCustomer loses access now, no automatic refund issued

What Happens After Cancellation:

If cancel_at_period_end: true:

  • ✅ Subscription remains active until current_period_end
  • ✅ Customer can continue using service
  • ✅ No more charges after period ends
  • ✅ Status changes to canceled automatically at period end

If cancel_at_period_end: false:

  • ❌ Subscription becomes canceled immediately
  • ❌ Customer loses access immediately
  • ⚠️ No automatic refund (issue manually if needed)
  • ⚠️ Use for fraud, violations, or customer-requested immediate cancellation

Important Considerations:

  • Cannot Be Reactivated: Once canceled, subscription cannot be resumed
  • Historical Record: Canceled subscriptions remain in history for record-keeping
  • Customer Communication: Send confirmation email with cancellation details
  • Refunds: Handle refunds separately using Refund API if needed

Next Steps:

  • Send cancellation confirmation to customer
  • If cancel_at_period_end: true, remind customer they have access until current_period_end
  • If cancel_at_period_end: false, confirm immediate cancellation
  • Use cancel_reason for analytics to reduce churn
  • Consider offering retention incentives before final cancellation

Important Notes:

  1. Cancel at Period End (Recommended):

    • Set cancel_at_period_end: true to let customer use service until period ends
    • Subscription remains status: active until the period end date
    • No more charges after the current period ends
    • Customer gets full value for their last payment
    • After period ends, status changes to canceled
  2. Cancel Immediately:

    • Set cancel_at_period_end: false for immediate cancellation
    • Subscription status changes to canceled immediately
    • No refund is automatically issued (you must issue refund separately if needed)
    • Customer loses access immediately
    • Use this for fraud, payment failures, or customer-requested immediate cancellation
  3. Cancellation Fields: After cancellation:

    • canceled_at: Timestamp when cancellation was initiated
    • cancel_reason: Reason provided (useful for analytics and reporting)
    • cancel_at_period_end: Indicates if cancellation is deferred
    • status: Changes to canceled immediately (if cancel_at_period_end=false) or after period ends
  4. Cannot Be Reactivated: Once canceled, a subscription cannot be resumed or reactivated. The customer would need to create a new subscription.

  5. Invoices:

    • If cancel_at_period_end: true, the current invoice remains valid
    • If cancel_at_period_end: false, pending invoices may be voided
    • Check latest_invoice_id for invoice status
  6. Best Practice: Use cancel_at_period_end: true by default to provide better customer experience and avoid refund complications.

5. Pause Subscription

Temporarily pauses a subscription, preventing new charges from being created. Useful for seasonal businesses or when customers want to temporarily suspend service.

For detailed API reference, see: Pause Subscription API

Request:

curl --location --request POST 'https://api.martianpay.com/v1/subscriptions/sub_abc123xyz/pause' \
--header 'Content-Type: application/json' \
--header 'Authorization: Basic {BASE64_ENCODE(api_key + ":")}' \
--data-raw '{
"behavior": "void",
"resumes_at": 1709424000
}'

Request (Pause Indefinitely):

curl --location --request POST 'https://api.martianpay.com/v1/subscriptions/sub_abc123xyz/pause' \
--header 'Content-Type: application/json' \
--header 'Authorization: Basic {BASE64_ENCODE(api_key + ":")}' \
--data-raw '{
"behavior": "keep_as_draft"
}'

Request Body Parameters:

  • behavior (required): How to handle pending invoices
    • void: Cancel/void any pending invoices (recommended)
    • keep_as_draft: Keep pending invoices as draft
    • mark_uncollectible: Mark pending invoices as uncollectible
    • free: Continue creating invoices but mark them as free
  • resumes_at (optional): Unix timestamp for automatic resumption

Response:

{
"error_code": "success",
"msg": "success",
"data": {
"id": "sub_abc123xyz",
"merchant_id": "mer_def456",
"merchant_name": "My Store",
"customer_id": "cus_789ghi",
"customer_name": "John Doe",
"customer_email": "john@example.com",
"product_id": "prod_jkl012",
"product_name": "Premium Monthly Box",
"product_description": "Curated monthly subscription box",
"status": "paused",
"collection_method": "charge_automatically",
"default_payment_method_id": "pm_stu901",
"paused_at": 1706745600,
"pause_collection_behavior": "void",
"resumes_at": 1709424000,
"cancel_at_period_end": false,
"current_period_start": 1704067200,
"current_period_end": 1706745600,
"metadata": {},
"created_at": 1704067200,
"updated_at": 1706745600
}
}

Understanding the Response

How to Check if the Request Was Successful:

FieldValueMeaning
error_code"success"✅ Subscription paused successfully
error_code"subscription_not_found"❌ No subscription exists with this ID
error_code"subscription_already_paused"❌ Subscription is already paused
error_codeOther value❌ Other error occurred

Key Pause Fields:

FieldDescription
statusChanged to paused
paused_atTimestamp when pause was initiated
pause_collection_behaviorHow pending invoices are handled (void, keep_as_draft, mark_uncollectible, free)
resumes_atTimestamp for automatic resumption (null if indefinite)

Pause Behaviors Explained:

BehaviorWhat It DoesWhen to Use
void ✅ (Recommended)Cancels pending invoices, no chargesCustomer doesn't want any billing during pause
keep_as_draftKeeps invoices as drafts, can finalize laterWant to manually review before resuming
mark_uncollectibleMarks invoices as uncollectibleFor accounting/reporting purposes
freeCreates $0 invoices during pauseTrack billing cycles with free service

Auto-Resume:

  • If resumes_at is set: Subscription will automatically resume at that timestamp
  • If resumes_at is null: Paused indefinitely until manually resumed
  • Use auto-resume for seasonal businesses or temporary suspensions

What Happens During Pause:

  • ⏸️ Status changes to paused immediately
  • 🛑 No new charges during pause period (unless behavior=free)
  • 📅 Billing cycle may be extended based on pause duration
  • 🔄 Can be resumed anytime with Resume API

Customer Access: You control whether customers retain access during pause:

  • Define your own business rules for paused subscriptions
  • Some businesses give limited access, others revoke completely
  • Document your pause policy clearly to customers

Next Steps:

  • Notify customer that subscription is paused
  • Explain pause behavior and when charges will resume
  • If resumes_at is set, inform customer of auto-resume date
  • Consider offering alternative plans if customer wants to remain active
  • Track pause reasons for analytics to improve retention

Important Notes:

  1. Pause Behaviors:

    • void (Recommended): Cancels pending invoices, no charges during pause

      • Clean and simple
      • Customer not charged during pause period
      • Use when customer doesn't want any billing activity
    • keep_as_draft: Keeps invoices as drafts, can be finalized later

      • Invoices created but not sent or charged
      • Useful if you want to manually review before resuming
      • Drafts can be voided or finalized when resuming
    • mark_uncollectible: Marks invoices as uncollectible

      • Indicates invoices won't be collected
      • Useful for accounting/reporting purposes
      • Cleaner than void for some accounting systems
    • free: Creates invoices but marks them as $0

      • Maintains billing cycle tracking
      • Shows free service periods in billing history
      • Useful when giving free service during pause
  2. Auto-Resume:

    • Set resumes_at (Unix timestamp) to automatically resume subscription
    • Subscription status changes from paused to active at that time
    • Billing resumes according to original schedule
    • Omit resumes_at to pause indefinitely (manual resume required)
  3. Pause Period:

    • Subscription status changes to paused immediately
    • paused_at timestamp records when pause started
    • pause_collection_behavior shows which behavior is active
    • No new invoices created during pause (unless behavior=free)
  4. Billing Cycle:

    • Current period end date may be extended based on pause duration
    • Billing cycle anchor may shift when resumed
    • Check subscription after resume to see updated billing dates
  5. Customer Access: Define your own rules for customer access during pause:

    • You control whether customer retains access to service
    • Some businesses give limited access, others revoke completely
    • Document your pause policy clearly to customers
  6. Multiple Pauses: You can pause and resume a subscription multiple times.

6. Resume Subscription

Resumes a paused subscription, reactivating billing according to the original schedule.

For detailed API reference, see: Resume Subscription API

Request:

curl --location --request POST 'https://api.martianpay.com/v1/subscriptions/sub_abc123xyz/resume' \
--header 'Authorization: Basic {BASE64_ENCODE(api_key + ":")}'

Response:

{
"error_code": "success",
"msg": "success",
"data": {
"id": "sub_abc123xyz",
"merchant_id": "mer_def456",
"merchant_name": "My Store",
"customer_id": "cus_789ghi",
"customer_name": "John Doe",
"customer_email": "john@example.com",
"product_id": "prod_jkl012",
"product_name": "Premium Monthly Box",
"product_description": "Curated monthly subscription box",
"status": "active",
"collection_method": "charge_automatically",
"default_payment_method_id": "pm_stu901",
"paused_at": null,
"pause_collection_behavior": null,
"resumes_at": null,
"cancel_at_period_end": false,
"current_period_start": 1706745600,
"current_period_end": 1709424000,
"billing_cycle_anchor": 1704067200,
"metadata": {},
"created_at": 1704067200,
"updated_at": 1709337600
}
}

Understanding the Response

How to Check if the Request Was Successful:

FieldValueMeaning
error_code"success"✅ Subscription resumed successfully
error_code"subscription_not_found"❌ No subscription exists with this ID
error_code"subscription_not_paused"❌ Subscription is not currently paused
error_codeOther value❌ Other error occurred

Key Resume Fields:

FieldBefore ResumeAfter Resume
statuspausedactive
paused_atPause timestampnull (cleared)
pause_collection_behaviorPause behaviornull (cleared)
resumes_atAuto-resume time or nullnull (cleared)
current_period_startOld periodNew period starts from resume date
current_period_endOld periodNew period ends based on billing cycle

What Happens on Resume:

  • ✅ Status changes from paused to active
  • ✅ All pause-related fields are cleared
  • ✅ New billing period starts from resume date
  • ✅ Next invoice will be generated based on selling plan schedule
  • ✅ Automatic billing resumes according to original schedule

Billing After Resume:

  • New Period Dates: current_period_start and current_period_end are updated
  • Next Charge: Will occur at the end of the new period
  • Billing Cycle: Continues according to selling plan (monthly, yearly, etc.)
  • Pricing: Uses the selling plan pricing tier for current cycle number

Draft Invoices (if pause behavior was keep_as_draft):

  • Review any draft invoices from the pause period
  • Manually decide whether to finalize, void, or modify them
  • Draft invoices are NOT automatically processed on resume
  • Handle them separately through invoice management

Auto-Resume vs Manual Resume:

  • If subscription had resumes_at set, system auto-resumes at that time
  • You can manually resume before the auto-resume time
  • Manual resume clears the resumes_at timestamp
  • Either method results in the same active state

Payment Method Check: Ensure payment method is still valid before resuming, especially if:

  • Subscription was paused for a long time
  • Credit card may have expired
  • Customer may have changed payment preferences

Next Steps:

  • Send confirmation email to customer about resumed subscription
  • Show next billing date and amount for transparency
  • If customer forgot about subscription, give them option to cancel
  • Verify payment method is up to date to avoid payment failures
  • Monitor first payment after resume for any issues

Important Notes:

  1. Status Change:

    • Subscription status changes from paused to active
    • Pause-related fields are cleared: paused_at, pause_collection_behavior, resumes_at
    • Normal billing cycle resumes
  2. Billing Resumption:

    • New billing period starts from resume date
    • current_period_start and current_period_end are updated
    • Next invoice will be generated based on new period dates
    • Billing occurs according to the selling plan schedule
  3. Draft Invoices: If pause behavior was keep_as_draft:

    • Review and manually handle any draft invoices
    • Decide whether to finalize, void, or modify them
    • Draft invoices are not automatically processed on resume
  4. Auto-Resume: If subscription was set to auto-resume with resumes_at:

    • System automatically resumes at the specified timestamp
    • You can also manually resume before the auto-resume time
    • Manual resume clears the resumes_at timestamp
  5. Customer Notification: Consider notifying customers when their subscription resumes, especially if:

    • Resume will trigger immediate payment
    • Customer manually requested pause
    • Customer may have forgotten about the subscription
  6. Payment Method: Ensure the payment method on file is still valid before resuming, especially for long pauses.

7. Update Subscription (Plan Change)

Updates a subscription's plan, allowing customers to upgrade or downgrade their subscription. Supports automatic proration calculation for mid-cycle changes.

For detailed API reference, see: Update Subscription API

Request:

curl --location --request POST 'https://api.martianpay.com/v1/subscriptions/sub_abc123xyz' \
--header 'Content-Type: application/json' \
--header 'Authorization: Basic {BASE64_ENCODE(api_key + ":")}' \
--data-raw '{
"primary_variant": {
"selling_plan_id": "sp_newplan456"
},
"proration_behavior": "always_invoice",
"billing_cycle_anchor": "now"
}'

Request Body Parameters:

ParameterTypeRequiredDescription
primary_variantobjectYesThe primary variant selection to update
primary_variant.selling_plan_idstringYesThe new selling plan ID to switch to
primary_variant.variant_idstringNoThe new variant ID (optional, keeps current if not specified)
primary_variant.quantityintegerNoQuantity (default: 1)
proration_behaviorstringNoHow proration is handled (see detailed explanation below)
proration_dateintegerNoUnix timestamp for custom proration calculation (see detailed explanation below)
billing_cycle_anchorstringNoControls billing cycle timing (see detailed explanation below)
metadataobjectNoKey-value pairs for additional information
addonsarrayNoAddon variant selections (reserved for future use)

Parameter Details

proration_behavior

Controls how proration charges are handled during plan changes.

Values:

  • "always_invoice": Create an invoice immediately for the prorated amount. Customer is charged right away.
  • "create_prorations": Defer the plan change to next billing cycle (used for deferred upgrades).
  • "none": No proration calculation. Only valid for downgrades.

Default behavior (when not specified):

  • Upgrades: "always_invoice" (immediate charge with credit for unused time on old plan)
  • Downgrades: "none" (no charge, change takes effect at period end)

VALID COMBINATIONS (other combinations return error invalid_proration_config):

For UPGRADES (3 valid combinations):

proration_behaviorbilling_cycle_anchorResult
always_invoicenowImmediate upgrade, charge now, reset cycle
always_invoiceunchangedImmediate upgrade, charge now, keep cycle
create_prorationsunchangedDeferred upgrade, change at period end

For DOWNGRADES (1 valid combination):

proration_behaviorbilling_cycle_anchorResult
noneunchangedDeferred downgrade, change at period end

INVALID COMBINATIONS (return error):

  • create_prorations + now: Would reset cycle without settling, customer credit lost
  • none + now/unchanged (upgrade): Would give customer free upgrade
  • any + now (downgrade): Downgrade cannot take effect immediately

billing_cycle_anchor

Controls when the new billing cycle starts after a plan change.

Values:

  • "now": Reset billing cycle immediately. New period starts from the change time.
  • "unchanged": Keep current billing cycle anchor.

Default behavior (when not specified):

  • Upgrades: "now" (billing cycle resets to start fresh)
  • Downgrades: Must be "unchanged" (downgrades always take effect at period end)

Note: For downgrades, only "unchanged" is valid. Using "now" will return an error. See proration_behavior documentation for valid combinations.

proration_date

A Unix timestamp (seconds) for custom proration calculation (backdating). When provided, proration credits are calculated as if the plan change happened at this time instead of now.

How it works:

  • The remaining time on the old plan is calculated from proration_date to current_period_end
  • Credit = (old_plan_price) × (remaining_time / total_period_time)
  • This allows backdating: if a customer requested a change yesterday, you can use yesterday's timestamp

Constraints:

  • Must be within the current billing period (between current_period_start and current_period_end)
  • If not specified, current time (now) is used for calculation

Example: If period is Dec 1-31 and proration_date is Dec 15, customer gets credit for 16 remaining days.

Response:

{
"error_code": "success",
"msg": "success",
"data": {
"id": "sub_abc123xyz",
"merchant_id": "mer_def456",
"customer_id": "cus_789ghi",
"customer_name": "John Doe",
"customer_email": "john@example.com",
"product_id": "prod_jkl012",
"product_name": "Premium Monthly Box",
"variant_id": "var_mno345",
"variant_title": "Premium / Monthly",
"variant_price": "99.99",
"selling_plan_id": "sp_newplan456",
"selling_plan_name": "Premium Monthly Subscription",
"status": "active",
"collection_method": "charge_automatically",
"default_payment_method_id": "pm_stu901",
"created_at": 1704067200,
"updated_at": 1706745600,
"billing_cycle_anchor": 1706745600,
"current_period_start": 1706745600,
"current_period_end": 1709424000,
"current_cycle_number": 3,
"next_charge_amount": "99.99",
"next_charge_amount_display": "$99.99",
"next_charge_date": 1709424000,
"applied": true,
"is_upgrade": true,
"effective_date": 1706745600,
"charge_today": "66.66",
"proration_behavior": "always_invoice",
"proration_date": 1706745600,
"proration_credit": "33.33",
"proration_details": {
"current_price": "4999",
"target_price": "9999",
"days_remaining": 20,
"total_days": 30,
"credited_amount": "3333",
"charged_amount": "6666",
"net_amount": "3333"
},
"pending_update": null,
"metadata": {}
}
}

Understanding the Response

How to Check if the Request Was Successful:

FieldValueMeaning
error_code"success"✅ Subscription updated successfully
error_code"subscription_not_found"❌ No subscription exists with this ID
error_code"subscription_has_open_invoice"❌ Cannot update while there's an unpaid invoice
error_code"subscription_has_pending_update"❌ Cannot update while there's a pending update
error_codeOther value❌ Other error occurred

Key Update Fields:

FieldDescription
appliedtrue indicates the change was applied (vs. preview)
is_upgradetrue for upgrade, false for downgrade
effective_dateUnix timestamp when the change took/will take effect
charge_todayNet amount charged today (formatted string, e.g., "66.66")
proration_behaviorHow proration was handled
proration_dateTimestamp used for proration calculation
proration_creditCredit for unused time on old plan
proration_detailsDetailed breakdown of proration calculation
pending_updateContains scheduled changes for downgrades (null for immediate changes)

Proration Details Object:

FieldDescription
current_priceCurrent plan price in cents
target_priceTarget plan price in cents
days_remainingDays remaining in current billing period
total_daysTotal days in the billing period
credited_amountCredit for unused time (in cents)
charged_amountCharge for new plan's remaining time (in cents)
net_amountFinal net amount: charged - credited (in cents)

Upgrade vs Downgrade Behavior

Upgrades (new price > current price):

  • ✅ Applied immediately
  • ✅ Customer charged prorated difference today
  • ✅ New plan takes effect immediately
  • ✅ Billing cycle can be reset (billing_cycle_anchor: "now")

Downgrades (new price < current price):

  • ⏳ Scheduled as pending update
  • ⏳ Takes effect at end of current billing period
  • ⏳ Customer continues on current plan until effective_date
  • pending_update object contains scheduled change details

Example Downgrade Response with Pending Update:

{
"error_code": "success",
"msg": "success",
"data": {
"id": "sub_abc123xyz",
"status": "active",
"selling_plan_id": "sp_currentplan123",
"selling_plan_name": "Premium Monthly",
"applied": false,
"is_upgrade": false,
"effective_date": 1709424000,
"pending_update": {
"target_selling_plan_id": "sp_basicplan789",
"target_selling_plan_name": "Basic Monthly",
"target_variant_id": "var_basic123",
"target_variant_title": "Basic / Monthly",
"target_variant_price": "$29.99",
"change_type": "downgrade",
"effective_date": 1709424000,
"scheduled_at": 1706745600,
"proration_behavior": "none",
"next_charge_amount": "29.99",
"billing_cycle_anchor": "unchanged"
},
"current_period_end": 1709424000
}
}

Pending Update Object

When a downgrade is scheduled, the pending_update object contains:

FieldDescription
target_selling_plan_idThe selling plan to change to
target_selling_plan_nameName of the target selling plan
target_variant_idThe variant to change to (if specified)
target_variant_titleDisplay title of the target variant
target_variant_priceBase price of the target variant (formatted)
target_variant_option_valuesOption values of the target variant
change_type"upgrade" or "downgrade"
effective_dateUnix timestamp when the change will take effect
scheduled_atUnix timestamp when the update was scheduled
proration_behaviorHow proration will be handled
proration_dateTimestamp for proration calculation (optional)
next_charge_amountAmount to be charged after change takes effect
billing_cycle_anchorBilling cycle timing setting
metadataAdditional information for this pending update

Restrictions

The update API will return errors in the following cases:

  1. Open Invoice: Cannot update if there's an unpaid invoice

    • Error: subscription_has_open_invoice
    • Solution: Wait for invoice to be paid or void it first
  2. Pending Update: Cannot update if there's already a pending update

    • Error: subscription_has_pending_update
    • Solution: Cancel the pending update first or wait for it to apply
  3. Invalid Status: Cannot update canceled or incomplete subscriptions

    • Error: subscription_invalid_status
    • Solution: Subscription must be active or paused

Important Notes:

  1. Proration Calculation: The system automatically calculates:

    • Credit for unused days on current plan
    • Charge for remaining days on new plan
    • Net amount = Charge - Credit
  2. Billing Cycle Anchor:

    • "now": Resets billing cycle to start today (useful for upgrades)
    • "unchanged": Keeps current billing cycle dates (default for downgrades)
  3. Immediate vs Deferred:

    • Upgrades are applied immediately by default
    • Downgrades are scheduled for period end by default
    • This prevents giving customers "free" upgrades by downgrading mid-cycle
  4. Invoice Generation:

    • For upgrades with always_invoice: A proration invoice is created and charged immediately
    • For downgrades: No immediate charge; next invoice reflects new price
  5. Webhooks: Listen for these events after update:

    • subscription.updated: Subscription details changed
    • invoice.created: Proration invoice created (for upgrades)
    • invoice.paid: Proration invoice paid

8. Preview Subscription Update

Preview the proration calculation before actually updating a subscription plan. This allows you to show customers what they'll be charged before confirming the change.

For detailed API reference, see: Preview Subscription Update API

Request:

curl --location --request POST 'https://api.martianpay.com/v1/subscriptions/sub_abc123xyz/preview' \
--header 'Content-Type: application/json' \
--header 'Authorization: Basic {BASE64_ENCODE(api_key + ":")}' \
--data-raw '{
"primary_variant": {
"selling_plan_id": "sp_newplan456"
},
"proration_behavior": "always_invoice",
"billing_cycle_anchor": "now"
}'

Request Body Parameters:

Same as Update Subscription API.

Response:

{
"error_code": "success",
"msg": "success",
"data": {
"id": "sub_abc123xyz",
"merchant_id": "mer_def456",
"customer_id": "cus_789ghi",
"customer_name": "John Doe",
"customer_email": "john@example.com",
"product_id": "prod_jkl012",
"product_name": "Premium Monthly Box",
"variant_id": "var_mno345",
"variant_title": "Standard / Monthly",
"variant_price": "49.99",
"selling_plan_id": "sp_pqr678",
"selling_plan_name": "Monthly Subscription",
"status": "active",
"applied": false,
"is_upgrade": true,
"effective_date": 1706745600,
"charge_today": "66.66",
"proration_behavior": "always_invoice",
"proration_date": 1706745600,
"proration_credit": "33.33",
"proration_details": {
"current_price": "4999",
"target_price": "9999",
"days_remaining": 20,
"total_days": 30,
"credited_amount": "3333",
"charged_amount": "6666",
"net_amount": "3333"
},
"next_charge_amount": "99.99",
"next_charge_amount_display": "$99.99",
"next_charge_date": 1709424000
}
}

Understanding the Response

Key Difference from Update API:

  • applied is false - indicates this is a preview, no changes were made

How to Check if Preview Was Successful:

FieldValueMeaning
error_code"success"✅ Preview calculated successfully
error_code"subscription_not_found"❌ No subscription exists with this ID
error_codeOther value❌ Other error occurred

Key Preview Fields:

FieldDescription
appliedAlways false for preview (no changes made)
is_upgradetrue if new plan is more expensive, false for downgrade
charge_todayAmount that would be charged today (for upgrades)
effective_dateWhen the change would take effect
proration_detailsDetailed breakdown of proration calculation
next_charge_amountWhat the next regular charge would be
next_charge_dateWhen the next regular charge would occur

Use Cases

1. Display Plan Change Confirmation:

// Frontend example
const preview = await fetch('/api/subscriptions/sub_abc123/preview', {
method: 'POST',
body: JSON.stringify({
primary_variant: { selling_plan_id: 'sp_premium' }
})
});

const data = await preview.json();

// Display to customer
if (data.is_upgrade) {
showMessage(`
Upgrade to ${data.selling_plan_name}
Charge today: $${data.charge_today}
Credit for unused time: $${data.proration_credit}
Next billing date: ${formatDate(data.next_charge_date)}
Next charge: ${data.next_charge_amount_display}
`);
} else {
showMessage(`
Downgrade to ${data.selling_plan_name}
Effective: ${formatDate(data.effective_date)}
You'll continue on your current plan until then.
New price after change: ${data.next_charge_amount_display}
`);
}

2. Compare Multiple Plans:

// Preview multiple plan options
const plans = ['sp_basic', 'sp_pro', 'sp_enterprise'];
const previews = await Promise.all(
plans.map(planId =>
previewPlanChange(subscriptionId, planId)
)
);

// Display comparison table
displayPlanComparison(previews);

Important Notes:

  1. No Side Effects: Preview does not:

    • Modify the subscription
    • Create invoices
    • Charge the customer
    • Send any webhooks
  2. Real-Time Calculation: Preview uses current date/time for proration, so results may vary if customer waits before confirming

  3. Same Validation: Preview applies the same validation as update (e.g., checks for open invoices)

  4. Use Before Update: Always preview before calling the update API to ensure customers understand what they'll be charged

9. Customer Portal for Subscription Management

The Customer Portal allows your customers to self-service their subscription management, including viewing, pausing, resuming, and canceling subscriptions. You can use ephemeral tokens to provide secure, seamless access to the portal without requiring additional login.

Using Ephemeral Tokens for Portal Access

Generate an ephemeral token and append it to the portal URL to authenticate customers automatically:

Step 1: Generate Ephemeral Token

curl --location --request POST 'https://api.martianpay.com/v1/customers/ephemeral_tokens' \
--header 'Content-Type: application/json' \
--header 'Authorization: Basic {BASE64_ENCODE(api_key + ":")}' \
--data '{
"idp_key": "email",
"idp_subject": "customer@example.com",
"provider": "your_platform",
"allow_create": false,
"issued_by": "your_backend_service"
}'

Response:

{
"error_code": "success",
"msg": "success",
"data": {
"token": "eph_abc123xyz789",
"expires_at": 1735689600
}
}

Step 2: Direct Customer to Portal

Construct the portal URL with the ephemeral token and your merchant ID:

https://buy.martianpay.com/portal?merchant_id=accu_M7PTgveSgMtTtPHbjFgEtAlD&ephemeral_token=eph_abc123xyz789

URL Parameters:

  • merchant_id: Your merchant account ID (starts with accu_)
  • ephemeral_token: The token generated from the API

Portal Features

Once authenticated, customers can:

  • View all subscriptions: See active, paused, and canceled subscriptions
  • View subscription details: Check billing dates, pricing, and product information
  • Pause subscriptions: Temporarily pause active subscriptions
  • Resume subscriptions: Reactivate paused subscriptions
  • Cancel subscriptions: Cancel unwanted subscriptions
  • Update payment methods: Change card or payment information
  • View billing history: Access past invoices and receipts

Use Cases

Email Integration:

Hi {customer_name},

Manage your subscriptions here:
https://buy.martianpay.com/portal?merchant_id=accu_M7PTgveSgMtTtPHbjFgEtAlD&ephemeral_token={generated_token}

This link expires in 15 minutes.

In-App/Website Button:

// Generate token on your backend
const response = await fetch('/api/generate-portal-token', {
method: 'POST',
body: JSON.stringify({ customer_email: user.email })
});
const { token } = await response.json();

// Redirect user to portal
const portalUrl = `https://buy.martianpay.com/portal?merchant_id=${MERCHANT_ID}&ephemeral_token=${token}`;
window.location.href = portalUrl;

Webhook-Triggered Access: When a subscription needs attention (e.g., payment failed), automatically send customers a portal link:

Your payment failed. Please update your payment method:
https://buy.martianpay.com/portal?merchant_id=accu_M7PTgveSgMtTtPHbjFgEtAlD&ephemeral_token={generated_token}

Security Considerations

  • Short-lived tokens: Ephemeral tokens typically expire in 5-15 minutes
  • Single-use recommended: Generate a new token for each portal access
  • Customer verification: Tokens are tied to specific customer identities (email, phone, or UUID)
  • HTTPS only: Portal URLs must always use HTTPS
  • Backend generation: Never generate tokens in frontend code - always use your secure backend

Alternative Authentication Methods

If you prefer not to use ephemeral tokens, the portal also supports:

  • Email + OTP: Customers enter their email and receive a one-time code
  • Auth tokens: Long-lived tokens for authenticated sessions

10. Webhook Events for Subscriptions

MartianPay sends webhook notifications for subscription and invoice events, enabling you to track subscription lifecycle and billing in real-time.

10.1 Subscription Events

Event TypeDescriptionTriggered When
subscription.createdSubscription createdA new subscription is successfully created
subscription.updatedSubscription details updatedSubscription details change (e.g., status, payment method)
subscription.deletedSubscription deletedSubscription is permanently deleted
subscription.pausedSubscription pausedSubscription billing is paused
subscription.resumedSubscription resumedPaused subscription is reactivated
subscription.trial_will_endTrial ending soonSent 3 days before trial period ends

10.2 Invoice Events

Subscriptions generate invoices for each billing cycle. Monitor these events to track billing:

Event TypeDescriptionTriggered When
invoice.createdInvoice createdNew invoice generated for billing cycle
invoice.finalizedInvoice finalizedInvoice finalized and ready for payment
invoice.paidInvoice paid successfullyPayment for invoice completed
invoice.payment_succeededInvoice payment succeededPayment successfully processed (same as paid)
invoice.payment_failedInvoice payment failedPayment attempt failed
invoice.payment_action_requiredPayment action neededAdditional customer action required (e.g., 3D Secure)
invoice.marked_uncollectibleInvoice uncollectibleMarked as uncollectible after retries
invoice.voidedInvoice voidedInvoice canceled and voided

10.3 Webhook Payload Example

Subscription Event:

{
"id": "evt_056qw9fr9PndljykFUqSIf6t",
"object": "event",
"api_version": "2025-01-22",
"created": 1748580845,
"data": {
"object": {
"id": "sub_abc123xyz",
"object": "subscription",
"status": "active",
"created": 1748500000,
"updated": 1748580845,
"customer": "cus_7p51tXM7GBi7sz5J",
"current_period_start": 1748500000,
"current_period_end": 1751178400,
"billing_cycle_anchor": 1748500000,
"cancel_at_period_end": false,
"canceled_at": null,
"ended_at": null,
"trial_start": null,
"trial_end": null,
"metadata": {
"customer_tier": "premium",
"source": "web"
}
},
"previous_attributes": {
"status": "trialing",
"updated": 1748580000
}
},
"livemode": false,
"pending_webhooks": 0,
"type": "subscription.updated"
}

Invoice Event:

{
"id": "evt_056qw9fr9PndljykFUqSIf6t",
"object": "event",
"api_version": "2025-01-22",
"created": 1748580845,
"data": {
"object": {
"id": "inv_abc123xyz",
"object": "invoice",
"subscription": "sub_abc123xyz",
"customer": "cus_7p51tXM7GBi7sz5J",
"amount_due": "49.99",
"amount_paid": "49.99",
"currency": "USD",
"status": "paid",
"created": 1748580000,
"due_date": 1748583600,
"period_start": 1748500000,
"period_end": 1751178400,
"billing_reason": "subscription_cycle"
}
},
"livemode": false,
"pending_webhooks": 0,
"type": "invoice.paid"
}

10.4 Webhook Event Flow

Typical Subscription Lifecycle:

  1. subscription.created - New subscription created (status: active or trialing)
  2. subscription.trial_will_end - (if trial) Sent 3 days before trial ends
  3. invoice.created - Invoice generated for billing cycle
  4. invoice.finalized - Invoice ready for payment
  5. invoice.payment_succeeded - Payment successful
  6. invoice.paid - Invoice marked as paid
  7. Repeat steps 3-6 for each billing cycle
  8. subscription.paused - (optional) Subscription paused
  9. subscription.resumed - (optional) Subscription resumed
  10. subscription.deleted - Subscription canceled and deleted

Failed Payment Flow:

  1. invoice.created - Invoice generated
  2. invoice.finalized - Invoice ready
  3. invoice.payment_failed - Payment attempt failed
  4. Automatic retry with invoice.payment_failed on each retry
  5. invoice.marked_uncollectible - (if all retries fail)
  6. Subscription may be paused or canceled depending on settings

10.5 Best Practices

  1. Prioritize invoice events for billing - Monitor invoice.paid and invoice.payment_failed events:

    • invoice.paid: Confirm billing cycle payment, grant continued access
    • invoice.payment_failed: Alert customer about payment issue, update payment method
  2. Handle trial expiration proactively - Monitor subscription.trial_will_end:

    • Send reminder emails 3 days before trial ends
    • Encourage users to update payment information
    • Highlight subscription benefits to reduce churn
  3. Track subscription status changes - Use subscription.updated to:

    • Monitor status transitions (trialing → active, active → past_due)
    • Update customer access privileges
    • Trigger billing notifications
  4. Manage failed payments gracefully - When invoice.payment_failed occurs:

    • Notify customers immediately
    • Provide easy payment method update links
    • Implement grace period before service suspension
    • Track retry attempts and failure reasons
  5. Monitor payment action requirements - Handle invoice.payment_action_required:

    • Alert customers about required actions (e.g., 3D Secure authentication)
    • Provide clear instructions and links
    • Track completion rates
  6. Handle paused subscriptions - Track subscription.paused and subscription.resumed:

    • Update customer access accordingly
    • Send confirmation emails
    • Track pause/resume patterns for analytics
  7. Reconcile billing with invoices - Use invoice events to:

    • Match payments to billing cycles
    • Generate accounting reports
    • Track revenue by subscription
    • Identify billing discrepancies
  8. Handle idempotency - Store event IDs (evt_*) to prevent duplicate processing

  9. Reconcile with subscription ID - Use the subscription field in invoice events to:

    • Match invoices to subscriptions
    • Update subscription status in your system
    • Track payment history per subscription

For more details on webhook setup, signature verification, and complete event schemas, see the Webhook Events documentation.

Best Practices

  1. Subscription Creation:
    • Create subscriptions through payment intents with selling plans, not directly
    • Ensure products have properly configured selling plan groups
    • Set appropriate trial periods to encourage adoption
    • Clearly communicate billing terms to customers

1a. Selling Plan Management:

  • Selling plans cannot be deleted while active - You must first set the selling plan status to inactive before deletion (Error code 274: selling_plan_active_no_delete)
  • When deleting a selling plan group, all associated selling plans within that group are automatically deleted
  • Before deleting a selling plan, ensure no active subscriptions are using it
  • Consider deactivating selling plans instead of deleting them to preserve historical data
  1. Monitoring Subscriptions:

    • Regularly check for incomplete subscriptions and follow up
    • Monitor past_due subscriptions for payment failures
    • Track hours_since_creation for incomplete subscriptions
    • Set up alerts for subscriptions approaching expiration
  2. Cancellation Handling:

    • Default to cancel_at_period_end: true for better customer experience
    • Collect cancel_reason for analytics and improvement insights
    • Consider offering pause as an alternative to cancellation
    • Send confirmation emails after cancellation
  3. Pause Management:

    • Use void behavior for most pause scenarios
    • Set resumes_at for predictable auto-resume (e.g., seasonal business)
    • Document your pause policy clearly for customers
    • Limit pause duration if needed (implement in your business logic)
  4. Pricing Transparency:

    • Display both current_pricing_tier and upcoming_pricing_tier to customers
    • Show next_charge_amount clearly before billing
    • Communicate when pricing will change between cycles
    • Use selling_plan_pricing.pricing_tiers to preview full pricing schedule
  5. Payment Method Management:

    • Verify payment method is valid before each billing cycle
    • Send reminders when payment method is expiring
    • Allow customers to update payment methods easily
    • Handle payment failures gracefully with retry logic
  6. Customer Communication:

    • Send receipts after successful payments
    • Notify customers before upcoming charges
    • Alert customers of payment failures
    • Confirm when subscriptions are paused, resumed, or canceled
    • Provide clear instructions for managing subscriptions
    • Include Customer Portal links in emails for easy self-service access
    • Generate fresh ephemeral tokens for each portal link to maintain security
  7. Metadata Usage:

    • Store subscription source (e.g., website, mobile app)
    • Track marketing campaigns in metadata
    • Add customer segment information
    • Include any custom fields relevant to your business
  8. Filtering and Search:

    • Use customer_id filter to show customer their subscriptions
    • Filter by status for admin dashboards
    • Combine filters for targeted queries
    • Implement pagination for large subscription lists
  9. Error Handling:

    • Handle incomplete subscriptions with payment reminders
    • Retry failed payments with appropriate intervals
    • Provide clear error messages to customers
    • Log subscription events for debugging and analytics
  10. Compliance and Security:

    • Store payment method information securely
    • Comply with PCI DSS requirements
    • Implement clear cancellation policies
    • Maintain audit logs of subscription changes
    • Honor customer requests promptly
  11. Analytics and Reporting:

    • Track subscription churn rates by analyzing cancellations
    • Monitor pause vs cancel ratios
    • Analyze cancel_reason for improvement opportunities
    • Calculate lifetime value (LTV) from subscription data
    • Monitor trial-to-paid conversion rates
  12. Customer Portal Usage:

    • Provide easy access to Customer Portal for self-service management
    • Generate ephemeral tokens server-side (never in frontend)
    • Include portal links in subscription confirmation emails
    • Add "Manage Subscription" buttons in user dashboards
    • Send portal links proactively when payment issues occur
    • Use allow_create: false when generating tokens for existing customers only
    • Track portal usage metrics to understand customer engagement