Skip to Content
FrameworksLaravel

Laravel

QPay V2 payment integration for Laravel 10+. Built on top of qpay-php , this package provides a service provider, Facade, Blade components, webhook controller, and Laravel events.


Installation

composer require qpay-sdk/laravel

Run the install command to publish the config file:

php artisan qpay:install

This publishes config/qpay.php and displays the required environment variables.

Manual Setup (Optional)

If you prefer not to use the install command, publish the config manually:

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

To publish the Blade view templates for customization:

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

The service provider and Facade alias are auto-discovered via composer.json — no manual registration needed.


Configuration

Environment Variables

Add the following to your .env file:

QPAY_BASE_URL=https://merchant.qpay.mn QPAY_USERNAME=your_username QPAY_PASSWORD=your_password QPAY_INVOICE_CODE=your_invoice_code QPAY_CALLBACK_URL=https://yoursite.com/qpay/webhook

Config File

The published config/qpay.php file:

return [ 'base_url' => env('QPAY_BASE_URL', 'https://merchant.qpay.mn'), 'username' => env('QPAY_USERNAME'), 'password' => env('QPAY_PASSWORD'), 'invoice_code' => env('QPAY_INVOICE_CODE'), 'callback_url' => env('QPAY_CALLBACK_URL'), 'webhook_path' => env('QPAY_WEBHOOK_PATH', '/qpay/webhook'), ];
KeyEnv VariableDefaultDescription
base_urlQPAY_BASE_URLhttps://merchant.qpay.mnQPay API base URL
usernameQPAY_USERNAMEMerchant username
passwordQPAY_PASSWORDMerchant password
invoice_codeQPAY_INVOICE_CODEDefault invoice code
callback_urlQPAY_CALLBACK_URLPayment callback URL
webhook_pathQPAY_WEBHOOK_PATH/qpay/webhookWebhook endpoint path

Quick Start

use QPay\Laravel\Facades\QPay; use QPay\Models\CreateSimpleInvoiceRequest; // Create an invoice $invoice = QPay::createSimpleInvoice(new CreateSimpleInvoiceRequest( invoiceCode: config('qpay.invoice_code'), senderInvoiceNo: 'ORDER-001', amount: 10000, callbackUrl: config('qpay.callback_url'), )); // $invoice->invoiceId -- QPay invoice ID // $invoice->qrImage -- Base64-encoded QR code image // $invoice->qrText -- QR code text for rendering // $invoice->qPayShortUrl -- Short URL for payment // $invoice->urls -- Array of bank deep links

Create Invoice

Simple Invoice

use QPay\Laravel\Facades\QPay; use QPay\Models\CreateSimpleInvoiceRequest; $invoice = QPay::createSimpleInvoice(new CreateSimpleInvoiceRequest( invoiceCode: config('qpay.invoice_code'), senderInvoiceNo: 'ORDER-' . $order->id, amount: $order->total, callbackUrl: config('qpay.callback_url'), ));

Full Invoice (Advanced)

use QPay\Laravel\Facades\QPay; use QPay\Models\CreateInvoiceRequest; $invoice = QPay::createInvoice(new CreateInvoiceRequest( invoiceCode: config('qpay.invoice_code'), senderInvoiceNo: 'ORDER-' . $order->id, invoiceReceiverCode: 'receiver_001', invoiceDescription: 'Payment for Order #' . $order->id, amount: $order->total, callbackUrl: config('qpay.callback_url'), ));

Cancel Invoice

QPay::cancelInvoice($invoiceId);

Check Payment Status

