Architecture

Payment Processing Domain Architecture

Last updated: 2026-02-01 | Architecture

Payment Processing Domain Architecture

Part 1: Stripe + Subscriptions (Session 4)

Key Takeaways

  1. Stripe is the sole payment processor — All payments (one-time and recurring) flow through Stripe. No other payment gateway is used for billing. Stripe SDK 27.1.0.
  2. Virtual currency (“coins”) system — The platform has an in-app currency with exchange rates, discount codes, and promo codes. Fans buy coins via Stripe, then spend coins on content/events via the wallet service.
  3. Purchase-request-bpm orchestrates purchase fulfillment — CIB Seven 2.0 (not Camunda 7) runs a BPMN process that handles the full purchase lifecycle: payment → entitlements → wallet debit/credit → notifications → refunds.
  4. Stripe product sync is message-driven — When subscriptions, courses, journeys, or office hours are created, RabbitMQ messages trigger Stripe product creation. Stripe product IDs are stored back in inventory.
  5. Subscriptions service is a product catalog, not billing — It manages subscription definitions (name, price, recurrence, entitlements) but delegates all payment to Stripe and all fulfillment to the BPM engine.

Migration Decision Question

What is the payment infrastructure migration path?

Migration Verdict

Verdict: Upgrade Complexity: L Key Constraint: CIB Seven BPM engine (EOL risk) orchestrates purchase fulfillment; Stripe webhook URLs and product catalog sync must be preserved Dependencies: Stripe API keys, CIB Seven Keycloak plugin, wallet service (Session 5), inventory service, email service

Services Inventory

Service Gen Deployed? Stack Purpose
stripe 2 Yes (4/4 tenants) Java 21 / SB 3.5.4 Stripe payment processing, webhooks, checkout
subscriptions 2 Yes (4/4 tenants) Java 21 / SB 3.5.4 Subscription product catalog, series
purchase-request-bpm 2 Yes (4/4 tenants) Java 21 / SB 3.5.4 / CIB Seven 2.0 Purchase fulfillment orchestration

Database Repos

Repo Migrations Tables Purpose
peeq-stripe-db 13 7 Exchange rates, discount codes, Stripe customers, checkout data
peeq-subscriptions-db 14 4 Subscriptions, series, tags, notification log

API Surface

Stripe Service (11 GraphQL + 1 REST)

GraphQL Mutations (4): - createPaymentIntent — Creates Stripe PaymentIntent, returns client secret - createSetupIntent — Creates Stripe SetupIntent for card registration - cancelActiveStripeSubscription — Cancels active Stripe subscription - createDiscountCode / updateDiscountCode — Discount code CRUD (financial role)

GraphQL Queries (7): - getStripePortalSession — Creates Stripe customer portal session URL - getStripeCheckoutSession — Creates checkout session for purchases - queryActiveStripeSubscriptions — Gets active subscriptions for user - queryStripeSubscriptions — Gets subscriptions by state (ACTIVE, CANCELED, ALL) - getAllDiscountCodes — List discount codes (admin) - validateDiscountCodeForCoinPurchase — Validates discount code - getExchangeRates / getExchangeRatesCount / getDeletedExchangeRates — Coin exchange rate CRUD

GraphQL Mutations (Exchange Rate, 3): - createExchangeRate / updateExchangeRate / deleteExchangeRate — Admin coin rate management

REST (1): - POST /api/stripe/callback — Stripe webhook handler (signature-verified)

Webhook Events Handled: 1. checkout.session.completed 2. payment_intent.succeeded 3. setup_intent.succeeded 4. invoice.payment_succeeded 5. invoice.payment_failed 6. invoice.created 7. charge.refunded

Subscriptions Service (8 GraphQL + 1 REST)

GraphQL Mutations (7): - createSubscription — Create subscription product definition - updateSubscription — Update subscription product - createSeries / updateSeries — Content series CRUD - batchUpdateSubscriptionDisplayOrder — Reorder subscriptions in UI - setSubscriptionPairing / unsetSubscriptionPairing — Link/unlink subscription tiers (admin)

GraphQL Queries (4): - searchSubscriptions — Multi-criteria search with pagination and tag filtering - readSeriesById — Read series by ID list - searchSeries — Search series by name/creator

REST (1): - GET /series-share/{id} — Series social media share page

Purchase-Request-BPM (Event-Driven Only)

