# GoodGo Pricing → Checkout Audit Summary

## 🎯 Quick Overview

| Aspect | Status | Key Details |
|--------|--------|-------------|
| **Pricing Page** | ✅ Complete | `/pricing` displays 4 tiers, monthly/yearly toggle |
| **Plan API** | ✅ Complete | `GET /subscriptions/plans` with fallback data |
| **Subscription Backend** | ✅ Complete | CQRS pattern, domain entities, repositories |
| **Payment Gateway Integration** | ✅ Complete | VNPay, MoMo, ZaloPay ready to use |
| **Payment API** | ✅ Complete | Create payment, get status, handle callbacks |
| **Database Models** | ✅ Complete | Plan, Subscription, Payment, UsageRecord |
| **Frontend Checkout Flow** | ❌ MISSING | No modal/page to initiate payment |
| **Payment Return Handler** | ❌ MISSING | No page to handle gateway redirect |
| **Subscription Auto-Creation** | ❌ MISSING | Manual process after payment |

---

## 🏗️ Architecture Overview

### Frontend Stack
```
Pricing Page (/pricing)
    ↓ usePlans() hook
    ↓ React Query
API Client: subscriptionApi.getPlans()
    ↓ GET /subscriptions/plans
Backend (/subscriptions/plans endpoint)
```

### Payment Flow (Currently Broken)
```
Pricing Page (Select Plan)
    ✅ Displays plans, prices, features
    ❌ CTAs link to /register instead of checkout

[MISSING] Checkout Modal/Page
    ❌ Not implemented
    ❌ No plan confirmation
    ❌ No payment method selection

[MISSING] Payment Creation
    ❌ Should call POST /payments
    ❌ Should redirect to paymentUrl

Payment Gateway (VNPay/MoMo/ZaloPay)
    ✅ Backend has createPaymentUrl implementations
    ✅ Signature verification ready
    ❌ Frontend redirect not implemented

[MISSING] Return Handler
    ❌ No page for gateway callback
    ❌ No payment status polling
    ❌ No subscription creation

[MISSING] Subscription Creation
    ❌ Should call POST /subscriptions
    ❌ Should show success message

Dashboard/Home
    ✅ Has payments page to view history
    ❌ No subscription management UI
```

---

## 📁 Frontend File Structure

```
apps/web/
├── app/[locale]/(public)/pricing/
│   └── page.tsx                          ✅ Main pricing page
│
├── lib/
│   ├── subscription-api.ts               ✅ API client & types (PlanDto, CreateSubscriptionResult, etc.)
│   ├── payment-api.ts                    ✅ API client & types (CreatePaymentResult, PaymentStatusDto, etc.)
│   └── hooks/
│       ├── use-subscription.ts           ✅ usePlans(), useBillingHistory(), useQuota()
│       └── use-payments.ts               ✅ useTransactions(), usePaymentStatus()
│
├── app/[locale]/(dashboard)/dashboard/
│   └── payments/page.tsx                 ✅ Transaction history viewer
│
└── components/
    └── (needs new components for checkout)
        ├── checkout-modal/               ❌ Missing
        ├── payment-provider-select/      ❌ Missing
        └── subscription-status/          ❌ Missing
```

---

## 🔧 Backend File Structure

