Add StoreKit history and modernize admin console
CI / verify (push) Has been cancelled
CI / publish (push) Has been cancelled

Expose ledger-backed cross-device purchase history while shipping the tested React admin redesign in the same reproducible deployment revision.
This commit is contained in:
Rocky
2026-08-19 22:13:13 +08:00
parent 11ec34dacb
commit 231c5040a5
51 changed files with 6484 additions and 4236 deletions
+11
View File
@@ -47,3 +47,14 @@ mix and require ongoing margin monitoring.
transaction. A client retry returns the original grant.
- The app finishes the StoreKit transaction only after server acknowledgement.
- Signed transaction bodies and Apple certificate contents are never logged.
## Purchase history
- `GET /v1/storekit/transactions` requires the existing Bearer access token.
- The account comes only from the authenticated session; clients never submit it.
- History is read from the existing purchase audit and immutable credit ledger,
so purchases credited before the endpoint was deployed are included.
- Results are ordered by `purchasedAt` and then `transactionId`, both descending,
with opaque cursor pagination (`limit` defaults to 50 and is bounded to 1100).
- Responses omit JWS data, Apple identifiers, account IDs, and internal record IDs.
- History queries never call the App Store.
+45
View File
@@ -177,6 +177,28 @@ paths:
items: { $ref: "#/components/schemas/StoreKitProduct" }
default: { $ref: "#/components/responses/Error" }
/v1/storekit/transactions:
get:
summary: Return credited StoreKit purchase history for the current account
description: |
Returns only App Store transactions that were previously verified and
committed with their immutable credit-ledger entries. Results include
purchases credited before this endpoint was introduced and never call
the App Store at query time. The opaque cursor follows the stable
descending order of `purchasedAt` and `transactionId`.
parameters:
- $ref: "#/components/parameters/Limit"
- name: cursor
in: query
description: Opaque cursor returned by the preceding page.
schema: { type: string, minLength: 1, maxLength: 256 }
responses:
"200":
description: Credited StoreKit purchases for the authenticated account
content:
application/json:
schema: { $ref: "#/components/schemas/StoreKitTransactionHistory" }
"400": { $ref: "#/components/responses/Error" }
default: { $ref: "#/components/responses/Error" }
post:
summary: Verify an App Store transaction and idempotently grant credits
description: |
@@ -1306,6 +1328,29 @@ components:
creditsGranted: { type: integer, format: int64, minimum: 1 }
balanceAfter: { type: integer, format: int64, minimum: 0 }
replayed: { type: boolean }
StoreKitTransactionHistoryItem:
type: object
additionalProperties: false
required:
[transactionId, productId, creditsGranted, balanceAfter, purchasedAt, status]
properties:
transactionId: { type: string, pattern: "^[0-9]{1,64}$" }
productId: { type: string, minLength: 3, maxLength: 128 }
creditsGranted: { type: integer, format: int64, minimum: 1 }
balanceAfter: { type: integer, format: int64, minimum: 0 }
purchasedAt: { type: string, format: date-time }
status: { type: string, enum: [credited] }
StoreKitTransactionHistory:
type: object
additionalProperties: false
required: [items, nextCursor]
properties:
items:
type: array
items: { $ref: "#/components/schemas/StoreKitTransactionHistoryItem" }
nextCursor:
type: ["string", "null"]
maxLength: 256
AppleAppSiteAssociation:
type: object
additionalProperties: false