Payment Process Overview

Consolidated notes on the foodpanda payment stack across the pd-microfrontend, pd-pablo-payment-gateway, pd-ops-portal-payments, and pd-ops-portal-wallets repositories. Paths cited inline reference the relevant implementation spots.

Repository Roles

  • pd-microfrontend: customer-facing web microfrontends, including the shared Payment React library and checkout integration (packages/libraries/payment/README.md:1, packages/checkout-microfrontend/services/place-order-service/place-order-service.ts:1).
  • pd-pablo-payment-gateway: Go services (REST + gRPC) orchestrating Alfred, wallet, customer, and vendor dependencies for purchases and direct payments (pkg/service/v1/payment.go:1).
  • pd-ops-portal-payments: Ops Portal plugin used by finops/admin teams to create and edit payment methods and toggles (src/components/payment-method-form/PaymentMethodForm.tsx:45).
  • pd-ops-portal-wallets: Ops tooling for cashback campaigns and wallet disbursements that also consumes payment- and wallet-admin APIs (src/global/api.ts:1).

Web Purchase Flow (Order Checkout)

sequenceDiagram
    autonumber
    participant Client as Web Client
    participant CheckoutMFE as Checkout MFE (`pd-microfrontend`)
    participant PaymentLib as Payment Library
    participant Pablo as Pablo REST (`pd-pablo-payment-gateway`)
    participant Alfred as Alfred
    participant PSP as PSP

    Client->>CheckoutMFE: Load /checkout
    CheckoutMFE->>PaymentLib: Initialise with appConfig & context
    PaymentLib->>Pablo: POST /api/v5/purchase/intent (create)
    Pablo->>Alfred: Create purchase intent
    Alfred-->>Pablo: Intent response
    Pablo-->>PaymentLib: Intent data (methods, balances)
    PaymentLib-->>CheckoutMFE: onPaymentsUpdate(payload)
    Client->>CheckoutMFE: Place Order
    CheckoutMFE->>Pablo: POST /api/v5/cart/checkout (payload incl. purchase_intent_id)
    Pablo->>Alfred: Confirm purchase intent
    Alfred->>PSP: Authorise / redirect
    PSP-->>Alfred: Redirect details
    Alfred-->>Pablo: Confirm response (incl. redirect URL)
    Pablo-->>CheckoutMFE: Checkout result (redirectUrl or success)
    CheckoutMFE-->>Client: Redirect to PSP/Bruce or success page

Detailed Steps

  1. Initialisation – Checkout MFE renders the shared <Payment> component, passing appConfig, context="Purchase", user data, and tokenisation callbacks (packages/libraries/payment/payment.tsx:1).
  2. Create Intent – For purchase context, the Payment library dispatches createIntent, which shapes a PurchaseIntentRequest and posts via purchaseIntentProvider (wrapping FD API POST /api/v5/purchase/intent) (packages/libraries/payment/services/create-purchase-intent/create-purchase-intent.ts:6, openapi.yaml:3012).
  3. Surface Methods – The returned intent is adapted into local models, default method chosen, and onPaymentsUpdate fired so the checkout flow knows the selected instrument and validation state (packages/libraries/payment/redux/thunks/create-intent/create-intent.ts:79).
  4. Adjust Intent – If totals or method change, updateIntent PUTs the new selection back to Pablo/Alfred, embedding selectedPaymentInstrumentId when available (packages/libraries/payment/redux/thunks/update-intent/update-intent.ts:67, packages/libraries/payment/services/update-purchase-intent/update-purchase-intent.ts:7).
  5. Submit Order – On confirm, the checkout service builds orderPayload containing payment.methods (from the library), the purchase_intent_id, and a redirect template URL before calling placeOrderProvider (FD POST /api/v5/cart/checkout) (packages/checkout-microfrontend/services/place-order-service/place-order-service.ts:72).
  6. Status Polling – While waiting for PSP callbacks, the UI polls Pablo’s GET /api/v5/payment/status via handlePaymentStatus, handling success, pending (with retry), and action_required (redirect) (packages/checkout-microfrontend/services/order-payment-service/handle-payment-status.ts:61, openapi.yaml:3408).
  7. Redirect Handling – PSP/Bruce redirects land on /payments/handle-payment, which sets cookies for checkout errors or forwards to order tracking on success (packages/payments-microfrontend/payments/handle-payment/router.tsx:8).

Key API Touchpoints

StepEndpointRepo EntryNotes
Create intentPOST /api/v5/purchase/intentopenapi.yaml:3012Query params include, optional fast-top-up.
Update intentPUT /api/v5/purchase/intent/{intentId}packages/libraries/payment/services/update-purchase-intent/update-purchase-intent.ts:7Adds paymentSessionDetails with selected instrument.
CheckoutPOST /api/v5/cart/checkoutpackages/checkout-microfrontend/services/place-order-service/place-order-service.ts:72Includes external_payment_url_template for fallback tracking.
Status pollGET /api/v5/payment/statuspackages/checkout-microfrontend/providers/place-order-provider/payment-status-provider.ts:5Requires purchaseId, platformReferenceId, attempt.