```
apps/api/src/modules/
│
├── subscriptions/
│   ├── presentation/
│   │   ├── controllers/subscriptions.controller.ts       ✅ 8 endpoints
│   │   └── dto/
│   │       ├── create-subscription.dto.ts                ✅ { planTier, billingCycle }
│   │       ├── upgrade-subscription.dto.ts               ✅
│   │       ├── cancel-subscription.dto.ts                ✅
│   │       └── meter-usage.dto.ts                        ✅
│   │
│   ├── application/
│   │   ├── commands/
│   │   │   ├── create-subscription/                      ✅ Creates subscription
│   │   │   ├── upgrade-subscription/                     ✅
│   │   │   ├── cancel-subscription/                      ✅
│   │   │   └── meter-usage/                              ✅
│   │   └── queries/
│   │       ├── get-plan/                                 ✅ Returns PlanDto[]
│   │       ├── check-quota/                              ✅
│   │       └── get-billing-history/                      ✅
│   │
│   ├── domain/
│   │   ├── entities/subscription.entity.ts               ✅ CQRS aggregate
│   │   ├── events/                                       ✅ 5 domain events
│   │   └── repositories/subscription.repository.ts       ✅ Interface
│   │
│   └── infrastructure/
│       ├── repositories/prisma-subscription.repository.ts ✅
│       └── event-handlers/listing-created-usage.handler.ts ✅
│
└── payments/
    ├── presentation/
    │   ├── controllers/payments.controller.ts            ✅ 5 endpoints
    │   └── dto/
    │       ├── create-payment.dto.ts                     ✅ { provider, type, amountVND, description, returnUrl }
    │       ├── refund-payment.dto.ts                     ✅
    │       └── list-transactions.dto.ts                  ✅
    │
    ├── application/
    │   ├── commands/
    │   │   ├── create-payment/                           ✅ Main payment creation logic
    │   │   ├── handle-callback/                          ✅ Webhook handler
    │   │   └── refund-payment/                           ✅
    │   └── queries/
    │       ├── get-payment-status/                       ✅ Poll status
    │       └── list-transactions/                        ✅
    │
    ├── domain/
    │   ├── entities/payment.entity.ts                    ✅ CQRS aggregate
    │   ├── events/                                       ✅ 4 domain events
    │   ├── value-objects/money.vo.ts                     ✅
    │   └── repositories/payment.repository.ts            ✅ Interface
    │
    └── infrastructure/
        ├── repositories/prisma-payment.repository.ts     ✅
        └── services/
            ├── payment-gateway.interface.ts              ✅ IPaymentGateway
            ├── payment-gateway.factory.ts                ✅ Gets correct gateway
            ├── vnpay.service.ts                          ✅ createPaymentUrl() + verifyCallback()
            ├── momo.service.ts                           ✅ createPaymentUrl() + verifyCallback()
            └── zalopay.service.ts                        ✅ createPaymentUrl() + verifyCallback()
```

---

## 🔌 API Endpoints Summary

### Subscription Endpoints
```
GET    /subscriptions/plans                    → PlanDto[]
GET    /subscriptions/plans/:tier              → PlanDto
POST   /subscriptions                          → CreateSubscriptionResult (requires auth)
PUT    /subscriptions/upgrade                  → UpgradeSubscriptionResult (requires auth)
DELETE /subscriptions                          → CancelSubscriptionResult (requires auth)
POST   /subscriptions/usage                    → MeterUsageResult (requires auth)
GET    /subscriptions/quota/:metric            → QuotaCheckResult (requires auth)
GET    /subscriptions/billing                  → BillingHistoryDto (requires auth)
```

### Payment Endpoints
```
POST   /payments                               → CreatePaymentResult (requires auth)
POST   /payments/callback/:provider            → HandleCallbackResult (webhook)
GET    /payments/:id                           → PaymentStatusDto (requires auth)
GET    /payments                               → TransactionListDto (requires auth)
POST   /payments/:id/refund                    → RefundPaymentResult (admin only)
```

---

## 💰 Pricing Tiers

```javascript
const TIERS = [
  {
    tier: 'FREE',
    monthlyVND: '0',
    yearlyVND: '0',
    maxListings: 3,
    maxSearches: 5,
  },
  {
    tier: 'AGENT_PRO',
    monthlyVND: '499,000',
    yearlyVND: '4,990,000',
    maxListings: 50,
    maxSearches: 30,
    popular: true,
  },
  {
    tier: 'INVESTOR',
    monthlyVND: '999,000',
    yearlyVND: '9,990,000',
    maxListings: 20,
    maxSearches: 100,
  },
  {
    tier: 'ENTERPRISE',
    monthlyVND: '4,990,000',
    yearlyVND: '49,900,000',
    maxListings: -1,      // Unlimited
    maxSearches: -1,      // Unlimited
  },
];
```

