Freshly Baked • Deliciously Served • Always with Love

Sign In to Eniceberny

Order faster & track deliveries

Sign In →
Eniceberny Bakery and Catering Enterprise

Multi-Gateway Payment Architecture & Ghanaian Commerce

This document details the multi-gateway payment subsystem, Mobile Money integrations, Paystack card processing, public invoice lookup, and transactional reconciliation guarantees implemented across the Eniceberny platform.


1. Architectural Strategy & Gateway Abstraction

Payment gateways in Eniceberny are designed using the Driver / Manager pattern, ensuring that adding new payment aggregators (e.g. Hubtel, Slydepay, ExpressPay) requires zero changes to core checkout or POS workflows:

sequenceDiagram
    autonumber
    actor Patron as Patron / Cashier
    participant Store as Checkout / POS Controller
    participant Manager as PaymentManager
    participant Driver as PaymentGatewayDriver
    participant Gateway as Paystack / MoMo Network
    participant DB as Relational Database

    Patron->>Store: Submit Order & Choose Payment Method
    Store->>Manager: driver($method)
    Manager->>Driver: initiatePayment($order, $payload)
    alt Paystack / MoMo Redirect
        Driver->>Gateway: Initialize Transaction API
        Gateway-->>Driver: Return Auth URL / USSD Prompt
        Driver-->>Store: Return Redirect / Instructions
        Store-->>Patron: Forward to Payment Screen
        Gateway-->>Driver: Webhook Event (charge.success)
        Driver->>DB: Verify Signature & Mark Order 'paid'
    else Physical Cash / Counter MoMo
        Driver->>DB: Log Transaction 'pending' (or 'success' for POS)
        DB-->>Store: Order Confirmed
        Store-->>Patron: Display Receipt & QR Code
    end

Gateway Contract:

All payment providers adhere strictly to App\Services\Payments\PaymentGatewayInterface:

namespace App\Services\Payments;

use App\Models\Order;

interface PaymentGatewayInterface
{
    /**
     * Initiate payment processing with the gateway.
     */
    public function initiatePayment(Order $order, array $payload = []): array;

    /**
     * Query gateway to verify settlement status.
     */
    public function verifyPayment(string $reference): array;

    /**
     * Process full or partial refund.
     */
    public function refund(Order $order, ?float $amount = null, ?string $reason = null): bool;
}

2. Supported Payment Methods in Ghana

2.1 Cash on Delivery & Counter Cash (cash)

  • Online Checkout: Customer chooses "Cash on Delivery" or "Pay upon In-Store Collection".
    • Order is placed with payment_status: 'pending'.
    • Upon delivery by the dispatch rider or patron pickup, staff marks the order paid via the Admin Order console.
  • In-Store POS: Cashier tenders physical notes, calculates change, and completes the sale atomically with payment_status: 'paid'.

2.2 Mobile Money Direct & USSD Push (momo)

Mobile Money is the primary consumer payment rail in Ghana:

  • Networks Supported: MTN Mobile Money, Telecel Cash, AT Money.
  • Storefront Checkout:
    • The patron enters their 10-digit Ghanaian mobile number.
    • The system records a unique payment reference (EB-MOMO-XXXXXX) and instructs the patron to authorize the USSD prompt on their handset.
    • Verification occurs either automatically via network callback or manually by entering the telecom transaction ID.

2.3 Paystack Cards & Digital Wallets (paystack)

  • Payment Instruments: Visa, Mastercard, Verve, and instant Paystack Mobile Money.
  • Server-Side Security: Orders are never marked paid simply because a user's browser redirected to a success URL. All settlements require cryptographic verification.

3. Webhook Architecture & Cryptographic Signature Verification

Paystack dispatches automated HTTP POST notifications when transactions are completed.

Webhook Verification Workflow:

  1. Paystack sends a POST request to /api/v1/payments/webhook with the header X-Paystack-Signature.
  2. The middleware computes the HMAC SHA512 hash of the raw incoming request body using the configured secret key:
    $computedSignature = hash_hmac('sha512', $request->getContent(), config('services.paystack.secret_key'));
    if (!hash_equals($computedSignature, $request->header('X-Paystack-Signature', ''))) {
        Log::warning('Paystack webhook rejected: Invalid signature');
        return response()->json(['error' => 'Invalid signature'], 401);
    }
    
  3. Idempotency Guarantee: If the webhook references an order that is already marked paid, the system acknowledges HTTP 200 without executing duplicate inventory or notification side-effects.
  4. Order Status Progression:
    • Order payment_status updates to paid.
    • PaymentTransaction record created with provider reference and fee breakdown.
    • An immutable audit trail entry is logged.
    • Order confirmation email with attached PDF receipt is automatically dispatched.

4. Public Invoice Lookup & Balance Settlement (/invoices)

For corporate catering clients, custom cake orders, and telephone bookings, the platform provides a self-service public invoice lookup portal:

  • Lookup URL: GET /invoices
  • Search Parameters: Patrons can look up any order or proposal using either:
    1. Official Order / Invoice Number (e.g. EB-20260927-1402)
    2. Customer Telephone Number (e.g. 0532342126 or +233532342126)
  • Detailed Invoice View (/invoices/{order_number}):
    • Displays business branding crest, hotlines, and address.
    • Shows line items, itemized rates, delivery fees, and discounts.
    • Real-time payment status badge (PAID, PARTIALLY PAID, UNPAID).
    • Single-click "Pay Online Now" button launching instant Paystack checkout for outstanding balances.
    • Bank and Mobile Money remittance instructions for direct offline bank wire transfers.

5. Automated PDF Invoicing & QR Code Engine

Receipts and invoices can be downloaded or emailed as high-resolution PDF documents:

Technical Stack:

  • DomPDF (barryvdh/laravel-dompdf): Renders HTML/Blade templates into crisp PDF documents.
  • Standalone QR Code Generator (chillerlan/php-qrcode): Generates offline base64 PNG QR code data streams embedded directly into the PDF:
    $qrCodeData = (new QRCode)->render(route('orders.receipt', $order->order_number));
    

Automated Dispatch:

  • An automated PDF receipt (Receipt-EB-*.pdf) is attached to:
    • OrderPlacedReceiptMail: Dispatched immediately upon order placement.
    • OrderStatusUpdatedMail: Dispatched whenever an order status transitions to completed or delivered.
  • Store managers can also dispatch receipts on demand via the "Send Email Receipt (PDF Attached)" action in /admin/orders/{id}.

6. Financial Integrity & Decimal Precision

To eliminate floating-point rounding errors and ensure compliance with accounting standards:

  1. Database Types: All monetary columns (orders.subtotal, orders.delivery_fee, orders.tax_amount, orders.discount_amount, orders.total, payment_transactions.amount) use DECIMAL(10, 2).
  2. Display Helper: Amounts are formatted consistently across templates using format_currency($amount) (e.g. GH₵ 125.00) with font-sans font-bold tabular-nums to ensure scannable visual alignment.
  3. Database Transactions: All financial modifications are enclosed within DB::transaction() to guarantee atomicity.

Your Bakery Basket

Subtotal:

Fresh orders prepared promptly upon checkout