Architecture Deployment API

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

Stripe is live. StripeService (Stripe.net + Polly retries) is wired into CreateOrderCommandHandler and handles payment intents, webhook signature verification, refunds, and reconciliation. StubStripeService is only a dev fallback used when Stripe:SecretKey is not configured; production sets Stripe:SecretKey and Stripe:WebhookSecret.

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