# EDU-019 — Secure learning activation codes ## Status Done for the bounded V4310 contract. Legacy activation-code import and coupons remain separate. ## Decision Activation codes are Education learning-access credentials, not Mall Promotion coupons. They reference a Mall-owned SPU through the existing tenant-scoped `education_resource_product_binding`, authenticate redemption with the existing Member principal, and grant access through the existing idempotent `EducationEntitlementService` event pipeline. No product, member, coupon, or second entitlement ledger is introduced. ## Delivered contract - Tenant-owned activation-code batches and codes with database constraints, tenant-composite foreign keys, optimistic versions, and separate query/manage/generate permissions. - Admin batch page/create/update/generate and masked code page/disable endpoints. - Member-only app check/redeem endpoints; administrator and anonymous principals fail closed. - Cryptographically random codes normalized for redemption. Plaintext is returned only by the successful generation response; persistence stores SHA-256 digest and a mask. - `SELECT ... FOR UPDATE` serializes redemption. A successful grant writes the existing entitlement event/aggregate, marks the code redeemed, and increments the batch count in one transaction. - Same-member retry returns the existing entitlement idempotently; another member receives an already-used conflict. - Batch SPU, duration, and prefix become immutable after generation. Disabled batches, codes, or resource bindings cannot be redeemed. `durationDays=0` intentionally means no expiry. - Vben page `education/activation-code/index` provides batch and masked-code tables, create/edit/generate flows, copy/download, unsaved-plaintext dismissal warning, and disable confirmation. Closing the generation modal clears plaintext. ## Verification - PostgreSQL Flyway verifies both tables, constraints, cross-tenant batch references, four menu rows, and migration history through V4310. - Controller contracts verify independent admin permissions and Member-only app access. - Unit tests verify digest/mask persistence, entitlement composition, replay/conflict, target validation, and generated-batch immutability. - Real PostgreSQL service tests verify digest-only persistence, tenant isolation, entitlement/event creation, same/different-member behavior, disabled dependencies, and one winner under concurrent redemption. - Vben formatting, lint, Vue typecheck, and production build cover the fifteenth custom Education page. ## Explicit non-goals - Importing or preserving plaintext from legacy activation-code rows. - Coupon templates, claims, discounts, stacking, or redemption; those remain Mall Promotion-owned. - Payment/order/refund fulfillment, legacy payment mode/provider mapping, tenant PNVS, or generic encrypted secret rotation.