No HTTP endpoints for business logic. All interaction is via RabbitMQ messages: - Process start: InventoryItemPurchaseRequested - Process correlation: PurchasesFinalized, IssueRefunds - Task completion: PurchaseStateUpdated, TransactionCreated, UserEntitlementsCreated - Process stop: StopRecurringPayments

CIB Seven Admin UI: Available at /purchase-request-bpm for process monitoring.

Who Calls This Domain? Who Does It Call?

graph LR
    subgraph "Payment Domain"
        STR[Stripe Service]
        SUB[Subscriptions]
        BPM[Purchase-Request BPM]
    end

    subgraph "Callers"
        FE1[peeq-mono]
        FE2[frontends]
        STRIPE_API[Stripe Webhooks]
    end

    subgraph "Called Services"
        INV[Inventory]
        WAL[Wallet]
        EMAIL[Email]
        KC[Keycloak]
        TAGS[Tags]
        ORG[Org Manager]
    end

    FE1 -->|GraphQL| STR & SUB
    FE2 -->|GraphQL| STR & SUB
    STRIPE_API -->|Webhook| STR

    STR -->|GraphQL| INV
    STR -->|GraphQL| ORG
    STR -->|Admin API| KC

    SUB -->|GraphQL| INV
    SUB -->|Admin API| KC

    BPM -->|Admin API| KC
    BPM -.->|RabbitMQ| WAL
    BPM -.->|RabbitMQ| INV
    BPM -.->|RabbitMQ| EMAIL

    STR -.->|RabbitMQ| BPM
    SUB -.->|RabbitMQ| STR
    SUB -.->|RabbitMQ| INV

API Backward Compatibility Constraints

Data Model

Stripe Schema (13 Flyway migrations — 7 tables)

