Customer Persona Flows
1. Browse Bouquets Nearby / QR Scan
sequenceDiagram
participant App as Mobile App
participant API as REST API
participant Cache as Redis Cache
participant DB as PostgreSQL
participant GEO as Nominatim
App->>API: GET /api/v1/bouquets/nearby?lat=&lng=&radius=
API->>Cache: GetAsync(BouquetsNearby:lat:lng:radius:page)
alt cache hit
Cache-->>API: Cached result
else cache miss
API->>DB: Geo query — bouquets within radius, status=Available
DB-->>API: Bouquet list with vendor + vase location
API->>Cache: SetAsync(result, ttl=5min)
end
API-->>App: 200 [{bouquetId, name, price, distance, vendorName, photoUrl}]
App->>API: GET /api/v1/bouquets/{id}/detail
API->>DB: Bouquet + vendor + vase details
API-->>App: 200 {detail, feedbackSummary, vendorProfile}
Note over App,GEO: QR Scan path
App->>API: GET /api/v1/bouquets/qr/{code}
API->>DB: Resolve bouquet by QR code
API-->>App: 200 Bouquet detail (same as above)
2. Cart Management
sequenceDiagram
participant App as Mobile App
participant API as REST API
participant DB as PostgreSQL
App->>API: GET /api/v1/cart/me
API->>DB: Cart for customerId
API-->>App: 200 {items, subtotal, vendorId}
App->>API: POST /api/v1/cart/items {bouquetId, quantity}
API->>DB: Load cart + bouquet
alt different vendor
API-->>App: 409 vendor-mismatch
else same vendor or empty cart
API->>DB: Add CartItem, update subtotal
API-->>App: 200 Updated cart
end
App->>API: DELETE /api/v1/cart/items/{itemId}
API->>DB: Remove CartItem
API-->>App: 200 Updated cart
App->>API: PATCH /api/v1/cart/items/{itemId} {quantity}
API->>DB: Update CartItem quantity
API-->>App: 200 Updated cart
3. Order + Payment
sequenceDiagram
participant App as Mobile App
participant API as REST API
participant DB as PostgreSQL
participant Stripe
participant Bus as InMemoryEventBus
participant Hub as BouquetHub (SignalR)
App->>API: POST /api/v1/orders/preview
API->>DB: Check bouquet prices + availability
API-->>App: 200 {lines, subtotal, deliveryAddress}
App->>API: POST /api/v1/orders {deliveryAddress}
API->>DB: Create Order, set status=PendingPayment
API->>DB: Reserve bouquets (status=Reserved, reservedUntil=+15min)
API-->>App: 201 {orderId, clientSecret}
App->>Stripe: Confirm payment intent (clientSecret)
Stripe-->>API: POST /api/stripe/webhook (payment_intent.succeeded)
API->>DB: Order status → Confirmed, bouquets → Sold
API->>Bus: Publish OrderPaidEvent
Bus->>Hub: SendAsync("OrderStatusChanged", orderId, status)
Hub-->>App: Real-time: order confirmed
note over DB,Stripe: Error: payment declined → reservation expires after 15min
note over DB,Stripe: Error: price changed between preview and create → 409 price-conflict
4. Delivery Confirmation + Feedback
sequenceDiagram
participant App as Mobile App
participant API as REST API
participant DB as PostgreSQL
participant Bus as InMemoryEventBus
App->>API: POST /api/v1/orders/{id}/confirm-delivery
API->>DB: Order status → Delivered
API->>Bus: Publish OrderDeliveredEvent
API-->>App: 200 OK
App->>API: GET /api/v1/orders/me/pending-feedback
API->>DB: OrderLines where FeedbackSubmitted=false, order=Delivered
API-->>App: 200 [{orderId, bouquetId, bouquetName}]
App->>API: POST /api/v1/orders/{id}/lines/{bouquetId}/feedback {rating, comment, isAnonymous}
API->>DB: Create BouquetFeedback, mark OrderLine.FeedbackSubmitted=true
API->>Bus: Publish BouquetFeedbackSubmittedEvent
API-->>App: 201 Created