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 addressamount(float) — Amount in naira (converted to kobo internally)reference(string) — Unique transaction referencemetadata(array) — Additional metadata (merged withsource: whatsapp_commerce)callbackUrl(string, optional) — URL to redirect after paymentsubaccountCode(string, optional) — Paystack subaccount code for marketplace splittingtransactionCharge(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 (requiresid,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 towhatsapp-{local}@nviti.ng - Phone-prefixed emails (
+234...@...) are normalized - Empty or invalid local parts use the
visitor_idororder.idas 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:
generateOrderPaymentLink()looks up the vendor'sCompany.metadataforpaystack_subaccount_code- Calculates Nviti platform fee via
NvitiFeeService(based on order total) - Calls Paystack
transaction/initializewith:amount= order total in kobosubaccount= vendor's subaccount codetransaction_charge= Nviti fee in kobo (flat charge, not percentage)
- Shortens the resulting payment URL via
yorcreative/laravel-urlshortener - Stores
payment_link,payment_reference,payment_vendorinOrder.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 amountpercentage— Applied percentageraw_fee— Fee before capfee— Final fee (after cap)capped— Whether the cap was appliedcap— Maximum cap valuevendor_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.phptests/Feature/Modules/Commerce/PaymentServiceExtendedTest.phptests/Feature/Modules/Commerce/NvitiFeeServiceTest.phptests/Feature/Modules/Commerce/PaystackSubaccountServiceTest.php
Keywords
payment, paystack, subaccount, fee, commission, marketplace, checkout, payment link, naira, kobo