Files
igny8/docs/90-REFERENCE/PAYMENT-SYSTEM.md
IGNY8 VPS (Salman) 3028db5197 Version 1.6.0
2026-01-08 00:36:32 +00:00

678 lines
21 KiB
Markdown

# 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*