Payment Processing
Payment Processing Stripe-based credit purchases with a chargeback-prevention tier system, EU consumer law compliance, and planned SEPA/Paddle support. Why T...
Payment Processing
Stripe-based credit purchases with a chargeback-prevention tier system, EU consumer law compliance, and planned SEPA/Paddle support.
Why This Exists
OpenMates uses a credit-based billing model. Users purchase credits via card payments, which are consumed by AI model usage and other API calls. The tier system limits chargeback exposure for new users while allowing trusted users higher limits.
How It Works
sequenceDiagram
participant U as User / Client
participant S as Server
participant ST as Stripe
participant D as Directus
U->>S: POST /payments/create-order<br/>(tier, currency)
S->>S: Check tier limit + monthly spend
S->>ST: Create PaymentIntent
ST-->>S: client_secret
S-->>U: client_secret
U->>ST: Stripe.js card input + 3D Secure
ST-->>U: Payment confirmed
ST->>S: Webhook: payment_intent.succeeded
S->>S: Decrypt credit balance<br/>+ add credits
S->>D: Update user record
S-->>U: WebSocket: balance update
Payment Flow
- User selects credit tier and currency (EUR, USD, JPY).
- Client creates a Stripe PaymentIntent via
POST /v1/payments/create-order. - Stripe.js handles card input and 3D Secure if required.
- On success, Stripe sends
payment_intent.succeededwebhook. - Backend extracts order details from metadata, decrypts and adds credits, updates cache, broadcasts balance via WebSocket.
Chargeback Prevention
Email + 2FA required: Users must confirm email and set up 2FA (TOTP or passkey) during signup before purchasing – filters automated scam attempts.
Tier system (O(1) cached lookup):
| Tier | Monthly Limit | Requirement |
|---|---|---|
| Tier 0 | No card | 2+ chargebacks (SEPA only) |
| Tier 1 | 75 EUR | Default for new users, or after 1 chargeback |
| Tier 2 | 150 EUR | 3 consecutive months without chargeback |
| Tier 3 | 300 EUR | 6 consecutive months without chargeback |
| Tier 4 | 500 EUR | 12 consecutive months without chargeback |
- First chargeback: reset to Tier 1, reset consecutive months counter.
- Second chargeback: reset to Tier 0 (card payments blocked, SEPA only).
- Monthly spending counter resets at start of each calendar month.
- USD/JPY purchases converted to EUR for limit checking.
- SEPA transfers are not subject to tier limits.
EU Consumer Law Compliance
- Explicit consent checkbox (unchecked by default) for immediate digital service execution at checkout.
- Consent logged with timestamp and hashed IP for compliance.
- Purchase confirmation email + invoice PDF include withdrawal waiver notice (durable medium).
- Once credits used, withdrawal right expires (legally protected).
- Gift cards cannot be refunded once redeemed.
- Unused credits refundable within 14 days.
Receipt Email Decryption
- User-initiated payments:
encrypted_email_addressdecrypted with client-providedemail_encryption_key. - Auto top-up payments: No client key available; uses
encrypted_email_auto_topup(Vault-transit encrypted server-side when user enables auto top-up).
SEPA Bank Transfer
Available to all users regardless of payment tier (tier system only limits card payments).
- Provider: Revolut Business API — webhooks detect incoming transfers.
- Reference format:
OM-{account_id}-{order_id_short}(shown to user, must be included in transfer). - Flow: User selects bank transfer → backend creates pending order in Directus + Redis → shows IBAN/BIC/reference → user transfers from their bank → Revolut webhook (
TransactionCreated) fires → match by reference → validate amount (±€0.50 tolerance) → apply credits → send invoice email. - Order persistence:
pending_bank_transfersDirectus collection (7-day TTL), Redis cache for fast webhook matching. - Expiry: Celery beat task runs every 6 hours, expires pending orders past 7-day deadline.
- Amount tolerance: ±€0.50 to absorb intermediary bank fees. Outside tolerance → flagged for admin review.
- Support contributions: Separate endpoint, same flow but no credits granted — dispatches support receipt email.
- Webhook verification: HMAC-SHA256 via
Revolut-Signature+Revolut-Request-Timestampheaders, 5-minute replay protection. - Vault secrets:
kv/data/providers/revolut_business—{env}_webhook_secret,iban,bic,bank_name.
Edge Cases
- Tier 0 users receive clear error message directing them to SEPA transfer.
- Tier data cached on user record for O(1) lookup – no expensive invoice queries per purchase.
- Currency conversion uses current rates; limits checked in EUR equivalent.
Related Docs
- Auto Top-Up – monthly subscriptions and planned low-balance triggers
- Signup & Auth – email confirmation and 2FA setup flow
- Privacy Policy:
shared/docs/privacy_policy.yml