Payment Event Processor — Technical Specifications
Technical specifications only: data schemas, API contracts, rules, algorithms, and in-process behavior. This document does not restate business goals or end-to-end integration flows.
1. Conventions
- Amounts: integers in cents unless noted.
- IDs: Stripe
payment_intent_id(pi_...),charge_id,acct_...Connect accounts, Firestore document IDs as implemented. - API version:
/api/v1blueprint inapp/api/v1.py.
2. Data model (Firestore)
2.1 Ledger and events
| Collection | Purpose |
|---|---|
payments |
One document per payment; keyed by payment_intent_id (document id). Current state snapshot. |
payment_transactions |
Immutable ledger rows for financial events. |
event_logs |
Chronological timeline for UI/API (transaction_id often tied to payment_intent_id). |
webhook_events |
Received Stripe events; idempotency and debugging. |
refunds |
Refund records (listing and audit). |
disputes |
Dispute metadata (may include legacy stripe_account_id field). |
2.2 Identity, tenants, RBAC
| Collection | Purpose |
|---|---|
tenants |
tenant_id, merchant_account_id, onboarding flags, requirements_due, etc. |
users |
email, password_hash, tenant_id, default_tenant_id, merchant_account_id, email_verified, role, platform_role, active. |
org_units |
Tenant-scoped hierarchy. |
memberships |
(user_id, tenant_id), role_id, status. |
roles, permissions, role_permissions |
RBAC matrix; role_permissions cached in-process. |
api_keys |
API key records (hashed), tenant/merchant binding. |
verification_tokens |
Email verification token + code + expiry. |
audit_logs |
Security-relevant audit entries (optional population). |
2.3 Payment document fields (representative)
- Lifecycle:
status,authorization_status,refund_status,dispute_status - Money:
original_amount,amount_captured,amount_refundable,refunded_amount,disputed_amount,net_merchant_exposure - Scope:
tenant_id,org_unit_id,merchant_account_id - Risk:
risk_score,risk_level,health_score,auth_type,cvv_result, etc. (as populated by webhook pipeline)
3. Authentication and authorization (technical)
3.1 Middleware
jwt_required: cookie or Bearer JWT; loadsg.user,g.tenant_id,g.membership,g.role_id.auth_required: JWT first, then API key (sk_*Bearer orX-API-Key).@require_permission(permission_id): checksrole_permissionsforg.role_id.
3.2 Tenant context (CTX)
Resolution order (see utils/security.py):
X-Tenant-Idheaderuser.tenant_iduser.default_tenant_id
Membership query: memberships where user_id, tenant_id, status == active.
3.3 Token types
- Access token: JWT, type
access. - Refresh token: JWT, type
refresh(used byPOST /api/v1/refresh).
4. Webhook endpoint (technical)
- Route:
POST /webhook(implemented outside v1 blueprint; seestripe_app/webhooks.py). - Verification: Stripe signing secret; reject invalid signatures.
- Idempotency:
stripe_event_idprocessed once per application rules (webhook_events/ processed_events pattern in ledger code).
5. API v1 — Technical specifications
Source: app/api/v1.py, prefix /api/v1.
5.1 Discovery and health
| Method | Path | Auth | Notes |
|---|---|---|---|
| GET | / |
None | Static JSON discovery; Cache-Control: public, max-age=300. |
| GET | /health |
None | JSON dependency payload. |
| GET | /health/liveness |
None | Always alive JSON. |
| GET | /health/readiness |
None | May return 503 if degraded. |
5.2 Auth (stateless API)
| Method | Path | Auth | Request body | Response (success) |
|---|---|---|---|---|
| POST | /login |
None | { email, password } |
{ access_token, refresh_token, token_type, expires_in, role, merchant_account_id, tenant_id } |
| POST | /refresh |
None | { refresh_token } |
{ access_token, token_type, expires_in } |
| POST | /signup |
None | { email, password, business_name?, country? } |
201 { tenant_id, user_id, email, message } |
Rules: login fails if email_verified is false; inactive users rejected; roles admin | merchant for API login.
5.3 Stripe Connect
| Method | Path | Auth | Permission |
|---|---|---|---|
| POST | /connect/accounts |
JWT | manage_stripe_account |
| GET | /connect/onboarding-link |
JWT | manage_stripe_account |
| GET | /connect/status |
JWT | manage_stripe_account |
| GET | /connect/express-login-link |
JWT | manage_stripe_account |
| POST | /connect/sync |
JWT | manage_stripe_account |
Connect account rules: POST /connect/accounts no-ops if tenant.merchant_account_id already set; returns existing id.
5.4 Checkout
| Method | Path | Auth | Permission |
|---|---|---|---|
| POST | /checkout-sessions |
JWT or API key | create_payment |
| POST | /create-checkout-session |
Same | Same |
Body (validated wrapper): connected_account_id (required, acct_), amount (int cents), platform_fee, currency, description, customer_email, metadata, idempotency_key.
Idempotency: Idempotency-Key header or body; max 128 chars; no /; fingerprint stored for replay/conflict detection.
5.5 Payments (read)
| Method | Path | Auth | Permission |
|---|---|---|---|
| GET | /payments |
JWT | view_payment |
Query: status, risk_level, merchant / merchant_account_id (admin), limit (1–100, default 50).
Scoping: merchant-scoped users filter payments by merchant_account_id == user.merchant_account_id.
5.6 Timeline
| Method | Path | Auth |
|---|---|---|
| GET | /payments/<payment_id>/timeline |
JWT |
| GET | /timeline/<payment_id> |
JWT |
Delegates to legacy timeline handler; reads event_logs (per implementation).
5.7 Disputes
| Method | Path | Auth | Permission |
|---|---|---|---|
| GET | /disputes |
JWT | view_disputes |
| GET | /disputes/<dispute_id> |
JWT | None (JWT only; tenant check in handler) |
Query (list): status, payment_intent_id, limit.
5.8 Refunds
| Method | Path | Auth | Permission |
|---|---|---|---|
| GET | /refunds |
JWT | view_payment |
| POST | /refunds |
JWT | refund_payment |
POST body: payment_intent_id or payment_id, optional amount / amount_cents, reason, idempotency_key; headers Idempotency-Key / X-Idempotency-Key.
Rules: payment must be status == succeeded; tenant must match payment merchant_account_id; dispute states block refund per message map.
5.9 Manual capture / cancel
| Method | Path | Auth | Permission |
|---|---|---|---|
| POST | /payments/<payment_id>/capture |
JWT | create_payment |
| POST | /payments/<payment_id>/cancel |
JWT | create_payment |
Rules: payment status == requires_capture; merchant_account_id must be acct_...; tenant must match for non-admin.
Idempotency: Idempotency-Key / X-Idempotency-Key.
6. Risk engine (technical)
- Centralized logic in
risk/risk_engine.py(health score, net exposure). - Derived fields updated when webhook pipeline applies events that change amounts or dispute/refund state.
7. Engineering acceptance tests (technical)
- Idempotency: replay same Stripe
event_id→ ledger unchanged after first apply. - RBAC:
require_permissiondenies unauthorized roles. - Query limits:
limitcapped at 100 where applicable. - Refund math:
requested_amount <= remaining_refundableenforced before Stripe call.
Appendix A. API v1 endpoint matrix (reference)
| Endpoint | Permission | Primary Firestore reads/writes | Stripe SDK (if any) |
|---|---|---|---|
GET / |
— | — | — |
GET /health* |
— | optional connectivity | config only |
POST /login |
— | users read |
— |
POST /refresh |
— | users read |
— |
POST /signup |
— | tenants, users, memberships, verification_tokens | — |
POST /connect/accounts |
manage_stripe_account |
tenants, users update | Account.create |
GET /connect/onboarding-link |
manage_stripe_account |
tenants read | AccountLink.create |
GET /connect/status |
manage_stripe_account |
tenants read | — |
GET /connect/express-login-link |
manage_stripe_account |
tenants read | Account.create_login_link |
POST /connect/sync |
manage_stripe_account |
tenants update | Account.retrieve |
POST /checkout-sessions |
create_payment |
idempotency docs | delegated (Checkout Session) |
GET /payments |
view_payment |
payments query |
— |
GET .../timeline |
JWT | event_logs (delegated) |
— |
GET /disputes |
view_disputes |
disputes queries |
— |
GET /disputes/{id} |
JWT | disputes get/query |
— |
GET /refunds |
view_payment |
refunds query |
— |
POST /refunds |
refund_payment |
payments read, idempotency | Refund.create |
POST .../capture |
create_payment |
add_api_ledger_entry |
PaymentIntent.capture |
POST .../cancel |
create_payment |
add_api_ledger_entry |
PaymentIntent.cancel |
Stripe column details and sequence diagrams are in docs/INTEGRATION_DESIGN.md.