# Payment System Documentation

> **Version:** 1.6.0  
> **Last Updated:** January 8, 2026  
> **Status:** Production Ready

This document provides comprehensive documentation of the IGNY8 payment system architecture, implementation, and flows.

---

## Table of Contents

1. [System Overview](#system-overview)
2. [Payment Entry Points](#payment-entry-points)
3. [Backend Architecture](#backend-architecture)
4. [Frontend Architecture](#frontend-architecture)
5. [Payment Flows](#payment-flows)
6. [Country-Based Payment Rules](#country-based-payment-rules)
7. [Webhook Processing](#webhook-processing)
8. [Models Reference](#models-reference)
9. [Security Features](#security-features)

---

## System Overview

### Supported Payment Methods

| Method | Type | Regions | Use Cases |
|--------|------|---------|-----------|
| **Stripe** | Credit/Debit Card | Global | Subscriptions, Credit packages |
| **PayPal** | PayPal account | Global (except PK) | Subscriptions, Credit packages |
| **Bank Transfer** | Manual | Pakistan (PK) | Subscriptions, Credit packages |

### Payment Method Selection Logic

```
┌────────────────────────────────────────────────────────┐
│              Country-Based Payment Rules               │
├────────────────────────────────────────────────────────┤
│                                                        │
│  Global Users (non-PK):                                │
│    ✅ Stripe (Credit/Debit Card)                       │
│    ✅ PayPal                                           │
│    ❌ Bank Transfer (not available)                    │
│                                                        │
│  Pakistan Users (PK):                                  │
│    ✅ Stripe (Credit/Debit Card)                       │
│    ❌ PayPal (not available in PK)                     │
│    ✅ Bank Transfer (manual)                           │
│                                                        │
└────────────────────────────────────────────────────────┘
```

### Architecture Overview

```
┌─────────────────────────────────────────────────────────────────┐
│                    PAYMENT SYSTEM FLOW                           │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  ┌──────────────┐         ┌──────────────┐                      │
│  │   Signup     │───────▶ │  /account/   │                      │
│  │   (no pay)   │         │   plans      │                      │
│  └──────────────┘         └──────┬───────┘                      │
│                                  │                               │
│                    ┌─────────────┴─────────────┐                 │
│                    │                           │                 │
│              New User?                   Existing User?          │
│                    │                           │                 │
│                    ▼                           ▼                 │
│           ┌──────────────┐           ┌──────────────┐           │
│           │ PendingPay-  │           │ Plans/Billing│           │
│           │   mentView   │           │  Dashboard   │           │
│           └──────┬───────┘           └──────┬───────┘           │
│                  │                          │                    │
│        ┌─────────┼─────────┐      ┌────────┼────────┐           │
│        │         │         │      │        │        │           │
│        ▼         ▼         ▼      ▼        ▼        ▼           │
│     Stripe    PayPal    Bank   Upgrade  Credits  Manage         │
│                      Transfer                                    │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘
```

---

## Payment Entry Points

### 1. Signup Flow

**File:** `frontend/src/components/auth/SignUpFormUnified.tsx`

**Simplified Signup (No Payment on Signup):**
- User selects plan and provides details
- Account created with `status='pending_payment'` for paid plans
- User redirected to `/account/plans` to complete payment
- No payment gateway redirect from signup page

```typescript
// Signup flow creates account only, no checkout
const handleSignup = async (data) => {
  const result = await register({
    email, password, plan_slug, billing_country
  });
  // Redirect to plans page for payment
  navigate('/account/plans');
};
```

### 2. Plans & Billing Page

**File:** `frontend/src/pages/account/PlansAndBillingPage.tsx`

Central hub for all payment-related actions:

**For New Users (pending_payment):**
- Shows `PendingPaymentView` component
- Full-page payment interface
- Invoice details and payment method selection

**For Existing Users:**
- Current plan and subscription status
- Credit balance and purchase
- Invoice history and downloads
- Subscription management

### 3. PendingPaymentView Component

**File:** `frontend/src/components/billing/PendingPaymentView.tsx`

Full-page payment interface for new users:
- Displays invoice details and plan info
- Payment method selection based on country
- Stripe/PayPal redirect or Bank Transfer form
- Status checking for bank transfer submissions

### 4. Bank Transfer Form

**File:** `frontend/src/components/billing/BankTransferForm.tsx`

Manual payment submission for Pakistan users:
- Bank account details display
- Transaction reference input
- File upload for payment proof
- Submission and status tracking

---

## Backend Architecture

### Service Layer

#### StripeService

**File:** `backend/igny8_core/business/billing/services/stripe_service.py`

| Method | Description |
|--------|-------------|
| `create_checkout_session()` | Create subscription checkout |
| `create_credit_checkout_session()` | Create credit package checkout |
| `create_billing_portal_session()` | Customer billing portal |
| `get_or_create_customer()` | Stripe customer management |
| `construct_webhook_event()` | Verify webhook signatures |

#### PayPalService

**File:** `backend/igny8_core/business/billing/services/paypal_service.py`

| Method | Description |
|--------|-------------|
| `create_order()` | Create one-time payment order |
| `create_subscription_order()` | Create subscription order |
| `capture_order()` | Capture approved payment |
| `verify_webhook_signature()` | Webhook verification |

#### InvoiceService

**File:** `backend/igny8_core/business/billing/services/invoice_service.py`

| Method | Description |
|--------|-------------|
| `create_subscription_invoice()` | Invoice for plan subscription |
| `create_credit_package_invoice()` | Invoice for credit purchase |
| `mark_paid()` | Mark invoice as paid |
| `generate_pdf()` | Generate PDF invoice |

#### PaymentService

**File:** `backend/igny8_core/business/billing/services/payment_service.py`

| Method | Description |
|--------|-------------|
| `create_stripe_payment()` | Record Stripe payment |
| `create_paypal_payment()` | Record PayPal payment |
| `create_manual_payment()` | Record bank transfer |
| `approve_manual_payment()` | Admin approval |

### API Endpoints

#### Stripe Endpoints

| Endpoint | Method | Description |
|----------|--------|-------------|
| `/v1/billing/stripe/config/` | GET | Publishable key |
| `/v1/billing/stripe/checkout/` | POST | Create checkout session |
| `/v1/billing/stripe/credit-checkout/` | POST | Credit package checkout |
| `/v1/billing/stripe/billing-portal/` | POST | Billing portal |
| `/v1/billing/webhooks/stripe/` | POST | Webhook handler |

#### PayPal Endpoints

| Endpoint | Method | Description |
|----------|--------|-------------|
| `/v1/billing/paypal/config/` | GET | Client ID |
| `/v1/billing/paypal/create-order/` | POST | Credit package order |
| `/v1/billing/paypal/create-subscription-order/` | POST | Subscription order |
| `/v1/billing/paypal/capture-order/` | POST | Capture payment |
| `/v1/billing/webhooks/paypal/` | POST | Webhook handler |

#### Invoice Endpoints

| Endpoint | Method | Description |
|----------|--------|-------------|
| `/v1/billing/invoices/` | GET | List invoices |
| `/v1/billing/invoices/{id}/` | GET | Invoice detail |
| `/v1/billing/invoices/{id}/download_pdf/` | GET | Download PDF |

#### Payment Endpoints

| Endpoint | Method | Description |
|----------|--------|-------------|
| `/v1/billing/payments/` | GET | List payments |
| `/v1/billing/payments/manual/` | POST | Submit bank transfer |
| `/v1/billing/admin/payments/confirm/` | POST | Admin approve/reject |

---

## Frontend Architecture

### Services

**File:** `frontend/src/services/billing.api.ts`

Key functions:

```typescript
// Gateway availability (country-based)
getAvailablePaymentGateways(userCountry?: string)

// Subscription helpers
subscribeToPlan(planId, gateway, options)
purchaseCredits(packageId, gateway, options)

// Stripe functions
createStripeCheckout(planId, options)
createStripeCreditCheckout(packageId, options)
openStripeBillingPortal(returnUrl)

// PayPal functions
createPayPalSubscriptionOrder(planId, options)
createPayPalCreditOrder(packageId, options)
capturePayPalOrder(orderId, metadata)

// Manual payment
submitManualPayment(invoiceId, data)
```

### Components

| Component | File | Purpose |
|-----------|------|---------|
| `PendingPaymentView` | `/components/billing/PendingPaymentView.tsx` | New user payment |
| `BankTransferForm` | `/components/billing/BankTransferForm.tsx` | Bank transfer submission |
| `PendingPaymentBanner` | `/components/billing/PendingPaymentBanner.tsx` | Alert for pending payments |
| `PaymentGatewaySelector` | `/components/billing/PaymentGatewaySelector.tsx` | Gateway selection UI |
| `PayInvoiceModal` | `/components/billing/PayInvoiceModal.tsx` | Pay invoice modal |

---

## Payment Flows

### Flow 1: New User Signup with Stripe

```
1. User submits signup form with plan
2. Backend creates:
   - User account
   - Account (status='pending_payment')
   - Subscription (status='pending_payment')
   - Invoice (status='pending')
3. User redirected to /account/plans
4. PendingPaymentView displays
5. User selects Stripe, clicks Pay
6. Redirect to Stripe Checkout
7. User completes payment
8. Stripe webhook received:
   - Payment recorded
   - Invoice marked paid
   - Account activated
   - Credits added
9. User redirected back to /account/plans
10. Success message, dashboard displays
```

### Flow 2: New User with PayPal (Non-PK)

```
1. Same as Stripe steps 1-4
5. User selects PayPal, clicks Pay
6. PayPal order created
7. Redirect to PayPal approval
8. User approves on PayPal
9. Redirect back with order_id
10. Frontend calls capture-order
11. Backend processes:
    - Payment captured
    - Payment recorded
    - Invoice marked paid
    - Account activated
    - Credits added
12. Success displayed
```

### Flow 3: Pakistan User with Bank Transfer

```
1. Same as signup steps 1-4
5. User sees Stripe + Bank Transfer options
6. User selects Bank Transfer
7. BankTransferForm displays:
   - Bank details (SCB Pakistan)
   - Reference input
   - Proof upload option
8. User makes transfer, submits form
9. Backend creates:
   - Payment (status='pending_approval')
10. User sees "Awaiting Approval" status
11. Admin reviews in Django Admin
12. Admin approves:
    - Payment marked succeeded
    - Invoice marked paid
    - Account activated
    - Credits added
13. User receives email confirmation
```

### Flow 4: Existing User Buys Credits

```
1. User on /account/plans clicks "Buy Credits"
2. Credit package selection modal
3. User selects package and gateway
4. For Stripe/PayPal: redirect flow
5. For Bank Transfer: form submission
6. On success: credits added to account
```

---

## Country-Based Payment Rules

### Implementation in Frontend

```typescript
// billing.api.ts
export async function getAvailablePaymentGateways(userCountry?: string) {
  const [stripeAvailable, paypalAvailable] = await Promise.all([
    isStripeConfigured(),
    isPayPalConfigured(),
  ]);

  const isPakistan = userCountry?.toUpperCase() === 'PK';

  return {
    stripe: stripeAvailable,
    // PayPal: NOT available for Pakistan
    paypal: !isPakistan && paypalAvailable,
    // Bank Transfer: ONLY for Pakistan
    manual: isPakistan,
  };
}
```

### Usage in Components

```typescript
// PendingPaymentView.tsx
const isPakistan = userCountry === 'PK';

// Load gateways with country filter
const gateways = await getAvailablePaymentGateways(userCountry);

// Show appropriate options
const paymentOptions = [
  { type: 'stripe', ... }, // Always shown if configured
  isPakistan ? { type: 'manual', ... } : { type: 'paypal', ... },
];
```

---

## Webhook Processing

### Stripe Webhooks

**Endpoint:** `POST /v1/billing/webhooks/stripe/`

| Event | Handler Action |
|-------|----------------|
| `checkout.session.completed` | Activate subscription, add credits |
| `invoice.paid` | Add renewal credits |
| `invoice.payment_failed` | Send notification |
| `customer.subscription.updated` | Sync changes |
| `customer.subscription.deleted` | Cancel subscription |

**Idempotency:** Checks `WebhookEvent` model before processing:
```python
# Check if already processed
if WebhookEvent.objects.filter(
    provider='stripe',
    event_id=event_id,
    status='processed'
).exists():
    return  # Already handled
```

### PayPal Webhooks

**Endpoint:** `POST /v1/billing/webhooks/paypal/`

| Event | Handler Action |
|-------|----------------|
| `CHECKOUT.ORDER.APPROVED` | Auto-capture if configured |
| `PAYMENT.CAPTURE.COMPLETED` | Mark succeeded, add credits |
| `PAYMENT.CAPTURE.DENIED` | Mark failed |
| `BILLING.SUBSCRIPTION.ACTIVATED` | Activate subscription |

**Signature Verification:** Enabled and enforced:
```python
is_valid = service.verify_webhook_signature(...)
if not is_valid:
    return Response({'error': 'Invalid signature'}, status=400)
```

---

## Models Reference

### Invoice Model

```python
class Invoice(AccountBaseModel):
    STATUS_CHOICES = [
        ('draft', 'Draft'),
        ('pending', 'Pending'),
        ('paid', 'Paid'),
        ('void', 'Void'),
        ('uncollectible', 'Uncollectible'),
    ]
    
    invoice_number = models.CharField(max_length=50, unique=True)
    subscription = models.ForeignKey('auth.Subscription', ...)
    status = models.CharField(max_length=20, choices=STATUS_CHOICES)
    subtotal = models.DecimalField(...)
    tax = models.DecimalField(...)
    total = models.DecimalField(...)
    currency = models.CharField(max_length=3, default='USD')
    due_date = models.DateField()
    line_items = models.JSONField(default=list)
```

### Payment Model

```python
class Payment(AccountBaseModel):
    STATUS_CHOICES = [
        ('pending', 'Pending'),
        ('pending_approval', 'Pending Approval'),
        ('succeeded', 'Succeeded'),
        ('failed', 'Failed'),
        ('refunded', 'Refunded'),
    ]
    
    PAYMENT_METHOD_CHOICES = [
        ('stripe', 'Stripe'),
        ('paypal', 'PayPal'),
        ('bank_transfer', 'Bank Transfer'),
        ('manual', 'Manual'),
    ]
    
    invoice = models.ForeignKey('Invoice', ...)
    amount = models.DecimalField(...)
    currency = models.CharField(max_length=3)
    payment_method = models.CharField(choices=PAYMENT_METHOD_CHOICES)
    status = models.CharField(choices=STATUS_CHOICES)
    stripe_payment_intent_id = models.CharField(...)
    paypal_order_id = models.CharField(...)
    manual_reference = models.CharField(..., unique=True)
```

### WebhookEvent Model

```python
class WebhookEvent(models.Model):
    """Audit trail for webhook processing"""
    PROVIDER_CHOICES = [
        ('stripe', 'Stripe'),
        ('paypal', 'PayPal'),
    ]
    
    provider = models.CharField(choices=PROVIDER_CHOICES)
    event_id = models.CharField(max_length=255)
    event_type = models.CharField(max_length=100)
    payload = models.JSONField()
    status = models.CharField()  # 'pending', 'processed', 'failed'
    processed_at = models.DateTimeField(null=True)
    error_message = models.TextField(blank=True)
```

---

## Security Features

### Implemented Security Measures

1. **Webhook Signature Verification**
   - Stripe: `stripe.Webhook.construct_event()` with signing secret
   - PayPal: `verify_webhook_signature()` API call

2. **Idempotency**
   - `WebhookEvent` model tracks processed events
   - Duplicate detection before processing

3. **Amount Validation**
   - PayPal capture validates amount matches expected
   - Prevents manipulation attacks

4. **Manual Reference Uniqueness**
   - Database constraint prevents duplicate bank transfer references
   - Prevents double submission

5. **CSRF Protection**
   - Webhook endpoints exempt (external callers)
   - All other endpoints protected

6. **Authentication**
   - Payment endpoints require `IsAuthenticatedAndActive`
   - Config endpoints allow `AllowAny` (public keys only)

---

## Admin Operations

### Django Admin Features

**Location:** Django Admin > Billing

- **Invoices:** View, filter, download PDF
- **Payments:** View, approve/reject manual payments
- **Credit Transactions:** Audit trail
- **Credit Packages:** Manage packages

### Manual Payment Approval

```
1. Admin navigates to Payments in Django Admin
2. Filter by status='pending_approval'
3. Review payment details and proof
4. Click "Approve" or "Reject" action
5. System automatically:
   - Updates payment status
   - Marks invoice paid (if approved)
   - Activates account (if approved)
   - Adds credits (if approved)
   - Sends email notification
```

---

## Configuration

### Environment Variables

```bash
# Stripe
STRIPE_SECRET_KEY=sk_...
STRIPE_PUBLISHABLE_KEY=pk_...
STRIPE_WEBHOOK_SECRET=whsec_...

# PayPal
PAYPAL_CLIENT_ID=...
PAYPAL_CLIENT_SECRET=...
PAYPAL_WEBHOOK_ID=...
PAYPAL_MODE=sandbox|live

# General
DEFAULT_CURRENCY=USD
```

### IntegrationProvider Setup

Payment gateways configured via `IntegrationProvider` model in Django Admin:

1. **Stripe Provider:**
   - Name: "Stripe"
   - Provider Type: "stripe"
   - Credentials: `{"secret_key": "...", "publishable_key": "...", "webhook_secret": "..."}`

2. **PayPal Provider:**
   - Name: "PayPal"
   - Provider Type: "paypal"
   - Credentials: `{"client_id": "...", "client_secret": "...", "webhook_id": "..."}`

---

## Troubleshooting

### Common Issues

| Issue | Cause | Solution |
|-------|-------|----------|
| "Stripe not configured" | Missing IntegrationProvider | Add Stripe provider in admin |
| "PayPal not configured" | Missing IntegrationProvider | Add PayPal provider in admin |
| PayPal shown for PK users | Country not passed correctly | Ensure `billing_country` saved on account |
| Duplicate payments | Webhook retry without idempotency | Check `WebhookEvent` for duplicates |
| PDF download fails | Missing `reportlab` | Run `pip install reportlab` |

### Debug Logging

Enable billing debug logs:
```python
# settings.py
LOGGING = {
    'loggers': {
        'igny8_core.business.billing': {
            'level': 'DEBUG',
        },
    },
}
```

---

## File Reference

### Backend Files

| File | Description |
|------|-------------|
| `billing/views/stripe_views.py` | Stripe API endpoints |
| `billing/views/paypal_views.py` | PayPal API endpoints |
| `billing/views/refund_views.py` | Refund processing |
| `billing/services/stripe_service.py` | Stripe service layer |
| `billing/services/paypal_service.py` | PayPal service layer |
| `billing/services/invoice_service.py` | Invoice operations |
| `billing/services/payment_service.py` | Payment operations |
| `billing/services/pdf_service.py` | PDF generation |
| `billing/services/email_service.py` | Email notifications |
| `billing/models.py` | Billing models |
| `billing/urls.py` | URL routing |

### Frontend Files

| File | Description |
|------|-------------|
| `services/billing.api.ts` | API client functions |
| `pages/account/PlansAndBillingPage.tsx` | Main billing page |
| `components/billing/PendingPaymentView.tsx` | New user payment |
| `components/billing/BankTransferForm.tsx` | Bank transfer form |
| `components/billing/PayInvoiceModal.tsx` | Invoice payment modal |
| `components/billing/PaymentGatewaySelector.tsx` | Gateway selection |
| `components/billing/PendingPaymentBanner.tsx` | Payment alert banner |

---

*Document generated from production codebase - January 8, 2026*
