Looking to hire Laravel developers? Try LaraJobs

laravel maintained by ifthenpay

Description
Laravel package for the ifthenpay Payment Gateway (Multibanco, MB WAY, Payshop, Credit Card, Pix, Pay by Link).
Last update
2026/08/04 16:26 (dev-main)
License
Downloads
0

Comments
comments powered by Disqus

ifthenpay/laravel

Laravel package for ifthenpay payment gateway that facilitates payment generation with Portuguese payment methods and others.

Table of Contents

Requirements

(working on backwards compatibility for Laravel 11 and 12)

  • PHP ^8.3
  • Laravel ^13.0

Installation

composer require ifthenpay/laravel

Publish the config file:

php artisan vendor:publish --tag=ifthenpay-config

Run the migrations (creates the ifthenpay_payments table used for optional persistence, loaded automatically by the package):

php artisan migrate

Configure the package by adding these keys to your .env file. The following is an example of how you would fully configure the package.

IFTHENPAY_BACKOFFICE_KEY=0000-0000-0000-0000
IFTHENPAY_MULTIBANCO_KEY=ITP-000000
IFTHENPAY_MBWAY_KEY=ITP-000000
IFTHENPAY_PAYSHOP_KEY=ITP-000000
IFTHENPAY_CREDITCARD_KEY=ITP-000000
IFTHENPAY_PIX_KEY=ITP-000000
IFTHENPAY_PAYBYLINK_KEY=ITPG-000000
IFTHENPAY_PAYBYLINK_METHODS=MULTIBANCO|ITP-000000;MBWAY|ITP-000000;PAYSHOP|ITP-000000;CREDITCARD|ITP-000000;PIX|ITP-000000;GOOGLE|ITP-000000;APPLE|ITP-000000
IFTHENPAY_PAYBYLINK_DEFAULT_METHOD=GOOGLE
IFTHENPAY_ANTIPHISHING_KEY=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
IFTHENPAY_WEBHOOK_PATH=ifthenpay/webhook
IFTHENPAY_WEBHOOK_RATE_LIMIT_PER_MINUTE=60

Configure only what you need, use the table below for reference.

configuration purpose example required for
IFTHENPAY_BACKOFFICE_KEY Authenticates the webhook registration call 0000-0000-0000-0000 Registering webhooks
IFTHENPAY_MULTIBANCO_KEY Multibanco key — dynamic (online) or entity-subentity (offline) (online) ITP-000000 or (offline) 00000-000 Generating Multibanco payments
IFTHENPAY_MBWAY_KEY MB WAY key ITP-000000 Generating MB WAY payments
IFTHENPAY_PAYSHOP_KEY Payshop key ITP-000000 Generating Payshop payments
IFTHENPAY_CREDITCARD_KEY Credit Card key ITP-000000 Generating Credit Card payments
IFTHENPAY_PIX_KEY Pix key ITP-000000 Generating Pix payments
IFTHENPAY_PAYBYLINK_KEY Pay-by-link key ITPG-000000 Generating Pay by Link payments
IFTHENPAY_PAYBYLINK_METHODS Method|key pairs, separated by ;, used to define what methods to display in the gateway page GOOGLE|ITP-000000;APPLE|ITP-000000 Generating Pay by Link payments
IFTHENPAY_PAYBYLINK_DEFAULT_METHOD Sets preselected method on the gateway page GOOGLE Generating Pay by Link payments
IFTHENPAY_ANTIPHISHING_KEY Validated against incoming webhook requests aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Registering webhooks; updating payment status through webhook
IFTHENPAY_WEBHOOK_PATH Relative path used to register the webhook URL (defaults to ifthenpay/webhook) ifthenpay/webhook Receiving webhooks
IFTHENPAY_WEBHOOK_RATE_LIMIT_PER_MINUTE Requests per minute allowed on the webhook route middleware (defaults to 60) 60 Receiving webhooks

Usage

Generating Payments

There are two ways to generate payments:

Using the facade This is the recommended approach, but keep in mind it requires you to configure the payment method keys you'll be using.

use Ifthenpay\Laravel\Facades\Ifthenpay;

$paymentResponse = Ifthenpay::multibanco()->generatePayment(
    orderId: 'order-1887',
    amount: '10.99',
    description: 'Order #1887', // optional
    expiryDays: 3,               // optional
);

