make_post_sell/docs/tickets/mps-10.md
russell@unturf.com 5d501652c2 feat: add gift card system for shops (MPS-10 through MPS-13)
Variable-amount gift cards purchasable with any payment method.
Code-based redemption at checkout (applied to cart like coupons).
Partial use across multiple purchases, never expire. Shop owners
control min/max amounts and can disable individual cards.

Models: GiftCard, GiftCardTransaction, CartGiftCard + migration.
Views: purchase page, cart apply/remove, shop admin manage/detail/toggle.
Templates: gift_card.j2, gift_card_manage.j2, gift_card_detail.j2.
Cart integration: gift cards deduct after coupons in all checkout paths.
Tests: 10 new unit tests covering model logic (677 total pass).
2026-03-07 15:38:40 -05:00

3 KiB

MPS-10: Gift Card System — Models & Migration

Problem

Shops want to sell variable-amount gift cards. Buyers pick an amount (slider), purchase with any payment method (including crypto), and receive a code. The recipient enters the code at checkout (like a coupon) and the balance decrements across purchases. Gift cards never expire (permacomputer rules).

This ticket covers the data layer only. Purchase flow (MPS-11), redemption flow (MPS-12), and shop admin UI (MPS-13) are separate tickets.

Solution

New model: MpsGiftCard

Column Type Notes
id UUIDType PK (uuid1)
shop_id UUIDType FK to Shop — card is scoped to one shop
code Unicode(64) Unique redemption code, uppercase alphanumeric
initial_amount_in_cents BigInteger Amount at time of purchase
balance_in_cents BigInteger Current remaining balance
purchaser_email Unicode(256) Email of the buyer
gift_email Unicode(256) Optional recipient email
invoice_id UUIDType FK to Invoice — the purchase transaction
created_timestamp BigInteger Milliseconds
disabled Boolean Admin kill switch, default False

Properties:

  • is_valid — not disabled and balance > 0
  • balancecents_to_dollars(balance_in_cents)
  • initial_amountcents_to_dollars(initial_amount_in_cents)
  • shop_uuid_str — string form of shop_id

Code generation: 16-char uppercase alphanumeric (secrets.token_hex(8).upper()), prefixed with GC- for human readability. Example: GC-A1B2C3D4E5F6G7H8.

New model: MpsGiftCardTransaction

Tracks every time a gift card balance is used at checkout.

Column Type Notes
id UUIDType PK (uuid1)
gift_card_id UUIDType FK to MpsGiftCard
invoice_id UUIDType FK to Invoice — the purchase that used the card
amount_in_cents BigInteger Amount deducted from balance
created_timestamp BigInteger Milliseconds

Shop settings columns

Add to Shop model:

Column Type Default
gift_card_enabled Boolean False
gift_card_min_in_cents BigInteger 500 ($5.00)
gift_card_max_in_cents BigInteger 25000 ($250.00)

Helper functions

  • get_gift_card_by_code(dbsession, code, shop=None) — lookup by code, optionally scoped to shop
  • get_gift_card_by_id(dbsession, gift_card_id) — standard ID lookup
  • get_gift_cards_by_shop(dbsession, shop) — all cards for a shop (admin view)

Files Changed

File Change
models/gift_card.py New: MpsGiftCard model
models/gift_card_transaction.py New: MpsGiftCardTransaction model
models/shop.py Add gift_card_enabled, gift_card_min_in_cents, gift_card_max_in_cents
models/__init__.py Import new models
scripts/alembic/versions/*_gift_card_tables.py Migration: new tables + shop columns

Depends On

Nothing. Foundation ticket.