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 thepaymentProviderconfiguration.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):
- Intent Creation:
createPaymentIntentsigns parameters withSHA_REQUEST_PHRASE. - Bridge Navigation: The frontend loads
/payment/aps/checkoutin an iframe/WebView. - Secure POST: The bridge page auto-submits a hidden form to APS using
target="_self". - Double-Verification Path:
- Callback (Push): APS POSTs to
/payment/aps/callback. The backend verifies the signature and callsgetAndUpdateReservationPayment. - Query (Pull): Simultaneously, the frontend redirects back to the app and calls
GetItinerary, which also callsgetAndUpdateReservationPayment.
- Callback (Push): APS POSTs to
- Standardized Finalization:
getAndUpdateReservationPaymentcallsAPSProvider.verifyIntent(API check) and ensures theReservationWorkflow.PAYaction 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):
- Intent Creation: Backend communicates with Paymob APIs to generate a unique
paymentUrl. - Direct Navigation: The frontend navigates the iframe/WebView directly to the Paymob URL.
- User Redirection: Upon completing the payment, the user is redirected back to the app (
redirectionUrl). - Query Verification (Pull): The frontend calls
GetItinerary, which triggersgetAndUpdateReservationPayment. - Standardized Finalization:
getAndUpdateReservationPaymentcallsPaymobProvider.verifyIntentto confirm status before executing theReservationWorkflow.PAYaction.
[!NOTE]
WhypaymentIntentIdmirrorsconfirmationCodefor APS?
For provider-agnosticism, the system always uses thepaymentIntentIdcolumn as the primary key for theverifyIntent(id)call. For Paymob, this stores the gateway'sintention_id. For APS (which doesn't return an ID during intent creation), we store theconfirmationCodehere early so that the generic verification helper knows whatmerchant_referenceto query if the user returns to the app before a callback is received.
Comparison Table
| Feature | Amazon Payment Services (APS) | Paymob |
|---|---|---|
| Submission Method | POST via Backend Bridge Page | GET via direct URL |
| Request Auth | SHA-256 Signature (Request Phrase) | API Token -> Payment Key |
| Verification | Push (Server Callback Handler) | Pull (API verifyIntent + status check) |
| Identification | merchant_reference (Confirmation Code) | order_id (Paymob internal ID) |
| Redirection UI | Adamondo Bridge Page (HTML/JS) | Paymob Checkout Page |
Administrative Operations
Admins can perform manual operations via the Site Admin dashboard. These mutations are provider-aware.
| Operation | Mutation | Description |
|---|---|---|
| Refund | adminRefundTransaction | Routes to provider.refund() based on the original reservation's provider. |
| Capture | adminCaptureTransaction | Used for Two-Step payments. Routes to provider.capture(). |
| Void | adminVoidTransaction | Cancels an authorized but uncaptured payment. |
| Execute | adminExecuteReservationTransaction | Executes 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(previouslyAdminPaymobScreen.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:PAYMOBorAPS.
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.