* feat(sca): sweep abandoned 3ds requires_action orders to failed
* fix(sca): address pr review on abandoned 3ds sweep
- Drop stripe.Key write in ReconcileStale3DSOrders; wire once in api/main.go after cfg.Load so concurrent reconcilers don't
race the SDK global.
- Add empty-PI short-circuit before paymentintent.Get to bury a future regression in a slog.Warn instead of a Stripe 4xx.
- Swap fmt.Printf for slog.Warn with k/v attrs in loadAbandoned3DSThresholdMinutes so Loki sees structured fields instead of
literal %q/%d.
- Comment the updated_at predicate (RetryAuth bumps it via GORM auto-stamp, intentional grace-window extension) + the
conditional-UPDATE/map-form choice.
- Tests: processing/requires_capture skip, unknown-status skip, stripe-error skip, empty-PI skip, full env-parse table for
loadAbandoned3DSThresholdMinutes.
* fix(stripe): wire key once at init drop per-call writes
* refactor(stripe): drop dead stripeKey fields and pre-call sdk params
* feat(sca): add order status endpoint adn SCA error code mapping
adds server-side infra for the upcoming SCA / 3DS challenge flow im
working on. No behavior change to existing checkout paths.
* fix(sca): suppress unused lint on SCA scaffolding helpers
* feat(refund): add refund request flow
- tui modal composer with reason select + free-form message
(pkg/tui/refund.go)
- POST /api/v1/orders/{id}/refund-request posts to orders slack thread
- 15 min server side cooldown via Order.LastRefundRequestAt (429 +
retry-after)
- shared refundrequestreasons + can requestrefund hoisted to pkg/models
- slack message includes customer name, email, order total, quoted body
- tests over invalid-state, missing-message, and cooldown paths
* style(tui): run gofmt to fix formatting
Pulls events off the new order_events table and shows them as a timeline
block in the order detail view: Paid, Label created, In transit,
Delivered (etc) with timestamps. Filters out ops-only events like
payment_critical, label_orphaned, and tracking_marked_manually so the
customer never sees internal plumbing.
DisplayLabel on OrderEvent decides what's customer-visible and what's
redacted, so the same order_events rows can later power an admin view
with no filter.
Adds POST /api/v1/orders/{id}/label (admin-only) that buys a shipping
label through Shippo and writes carrier + tracking + label fields back
onto the order, transitioning paid -> shipped.
realized per-shipment table is kinda bad, switched to single embedded tracking blob. since im only
shipping from my house no split is needed. adds tracking_status, tracking_status_details,
tracking_status_updated_at columns. reverse-backfills any historical shipments rows back onto orders
(idempotent, table-guarded). UpdateTracking rewritten to write to otder columns direcly
adds a /pay/{token} redirect endpoint that generates a short URL
pointing to a stripe checkout sessions in setup mode. The TUI shows a QR
code and URL on the payment page so users can add a card via the browser
instead of entering raw card data over SSH.
the checkout.session.completed webhook saves the card to the database.
the TUI polls GET /api/v1/cards every second and auto-proceeds to
confirmation once the new card is detected.
also adds a dev.sh startup script that runs all four required processes.
(SSH server, API server, Stripe CLI listener, ngrok) in a single
terminal