---

## 📊 Data Models (Prisma)

### Plan
```prisma
id: String @id
tier: PlanTier @unique (FREE, AGENT_PRO, INVESTOR, ENTERPRISE)
name: String
priceMonthlyVND: BigInt
priceYearlyVND: BigInt
maxListings: Int?
maxSavedSearches: Int?
maxAnalyticsQueries: Int?
maxMediaUploads: Int?
features: Json           // { analytics: true, aiValuation: false, ... }
isActive: Boolean
```

### Subscription
```prisma
id: String @id
userId: String @unique
user: User
planId: String
plan: Plan
status: SubscriptionStatus (ACTIVE, PAST_DUE, CANCELLED, EXPIRED)
currentPeriodStart: DateTime
currentPeriodEnd: DateTime
cancelledAt: DateTime?
createdAt: DateTime
updatedAt: DateTime
```

### Payment
```prisma
id: String @id
userId: String
provider: PaymentProvider (VNPAY, MOMO, ZALOPAY, BANK_TRANSFER)
type: PaymentType (SUBSCRIPTION, LISTING_FEE, DEPOSIT, FEATURED_LISTING)
amountVND: BigInt
status: PaymentStatus (PENDING, PROCESSING, COMPLETED, FAILED, REFUNDED)
providerTxId: String?
callbackData: Json?
idempotencyKey: String?  ← Prevents duplicate payments
createdAt: DateTime
updatedAt: DateTime
```

---

## 🔑 Key Implementation Details

### Payment Creation Flow (Backend)
```
User clicks "Pay Now"
  ↓
Frontend: POST /payments {
  provider: 'VNPAY',
  type: 'SUBSCRIPTION',
  amountVND: 499000,
  description: 'Agent Pro - Monthly',
  returnUrl: 'https://goodgo.vn/payment-return',
  idempotencyKey: UUID  ← Unique per payment attempt
}
  ↓
Backend CreatePaymentHandler:
  1. Check idempotencyKey (prevent duplicates)
  2. Validate amount (1 to 100 billion VND)
  3. Get payment gateway (VNPay/MoMo/ZaloPay)
  4. Call gateway.createPaymentUrl()
     - Returns paymentUrl: "https://gateway.com/pay?params..."
     - Returns providerTxId: "VNP-12345..."
  5. Mark payment as PROCESSING in DB
  6. Publish PaymentCreatedEvent
  7. Return to client: { paymentId, paymentUrl, providerTxId }
  ↓
Frontend:
  window.location = paymentUrl  ← Redirect to gateway
  ↓
User completes payment at gateway
  ↓
Gateway redirects to returnUrl with callback params
  ↓
Backend webhook: POST /payments/callback/vnpay?params...
  1. Verify callback signature
  2. Check payment status
  3. Update payment status in DB
  4. Publish PaymentCompletedEvent
  ↓
PaymentCompletedEvent triggers:
  - Send email notification
  - Update user's plan association (eventually)
  ↓
Frontend callback handler (if implemented):
  1. Get paymentId from URL
  2. Poll GET /payments/{paymentId}
  3. When status = COMPLETED:
     - POST /subscriptions { planTier, billingCycle }
     - Show success message
     - Redirect to dashboard
```

### Payment Gateway Implementations

#### VNPay
```typescript
// Signature: HMAC SHA-512
// Request via: URL parameters
// Response Code: vnp_ResponseCode = '00' means success
// Transaction ID: vnp_TransactionNo
```

#### MoMo
```typescript
// Signature: HMAC SHA-256
// Request via: JSON POST body
// Response Code: resultCode = 0 means success
// Transaction ID: transId
```

