credis pacakges ifxes

This commit is contained in:
IGNY8 VPS (Salman)
2026-01-20 04:32:12 +00:00
parent 0957ece3a4
commit a97c72640a
6 changed files with 787 additions and 19 deletions

View File

@@ -200,7 +200,7 @@ class InvoiceService:
'billing_snapshot': {
'email': billing_email,
'name': account.name,
'billing_address': account.billing_address or '',
'billing_address': getattr(account, 'billing_address', '') or '',
},
'credit_package_id': credit_package.id,
'credit_package_name': credit_package.name,

View File

@@ -0,0 +1,484 @@
# IGNY8 Billing System Comprehensive Audit
**Date:** January 20, 2026
**Status:** CRITICAL - Multiple issues identified
**Reviewer:** System Audit
---
## Executive Summary
The billing system has **fundamental architectural flaws** that cause incorrect behavior across multiple payment methods, credit management, and account activation flows. This audit identifies all issues and provides a detailed analysis of the current implementation.
---
## Table of Contents
1. [Critical Issues](#1-critical-issues)
2. [Account Status & Activation Flow](#2-account-status--activation-flow)
3. [Credit System Analysis](#3-credit-system-analysis)
4. [Invoice Management](#4-invoice-management)
5. [Payment Method Flows](#5-payment-method-flows)
6. [Subscription Renewal Flow](#6-subscription-renewal-flow)
7. [Credit Package Purchases](#7-credit-package-purchases)
8. [Data Model Issues](#8-data-model-issues)
9. [Missing Features](#9-missing-features)
10. [Recommended Fixes](#10-recommended-fixes)
---
## 1. Critical Issues
### Issue #1: Credit Package Approval Adds WRONG Credits (CRITICAL)
**Location:** `AdminBillingViewSet.approve_payment()` and `BillingViewSet.approve_payment()`
**Current Behavior:**
- When admin approves a credit package payment, the code adds `subscription.plan.included_credits` (e.g., 200 credits from the plan)
- It should add the `credit_package.credits` from the purchased package (e.g., 500 credits)
**Evidence:**
```
User bought 500-credit package → Admin approved → System added 200 credits (plan credits)
```
**Root Cause:** Both approval methods look for subscription/plan credits instead of checking if the invoice is for a credit package purchase via `invoice.metadata.credit_package_id`.
---
### Issue #2: Invoice Types Not Differentiated (CRITICAL)
**Problem:** No clear distinction between:
- Subscription invoices (for plan activation/renewal)
- Credit package invoices (for additional credit purchases)
**Impact:**
- Frontend checks `hasPendingInvoice` which includes BOTH types
- Creating a credit package invoice should NOT affect account status
- Creating a credit package invoice should NOT block other purchases
---
### Issue #3: Credits Have No Validity/Expiration (CRITICAL)
**Current State:** Credits are a simple integer counter on Account model:
```python
credits = models.IntegerField(default=0)
```
**Missing:**
- No expiration date for credits
- No validity period tracking (1 month as business rule)
- No automatic reset mechanism
- Purchased credits vs subscription credits not tracked separately
**Impact:** Users can accumulate credits indefinitely, no monthly reset logic.
---
### Issue #4: Subscription Renewal Does NOT Reset Credits Correctly (CRITICAL)
**Current Behavior:**
- Stripe webhook `_handle_invoice_paid()` calls `CreditService.reset_credits_for_renewal()`
- Manual payment approval uses `CreditService.add_credits()` instead of reset ✗
**Problem with add_credits:**
- If user had 50 credits remaining
- Plan gives 200 credits monthly
- `add_credits(200)` → User now has 250 credits
- Should be: Reset to 200 credits
---
### Issue #5: No Invoice Expiration/Cancellation Mechanism
**Current State:**
- Pending invoices remain pending indefinitely
- No automatic void/cancellation after timeout (e.g., 24-48 hours)
- User cannot cancel/void their own pending credit package invoices
- No cleanup task for stale invoices
---
## 2. Account Status & Activation Flow
### Status Values
| Status | Description | When Set |
|--------|-------------|----------|
| `active` | Account fully operational | After successful payment or trial activation |
| `trial` | Free trial period | On signup with free plan |
| `pending_payment` | Awaiting first payment | After signup with paid plan, before payment |
| `suspended` | Manually suspended | Admin action |
| `cancelled` | Subscription cancelled | After cancellation |
### Current Flow Problems
#### New User with Paid Plan (Bank Transfer):
1. User signs up selecting paid plan with bank transfer → `status = pending_payment`
2. Invoice created → Invoice `status = pending`
3. User submits bank transfer confirmation → Payment `status = pending_approval`
4. Admin approves payment → Account `status = active`
5. **BUG:** Credits added = plan credits (not credit package credits if that's what was purchased)
#### Existing Active User Buying Credits (Bank Transfer):
1. User has `status = active`
2. User creates credit package invoice → Invoice created with `status = pending`
3. **BUG:** Frontend incorrectly interprets this as "account has pending invoice"
4. **BUG:** Some UI elements get disabled unnecessarily
5. Admin approves → **BUG:** Wrong credits added (plan credits instead of package credits)
---
## 3. Credit System Analysis
### Current Data Model
**Account.credits** (integer)
- Single counter for all credits
- No distinction between:
- Plan/subscription credits
- Purchased credit packages
- Bonus/promotional credits
- No expiration tracking
**CreditTransaction** (history log)
- Records additions and deductions
- Transaction types: `purchase`, `subscription`, `refund`, `deduction`, `adjustment`
- Has `balance_after` field for audit trail
**CreditUsageLog** (detailed usage)
- Per-operation usage tracking
- Links to site, operation type, model used
- Used for analytics and billing reports
### Credit Flow Issues
| Scenario | Expected Behavior | Current Behavior | Status |
|----------|-------------------|------------------|--------|
| New subscription | Reset credits to plan amount | Add credits to existing | ❌ BUG |
| Subscription renewal (Stripe) | Reset credits to plan amount | Reset credits | ✓ OK |
| Subscription renewal (Manual) | Reset credits to plan amount | Add credits | ❌ BUG |
| Credit package purchase | Add package credits | Add plan credits | ❌ BUG |
| Monthly reset | Reset subscription credits, keep purchased | No reset logic | ❌ MISSING |
### Credit Validity Rules (Business Requirement - NOT IMPLEMENTED)
Per business rules, credits should have a **1-month validity period**:
- Subscription credits reset monthly on billing cycle
- Purchased credits have 1-month validity from purchase date
- Expired credits should be voided
**Current Implementation:** None - credits never expire or reset.
---
## 4. Invoice Management
### Invoice Types (Implicit, Not Enforced)
| Type | Identifier | Purpose |
|------|-----------|---------|
| Subscription | `invoice.subscription IS NOT NULL` | Plan activation/renewal |
| Credit Package | `invoice.metadata.credit_package_id EXISTS` | Additional credit purchase |
| Custom | Neither | Admin-created invoices |
### Invoice Status Flow
```
draft → pending → paid/void/uncollectible
```
### Issues
1. **No Expiration:** Invoices stay pending forever
2. **No User Cancellation:** Users cannot cancel their own credit package invoices
3. **No Automatic Cleanup:** No task to void stale invoices
4. **No Type Enforcement:** Invoice type determined by checking multiple fields
### Recommended Invoice Statuses
| Status | Description | Auto-Transition |
|--------|-------------|-----------------|
| `draft` | Created, not sent | → pending when finalized |
| `pending` | Awaiting payment | → void after 48h (credit packages) |
| `paid` | Payment received | Terminal |
| `void` | Cancelled/expired | Terminal |
| `uncollectible` | Cannot be collected | Terminal |
---
## 5. Payment Method Flows
### Supported Payment Methods
| Method | Countries | Auto-Approval | Account Activation |
|--------|-----------|---------------|-------------------|
| Stripe (Card) | All except PK | Yes | Immediate |
| PayPal | All except PK | Yes | Immediate |
| Bank Transfer | PK only | No (Manual) | After admin approval |
### Stripe Flow (Working ✓)
1. User initiates checkout → Stripe session created
2. User completes payment on Stripe
3. Webhook `checkout.session.completed` → Account activated, credits added
4. Webhook `invoice.paid` (renewal) → Credits reset
### PayPal Flow (Working ✓)
1. User initiates checkout → PayPal order created
2. User approves on PayPal
3. Payment captured → Account activated, credits added
### Bank Transfer Flow (BUGGY ❌)
1. User initiates → Invoice created, account status may change incorrectly
2. User submits confirmation → Payment created with `pending_approval`
3. Admin approves → **WRONG CREDITS ADDED**
4. User rejection flow exists but no automatic expiration
---
## 6. Subscription Renewal Flow
### Automatic (Stripe/PayPal)
- Stripe handles automatic billing
- Webhook `invoice.paid` triggers renewal
- `reset_credits_for_renewal()` correctly resets credits ✓
### Manual (Bank Transfer)
1. Task `process_subscription_renewals` creates renewal invoice
2. Subscription status → `pending_renewal`
3. Grace period (7 days) starts
4. User pays manually
5. **BUG:** Admin approval uses `add_credits()` instead of `reset_credits_for_renewal()`
6. Task `check_expired_renewals` expires after grace period ✓
---
## 7. Credit Package Purchases
### Current Flow
1. User selects package
2. For Stripe/PayPal: Redirect to checkout
3. For Bank Transfer: Create invoice → Show payment form
### Issues
| Step | Issue | Severity |
|------|-------|----------|
| Invoice Creation | Uses `InvoiceService.create_credit_package_invoice()` | OK |
| Invoice stores `credit_package_id` in metadata | OK | OK |
| Approval reads `subscription.plan.included_credits` | **WRONG** | CRITICAL |
| Should read `invoice.metadata.credit_package_id` → get package → add `package.credits` | Missing | CRITICAL |
### Stripe Credit Purchase (Partially Working)
- Webhook handler `_handle_credit_purchase()` correctly reads metadata
- Uses `CreditService.add_credits()` with correct amount ✓
### Manual Credit Purchase (BROKEN)
- `approve_payment()` does NOT check if invoice is credit package
- Adds plan credits instead of package credits
- Completely wrong behavior
---
## 8. Data Model Issues
### Missing Fields
**Invoice Model:**
- `invoice_type` enum: `subscription`, `credit_package`, `custom`
- `expires_at` datetime for auto-void
- `credit_package_id` FK (instead of in metadata)
**Account Model:**
- `subscription_credits` (monthly reset)
- `purchased_credits` (expire after 1 month)
- `credits_valid_until` datetime
**CreditTransaction:**
- `expires_at` datetime
- `source_type`: `subscription`, `purchase`, `bonus`
### Proposed Schema Changes
```
Account:
+ subscription_credits: int (resets monthly)
+ purchased_credits: int (separate pool)
+ credits_last_reset: datetime
Invoice:
+ invoice_type: enum('subscription', 'credit_package', 'custom')
+ expires_at: datetime (nullable)
CreditPurchase: (NEW MODEL)
- account: FK
- credit_package: FK
- credits: int
- purchase_date: datetime
- expires_at: datetime
- credits_remaining: int
- status: enum('active', 'expired', 'used')
```
---
## 9. Missing Features
### Invoice Management
- [ ] User can cancel own pending credit package invoices
- [ ] Auto-void invoices after 24-48 hours
- [ ] Invoice expiration emails
- [ ] Invoice reminder emails (before expiration)
### Credit Management
- [ ] Credit expiration tracking
- [ ] Monthly credit reset task
- [ ] Separate subscription vs purchased credits
- [ ] Credit validity period configuration
- [ ] Low credit warnings before operations
### Payment Management
- [ ] Payment retry for failed payments
- [ ] Automatic payment reminder emails
- [ ] Payment receipt PDFs
- [ ] Refund processing
### Admin Features
- [ ] Bulk invoice void
- [ ] Credit adjustment with reason
- [ ] Payment history export
- [ ] Revenue reports
---
## 10. Recommended Fixes
### Priority 1: Critical (Immediate Fix Required)
#### Fix 1.1: Credit Package Approval Logic
**File:** `billing_views.py` - `approve_payment()` method
**Change:** Before adding credits, check if invoice has `credit_package_id` in metadata:
```
IF invoice.metadata.credit_package_id EXISTS:
package = CreditPackage.get(invoice.metadata.credit_package_id)
credits_to_add = package.credits
ELSE:
credits_to_add = subscription.plan.included_credits
```
#### Fix 1.2: Manual Renewal Should Reset Credits
**File:** `billing_views.py` - `approve_payment()` method
**Change:** For subscription renewals, use `reset_credits_for_renewal()` instead of `add_credits()`.
#### Fix 1.3: Separate Invoice Type Checking in Frontend
**File:** `PlansAndBillingPage.tsx`
**Already Fixed:** `hasPendingSubscriptionInvoice` and `hasPendingCreditInvoice` now separate.
### Priority 2: High (Fix Within 1 Week)
#### Fix 2.1: Add Invoice Expiration
- Add `expires_at` field to Invoice model
- Set expiration on creation (48 hours for credit packages)
- Create Celery task to void expired invoices
- Send reminder email 24 hours before expiration
#### Fix 2.2: User Invoice Cancellation
- Add API endpoint for user to cancel own pending credit package invoices
- Add "Cancel" button in frontend invoice list
- Set invoice status to `void` on cancellation
### Priority 3: Medium (Fix Within 1 Month)
#### Fix 3.1: Credit Expiration System
- Add credit validity tracking
- Create monthly reset task for subscription credits
- Track purchased credits separately with expiration
#### Fix 3.2: Invoice Type Field
- Add `invoice_type` enum field
- Migrate existing invoices based on metadata
- Update all queries to use type field
### Priority 4: Low (Future Enhancement)
- Payment retry automation
- Revenue reporting
- Credit pool separation
- Advanced analytics
---
## Appendix A: Current Code Locations
| Component | File Path |
|-----------|-----------|
| Account Model | `auth/models.py` |
| Plan Model | `auth/models.py` |
| Subscription Model | `auth/models.py` |
| Invoice Model | `business/billing/models.py` |
| Payment Model | `business/billing/models.py` |
| CreditPackage Model | `business/billing/models.py` |
| CreditTransaction Model | `business/billing/models.py` |
| CreditService | `business/billing/services/credit_service.py` |
| InvoiceService | `business/billing/services/invoice_service.py` |
| PaymentService | `business/billing/services/payment_service.py` |
| Admin Approval (v1) | `business/billing/billing_views.py` |
| Admin Approval (v2) | `modules/billing/views.py` |
| Stripe Webhooks | `business/billing/views/stripe_views.py` |
| PayPal Webhooks | `business/billing/views/paypal_views.py` |
| Renewal Tasks | `business/billing/tasks/subscription_renewal.py` |
---
## Appendix B: Test Scenarios Required
| Scenario | Current Result | Expected Result |
|----------|---------------|-----------------|
| PK user buys 500 credit package via bank transfer | Gets 200 credits (plan) | Gets 500 credits |
| Active user creates credit invoice | Account blocked | Account stays active |
| Subscription renewal (manual) | Credits added | Credits reset |
| Invoice unpaid after 48 hours | Stays pending | Auto-void |
| User wants to cancel own invoice | No option | Can cancel |
| Credits unused for 1 month | Stay forever | Should expire |
---
## Appendix C: Affected User Flows
### Flow 1: New PK User Subscription
```
Signup → Select Plan → Bank Transfer → Submit Confirmation → Wait for Approval → Account Active
```
**Issues:** None if subscription only
### Flow 2: Active PK User Buys Credits
```
Go to Credits → Select Package → Bank Transfer → Invoice Created → Submit Confirmation → Wait → WRONG CREDITS ADDED
```
**Issues:**
- Invoice creation may show incorrect UI states
- Wrong credits added on approval
### Flow 3: Active Non-PK User Buys Credits
```
Go to Credits → Select Package → Stripe Checkout → Payment → Credits Added
```
**Issues:** None - Stripe handler works correctly
### Flow 4: Subscription Renewal (Manual)
```
Renewal Date → Invoice Created → User Pays → Admin Approves → CREDITS ADDED (not reset)
```
**Issues:** Should reset credits, not add
---
**End of Audit Report**
**Next Steps:**
1. Review this audit with team
2. Prioritize fixes based on business impact
3. Implement Priority 1 fixes immediately
4. Create automated tests for all scenarios
5. Schedule remaining fixes

View File

@@ -0,0 +1,267 @@
# IGNY8 Billing System Audit + Refactor Plan
**Date:** January 20, 2026
**Status:** Critical
**Scope:** Billing system, payment methods, subscriptions, credits, invoices, PK (Pakistan) bank transfer flow
---
## 1) Executive Summary
The billing system has **critical functional and architectural issues** affecting credit accuracy, manual payment approvals, invoice lifecycles, and monthly credit resets. Stripe and PayPal flows largely work, but manual approvals (especially PK bank transfer) and credit lifecycle rules are broken. Credits do not expire, and subscription credits do not reset correctly for manual renewals.
The plan below preserves all current payment methods while fixing the broken flows and making all states visible and auditable.
---
## 2) Current System Coverage (What Exists Today)
### Payment Methods (Country Rules)
| Region | Available Methods | Notes |
|---|---|---|
| NonPK | Stripe, PayPal | Bank transfer disabled |
| PK | Stripe, Bank Transfer | PayPal disabled |
### Core Billing Components
| Component | Purpose | Current State |
|---|---|---|
| Account status | Controls access | Active, trial, pending_payment used |
| Subscription | Plan + billing cycle | Present, but manual renewals mishandle credits |
| Invoices | Payment tracking | Present, but no type/expiry enforcement |
| Payments | Gateway/Manual transactions | Present, but approval logic incomplete |
| Credits | Single counter | No expiry, no separation |
---
## 3) Critical Issues (Audit Findings)
### Issue A — Wrong Credits Added for Manual Credit Purchases
**Impact:** Users buy a credit package (e.g., 500) but receive subscription credits (e.g., 200).
**Cause:** Approval logic uses plan credits instead of credit package credits.
### Issue B — Manual Renewal Adds Instead of Resets
**Impact:** Monthly renewal should reset credits but instead adds to leftover balance.
**Cause:** Manual approval uses add instead of reset.
### Issue C — No Credit Expiry / Validity Rules
**Impact:** Credits never expire. Business rule requires 1month validity.
**Cause:** No expiry metadata or tasks.
### Issue D — Invoice Types Not Enforced
**Impact:** Subscription invoices and credit invoices are mixed, causing UI and logic mistakes.
**Cause:** No explicit invoice type field and insufficient filtering.
### Issue E — No AutoExpiry of Pending Invoices
**Impact:** Pending invoices remain forever, creating clutter and wrong user states.
**Cause:** No expiration field and no scheduled task to void.
---
## 4) Payment Flows (Current Behavior vs Expected)
### 4.1 Subscription Purchase (New User)
| Step | Current Behavior | Expected |
|---|---|---|
| Signup with paid plan | Account pending_payment | Correct |
| Manual payment submission | Payment pending_approval | Correct |
| Admin approval | Account active + credits added | **Should reset to plan credits** |
### 4.2 Subscription Renewal (Manual)
| Step | Current Behavior | Expected |
|---|---|---|
| Renewal invoice created | Subscription pending_renewal | Correct |
| Admin approval | Credits added | **Should reset** |
### 4.3 Credit Package Purchase (PK Manual)
| Step | Current Behavior | Expected |
|---|---|---|
| Invoice created | Pending invoice | Correct |
| Admin approval | Adds plan credits | **Should add package credits** |
### 4.4 Credit Package Purchase (Stripe/PayPal)
| Step | Current Behavior | Expected |
|---|---|---|
| Checkout | Works | Correct |
| Payment | Credits added correctly | Correct |
---
## 5) Credit System Audit
### Current Structure
- Single integer balance on Account
- No separation between subscription credits and purchased credits
- No validity period or expiry
### Required Behaviors
- Subscription credits reset monthly
- Purchased credits expire after 1 month
- Ledger always traceable for audit and reconciliation
---
## 6) Invoice System Audit
### Current Structure
- No invoice type field (implicit only)
- Pending invoices never expire
- No user cancellation for credit invoices
### Required Behaviors
- Explicit invoice_type = subscription / credit_package / custom
- Credit invoices expire after a set time (2448 hours)
- Users can cancel pending credit invoices
- Clear separation of subscription vs credit invoices for UI and status
---
## 7) PK (Pakistan) Bank Transfer Special Case
### Must Remain Working
- Stripe still available
- PayPal hidden
- Bank details displayed in UI
- Manual payment submission works
### Required Fixes
- Manual approvals must respect invoice type
- Credit package approvals must add package credits
- Subscription approvals must reset credits
- Pending credit invoices must expire after 24 hours
---
# Part II — Refactor Plan (No Code)
## Phase 1 — Data Model Stabilization
### 1.1 Invoice Type & Expiry
Add:
- invoice_type: subscription / credit_package / custom
- expires_at: datetime
- void_reason: text
Rules:
- credit_package invoices expire after 24 hours
- subscription invoices follow grace period rules
### 1.2 Credit Buckets
Add:
- subscription_credits (monthly reset)
- purchased_credits (expires rolling 30 days)
- credits_total (computed)
---
## Phase 2 — Payment Approval Logic
### 2.1 Unified Approval Logic (Single Source)
Rules:
- If invoice_type == credit_package → add package credits
- If invoice_type == subscription → reset plan credits
- Account status updated only for subscription invoices
### 2.2 Manual Renewals
- Replace addcredits with resetcredits
---
## Phase 3 — Invoice Lifecycle Governance
### 3.1 AutoExpire Credit Invoices
- Scheduled task: void invoices past expires_at
### 3.2 User Cancellation
- Allow user to void their own pending credit invoices
---
## Phase 4 — Monthly Credit Reset + Expiry
### 4.1 Subscription Reset
- On every billing cycle, subscription credits set to plan amount
### 4.2 Purchased Credit Expiry
- Credits expire 30 days after purchase
- Expired credits removed automatically
---
## Phase 5 — Observability & Health
### Metrics to Track
| Metric | Purpose |
|---|---|
| Pending credit invoices > 24h | Detect stuck manual payments |
| Subscription renewals not resetting | Detect ledger issues |
| Credits mismatch vs ledger | Detect consistency bugs |
### Admin Visibility
- Active subscription health panel
- Manual approvals queue
- Credit expiry report
---
# Part III — Target Healthy Flow (All Methods Preserved)
## Subscription Purchase (Any Method)
1. Invoice created (type=subscription)
2. Payment processed
3. Credits reset to plan amount
4. Account active
## Subscription Renewal (Any Method)
1. Invoice created
2. Payment processed
3. Credits reset (not added)
## Credit Package Purchase
1. Invoice created (type=credit_package, expires in 24h)
2. Payment processed
3. Package credits added to purchased pool
---
# Part IV — User Visibility (Frontend)
| User State | What User Sees |
|---|---|
| Pending subscription invoice | “Pay to activate plan” view |
| Pending credit invoice | “Complete payment for credits” view |
| Credit expiry | “Expires in X days” message |
| Renewal date | “Credits reset on” message |
---
# Part V — Rollout Plan
### Step 1: Fix Approval Logic (Critical)
- Ensure credit package approvals add correct credits
- Ensure manual renewals reset credits
### Step 2: Enforce Invoice Type + Expiry
- Add explicit invoice_type and expires_at
- Run migration and update existing invoices
### Step 3: Implement Credit Validity
- Add purchased credit tracking
- Expire credits monthly
### Step 4: UI Cleanup
- Show invoicetype specific status
- Avoid blocking account on credit invoices
---
# Final Note
This plan keeps **all payment methods working** while fixing broken credit logic, invoice lifecycle, and monthly reset rules. It also introduces explicit accounting for credit sources, enabling correct expiry and accurate audits across all payment methods and regions.

View File

@@ -339,7 +339,7 @@ export default function PayInvoiceModal({
) : (
<>
{/* Header */}
<div className="flex items-center justify-between mb-5">
<div className="flex items-center justify-between mb-5 pr-8">
<div>
<h2 className="text-xl font-semibold text-gray-900 dark:text-white">Pay Invoice</h2>
<p className="text-sm text-gray-500 dark:text-gray-400">#{invoice.invoice_number}</p>
@@ -348,6 +348,11 @@ export default function PayInvoiceModal({
<div className="text-2xl font-bold text-gray-900 dark:text-white">
{currency} {amount.toFixed(2)}
</div>
{isPakistan && selectedOption === 'bank_transfer' && (
<div className="text-sm text-gray-500 dark:text-gray-400">
PKR {Math.round(amount * 278).toLocaleString()}
</div>
)}
</div>
</div>

View File

@@ -71,11 +71,11 @@ export const Modal: React.FC<ModalProps> = ({
{showCloseButton && (
<button
onClick={onClose}
className="absolute right-3 top-3 z-999 flex h-9.5 w-9.5 items-center justify-center rounded-full bg-gray-100 text-gray-400 transition-colors hover:bg-gray-200 hover:text-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white sm:right-6 sm:top-6 sm:h-11 sm:w-11"
className="absolute right-2 top-2 z-999 flex h-7 w-7 items-center justify-center rounded-full bg-gray-100 text-gray-400 transition-colors hover:bg-gray-200 hover:text-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white sm:right-3 sm:top-3 sm:h-8 sm:w-8"
>
<svg
width="24"
height="24"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"

View File

@@ -667,11 +667,19 @@ export default function PlansAndBillingPage() {
// FIX: hasActivePlan should check account status, not just plan existence
const accountStatus = user?.account?.status || '';
const hasPendingInvoice = invoices.some((inv) => inv.status === 'pending');
// FIX: Only consider SUBSCRIPTION invoices as pending for activation status
// Credit package invoices should NOT affect account active status
const hasPendingSubscriptionInvoice = invoices.some((inv) =>
inv.status === 'pending' && inv.subscription !== null && inv.subscription !== undefined
);
// Credit package invoices - pending but NOT subscription related
const hasPendingCreditInvoice = invoices.some((inv) =>
inv.status === 'pending' && (inv.subscription === null || inv.subscription === undefined)
);
// Accept both 'active' and 'trial' status (free plans get 'trial' status)
const hasActivePlan = (accountStatus === 'active' || accountStatus === 'trial')
&& effectivePlanId
&& !hasPendingInvoice;
&& !hasPendingSubscriptionInvoice;
const hasPendingPayment = payments.some((p) => p.status === 'pending_approval');
@@ -680,7 +688,10 @@ export default function PlansAndBillingPage() {
// - user has never made a successful payment
const hasEverPaid = payments.some((p) => p.status === 'succeeded' || p.status === 'completed');
const isNewUserPendingPayment = accountStatus === 'pending_payment' && !hasEverPaid;
const pendingInvoice = invoices.find((inv) => inv.status === 'pending');
// For new user flow, find specifically a SUBSCRIPTION pending invoice
const pendingSubscriptionInvoice = invoices.find((inv) =>
inv.status === 'pending' && inv.subscription !== null && inv.subscription !== undefined
);
const billingCountry = (user?.account as any)?.billing_country || 'US';
// FIX: canManageBilling should check if user actually paid via Stripe
@@ -691,8 +702,9 @@ export default function PlansAndBillingPage() {
// Determine effective currency for display based on country and payment method
const effectiveCurrency = getCurrencyForDisplay(billingCountry, userPaymentMethod);
// Combined check: disable Buy Credits if no active plan OR has pending invoice
const canBuyCredits = hasActivePlan && !hasPendingInvoice;
// Combined check: disable Buy Credits if no active plan OR has pending credit invoice
// Subscription invoices don't block credit purchases, only existing credit package invoices do
const canBuyCredits = hasActivePlan && !hasPendingCreditInvoice;
// Credit usage percentage
const creditUsage = creditBalance && creditBalance.plan_credits_per_month > 0
@@ -776,13 +788,13 @@ export default function PlansAndBillingPage() {
// NEW USER PENDING PAYMENT - Show full-page payment view
// This is the simplified flow for users who just signed up with a paid plan
if (isNewUserPendingPayment && pendingInvoice) {
const planName = currentPlan?.name || pendingInvoice.subscription?.plan?.name || 'Selected Plan';
const invoiceCurrency = pendingInvoice.currency || 'USD';
if (isNewUserPendingPayment && pendingSubscriptionInvoice) {
const planName = currentPlan?.name || pendingSubscriptionInvoice.subscription?.plan?.name || 'Selected Plan';
const invoiceCurrency = pendingSubscriptionInvoice.currency || 'USD';
// Get USD price from plan, PKR price from invoice
const planUSDPrice = currentPlan?.price || pendingInvoice.subscription?.plan?.price || '0';
const invoicePKRPrice = invoiceCurrency === 'PKR' ? (pendingInvoice.total_amount || pendingInvoice.total || '0') : undefined;
const planUSDPrice = currentPlan?.price || pendingSubscriptionInvoice.subscription?.plan?.price || '0';
const invoicePKRPrice = invoiceCurrency === 'PKR' ? (pendingSubscriptionInvoice.total_amount || pendingSubscriptionInvoice.total || '0') : undefined;
// Check if user has a pending bank transfer (status = pending_approval with payment_method = bank_transfer)
const hasPendingBankTransfer = payments.some(
@@ -801,7 +813,7 @@ export default function PlansAndBillingPage() {
return (
<PendingPaymentView
invoice={pendingInvoice}
invoice={pendingSubscriptionInvoice}
userCountry={billingCountry}
planName={planName}
planPrice={planUSDPrice}
@@ -1067,14 +1079,14 @@ export default function PlansAndBillingPage() {
Subscribe to a plan first to purchase additional credits
</p>
</div>
) : hasPendingInvoice ? (
) : hasPendingCreditInvoice ? (
<div className="text-center py-8 border-2 border-dashed border-warning-200 dark:border-warning-700 bg-warning-50 dark:bg-warning-900/20 rounded-xl">
<AlertCircleIcon className="w-8 h-8 mx-auto text-warning-500 mb-2" />
<p className="text-warning-700 dark:text-warning-300 text-sm font-medium">
Please pay your pending invoice first
You have a pending credit invoice
</p>
<p className="text-warning-600 dark:text-warning-400 text-xs mt-1">
Credit purchases are disabled until your outstanding balance is settled
Complete or cancel your pending payment before purchasing more credits
</p>
</div>
) : (