Update pricing UX, billing flows, and API rules

This commit is contained in:
Dwindi Ramadhana
2026-02-12 00:52:40 +07:00
parent cf065fab1e
commit a905256353
202 changed files with 22348 additions and 301 deletions

View File

@@ -0,0 +1,195 @@
# Dewemoji Billing Integration Plan (QRIS + PayPal)
This document outlines a proper, production-grade billing flow for Dewemoji using **QRIS (Pakasir)** and **PayPal Subscriptions**, including webhooks, retries, and license activation.
---
## 1) Goals
- Replace primitive payment links with real provider integrations.
- Support **subscription** billing (monthly/annual) and **one-time lifetime**.
- Activate or revoke licenses based on webhook-confirmed payments.
- Log all webhook events and payment activity for audit.
---
## 2) Data Model
### `orders` (new)
Acts as the primary record of what the user is buying. Payments link back to orders.
- `id`
- `user_id`
- `plan_code`
- `type` (`one_time`, `subscription`)
- `currency` (`IDR`, `USD`)
- `amount`
- `status` (`pending`, `paid`, `failed`, `expired`, `refunded`)
- `provider` (`qris`, `paypal`)
- `provider_ref`
- `created_at`, `updated_at`
### `payments` (new)
- `id`
- `user_id`
- `order_id`
- `provider` (`qris`, `paypal`)
- `type` (`one_time`, `subscription`)
- `plan_code` (`personal_monthly`, `personal_annual`, `personal_lifetime`)
- `currency` (`IDR`, `USD`)
- `amount`
- `status` (`pending`, `paid`, `failed`, `expired`, `refunded`)
- `provider_ref` (invoice_id / order_id / subscription_id)
- `raw_payload` (json)
- `created_at`, `updated_at`
### `subscriptions` (existing)
Extend with:
- `provider`
- `provider_ref`
- `status` (`active`, `pending`, `canceled`, `expired`)
- `started_at`, `expires_at`, `canceled_at`
- `next_renewal_at` (optional)
### `webhook_events` (existing)
Continue to log inbound payloads and processing status:
- `provider`, `event_type`, `status`, `payload`, `received_at`, `processed_at`, `error_message`
---
## 3) Payment Flow (User Journey)
### Pricing Page (Frontend)
Each plan shows:
- **Primary currency** (based on geo + user toggle)
- **Two payment buttons** (real provider flow):
- **QRIS (IDR)** → subscription or one-time
- **PayPal (USD)** → subscription or one-time
### Backend Endpoints
#### QRIS (Pakasir)
- `POST /billing/qris/create`
- Creates invoice via Pakasir API
- Stores `payments` with `pending`
- Returns QR payment URL or QR code data
- `GET /billing/qris/return` (optional)
- Shows “pending / processing” state
#### PayPal Subscriptions
- `POST /billing/paypal/create`
- Creates PayPal subscription
- Stores `payments` with `pending`
- Returns approval URL
- `GET /billing/paypal/return`
- Shows “pending / processing” state
---
## 4) Webhook Processing (Critical)
Webhook endpoint:
```
POST /webhooks/{provider}
```
Store inbound payloads in `webhook_events`, then process async (queue).
### PayPal Events
- `BILLING.SUBSCRIPTION.ACTIVATED` → mark subscription active, set `users.tier = personal`
- `BILLING.SUBSCRIPTION.CANCELLED` → mark subscription canceled
- `PAYMENT.SALE.COMPLETED` → mark payment paid
- `PAYMENT.SALE.DENIED` → mark payment failed
### Pakasir / QRIS Events
- `payment.paid` → mark payment paid, grant access
- `payment.expired` → mark payment failed/expired
---
## 5) License Activation Rules
When a payment or subscription is confirmed:
- Create or update a `subscriptions` record
- Set `users.tier = personal`
- Store provider refs (`provider_ref`)
- Log admin audit record
When revoked/expired:
- Update `subscriptions.status`
- Downgrade user if no active subscription remains
### Renewal Logic (QRIS manual renew)
- **If still active:** extend from current `expires_at`
- `expires_at = expires_at + duration`
- **If expired:** start from now
- `expires_at = now + duration`
---
## 6) Admin Dashboard Enhancements
Add or extend:
- **Payments list** (new screen)
- filter by provider/status/currency
- show raw provider ref
- **Subscriptions list** (already exists)
- show provider + status
- **Webhook events** (already exists)
- replay capability
---
## 7) Security & Reliability
- Validate webhook signatures (PayPal + Pakasir)
- Reject duplicate events (idempotency)
- Use queues for webhook processing
- Log all webhook failures
---
## 8) Required Inputs (From Owner)
Before implementation:
1. **Pakasir API docs** (create invoice, webhook payload format)
2. **PayPal API credentials** (client_id, secret, webhook signing key)
3. Confirm **plans & pricing**:
- Monthly
- Annual
- Lifetime
---
## 9) Implementation Phases
**Phase 1 — Schema + Core Models**
- Add `orders` table
- Add `payments` table (link to orders)
- Extend `subscriptions`
- Update webhook model if needed
**Phase 2 — Provider APIs**
- Pakasir invoice create
- PayPal subscription create
**Phase 3 — Webhooks**
- Save raw events
- Process via queue + idempotency
**Phase 4 — UI**
- Pricing page buttons → real flows
- Admin payment + subscription tools
---
## 10) Notes
- This plan assumes **proper subscription lifecycle** with webhooks.
- PayPal.me / static links are **not sufficient** for subscriptions.
- All access control must be tied to **confirmed payment status**.