# Laravel

Official Laravel integration for ZevPay Checkout. Provides a service provider, facade, webhook handling with signature verification, and event dispatching.

## Installation

```bash
composer require zevpay/laravel
```

The package uses Laravel auto-discovery — no manual provider registration needed.

Publish the config file:

```bash
php artisan vendor:publish --tag=zevpay-config
```

Requires PHP 8.1+ and Laravel 10, 11, or 12.

## Configuration

Add your API keys to `.env`:

```env
ZEVPAY_SECRET_KEY=sk_live_your_secret_key
ZEVPAY_PUBLIC_KEY=pk_live_your_public_key
ZEVPAY_WEBHOOK_SECRET=whsec_your_webhook_secret
```

Get your keys from the [ZevPay Dashboard](https://dashboard.zevpaycheckout.com).

The published config file (`config/zevpay.php`) supports these options:

| Variable | Default | Description |
|----------|---------|-------------|
| `ZEVPAY_SECRET_KEY` | — | Your `sk_live_*` or `sk_test_*` key |
| `ZEVPAY_PUBLIC_KEY` | — | Your `pk_live_*` or `pk_test_*` key (optional, for frontend) |
| `ZEVPAY_WEBHOOK_SECRET` | — | Webhook signing secret from Dashboard |
| `ZEVPAY_WEBHOOK_PATH` | `webhooks/zevpay` | Webhook endpoint path |
| `ZEVPAY_BASE_URL` | `https://api.zevpaycheckout.com` | API base URL |
| `ZEVPAY_TIMEOUT` | `30` | Request timeout in seconds |
| `ZEVPAY_MAX_RETRIES` | `2` | Retries on 5xx errors |

## Usage

### Facade

```php
use ZevPay\Laravel\Facades\ZevPay;

// Initialize a checkout session
$session = ZevPay::checkout()->initialize([
    'amount' => 500000, // ₦5,000 in kobo
    'email' => 'customer@example.com',
    'reference' => 'ORDER-123',
    'callback_url' => 'https://yoursite.com/callback',
]);

return redirect($session['checkout_url']);
```

### Dependency injection

```php
use ZevPay\ZevPay;

class PaymentController extends Controller
{
    public function checkout(ZevPay $zevpay)
    {
        $session = $zevpay->checkout->initialize([
            'amount' => 500000,
            'email' => 'customer@example.com',
        ]);

        return redirect($session['checkout_url']);
    }
}
```

### Available resources

| Resource | Facade | Methods |
|----------|--------|---------|
| Checkout | `ZevPay::checkout()` | `initialize`, `verify`, `get`, `selectPaymentMethod` |
| Transfers | `ZevPay::transfers()` | `create`, `list`, `get`, `verify`, `listBanks`, `resolveAccount`, `calculateCharges`, `getBalance` |
| Invoices | `ZevPay::invoices()` | `create`, `list`, `get`, `update`, `send`, `cancel` |
| Static PayID | `ZevPay::staticPayId()` | `create`, `list`, `get`, `update`, `deactivate`, `reactivate` |
| Dynamic PayID | `ZevPay::dynamicPayId()` | `create`, `list`, `get`, `deactivate` |
| Virtual Accounts | `ZevPay::virtualAccounts()` | `create`, `list`, `get` |
| Wallet | `ZevPay::wallet()` | `get`, `update`, `listMembers`, `addMember`, `removeMember`, `updateMember` |

## Webhooks

The package registers a webhook route at `POST /webhooks/zevpay` with automatic HMAC-SHA256 signature verification.

### Setup

1. Copy the webhook URL: `https://yoursite.com/webhooks/zevpay`
2. Go to your [ZevPay Dashboard](https://dashboard.zevpaycheckout.com) webhook settings
3. Paste the URL and save
4. Copy the **webhook secret** and add it to `.env` as `ZEVPAY_WEBHOOK_SECRET`

::: warning Always configure webhooks
Webhooks provide reliable server-to-server payment confirmation. Client-side callbacks may fail if the customer closes their browser.
:::

### Listening to events

Register listeners in your `EventServiceProvider` or use closures:

```php
use ZevPay\Laravel\Events\ChargeSuccess;
use ZevPay\Laravel\Events\WebhookReceived;

// Listen to a specific event
class HandleChargeSuccess
{
    public function handle(ChargeSuccess $event): void
    {
        $reference = $event->payload['data']['reference'];
        $amount = $event->payload['data']['amount'];

        // Update your order, send receipt, etc.
        Order::where('reference', $reference)->update([
            'status' => 'paid',
        ]);
    }
}

// Or listen to all webhook events
class LogAllWebhooks
{
    public function handle(WebhookReceived $event): void
    {
        logger()->info("ZevPay: {$event->eventType}", $event->payload);
    }
}
```

### Available events

| Event class | Webhook type |
|-------------|-------------|
| `ChargeSuccess` | `charge.success` |
| `TransferSuccess` | `transfer.success` |
| `TransferFailed` | `transfer.failed` |
| `TransferReversed` | `transfer.reversed` |
| `InvoiceCreated` | `invoice.created` |
| `InvoiceSent` | `invoice.sent` |
| `InvoicePaymentReceived` | `invoice.payment_received` |
| `InvoicePaid` | `invoice.paid` |
| `InvoiceOverdue` | `invoice.overdue` |
| `InvoiceCancelled` | `invoice.cancelled` |
| `WebhookReceived` | All events (generic catch-all) |

All specific event classes extend `WebhookEvent` and have a `$payload` property containing the raw webhook data.

### Custom webhook path

```env
ZEVPAY_WEBHOOK_PATH=api/webhooks/zevpay
```

### CSRF exemption

The webhook route uses `VerifyWebhookSignature` middleware instead of CSRF. For Laravel 10 and older, add the path to your `VerifyCsrfToken` middleware `$except` array:

```php
protected $except = [
    'webhooks/zevpay',
];
```

Laravel 11+ handles this automatically.

## Artisan commands

### Verify API keys

```bash
php artisan zevpay:verify-keys
```

Checks that your keys are configured, have valid format (`sk_*`, `pk_*`), and tests API connectivity.

### Test webhook

```bash
php artisan zevpay:webhook-test
php artisan zevpay:webhook-test transfer.success
```

Sends a signed test webhook payload to your local endpoint. Useful for testing your event listeners during development.

## Error handling

The package uses the [PHP SDK](/sdks/php) exception hierarchy:

```php
use ZevPay\Laravel\Facades\ZevPay;
use ZevPay\Exceptions\ValidationException;
use ZevPay\Exceptions\AuthenticationException;
use ZevPay\Exceptions\NotFoundException;
use ZevPay\Exceptions\RateLimitException;

try {
    $session = ZevPay::checkout()->initialize([
        'amount' => 500000,
        'email' => 'customer@example.com',
    ]);
} catch (ValidationException $e) {
    // $e->details contains field-level errors
    // e.g. ['email' => 'Email is required']
} catch (AuthenticationException $e) {
    // Invalid API key
} catch (NotFoundException $e) {
    // Resource not found
} catch (RateLimitException $e) {
    // $e->retryAfter — seconds to wait
}
```

## Resources

- [API Reference](/api/overview) — Full endpoint documentation
- [PHP SDK](/sdks/php) — Underlying server-side SDK
- [Webhook Events](/webhooks/events) — All event types
- [Packagist](https://packagist.org/packages/zevpay/laravel) — Package registry
