Payment Gateway System
Gateway-agnostic payment system enabling multi-provider support with unified DTOs and market-specific configuration.
Architecture Overview
Section titled “Architecture Overview”The payment system uses a modular architecture where:
- Gateway-agnostic DTOs standardize customer and payment data
- Gateway Factory selects the appropriate provider per market/payment method
- PaymentService orchestrates all payment operations
- Individual gateways transform DTOs to provider-specific formats
Controller/Livewire │ ▼┌──────────────────┐│ PaymentService │ ◄── Main orchestrator└───────┬──────────┘ │ ▼┌──────────────────┐ ┌─────────────────┐│ GatewayFactory │────►│ MarketConfig │└───────┬──────────┘ └─────────────────┘ │ ▼┌──────────────────┐│ PaymentGateway │ ◄── Stripe/Adyen/Redsys│ Interface │└──────────────────┘Gateway-Agnostic DTOs
Section titled “Gateway-Agnostic DTOs”CustomerData
Section titled “CustomerData”Standardizes customer information across all gateways.
Source: backend/app/Services/Payment/DTOs/CustomerData.php
Properties:
id- Internal client IDname- Customer’s full nameemail- Customer’s email addressphone- Customer’s phone number (optional)billingAddress- AddressData object (optional)preferredLocale- Locale code e.g., ‘es_ES’ (optional)preferredCurrency- Currency code e.g., ‘EUR’ (optional)metadata- Additional key-value pairs
Gateway transformations:
toStripeFormat()- Stripe customer creation formattoAdyenFormat()- Adyen shopper format with shopperReferencetoRedsysFormat()- Redsys DS_MERCHANT fields
AddressData
Section titled “AddressData”Standardizes billing addresses across gateways.
Source: backend/app/Services/Payment/DTOs/AddressData.php
Properties:
line1,line2- Street addresscity,state,postalCodecountry- ISO 3166-1 alpha-2 code (e.g., ‘ES’)
Methods:
isEmpty()- Check if address has any dataisValid()- Check if address has minimum required data (country)toStripeFormat()- Stripe address formattoAdyenFormat()- Adyen address withstreet,houseNumberOrName
Creating Customer Data from Client
Section titled “Creating Customer Data from Client”The Client model provides a single source of truth for payment customer data:
Source: backend/app/Models/Client.php:156
// Get gateway-agnostic customer data$customerData = $client->toPaymentCustomerData();
// With additional metadata$customerData = $client->toPaymentCustomerData([ 'booking_id' => $booking->id, 'source' => 'checkout',]);
// Convert to specific gateway format$stripeData = $customerData->toStripeFormat();$adyenData = $customerData->toAdyenFormat();$redsysData = $customerData->toRedsysFormat();Supported Gateways
Section titled “Supported Gateways”| Gateway | Status | Payment Methods |
|---|---|---|
| Stripe | Active | Card, Apple Pay, Google Pay, SEPA Debit |
| Adyen | Planned | Card, Apple Pay, Google Pay, iDEAL, Klarna |
| Redsys | Planned | Card (Spanish banks) |
Gateway availability is configured per market in market_payment_methods table.
Payment Service
Section titled “Payment Service”The main orchestrator for all payment operations.
Source: backend/app/Services/Payment/PaymentService.php
Key Operations
Section titled “Key Operations”Get payment methods for market:
$methods = $paymentService->getPaymentMethodsForMarket($market);// Returns: Collection of {code, name, icon, gateway, display_order}Create payment intent:
$result = $paymentService->createPaymentIntent( booking: $booking, client: $client, market: $market, paymentMethodCode: 'card', depositOnly: true,);// Returns: {intent_result, payment_type, amount_cents}Record payment after frontend confirmation:
$payment = $paymentService->recordPayment( booking: $booking, client: $client, gatewayCode: 'stripe', paymentMethodCode: 'card', gatewayPaymentId: 'pi_xxx', paymentType: PaymentType::Deposit, amountInCents: 15000, status: PaymentStatus::Succeeded,);Post-Payment Finalization
Section titled “Post-Payment Finalization”After successful payment, BookingFinalizationService completes the booking:
- Creates
Passengerrecords from checkout session’straveler_data - Attaches passengers to booking (first passenger = lead)
- Attaches passengers to client
- Creates
BookingUpsellrecords from session selections - Updates booking with
base_price,extras_price,total_amount
Source: backend/app/Services/Booking/BookingFinalizationService.php
// Called automatically in PaymentController::confirm() after successful payment$this->bookingFinalizationService->finalizeFromCheckoutSession($booking, $session);Deposit/Balance Payments
Section titled “Deposit/Balance Payments”The system supports split payments where customers pay a deposit upfront and the balance before departure.
Source: backend/app/Services/Payment/PaymentCalculatorService.php
Calculation Logic
Section titled “Calculation Logic”- Deposit available: When balance due date > today
- Deposit amount:
total * deposit_percentage / 100(uses session’stotal_priceincluding extras) - Balance due date:
departure_date - balance_days_before
Configuration
Section titled “Configuration”Config: config/payment.php
'deposit' => [ 'percentage' => 50, // 50% deposit 'balance_days_before' => 7, // Balance due 7 days before departure],Payment Types
Section titled “Payment Types”| Type | When Used |
|---|---|
deposit | Initial partial payment |
balance | Remainder charged before departure |
full | When deposit not available (close to departure) |
Gateway Dashboard URLs
Section titled “Gateway Dashboard URLs”Payments include links to view transactions in the gateway’s dashboard.
Source: backend/app/Models/Payment.php:250
Supported Gateways
Section titled “Supported Gateways”Stripe:
- Test mode:
https://dashboard.stripe.com/test/payments/{id} - Live mode:
https://dashboard.stripe.com/payments/{id}
Adyen:
https://ca-{environment}.adyen.com/ca/ca/accounts/showTx.shtml?pspReference={id}
Admin Panel Integration
Section titled “Admin Panel Integration”The ViewPayment page includes a “View in {Gateway}” action button.
Source: backend/app/Filament/Resources/Payments/Pages/ViewPayment.php:29
Gateway Interface
Section titled “Gateway Interface”All gateways implement PaymentGatewayInterface.
Source: backend/app/Contracts/Payment/PaymentGatewayInterface.php
Required Methods
Section titled “Required Methods”| Method | Purpose |
|---|---|
createCustomer() | Create customer in gateway |
createSetupIntent() | Save payment method for future use |
createPaymentIntent() | Create one-time or initial charge |
confirmPayment() | Confirm after 3DS authentication |
chargeOffSession() | Charge saved method (scheduled balance) |
refund() | Process refund |
handleWebhook() | Process gateway webhooks |
supportsPaymentMethod() | Check method support |
getGatewayCode() | Return gateway identifier |
Result DTOs
Section titled “Result DTOs”| DTO | Purpose |
|---|---|
PaymentIntentResult | Payment intent creation result with client_secret |
PaymentResult | Charge/confirm result with success/failure info |
SetupIntentResult | Setup intent for saving payment methods |
RefundResult | Refund transaction result |
WebhookResult | Webhook processing result |
Configuration
Section titled “Configuration”Environment Variables
Section titled “Environment Variables”# Stripe (via Laravel Cashier)STRIPE_KEY=pk_test_xxxSTRIPE_SECRET=sk_test_xxxSTRIPE_WEBHOOK_SECRET=whsec_xxx
# Payment settingsPAYMENT_DEFAULT_CURRENCY=EURPAYMENT_DEPOSIT_PERCENTAGE=50PAYMENT_BALANCE_DAYS_BEFORE=7Database Tables
Section titled “Database Tables”payment_gateways- Gateway definitions (stripe, adyen, redsys)payment_methods- Method types (card, apple_pay, sepa_debit)market_payment_methods- Market-specific gateway/method configurationpayments- Payment transactionsclient_payment_methods- Saved payment methods per client
Testing
Section titled “Testing”Tests use dedicated factories for payment components.
Source: backend/tests/Unit/Services/Payment/
# Run payment tests./vendor/bin/sail artisan test --filter=Payment
# Test DTO transformations./vendor/bin/sail artisan test --filter=CustomerDataTest./vendor/bin/sail artisan test --filter=AddressDataTestRelated
Section titled “Related”- Checkout API - Frontend checkout flow
- Booking Model - Booking payment status
- Source:
backend/app/Services/Payment/