use QPay\Models\PaymentCheckRequest; $result = QPay::checkPayment(new PaymentCheckRequest( objectType: 'INVOICE', objectId: $invoiceId, )); if (count($result->rows) > 0) { // Payment confirmed }

List Payments

use QPay\Models\PaymentListRequest; $payments = QPay::listPayments(new PaymentListRequest( objectType: 'INVOICE', objectId: $invoiceId, ));

Get Payment Detail

$payment = QPay::getPayment($paymentId);

Webhook Handling

The package automatically registers a webhook route at the configured path (default: /qpay/webhook). The route is excluded from CSRF verification.

When QPay sends a callback, the webhook controller:

  1. Extracts the invoice_id from the request body
  2. Calls checkPayment to verify the payment
  3. Dispatches a PaymentReceived event if payment rows are found
  4. Dispatches a PaymentFailed event if no payment is found or an error occurs
  5. Returns a JSON response with the status

Webhook Route

The route is registered automatically in routes/qpay.php:

Route::post(config('qpay.webhook_path', '/qpay/webhook'), WebhookController::class) ->name('qpay.webhook') ->withoutMiddleware([\Illuminate\Foundation\Http\Middleware\VerifyCsrfToken::class]);

You can customize the webhook path via the QPAY_WEBHOOK_PATH environment variable.


Events

The package dispatches two Laravel events:

PaymentReceived

Fired when a payment is confirmed (at least one payment row found).

use QPay\Laravel\Events\PaymentReceived; // In EventServiceProvider or using Event::listen Event::listen(PaymentReceived::class, function (PaymentReceived $event) { $event->invoiceId; // string -- QPay invoice ID $event->result; // PaymentCheckResponse -- Full check result // Example: Update your order status $order = Order::where('qpay_invoice_id', $event->invoiceId)->first(); $order->update(['status' => 'paid']); });

PaymentFailed

Fired when no payment is found or when an error occurs during verification.

use QPay\Laravel\Events\PaymentFailed; Event::listen(PaymentFailed::class, function (PaymentFailed $event) { $event->invoiceId; // string -- QPay invoice ID $event->reason; // string -- Failure reason Log::warning("QPay payment failed for {$event->invoiceId}: {$event->reason}"); });

Blade Components

QR Code

Renders the QPay QR code image.

<x-qpay-qr-code :qr-image="$invoice->qrImage" /> {{-- With custom size --}} <x-qpay-qr-code :qr-image="$invoice->qrImage" :size="200" /> {{-- Using QR text instead of image --}} <x-qpay-qr-code :qr-text="$invoice->qrText" />

Props:

PropTypeDefaultDescription
qr-imagestring''Base64-encoded QR code image
qr-textstring''QR code text to render
sizeint256QR code width/height in pixels

Payment Button

Renders bank deep link buttons.

<x-qpay-payment-button :urls="$invoice->urls" :short-url="$invoice->qPayShortUrl" />

Props:

PropTypeDefaultDescription
urlsarray[]Bank deep link URLs from invoice response
short-urlstring''QPay short URL for payment

Customizing Views

Publish the views and edit them:

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

Published to resources/views/vendor/qpay/.


Testing

Use Laravel’s built-in mocking to test QPay integration.

Mocking the Client

use QPay\QPayClient; use QPay\Models\PaymentCheckResponse; // In your test $mockResult = new PaymentCheckResponse( count: 1, paidAmount: 10000.0, rows: [(object)['paymentId' => 'pay_123']], ); $mock = $this->createMock(QPayClient::class); $mock->method('checkPayment')->willReturn($mockResult); $this->app->instance(QPayClient::class, $mock);

Testing Webhook Events

use Illuminate\Support\Facades\Event; use QPay\Laravel\Events\PaymentReceived; Event::fake(); $this->postJson('/qpay/webhook', ['invoice_id' => 'inv_123']); Event::assertDispatched(PaymentReceived::class, function ($event) { return $event->invoiceId === 'inv_123'; });

Testing with Testbench

The package uses Orchestra Testbench  for testing:

use Orchestra\Testbench\TestCase; use QPay\Laravel\QPayServiceProvider; class MyTest extends TestCase { protected function getPackageProviders($app): array { return [QPayServiceProvider::class]; } }

API Reference

Facade Methods (QPay::)

MethodReturn TypeDescription
createInvoice(CreateInvoiceRequest $req)InvoiceResponseCreate a full invoice
createSimpleInvoice(CreateSimpleInvoiceRequest $req)InvoiceResponseCreate a simple invoice
cancelInvoice(string $invoiceId)voidCancel an invoice
getPayment(string $paymentId)PaymentDetailGet payment details
checkPayment(PaymentCheckRequest $req)PaymentCheckResponseCheck payment status
listPayments(PaymentListRequest $req)PaymentListResponseList payments

Classes

ClassNamespaceDescription
QPayServiceProviderQPay\LaravelAuto-discovered service provider
QPay (Facade)QPay\Laravel\FacadesFacade for QPayClient
WebhookControllerQPay\Laravel\Http\ControllersWebhook endpoint handler
PaymentReceivedQPay\Laravel\EventsEvent dispatched on payment success
PaymentFailedQPay\Laravel\EventsEvent dispatched on payment failure
QrCodeQPay\Laravel\ComponentsBlade component for QR codes
PaymentButtonQPay\Laravel\ComponentsBlade component for bank links
InstallCommandQPay\Laravel\Consoleqpay:install artisan command

Last updated on