docs: clarify payment finality semantics
This commit is contained in:
@@ -10,7 +10,7 @@ The current payment system covers:
|
|||||||
- default subscription plan bootstrap
|
- default subscription plan bootstrap
|
||||||
- user subscription creation at registration time
|
- user subscription creation at registration time
|
||||||
- invoice creation through a provider adapter
|
- invoice creation through a provider adapter
|
||||||
- manual admin activation when an invoice is confirmed as paid
|
- manual admin activation after an operator verifies that the provider reported a final successful payment status
|
||||||
- quota-cycle reset on successful activation
|
- quota-cycle reset on successful activation
|
||||||
|
|
||||||
The current payment system does not yet cover:
|
The current payment system does not yet cover:
|
||||||
@@ -42,6 +42,8 @@ The current payment system does not yet cover:
|
|||||||
|
|
||||||
### `PaymentInvoice`
|
### `PaymentInvoice`
|
||||||
- stores one provider-facing payment attempt for a subscription
|
- stores one provider-facing payment attempt for a subscription
|
||||||
|
- `pending` means the invoice exists, but the payment is not yet considered final or accepted
|
||||||
|
- `paid` means the payment is considered final and safe to activate against
|
||||||
- important fields:
|
- important fields:
|
||||||
- local `id`
|
- local `id`
|
||||||
- `subscriptionId`
|
- `subscriptionId`
|
||||||
@@ -91,6 +93,13 @@ Current runtime note:
|
|||||||
5. The returned provider invoice data is persisted as a new local `PaymentInvoice` in `pending`.
|
5. The returned provider invoice data is persisted as a new local `PaymentInvoice` in `pending`.
|
||||||
6. The API returns invoice details, including provider invoice id, amount, address, and expiry time.
|
6. The API returns invoice details, including provider invoice id, amount, address, and expiry time.
|
||||||
|
|
||||||
|
## Payment status semantics
|
||||||
|
- `pending` does not count as paid.
|
||||||
|
- `pending` does not activate the subscription.
|
||||||
|
- `pending` does not reset quota.
|
||||||
|
- The system must treat an invoice as `paid` only after the payment provider reports a final successful status, meaning the funds are accepted strongly enough for access activation.
|
||||||
|
- The current implementation does not fetch or verify that provider-final status automatically yet.
|
||||||
|
|
||||||
## Invoice listing flow
|
## Invoice listing flow
|
||||||
- `GET /api/billing/invoices` returns the user's invoices ordered by newest first.
|
- `GET /api/billing/invoices` returns the user's invoices ordered by newest first.
|
||||||
- This is a read-only view over persisted `PaymentInvoice` rows.
|
- This is a read-only view over persisted `PaymentInvoice` rows.
|
||||||
@@ -100,8 +109,9 @@ The implemented activation path is manual and admin-driven.
|
|||||||
|
|
||||||
1. An authenticated admin calls `POST /api/admin/invoices/:id/mark-paid`.
|
1. An authenticated admin calls `POST /api/admin/invoices/:id/mark-paid`.
|
||||||
2. The web app resolves the admin session and passes actor metadata into the billing store.
|
2. The web app resolves the admin session and passes actor metadata into the billing store.
|
||||||
3. `markInvoicePaid` runs inside one database transaction.
|
3. This endpoint is intended to be used only after the operator has already verified that the provider reached a final successful payment state.
|
||||||
4. If the invoice is `pending`, the store:
|
4. `markInvoicePaid` runs inside one database transaction.
|
||||||
|
5. If the invoice is `pending`, the store:
|
||||||
- updates the invoice to `paid`
|
- updates the invoice to `paid`
|
||||||
- sets `paidAt`
|
- sets `paidAt`
|
||||||
- updates the related subscription to `active`
|
- updates the related subscription to `active`
|
||||||
@@ -111,7 +121,11 @@ The implemented activation path is manual and admin-driven.
|
|||||||
- clears `canceledAt`
|
- clears `canceledAt`
|
||||||
- writes a `UsageLedgerEntry` with `entryType = cycle_reset`
|
- writes a `UsageLedgerEntry` with `entryType = cycle_reset`
|
||||||
- writes an `AdminAuditLog` entry `invoice_mark_paid`
|
- writes an `AdminAuditLog` entry `invoice_mark_paid`
|
||||||
5. The API returns the updated invoice.
|
6. The API returns the updated invoice.
|
||||||
|
|
||||||
|
Important constraint:
|
||||||
|
- `mark-paid` is not evidence by itself that a `pending` invoice became payable.
|
||||||
|
- It is only the current manual mechanism for recording that the provider has already given final confirmation outside the app.
|
||||||
|
|
||||||
## Idempotency and transition rules
|
## Idempotency and transition rules
|
||||||
`markInvoicePaid` is replay-safe.
|
`markInvoicePaid` is replay-safe.
|
||||||
@@ -156,11 +170,17 @@ Current payment-specific errors surfaced by the web app:
|
|||||||
|
|
||||||
## Current limitations
|
## Current limitations
|
||||||
- The system still depends on manual admin confirmation to activate access.
|
- The system still depends on manual admin confirmation to activate access.
|
||||||
|
- Because provider-final status is not ingested automatically yet, the app currently relies on operator judgment when calling `mark-paid`.
|
||||||
- No provider callback or reconciliation job updates invoice state automatically.
|
- No provider callback or reconciliation job updates invoice state automatically.
|
||||||
- No runtime path currently moves invoices to `expired` or `canceled`.
|
- No runtime path currently moves invoices to `expired` or `canceled`.
|
||||||
- The provider adapter does not yet verify external status or signatures.
|
- The provider adapter does not yet verify external status or signatures.
|
||||||
- Subscription lifecycle beyond the current `mark-paid` path is still incomplete.
|
- Subscription lifecycle beyond the current `mark-paid` path is still incomplete.
|
||||||
|
|
||||||
|
## Required future direction
|
||||||
|
- Add provider callbacks or polling-based reconciliation.
|
||||||
|
- Persist provider-final status before activating access automatically.
|
||||||
|
- Reduce or remove the need for operator judgment in the normal payment-success path.
|
||||||
|
|
||||||
## Code references
|
## Code references
|
||||||
- `packages/db/src/bootstrap.ts`
|
- `packages/db/src/bootstrap.ts`
|
||||||
- `packages/db/src/auth-store.ts`
|
- `packages/db/src/auth-store.ts`
|
||||||
|
|||||||
Reference in New Issue
Block a user