Direct instantiation In this approach you are responsible for configuring and instantiating the gateway object

use Ifthenpay\Laravel\Gateways\MultibancoGateway;

$multibancoGateway = new MultibancoGateway(['multibanco_key' => 'ITP-000000']);

$multibancoGateway->generatePayment(
    orderId: 'order-1887',
    amount: '10.99',
    description: 'Order #1887', // optional
    expiryDays: 3,               // optional
);

Given the payment method type, the generatePayment() method will expect different parameters.

// ifthenpay/laravel-ifthenpay/src/Gateways/MultibancoGateway.php
public function generatePayment(string $orderId, string $amount, ?string $description = null, ?int $expiryDays = null): MultibancoResponse

// ifthenpay/laravel-ifthenpay/src/Gateways/MbwayGateway.php
public function generatePayment(string $orderId, string $amount, string $mobileNumber, ?string $description = null, ?string $email = null, ?int $expiryMinutes = null): MbwayResponse

// ifthenpay/laravel-ifthenpay/src/Gateways/PayshopGateway.php
public function generatePayment(string $orderId, string $amount, ?int $expiryDays = null): PayshopResponse

// ifthenpay/laravel-ifthenpay/src/Gateways/CreditcardGateway.php
public function generatePayment(string $orderId, string $amount, string $successUrl, string $errorUrl, string $cancelUrl, string $language = 'en', ?int $expiryMinutes = null): CreditcardResponse

// ifthenpay/laravel-ifthenpay/src/Gateways/PixGateway.php
public function generatePayment(string $orderId, string $amount, string $customerCpf, string $customerName, string $customerEmail, string $customerPhone, string $redirectUrl, ?string $description = null, ?string $customerAddress = null, ?string $customerStreetNumber = null, ?string $customerCity = null, ?string $customerZipCode = null, ?string $customerState = null, ?int $expiryMinutes = null): PixResponse

// ifthenpay/laravel-ifthenpay/src/Gateways/PaybylinkGateway.php
public function generatePayment(string $orderId, string $amount, string $successUrl, string $errorUrl, string $cancelUrl, ?string $description = null, ?int $expiryDays = null, bool $otp = false, string $lang = 'pt', ?string $btnCloseUrl = null, ?string $btnCloseLabel = null): PaybylinkResponse

But their return will always be an implementation of IfthenpayResponse interface.

// ifthenpay/laravel-ifthenpay/src/Contracts/IfthenpayResponse.php
public function isSuccessful(): bool;   // result of payment generation
public function getMessage(): ?string;  // error message, in case of failure
public function toArray(): array;       // response payload with keys prepared for mass-assignment to ifthenpay_payments model

Multibanco

use Ifthenpay\Laravel\Facades\Ifthenpay;
use Ifthenpay\Laravel\Models\Payment;

$multibancoResponse = Ifthenpay::multibanco()->generatePayment(
    orderId: '1000',
    amount: '10.99',
    description: 'Order #1887', // optional
    expiryDays: 3,               // optional
);

if(!$multibancoResponse->isSuccessful()) {
	throw new Exception("Error Generating Payment", 1);
}

// store payment or another action
Payment::create($response->toArray());

MB WAY

use Ifthenpay\Laravel\Facades\Ifthenpay;
use Ifthenpay\Laravel\Models\Payment;

$mbwayResponse = Ifthenpay::mbway()->generatePayment(
    orderId: '1000',
    amount: '10.99',
    mobileNumber: '351#912345678',
    description: 'Order #1887', // optional
    email: 'jane@example.com',  // optional
    expiryMinutes: 10,          // optional
);

if(!$mbwayResponse->isSuccessful()) {
	throw new Exception("Error Generating Payment", 1);
}

// store payment or another action
Payment::create($mbwayResponse->toArray());

Payshop

use Ifthenpay\Laravel\Facades\Ifthenpay;
use Ifthenpay\Laravel\Models\Payment;

$payshopResponse = Ifthenpay::payshop()->generatePayment(
    orderId: '1000',
    amount: '10.99',
    expiryDays: 5, // optional
);

if(!$payshopResponse->isSuccessful()) {
	throw new Exception("Error Generating Payment", 1);
}

// store payment or another action
Payment::create($payshopResponse->toArray());

Credit Card

