Payment Processing

Handle payment links and Paystack integration for WhatsApp commerce orders.

Overview

The payment system generates Paystack payment links for orders placed through the WhatsApp commerce flow. It supports subaccount splitting for marketplace commissions and the Nviti platform fee.

PaymentService

File: app/Modules/Commerce/Services/PaymentService.php

The PaymentService handles Paystack payment link generation and order payment workflows.

Methods

generatePaystackLink(string $email, float $amount, string $reference, array $metadata, ?string $callbackUrl, ?string $subaccountCode, ?int $transactionCharge): ?array

Creates a Paystack payment link for a transaction.

Parameters:

  • email (string) — Customer email address
  • amount (float) — Amount in naira (converted to kobo internally)
  • reference (string) — Unique transaction reference
  • metadata (array) — Additional metadata (merged with source: whatsapp_commerce)
  • callbackUrl (string, optional) — URL to redirect after payment
  • subaccountCode (string, optional) — Paystack subaccount code for marketplace splitting
  • transactionCharge (int, optional) — Amount to charge in kobo (for Nviti fee)

Returns: Array with authorization_url, reference, access_code or null on failure

generateOrderPaymentLink($order, string $email): ?string

Generates a complete payment link for an order, including URL shortening and order metadata updates. Handles email normalization for WhatsApp customers who don't have real email addresses.

Parameters:

  • order — The order model (requires id, uuid, company_id, visitor_id, total, metadata)
  • email (string) — Customer email (will be normalized if it's a WhatsApp placeholder)

Returns: Shortened payment URL or null on failure

Metadata stored on order: payment_link, payment_reference, payment_vendor, payment_total, payment_subtotal, payment_gateway_fee, payment_paystack_fee, payment_nviti_fee, payment_services_fee, payment_fee_breakdown

normalizePaystackEmail(string $email, $order): string

Normalizes email addresses for WhatsApp customers. Paystack requires a valid email, but WhatsApp visitors may not have one.

Behavior:

  • Valid emails pass through unchanged
  • WhatsApp placeholder emails (@whatsapp.placeholder) are converted to whatsapp-{local}@nviti.ng
  • Phone-prefixed emails (+234...@...) are normalized
  • Empty or invalid local parts use the visitor_id or order.id as the local part

shortenUrl(string $url): string

Shortens a payment URL using YorCreative\UrlShortener. Falls back to the original URL if shortening fails or the package is unavailable.

Payment Split Flow

When a vendor has a Paystack subaccount configured:

  1. generateOrderPaymentLink() looks up the vendor's Company.metadata for paystack_subaccount_code
  2. Calculates Nviti platform fee via NvitiFeeService (based on order total)
  3. Calls Paystack transaction/initialize with:
    • amount = order total in kobo
    • subaccount = vendor's subaccount code
    • transaction_charge = Nviti fee in kobo (flat charge, not percentage)
  4. Shortens the resulting payment URL via yorcreative/laravel-urlshortener
  5. Stores payment_link, payment_reference, payment_vendor in Order.metadata

Paystack API Notes

  • Bank list (GET /bank) is a public endpoint — works without valid auth
  • Subaccount and Transaction endpoints require a valid Paystack secret key
  • Bank list results are cached for 24 hours
  • Payment amounts must be in kobo (multiply naira by 100)

NvitiFeeService

File: app/Modules/Commerce/Services/NvitiFeeService.php

Calculates the platform fee using tiered percentage bands with a maximum cap.

Fee Structure

Order Amount (₦) Percentage
0 – 50,000 1.00%
50,001 – 100,000 0.85%
100,001 – 150,000 0.75%
150,001 – 300,000 0.65%
300,001+ 0.50%

Maximum fee cap: ₦1,500

Methods

calculateFee(float $amount): float

Returns the platform fee for a given order amount. Returns 0.0 if fees are disabled or amount is ≤ 0.

calculateFeeInKobo(float $amount): int

Returns the fee in kobo (multiplied by 100, rounded).

getFeeBreakdown(float $amount): array

Returns a detailed breakdown:

  • amount — Original amount
  • percentage — Applied percentage
  • raw_fee — Fee before cap
  • fee — Final fee (after cap)
  • capped — Whether the cap was applied
  • cap — Maximum cap value
  • vendor_receives — Amount after fee deduction

PaystackSubaccountService

File: app/Modules/Commerce/Services/PaystackSubaccountService.php

Manages Paystack subaccounts for marketplace vendors (businesses using Nviti).

Methods

createSubaccount(array $params): ?array

Creates a Paystack subaccount for a vendor. Requires business_name, bank_code, account_number.

updateSubaccount(string $subaccountCode, array $params): ?array

Updates an existing subaccount's details.

fetchSubaccount(string $subaccountCode): ?array

Fetches subaccount details from Paystack.

getBankList(): array

Returns a cached list of Nigerian banks (code => name), filtered to active banks, sorted alphabetically. Cached for 24 hours.

See Also

  • Order Notifications
  • Checkout Flow
  • Test files:
    • tests/Feature/Modules/Commerce/PaymentServiceTest.php
    • tests/Feature/Modules/Commerce/PaymentServiceExtendedTest.php
    • tests/Feature/Modules/Commerce/NvitiFeeServiceTest.php
    • tests/Feature/Modules/Commerce/PaystackSubaccountServiceTest.php

Keywords

payment, paystack, subaccount, fee, commission, marketplace, checkout, payment link, naira, kobo