Read Fundamentals first if you haven’t.
Generate it
--webhook if the provider has no webhooks.
What you get
What you get
Two naming rules
Both are enforced by how discovery works, so they aren’t optional.1
The module must be named {Something}Gateway
PaymentServiceProvider scans Modules/Gateways/* and derives a key from the name, so
AcmeGateway becomes acme.2
The payment service must be at Modules\{Module}\Services\{Module}PaymentService
And
getGatewayName() has to return that same key.Registration is lazy: nothing is instantiated at boot, so credentials are only decrypted when a
payment actually happens. Don’t put API calls in your constructor.
What you implement
BasePaymentService handles every billing flow already. It looks at $paymentData['type'] and routes
to the right one.
The flows it covers
The flows it covers
app/Services/AcmeGatewayPaymentService.php
enabled is the on/off switch
Keep itfalse until the gateway is fully configured. A half-set-up gateway appearing at checkout is
worse than it not appearing at all.
Verification: pick one
Four rules, and none of them are stylistic.
Never trust the redirect
Never trust the redirect
Re-query the provider inside
executePayment(). Query strings are attacker-controlled.Verify the webhook signature against the raw body, before parsing
Verify the webhook signature against the raw body, before parsing
Assume webhooks arrive twice
Assume webhooks arrive twice
Providers retry.
handlePaymentSuccess() has to be safe to call again for the same payment, so
check the PaymentToken state first.Check the amount and the currency
Check the amount and the currency
Not just the status. Confirm what was paid is what you asked for.
Native subscriptions
Optional.
Each is a
should...() predicate plus a handle...() action. Core probes with method_exists()
first, so a half-finished handler falls back to the normal flow rather than erroring.
Declare it explicitly rather than relying on the class name:
Several checkout options from one module
Optional.
GatewayVariantInterface to appear more than once, like “PayPal” and “PayPal Subscription”.
Test it
type switch:
1
New purchase
2
Renewal
3
Invoice payment
4
Upgrade
5
Balance top-up
6
Cancel halfway
Confirm no service is created.
7
Pay, then close the browser before the redirect
The webhook should still finish the order.
8
Replay the same webhook twice
Nothing should double-charge or double-provision.
Worth reading
BankWireGateway
Offline payment, no API at all.
CashfreeGateway
Clean HMAC webhook verification.
VippsGateway
Straightforward redirect flow.
StripeGateway
Cards plus native subscriptions.
PayPalGateway
Everything, including variants and disputes.
Two rough edges.
PaymentGatewayInterface assumes one flow (create, approve, execute), so refunds
and partial captures have no contract and gateways that support them expose their own methods. And
BankWireGateway mixes root and app/ layouts, which makes it a good behavioural reference and a
poor structural one.
