Payment Flows
Full Stripe Checkout Flow
sequenceDiagram
participant App as Mobile App
participant API as REST API
participant DB as PostgreSQL
participant Stripe
participant Outbox as EfOutboxWriter
participant Dispatcher as OutboxDispatcherService
participant Bus as InMemoryEventBus
participant Hub as BouquetHub (SignalR)
App->>API: POST /api/v1/orders {deliveryAddress}
API->>DB: Create Order (status=PendingPayment)
API->>DB: Reserve bouquets (status=Reserved, reservedUntil=now+15min)
API->>Stripe: StripeService.CreatePaymentIntentAsync(amount, currency)
Stripe-->>API: {paymentIntentId, clientSecret}
API->>DB: Order.StripePaymentIntentId = paymentIntentId
API-->>App: 201 {orderId, clientSecret}
App->>Stripe: confirmCardPayment(clientSecret)
Stripe-->>App: Payment result
alt Payment succeeded
Stripe->>API: POST /api/stripe/webhook (payment_intent.succeeded)
API->>API: Verify webhook signature (Stripe-Signature header)
API->>DB: Order status → Confirmed, bouquets → Sold
API->>Outbox: EfOutboxWriter.Write(OrderPaidEvent)
API->>DB: SaveChangesAsync (order + outbox in one transaction)
Dispatcher->>DB: Poll OutboxMessages (every 5s)
Dispatcher->>Bus: InMemoryEventBus.PublishAsync(OrderPaidEvent)
Bus->>Hub: BouquetHub.SendAsync("OrderStatusChanged", orderId, status)
Hub-->>App: Real-time notification
else Payment declined
Stripe->>API: POST /api/stripe/webhook (payment_intent.payment_failed)
API->>DB: Order status → PaymentFailed
API->>DB: Release bouquet reservations (status → Available)
API-->>App: Real-time: payment-failed
end
note over DB,Stripe: Reservation auto-expires after 15min via OrderReservationExpiryService
⚠️ Known gap (EP-04): When
Stripe:SecretKeyis not configured,StubStripeServiceis active — it returns synthetic payment intent IDs and no real charge occurs. Full Stripe integration requires configuration ofStripe:SecretKeyandStripe:WebhookSecretin production. End-to-end payment testing against the live Stripe sandbox is pending EP-04.
Order Cancellation
sequenceDiagram
participant App as Mobile App
participant API as REST API
participant DB as PostgreSQL
participant Stripe
App->>API: POST /api/v1/orders/{id}/cancel
API->>DB: Verify order status is PendingPayment or Confirmed (not Delivered)
API->>DB: Order status → Cancelled
API->>DB: Release bouquet reservations (status → Available)
alt Has Stripe payment intent
API->>Stripe: CancelPaymentIntent or Refund
end
API-->>App: 200 OK