849ef3c3107a33087d6f0de2913f5fb1c304603a
tithe-1 / tithe-5 / band-1 + wallet-shop Applet — Chunk 2 of [[project-wallet-shop-expansion]]. ShopItem is the admin-managed catalog: slug, name, description, icon (FA class), badge_text (eg "×5"), price_cents, granted_token_type (any Token type), granted_count, granted_writs (default 0), max_owned (nullable; BAND=1), display_order, active. is_available_for(user) enforces max_owned by comparing user's owned-count of the granted token type. price_display() renders cents → "$1" / "$4.20" for tooltip prose. Purchase is the per-tx audit trail: user + shop_item + stripe_payment_intent_id (unique) + status (PENDING/SUCCEEDED/FAILED/REFUNDED) + amount_cents snapshot + granted_writs snapshot + granted_token_ids JSONField (PKs of minted tokens) + created_at + succeeded_at. fulfill() is idempotent — short-circuits if status==SUCCEEDED + refuses non-PENDING rows so a webhook + sync /shop/confirm racing each other can't double-mint. Schema migration lyric/0008_shopitem_purchase autogenerated. Seed migration lyric/0009_seed_shop_items populates the 3 starting items per locked decisions: tithe-1 ($1 → 1 TITHE + 144 writs, no cap, order=10); tithe-5 ($4 → 5 TITHE + 750 writs, no cap, badge "×5", order=20); band-1 ($20 → 1 BAND + 0 writs, max_owned=1, order=30). Applet migration applets/0011_seed_wallet_shop_applet adds the wallet-shop Applet (context=wallet, 12 cols × 3 rows). Stub _applet-wallet-shop.html lands w. just <section id="id_wallet_shop"> + <h2>Shop</h2> — _applets.html's auto-include-by-slug pattern would 500 the wallet page on TemplateDoesNotExist otherwise (caught mid-Chunk-2 by the full app suite). Chunk 4 fills in the shop-tile grid + BUY-ITEM microtooltip + Stripe.js wiring. TDD — 22 ITs in test_shop_models.py: ShopItemModelTest (9 cases — minimal create, defaults for granted_writs / max_owned / active, is_available_for w/ + w/o max_owned cap, str repr), PurchaseModelTest (8 cases — minimal create, PI ID uniqueness constraint, fulfill mints tokens + grants writs + marks SUCCEEDED + records granted_token_ids + is idempotent on re-fire + creates N tokens for bundle), SeededShopCatalogTest (4 cases pin tithe-1 / tithe-5 / band-1 row shapes + display_order ascending), SeededWalletShopAppletTest (1 case pins Applet seeded). 1191 IT/UT green
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Description
No description provided
Languages
Python
45%
JavaScript
37.6%
HTML
9%
SCSS
8.2%
Jinja
0.1%