comgate-laravel-package maintained by sloukapetr
comgate-laravel-package
Clean, reusable Laravel package for Comgate Merchant REST API v2.0 integration with Laravel 11, 12 and 13.
Features
- One-off payments (
POST /v2.0/payment.json) - Redirect-based one-off payments (
POST /v2.0/paymentRedirect/merchant/{merchant_id}) - Recurring payments (
initRecurring=true,POST /v2.0/payment.json,POST /v2.0/recurring.json) - Pre-authorization capture/cancel (
PUT /v2.0/preauth/transId/{transId}.json,DELETE /v2.0/preauth/transId/{transId}.json) - Refunds and payment cancellations (
POST /v2.0/refund.json,DELETE /v2.0/payment/transId/{transId}.json) - Payment status verification (
GET /v2.0/payment/transId/{transId}.json) - Payment method discovery (
GET /v2.0/method.json) - Transfer reporting and payout export helpers (
GET /v2.0/transferList/date/{date}.json,GET /v2.0/singleTransfer/transferId/{transferId}.json,GET /v2.0/csvSingleTransfer/transferId/{transferId}.json,GET /v2.0/aboSingleTransfer/transferId/{transferId}.json,GET /v2.0/csvDownload/date/{date},GET /v2.0/aboDownload/date/{date}) - Extra Merchant API helpers for
refundPos,configand Apple Pay domain association - Comgate Status API integration (
GET https://status.comgate.cz/health,GET https://status.comgate.cz/outages) - Aggregated payment availability feedback for gateway health, allowed methods and outages
- Typed availability report and guarded payment creation helper
- Typed webhook notification DTO and payment-state events for
PAIDandCANCELLED - HTTP Basic authentication with merchant ID and secret
- Webhook endpoint with secret verification and required
code=0&message=OKresponse Comgate::fake()test double with recorded calls and configurable responses- Operation-specific exceptions for common Comgate API failures
Installation
composer require sloukapetr/comgate-laravel-package
For local package linking:
{
"repositories": [
{
"type": "path",
"url": "../comgate-laravel-package"
}
]
}
Laravel package discovery is enabled automatically.
Compatibility
- PHP:
^8.2 - Laravel:
11,12,13 - Package test matrix:
orchestra/testbench:^9|^10|^11
The Testbench range is intentional. It keeps package development aligned with the corresponding Laravel major versions instead of pinning local tests to only one framework branch.
Configuration
Publish the package config:
php artisan vendor:publish --tag=comgate-config
Configure .env values:
COMGATE_MERCHANT_ID=...
COMGATE_SECRET=...
COMGATE_TEST=true
COMGATE_BASE_URL=https://payments.comgate.cz/v2.0
COMGATE_TIMEOUT=30
COMGATE_STATUS_BASE_URL=https://status.comgate.cz
COMGATE_STATUS_TIMEOUT=30
COMGATE_WEBHOOK_ENABLED=true
COMGATE_WEBHOOK_PATH=/comgate/webhook
COMGATE_WEBHOOK_ROUTE_NAME=comgate.webhook
Recommended Laravel Use Cases
This package is a good fit when your Laravel app needs to own the payment lifecycle, not just send the customer to Comgate.
- Classic e-shop checkout with one-off payments and status tracking.
- Subscription or membership billing with an initial payment followed by recurring charges.
- Reservation or deposit flows where you first authorize a card payment and later capture or cancel it.
- Admin back office for refunds, cancellations, payment lookup and payout reconciliation.
- Internal payment links or order-payment screens where a merchant wants to control the business workflow in Laravel.
Typical pattern in the app:
- create an internal payment record first
- call Comgate to create the external payment
- store
transId,refId, method and expected amount - process webhook events idempotently
- update your order/subscription state from your own domain model
- expose admin actions for refund, cancel, capture and status refresh
Displaying the Payment Gateway
Comgate supports three main frontend integration styles. The best choice depends on how much control you want over the checkout UX.
REDIRECT
Use redirect when you want the simplest and most robust implementation.
- Customer is redirected from your app to Comgate and then back again.
- Lowest frontend complexity.
- Usually the safest choice for standard Laravel e-shops.
- Best when you want to keep payment UI logic outside your app.
Laravel recommendation:
- create the payment in your controller or service
- store the returned redirect URL
- send the user to that URL
- handle the return page as a UI-only confirmation
- rely on webhook +
status()for authoritative payment state
INLINE
Use inline when you want the gateway embedded in your checkout page.
- Gateway is rendered inside your cart or checkout page via iframe or popup.
- Better UX continuity than redirect.
- More frontend work and more browser/UI edge cases.
- Good if you need a more seamless checkout but still want the standard gateway flow.
Laravel recommendation:
- keep the payment orchestration in PHP
- let the frontend open the gateway container
- use the backend for payment creation and state updates
- keep webhook handling identical to redirect mode
Checkout SDK
Use Checkout SDK if you want the deepest checkout integration and the most polished in-app card/Apple Pay/Google Pay experience.
- Best for custom checkout experiences.
- Supports card payments, Apple Pay and Google Pay.
- Gives you the most control, but also the most implementation work.
- Well suited for products where the gateway should feel like part of the app rather than an external step.
Laravel recommendation:
- keep your Laravel app as the source of truth for payment state
- use the backend to create and verify payments
- use the frontend SDK only for payment UI and tokenization flow
- still process payment confirmation through webhook and status checks
Choosing Between Them
- Choose
REDIRECTif you want the fastest and least risky implementation. - Choose
INLINEif you want a branded checkout without a full SDK rollout. - Choose
Checkout SDKif you need the best UX and are willing to invest more frontend effort.
For most Laravel teams, the practical default is REDIRECT first, then INLINE, and only later Checkout SDK if you need the extra UX control.
Usage
use Sloukapetr\ComgateLaravelPackage\ComgateService;
$comgate = app(ComgateService::class);
$response = $comgate->createPayment([
'price' => 1000,
'curr' => 'CZK',
'label' => 'ORDER-1001',
'refId' => '1001',
'method' => 'ALL',
]);
$redirectUrl = $comgate->redirectUrl($response);
$redirect = $comgate->paymentRedirect([
'price' => 1000,
'curr' => 'CZK',
'label' => 'ORDER-1001',
'refId' => '1001',
'method' => 'ALL',
]);
Initial recurring payment
$init = $comgate->createInitialRecurringPayment([
'price' => 1000,
'curr' => 'CZK',
'label' => 'SUBSCRIPTION-INIT',
'refId' => 'SUB-42',
'method' => 'CARD_CZ_CS',
]);
Subsequent recurring charge
$charge = $comgate->recurringPayment([
'initRecurringId' => $init['transId'] ?? null,
'price' => 1000,
'curr' => 'CZK',
'label' => 'SUBSCRIPTION-RENEW',
'refId' => 'SUB-42-2026-09',
]);
For backward compatibility, the service also accepts initTransId and maps it to initRecurringId.
Pre-authorization
$preauth = $comgate->createPayment([
'price' => 1500,
'curr' => 'CZK',
'preauth' => true,
'label' => 'PREAUTH-ORDER-1',
]);
$capture = $comgate->capture([
'transId' => $preauth['transId'] ?? null,
]);
$cancel = $comgate->cancelPreauth([
'transId' => $preauth['transId'] ?? null,
]);
Refund, cancellation and status
$refund = $comgate->refund([
'transId' => 'YOUR_TRANSACTION_ID',
'amount' => 500,
]);
$cancelPayment = $comgate->cancelPayment([
'transId' => 'YOUR_PENDING_TRANSACTION_ID',
]);
$status = $comgate->status([
'transId' => 'YOUR_TRANSACTION_ID',
]);
$transferList = $comgate->transferList('2025-04-25');
$singleTransfer = $comgate->singleTransfer('1234567');
Available payment methods
$methods = $comgate->methods([
'country' => 'CZ',
'curr' => 'CZK',
'price' => 1000,
'lang' => 'cs',
'initRecurring' => true,
]);
Gateway health and outages
$health = $comgate->gatewayHealth();
$outages = $comgate->outages();
Payment availability feedback
Use this when you want a higher-level answer than raw /methods or raw status data.
$availability = $comgate->paymentAvailability(
[
'country' => 'CZ',
'curr' => 'CZK',
'price' => 1000,
'initRecurring' => true,
],
['CARD_CZ_CSOB_2', 'BANK_CZ_KB']
);
if (! $availability['available']) {
$summary = $availability['summary'];
$reasons = $availability['reasons'];
$usableMethods = $availability['usableMethods'];
}
Returned report includes:
available: final yes/no decisionsummary: short human-readable outcomegateway: raw health payload from Status APIavailableMethods: methods allowed by Merchant API filtersusableMethods: methods still usable after outage filteringmissingRequiredMethods: requested methods not currently available for the provided filtersoutages: only outages relevant to the checked methodschecksandreasons: machine-friendly and human-friendly diagnostics
For stricter application flow, you can use the typed report or block payment creation up front.
$report = $comgate->paymentAvailabilityReport(
['country' => 'CZ', 'curr' => 'CZK', 'price' => 1000],
['CARD_CZ_CSOB_2']
);
if (! $report->isAvailable()) {
$summaryKey = $report->summaryKey;
$localizedSummary = $report->summaryFor('cs');
$reasonCodes = $report->reasonCodes;
$reasonMessages = $report->reasonMessages('cs');
}
use Sloukapetr\ComgateLaravelPackage\Exceptions\PaymentUnavailableException;
try {
$response = $comgate->createPaymentChecked([
'country' => 'CZ',
'price' => 1000,
'curr' => 'CZK',
'label' => 'ORDER-1001',
'refId' => '1001',
'method' => 'CARD_CZ_CSOB_2',
]);
} catch (PaymentUnavailableException $exception) {
$availability = $exception->availability();
}
cancel() remains available as a backward-compatible alias for pre-authorization cancellation.
Exceptions
The package throws ComgateRequestException by default and narrows a few common API cases into dedicated exceptions:
MissingParameterExceptionfor API code1400on create, recurring, refund and preauth callsPaymentNotFoundExceptionfor API code1400on status lookupsPreauthFailedExceptionfor API code1401on pre-authorization capture/cancel calls
Subscription management
Comgate handles the payment tokenization flow for recurring card charges, but the subscription lifecycle remains in your Laravel application.
Typical Laravel design:
- store the initial recurring
transIdper user after the first successful payment - schedule renewals in your app and call
recurringPayment()with a newrefId - process webhook updates to mark payments as paid, cancelled or failed
- issue
refund()only for paid transactions andcancelPayment()only for pending transactions
Admin Payment Overview
If you are building an admin panel, keep a local payment overview table or screen. That makes refunds, cancellations and subscription maintenance much easier.
Useful columns and fields:
transId,refId,method,curr,price,status- relation to order, customer or subscription
- timestamps for created, paid, cancelled, refunded and last webhook
- internal processing flags such as
refunded,captured,processed_at
Useful admin actions:
- view payment detail and raw webhook history
- refresh status from Comgate
- create refund for paid payments
- cancel pending payments
- capture authorized preauth payments
- navigate to related order or subscription detail
Recommended admin rules:
- only allow refund/cancel actions when the current state matches the Comgate rules
- write every admin action to an audit log
- treat webhook events as the trigger for state changes, not the email notifications
- make all listeners idempotent so retries do not duplicate side effects
Webhooks
When enabled, the package registers a POST webhook route (/comgate/webhook by default).
- Secret is validated from
X-Comgate-Secretheader orsecretpayload field. - On success, the package dispatches
Sloukapetr\ComgateLaravelPackage\Events\ComgateWebhookReceived. - For
PAIDnotifications, it also dispatchesSloukapetr\ComgateLaravelPackage\Events\ComgatePaymentPaid. - For
CANCELLEDnotifications, it also dispatchesSloukapetr\ComgateLaravelPackage\Events\ComgatePaymentFailed. - The event exposes both raw payload and a typed
notificationDTO with helpers likeisPaid()andisCancelled(), plus typed status fields such ascardNumber,appliedFeeType,threeDSPreferenceandthreeDSApplied. - The DTO also exposes
getPriceInMainUnit()andgetFormattedPriceAttribute()to convert Comgate cents/haléře into the main currency unit. - Response is always
code=0&message=OKfor valid webhook calls.
use Sloukapetr\ComgateLaravelPackage\Events\ComgateWebhookReceived;
class HandleComgateWebhook
{
public function handle(ComgateWebhookReceived $event): void
{
// Keep this listener idempotent: Comgate can retry webhook delivery.
if ($this->alreadyProcessed($event->notification->transId)) {
return;
}
if ($event->notification->isPaid()) {
$transactionId = $event->notification->transId;
$referenceId = $event->notification->refId;
// mark order or subscription renewal as paid
}
}
private function alreadyProcessed(?string $transactionId): bool
{
return false;
}
}
Facade
use Comgate;
$response = Comgate::status(['transId' => 'YOUR_TRANSACTION_ID']);
$fake = Comgate::fake([
'status' => ['code' => 0, 'message' => 'OK', 'transId' => 'TX-001', 'status' => 'PAID'],
]);
Official API Reference
Testing
composer test
The package includes HTTP-layer tests using Laravel Http::fake() and Testbench for webhook route coverage.
The facade fake helper can be used in application tests when you want to avoid HTTP calls and assert recorded Comgate operations.
Release
See RELEASE_CHECKLIST.md for the package release checklist.