Skip to Content
DocumentationGuidesSubscriptions

Subscriptions

Subscriptions enable automatic recurring billing for your customers. They link customers to subscription plans and handle the entire billing lifecycle, from creation to management and updates.

Overview

Subscriptions are the active billing relationships between customers and your plans. Once created, they automatically charge customers according to the plan’s billing interval, handle failed payments, and provide tools for subscription management.

Key Concepts

  • Subscription Code: Unique identifier for each subscription (SUB_xyz123)
  • Authorization: Payment method authorization for recurring billing
  • Status: Current subscription state (active, cancelled, attention)
  • Customer: The subscriber linked to the subscription
  • Plan: The billing plan defining amount and frequency

Getting Started

First, initialize the MindPaystack SDK:

import 'package:mind_paystack/mind_paystack.dart'; // Initialize the SDK await MindPaystack.initialize( PaystackConfig( publicKey: 'pk_test_your_public_key', secretKey: 'sk_test_your_secret_key', environment: Environment.test, ), ); final sdk = MindPaystack.instance;

Creating Subscriptions

Create subscriptions to start recurring billing for customers:

// Create a subscription with existing customer and plan final subscriptionResult = await sdk.subscription.create( CreateSubscriptionOptions( customer: 'CUS_abc123', // Customer code plan: 'PLN_xyz789', // Plan code authorization: 'AUTH_def456', // Authorization from previous transaction startDate: DateTime.now().add(Duration(days: 7)), // Start in 7 days quantity: 1, metadata: { 'order_id': '12345', 'source': 'website', }, ), ); if (subscriptionResult.isSuccess) { final subscription = subscriptionResult.data!; print('✅ Subscription created: ${subscription.subscriptionCode}'); print('Status: ${subscription.status}'); print('Next payment: ${subscription.nextPaymentDate}'); } // Create subscription with customer email (auto-creates customer) final emailSubscriptionResult = await sdk.subscription.create( CreateSubscriptionOptions( customer: 'customer@example.com', // Email instead of customer code plan: 'PLN_xyz789', authorization: 'AUTH_def456', ), );

Subscription Parameters

ParameterRequiredDescription
customerYesCustomer code or email address
planYesPlan code for billing
authorizationNoAuthorization code for payments
startDateNoWhen to start billing (default: now)
quantityNoNumber of units (default: 1)
metadataNoCustom key-value pairs

Listing Subscriptions

Retrieve subscriptions with filtering and pagination:

// List all subscriptions final allSubscriptions = await sdk.subscription.list(); if (allSubscriptions.isSuccess) { final subscriptions = allSubscriptions.data!; print('📋 Found ${subscriptions.length} subscriptions'); for (final sub in subscriptions) { print('${sub.customer?.email}: ${sub.plan?.name} - ${sub.status}'); } } // List with filtering final filteredResult = await sdk.subscription.list( ListSubscriptionsOptions( perPage: 20, page: 1, customer: 'CUS_abc123', // Specific customer plan: 'PLN_xyz789', // Specific plan status: 'active', // Only active subscriptions from: DateTime(2024, 1, 1), // Date range to: DateTime(2024, 12, 31), ), ); // List customer subscriptions final customerSubs = await sdk.subscription.list( ListSubscriptionsOptions( customer: 'CUS_abc123', status: 'active', ), );

List Options

ParameterDescription
perPageNumber of subscriptions per page (max 100)
pagePage number for pagination
customerFilter by customer code
planFilter by plan code
statusFilter by subscription status
fromStart date for filtering
toEnd date for filtering

Fetching Specific Subscriptions

Get detailed information about a subscription:

// Fetch by subscription code final subscriptionResult = await sdk.subscription.fetch('SUB_abc123xyz'); if (subscriptionResult.isSuccess) { final subscription = subscriptionResult.data!; print('📊 Subscription Details:'); print('Status: ${subscription.status}'); print('Customer: ${subscription.customer?.email}'); print('Plan: ${subscription.plan?.name}'); print('Amount: ₦${(subscription.amount! / 100).toStringAsFixed(2)}'); print('Next payment: ${subscription.nextPaymentDate}'); print('Created: ${subscription.createdAt}'); // Check payment authorization final auth = subscription.authorization; if (auth != null) { print('Card: **** **** **** ${auth.last4}'); print('Bank: ${auth.bank}'); print('Expires: ${auth.expMonth}/${auth.expYear}'); } } // Fetch by subscription ID final subscriptionById = await sdk.subscription.fetch('12345');

Managing Subscription Status

Enabling Subscriptions

Reactivate a disabled subscription:

// Enable subscription final enableResult = await sdk.subscription.enable('SUB_abc123xyz'); if (enableResult.isSuccess) { final subscription = enableResult.data!; print('✅ Subscription enabled: ${subscription.status}'); } // Enable with email token (for customer self-service) final tokenEnableResult = await sdk.subscription.enable( 'SUB_abc123xyz', EnableSubscriptionOptions( token: 'email_token_from_customer_link', ), );

Disabling Subscriptions

Temporarily stop a subscription:

// Disable subscription final disableResult = await sdk.subscription.disable('SUB_abc123xyz'); if (disableResult.isSuccess) { final subscription = disableResult.data!; print('⏸️ Subscription disabled: ${subscription.status}'); } // Disable with email token final tokenDisableResult = await sdk.subscription.disable( 'SUB_abc123xyz', DisableSubscriptionOptions( token: 'email_token_from_customer_link', ), );

Subscription Status Values

StatusDescription
activeSubscription is active and billing
cancelledSubscription has been cancelled
attentionRequires attention (failed payment, etc.)

Create secure links for customers to update their payment methods:

// Generate update link final linkResult = await sdk.subscription.generateUpdateLink('SUB_abc123xyz'); if (linkResult.isSuccess) { final link = linkResult.data!; print('🔗 Update link generated: ${link.link}'); // Share this link with your customer // They can update card details, billing info, etc. await shareWithCustomer(link.link); }

Automatically email update links to customers:

// Send update link to customer's email final emailResult = await sdk.subscription.sendUpdateLink('SUB_abc123xyz'); if (emailResult.isSuccess) { print('📧 Update link sent to customer email'); } else { print('❌ Failed to send email: ${emailResult.message}'); }

Error Handling

Handle common subscription operation errors:

try { final result = await sdk.subscription.create(subscriptionOptions); if (result.isSuccess) { // Subscription created successfully final subscription = result.data!; handleSubscriptionCreated(subscription); } else { // Handle API errors switch (result.message) { case 'Customer not found': showError('Customer does not exist. Please create customer first.'); break; case 'Plan not found': showError('Selected plan is not available.'); break; case 'Invalid authorization': showError('Payment method is invalid or expired.'); break; default: showError('Failed to create subscription: ${result.message}'); } } } on MindException catch (e) { // Handle SDK-specific errors print('MindPaystack Error: ${e.message}'); print('Error Code: ${e.code}'); if (e.validationErrors?.isNotEmpty == true) { for (final error in e.validationErrors!) { print('Validation Error: ${error.field} - ${error.message}'); } } } catch (e) { // Handle unexpected errors print('Unexpected error: $e'); }

Best Practices

Customer Communication

Keep customers informed about their subscriptions:

// After creating subscription final subscription = subscriptionResult.data!; await sendWelcomeEmail( subscription.customer!.email!, subscription.plan!.name!, subscription.nextPaymentDate!, ); // Before payment attempts await sendPaymentReminderEmail( subscription.customer!.email!, subscription.nextPaymentDate!, subscription.amount!, );

Handling Failed Payments

Monitor subscription status and handle failures:

// Check subscription status regularly final subscription = await sdk.subscription.fetch('SUB_abc123xyz'); if (subscription.isSuccess) { final sub = subscription.data!; if (sub.status == 'attention') { // Handle payment failure await handlePaymentFailure(sub); // Send update link to customer await sdk.subscription.sendUpdateLink(sub.subscriptionCode!); } } async function handlePaymentFailure(Subscription subscription) { // Notify customer await sendPaymentFailedEmail(subscription.customer!.email!); // Optionally pause features await pauseCustomerAccess(subscription.customer!.customerCode!); // Set retry logic await scheduleRetryAttempt(subscription.subscriptionCode!); }

Subscription Lifecycle Management

// Monitor subscription health final subscriptions = await sdk.subscription.list( ListSubscriptionsOptions(status: 'attention'), ); if (subscriptions.isSuccess) { for (final sub in subscriptions.data!) { await handleSubscriptionIssue(sub); } } // Upgrade/downgrade subscriptions async function changeSubscriptionPlan( String subscriptionCode, String newPlanCode, ) { // 1. Disable current subscription await sdk.subscription.disable(subscriptionCode); // 2. Create new subscription with new plan final newSub = await sdk.subscription.create( CreateSubscriptionOptions( customer: currentSubscription.customer!.customerCode!, plan: newPlanCode, authorization: currentSubscription.authorization!.authorizationCode!, ), ); return newSub; }

Webhook Integration

Handle subscription webhooks for real-time updates:

// Webhook handler example async function handleSubscriptionWebhook(Map<String, dynamic> payload) { final event = payload['event']; final data = payload['data']; switch (event) { case 'subscription.create': await onSubscriptionCreated(data); break; case 'subscription.disable': await onSubscriptionDisabled(data); break; case 'invoice.create': await onInvoiceCreated(data); break; case 'invoice.payment_failed': await onPaymentFailed(data); break; case 'subscription.not_renew': await onSubscriptionExpired(data); break; } }

Common Use Cases

SaaS Subscriptions

// Monthly SaaS subscription await createSaasSubscription( customerEmail: 'user@company.com', planCode: 'saas_pro_monthly', authorizationCode: 'AUTH_from_signup', ); // Annual subscription with discount await createAnnualSubscription( customerCode: 'CUS_xyz123', planCode: 'saas_pro_yearly', authorizationCode: 'AUTH_saved_card', );

Content Subscriptions

// Magazine subscription await sdk.subscription.create( CreateSubscriptionOptions( customer: 'subscriber@email.com', plan: 'magazine_monthly', authorization: authCode, metadata: { 'subscription_type': 'digital_magazine', 'delivery_format': 'email', }, ), );

Membership Subscriptions

// Gym membership await sdk.subscription.create( CreateSubscriptionOptions( customer: customerCode, plan: 'gym_premium', authorization: authCode, startDate: DateTime.now().add(Duration(days: 1)), // Start tomorrow metadata: { 'membership_type': 'premium', 'gym_location': 'downtown', }, ), );

Integration with Plans

Use subscriptions with the plans you’ve created:

// First, create plans final basicPlan = await sdk.plan.create(CreatePlanOptions( name: 'Basic Monthly', amount: 299900, // ₦2,999 interval: 'monthly', planCode: 'basic_monthly', )); final proPlan = await sdk.plan.create(CreatePlanOptions( name: 'Pro Monthly', amount: 599900, // ₦5,999 interval: 'monthly', planCode: 'pro_monthly', )); // Then create subscriptions using these plans final subscription = await sdk.subscription.create( CreateSubscriptionOptions( customer: 'customer@example.com', plan: 'basic_monthly', // Use the plan code authorization: authorizationCode, ), );

Next Steps

  • Learn about Plans to create subscription billing structures
  • Explore Transactions for one-time payments
  • Check Payment Methods for customer payment options
  • Review webhook handling for real-time subscription updates
  • Monitor subscription health through the Paystack dashboard

Resources

Last updated on