use Ifthenpay\Laravel\Facades\Ifthenpay;
use Ifthenpay\Laravel\Models\Payment;

$creditcardResponse = Ifthenpay::creditcard()->generatePayment(
    orderId: '1000',
    amount: '10.99',
    successUrl: 'https://example.com/success',
    errorUrl: 'https://example.com/error',
    cancelUrl: 'https://example.com/cancel',
    language: 'en',    // optional
    expiryMinutes: 15, // optional
);

if(!$creditcardResponse->isSuccessful()) {
	throw new Exception("Error Generating Payment", 1);
}

// store payment or another action
Payment::create($creditcardResponse->toArray());

Pix

use Ifthenpay\Laravel\Facades\Ifthenpay;
use Ifthenpay\Laravel\Models\Payment;

$pixResponse = Ifthenpay::pix()->generatePayment(
    orderId: '1000',
    amount: '10.99',
    customerCpf: '123.456.789-00',
    customerName: 'John Doe',
    customerEmail: 'john@example.com',
    customerPhone: '351912345678',
    redirectUrl: 'https://example.com/redirect',
    description: 'Order #1887',     // optional
    customerAddress: 'Rua Exemplo', // optional
    customerStreetNumber: '10',     // optional
    customerCity: 'Lisboa',         // optional
    customerZipCode: '1000-001',    // optional
    customerState: 'Lisboa',        // optional
    expiryMinutes: 15,              // optional
);

if(!$pixResponse->isSuccessful()) {
	throw new Exception("Error Generating Payment", 1);
}

// store payment or another action
Payment::create($pixResponse->toArray());

Pay by Link

use Ifthenpay\Laravel\Facades\Ifthenpay;
use Ifthenpay\Laravel\Models\Payment;

$paybylinkResponse = Ifthenpay::paybylink()->generatePayment(
    orderId: '1000',
    amount: '10.99',
    successUrl: 'https://example.com/success',
    errorUrl: 'https://example.com/error',
    cancelUrl: 'https://example.com/cancel',
    description: 'Order #1887',               // optional
    expiryDays: 5,                             // optional
    otp: false,                                // optional
    lang: 'pt',                                // optional
    btnCloseUrl: 'https://example.com/close',  // optional
    btnCloseLabel: 'Close',                    // optional
);

if(!$paybylinkResponse->isSuccessful()) {
	throw new Exception("Error Generating Payment", 1);
}

// store payment or another action
Payment::create($paybylinkResponse->toArray());

Persisting payments

If you published and ran the migration for this package, you can use the Payment model Ifthenpay\Laravel\Models\Payment to persist the payments in your database

You can store a payment separately right after generating it:

use Ifthenpay\Laravel\Facades\Ifthenpay;
use Ifthenpay\Laravel\Models\Payment;

$multibancoResponse = Ifthenpay::multibanco()->generatePayment(orderId: 'order-1887', amount: '10.99', expiryDays: 3);

Payment::create($multibancoResponse->toArray());

Or add the HasIfthenpayPayments trait to any Eloquent model (e.g. your Order) to generate and persist a payment linked to it in one line.

// app/Models/Order.php
use Ifthenpay\Laravel\Traits\HasIfthenpayPayments;

class Order extends Model
{
    use HasIfthenpayPayments;
}
// app/Http/Controllers/YourController.php
use App\Models\Order;

$order = Order::find(1);
$payment = $order->createMbwayPayment(orderId: 'order-1887', amount: '10.99', mobileNumber: '351#912345678');

$orderPayments = $order->payments;          // all payments for this order (MorphMany)
$orderMbwayPayments = $order->mbwayPayments;     // only its MB WAY payments

Equivalent methods to createMbwayPayment() and mbwayPayments() exist for every payment method.

Payment method Method Eloquent relation
Multibanco createMultibancoPayment() multibancoPayments()
MB WAY createMbwayPayment() mbwayPayments()
Payshop createPayshopPayment() payshopPayments()
Pix createPixPayment() pixPayments()
Credit Card createCreditcardPayment() creditcardPayments()
Pay by Link createPaybylinkPayment() paybylinkPayments()

The payment model also exposes the following methods

