21 KiB
Payment System Documentation
Version: 2.0.0
Last Updated: January 20, 2026
Status: Production Ready
Complete Billing Reference: For comprehensive billing documentation including the two-pool credit system and renewal workflows, see BILLING-PAYMENTS-COMPLETE.md
This document provides payment gateway implementation details for IGNY8.
Table of Contents
- System Overview
- Payment Entry Points
- Backend Architecture
- Frontend Architecture
- Payment Flows
- Country-Based Payment Rules
- Webhook Processing
- Models Reference
- 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/plansto complete payment - No payment gateway redirect from signup page
// 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
PendingPaymentViewcomponent - 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:
// 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
// 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
// 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:
# 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:
is_valid = service.verify_webhook_signature(...)
if not is_valid:
return Response({'error': 'Invalid signature'}, status=400)
Models Reference
Invoice Model
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
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
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
-
Webhook Signature Verification
- Stripe:
stripe.Webhook.construct_event()with signing secret - PayPal:
verify_webhook_signature()API call
- Stripe:
-
Idempotency
WebhookEventmodel tracks processed events- Duplicate detection before processing
-
Amount Validation
- PayPal capture validates amount matches expected
- Prevents manipulation attacks
-
Manual Reference Uniqueness
- Database constraint prevents duplicate bank transfer references
- Prevents double submission
-
CSRF Protection
- Webhook endpoints exempt (external callers)
- All other endpoints protected
-
Authentication
- Payment endpoints require
IsAuthenticatedAndActive - Config endpoints allow
AllowAny(public keys only)
- Payment endpoints require
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
# 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:
-
Stripe Provider:
- Name: "Stripe"
- Provider Type: "stripe"
- Credentials:
{"secret_key": "...", "publishable_key": "...", "webhook_secret": "..."}
-
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:
# 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