# Business & Birthdays — Paystack Subscription Guide

> **Version:** 1.1
> **Last Updated:** 2026-07-13
> **Base URL:** `{{APP_URL}}/api/v1`
> **Auth:** Sanctum Bearer Token (returned on login/register)

---

## Table of Contents

1. [Overview](#1-overview)
2. [Admin Setup: Creating and Syncing Plans](#2-admin-setup-creating-and-syncing-plans)
    - [2.1 Create Plans Locally](#21-create-plans-locally)
    - [2.2 Sync Plans to Paystack](#22-sync-plans-to-paystack)
    - [2.3 Verify Plans are Ready](#23-verify-plans-are-ready)
3. [Quick Start & Base Configuration](#3-quick-start--base-configuration)
4. [Subscription Flow Overview](#4-subscription-flow-overview)
5. [Endpoint Reference](#5-endpoint-reference)
   - [5.1 List Subscription Plans](#51-get-v1subscriptionsplans)
   - [5.2 Initialize Subscription Payment](#52-post-v1subscriptionsinitialize)
   - [5.3 Verify Payment & Create Subscription](#53-post-v1subscriptionsverify-and-create)
   - [5.4 Get My Subscription](#54-get-v1subscriptionsmy-subscription)
   - [5.5 Get Subscription History](#55-get-v1subscriptionshistory)
   - [5.6 Enable Subscription](#56-post-v1subscriptionspaystackenable)
   - [5.7 Disable Subscription](#57-post-v1subscriptionspaystackdisable)
   - [5.8 Generate Card Update Link](#58-get-v1subscriptionspaystackcodemanagelink)
   - [5.9 Send Card Update Email](#59-post-v1subscriptionspaystackcodemanageemail)
   - [5.10 Cancel Auto-Renewal](#510-post-v1subscriptionscancel-auto-renew)
   - [5.11 Get Remaining Hours](#511-get-v1subscriptionsremaining-hours)
6. [TypeScript Interfaces](#6-typescript-interfaces)
7. [Complete Workflow Examples](#7-complete-workflow-examples)
8. [Error Handling](#8-error-handling)

---

## 1. Overview

This guide documents the **Paystack Subscription API** integration for the Business & Birthdays platform. The system uses Paystack's recurring payment infrastructure to manage subscription billing automatically.

### Architecture

```
Frontend (React/React Native) → Laravel API → Paystack API
                                    ↕
                            Webhooks (recurring billing updates)
```

### Key Concepts

| Concept | Description |
|---------|-------------|
| **Plan** | A subscription tier defined on Paystack (e.g., Basic ₦2K, Standard ₦3.5K, Premium ₦5K) |
| **Authorization** | A saved card/payment method on Paystack (obtained after first successful payment) |
| **Subscription** | A recurring billing agreement linked to a customer + plan + authorization |
| **Email Token** | A token required to enable/disable a Paystack subscription |
| **Subscription Code** | Paystack's unique identifier for a subscription (e.g., `SUB_xxxxx`) |

---

## 2. Admin Setup: Creating and Syncing Plans

Before users can subscribe, you must create plans on Paystack. The admin endpoint **automatically creates the plan on Paystack** when saving, so no manual sync step is needed.

### 2.1 Create Plan — Full Paystack API Support

**Endpoint:** `POST /api/v1/admin/subscriptions/plans`

This endpoint creates the plan **locally** AND **on Paystack** in one call. It passes the following fields directly to Paystack's `/plan` API:

| Field | Type | Required | Maps To Paystack | Description |
|-------|------|----------|-----------------|-------------|
| `name` | string | ✅ Yes | ✅ `name` | Plan name (e.g., "Basic Plan") |
| `slug` | string | ✅ Yes | ❌ Local only | URL-friendly identifier, unique |
| `price` | number | ✅ Yes | ✅ `amount` (×100 → kobo) | Price in Naira |
| `daily_hours_limit` | integer | ✅ Yes | ❌ Local only | Max hours per day (0 = unlimited) |
| `duration_days` | integer | ✅ Yes | ❌ Local only | Subscription period in days |
| `interval` | string | No | ✅ `interval` | `daily`, `weekly`, `monthly` (default), `quarterly`, `biannually`, `yearly` |
| `currency` | string | No | ✅ `currency` | Default: `NGN` |
| `description` | string | No | ✅ `description` | Plan description sent to Paystack |
| `send_invoices` | boolean | No | ✅ `send_invoices` | Send invoices to customers (default: Paystack default) |
| `send_sms` | boolean | No | ✅ `send_sms` | Send SMS to customers (default: Paystack default) |
| `invoice_limit` | integer | No | ✅ `invoice_limit` | **Max number of payments/subscriptions** for this plan (e.g., `1` = one-time, `12` = 12 cycles) |
| `features` | array | No | ❌ Local only | List of feature descriptions |
| `is_active` | boolean | No | ❌ Local only | Whether plan is active (default: `true`) |

> **Note:** `daily_hours_limit` and `duration_days` are **application-specific** fields that control how the app behaves. They are NOT sent to Paystack. Paystack only controls the billing via `amount`, `interval`, and `invoice_limit`.

#### Example — Basic Plan (with all options):

```json
{
    "name": "Basic Plan",
    "slug": "basic",
    "price": 2000,
    "daily_hours_limit": 6,
    "duration_days": 30,
    "interval": "monthly",
    "currency": "NGN",
    "description": "6 hours daily access for 30 days",
    "send_invoices": true,
    "send_sms": true,
    "invoice_limit": 12,
    "features": [
        "6 Hours Daily Access",
        "Email Support",
        "Portfolio Creation",
        "Access To Resources"
    ]
}
```

#### Example — Premium Plan (unlimited, 12 cycles):

```json
{
    "name": "Premium Plan",
    "slug": "premium",
    "price": 5000,
    "daily_hours_limit": 0,
    "duration_days": 30,
    "interval": "monthly",
    "currency": "NGN",
    "description": "Unlimited daily access with VIP support",
    "send_invoices": true,
    "send_sms": true,
    "invoice_limit": 12,
    "features": [
        "Unlimited Daily Access",
        "VIP Priority Support",
        "Birthday Reward Eligibility",
        "Exclusive Business Tools"
    ]
}
```

#### Example — Enterprise Plan (quarterly, 4 cycles = 1 year):

```json
{
    "name": "Enterprise Plan",
    "slug": "enterprise",
    "price": 10000,
    "daily_hours_limit": 0,
    "duration_days": 90,
    "interval": "quarterly",
    "currency": "NGN",
    "description": "Quarterly enterprise subscription with dedicated support",
    "send_invoices": true,
    "send_sms": false,
    "invoice_limit": 4,
    "features": [
        "Unlimited Daily Access",
        "90 Days Duration",
        "Dedicated Account Manager",
        "API Access",
        "Team Collaboration Tools"
    ]
}
```

#### Example — One-Time Payment Plan (invoice_limit: 1):

```json
{
    "name": "Annual Pass",
    "slug": "annual-pass",
    "price": 20000,
    "daily_hours_limit": 0,
    "duration_days": 365,
    "interval": "annually",
    "currency": "NGN",
    "description": "One-time annual payment",
    "send_invoices": false,
    "send_sms": false,
    "invoice_limit": 1,
    "features": [
        "Unlimited Daily Access",
        "Full Year Access",
        "All Premium Features"
    ]
}
```

### 2.2 Sync Existing Plans to Paystack

If you already have plans in the database that were created **before** the auto-sync feature, run:

```bash
# Sync all plans without a Paystack code
php artisan paystack:sync-plans

# Sync a specific plan
php artisan paystack:sync-plans --plan-id=1

# Force re-sync (overwrites existing codes)
php artisan paystack:sync-plans --force
```

### 2.3 Verify Plans are Ready

```bash
curl -H "Accept: application/json" {{APP_URL}}/api/v1/subscriptions/plans
```

```json
{
    "success": true,
    "data": {
        "plans": [
            {
                "id": 1,
                "name": "Basic Plan",
                "slug": "basic",
                "price": 2000.00,
                "daily_hours_limit": 6,
                "duration_days": 30,
                "features": ["6 Hours Daily Access", "Email Support", "Portfolio Creation"]
            }
        ]
    }
}
```

---

## 3. Quick Start & Base Configuration

### Base Configuration

```typescript
const API_BASE_URL = 'https://yourdomain.com/api/v1';

// Axios instance with auth interceptor
const api = axios.create({
    baseURL: API_BASE_URL,
    headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json',
    }
});

// Attach auth token
api.interceptors.request.use(config => {
    const token = localStorage.getItem('auth_token');
    if (token) {
        config.headers.Authorization = `Bearer ${token}`;
    }
    return config;
});
```

### Paystack Configuration (Frontend)

Include Paystack's inline script in your HTML or use the Paystack React Native SDK:

```html
<script src="https://js.paystack.co/v1/inline.js"></script>
```

For React Native, use `react-native-paystack-webview` or `@paystack/react-native`.

---

## 4. Subscription Flow Overview

### Full Subscription Flow (First Time User)

```mermaid
sequenceDiagram
    participant User
    participant Frontend
    participant Backend
    participant Paystack

    User->>Frontend: 1. Select subscription plan
    Frontend->>Backend: 2. POST /subscriptions/initialize {plan_id}
    Backend->>Paystack: 3. POST /transaction/initialize
    Paystack-->>Backend: 4. {authorization_url, reference}
    Backend-->>Frontend: 5. {authorization_url, reference}
    Frontend->>Paystack: 6. Redirect user to authorization_url
    User->>Paystack: 7. Enter card details & complete payment
    Paystack-->>Frontend: 8. Redirect back with ?reference=xxx
    Frontend->>Backend: 9. POST /subscriptions/verify-and-create {reference, plan_id}
    Backend->>Paystack: 10. GET /transaction/verify/{reference}
    Backend->>Paystack: 11. POST /subscription {customer, plan, authorization}
    Paystack-->>Backend: 12. {subscription_code, email_token, status: active}
    Backend-->>Frontend: 13. {subscription details}
    Frontend-->>User: 14. Show "Subscription Active" screen
```

### Step-by-Step Frontend Implementation

#### Step 1: Fetch Available Plans

```typescript
// GET /v1/subscriptions/plans
const { data } = await api.get('/subscriptions/plans');
const plans = data.data.plans;
// Display plans to user as cards/options
```

#### Step 2: Initialize Payment

```typescript
// POST /v1/subscriptions/initialize
const planId = selectedPlan.id; // From step 1
const { data } = await api.post('/subscriptions/initialize', {
    plan_id: planId,
});

const { authorization_url, reference } = data.data;
```

#### Step 3: Redirect to Paystack Checkout

```typescript
// Option A: Redirect (web)
window.location.href = authorization_url;

// Option B: Inline Popup (web - Paystack Popup)
const handler = PaystackPop.setup({
    key: 'pk_test_xxxxxxxxxxxxx', // Your Paystack public key
    email: user.email,
    amount: selectedPlan.price * 100, // Amount in kobo
    ref: reference, // Reference from step 2
    callback: function(response) {
        // User completed payment
        // Proceed to step 4 with response.reference
        verifyAndCreateSubscription(response.reference, planId);
    },
    onClose: function() {
        // User closed the popup without completing
        console.log('Payment cancelled');
    }
});
handler.openIframe();
```

#### Step 4: Verify & Create Subscription

```typescript
// POST /v1/subscriptions/verify-and-create
async function verifyAndCreateSubscription(reference: string, planId: number) {
    try {
        const { data } = await api.post('/subscriptions/verify-and-create', {
            reference: reference,
            plan_id: planId,
        });

        if (data.success) {
            // Subscription created successfully
            const subscription = data.data.subscription;
            showSuccessScreen(subscription);
        }
    } catch (error) {
        handleError(error);
    }
}
```

#### Step 5: Check Subscription Status

```typescript
// GET /v1/subscriptions/my-subscription
const { data } = await api.get('/subscriptions/my-subscription');
const { has_active_subscription, subscription } = data.data;
```

---

## 5. Endpoint Reference

### 5.1 GET `/v1/subscriptions/plans`

**Auth:** None (Public)  
**Description:** List all active subscription plans available on the platform.

**Response (200):**
```json
{
    "success": true,
    "message": "Plans retrieved successfully",
    "data": {
        "plans": [
            {
                "id": 1,
                "name": "Basic Plan",
                "slug": "basic",
                "price": 2000.00,
                "daily_hours_limit": 6,
                "duration_days": 30,
                "features": [
                    "6 hours daily access",
                    "Basic portfolio",
                    "Email support"
                ]
            },
            {
                "id": 2,
                "name": "Standard Plan",
                "slug": "standard",
                "price": 3500.00,
                "daily_hours_limit": 12,
                "duration_days": 30,
                "features": [
                    "12 hours daily access",
                    "Enhanced portfolio",
                    "Priority support"
                ]
            },
            {
                "id": 3,
                "name": "Premium Plan",
                "slug": "premium",
                "price": 5000.00,
                "daily_hours_limit": 0,
                "duration_days": 30,
                "features": [
                    "Unlimited daily access",
                    "Premium portfolio",
                    "VIP support",
                    "Birthday rewards eligibility"
                ]
            }
        ]
    }
}
```

---

### 5.2 POST `/v1/subscriptions/initialize`

**Auth:** Bearer Token (User)  
**Description:** Initialize a Paystack transaction for a subscription plan. Returns an `authorization_url` that the frontend should use to redirect the user to Paystack's checkout page.

**Request Body:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `plan_id` | integer | Yes | The ID of the subscription plan |
| `authorization_code` | string | No | Specific authorization code to use (if customer has multiple saved cards) |

**Example Request:**
```json
{
    "plan_id": 1
}
```

**Response (200):**
```json
{
    "success": true,
    "message": "Subscription payment initialized. Redirect user to authorization_url.",
    "data": {
        "authorization_url": "https://checkout.paystack.com/0peioxfhpn",
        "reference": "trx-ps-67890abcde",
        "plan": {
            "id": 1,
            "name": "Basic Plan",
            "price": 2000.00
        }
    }
}
```

**Error Responses:**

| Status | Message | Description |
|--------|---------|-------------|
| 401 | Unauthenticated | User not logged in |
| 400 | You already have an active subscription | User already subscribed |
| 400 | This plan is not configured for Paystack payments | Plan missing Paystack plan code |
| 422 | Validation error | Missing or invalid `plan_id` |
| 500 | Failed to initialize subscription | Internal server error |

---

### 5.3 POST `/v1/subscriptions/verify-and-create`

**Auth:** Bearer Token (User)  
**Description:** After the user completes payment on Paystack, call this endpoint to verify the transaction and create a Paystack subscription. This sets up recurring billing.

**Request Body:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `reference` | string | Yes | The transaction reference returned from `initialize` |
| `plan_id` | integer | Yes | The ID of the subscription plan |

**Example Request:**
```json
{
    "reference": "trx-ps-67890abcde",
    "plan_id": 1
}
```

**Response (200):**
```json
{
    "success": true,
    "message": "Paystack subscription created successfully",
    "data": {
        "subscription": {
            "id": 10,
            "plan_name": "Basic Plan",
            "start_date": "2026-07-12",
            "end_date": "2026-08-11",
            "days_remaining": 30,
            "payment_status": "paid",
            "auto_renew": true,
            "paystack_subscription_code": "SUB_vsyqdmlzble3uii",
            "next_payment_date": "2026-08-11"
        }
    }
}
```

**Error Responses:**

| Status | Message | Description |
|--------|---------|-------------|
| 400 | Payment verification failed | Invalid or expired reference |
| 400 | No authorization found | Payment may not be complete |
| 500 | Unable to create subscription | Paystack API error |

---

### 5.4 GET `/v1/subscriptions/my-subscription`

**Auth:** Bearer Token (User)  
**Description:** Get the current user's active subscription details including daily remaining hours.

**Response (200) — Active Subscription:**
```json
{
    "success": true,
    "message": "Subscription retrieved successfully",
    "data": {
        "has_active_subscription": true,
        "subscription": {
            "id": 10,
            "plan_name": "Basic Plan",
            "plan_slug": "basic",
            "price": 2000.00,
            "start_date": "2026-07-12",
            "end_date": "2026-08-11",
            "days_remaining": 30,
            "payment_status": "paid",
            "auto_renew": true,
            "daily_hours": {
                "limit": 6,
                "limit_minutes": 360,
                "used_minutes": 45,
                "remaining_minutes": 315
            }
        }
    }
}
```

**Response (200) — No Active Subscription:**
```json
{
    "success": true,
    "message": "No active subscription",
    "data": {
        "has_active_subscription": false,
        "subscription": null
    }
}
```

---

### 5.5 GET `/v1/subscriptions/history`

**Auth:** Bearer Token (User)  
**Description:** Get subscription history for the authenticated user with pagination.

**Query Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `page` | integer | No | Page number (default: 1) |
| `per_page` | integer | No | Items per page (default: 20) |

**Response (200):**
```json
{
    "success": true,
    "message": "Subscription history retrieved successfully",
    "data": {
        "subscriptions": [
            {
                "id": 10,
                "plan": "Basic Plan",
                "start_date": "2026-07-12",
                "end_date": "2026-08-11",
                "amount": 2000.00,
                "payment_status": "paid",
                "is_active": true,
                "created_at": "2026-07-12 10:30:00"
            },
            {
                "id": 9,
                "plan": "Basic Plan",
                "start_date": "2026-06-12",
                "end_date": "2026-07-12",
                "amount": 2000.00,
                "payment_status": "paid",
                "is_active": false,
                "created_at": "2026-06-12 10:30:00"
            }
        ],
        "pagination": {
            "current_page": 1,
            "per_page": 20,
            "total": 2,
            "last_page": 1
        }
    }
}
```

---

### 5.6 POST `/v1/subscriptions/paystack/enable`

**Auth:** Bearer Token (User)  
**Description:** Enable a previously disabled Paystack subscription. Requires the subscription code and email token.

**Request Body:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `subscription_code` | string | Yes | The Paystack subscription code (e.g., `SUB_xxxxx`) |
| `email_token` | string | Yes | The email token for the subscription |

**Example Request:**
```json
{
    "subscription_code": "SUB_vsyqdmlzble3uii",
    "email_token": "d7gofp6yppn3qz7"
}
```

**Response (200):**
```json
{
    "success": true,
    "message": "Subscription enabled successfully",
    "data": {
        "subscription_id": 10,
        "paystack_status": "active"
    }
}
```

---

### 5.7 POST `/v1/subscriptions/paystack/disable`

**Auth:** Bearer Token (User)  
**Description:** Disable an active Paystack subscription. Prevents future recurring charges. Requires the subscription code and email token.

**Request Body:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `subscription_code` | string | Yes | The Paystack subscription code (e.g., `SUB_xxxxx`) |
| `email_token` | string | Yes | The email token for the subscription |

**Example Request:**
```json
{
    "subscription_code": "SUB_vsyqdmlzble3uii",
    "email_token": "d7gofp6yppn3qz7"
}
```

**Response (200):**
```json
{
    "success": true,
    "message": "Subscription disabled successfully",
    "data": {
        "subscription_id": 10,
        "paystack_status": "cancelled"
    }
}
```

---

### 5.8 GET `/v1/subscriptions/paystack/{code}/manage/link`

**Auth:** Bearer Token (User)  
**Description:** Generate a link that the user can use to update their card/payment method on a subscription.

**Path Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `code` | string | Yes | The Paystack subscription code (e.g., `SUB_xxxxx`) |

**Response (200):**
```json
{
    "success": true,
    "message": "Card update link generated successfully",
    "data": {
        "link": "https://paystack.com/manage/subscriptions/qlgwhpyq1ts9nsw?subscription_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
        "subscription_code": "SUB_vsyqdmlzble3uii"
    }
}
```

**Usage:** Open the `link` in a browser/webview so the user can update their card details.

```typescript
// Open in new window (web)
window.open(data.data.link, '_blank');

// Open in WebView (React Native)
<WebView source={{ uri: data.data.link }} />
```

---

### 5.9 POST `/v1/subscriptions/paystack/{code}/manage/email`

**Auth:** Bearer Token (User)  
**Description:** Send an email to the customer containing a link to update their card on the subscription.

**Path Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `code` | string | Yes | The Paystack subscription code (e.g., `SUB_xxxxx`) |

**Response (200):**
```json
{
    "success": true,
    "message": "Card update email sent successfully",
    "data": {
        "subscription_code": "SUB_vsyqdmlzble3uii"
    }
}
```

---

### 5.10 POST `/v1/subscriptions/cancel-auto-renew`

**Auth:** Bearer Token (User)  
**Description:** Cancel auto-renewal for the user's active subscription. The subscription remains active until the end date but won't renew.

**Response (200):**
```json
{
    "success": true,
    "message": "Auto-renewal cancelled successfully",
    "data": {
        "subscription_id": 10,
        "auto_renew": false
    }
}
```

---

### 5.11 GET `/v1/subscriptions/remaining-hours`

**Auth:** Bearer Token (User)  
**Description:** Get the remaining hours for today based on the user's active subscription plan.

**Response (200):**
```json
{
    "success": true,
    "message": "Remaining hours retrieved",
    "data": {
        "daily_hours": {
            "limit": 6,
            "limit_minutes": 360,
            "used_minutes": 45,
            "remaining_minutes": 315
        }
    }
}
```

---

## 6. TypeScript Interfaces

```typescript
// ──────────────────────────────────────────────
// Subscription Plan
// ──────────────────────────────────────────────
interface SubscriptionPlan {
    id: number;
    name: string;
    slug: string;
    price: number;
    daily_hours_limit: number; // 0 = unlimited
    duration_days: number;
    features: string[] | null;
}

// ──────────────────────────────────────────────
// Daily Hours Info
// ──────────────────────────────────────────────
interface DailyHoursInfo {
    limit: number;           // Daily limit in hours (0 = unlimited)
    limit_minutes: number;   // Daily limit in minutes
    used_minutes: number;    // Minutes used today
    remaining_minutes: number; // Minutes remaining (-1 = unlimited)
}

// ──────────────────────────────────────────────
// Active Subscription
// ──────────────────────────────────────────────
interface ActiveSubscription {
    id: number;
    plan_name: string;
    plan_slug: string;
    price: number;
    start_date: string; // YYYY-MM-DD
    end_date: string;   // YYYY-MM-DD
    days_remaining: number;
    payment_status: 'pending' | 'paid' | 'failed' | 'refunded';
    auto_renew: boolean;
    paystack_subscription_code: string | null;
    next_payment_date: string | null; // YYYY-MM-DD
    daily_hours: DailyHoursInfo;
}

// ──────────────────────────────────────────────
// Subscription History Item
// ──────────────────────────────────────────────
interface SubscriptionHistoryItem {
    id: number;
    plan: string;
    start_date: string;
    end_date: string;
    amount: number;
    payment_status: string;
    is_active: boolean;
    created_at: string;
}

// ──────────────────────────────────────────────
// Pagination
// ──────────────────────────────────────────────
interface PaginationInfo {
    current_page: number;
    per_page: number;
    total: number;
    last_page: number;
}

// ──────────────────────────────────────────────
// API Response Wrappers
// ──────────────────────────────────────────────
interface ApiResponse<T> {
    success: boolean;
    message: string;
    data: T;
}

interface ErrorResponse {
    success: false;
    message: string;
    code: number;
    errors?: Record<string, string[]>;
}

// ──────────────────────────────────────────────
// Plans Response
// ──────────────────────────────────────────────
interface PlansResponse {
    plans: SubscriptionPlan[];
}

// ──────────────────────────────────────────────
// Initialize Subscription Response
// ──────────────────────────────────────────────
interface InitializeSubscriptionResponse {
    authorization_url: string;
    reference: string;
    plan: {
        id: number;
        name: string;
        price: number;
    };
}

// ──────────────────────────────────────────────
// Verify & Create Subscription Response
// ──────────────────────────────────────────────
interface VerifyAndCreateResponse {
    subscription: {
        id: number;
        plan_name: string;
        start_date: string;
        end_date: string;
        days_remaining: number;
        payment_status: string;
        auto_renew: boolean;
        paystack_subscription_code: string | null;
        next_payment_date: string | null;
    };
}

// ──────────────────────────────────────────────
// My Subscription Response
// ──────────────────────────────────────────────
interface MySubscriptionResponse {
    has_active_subscription: boolean;
    subscription: ActiveSubscription | null;
}

// ──────────────────────────────────────────────
// History Response
// ──────────────────────────────────────────────
interface HistoryResponse {
    subscriptions: SubscriptionHistoryItem[];
    pagination: PaginationInfo;
}

// ──────────────────────────────────────────────
// Enable/Disable Response
// ──────────────────────────────────────────────
interface EnableDisableResponse {
    subscription_id: number;
    paystack_status: string;
}

// ──────────────────────────────────────────────
// Manage Link Response
// ──────────────────────────────────────────────
interface ManageLinkResponse {
    link: string;
    subscription_code: string;
}

// ──────────────────────────────────────────────
// Remaining Hours Response
// ──────────────────────────────────────────────
interface RemainingHoursResponse {
    daily_hours: DailyHoursInfo;
}
```

---

## 7. Complete Workflow Examples

### 6.1 Complete Subscription Purchase Flow

```typescript
import axios from 'axios';
import { Paystack } from 'react-native-paystack'; // or PaystackPop for web

const api = axios.create({
    baseURL: 'https://yourdomain.com/api/v1',
    headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' }
});

// Attach auth token
api.interceptors.request.use(config => {
    const token = localStorage.getItem('auth_token');
    if (token) config.headers.Authorization = `Bearer ${token}`;
    return config;
});

class SubscriptionService {
    /**
     * Step 1: Fetch available plans
     */
    async getPlans(): Promise<SubscriptionPlan[]> {
        const { data } = await api.get<ApiResponse<PlansResponse>>('/subscriptions/plans');
        return data.data.plans;
    }

    /**
     * Step 2: Initialize payment
     */
    async initializePayment(planId: number): Promise<InitializeSubscriptionResponse> {
        const { data } = await api.post<ApiResponse<InitializeSubscriptionResponse>>(
            '/subscriptions/initialize',
            { plan_id: planId }
        );
        return data.data;
    }

    /**
     * Step 3: Process payment via Paystack
     */
    async processPaystackPayment(
        paystackPublicKey: string,
        email: string,
        amount: number,
        reference: string
    ): Promise<boolean> {
        return new Promise((resolve, reject) => {
            // For web: Use PaystackPop
            const handler = (window as any).PaystackPop.setup({
                key: paystackPublicKey,
                email,
                amount: Math.round(amount * 100), // Convert to kobo
                ref: reference,
                callback: () => resolve(true),
                onClose: () => resolve(false),
            });
            handler.openIframe();
        });
    }

    /**
     * Step 4: Verify payment and create subscription
     */
    async verifyAndCreate(
        reference: string,
        planId: number
    ): Promise<VerifyAndCreateResponse> {
        const { data } = await api.post<ApiResponse<VerifyAndCreateResponse>>(
            '/subscriptions/verify-and-create',
            { reference, plan_id: planId }
        );
        return data.data;
    }

    /**
     * Complete purchase flow
     */
    async purchaseSubscription(
        planId: number,
        paystackPublicKey: string,
        userEmail: string
    ): Promise<VerifyAndCreateResponse> {
        // Step 1: Initialize
        const initResult = await this.initializePayment(planId);

        // Step 2: Process Paystack payment
        const paid = await this.processPaystackPayment(
            paystackPublicKey,
            userEmail,
            initResult.plan.price,
            initResult.reference
        );

        if (!paid) {
            throw new Error('Payment was cancelled by user');
        }

        // Step 3: Verify and create subscription
        const subscription = await this.verifyAndCreate(
            initResult.reference,
            planId
        );

        return subscription;
    }

    /**
     * Get current subscription status
     */
    async getMySubscription(): Promise<MySubscriptionResponse> {
        const { data } = await api.get<ApiResponse<MySubscriptionResponse>>(
            '/subscriptions/my-subscription'
        );
        return data.data;
    }

    /**
     * Enable subscription
     */
    async enableSubscription(
        subscriptionCode: string,
        emailToken: string
    ): Promise<EnableDisableResponse> {
        const { data } = await api.post<ApiResponse<EnableDisableResponse>>(
            '/subscriptions/paystack/enable',
            {
                subscription_code: subscriptionCode,
                email_token: emailToken,
            }
        );
        return data.data;
    }

    /**
     * Disable subscription
     */
    async disableSubscription(
        subscriptionCode: string,
        emailToken: string
    ): Promise<EnableDisableResponse> {
        const { data } = await api.post<ApiResponse<EnableDisableResponse>>(
            '/subscriptions/paystack/disable',
            {
                subscription_code: subscriptionCode,
                email_token: emailToken,
            }
        );
        return data.data;
    }

    /**
     * Get card update link
     */
    async getCardUpdateLink(
        subscriptionCode: string
    ): Promise<ManageLinkResponse> {
        const { data } = await api.get<ApiResponse<ManageLinkResponse>>(
            `/subscriptions/paystack/${subscriptionCode}/manage/link`
        );
        return data.data;
    }

    /**
     * Send card update email
     */
    async sendCardUpdateEmail(subscriptionCode: string): Promise<void> {
        await api.post(
            `/subscriptions/paystack/${subscriptionCode}/manage/email`
        );
    }
}

export default new SubscriptionService();
```

### 6.2 Subscription Management Screen (React)

```tsx
import React, { useState, useEffect } from 'react';
import SubscriptionService from './SubscriptionService';

const SubscriptionScreen: React.FC = () => {
    const [subscription, setSubscription] = useState<ActiveSubscription | null>(null);
    const [loading, setLoading] = useState(true);

    useEffect(() => {
        loadSubscription();
    }, []);

    const loadSubscription = async () => {
        try {
            const result = await SubscriptionService.getMySubscription();
            setSubscription(result.subscription);
        } catch (error) {
            console.error('Failed to load subscription:', error);
        } finally {
            setLoading(false);
        }
    };

    const handleDisable = async () => {
        if (!subscription?.paystack_subscription_code) return;

        try {
            const userInput = prompt('Enter your email token:');
            if (!userInput) return;

            await SubscriptionService.disableSubscription(
                subscription.paystack_subscription_code,
                userInput
            );
            await loadSubscription(); // Refresh
        } catch (error) {
            console.error('Failed to disable:', error);
        }
    };

    const handleCardUpdate = async () => {
        if (!subscription?.paystack_subscription_code) return;

        try {
            const result = await SubscriptionService.getCardUpdateLink(
                subscription.paystack_subscription_code
            );
            window.open(result.link, '_blank');
        } catch (error) {
            console.error('Failed to get update link:', error);
        }
    };

    if (loading) return <div>Loading...</div>;

    if (!subscription) {
        return <div>No active subscription. <a href="/plans">View Plans</a></div>;
    }

    return (
        <div className="subscription-card">
            <h2>{subscription.plan_name}</h2>
            <p>Status: {subscription.payment_status}</p>
            <p>Days Remaining: {subscription.days_remaining}</p>
            <p>Auto-Renew: {subscription.auto_renew ? 'Yes' : 'No'}</p>
            <p>Next Payment: {subscription.next_payment_date || 'N/A'}</p>

            <div className="daily-hours">
                <p>Daily Limit: {subscription.daily_hours.limit}h</p>
                <p>Used Today: {subscription.daily_hours.used_minutes}m</p>
                <p>Remaining: {subscription.daily_hours.remaining_minutes}m</p>
            </div>

            <div className="actions">
                <button onClick={handleDisable}>
                    Disable Auto-Renew
                </button>
                <button onClick={handleCardUpdate}>
                    Update Card
                </button>
            </div>
        </div>
    );
};
```

### 6.3 Admin: Fetch and Sync Paystack Plans

```typescript
// Admin only: List Paystack plans
async function fetchPaystackPlans() {
    const { data } = await api.get('/admin/subscriptions/paystack/plans');
    return data.data.plans;
}

// Admin only: List Paystack subscriptions
async function fetchPaystackSubscriptions() {
    const { data } = await api.get('/admin/subscriptions/paystack/subscriptions');
    return data.data.subscriptions;
}

// Admin only: Fetch specific subscription details from Paystack
async function fetchPaystackSubscription(code: string) {
    const { data } = await api.get(`/admin/subscriptions/paystack/subscriptions/${code}`);
    return data.data.subscription;
}
```

---

## 8. Error Handling

### Error Response Format

All errors follow this structure:

```json
{
    "success": false,
    "message": "Human-readable error description",
    "code": 400
}
```

For validation errors:

```json
{
    "success": false,
    "message": "The plan_id field is required.",
    "code": 422,
    "errors": {
        "plan_id": ["The plan_id field is required."]
    }
}
```

### Common Error Codes

| Code | Meaning | Handling |
|------|---------|----------|
| 401 | Unauthenticated | Redirect to login screen |
| 400 | Bad request (already subscribed, invalid plan) | Show error message to user |
| 404 | Not found (subscription not found) | Show "not found" message |
| 422 | Validation error | Show specific field errors |
| 500 | Server error | Show generic error, retry later |

### Frontend Error Handler

```typescript
import axios, { AxiosError } from 'axios';

interface AppError {
    message: string;
    code: number;
    fields?: Record<string, string[]>;
}

function handleApiError(error: unknown): AppError {
    if (axios.isAxiosError(error)) {
        const response = error.response?.data;
        if (response?.success === false) {
            return {
                message: response.message || 'An error occurred',
                code: response.code || error.response?.status || 500,
                fields: response.errors,
            };
        }
        // Network or timeout error
        return {
            message: error.message || 'Network error. Please check your connection.',
            code: error.response?.status || 0,
        };
    }
    // Unknown error
    return {
        message: 'An unexpected error occurred',
        code: 500,
    };
}

// Usage
try {
    await api.post('/subscriptions/verify-and-create', data);
} catch (error) {
    const appError = handleApiError(error);
    showToast(appError.message, 'error');
}
```

---

## Appendix: Webhook Events

The backend automatically processes these Paystack webhook events to keep subscriptions in sync:

| Paystack Event | Backend Action |
|----------------|----------------|
| `subscription.create` | Updates local subscription status to `active`, sets `payment_status: paid` |
| `subscription.disable` | Marks local subscription as `cancelled`, sets `is_active: false` |
| `invoice.updated` (success) | Extends subscription `end_date`, updates `next_payment_date` |
| `invoice.updated` (failed) | Sets `payment_status: failed` |
| `invoice.create` | Sets `payment_status: pending`, notifies user about upcoming payment |

These are handled server-side and require no frontend action.

---

## Appendix: Admin Endpoints Summary

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/v1/admin/subscriptions` | List all local subscriptions with filters |
| POST | `/v1/admin/subscriptions/plans` | Create a new subscription plan |
| PUT | `/v1/admin/subscriptions/plans/{id}` | Update a subscription plan |
| DELETE | `/v1/admin/subscriptions/plans/{id}` | Deactivate a subscription plan |
| GET | `/v1/admin/subscriptions/paystack/plans` | List plans from Paystack |
| GET | `/v1/admin/subscriptions/paystack/subscriptions` | List subscriptions from Paystack |
| GET | `/v1/admin/subscriptions/paystack/subscriptions/{code}` | Fetch a subscription from Paystack |
