Data Model: UnivaPay Card Payment Integration (MVP)

Draft for planning — final schema is decided in /speckit-plan. Based on the old
docs/univaPayment/ schema, adjusted for the MVP scope (manual registration, no auto-onboarding).

1. user_payment_univapays (new table — one row per supplier)

Stores the supplier’s UnivaPay connection. Mirrors the role of
user_payment_settings.stripe_connect_id in the Stripe flow.

Column Type Notes
id bigint PK
user_id FK users UNIQUE — one connection per supplier
store_id string(64) UnivaPay store UUID; candidate for auto-retrieval from the token
api_key string(512) The app token (JWT) — store-type token
secret_key string(512) Encrypted at rest; never returned to the frontend; never re-displayed
webhook_auth_token string(64) nullable auth_token Yoyacoo generated/displayed for the supplier’s webhook; used for inbound webhook verification (per-store, unlike Stripe’s global env secret)
charges_enabled boolean Whether UnivaPay reports the store ready for charges
status enum not_connected / connected / unavailable (spec FR-003)
mode enum test / live — which token was registered (FR-015 separation)
timestamps

2. orders (modify)

Column Type Notes
univapay_charge_id string(64) nullable Charge UUID returned when the charge is created; used to look up applications from refund_finished / cancel_finished webhooks (charge_id) and for refund/cancel API calls
univapay_status string nullable Last known charge status (successful / failed / canceled …) — optional; application status remains the source of truth
Concept Notes
univapay payment type Allow selecting “Card Payment (UnivaPay)” per service (spec FR-005/FR-006)
univapay_installment_enabled boolean — installment ON/OFF only (spec FR-014); count selection happens in the UnivaPay inline form

4. Webhook idempotency record (new, small)

Column Type Notes
id bigint PK
webhook_event_id string(64) UNIQUE Top-level webhook event id — the idempotency key (replays skip)
payload_hash / raw payload Audit/logging
processed_at datetime

Alternative: reuse the existing failed/log tables per codebase conventions — decided in planning.

Relationships

User 1──1 UserPaymentUnivapay   (per-supplier credentials + status)
Order N──1 User                  (application paid via Order.univapay_charge_id)
Order 1──1 WebhookEventLog      (optional; webhook replay guard, keyed by webhook_event_id)
Service N──M PaymentMethod      (per-service enablement incl. univapay + installment flag)