Method Description
isPaid() check if payment record has paid status, returns true if payment status is paid (Enum of PaymentStatus)
secondsToExpire() returns seconds remaining until expires_at, or 0 if already past/unset
totalSecondsToExpire() returns total seconds between created_at and expires_at, or 0 if either is unset, useful if you want to create a progress bar like blade component of the remaining time a user has to complete the payment
markAsPaid() sets status to paid and stamps paid_at (if not already set)
markAsCanceled() sets status to canceled and stamps canceled_at
markAsExpired() sets status to expired
scopePending() query scope for payments with status pending
scopePaid() query scope for payments with status paid
scopeCanceled() query scope for payments with status canceled
scopeExpired() query scope for pending payments whose expires_at has passed
scopeByMultibanco() query scope for payments generated via Multibanco
scopeByMbway() query scope for payments generated via MB WAY
scopeByPayshop() query scope for payments generated via Payshop
scopeByPix() query scope for payments generated via Pix
scopeByCreditcard() query scope for payments generated via Credit Card
scopeByPaybylink() query scope for payments generated via Pay by Link

Registering webhooks

Register your callback URL with ifthenpay via the ifthenpay:register-webhook command. Note: If IFTHENPAY_ANTIPHISHING_KEY isn't set yet, the command offers to generate and save one for you.

With no options (recommended), it registers a webhook for every payment method you have a key configured for (including every method bundled into paybylink_methods):

php artisan ifthenpay:register-webhook

Or register a single method explicitly, if you are not using the config to load the method keys and are instantiating the gateway classes directly:

php artisan ifthenpay:register-webhook --method=mbway --key=ITP-000000

By default this command registers the named route ifthenpay.webhook as the url, but you can use --url to override it and set your own route, just be sure to pass an absolute path without any querystrings.

php artisan ifthenpay:register-webhook --method=mbway --key=ITP-000000 --url="https://yoururl.com"

If you need to add more parameters in the webhook, you must add them with --params

php artisan ifthenpay:register-webhook --method=mbway --key=ITP-000000 --url="https://yoururl.com" --params="scope=01"

Warning: If the same key is used both standalone and inside paybylink_methods (e.g. IFTHENPAY_MULTIBANCO_KEY=ITP-000000 and IFTHENPAY_PAYBYLINK_METHODS=MULTIBANCO|ITP-000000;...), running the command with no options registers a webhook for that key twice — once for the standalone method, once for paybylink — and whichever registration ifthenpay processes last wins, silently overriding the other's callback URL. This is easy to hit by accident since paybylink_methods commonly reuses the same keys configured for the individual methods. Avoid reusing a key across a standalone method and paybylink_methods if they need different callback URLs, or re-run the command for the affected method/key after registering all methods to make sure the URL you want ends up as the last one registered.

Receiving webhooks

By default the package registers a route at the path configured by ifthenpay.webhook_path (default ifthenpay/webhook), protected by the api middleware group and a named rate limiter (ifthenpay-webhook, default 60/minute — configurable via IFTHENPAY_WEBHOOK_RATE_LIMIT_PER_MINUTE).

To fully customize or disable this route (e.g. to point it at your own controller), publish the routes file into your app:

php artisan vendor:publish --tag=ifthenpay-routes

Once routes/ifthenpay.php exists in your application, the package loads that file instead of its own — edit, replace, or empty it as needed.

Incoming requests are validated (required pm/apk/val/oid/ref/req query params, apk checked against IFTHENPAY_ANTIPHISHING_KEY), matched to a stored Payment by method-specific fields, and amount-checked in cents to avoid float issues. On success the matching payment is marked paid; a negative amount is treated as a refund and leaves the payment's status untouched. Every outcome fires an event you can listen for — see Payment Webhook Confirmed, Refunded, and Rejected below.

Expiring payments

All payment methods can be attributed an expiration, but only Multibanco(online), Payshop, Pay-by-link can use it to block access to payment after that time expires, meaning after that payment has expired, the user will not be able to pay (Multibanco and Payshop) or access the gateway page (Pay-by-link).

This functionality can still be used to manage abandoned payments for all payment methods, so that you can update the payments that are pending to expired. To use this functionality you need to pass the expiration (varies by payment method: days or minutes) when generating the payment.

The command ifthenpay:payments:expire, marks pending payments as expired once expires_at has passed and fires an event Ifthenpay\Laravel\Events\PaymentExpired for each (--dry-run lists them without changing anything). Schedule it yourself in routes/console.php, and set the frequency you like.

