196 lines
4.9 KiB
Markdown
196 lines
4.9 KiB
Markdown
# 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**.
|