feat: add invite rewards and redeem codes
This commit is contained in:
@@ -4,9 +4,9 @@ This protocol defines the canonical data contract for user profile, points accou
|
||||
|
||||
Protocol verification status:
|
||||
|
||||
- Last audited migration: `backend/alembic/versions/20260413_0004_register_bonus_claims_snapshot.py`
|
||||
- Last audited models: `backend/src/models/profile.py`, `backend/src/models/user_points.py`, `backend/src/models/points_ledger.py`, `backend/src/models/points_audit_ledger.py`, `backend/src/models/register_bonus_claims.py`, `backend/src/models/agent_chat_session.py`, `backend/src/models/agent_chat_message.py`
|
||||
- Current status: aligned with register bonus moved to application service
|
||||
- Last audited migration: `backend/alembic/versions/20260521_0002_invite_referrals_and_redeem_codes.py`
|
||||
- Last audited models: `backend/src/models/profile.py`, `backend/src/models/user_points.py`, `backend/src/models/points_ledger.py`, `backend/src/models/points_audit_ledger.py`, `backend/src/models/register_bonus_claims.py`, `backend/src/models/invite_referral.py`, `backend/src/models/redeem_code_batch.py`, `backend/src/models/redeem_code.py`, `backend/src/models/system_audit_log.py`, `backend/src/models/agent_chat_session.py`, `backend/src/models/agent_chat_message.py`
|
||||
- Current status: aligned with referral rewards and redeem code activation
|
||||
|
||||
## Scope
|
||||
|
||||
@@ -15,6 +15,10 @@ Protocol verification status:
|
||||
- `points_ledger`
|
||||
- `points_audit_ledger`
|
||||
- `register_bonus_claims`
|
||||
- `invite_referrals`
|
||||
- `redeem_code_batches`
|
||||
- `redeem_codes`
|
||||
- `system_audit_logs`
|
||||
- `sessions`
|
||||
- `messages`
|
||||
|
||||
@@ -23,6 +27,7 @@ Protocol verification status:
|
||||
- Current strategy: additive evolution.
|
||||
- Breaking changes (drop/rename/type change on core fields) require explicit migration + rollback notes.
|
||||
- `points_ledger.metadata.schema_version` is mandatory and current value is `1`.
|
||||
- Invite reward amount is configured by backend env `ERYAO_POINTS_POLICY__INVITE_REWARD_POINTS`.
|
||||
|
||||
## Runtime charging policy (chat)
|
||||
|
||||
@@ -53,6 +58,14 @@ Protocol verification status:
|
||||
|
||||
Note: `register` and `adjust` do not bind to any `biz_type` (they are `null`).
|
||||
|
||||
### `adjust` metadata ext.reason conventions
|
||||
|
||||
For referral rewards and redeem codes, `change_type='adjust'` must use one of:
|
||||
|
||||
- `invite_reward_inviter`
|
||||
- `invite_reward_invitee`
|
||||
- `redeem_code_activation`
|
||||
|
||||
## Table contract
|
||||
|
||||
### profiles
|
||||
@@ -129,6 +142,87 @@ Note: `register` and `adjust` do not bind to any `biz_type` (they are `null`).
|
||||
- `balance_snapshot` stores the latest pre-delete account balance for same-email re-registration recovery
|
||||
- `has_purchased_starter_pack` tracks whether user has purchased the starter pack ($0.99/60 credits)
|
||||
|
||||
### invite_referrals
|
||||
|
||||
- PK: `id`
|
||||
- FK:
|
||||
- `inviter_user_id -> profiles.id`
|
||||
- `invitee_user_id -> profiles.id`
|
||||
- `invite_code_id -> invite_codes.id`
|
||||
- `first_creem_transaction_id -> creem_transactions.id` (`on delete set null`)
|
||||
- Core fields:
|
||||
- `invite_code_snapshot`
|
||||
- `bound_at`
|
||||
- `first_creem_paid_at`
|
||||
- `inviter_reward_event_id`
|
||||
- `invitee_reward_event_id`
|
||||
- `inviter_reward_granted_at`
|
||||
- `invitee_reward_granted_at`
|
||||
- `created_at`
|
||||
- `updated_at`
|
||||
- Constraints:
|
||||
- one invitee can only appear once
|
||||
- inviter cannot equal invitee
|
||||
- invite code snapshot is immutable after bind
|
||||
- Notes:
|
||||
- historical bindings should be backfilled from `profiles.referred_by`
|
||||
- reward grant is idempotent per side via unique event ids
|
||||
|
||||
### redeem_code_batches
|
||||
|
||||
- PK: `id`
|
||||
- Core fields:
|
||||
- `batch_key`
|
||||
- `created_by`
|
||||
- `notes`
|
||||
- `created_at`
|
||||
- `updated_at`
|
||||
- Constraints:
|
||||
- `batch_key` unique
|
||||
|
||||
### redeem_codes
|
||||
|
||||
- PK: `id`
|
||||
- FK:
|
||||
- `batch_id -> redeem_code_batches.id`
|
||||
- `redeemed_by_user_id -> auth.users.id` (`on delete set null`)
|
||||
- Core fields:
|
||||
- `code`
|
||||
- `package_product_code`
|
||||
- `package_type`
|
||||
- `package_name_snapshot`
|
||||
- `credits`
|
||||
- `sort_order`
|
||||
- `status`
|
||||
- `redeemed_at`
|
||||
- `redeem_event_id`
|
||||
- `created_at`
|
||||
- `updated_at`
|
||||
- Constraints:
|
||||
- `code` unique
|
||||
- `status in ('active', 'redeemed', 'disabled')`
|
||||
- redeemed rows must have `redeemed_at`, `redeemed_by_user_id`, `redeem_event_id`
|
||||
- Notes:
|
||||
- only regular packages are eligible for batch generation in this feature
|
||||
- redeem codes do not qualify as CREEM payments for invite binding rewards
|
||||
|
||||
### system_audit_logs
|
||||
|
||||
- PK: `id`
|
||||
- Core fields:
|
||||
- `actor_user_id`
|
||||
- `target_user_id`
|
||||
- `action`
|
||||
- `entity_type`
|
||||
- `entity_id`
|
||||
- `metadata`
|
||||
- `created_at`
|
||||
- `updated_at`
|
||||
- Constraints:
|
||||
- metadata must be object
|
||||
- Notes:
|
||||
- used for invite bind, invite reward grant, redeem code batch generation, redeem code activation
|
||||
|
||||
#### points_ledger.metadata (schema_version=1)
|
||||
|
||||
Canonical shape:
|
||||
@@ -174,6 +268,7 @@ JSON constraints:
|
||||
- Application service (in `POST /auth/email-session`):
|
||||
- `grant_register_bonus_if_eligible()` restores `balance_snapshot` first when present; otherwise grants register bonus via `register_bonus_claims`
|
||||
- Bonus amount from `config.points_policy.register_bonus_points`
|
||||
- referral binding remains write-once after signup via API or historical trigger snapshot
|
||||
|
||||
### sessions
|
||||
|
||||
@@ -260,6 +355,13 @@ Returns the authenticated user's points ledger in reverse chronological order.
|
||||
}
|
||||
```
|
||||
|
||||
## Invite and redeem integration notes
|
||||
|
||||
- Invite binding is write-once, cannot be unbound, and is allowed regardless of previous completed `creem_transactions` rows.
|
||||
- Referral reward qualification is based on the first completed CREEM payment after the invite binding has been created.
|
||||
- Invite rewards are credited as `adjust` ledger rows with reason `invite_reward_inviter` / `invite_reward_invitee`.
|
||||
- Redeem code activation is credited as an `adjust` ledger row with reason `redeem_code_activation`.
|
||||
|
||||
**Fields:**
|
||||
- `items`: ledger rows ordered by `createdAt desc`
|
||||
- `direction`: `1` for income, `-1` for spending/deduction
|
||||
|
||||
Reference in New Issue
Block a user