Integrate with vexpay/laravel
Laravel 12/13 package: facade, verified webhooks as events, Billable checkouts with a synced payment record, marketplace sellers, Blade checkout, and test fakes.
Install
composer require vexpay/laravel
php artisan vexpay:install
php artisan migrate| Requirement | Supported |
|---|---|
| Laravel | 12 or 13 (every release still receiving security fixes) |
| PHP | 8.2 or later (Laravel 13 needs 8.3) |
| HTTP client | The Guzzle 7 or 8 your app already has |
| Package | vexpay/laravel on Packagist (packagist.org/packages/vexpay/laravel); installs vexpay/vexpay-php |
VEXPAY_API_KEY=vk_test_replace_me
VEXPAY_WEBHOOK_SECRET=whsec_replace_meRegister https://your-app.example/vexpay/webhook as a webhook endpoint in the portal, then check delivery with php artisan vexpay:webhook-test. The VexPay facade (or an injected VexPay\VexPayClient) exposes the full PHP SDK.
Accept payments with Billable
use VexPay\Laravel\Billable;
class Order extends Model
{
use Billable;
}
// In a controller: redirects (303) to the hosted checkout, or returns
// { id, url, clientSecret } for JSON requests.
return $order->checkout($order->total_usd, [
'description' => "Pedido #{$order->number}",
'successUrl' => route('orders.thanks', $order),
]);checkout() creates a checkout session and stores a PENDING row in vexpay_payments linked to the model. Embed instead of redirecting with <x-vexpay-checkout :checkout="$checkout" />, which sends only the session client secret to the browser.
Fulfill from billing events
Verified payment.* webhooks update the local record. Status only moves forward (PENDING → COMPLETED / FAILED / CANCELED, COMPLETED → REVERSED), so retries and out-of-order deliveries are harmless and billing events fire exactly once per transition.
use VexPay\Laravel\Events\Billing\PaymentSucceeded;
Event::listen(function (PaymentSucceeded $event) {
$event->billable()->markPaid($event->payment->payment_id);
});Marketplace sellers
use VexPay\Laravel\HasVexPayMerchant;
class Seller extends Model
{
use HasVexPayMerchant;
}
$seller->createAsVexPayMerchant([
'name' => 'María Pérez',
'identification' => 'V12345678',
'contactEmail' => 'maria@example.com',
'contactPhone' => '04141234567',
]);
$seller->payout(1523.40, 'Venta #991', 'order-991'); // same reference never pays twiceTesting
use VexPay\Laravel\Facades\VexPay;
use VexPay\Laravel\Testing\InteractsWithVexPay;
VexPay::fake();
$this->post(route('orders.pay', $order))->assertRedirect();
VexPay::assertCheckoutCreated(25.00);
$this->postVexPayPaymentWebhook($order->vexpayPayments()->first(), 'payment.completed')
->assertNoContent();