treestoneit/laravel-converge-api
A Laravel wrapper for the Elavon Converge payment API that simplifies card payments, authorizations, captures, refunds, voids, and transaction reporting. Configure credentials and start processing payments from your Laravel application with a clean, expressive interface.
treeStoneIT/laravel-converge-api is a thin Laravel wrapper around TreeStone's Converge API client, packaged as a service provider + facade + publishable config. The v2.2.2 release only widens the framework constraint to Laravel 12 — nothing to migrate if you're already on 2.x.
composer require treestoneit/laravel-converge-api:^2.2.2
php artisan vendor:publish --provider="TreeStoneIT\LaravelConvergeApi\ConvergeApiServiceProvider"
Set credentials via .env (never inline in config), then confirm the mapping in config/converge.php:
CONVERGE_MERCHANT_ID=
CONVERGE_USER_ID=
CONVERGE_PIN=
CONVERGE_DEMO=true
Where to look first, in order:
config/converge.php — the credential keys and sandbox/live switch are the source of truth for every other decision.src/ — the facade accessor, the main client class, and the request/response DTOs. Note the exact method names and the shape of returned objects before you design around them.tests/ directory — they double as the most reliable API contract.First use case: a single charge in a checkout flow. Keep it out of the controller closure and put it behind a small service class you own, so the facade call is one line you can swap or fake later:
$result = Converge::sale([
'amount' => 1999, // integer minor units, not 19.99
'card' => $token,
'order_id' => $order->uuid,
]);
Run it against the demo endpoint first, assert the response shape, then flip CONVERGE_DEMO=false in the production environment only.
Wrap, don't sprinkle. The facade is convenient and global, which makes it easy to end up with gateway calls in controllers, jobs, listeners and Nova actions. Define a PaymentGateway interface in your app, bind an implementation that delegates to Converge, and inject that. It also gives you one place to add logging, retries, and result normalisation.
Normalise the response. Converge returns provider-specific status codes and messages. Convert them into your own result object (successful, reference, declineReason, raw) at the boundary. Your domain shouldn't speak Converge.
Persist the raw exchange. Every charge attempt should write a row with your order ID, the request payload (redacted), the raw response, and a timestamp. When a customer disputes a charge six weeks later, this is the only thing that saves you.
Tokenise, then charge. If the package exposes tokenisation/hosted-payment helpers, use them — you keep raw PAN data out of your servers and dramatically shrink your PCI scope. Store tokens alongside the customer, not in session.
Queue anything that can be slow. Gateway latency spikes. For recurring billing or bulk operations, use queued jobs with $tries, backoff(), and an explicit idempotency key derived from your order ID so a retry doesn't double-charge.
Per-tenant configuration. If you serve multiple merchants, the singleton config binding will bite you. Either build a factory that instantiates a configured client per tenant from a DB-backed settings row, or set config at runtime before resolving — and clear it after.
Testing. Bind a fake implementation of your own interface in tests. For integration-level tests, point the config at the demo environment and guard with a dedicated test suite that never runs in CI by default.
^2.2.2 explicitly — an unconstrained ^2.0 resolve can land on a version whose illuminate/support constraint excludes 12.env() calls outside config/ return null once php artisan config:cache runs. Only reference credentials through config('converge.*'), and run php artisan config:clear after touching .env.report(), and check your config/logging.php retention. The PIN especially should never appear in a log line.19.99 is the classic silent 1-cent mismatch between your ledger and the gateway's.sale() after a timeout on the response path can double-charge. Prefer querying the transaction status first.vendor/ is not.How can I help you explore Laravel packages today?