erDiagram
    exchange_rates {
        uuid id PK
        string name
        int price "Price in cents"
        int amount "Coins received"
        string[] keywords
        string meta_data
        boolean visible
        boolean active
        date created_on
        uuid created_by
        date deleted_on
    }
    discount_codes {
        uuid id PK
        string code UK
        string name
        int value
        boolean percentage
        int limit
        boolean active
        date expires_on
        string associated_coin_packages
        date created_on
    }
    discount_codes_usage {
        uuid id PK
        uuid fan_id
        uuid code_id FK
        date created_on
    }
    stripe_customers {
        uuid peeq_user_id PK
        string stripe_customer_id
        date created_on
    }
    checkout_purchase_products {
        uuid id PK
        uuid stripe_checkout_data_id
        string stripe_product_id
        uuid inventory_id
        int quantity
        int inventory_price
        date created_on
    }
    checkout_purchase_recipients {
        uuid id PK
        uuid stripe_checkout_data_id
        string first_name
        string last_name
        string email
        uuid keycloak_id
        string organization
        string phone_number
        string linked_in_address
        date created_on
    }
    promo_code_usage {
        uuid id PK
        uuid purchaser_id
        uuid inventory_item_id
        string promo_code
        date created_on
        date updated_on
    }
    one_off_subscriptions_to_cancel {
        uuid id PK
        string stripe_subscription_id
        boolean is_cancelled
        date cancelled_on
        date created_on
    }

    discount_codes ||--o{ discount_codes_usage : tracks

Subscriptions Schema (13 Flyway migrations — 4 tables)

erDiagram
    subscriptions {
        uuid id PK
        string name
        string description
        enum recurring_payment "DAILY|WEEKLY|MONTHLY|ANNUALLY|NONE"
        int auto_expires
        date created_on
        string created_by_id
        text inventory_info "JSON blob"
        uuid group_id
        int display_weight
        uuid paired_subscription_id
        int version "Optimistic lock"
        enum inventory_state "HIDDEN|AVAILABLE|NOT_AVAILABLE|COMPLETE|PRE_ORDER"
    }
    subscription_associated_tags {
        uuid id PK
        string tag_id
    }
    series {
        uuid id PK
        string name
        string description
        date created_on
        string created_by_id
        text inventory_info "JSON blob"
        uuid group_id
        int display_weight
    }
    series_published_notification_log {
        uuid publisher_id PK
        date publish_time
    }

    subscriptions ||--o{ subscription_associated_tags : tagged

Purchase-Request-BPM (No Custom Tables)

Uses CIB Seven engine tables (ACT_*) with process variables: - purchaseId (business key), fanUserId, itemId, itemType - price, quantity, isRefundable, autoExpires, recurringPayment - subscriptionEndDate, debitId, debitWasSuccessful

Flyway migrations V1–V6 handle CIB Seven schema upgrades (7.17 → 7.23).

Data Migration Specifics

In-Flight State

External Integrations

Integration Service Purpose Migration Impact
Stripe stripe Payment processing, webhooks, customer portal API key + webhook URL migration
Stripe Products stripe ← subscriptions Stripe product catalog sync Bidirectional via RabbitMQ
CIB Seven purchase-request-bpm Purchase fulfillment orchestration BPM engine version + running instances
Keycloak All three JWT validation + user info Standard JWT issuer config
GCS subscriptions Asset storage (thumbnails) Bucket access

Inter-Service Communication

Synchronous (REST/GraphQL)

From To Protocol Purpose
Stripe Inventory GraphQL Fetch membership discounts
Stripe Org Manager GraphQL Fetch user max membership discount
Stripe Keycloak Admin API User info
Subscriptions Inventory GraphQL Search inventory items
Subscriptions Keycloak Admin API User info
BPM Keycloak Admin API User display name for notifications

Asynchronous (RabbitMQ)

Publisher Message Consumer Purpose
Subscriptions SubscriptionCreated Stripe Create Stripe product
Subscriptions SubscriptionUpdated Stripe Update Stripe product
Subscriptions AddInventoryItem Inventory Register in catalog
Subscriptions UpdateInventoryItem Inventory Sync changes
Subscriptions SeriesCreated Notifications? Notify subscribers
Stripe StripeProductCreated Subscriptions, Inventory Link Stripe product ID
Stripe PaymentSucceeded BPM? Payment confirmation
Stripe PaymentFailed BPM? Payment failure
Stripe SubscriptionCanceled Subscriptions? Cancellation sync
Stripe (← external) JourneyCreated Stripe Create Stripe product for journey
Stripe (← external) SectionCreated Stripe Create Stripe product for course section
Stripe (← external) OfficeHourTimeSlotCreated Stripe Create Stripe product for office hours
BPM UpdatePurchaseState Inventory State transitions
BPM CreateTransaction Wallet Debit/credit wallet
BPM CreateUserEntitlements Inventory? Grant entitlements
BPM SendEmail Email Purchase notifications

Notable: Stripe service consumes 8 inbound message types from 4 different services (subscriptions, courses, journeys, office hours). It’s a message hub for product catalog sync.

Purchase Flow (End-to-End)

sequenceDiagram
    participant Fan
    participant FE as Frontend
    participant STR as Stripe Service
    participant STRIPE as Stripe API
    participant BPM as Purchase BPM
    participant WAL as Wallet
    participant INV as Inventory
    participant EMAIL as Email

    Fan->>FE: Click "Purchase"
    FE->>STR: getStripeCheckoutSession()
    STR->>STRIPE: Create Checkout Session
    STRIPE-->>STR: Session URL
    STR-->>FE: Redirect URL
    FE->>STRIPE: Fan completes payment

    STRIPE->>STR: Webhook: checkout.session.completed
    STR-->>BPM: InventoryItemPurchaseRequested (RabbitMQ)

    alt Subscription Item
        BPM->>INV: UpdatePurchaseState → PaidFor
        BPM->>INV: CreateUserEntitlements
        Note over BPM: Timer: wait for subscription end
        BPM->>INV: UpdatePurchaseState → PaidForAndFinalized
    else Regular Item (Wallet)
        BPM->>WAL: CreateTransaction (debit)
        WAL-->>BPM: TransactionCreated
        alt Sufficient Funds
            BPM->>INV: UpdatePurchaseState → PaidFor
            BPM->>EMAIL: SendEmail (charge notification)
            alt Refundable
                Note over BPM: Wait for PurchasesFinalized
                BPM->>INV: UpdatePurchaseState → PaidForAndFinalized
            else Non-Refundable
                BPM->>INV: UpdatePurchaseState → PaidForAndFinalized
            end
        else Insufficient Funds
            BPM->>INV: UpdatePurchaseState → InsufficientFunds
            BPM->>EMAIL: SendEmail (insufficient funds)
        end
    end

Gen 1 → Gen 2 Comparison

Aspect Gen 1 Gen 2 Gap
Payment gateway Stripe (assumed) Stripe SDK 27.1.0 Gen 2 adds checkout sessions, customer portal, invoice handling
Subscription model Basic Rich (pairing, entitlements, tags, display ordering, inventory state) Gen 2 significantly expanded
Purchase fulfillment Unknown CIB Seven BPM with full state machine Gen 2 adds formal workflow orchestration
Discount codes Unknown Full system (codes, usage tracking, promo codes) Gen 2 adds complete discount infrastructure
Exchange rates Unknown Coin-based virtual currency with admin CRUD Gen 2 adds in-app currency
BPM engine Camunda 7 CIB Seven 2.0 Fork migration completed

BPM Migration Risk Assessment

High Risk

  1. CIB Seven EOL — CIB Seven is a Camunda 7 fork. Camunda 7 CE ends October 2025. CIB Seven’s long-term support timeline is unclear. The purchase fulfillment process is critical — if the BPM engine becomes unsupported, this is a blocker.
  2. Running process instances — Active purchases in the BPM engine cannot be interrupted. Migration requires either completing all instances or migrating process state.

Medium Risk

  1. Keycloak identity plugin — CIB Seven uses a custom Keycloak plugin for identity. If CIB Seven is replaced, this plugin coupling is removed, but identity integration must be rebuilt.
  2. Timer events — Subscription end date timers in the BPM engine are persistent. Migrating these requires careful handling.

Low Risk

  1. Message-based integration — All BPM interaction is via RabbitMQ. The BPM engine is well-isolated from other services. Replacing it with a service-based saga would require reimplementing the state machine but not changing the message contracts.
  2. No custom JPA entities — BPM service has no custom database tables beyond CIB Seven’s engine tables. Data migration is limited to process state.

Modernization Implications

BPM Strategy Options

  1. Keep CIB Seven, upgrade — Lowest risk. CIB Seven 2.0 is current. Monitor EOL timeline. Replace Keycloak plugin when needed.
  2. Replace with saga pattern — Reimplement purchase flow as a service with state machine (no BPM engine). The flow is well-documented (~10 states, clear transitions). Removes BPM engine dependency entirely.
  3. Migrate to Camunda 8 — Move to Camunda’s cloud-native platform. Requires BPMN rework (Zeebe has different execution model). Higher effort, but vendor-supported.

Recommended: Option 2 (saga pattern) for Gen 3. The purchase flow is simple enough that a BPM engine is over-engineering. A state machine service with RabbitMQ integration would be simpler and have no external engine dependency.

Stripe Service Consolidation

The Stripe service handles three distinct concerns: 1. Payment processing (checkout, PaymentIntent, webhooks) 2. Virtual currency (exchange rates, discount codes, promo codes) 3. Stripe product catalog sync (message handlers)

Recommended: Keep as a single service in Gen 3. These concerns are tightly coupled through Stripe’s API. Splitting would create unnecessary inter-service communication.

Subscriptions Service

The subscriptions service is essentially a product catalog with rich metadata (entitlements, pairing, tags, display ordering). It has no direct Stripe SDK dependency — all Stripe interaction is via messages.

Recommended: Consider consolidating with inventory service in Gen 3. Subscriptions are a type of inventory item, and the two services already communicate heavily via RabbitMQ messages.

PCI Considerations (H9)


Part 2: Wallet + Transactions + Dwolla (Session 5)

Key Takeaways (Part 2)

  1. Wallet is a simple balance ledger — Integer-based “Peeq” virtual currency. No payment gateway integration. Credits come from admin GraphQL mutations; debits come from RabbitMQ messages (purchase-request-bpm). Pessimistic locking for concurrency.
  2. Transaction service is a payment reporting tool — Records payments made to celebrities/groups. Single table, no double-entry bookkeeping. Financial role required. Publishes TransactionPaidOut events.
  3. Dwolla is confirmed inactive (H2 → L2 Verified) — The Gen 2 wallet service has zero Dwolla references. peeq-dwolla is a Gen 1 service (Java 11/SB 2.6.8) last committed January 2023, not deployed to production. Dwolla was replaced by Stripe for all active payment flows.
  4. Wallet and Transaction are distinct services — Wallet handles fan coin balances (virtual currency). Transaction records celebrity/group payment disbursement records. They serve different audiences and different data flows.

Services Inventory (Part 2)

Service Gen Deployed? Stack Purpose
wallet 2 Yes (4/4 tenants) Java 21 / SB 3.5.4 Fan coin balance ledger
transaction 2 Yes (4/4 tenants) Java 21 / SB 3.5.4 Celebrity/group payment records
peeq-dwolla 1 No Java 11 / SB 2.6.8 Dwolla ACH payments (inactive)

Database Repos (Part 2)

Repo Migrations Tables Purpose
peeq-wallet-db 3 3 Balance tracking, transaction ledger
peeq-transaction-db 6 1 Celebrity/group payment records
peeq-dwolla-db 3 3 Vault secrets, webhook events (inactive)

API Surface (Part 2)

Wallet Service (1 GraphQL Mutation + 4 Queries)

GraphQL Mutations (1): - addToWallet — Admin-only: adds coins to user’s wallet

GraphQL Queries (4): - getBalance — Returns coin balance for current or specified user - getBalanceAllUsers — Admin-only: total platform coin balance - getWalletHistory — Paginated credit/debit transaction history - microservice — Service metadata

No REST endpoints. All interaction is GraphQL or RabbitMQ.

Transaction Service (6 Queries + 1 Mutation)

GraphQL Mutations (1): - markPayment — Financial role: records payment to celebrity/group, validates via inventory service

GraphQL Queries (6): - getPaidDetailsByCurrentCeleb — Celebrity’s own transaction summary - getPaidDetailsByCeleb — Financial role: any celebrity’s transactions - getPaidDetailsByGroup — Financial role: group transactions - getPaidItems — Deprecated: legacy query - findPaidItemsByCeleb — Financial role: paginated celebrity search - findPaidItemsByGroup — Financial role: paginated group search

No REST endpoints. Publish-only for RabbitMQ (no consumers).

peeq-dwolla (Gen 1 — Inactive)

GraphQL: Customer management, transfers, funding sources, W9 tax forms REST: Plaid bank linking (/plaid/link-token, /plaid/public-token, /plaid/processor-token), Dwolla webhooks Stack: Dwolla Kotlin SDK 0.1.2, Plaid Java SDK 13.0.0, vault-service 0.0.12, w9-service 0.0.8 Last commit: January 2023 (3 years inactive)

Who Calls Part 2 Services?

graph LR
    subgraph "Wallet & Transaction Domain"
        WAL[Wallet Service]
        TXN[Transaction Service]
    end

    subgraph "Callers"
        FE1[peeq-mono]
        FE2[frontends]
        BPM[Purchase-Request BPM]
    end

    subgraph "Called Services"
        INV[Inventory]
        SSE[SSE Service]
        KC[Keycloak]
    end

    FE1 -->|GraphQL| WAL & TXN
    FE2 -->|GraphQL| WAL & TXN
    BPM -.->|RabbitMQ: CreateTransaction| WAL

    WAL -.->|RabbitMQ: TransactionCreated| BPM
    WAL -.->|RabbitMQ: SendSseMessageToUser| SSE

    TXN -->|GraphQL| INV
    TXN -->|Admin API| KC
    TXN -.->|RabbitMQ: TransactionPaidOut| ???

Data Model (Part 2)

Wallet Schema (3 Flyway migrations — 3 tables)

erDiagram
    balance_transaction {
        uuid id PK
        string user_id
        int balance "Current coin balance"
    }
    create_transaction {
        uuid id PK
        string user_id
        string item_id
        boolean is_debit
        int amount
        string item_type
        string description
        string meta_data
        date created_on
    }
    create_transaction_related_transactions {
        uuid create_transaction_id FK
        string related_transaction_id
    }

    balance_transaction ||--o{ create_transaction : "tracks"
    create_transaction ||--o{ create_transaction_related_transactions : "links"

Transaction Schema (6 Flyway migrations — 1 table)

erDiagram
    transactions {
        uuid id PK
        uuid user_id "Celebrity/talent (nullable)"
        uuid group_id "Group (nullable)"
        string display_name
        text purchases "JSON array of InventoryInfo"
        int items_number
        int price "Total price"
        int quantity "Total quantity"
        uuid created_by_id
        date created_on
        string comment
    }

peeq-dwolla Schema (3 Flyway migrations — 3 tables, inactive)

erDiagram
    vault_namespaces {
        string namespace_id PK
        text public_key
        text private_key
        int version
        date created_on
    }
    vault_secrets {
        uuid secret_id PK
        string namespace_id FK
        string key
        text value "Encrypted"
        date created_on
    }
    dwolla_webhook_events {
        string webhook_event_id PK
        string resource_id
        string topic
        date created
        text body
        string status
    }

    vault_namespaces ||--o{ vault_secrets : contains

Data Migration Specifics (Part 2)

In-Flight State (Part 2)

External Integrations (Part 2)

Integration Service Purpose Migration Impact
Keycloak Both JWT validation Standard config
Inventory Transaction Purchase validation GraphQL dependency
SSE Wallet Real-time balance notifications RabbitMQ dependency
Dwolla peeq-dwolla (inactive) ACH bank transfers Retire — not in production
Plaid peeq-dwolla (inactive) Bank account linking Retire — not in production

Inter-Service Communication (Part 2)

Synchronous

From To Protocol Purpose
Transaction Inventory GraphQL Validate purchase IDs, fetch pricing
Transaction Keycloak Admin API User display names

Asynchronous (RabbitMQ)

Publisher Message Consumer Purpose
Purchase-Request-BPM CreateTransaction Wallet Debit/credit fan wallet
Wallet TransactionCreated Purchase-Request-BPM Confirm transaction (with hasFunds flag)
Wallet SendSseMessageToUser SSE Real-time balance update notification
Transaction TransactionPaidOut Unknown Notify of celebrity/group payment

Dwolla Status (H2 Evidence)

Dwolla is confirmed inactive and should be retired.

Evidence chain: 1. ArgoCD: No peeq-dwolla app in any of 4 production tenants (Session 0) 2. Gen 2 wallet service: Zero Dwolla references in source code, pom.xml, or config (Session 5) 3. peeq-dwolla: Gen 1 service (Java 11/SB 2.6.8), last commit January 2023 (3 years ago) 4. peeq-dwolla-db: Last commit September 2022 (3+ years ago) 5. Stripe replacement: All active payment flows use Stripe (Session 4). Stripe handles checkout, subscriptions, invoices, refunds. 6. Frontend dead code: DwollaService exists in frontends repo (admin-fe) but calls a backend not deployed to production. 7. Celebrity message: Celebrity service publishes CreateDwollaAccount — dead code, no consumer in production.

H2 promoted to L2 (Verified): Dwolla integration is confirmed inactive. The wallet service is a pure coin ledger with no payment gateway.

Recommendation: Archive peeq-dwolla, peeq-dwolla-db. Remove DwollaService from frontends. Remove CreateDwollaAccount message from celebrity service.

Wallet Model Analysis

Currency Model

Flow: Fan Purchases Content

  1. Fan buys coins via Stripe checkout → coins added to wallet (admin addToWallet)
  2. Fan spends coins on content/events → BPM sends CreateTransaction (debit) to wallet
  3. Wallet checks balance, deducts if sufficient, publishes TransactionCreated
  4. BPM receives confirmation, proceeds with fulfillment or handles insufficient funds

Flow: Celebrity Gets Paid

  1. Financial admin reviews earnings in transaction service
  2. Admin calls markPayment → validates purchases via inventory service
  3. Transaction record created with aggregated purchase data (JSON)
  4. TransactionPaidOut published (downstream handling unclear — may trigger external payout)

Limitation: No Double-Entry Bookkeeping

The transaction service is a simple payment log, not an accounting ledger. No debit/credit pairs, no reconciliation, no audit trail for individual line items. If Gen 3 needs proper financial reporting, this must be redesigned.

Modernization Implications (Part 2)

Wallet + Transaction Consolidation

Wallet and transaction serve different purposes: - Wallet: Fan-facing coin balance (virtual currency) - Transaction: Admin-facing payment records (celebrity disbursements)

Recommended: Keep separate in Gen 3. Different audiences, different access patterns, different data models. Consolidation would mix concerns.

Virtual Currency Simplification

The coin-based virtual currency (exchange rates → wallet → debit) adds complexity. Consider: - Keep coins: If the business needs an intermediary currency for pricing flexibility - Remove coins, charge directly: If all purchases can be priced in USD and charged via Stripe directly

This is a business decision, not a technical one.

Dwolla Retirement

Recommended: Archive all Dwolla-related repos and code: - Archive: peeq-dwolla, peeq-dwolla-db - Remove: DwollaService from frontends, CreateDwollaAccount from celebrity service - Remove: dwolla API gateway from frontend configs

Transaction Service Redesign

If Gen 3 needs proper financial reporting: - Add double-entry bookkeeping (debit/credit pairs) - Add ledger reconciliation - Add proper audit trail - Consider integration with accounting SaaS (QuickBooks, Xero)


Last updated: 2026-01-30 — Sessions 4-5 Review by: 2026-07-30 Staleness risk: Medium — Stripe API versions, CIB Seven support status, and subscription/wallet models evolve