// /routes/console.php
use Illuminate\Support\Facades\Schedule;

Schedule::command('ifthenpay:payments:expire')->hourly();

If you want to add expiration to payments you may use this table as suggestion

Payment method Suggested expiration
Multibanco 3 days
MB WAY 4 minutes
Payshop 3 days
Credit Card 15 minutes
Pix 5 minutes
Pay by Link 1 day

Method Specific Utilities

You can make use of these utilities if you want to change/improve the user experience, but are optional and you can still implement the methods without relying on them.

MB WAY check status

MB WAY payments can be actively polled for status via checkStatus(), in addition to being updated by webhook. It requires the Payment's request_id (returned by ifthenpay when the payment was generated) to be set. This functionality is useful when implementing a countdown timer that shows feedback about the status to the user.

use Ifthenpay\Laravel\Facades\Ifthenpay;
use Ifthenpay\Laravel\Models\Payment;

$payment = Payment::find(1);

$statusResponse = Ifthenpay::mbway()->checkStatus($payment);

if ($statusResponse->isSuccessful()) {
    $payment->markAsPaid();
}

checkStatus() returns a MbwayStatusResponse with a typed status (Ifthenpay\Laravel\Enums\MbwayStatus: PENDING, PAID, EXPIRED, REFUSED_BY_USER, REJECTED_BY_MBWAY) and a message. isSuccessful() only returns true when status is MbwayStatus::PAID.

// ifthenpay/laravel-ifthenpay/src/Responses/MbwayStatusResponse.php
public function __construct(
    public ?string $message,
    public MbwayStatus $status,
) {}

Credit Card verify return

When generating a credit card payment, you'll get a Credit Card gateway page url in the response, to which you will redirect the user, upon being redirected back (to successUrl, errorUrl, or cancelUrl), ifthenpay appends a sk (secret key) query parameter. Use verifyPayment() to confirm the redirect is authentic before trusting it — e.g. before showing a success page. You can also ignore this verification system and rely solely on the webhook.

use Ifthenpay\Laravel\Exceptions\IfthenpayException;
use Ifthenpay\Laravel\Facades\Ifthenpay;
use Ifthenpay\Laravel\Models\Payment;

$payment = Payment::where('order_id', request('orderId'))->firstOrFail();

try {
    Ifthenpay::creditcard()->verifyPayment(secretKey: request('sk'), payment: $payment);
} catch (IfthenpayException $e) {
    abort(403, 'Invalid payment return.');
}

// verification passed, safe to treat as returning from a genuine ifthenpay redirect

verifyPayment() returns void on success and throws IfthenpayException (secret key mismatch) on failure — it does not return a response object like the other gateway methods.

Blade Components

The package registers its views under the ifthenpay namespace, so its Blade components are available anywhere in your app as <x-ifthenpay::*>, no publishing required.

All of them are styled with Tailwind CSS v4 utility classes (including dark: variants for dark mode) and ship no CSS of their own — they render correctly out of the box only in an app that already has Tailwind set up. See Customizing the components below if your app doesn't use Tailwind, or if you just want a different look.

<x-ifthenpay::payment-details>

Renders a card summarizing a Payment model, dispatching to the right method-specific partial (below) based on $payment->method.

<x-ifthenpay::payment-details :payment="$payment" />

{{-- show a status badge (pending/paid/canceled/expired/failed) alongside the amount --}}
<x-ifthenpay::payment-details :payment="$payment" :show-status="true" />
Prop Type Default Purpose
payment Ifthenpay\Laravel\Models\Payment The payment to render.
showStatus bool false Show a colored status badge next to the amount.

Any attributes you pass through (e.g. class="...") are merged onto the root <div>.

Method-specific partials

payment-details renders one of these internally based on $payment->method, but each can also be used standalone if you're building your own layout — they all just take a :payment prop:

Component Method Renders
<x-ifthenpay::payment-details-multibanco> Multibanco Entity/reference, and expiry date if expires_at is set.
<x-ifthenpay::payment-details-mbway> MB WAY Instructions to confirm in-app, plus a live countdown bar to expires_at (vanilla JS, no Alpine/Livewire dependency).
<x-ifthenpay::payment-details-payshop> Payshop Reference to pay at a Payshop agent/CTT store, and expiry date if set.
<x-ifthenpay::payment-details-credit-card> Credit Card A link to payment_url (same tab, so the gateway's redirect back to successUrl/errorUrl/cancelUrl lands where the user started).
<x-ifthenpay::payment-details-pix> Pix A link to payment_url (same tab, so the gateway's redirect back to redirectUrl lands where the user started).
<x-ifthenpay::payment-details-pay-by-link> Pay by Link A link to payment_url (same tab).

Each partial no-ops (renders nothing) if the field it needs (entity/reference/payment_url) isn't set on the payment yet.

Customizing the components

These components are bland and generic, because they are meant as a base for what you may want to display to your user. Publish the views to get an editable copy in your own app and customize it to your needs:

php artisan vendor:publish --tag=ifthenpay-views

This copies every .blade.php file (payment-details and all six method-specific partials) into resources/views/vendor/ifthenpay/components/. Laravel prefers published views over the package's own, so once they exist there, editing them (markup, copy, or swapping the Tailwind classes for your own CSS/framework) is picked up automatically by every <x-ifthenpay::*> tag already in use — no need to change any of your existing usages.

If you keep the Tailwind classes, make sure your resources/views/vendor/ifthenpay/** directory is covered by Tailwind's @source/content scanning (Tailwind v4's automatic content detection already covers resources/views by default, so this is usually a non-issue unless you publish elsewhere or use a custom Tailwind config).

Events

The package fires typed events for webhook outcomes and payment expiry. Every event implements Ifthenpay\Laravel\Contracts\LoggableEvent (logLevel(), logMessage(), logContext()), which is what the bundled LogIfthenpayEvent listener relies on.

Payment Expired

Fired by the ifthenpay:payments:expire command for each payment it marks as expired (see Expiring payments).

use Ifthenpay\Laravel\Events\PaymentExpired;
use Illuminate\Support\Facades\Event;

Event::listen(function (PaymentExpired $event) {
    // $event->payment — the Payment model instance that was just marked as expired
});

Payment Webhook Confirmed

Fired when an incoming webhook passes validation with a positive amount matching the stored payment. The payment has already been marked as paid by the time this fires.

use Ifthenpay\Laravel\Events\PaymentWebhookConfirmed;
use Illuminate\Support\Facades\Event;

Event::listen(function (PaymentWebhookConfirmed $event) {
    // $event->payload — Ifthenpay\Laravel\DTO\WebhookPayload: method, orderId, reference, requestId, amount
});

Payment Webhook Refunded

Fired when an incoming webhook carries a negative amount. The matching payment's status is left untouched. This packaged does not have a refund feature, but you can make use of this event to implement it yourself. Check the official documentation on how to implement the refund endpoint LINK

use Ifthenpay\Laravel\Events\PaymentWebhookRefunded;
use Illuminate\Support\Facades\Event;

Event::listen(function (PaymentWebhookRefunded $event) {
    // $event->payload — Ifthenpay\Laravel\DTO\WebhookPayload: method, orderId, reference, requestId, amount
});

Payment Webhook Rejected

Fired when an incoming webhook fails validation — missing/invalid query params, an anti-phishing key mismatch, no matching payment, or an amount mismatch. The HTTP response is always a generic 400 regardless of reason, so a caller can't learn which check failed; the reason is only available to your listener.

use Ifthenpay\Laravel\Events\PaymentWebhookRejected;
use Illuminate\Support\Facades\Event;

Event::listen(function (PaymentWebhookRejected $event) {
    // $event->payload — Ifthenpay\Laravel\DTO\WebhookPayload, built defensively from the raw (possibly invalid) query
    // $event->reason  — internal string explaining why the webhook was rejected
});

Log

Optionaly, you can log the webhook events by just using the event listener LogIfthenpayEvent included in this package to enable logging for the four events mentioned above. To enable it, just add this line in the your AppServiceProvider.

// app/Providers/AppServiceProvider.php
use Ifthenpay\Laravel\Contracts\LoggableEvent;
use Ifthenpay\Laravel\Listeners\LogIfthenpayEvent;
use Illuminate\Support\Facades\Event;

Event::listen(LoggableEvent::class, LogIfthenpayEvent::class);

This log never includes secrets (e.g. the webhook's anti-phishing key is stripped before logging). It reports method, order_id, reference, request_id, amount for webhook events, or id, method, order_id, expires_at, status for PaymentExpired.

License

MIT.