Sprint 8: rewards & coupons — claim, validate, redeem with points burn - #22
Merged
Conversation
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… coverage (migration 0007) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Once claimed, a reward row previously replaced the claim button with the code+copy UI permanently, foreclosing legitimate repeat claims allowed by perUserLimit > 1. The row now shows both the most recently issued code and the claim button, still subject to the same disabled rules recomputed from refetched data; exceeding the per-user limit surfaces via the server's 409 claim_limit_reached through the existing inline error mapping.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ess, redeem slug from consumed claim, docs Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements the Sprint 8 plan (
docs/superpowers/plans/2026-07-09-sprint-8-rewards-coupons.md) — the burn side of Sprint 7's points economy, closing the earn/burn loop and delivering the "coupon/promo-code generation and validation" v1.x roadmap slice:rewardStrapi content type — generated (codePrefix+ 10-char code, ambiguity-free 32-char alphabet) or static shared codes, optionalpointsPrice, expiry window, per-user claim limit, inventory cap; lifecycle validation incl. per-project slug and static-code uniquenesscouponsrow per claim — the row is the claim record, so per-user and inventory counts derive fromCOUNT(*)with no driftpg_advisory_xact_lockper reward (plus per user when priced — one user racing two priced rewards cannot overspend), counts/balance read under the locks,decideClaimas the sole gate, wallet debit + coupon insert in one transaction; race-tested with exact-count assertions (inventory boundary, balance boundary, parallel redeems)POST /v1/coupons/validate(read-only, always 200) andPOST /v1/coupons/redeem(conditional UPDATE +FOR UPDATE SKIP LOCKED; shared static codes consume oldest-unredeemed; response slug resolved from the consumed claim)listRewards(),claimReward(slug), server-sidevalidateCoupon/redeemCoupon; claim failures surface as typedPromoceanApiError(branch oninsufficient_pointsvsclaim_limit_reached)<RewardsStore/>(balance, claim buttons, inline code + copy, server-authoritative repeat claims); demo earn→claim→redeem flow incl. an sk-backed coupon checkout form; new rewards-loop e2eBehavior notes for existing consumers
recent[].sourcegains'redemption'— older SDK/contracts versions will fail zod-parsing a wallet containing a redemption entry. Semver-minor contracts/sdk bump (changeset included); consumers must upgrade before points-spending exists in their project.DELETE /v1/users/:userIdcounts gaincoupons(additive); erasure removes coupon rows.reward_unavailable,claim_limit_reached,insufficient_points,already_redeemed.staticCodenever appears in pk-accessible responses — codes are revealed only by claiming (else the wallet debit is bypassable). Enforced by schema shape and asserted per-entry in tests.enabled: falseblocks new claims only; to stop redemption of outstanding codes, setendsAtin the past (expiry is evaluated at validate/redeem time — claimed codes die with the campaign).track()trust model (any pk holder can claim as any userId) — privacy note added to the README.docker compose --profile stack down -vto reseed.Test plan
Review notes
Nine tasks through the full implement→review gate cycle (two task fix waves: cms slug-uniqueness populate fix
97fc48e, widgets repeat-claim fixcf3a579) plus a final whole-branch review — verdict ready to merge after fix waveac33cf6(codePrefix capped at 54 so generated codes always fit the 64-char validate/redeem bound — a plan-defect catch; per-project static-code uniqueness; redeem response from the consumed claim; descriptive misconfiguration guard; doc corrections). Follow-ups filed: #20 (cms admin-session verification + legacy duplicate-staticCode scan), #21 (widgets test hardening).🤖 Generated with Claude Code