ZAP Ecosystem & Database Server Architecture
Complete end-to-end topology mapping 4 client applications, API Gateway, 4 Spring microservices, explicit Database Server Cluster connections, and external payment gateways.
https://prod-api.zap.vn
- GCP Project:
zap-ecosystem-production - Cloud SQL DB:
zap-postgres-db-prod(PostgreSQL 15) - SSL Certificate:
zap-prod-api-cert(Managed SSL) - Target Clients: Merchant POS WPF, Kiosk & Branded Ordering Apps (Pendogo, Pho24)
https://uat-api.zap.vn
- GCP Project:
zap-ecosystem-sandbox-504003 - Cloud SQL DB:
zap-postgres-db-final(Sandbox DB) - SSL Certificate:
zap-uat-api-cert-v2(Managed SSL) - Target Clients: Internal TestFlight QA, Staging Build & Developer Sandbox
Microservice Domain Breakdown
Handles E.164 User Registration, BCrypt Auth, Hardware RSA-2048 Public Key Binding, JWT Refresh Token Rotation, and Merchant Tenant RBAC.
Atomic Redis Lua Cart Mutate, Product Catalog, Voucher Reservation, POS Order Checkout, and Real-Time Kitchen Order Display (KDS).
Payoo, VietQR Instant Bank Transfer & Stripe Webhook Callbacks, Redis Lua Idempotency Lock, and Transactional Outbox Event Dispatcher.
FCM Push Notification Dispatcher for Customer Apps and Telegram Ops Bot (@zap_ops_alert_bot) for instant merchant order & alert triggers.
ZAP System Security Architecture Specification
Frontend SSL/TLS Certificate Pinning, Hardware-bound RSA 2048-bit Digital Signatures, and OAuth2 JWT Bearer Security.
Enforces SSL/TLS Certificate Pinning on mobile/desktop client apps to defeat MITM packet sniffing, paired with Hardware KeyStore (iOS Secure Enclave / Android StrongBox) for non-repudiation digital signatures.
1. End-to-End Multi-Layer Security Execution Order
Enforced on all FE Client Apps (Flutter & C# WPF).
• Pins SHA-256 Public Key Hashes of API Gateway TLS cert.
• Rejects untrusted CA certificates.
• Completely blocks Charles Proxy, Fiddler, & Burp Suite MITM inspection.
Validates identity token on Authorization: Bearer <token>.
• Checks RSA/HMAC-256 JWT signature using public key.
• Verifies token expiration time (exp claim).
• Extracts userId, roles, and merchantId into Spring SecurityContext.
Validates non-repudiation hardware signature.
• Fetches user's RSA Public Key from Redis (user:public_key:{userId}).
• Verifies body SHA-256 digest (x-auth-checksum).
• Checks epoch timestamp drift (< 300s).
• Blocks Replay Attacks via x-auth-nonce.
2. Mandatory Security Headers Breakdown
| Header Name | Requirement | Description & Cryptographic Verification Purpose | Example Value |
|---|---|---|---|
Authorization |
Mandatory | OAuth2 Bearer JWT Access Token for session authentication. | Bearer eyJhbGciOiJSUzI1Ni... |
x-auth-signature |
Secured APIs | Base64-encoded RSA-SHA256 signature computed over canonical request payload using Secure Enclave. | Base64SignatureString== |
x-auth-timestamp |
Secured APIs | Unix epoch timestamp in milliseconds. Gateway rejects requests with |T_server - T_req| > 300000ms. |
1723205160000 |
x-auth-nonce |
Secured APIs | Cryptographic 128-bit random hex string. Cached in Redis for 5 mins to prevent replay of captured packets. | nonce_8f92a10b4c |
x-auth-checksum |
Secured APIs | SHA-256 hash digest of the raw HTTP request body bytes. Prevents man-in-the-middle body tampering. | e3b0c44298fc1c149afb... |
x-internal-call |
Internal Only | Header injected by API Gateway for microservice-to-microservice traffic. Bypasses L2 signature check. | true |
3. Canonical String Assembly & RSA Verification Formula
Both Client (Flutter / .NET C#) and Gateway construct the exact same canonical UTF-8 string before computing/verifying the signature:
// Step 1: Compute Request Body Hash
String bodyChecksum = SHA256_Hex(requestBodyBytes);
// Step 2: Assemble Canonical Message String
String canonicalPayload = String.format("%s|%s|%s|%s|%s",
httpMethod.toUpperCase(), // e.g. "POST"
requestURI.getPath(), // e.g. "/v1/orders/checkout"
timestampMs, // e.g. "1723205160000"
nonceHex, // e.g. "nonce_8f92a10b4c"
bodyChecksum // SHA-256 string
);
// Step 3: RSA 2048-bit Signature Verification
boolean isValid = Signature.getInstance("SHA256withRSA")
.initVerify(userPublicKeyFromRedis)
.update(canonicalPayload.getBytes(StandardCharsets.UTF_8))
.verify(Base64.getDecoder().decode(xAuthSignature));
4. Threat Model & Attack Mitigation Matrix
| Attack Vector | Impact Risk | ZAP Defense Mechanism |
|---|---|---|
| Packet Replay Attack | High (Duplicate Orders / Fraud Payment) | x-auth-nonce stored in Redis TTL key nonce:{nonce}. Duplicate nonces within 5 mins are immediately rejected with HTTP 403 REQUEST_TIMESTAMP_EXPIRED. |
| Man-In-The-Middle Body Tampering | Critical (Price/Quantity Manipulation) | x-auth-checksum SHA-256 digest is signed by Hardware RSA Key. Any byte alteration in transit invalidates the digital signature. |
| Device Malware / Jailbreak Key Theft | Critical (Stolen User Identity) | RSA Private Keys generated in iOS Secure Enclave / Android StrongBox KeyStore are non-exportable and hardware-enforced. Malware cannot copy private keys. |
| OTP SMS Brute-force Attack | Medium (Account Takeover) | Redis sliding window rate limiter key otp:rate:{phone} limits SMS requests to max 1 per 60s and 5 per hour. Triggering returns HTTP 429 OTP_RATE_LIMIT_EXCEEDED. |
Redis Lua High-Concurrency Scripting Engine
Single-threaded atomic operations executing directly in Redis memory for zero DB lock contention, instant cart mutations, rate limiting, and idempotency locks.
Executing Lua scripts inside Redis server memory guarantees complete atomicity: no other command can run concurrently while a script executes. This eliminates database row locks, deadlocks during flash-sale concurrency spikes, and saves 3-4 TCP network round-trips (RTT).
1. Primary Production Lua Scripts Architecture
Target Key: cart:user:{userId}
• Atomically increments/decrements item quantities.
• Automatically deletes items if qty ≤ 0.
• Refreshes TTL to 24 hours (86,400s) on every modification.
Target Key: otp:rate:{phone}
• Uses Redis Sorted Set (ZSET) with epoch timestamps.
• Evicts timestamps older than sliding window.
• Returns HTTP 429 if request count exceeds threshold.
Target Key: payment:lock:{txnId}
• Verifies lock ownership UUID before deleting.
• Prevents thread A from releasing thread B's expired lock.
• Guarantees strict single-charge payment processing.
2. Production Script 1: Atomic Cart Quantity Mutation (`cart_mutate.lua`)
Executes directly on key cart:user:{userId} in Redis memory when users tap "+" or "-" in `flutter_order_app`:
-- Redis Lua Script: cart_mutate.lua
-- KEYS[1]: Cart Redis Hash Key (e.g., "cart:user:usr_99812")
-- ARGV[1]: Product Variant ID (e.g., "prd_milktea_large")
-- ARGV[2]: Delta Quantity (+1 or -1)
-- ARGV[3]: Selected Options JSON String
local cartKey = KEYS[1]
local productId = ARGV[1]
local delta = tonumber(ARGV[2])
local currentQty = redis.call('HGET', cartKey, productId)
local newQty = 0
if currentQty then
newQty = tonumber(currentQty) + delta
else
newQty = delta
end
if newQty <= 0 then
-- Remove product item from cart Hash
redis.call('HDEL', cartKey, productId)
else
-- Update item quantity atomically
redis.call('HSET', cartKey, productId, newQty)
end
-- Refresh 24-hour expiration TTL
redis.call('EXPIRE', cartKey, 86400)
-- Return complete updated cart payload
return redis.call('HGETALL', cartKey)
3. Production Script 2: Sliding Window Rate Limiter (`rate_limiter.lua`)
Protects SMS OTP Gateway from brute-force attacks and abuse on key otp:rate:{phone}:
-- Redis Lua Script: rate_limiter.lua
-- KEYS[1]: Rate limit ZSET key (e.g., "otp:rate:+84901234567")
-- ARGV[1]: Current Unix Timestamp (milliseconds)
-- ARGV[2]: Window Size (milliseconds, e.g., 60000 for 1 minute)
-- ARGV[3]: Max Requests Allowed (e.g., 1 request per min)
local rateKey = KEYS[1]
local now = tonumber(ARGV[1])
local window = tonumber(ARGV[2])
local maxLimit = tonumber(ARGV[3])
local clearBefore = now - window
-- 1. Remove expired timestamps outside the current window
redis.call('ZREMRANGEBYSCORE', rateKey, 0, clearBefore)
-- 2. Count remaining requests inside the sliding window
local currentRequests = redis.call('ZCARD', rateKey)
if currentRequests < maxLimit then
-- 3. Add current timestamp to ZSET
redis.call('ZADD', rateKey, now, now)
redis.call('EXPIRE', rateKey, math.ceil(window / 1000))
return 1 -- ALLOWED
else
return 0 -- REJECTED (HTTP 429 Rate Limit Exceeded)
end
4. Production Script 3: Safe Idempotency Lock Release (`release_lock.lua`)
Ensures a worker thread only releases a payment transaction lock if it still owns the lock UUID:
-- Redis Lua Script: release_lock.lua
-- KEYS[1]: Lock key (e.g., "payment:lock:txn_778129")
-- ARGV[1]: Unique Lock UUID generated by acquiring thread
if redis.call('GET', KEYS[1]) == ARGV[1] then
-- Only delete if lock value matches worker thread UUID
return redis.call('DEL', KEYS[1])
else
-- Lock has expired or been acquired by another thread
return 0
end
Event-Driven Pub/Sub Topic Catalog
Asynchronous microservice event streams dispatched via RabbitMQ / Apache Kafka Event Bus.
Publishers (e.g. commerce-service) publish domain events to MQ exchanges without waiting for consumers. Subscribers consume events asynchronously for zero-latency checkout.
| Event Topic Name | Publisher Microservice | Subscriber Microservices | Payload Event DTO & Triggers |
|---|---|---|---|
order.created.event |
commerce-service | notification-service, kiosk-terminal |
Dispatched when customer places order. Triggers Kitchen Kiosk push notification & customer receipt SMS. |
payment.success.event |
payment-service | commerce-service, notification-service |
Dispatched on Payoo / VietQR IPN callback. Updates Order status to PAID and exports PDF receipt to GCS. |
user.registered.event |
identity-service | notification-service, wallet-service |
Dispatched on customer OTP signup. Initializes loyalty wallet and sends SMS welcome voucher. |
PostgreSQL ERD & Table Schema Reference
Complete schema reference scanned directly from JPA Entities across 4 Cloud SQL PostgreSQL databases (100+ tables total).
Each microservice exclusively owns its database. Cross-domain data flow is performed via REST endpoints or RabbitMQ transactional outbox events — zero cross-database SQL joins.
🛒 commerce_db (Cloud SQL PostgreSQL 15 — 60+ Tables)
Owned by commerce-service. Powers POS terminals, Omnichannel store management, Product Catalog, Inventory, Fees, and Manager Analytics.
1. Manager Daily Aggregation Reports (Analytics & Audit)
| Table Name | Entity Class | Description |
|---|---|---|
manager_daily_sales_reports | ManagerDailySalesReportEntity | Daily revenue, net sales, tax total, discount totals per store branch |
manager_daily_item_reports | ManagerDailyItemReportEntity | Daily itemized sales count, revenue per SKU / variant |
manager_daily_category_reports | ManagerDailyCategoryReportEntity | Daily breakdown by category (Drinks, Food, Combo, Add-ons) |
manager_daily_payment_reports | ManagerDailyPaymentReportEntity | Daily breakdown by payment method (Payoo, VietQR, Cash, MoMo, Stripe) |
manager_daily_discount_reports | ManagerDailyDiscountReportEntity | Voucher & promotion redemption statistics and burn rate |
manager_daily_customer_reports | ManagerDailyCustomerReportEntity | Customer traffic, average order value (AOV), new vs returning buyers |
manager_daily_dining_option_reports | ManagerDailyDiningOptionReportEntity | Dine-in vs Takeaway vs Delivery fulfillment breakdown |
manager_daily_section_reports | ManagerDailySectionReportEntity | Floor plan section / zone performance aggregation |
manager_daily_location_reports | ManagerDailyLocationReportEntity | Geographic region & store location comparison matrix |
manager_daily_report_status | ManagerDailyReportStatusEntity | ETL aggregation batch status and locked reporting dates |
2. Product Catalog, SKUs & Variants
| Table Name | Entity Class | Description & Key Columns |
|---|---|---|
products | ProductEntity | Core product catalog (id, store_id, category_id, sku, name, base_price, status) |
product_skus | ProductSkuEntity | Barcode & SKU stock mapping (id, product_id, sku_code, barcode, cost_price) |
variant_groups | VariantGroupEntity | Variant categories e.g., "Size", "Topping", "Ice level", "Sugar level" |
variants | VariantEntity | Specific options e.g., "Size L (+10K)", "Less Ice", "Extra Boba" |
variant_option_sets | VariantOptionSetTemplateEntity | Reusable variant template presets across categories |
categories | CategoryEntity | Tree structure with self-referential parent_id and URL slugs |
menu_collections | MenuCollectionEntity | Curated product groupings e.g., "Best Sellers", "Seasonal Drinks" |
3. Orders, Line Items & Delivery
| Table Name | Entity Class | Description & Key Columns |
|---|---|---|
orders | OrderEntity | Order header (order_code, customer_id, store_id, total_amount, status, payment_method) |
order_items | OrderItemEntity | Line items (order_id, product_id, variant_id, quantity, unit_price, line_total) |
order_payments | OrderPaymentEntity | Payment attempt snapshots & split-pay references |
order_delivery_addresses | OrderDeliveryAddressEntity | Customer shipping address, contact phone, delivery notes |
order_times | OrderTimeEntity | Timestamps for KDS kitchen workflow (created, prepped, ready, dispatched) |
dining_options | DiningOptionEntity | Fulfillment modes: DINE_IN, TAKEAWAY, DELIVERY |
sales_channels | SalesChannelEntity | Channels e.g. POS_APP, WEB_PORTAL, MOBILE_FLUTTER, GRABFOOD_API |
4. POS Terminals & Store Floorplan
| Table Name | Entity Class | Description |
|---|---|---|
stores | StoreEntity | Physical branch store location details, coordinates, status |
devices | DeviceEntity | Registered POS Android/Windows hardware terminal binding |
floorplans | FloorplanEntity | Restaurant layout grids per store branch |
floorplan_squares | FloorplanSquareEntity | Individual dining table coordinates, capacity & live seating status |
location_business_hours | LocationBusinessHourEntity | Store opening/closing schedules & holiday overrides |
5. Inventory, Warehouse, Stock Documents & BOM Recipes
| Table Name | Entity Class | Description |
|---|---|---|
inventory_documents | InventoryDocumentEntity | Stock entry/exit/transfer documents (id, doc_code, doc_type, warehouse_id) |
inventory_document_items | InventoryDocumentItemEntity | Line items per inventory document with unit cost & quantity |
inventory_counts | InventoryCountEntity | Physical stock count audits & variance reconciliation |
inventory_movements | InventoryMovementEntity | Granular stock ledger audit log for every transaction increment/decrement |
inventory_reservations | InventoryReservationEntity | Temporary stock reservation held while customer is in checkout flow |
product_boms | ProductBomEntity | Bill of Materials (BOM) recipe composition per product/variant |
units | UnitEntity | Units of measurement e.g., kg, gram, ml, cup, piece |
suppliers | SupplierEntity | Vendor & raw material supplier master data |
supplier_sku_prices | SupplierSkuPriceEntity | Contracted supplier procurement prices per raw ingredient SKU |
geo_regions | GeoRegionEntity | Regional warehouse & store distribution zones |
customer_groups | CustomerGroupEntity | CRM customer segmentation e.g. VIP, Wholesale, Regular |
6. Price Lists & Multi-Tier Pricing Matrix
| Table Name | Entity Class | Description |
|---|---|---|
price_lists | PriceListEntity | Price books e.g. "Dine-in Menu", "GrabFood Menu", "Happy Hour Pricing" |
price_list_skus | PriceListSkuEntity | SKU-specific prices override per price list |
price_list_times | PriceListTimeEntity | Time-window schedule when price list is active (e.g. 14:00 - 17:00) |
7. Modifiers, Toppings, Combos & BOGO Deals
| Table Name | Entity Class | Description |
|---|---|---|
modifier_groups | ModifierGroupEntity | Topping groups e.g. "Choose 2 Toppings", "Crust Choice" |
modifiers | ModifierEntity | Individual topping choices e.g. "Boba (+5k)", "Cheese Foam (+10k)" |
order_item_modifiers | OrderItemModifierEntity | Selected toppings stored on order line items |
combo_conditions | ComboConditionEntity | Combo rules e.g. "1 Main + 1 Side + 1 Drink" |
combo_item_conditions | ComboItemConditionEntity | Eligible products qualifying for combo conditions |
discount_bogo_buys | DiscountBogoBuyEntity | Buy-X-Get-Y "Buy" triggers |
discount_bogo_gets | DiscountBogoGetEntity | Buy-X-Get-Y "Get" rewards (free or discounted item) |
order_bogo_collections | OrderBogoCollectionEntity | BOGO deals applied on active order |
order_combo_collections | OrderComboCollectionEntity | Combo bundles applied on active order |
8. Loyalty Points Rules & Membership Tiers
| Table Name | Entity Class | Description |
|---|---|---|
membership_tiers | MembershipTierEntity | Customer tiers: Bronze, Silver, Gold, Platinum |
membership_benefits | MembershipBenefitEntity | Perks per tier e.g., 5% discount, free birthday drink |
earn_point_rules | EarnPointRuleEntity | Point accumulation rate e.g. 10,000 VND = 1 Point |
9. Marketing Banners, Favorites & Customer CRM Profiles
| Table Name | Entity Class | Description |
|---|---|---|
banners | BannerEntity | App homepage promotional carousel banners |
favorites | FavoriteEntity | Customer favorite products & bookmarked stores |
customer_profiles | CustomerProfileEntity | Commerce CRM profile, birthday, preferences & total lifetime spend |
10. Media Assets & Transactional Outbox
| Table Name | Entity Class | Description |
|---|---|---|
media_assets | MediaAssetEntity | GCS image metadata (file size, mime, CDN URL, resolution) |
media_folders | MediaFolderEntity | Folder hierarchy for merchant asset organization |
media_asset_usages | MediaAssetUsageEntity | Cross-reference tracking where image is used |
commerce_outbox_events | CommerceOutboxEventEntity | Transactional outbox event log for order & product changes |
payment_db (Cloud SQL PostgreSQL 15 — 10 Tables)
Owned by payment-service. Manages 8 payment gateway integrations, Prepaid Wallets, Loyalty Points, Refunds, and Webhook Audit logs.
| Table Name | Entity Class | Description |
|---|---|---|
transactions | TransactionEntity | Main financial transaction log (txn_code, amount, status, gateway, gateway_ref) |
order_payments | OrderPaymentEntity | Bridge table between commerce orders and payment transactions |
payment_methods | PaymentMethodEntity | Active payment gateways: PAYOO, VIETQR, MOMO, STRIPE, VNPAY, SMARTPAY, PAYPAL, CASH |
user_payment_methods | UserPaymentMethodEntity | Vaulted payment tokens e.g., saved credit card / tokenized e-wallet |
brand_sales_channel_payment_methods | BrandSalesChannelPaymentMethodEntity | Per-merchant payment gateway routing configuration |
payment_refunds | PaymentRefundEntity | Refund requests, gateway refund response codes, admin requester ID |
payment_webhook_events | PaymentWebhookEventEntity | Raw inbound IPN payload audit trail for security forensic investigation |
wallets | WalletEntity | Prepaid customer digital wallet balance & deposit history |
loyalty_history | LoyaltyHistoryEntity | Reward points earn/burn history per order |
payment_outbox_events | PaymentOutboxEventEntity | Transactional outbox event log for payment.success RabbitMQ events |
identity_db (Cloud SQL PostgreSQL 15 — 20 Tables)
Owned by identity-service. Handles user accounts, RBAC security permissions, RSA hardware keys, OAuth2 SSO, Brands & Multi-tenancy.
| Table Name | Entity Class | Description |
|---|---|---|
customers | CustomerEntity | End-user customer profiles, phone numbers, status |
customer_identities | CustomerIdentityEntity | Authentication identity records & hashed passwords |
customer_avatar_collections | CustomerAvatarCollectionEntity | Avatar gallery history |
customer_logging_whitelists | CustomerLoggingWhitelistEntity | Audit logging & debug tracing whitelist per user |
employees | EmployeeEntity | Staff & merchant employee master profiles |
employee_identities | EmployeeIdentityEntity | Staff login credentials & PIN numbers |
employee_roles | EmployeeRoleEntity | Staff store-level role assignments |
merchants | MerchantEntity | Merchant business profiles, tax ID (MST), license GCS URLs |
merchant_journeys | MerchantJourneyEntity | Merchant onboarding wizard step progress |
brands | BrandEntity | Multi-tenant brand enterprise entities |
brand_configs | BrandConfigEntity | Brand-level system parameters & feature toggles |
brand_themes | BrandThemeEntity | White-label mobile app color palettes & logos |
addresses | AddressEntity | Normalized street addresses & GPS coordinates |
address_types | AddressTypeEntity | Address categories: HOME, WORK, STORE, WAREHOUSE |
countries | CountryEntity | ISO country codes, phone prefixes & currencies |
roles | RoleEntity | System roles: ROLE_CUSTOMER, ROLE_MERCHANT, ROLE_STORE_MANAGER, ROLE_ADMIN |
permissions | PermissionEntity | Granular API permissions e.g., order:read, report:export |
role_permissions | RolePermissionEntity | RBAC mapping bridge between roles and permissions |
systems | SystemEntity | Registered client applications e.g. FLUTTER_APP, POS_DESKTOP |
system_configs | SystemConfigEntity | Global system-wide configuration key-value pairs |
notification_db (Cloud SQL PostgreSQL 15 — 4 Tables)
Owned by notification-service. Manages Firebase Push Tokens, Templates, Deliveries, and FCM Configs.
| Table Name | Entity Class | Description |
|---|---|---|
user_notification_tokens | UserNotificationTokenEntity | Active Firebase Cloud Messaging device push tokens per user & device |
notification_templates | NotificationTemplateEntity | Push notification HTML/text templates with dynamic placeholder variables |
notification_deliveries | NotificationDeliveryEntity | Dispatch log & delivery receipt status for FCM, SMS, Telegram |
notification_firebase_configs | FirebaseConfigEntity | FCM Service Account JSON keys & channel settings |
Interactive End-to-End Sequence Flows
Step-by-step messaging sequences across Client Apps, API Gateway, Microservices, Redis, RabbitMQ, and PostgreSQL.
Flow 1: 🔐 3-Tier Auth & Hardware RSA Key Pair Binding
| Step | Sender ➔ Receiver | Protocol / Tech | Action & Payload Description |
|---|---|---|---|
| 1 | Flutter App ➔ API Gateway | POST /api/v1/auth/login | Sends E.164 phone + password hash or OAuth ID token |
| 2 | API Gateway ➔ identity-service | gRPC / REST internal | Validates credentials, increments/resets login fail counter |
| 3 | identity-service ➔ identity_db | Spring Data JPA | Fetches BCrypt hash & active user roles from users table |
| 4 | Flutter App ➔ identity-service | POST /api/v1/auth/rsa/register | Sends client-generated RSA-2048 public key PEM + device_id |
| 5 | identity-service ➔ Redis | SET user:public_key:{userId} | Caches RSA public key with 24h TTL for Layer 2 signature verification |
| 6 | identity-service ➔ Flutter App | HTTP 200 OK + JWT | Returns Access Token (15 min) + Refresh Token (30 days) |
Flow 2: 🛒 Atomic Cart Mutate & Order Reservation
| Step | Sender ➔ Receiver | Protocol / Tech | Action & Payload Description |
|---|---|---|---|
| 1 | POS / Flutter App ➔ Redis | Redis Lua Script | Executes cart_mutate.lua for atomic item add/remove (0ms lock) |
| 2 | Customer ➔ API Gateway | POST /api/v1/orders/checkout | Passes Layer 2 RSA signature header X-Signature |
| 3 | API Gateway ➔ Redis | GET user:public_key:{userId} | Verifies RSA-SHA256 signature using cached public key |
| 4 | commerce-service ➔ commerce_db | Transactional JPA | Creates orders record status = PENDING & locks voucher |
| 5 | commerce-service ➔ RabbitMQ | order.created.event | Pushes event to zap.orders.exchange for inventory & notification |
Flow 3: 💳 Payment Webhook Callback & Outbox Event Dispatch
| Step | Sender ➔ Receiver | Protocol / Tech | Action & Payload Description |
|---|---|---|---|
| 1 | Payoo / VietQR Gateway ➔ API Gateway | POST /v1/payments/{gw}/ipn | Inbound IPN webhook callback with checksum header |
| 2 | payment-service ➔ Security Check | IP Whitelist + HMAC | Validates source IP address and verifies HMAC-SHA256 signature |
| 3 | payment-service ➔ Redis | ipn_lock.lua | Acquires Redis lock for payment:ipn:lock:{orderId} to prevent double-pay |
| 4 | payment-service ➔ payment_db | Transactional Outbox | Updates transactions status=SUCCESS & writes to payment_outbox_events |
| 5 | PaymentOutboxPublisher ➔ RabbitMQ | payment.success.event | Scheduled poller reads outbox table & publishes event to RabbitMQ bus |
| 6 | notification-service ➔ FCM / Telegram | FCM API & Telegram Bot | Sends instant FCM push notification to Customer & Telegram alert to Merchant |
Postman Collection & OpenAPI 3.0 Exporter
Export all 350 microservice endpoints to standard Postman Collection v2.1 or OpenAPI 3.0 JSON format for instant import into Postman, Insomnia, or Swagger UI.
Postman Collection v2.1
Contains all 350 ZAP microservice endpoints organized by service folders, pre-configured headers (JWT, X-Signature), request bodies, and cURL samples.
OpenAPI 3.0.0 Specification
Standard OpenAPI 3.0 spec format compatible with Swagger UI, Redoc, Stoplight, and Automated API SDK Code Generators.
Generated Export JSON Preview:
{
"info": {
"name": "ZAP Ecosystem Microservices API Catalog",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
},
"item": [
{ "name": "commerce-service (215 APIs)" },
{ "name": "identity-service (83 APIs)" },
{ "name": "payment-service (44 APIs)" },
{ "name": "notification-service (7 APIs)" },
{ "name": "api-gateway (1 API)" }
]
}
Environment Matrix & Live Service Health Monitor
Environment configurations (Production, Staging, Local) and simulated live health uptime monitors across all ZAP microservice nodes.
Microservice Health & Latency Monitor
PROD (prod-api.zap.vn)Microservice Git Commit Velocity & Frequency
Environment Deployment Parameters
| Environment | Base Gateway API Domain | GCP Project ID | Cloud SQL Instance | Managed SSL Certificate |
|---|---|---|---|---|
| PROD (Production) | https://prod-api.zap.vn |
zap-ecosystem-production |
zap-postgres-db-prod (:5432) |
zap-prod-api-cert |
| SANDBOX / UAT | https://uat-api.zap.vn |
zap-ecosystem-sandbox-504003 |
zap-postgres-db-final (:5432) |
zap-uat-api-cert-v2 |
Payment IPN & Webhook Signature Verification
Per-gateway HMAC-SHA256 checksum rules, IP whitelist enforcement, and Redis idempotency lock for Payoo, VietQR (NAPAS), MoMo, and Stripe callbacks.
Every inbound IPN callback must pass 3 sequential checks before mutating order state: (1) Source IP Whitelist validation → (2) HMAC-SHA256 / RSA Signature verification → (3) Redis Idempotency Lock to prevent double-charge processing.
1. Gateway IPN Comparison Table
| Payment Gateway | IPN Endpoint | Signature Algorithm | Signature Location | IP Whitelist Required |
|---|---|---|---|---|
| Payoo (NAPAS) | POST /v1/payments/payoo/ipn |
HMAC-SHA256 with shared secret | X-Payoo-Checksum header |
Yes — Payoo server IPs |
| VietQR (BIDV) | POST /v1/payments/vietqr/ipn |
HMAC-SHA256 with client secret | checksum field in JSON body |
Yes — BIDV/NAPAS IPs |
| MoMo Wallet | POST /v1/payments/momo/ipn |
HMAC-SHA256 with secret key | signature field in JSON body |
Yes — MoMo server IPs |
| Stripe | POST /v1/payments/stripe/webhook |
HMAC-SHA256 with webhook secret + timestamp tolerance | Stripe-Signature header |
No — Uses signed secret |
2. Payoo IPN — HMAC-SHA256 Verification
Canonical string: amount + "|" + orderId + "|" + responseCode + "|" + payooSecret
@PostMapping("/v1/payments/payoo/ipn")
public ResponseEntity<?> handlePayooIpn(
@RequestBody PayooIpnRequest req,
@RequestHeader("X-Payoo-Checksum") String headerChecksum,
HttpServletRequest http) {
// 1. IP Whitelist Check
String clientIp = http.getHeader("X-Forwarded-For");
if (!PAYOO_ALLOWED_IPS.contains(clientIp))
return ResponseEntity.status(403).build();
// 2. Reconstruct canonical string & compute HMAC-SHA256
String canonical = req.getAmount() + "|" + req.getOrderId()
+ "|" + req.getResponseCode() + "|" + payooSecret;
String computed = HmacUtils.hmacSha256Hex(payooSecret, canonical);
if (!computed.equalsIgnoreCase(headerChecksum))
return ResponseEntity.status(400).body("INVALID_CHECKSUM");
// 3. Redis Idempotency Lock — prevent double-processing
String lockKey = "payment:ipn:lock:" + req.getOrderId();
Boolean acquired = redisTemplate.opsForValue()
.setIfAbsent(lockKey, "processing", Duration.ofMinutes(10));
if (Boolean.FALSE.equals(acquired))
return ResponseEntity.ok("ALREADY_PROCESSED");
// 4. Process — update order status & publish payment.success.event
paymentService.handlePayooSuccess(req);
return ResponseEntity.ok("00"); // Payoo expects "00" = success
}
3. VietQR (BIDV/NAPAS) IPN — HMAC-SHA256 Verification
Canonical string: clientId + "|" + apiKey + "|" + amount + "|" + orderId
@PostMapping("/v1/payments/vietqr/ipn")
public ResponseEntity<?> handleVietQrIpn(@RequestBody VietQrIpnRequest req) {
// 1. Reconstruct canonical string
String canonical = vietQrClientId + "|" + vietQrApiKey
+ "|" + req.getAmount() + "|" + req.getOrderId();
String computed = HmacUtils.hmacSha256Hex(vietQrApiKey, canonical);
// 2. Compare with checksum in body
if (!computed.equals(req.getChecksum()))
return ResponseEntity.badRequest().body(Map.of("code", "01", "desc", "Checksum invalid"));
// 3. Idempotency lock + event dispatch
String lockKey = "payment:ipn:lock:" + req.getOrderId();
if (Boolean.FALSE.equals(
redisTemplate.opsForValue().setIfAbsent(lockKey, "1", Duration.ofMinutes(10))))
return ResponseEntity.ok(Map.of("code", "00", "desc", "Already processed"));
paymentService.handleVietQrSuccess(req);
return ResponseEntity.ok(Map.of("code", "00", "desc", "Success"));
}
4. MoMo Wallet IPN — HMAC-SHA256 Verification
Canonical string built from sorted query params: accessKey + amount + extraData + message + orderId + orderInfo + orderType + partnerCode + payType + requestId + responseTime + resultCode + transId
@PostMapping("/v1/payments/momo/ipn")
public ResponseEntity<?> handleMomoIpn(@RequestBody MomoIpnRequest req) {
// 1. Reconstruct raw signature string (MoMo-sorted fields)
String raw = "accessKey=" + momoAccessKey
+ "&amount=" + req.getAmount()
+ "&extraData=" + req.getExtraData()
+ "&message=" + req.getMessage()
+ "&orderId=" + req.getOrderId()
+ "&orderInfo=" + req.getOrderInfo()
+ "&orderType=" + req.getOrderType()
+ "&partnerCode=" + req.getPartnerCode()
+ "&payType=" + req.getPayType()
+ "&requestId=" + req.getRequestId()
+ "&responseTime=" + req.getResponseTime()
+ "&resultCode=" + req.getResultCode()
+ "&transId=" + req.getTransId();
// 2. Compute HMAC-SHA256 with secretKey
String computed = HmacUtils.hmacSha256Hex(momoSecretKey, raw);
if (!computed.equals(req.getSignature()))
return ResponseEntity.badRequest().body("Signature mismatch");
// 3. Only process successful transactions (resultCode == 0)
if (req.getResultCode() != 0)
return ResponseEntity.ok("Noted — non-success callback");
// 4. Idempotency + event
paymentService.handleMomoSuccess(req);
return ResponseEntity.ok("OK");
}
5. Stripe Webhook — Signed Payload + Timestamp Tolerance
Stripe uses Stripe-Signature header containing t=timestamp,v1=hmac. A 5-minute timestamp tolerance prevents replay attacks.
@PostMapping("/v1/payments/stripe/webhook")
public ResponseEntity<?> handleStripeWebhook(
@RequestBody String payload,
@RequestHeader("Stripe-Signature") String sigHeader) {
// Stripe SDK validates HMAC-SHA256 + timestamp tolerance (300s)
Event event;
try {
event = Webhook.constructEvent(payload, sigHeader, stripeWebhookSecret);
} catch (SignatureVerificationException e) {
return ResponseEntity.status(400).body("Invalid Stripe signature");
}
// Handle specific event types
switch (event.getType()) {
case "payment_intent.succeeded":
PaymentIntent intent = (PaymentIntent) event.getData().getObject();
paymentService.handleStripeSuccess(intent.getId(), intent.getAmount());
break;
case "payment_intent.payment_failed":
paymentService.handleStripeFailure(intent.getId());
break;
}
return ResponseEntity.ok("received");
}
6. Redis Idempotency Guard — Prevent Double-Charge
Applied on every IPN handler before mutating order state. Uses SET NX EX atomic semantics via Lua script:
-- Redis Lua Idempotency Guard: ipn_lock.lua
-- KEYS[1]: "payment:ipn:lock:{orderId}"
-- ARGV[1]: Current processor ID
-- ARGV[2]: TTL in seconds (600 = 10 minutes)
if redis.call('SET', KEYS[1], ARGV[1], 'NX', 'EX', ARGV[2]) then
return 1 -- Lock acquired: safe to process IPN
else
return 0 -- Lock already held: IPN already processed, skip
end
Client Distribution & Multi-Tenant App Publishing Architecture
Visual architecture model of ZAP-managed internal single-app operations suite vs multi-tenant white-label customer ordering apps uploaded per merchant brand (Pendogo, Pho24, etc.).
• Merchant Operations Suite (POS / Kiosk / CRM): Managed centrally by ZAP as 1 single application stack across UAT and PROD.
• Customer Ordering Apps: Built from a single white-label codebase, compiled per merchant brand (Pendogo, Pho24, Brand X), and published to Apple App Store & Google Play Store as independent branded apps.
1. Client App Publishing & Environment Distribution Model (Visual Diagram)
2. Client Application Distribution & Publishing Matrix
| Application Category | Ownership & Management | App Store Publishing Model | Target Environments / Brands | Build Flavor / Scheme |
|---|---|---|---|---|
|
Merchant Operations Suite POS WPF, Kiosk & Staff CRM |
Managed 100% by ZAP Core Team | 1 Single App under ZAP Developer Account |
UAT
PROD
|
--flavor zap_staff |
|
Customer Ordering Suite White-Label Ordering Apps |
White-label Multi-Tenant Architecture | Uploaded as Multiple Separate Branded Apps per Merchant Account |
UAT App
Pendogo App
Pho24 App
Brand X...
|
--flavor pendogo_prod--flavor pho24_prod |
3. Application Product & Platform Build Inventory Matrix
| Application Product | Target Platform | UAT Environment Build | Production Environment Build | Managed Apps Count |
|---|---|---|---|---|
|
1. ZAP Manager Merchant Staff CRM, Store Manager & Admin Web flutter_manager
|
Apple iOS | 1 App (Manager UAT — TestFlight) |
1 App (Manager Prod — App Store) |
2 iOS Apps |
| Google Android | 1 App (Manager UAT — Play Internal) |
1 App (Manager Prod — Play Console) |
2 Android Apps | |
| Web & Desktop Browser | 1 App (Manager UAT Web — uat-manager.zap.vn) |
1 App (Manager Prod Web — manager.zap.vn) |
2 Web/Desktop Apps | |
|
2. ZAP POS Desktop Windows WPF POS & Thermal Printer Driver pos_wpf (.NET C#)
|
Microsoft Windows | 1 App (POS Cashier UAT Installer) |
1 App (POS Cashier Prod Installer) |
2 Windows Apps |
|
3. ZAP POS Mobile Handheld Mobile POS & Sunmi Terminal flutter_pos_mobile
|
Apple iOS (iPad/iPhone) | 1 App (POS Mobile UAT — TestFlight) |
1 App (POS Mobile Prod — App Store) |
2 iOS Apps |
| Google Android (Sunmi POS) | 1 App (POS Mobile UAT — Play Internal) |
1 App (POS Mobile Prod — Sunmi/Play Store) |
2 Android Apps | |
|
4. ZAP Kiosk Self-Service Touchscreen Kiosk App flutter_kiosk
|
Google Android | 1 App (Kiosk Android UAT) |
1 App (Kiosk Android Prod) |
2 Android Apps |
| Microsoft Windows | 1 App (Kiosk Windows UAT) |
1 App (Kiosk Windows Prod) |
2 Windows Apps | |
|
5. ZAP Order App Multi-Tenant White-Label Branded App flutter_order_app
|
Apple iOS | 1 App (Central ZAP Order UAT App) |
N Separate Branded Apps (Pendogo, Pho24...) | N + 1 iOS Apps |
| Google Android | 1 App (Central ZAP Order UAT App) |
N Separate Branded Apps (Pendogo, Pho24...) | N + 1 Android Apps | |
| TOTAL ECOSYSTEM APPS TO MANAGE: | 10 UAT Sandbox Builds | 2N + 7 Production Builds | 2N + 17 Active Builds | |
4. Target OS & Mobile Store Publishing Pipeline (iOS, Android & Windows)
4. Multi-Platform Build & Publishing Specification Matrix
| Target OS Platform | Build Artifact | Signing & Security Cert | UAT Testing Channel | Production Publishing Store |
|---|---|---|---|---|
| Apple iOS | .ipa Package |
Apple Distribution Cert + Provisioning Profile | Apple TestFlight (Internal Group) | Apple App Store Connect |
| Google Android | .aab / .apk |
Android Keystore (upload-keystore.jks) |
Play Internal App Sharing / Firebase | Google Play Console |
| Windows Desktop | .exe / .msi |
EV Code Signing Certificate | Direct MSI / UAT Update Server | ZAP Auto-Updater Service |
Software Continuous Deployment (CD) Architecture Diagram
End-to-end visual workflow diagram for automated container build, GCP Artifact Registry storage, zero-downtime deployment, health probes, and automatic rollback.
Every pull request merged to uat automatically triggers Cloud Build & deploys to UAT (zap-ecosystem-sandbox-504003). Merges to main trigger a tagged release requiring 1-click manual approval before deploying to Production (zap-ecosystem-production).
1. Automated CD Deployment Pipeline Architecture (Visual Workflow Diagram)
Monorepo Workspace Structure & Branching Architecture
Accurate repository topology matching the real zap-system workspace layout: Java 17 backend monorepo, multi-platform frontend workspace, GCP infrastructure scripts, and Gitflow branching rules.
The ZAP ecosystem is structured as a unified monorepo workspace at root zap-system, combining backend microservices, multi-platform client apps, and GCP infrastructure IaC submodules.
1. Workspace Directory & Monorepo Architecture (Visual Model)
2. Gitflow Branching & Release Timeline Architecture
3. Conventional Commit Classifier Standard (Visual Cards)
New Feature Implementation
Bug Fix & Patch
Configuration & Dependencies
Documentation Updates
Code Refactoring & Clean Up
Automated Unit & Integration Tests
Resilience4j Circuit Breaker & Fallback Policies
API Gateway fault-tolerance configuration to protect upstream microservices from cascading failures.
When a microservice failure rate exceeds threshold, the Circuit Breaker transitions to OPEN state, immediately short-circuiting all downstream requests and returning a pre-configured fallback response — no thread pool exhaustion.
1. Circuit Breaker State Machine
All requests pass through normally to microservices.
• Monitors failure rate using sliding window.
• Transitions to OPEN if failure rate > 50%.
All requests are immediately rejected (HTTP 503).
• Returns fallback JSON response instantly.
• Waits waitDurationInOpenState (default 30s).
Allows limited test requests to probe microservice.
• Success → transitions back to CLOSED.
• Failure → returns to OPEN state.
2. Gateway Circuit Breaker Configuration (application.yml)
resilience4j:
circuitbreaker:
configs:
default:
slidingWindowSize: 10 # Evaluate last 10 requests
failureRateThreshold: 50 # Trip if 50%+ requests fail
waitDurationInOpenState: 30s # Stay OPEN for 30 seconds
permittedNumberOfCallsInHalfOpenState: 3
registerHealthIndicator: true
timelimiter:
configs:
default:
timeoutDuration: 5s # Abort upstream call after 5s
spring:
cloud:
gateway:
routes:
- id: identity-service-route
predicates:
- Path=/api/v1/auth/**
filters:
- name: CircuitBreaker
args:
name: identity-service-cb
fallbackUri: forward:/fallback/identity
3. Fallback Response Contract
// Circuit Breaker Fallback Controller
@RestController
public class FallbackController {
@GetMapping("/fallback/identity")
public Mono<ApiResponse<?>> identityFallback() {
return Mono.just(ApiResponse.error(5000,
"identity-service is temporarily unavailable. Please retry in 30 seconds."));
}
@GetMapping("/fallback/commerce")
public Mono<ApiResponse<?>> commerceFallback() {
return Mono.just(ApiResponse.error(5000,
"commerce-service is temporarily unavailable."));
}
}
Google Cloud Storage (GCS) Media Upload Flow
Signed URL pre-authentication upload pattern for product images, store banners, and PDF invoice receipts.
Clients upload media directly to GCS using a short-lived signed URL issued by commerce-service — API Gateway is not involved in the media data transfer, reducing backend bandwidth by 100%.
1. Upload Flow Sequence
Client calls POST /v1/media/upload-url on commerce-service with filename + MIME type.
Server returns signed GCS URL valid for 15 mins: gs://zap-media-assets/products/{uuid}
Client streams file bytes via HTTP PUT directly to signed GCS URL. Backend is not involved.
2. Bucket Folder Organization
| GCS Folder Path | Content Type | Access Control |
|---|---|---|
gs://zap-media-assets/products/ |
Product food images, variants photos (JPEG/WebP) | Public read via CDN endpoint |
gs://zap-media-assets/stores/logos/ |
Brand logo images and banner artwork (PNG) | Public read via CDN endpoint |
gs://zap-media-assets/invoices/ |
Customer order PDF receipt exports | Private — Signed URL access only |
3. Java Signed URL Generation (commerce-service)
// Generate 15-minute GCS Signed URL for direct client upload
public String generateSignedUploadUrl(String fileName, String contentType) {
BlobInfo blobInfo = BlobInfo.newBuilder(
BlobId.of("zap-media-assets", "products/" + UUID.randomUUID() + "_" + fileName)
).setContentType(contentType).build();
return storage.signUrl(
blobInfo,
15, TimeUnit.MINUTES,
Storage.SignUrlOption.withV4Signature(),
Storage.SignUrlOption.httpMethod(HttpMethod.PUT)
).toString();
}
API Endpoint Reference Catalog
Parsed directly from Java Controller source code declarations across all microservices.
Backend Design System & API Envelope Contract
Standardized response formats, global error codes, and Redis cache conventions.
Standardized Response Envelope (ApiResponse<T>)
{
"code": 1000,
"message": "Operation completed successfully",
"data": { ... },
"timestamp": "2026-08-09T18:49:00Z"
}
Global System & Payment Gateway Error Codes (153 Total Codes)
Redis Cache Naming Conventions
ZAP Platform — Development Roadmap
Structured roadmap with detailed task breakdown across UAT, PROD setup, mobile/desktop app configurations, backend microservices, and upcoming releases (May–August 2026).