Enhance ledger operations and referral lifecycle
CI / verify (push) Has been cancelled
CI / publish (push) Has been cancelled

Add traceable ledger filtering and permanent referral codes so operators can investigate credit activity without weakening immutable accounting guarantees.
This commit is contained in:
Rocky
2026-08-20 22:14:22 +08:00
parent 74c3fcd45f
commit b5212dcdc2
38 changed files with 3080 additions and 620 deletions
+98 -13
View File
@@ -263,17 +263,18 @@ paths:
default: { $ref: "#/components/responses/Error" }
/v1/referrals/me:
get:
summary: Return the referral profile and idempotently provision its invite code
summary: Return the referral profile and provision its permanent account invite code
responses:
"200":
description: Referral profile
content:
application/json:
schema: { type: object, additionalProperties: true }
schema: { $ref: "#/components/schemas/ReferralProfile" }
default: { $ref: "#/components/responses/Error" }
/v1/referrals/code:
post:
summary: Idempotently create the current campaign invite code
deprecated: true
summary: Compatibility endpoint for the permanent account invite code
responses:
"200":
description: Invite code
@@ -685,11 +686,14 @@ paths:
- $ref: "#/components/parameters/AdminFrom"
- $ref: "#/components/parameters/AdminUntil"
- $ref: "#/components/parameters/AdminLedgerType"
- $ref: "#/components/parameters/AdminCreatedAtSort"
- $ref: "#/components/parameters/AdminLedgerEntryType"
- $ref: "#/components/parameters/AdminLedgerUsageType"
- $ref: "#/components/parameters/AdminLedgerReferenceId"
- $ref: "#/components/parameters/AdminLedgerSort"
- $ref: "#/components/parameters/AdminSortOrder"
responses:
"200":
description: Credit ledger entries ordered by creation time and entry ID
description: Credit ledger entries ordered by the requested field and unique entry ID
content:
application/json:
schema: { $ref: "#/components/schemas/AdminLedgerPage" }
@@ -710,11 +714,14 @@ paths:
- $ref: "#/components/parameters/AdminFrom"
- $ref: "#/components/parameters/AdminUntil"
- $ref: "#/components/parameters/AdminLedgerType"
- $ref: "#/components/parameters/AdminCreatedAtSort"
- $ref: "#/components/parameters/AdminLedgerEntryType"
- $ref: "#/components/parameters/AdminLedgerUsageType"
- $ref: "#/components/parameters/AdminLedgerReferenceId"
- $ref: "#/components/parameters/AdminLedgerSort"
- $ref: "#/components/parameters/AdminSortOrder"
responses:
"200":
description: Latest credit ledger entries ordered by creation time and entry ID
description: Latest credit ledger entries ordered by the requested field and unique entry ID
content:
application/json:
schema: { $ref: "#/components/schemas/AdminLedgerPage" }
@@ -1007,7 +1014,26 @@ components:
in: query
schema:
type: string
enum: [grant, reserve, settle, refund, adjustment]
enum: [grant, reserve, settle, refund]
AdminLedgerEntryType:
name: entryType
in: query
description: Exact immutable ledger entry type. This filter is ANDed with all other filters.
schema:
$ref: "#/components/schemas/LedgerEntryType"
AdminLedgerUsageType:
name: usageType
in: query
description: Product usage associated with the reservation. HOTWORD request source takes precedence over capability.
schema: { type: string, enum: [polish, asr, ai, agent, hotword] }
AdminLedgerReferenceId:
name: referenceId
in: query
schema: { type: string, format: uuid }
AdminLedgerSort:
name: sort
in: query
schema: { type: string, enum: [createdAt, amount], default: createdAt }
AdminLedgerLimit:
name: limit
in: query
@@ -1411,21 +1437,58 @@ components:
type: array
items: { $ref: "#/components/schemas/AdminUsageAggregate" }
referral: { $ref: "#/components/schemas/AdminUserReferral" }
LedgerEntryType:
type: string
enum:
- SIGNUP_TRIAL
- MANUAL_GRANT
- USAGE_RESERVE
- USAGE_SETTLE
- USAGE_RELEASE
- USAGE_REFUND
- REFERRAL_INVITER
- REFERRAL_INVITEE
- STOREKIT_PURCHASE
- SUBSCRIPTION_GRANT
AdminLedgerDetails:
type: object
additionalProperties: false
required: [kind]
description: Privacy-minimized source metadata. Missing source associations omit the details object.
properties:
kind: { type: string, enum: [manualGrant, storeKit, referral, usage] }
reason: { type: string, description: Present for manualGrant details }
operatorName: { type: string, description: Present for manualGrant details }
productId: { type: string, description: Present for storeKit details }
transactionId: { type: string, description: Present for storeKit details }
originalTransactionId: { type: string, description: Present for storeKit details }
environment: { type: string, enum: [SANDBOX, PRODUCTION], description: Present for storeKit details }
purchasedAt: { type: string, format: date-time, description: Present for storeKit details }
role: { type: string, enum: [inviter, invitee], description: Present for referral details }
relatedUserId: { type: string, format: uuid, description: Present for referral details }
reservationId: { type: string, format: uuid, description: Present for usage details }
AdminLedgerEntry:
type: object
additionalProperties: false
required: [entryId, userId, type, amount, balanceAfter, reasonCode, createdAt]
required: [entryId, userId, type, entryType, amount, balanceAfter, reasonCode, createdAt]
properties:
entryId: { type: string, format: uuid }
userId: { type: string, format: uuid }
type: { type: string, enum: [grant, reserve, settle, refund, adjustment] }
type: { type: string, enum: [grant, reserve, settle, refund] }
entryType: { $ref: "#/components/schemas/LedgerEntryType" }
amount: { type: integer, format: int64 }
balanceAfter: { type: integer, format: int64, minimum: 0 }
reasonCode: { type: string }
reasonCode:
allOf:
- $ref: "#/components/schemas/LedgerEntryType"
deprecated: true
description: Compatibility alias for entryType
referenceId: { type: ["string", "null"], format: uuid }
usageType:
type: ["string", "null"]
enum: [polish, asr, ai, agent, hotword, null]
description: Product usage associated with this ledger operation
details: { $ref: "#/components/schemas/AdminLedgerDetails" }
createdAt: { type: string, format: date-time }
AdminLedgerPage:
type: object
@@ -1728,9 +1791,31 @@ components:
items: { type: object, additionalProperties: true }
ReferralCode:
type: object
additionalProperties: true
additionalProperties: false
required: [code, inviteUrl, createdAt]
properties:
code: { type: string, pattern: "^[A-Za-z0-9_-]{22}$" }
code:
type: string
pattern: "^[A-Za-z0-9_-]{22}$"
description: Permanent opaque identifier assigned once to the account.
inviteUrl:
type: string
format: uri
description: Stable first-party invitation URL containing the permanent code.
campaignId:
type: ["string", "null"]
format: uuid
description: Legacy creation metadata; it does not limit the invitation lifetime.
createdAt: { type: string, format: date-time }
ReferralProfile:
type: object
additionalProperties: false
required: [code]
properties:
code: { $ref: "#/components/schemas/ReferralCode" }
binding:
type: ["object", "null"]
additionalProperties: true
RedeemReferralRequest:
type: object
additionalProperties: false