#### ZaloPay
```typescript
// Signature: HMAC SHA-256 (similar to MoMo)
// Request via: JSON POST body
// Response Code: return_code = 1 means success
// Transaction ID: zp_trans_id
```

---

## 🚨 Critical Gaps (What's Missing)

### 1. Checkout Modal/Page ❌
**What it should do:**
- Display selected plan details
- Show monthly vs yearly price
- Allow payment method selection (VNPay, MoMo, ZaloPay)
- Show terms & conditions
- Handle payment creation and redirect

**Current:** CTAs on pricing page link to `/register` instead of starting checkout

### 2. Payment Return Handler ❌
**What it should do:**
- Receive redirect from payment gateway
- Extract payment status from URL/callback
- Poll payment status via GET /payments/:id
- Create subscription when payment succeeds
- Show success/error UI

**Current:** No page exists for this flow

### 3. Subscription Auto-Creation ❌
**What it should do:**
- After successful payment, call POST /subscriptions
- Pass planTier and billingCycle
- Update user's subscription status
- Redirect to dashboard

**Current:** Manual process, no UI

### 4. Subscription Management UI ⚠️ Partial
**What exists:**
- Payments page shows transaction history

**What's missing:**
- Subscription status/details page
- Upgrade/downgrade plan UI
- Cancel subscription UI
- Usage/quota display

---

## 📋 Implementation Roadmap

### Phase 1: Basic Checkout (1-2 days)
```
✅ Pricing page exists
❌ Add CheckoutModal component
❌ Add payment provider selector
❌ Create /payment-return page
❌ Implement payment polling
❌ Wire subscription creation
```

### Phase 2: Full Integration (1-2 days)
```
✅ All backend endpoints ready
❌ Handle edge cases (timeout, user closes window, etc.)
❌ Add error recovery flows
❌ Add loading/success UI
❌ Test with all 3 payment providers
```

### Phase 3: Subscription Management (1-2 days)
```
✅ Upgrade/downgrade API endpoints exist
✅ Cancel subscription API exists
❌ Build subscription detail page
❌ Add upgrade/downgrade UI
❌ Add cancel UI with confirmation
❌ Add usage quota display
```

### Phase 4: Testing & Polish (1-2 days)
```
❌ E2E tests for all payment providers
❌ Error handling & edge cases
❌ Performance optimization
❌ Analytics/tracking integration
```

---

## 🎯 Next Steps

1. **Understand the desired checkout UX** - Where/how should checkout start?
   - Modal from pricing page?
   - Separate checkout page?
   - Inline on pricing page?

2. **Create CheckoutModal component** - Design it to match pricing page
   - Plan summary
   - Price breakdown
   - Payment provider selector
   - "Proceed to Payment" button

3. **Implement payment creation mutation** - Hook into React Query
   - `useCreatePayment()` hook
   - Handle loading/error states
   - Redirect to paymentUrl

4. **Build /payment-return page** - Handle gateway redirect
   - Parse URL params
   - Poll payment status
   - Create subscription on success

5. **Test with all 3 providers** - Ensure all integrations work
   - Use sandbox/test credentials
   - Verify callbacks

6. **Add subscription management UI** - Allow users to manage plans
   - View current subscription
   - Upgrade/downgrade
   - Cancel with confirmation

---

## 📚 Reference

Full audit document: `PRICING_CHECKOUT_AUDIT.md`

Key files to review:
- Frontend: `/apps/web/app/[locale]/(public)/pricing/page.tsx`
- Backend payments: `/apps/api/src/modules/payments/`
- Backend subscriptions: `/apps/api/src/modules/subscriptions/`
- Prisma schema: `/prisma/schema.prisma` (lines 451-514)

---

**Status:** Ready for checkout implementation  
**Estimated effort:** 4-6 days  
**Complexity:** Medium (all backend infrastructure is ready)
