2 Commits

Author SHA1 Message Date
e84444a56b feat(customer-web): Phase 4 — buy QR codes (cash flow)
Backend:
- Surface a default retail price per code on the public partner-store
  payload (config qr.default_retail_price_per_code_centavos, env
  QR_DEFAULT_RETAIL_PRICE_PER_CODE_CENTAVOS, default ₱10 = 1000c).
  Per-store overrides can come later — the LGU sets one official price
  for now.
- Resident-facing flow uses the existing ManualPaymentDriver: resident
  POSTs /me/payments/code-purchase, payment is created in pending,
  admin marks it paid via /admin/payments/{uuid}/mark-paid, fulfillment
  runs StoreOperations::sellToHousehold which activates the codes +
  fires the CodesPurchased notification.

Frontend (customer-web/):
- /stores — list nearby partner stores (1/2/5/10/25 km radius pills),
  shows in-stock pill + price + distance, links into purchase page
  only when has_stock.
- /stores/[uuid] — store detail with stat row (price / stock /
  distance) and PurchaseForm: quantity presets (5/10/20/50) + custom
  input clamped to inventory, live total computation, blue "cash
  payment" callout explaining the flow.
- /payments/[uuid] — three states:
  - Pending: ticket-style card with reference + amount + 4-step
    instructions; auto-polls every 8 s until paid.
  - Paid: success card with checkmark + paid_at timestamp + link
    to /codes.
  - Failed/refunded: red card with retry CTA back to /stores.
- Resident nav adds "Buy codes" link.
- API client: PartnerStore now carries retail_price_per_code_centavos;
  Payment + PaymentStatus types added; payments.show() returns the
  typed status union.

Build: 21 routes, all type-check + lint clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 20:23:58 +08:00
306f8fb4e8 feat(backend): customer-site API gaps + CORS
- GET /partner-stores/nearby — public, residents browse active stores
  ranked by distance via ST_Distance_Sphere; PublicPartnerStoreResource
  excludes commission rate / owner / permit (only what a buyer needs).
- GET /partner-stores/{uuid} — public details, 404 if not active.
- GET /me/collections — paginated resident QR scan history with
  optional from/to date filters.
- GET /me/upcoming-pickups — finds scheduled/in-progress trips whose
  route includes the resident's assigned drop-off point. Returns
  household_assigned: false when no household yet.
- GET /me/notifications — paginated database notifications inbox
  with unread_only filter + unread_count in meta.
- POST /me/notifications/{id}/read, POST .../mark-all-read,
  GET .../unread-count.
- config/cors.php — allow CUSTOMER_APP_URL and any EXTRA_CORS_ORIGINS
  to call the API with credentials. Same-origin admin web is
  unaffected.

202 feature tests passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 17:55:10 +08:00