Direct Payment Contexts

Direct payments cover wallet top-ups, subscriptions, dine-in, PandaGo, donations, etc. (docs/payment-and-purchase.md:5).

sequenceDiagram
    autonumber
    participant Client as Channel (Web/App)
    participant Pablo as Pablo REST/gRPC
    participant Alfred as Alfred

    Client->>Pablo: POST /api/v5/payment/intent (optional)
    Pablo->>Alfred: Create Direct/Donation intent (per context)
    Alfred-->>Pablo: Intent details (session)
    Pablo-->>Client: Response (paymentSessionId, methods)
    Client->>Pablo gRPC: CustomerPay / MerchantPay
    Pablo->>Alfred: Confirm payment (TransactionInitiator CIT/MIT)
    Alfred-->>Pablo: Confirm result
    Pablo-->>Client: PayResponse (status, redirect)
    Alfred-->>Pablo: Callback updates (state changes)

Context Strategies

  • CustomerPay (CIT) – The gRPC service validates the payment strategy, fetches wallet auth, stores a payment.Entity, and confirms via Alfred, tagging the transaction initiator as customer (pkg/service/v1/payment_customerpay.go:43).
  • MerchantPay (MIT) – Used for subscription renewals with whitelisted payment methods; Pablo creates and confirms direct payments without an initial intent call (docs/payment-and-purchase.md:83).
  • Donation / Dine-in / PandaGo – Strategy factory decides whether to call Alfred purchase intents or dedicated APIs (docs/payment-and-purchase.md:20).

Direct Payment Intent Payload

POST /api/v5/payment/intent expects payment details (amount, filterPaymentMethods) plus paymentContext discriminator to resolve the correct backend strategy (openapi.yaml:3346).

Payment Library Internals

  • Props Contract<Payment> requires appConfig with FD API credentials, context, hosted credit-card props, tokenisation callbacks, transaction amount, and onPaymentsUpdate. Optional props include intentData (for non-purchase contexts) and purchaseIntentRequestBody (purchase context) (packages/libraries/payment/README.md:7).
  • Tokenisation Flow – The library coordinates host-bruce tokenisation via proceedPaymentStatus, onTokenizationStart, onTokenizationComplete, and onTokenizationFailure, ensuring encrypted payloads are present before confirming payment (packages/libraries/payment/README.md:53).
  • State Management – Redux thunks encapsulate intent creation/update, storing payment details, balance state, default selection, and validation status. They also generate success and error messaging for the UI (packages/libraries/payment/redux/thunks/create-intent/create-intent.ts:43, packages/libraries/payment/redux/thunks/update-intent/update-intent.ts:67).
  • ProviderspurchaseIntentProvider appends locale/cashback query params, retrieves the auth cookie, and adds X-PD-Language-ID so Alfred returns localised content (packages/libraries/payment/providers/purchase-intent-provider/purchase-intent-provider.ts:14).

Payment Status & Callbacks

stateDiagram-v2
    state "Created" as Created
    state "Confirmed" as Confirmed
    state "Committed" as Committed
    state "Captured" as Captured

    Created --> Confirmed
    Confirmed --> Committed
    Committed --> Captured
  • Pablo records payment state transitions and publishes SNS events after confirmations so Cart and other services can react (docs/payment-and-purchase.md:188).
  • Alfred invokes Pablo callbacks at /api/v5/alfred/callback for payment status updates, cashback, SAP invoicing, and notifications (docs/payment-and-purchase.md:238).
  • Instruction callbacks differentiate purchase vs payment owners, retry failed confirmations, notify Redis/CCS, and trigger invoices (docs/payment-and-purchase.md:288).

Operational Tooling

  • Payment Method Admin – Ops users with admin/editor roles can create or edit payment methods via the React form, which maps UI fields (hosted, tokenisation, surcharges, is_pablo_processable) into Pablo’s admin schema (src/components/payment-method-form/PaymentMethodForm.tsx:45, src/helpers/generate-payment-method-api-model/generate-payment-method-api-model.ts:6, src/global/api.ts:3).
  • Wallet Ops Portal – Provides campaign, banner, and cashback disbursement management against /api/v5/wallet/admin/*, and also reads configuration/payment method lists for wallet features (pd-ops-portal-wallets/src/global/api.ts:1).

Observations & Gaps

  • The Cart service invoking PaymentService/Pay (Pablo gRPC) is external; request bodies and error handling between Cart and Pablo are inferred from documentation, not available in these repos.
  • PSP/Bruce frontends handling redirects, hosted forms, and 3DS challenges are out of scope here; only redirect URLs and cookies are visible.
  • Feature flag lookups (“FwF”) and country/vendor payment availability checks happen server-side in Pablo but implementation details reside in other packages not included above.
  • Native/mobile clients for direct payments (subscriptions, dine-in, donations) follow similar patterns but their codebases are not part of this workspace, so platform-specific nuances remain unknown.