ashrafic / bagisto-clover
Clover Hosted Checkout payment gateway integration for Bagisto 2.x — redirect checkout, signed webhooks, auto invoicing and order transactions.
Package info
github.com/ashrafic/bagisto-clover
Type:bagisto-package
pkg:composer/ashrafic/bagisto-clover
Requires
- php: ^8.3
Requires (Dev)
None
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Clover Hosted Checkout payment gateway for Bagisto 2.x.
Customers are redirected to a PCI-compliant, Clover-hosted payment page — no card data ever touches your server (SAQ-A PCI scope). After payment, the order, invoice and order transaction are created automatically, verified through Clover's signed webhook with a redirect fallback so an order is never lost if the webhook is delayed.
How it works
Checkout ──▶ Create session ──▶ Clover Hosted Checkout page ──▶ Customer pays
│
┌──────────────────────────────────────────────────────┤
▼ ▼
Signed webhook Redirect back to store
(HMAC-SHA256) (/clover/success)
│ │
creates + settles the order reuses the order if it exists,
(idempotent — skips if otherwise creates it, then
one already exists), always clears the cart session
deactivates the cart and shows the success page
│ │
└─────────────────▶ one order per payment ◀───────────┘
- Whichever arrives first settles the payment. The webhook creates and settles the order idempotently (skipping creation when an order already exists), so a payment is recorded even if the customer's browser never makes it back. The success return reuses the webhook's order — waiting a few seconds for it to appear — or creates the order itself when no webhook arrived.
- Cart clearing is the browser's job, always. On every return the cart is deactivated and the guest session binding cleared in the customer's own request — the only place it can be done — even when the webhook already deactivated the cart in the database.
- The webhook (
POST clover/webhook) verifies theClover-Signatureheader with HMAC-SHA256 against your signing secret; unsigned or forged requests are rejected with401. - A payment whose customer and webhook both never made it back can still be recovered by scheduling
php artisan clover:settle-abandoned(e.g. every 15 minutes). - Every session is tracked in the
clover_checkout_sessionstable (status, payment id, verification source), giving you a full audit trail per checkout attempt.
Features
- Hosted Checkout redirect flow — no card data on your server
- Signed webhook processing (
Clover-SignatureHMAC-SHA256) with strict rejection of unauthenticated calls - Idempotent dual-path order processing — webhook and redirect never double-create an order
- Automatic order creation, status update, invoice and order transaction
- Line items for products, shipping, tax and discounts, converted to the smallest currency unit
- Sandbox and production credentials per channel, with separate test merchant settings
- Optional Hosted Checkout page configuration UUID for merchants with multiple payment pages
- Admin configuration UI translated in 22 locales
- Pest unit and feature tests included
Requirements
| Requirement | Version |
|---|---|
| PHP | ^8.3 |
| Bagisto | 2.x |
| Laravel | 12.x |
You also need a Clover merchant account with Hosted Checkout enabled (Ecommerce section of the Merchant Dashboard) and an HTTPS store URL (required by Clover for redirect URLs and webhooks).
Installation
composer require ashrafic/bagisto-clover
Register the module in config/concord.php (inside the modules array) — this is the only manual step, and it is required by Bagisto's module system for every package:
Webkul\Clover\Providers\ModuleServiceProvider::class,
Run the migration, publish the checkout logo and refresh:
php artisan migrate php artisan vendor:publish --tag=clover php artisan optimize:clear
That's it — no core file edits are needed. The webhook route is CSRF-exempt by design (stateless, throttled), and the migration creates the clover_checkout_sessions table. If your app disables package discovery, register the provider manually in bootstrap/providers.php:
use Webkul\Clover\Providers\CloverServiceProvider; return [ // ... CloverServiceProvider::class, ];
When does Clover appear in the store?
- Admin: the configuration page (Configure → Sales → Payment Methods → Clover) appears automatically after installation.
- Checkout: the payment method only appears when it is actually usable — the admin must set Status to ON and provide valid credentials for the active mode (sandbox or production). A payment method without credentials is hidden on purpose, so customers can never reach a dead-end at redirect.
Clover dashboard setup
- Log in to the Clover Merchant Dashboard.
- Go to Settings → View all settings → Ecommerce → Hosted Checkout.
- Generate an Ecommerce API token for the Hosted Checkout integration type:
- The private token is your API token (Bearer token)
- Note your merchant ID (
mId)
- In the Webhook section, enter
https://your-store.com/clover/webhook, click Generate and copy the signing secret. - Leave the dashboard redirect URLs empty — the package sends per-transaction redirect URLs (
/clover/success,/clover/cancel) with the checkout session id embedded, and dashboard values would override them.
Configuration
In the Bagisto admin: Configure → Sales → Payment Methods → Clover.
| Field | Description |
|---|---|
| Status | Enable/disable the method |
| Title / Description | Shown at checkout (per channel and locale) |
| Logo | Payment method logo shown at checkout |
| Merchant ID / API Token / Webhook Signing Secret | Production credentials |
| Sandbox | Toggle between sandbox and production credentials |
| Test Merchant ID / Test API Token / Test Webhook Signing Secret | Sandbox credentials |
| Page Config UUID | Optional UUID when using multiple Hosted Checkout page configurations |
Testing
Sandbox test transaction
-
Create a free account on the Clover Developer Dashboard — a sandbox test merchant is provisioned automatically.
-
Configure the sandbox credentials in the admin (sandbox enabled) and set
APP_URLto an HTTPS URL (use a tunnel such as ngrok for local development). -
Check out and pay with the test cards (per Clover's test card numbers):
Scenario Card number Successful payment 4242 4242 4242 4242Successful payment (debit) 4000 0566 5566 5556Declined payment 4264 2815 1111 7771Expiry: any future date · CVV: any 3 digits · ZIP: any 5 digits.
Note that Clover's sandbox approves transactions below $100.00 and returns an error above it — keep the test cart total under $100.
Test suite
The tests run inside a Bagisto installation. Add the suites to the app's phpunit.xml:
<testsuite name="Clover Unit Test"> <directory suffix="Test.php">vendor/ashrafic/bagisto-clover/tests/Unit</directory> </testsuite> <testsuite name="Clover Feature Test"> <directory suffix="Test.php">vendor/ashrafic/bagisto-clover/tests/Feature</directory> </testsuite>
Bind the test case in tests/Pest.php:
use Webkul\Clover\Tests\CloverTestCase; uses(CloverTestCase::class)->in('../vendor/ashrafic/bagisto-clover/tests');
./vendor/bin/pest --testsuite="Clover Unit Test" ./vendor/bin/pest --testsuite="Clover Feature Test"
The feature suite covers the full payment flow: redirect processing, webhook-first processing, the webhook/redirect race, signature rejection and declined payments — no real Clover account required.
Troubleshooting
Open /{admin-url}/clover/diagnostics in the store (any logged-in admin) — it shows:
- the channel's configuration state (sandbox ON/OFF, token/merchant/webhook-secret set)
- the checkout sessions audit trail (status, payment id, verification source)
- the last 100 Clover entries from the application logs
Notes
- Hosted Checkout charges in the merchant's Clover currency — it must match your store's base currency.
- Discounts are sent as a negative line item; verify with a sandbox transaction if your account applies cart-level discounts.
- Webhook requests are rejected (
401) when no signing secret is configured — configure the secret before enabling webhooks.
Contributing
Contributions are welcome. Please make sure the existing code style (Laravel preset, pint.json) and the test suite pass:
./vendor/bin/pint --test ./vendor/bin/pest