System architecture diagrams plus native scanner view
CroaCard and the people & external systems it interacts with.
Major deployable units, services, and data stores within the CroaCard platform.
Detailed breakdown of backend services, routes, and their relationships.
Camera control, scan processing, callable functions, and Apple Wallet update path.
Key end-to-end flows through the system.
PassWizard (template, colours, assets, rewards)/api/customer-pass/createcustomerPassCoreService orchestrates creationpassGenerationService loads template, substitutes fields, processes assets, signs .pkpass.pkpass to Firebase StoragecustomerPass doc + giftCardLedger issuance entry (if gift card) — both succeed or neither doesamountMinor, currency, ledgerVersion, stripePaymentIntentId (when Stripe-backed)ScannerTab, camera reads QRcheckPassType (callable) returns reward mode and payment/amount requirementsTransactionAmountModal; if coupon requires payment: show PaymentModalAdapter; manual entry fallback allowedupdateVisit (callable) Firestore transaction: increment visits/points/stampsgiftCardLedger entry with amountMinor, currency, ledgerVersionevaluateRetentionForPassonCustomerPassUpdated trigger sends APNs silent push (background).pkpass via webServiceURLonPassScanned trigger logs scan eventScannerTabcheckPassType (callable) returns reward mode + requiresPayment: truePaymentModalAdapter shown for card payment/v1/transactions/prepare-payment validates coupon via couponValidator, calculates discount (percentage / fixed / freeItem)/v1/transactions/create-payment-intent creates Stripe Terminal PaymentIntent via Connect account with TransactionMetadata (merchantId, couponSerial, customerPassId)/v1/transactions/terminal-connection-token returns Stripe Terminal connection token + locationpayment_intent.succeeded webhook (or client fallback) triggers /v1/transactions/finalize-coupon-redemptionredeemCoupon marks coupon as redeemed in Firestoretransactions collectioncreatePayment callable with paymentType: 'giftcard' + customerPassIdstripeService.createPaymentIntent writes Stripe metadata: croacard_merchant_id, croacard_customer_pass_id, croacard_payment_typegiftCardPayments/{paymentIntentId} mapping doc (immutable, server-only)clientSecret for Stripe Elements payment flowcharge.refunded webhook (may contain multiple partial refunds)stripeWebhook reads croacard_customer_pass_id from charge metadatacharge.refunds.data, skips already-processed by stripeRefundIdgiftCardPayments mapping for amountPaid capsumRefundedSoFar from ledger; caps each refund so total ≤ paidoriginalBalanceSettingsTabcreateCheckoutSession (callable) returns Stripe Checkout URLsubscriptionWebhook receives events:checkout.session.completed writes subscription to Firestorecustomer.subscription.updated syncs statusinvoice.payment_succeeded updates lastPaymentDateinvoice.payment_failed flags merchantrunScheduledRetention fires every 24hevaluateRetentionForPass: builds signals, matches triggersretentionEvent to FirestorerunNoopDispatcher loads events + historydecideRetentionActions (pure policy) returns throttled decisionsDistributeTabprocessCsvUpload (callable) parses CSV rowscustomerPassCoreService to generate passAnalyticsTab queries Firestore collectionsAnalyticsService provides enhanced insightsreconcileGiftCardBalances fires every 24h (scheduled)customerPassesgiftCardLedger entriesexpectedBalance = totalCredits - totalDebits (pure ledger-driven)currentBalance on the pass documentreconciliationAlerts doc with delta and pass details/reports/gift-cards in the SPAexportGiftCardReport (callable)merchantId derived from auth.uid — client input ignoredamountMinor) + currency column + formatted convenience columnexports/{merchantId}/Where each component runs in production.
| Component | Platform | Region | Runtime |
|---|---|---|---|
| Web App (+ Reports UI) | Firebase Hosting | Global CDN | Static (Vite) |
| API + Callables + Triggers | Cloud Functions (2nd Gen) | europe-west2 | Node.js 20 |
| exportGiftCardReport | Cloud Functions (2nd Gen) — callable | europe-west2 | Node.js 20, 512 MiB |
| createPayment | Cloud Functions (2nd Gen) — callable | europe-west2 | Node.js 20 |
| reconcileGiftCardBalances | Cloud Functions (1st Gen) — scheduled 24h | europe-west2 | Node.js 20 |
| Scheduled Functions | Cloud Functions (1st Gen) | europe-west2 | Node.js 20 |
| Firestore | Cloud Firestore | europe-west2 | Managed |
↳ giftCardLedger | Subcollection of merchants | — | Immutable, server-write |
↳ giftCardPayments | Subcollection of merchants | — | Immutable, server-write |
↳ reconciliationAlerts | Subcollection of merchants | — | Server-write, merchant-read |
| Storage | Firebase Storage | Default bucket | Managed |
↳ exports/{merchantId}/ | CSV export files | — | Signed URLs, 1h expiry |
| Stripe Webhooks | Stripe to Cloud Functions | europe-west2 | HTTPS |
↳ charge.refunded | Stripe Connect webhook | — | Partial refund handler |