Skip to main content

Multi-Provider Payment Integration

This document outlines the architecture and workflows for the multi-provider payment system in Adamondo, supporting both Paymob and Amazon Payment Services (APS).

Architecture Overview

The system uses a Provider Pattern to abstract gateway-specific logic behind a unified interface. This allows the core business logic to remain agnostic of the underlying payment processor.

Core Components

  • PaymentProvider (Interface): Defines standard operations (createIntent, verifyIntent, refund, capture, void).
  • PaymentFactory: Responsible for instantiating the correct provider based on the paymentProvider configuration.
  • PaymobProvider: Concrete implementation for Paymob (Intention-based flow).
  • APSProvider: Concrete implementation for Amazon Payment Services (Hosted Checkout flow).

Class Diagram


Detailed Payment Flows

Regardless of the provider, the entry point is always the createPaymentIntent mutation. However, the redirection and verification steps differ significantly based on the provider's architecture.

1. Amazon Payment Services (APS) Flow

APS uses a Hosted Checkout model requiring a secure POST from a Bridge Page.

Sequence Diagram (APS)

Step-by-Step (APS):

  1. Intent Creation: createPaymentIntent signs parameters with SHA_REQUEST_PHRASE.
  2. Bridge Navigation: The frontend loads /payment/aps/checkout in an iframe/WebView.
  3. Secure POST: The bridge page auto-submits a hidden form to APS using target="_self".
  4. Double-Verification Path:
    • Callback (Push): APS POSTs to /payment/aps/callback. The backend verifies the signature and calls getAndUpdateReservationPayment.
    • Query (Pull): Simultaneously, the frontend redirects back to the app and calls GetItinerary, which also calls getAndUpdateReservationPayment.
  5. Standardized Finalization: getAndUpdateReservationPayment calls APSProvider.verifyIntent (API check) and ensures the ReservationWorkflow.PAY action is executed exactly once, regardless of which process finishes first.

2. Paymob Flow

Paymob follows a standard URL-redirection and polling/verification model.

Sequence Diagram (Paymob)

Step-by-Step (Paymob):

  1. Intent Creation: Backend communicates with Paymob APIs to generate a unique paymentUrl.
  2. Direct Navigation: The frontend navigates the iframe/WebView directly to the Paymob URL.
  3. User Redirection: Upon completing the payment, the user is redirected back to the app (redirectionUrl).
  4. Query Verification (Pull): The frontend calls GetItinerary, which triggers getAndUpdateReservationPayment.
  5. Standardized Finalization: getAndUpdateReservationPayment calls PaymobProvider.verifyIntent to confirm status before executing the ReservationWorkflow.PAY action.

[!NOTE]
Why paymentIntentId mirrors confirmationCode for APS?
For provider-agnosticism, the system always uses the paymentIntentId column as the primary key for the verifyIntent(id) call. For Paymob, this stores the gateway's intention_id. For APS (which doesn't return an ID during intent creation), we store the confirmationCode here early so that the generic verification helper knows what merchant_reference to query if the user returns to the app before a callback is received.


Comparison Table

FeatureAmazon Payment Services (APS)Paymob
Submission MethodPOST via Backend Bridge PageGET via direct URL
Request AuthSHA-256 Signature (Request Phrase)API Token -> Payment Key
VerificationPush (Server Callback Handler)Pull (API verifyIntent + status check)
Identificationmerchant_reference (Confirmation Code)order_id (Paymob internal ID)
Redirection UIAdamondo Bridge Page (HTML/JS)Paymob Checkout Page

Administrative Operations

Admins can perform manual operations via the Site Admin dashboard. These mutations are provider-aware.

OperationMutationDescription
RefundadminRefundTransactionRoutes to provider.refund() based on the original reservation's provider.
CaptureadminCaptureTransactionUsed for Two-Step payments. Routes to provider.capture().
VoidadminVoidTransactionCancels an authorized but uncaptured payment.
ExecuteadminExecuteReservationTransactionExecutes pending transactions (refunds/payouts) manually.

Administrative UI & Routes

The administrative interface is fully provider-agnostic. All payment-related administrative actions are consolidated under a single, generic route.

  • Old Route: /admin/paymob
  • New Route: /admin/payments
  • Screen Component: AdminPaymentScreen.tsx (previously AdminPaymobScreen.tsx)

This screen dynamically adapts to the paymentProvider associated with each reservation, ensuring a consistent experience regardless of the gateway used.


Configuration

Environment Variables (Backend)

  • APS_MERCHANT_IDENTIFIER: Your APS merchant ID.
  • APS_ACCESS_CODE: Access code for the environment.
  • APS_SHA_REQUEST_PHRASE: Signature key for requests.
  • APS_SHA_RESPONSE_PHRASE: Signature key for responses.

Frontend Configuration

  • EXPO_PUBLIC_DEFAULT_PAYMENT_PROVIDER: Sets the default provider for new reservations. Values: PAYMOB or APS.

Selecting the Provider

For Developers (Frontend)

When calling the createPaymentIntent mutation, you can explicitly pass the paymentProvider argument:

mutation CreatePaymentIntent($reservationId: Int!, $paymentProvider: String) {
createPaymentIntent(reservationId: $reservationId, paymentProvider: $paymentProvider) {
paymentUrl
status
}
}

If paymentProvider is omitted, the backend will default to PAYMOB (or you can customize this in src/data/mutations/Payment/createPaymentIntent.ts). In the current guest-frontend implementation, it uses the resolved value from config.DEFAULT_PAYMENT_PROVIDER (defined in src/utils/config.ts).

For Admins

Admins do not need to manually select a provider for refunds or captures. The system automatically retrieves the paymentProvider stored on the Reservation record and routes the request to the correct gateway.