PHPackages                             osoobe/dimepay-laravel-sdk - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Payment Processing](/categories/payments)
4. /
5. osoobe/dimepay-laravel-sdk

ActiveLibrary[Payment Processing](/categories/payments)

osoobe/dimepay-laravel-sdk
==========================

Laravel SDK for DimePay payment gateway — orders, payments, cards, split, recurring, and more.

1.1.0(4w ago)051[5 issues](https://github.com/osoobe/dimepay-laravel-sdk/issues)MITPHPPHP ^8.2CI passing

Since Mar 17Pushed 3w agoCompare

[ Source](https://github.com/osoobe/dimepay-laravel-sdk)[ Packagist](https://packagist.org/packages/osoobe/dimepay-laravel-sdk)[ RSS](/packages/osoobe-dimepay-laravel-sdk/feed)WikiDiscussions master Synced today

READMEChangelog (3)Dependencies (36)Versions (9)Used By (0)

DimePay Laravel SDK
===================

[](#dimepay-laravel-sdk)

[![Latest Version on Packagist](https://camo.githubusercontent.com/315a07db8de1312bc46d90bccdbd293c909e08e09b230579b3d918df40229d23/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6f736f6f62652f64696d657061792d6c61726176656c2d73646b2e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/osoobe/dimepay-laravel-sdk)[![Tests](https://camo.githubusercontent.com/1a85616f6ab03a48e58d6789028295e87b419c3f87d56b88d551fba4b99dc379/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f6f736f6f62652f64696d657061792d6c61726176656c2d73646b2f74657374732e796d6c3f6c6162656c3d7465737473267374796c653d666c61742d737175617265)](https://github.com/osoobe/dimepay-laravel-sdk/actions)[![PHP Version](https://camo.githubusercontent.com/2b772b0e9daa8f19f7ae8cbbd897a75f9daecc22ee83ef8865e569c50ac3565a/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7068702d253545382e322d626c75652e7376673f7374796c653d666c61742d737175617265)](https://www.php.net/)[![Laravel](https://camo.githubusercontent.com/b163b0073d1350263904b74b955991dfa212dd5f91f2d11ae5db118327e11c7a/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c61726176656c2d31302532302537432532303131253230253743253230313225323025374325323031332d7265642e7376673f7374796c653d666c61742d737175617265)](https://laravel.com)[![License: MIT](https://camo.githubusercontent.com/1b01ef0024ba0866c115986b895301f657c1b21fc29f05c4844b7f2e8d89204d/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d79656c6c6f772e7376673f7374796c653d666c61742d737175617265)](LICENSE.md)

A first-class Laravel SDK for the [DimePay](https://docs.dimepay.net) payment gateway. Handles everything — orders, hosted payments, card tokenization, auth/capture/void/refund, split payments (Dime Bridge), recurring subscriptions, webhooks, JWT signing, and full white-label extensibility.

---

Table of Contents
-----------------

[](#table-of-contents)

- [Requirements](#requirements)
- [Before You Start](#before-you-start)
- [Installation](#installation)
- [Environment Variables](#environment-variables)
- [Database Setup](#database-setup)
- [CSRF Setup](#csrf-setup)
- [Configuration Reference](#configuration-reference)
- [How It Works](#how-it-works)
- [Usage](#usage)
    - [Orders](#orders)
    - [Payments — Hosted Page](#payments--hosted-page)
    - [Payments — Auth / Sale / Capture / Void / Refund](#payments--auth--sale--capture--void--refund)
    - [Card Tokenization](#card-tokenization)
    - [Dime Bridge — Split Payments](#dime-bridge--split-payments)
    - [Recurring / Subscriptions](#recurring--subscriptions)
- [Webhooks](#webhooks)
- [Events Reference](#events-reference)
- [Exception Handling](#exception-handling)
- [Facade &amp; Dependency Injection](#facade--dependency-injection)
- [Multi-tenant / White-label](#multi-tenant--white-label)
- [Extending the Package](#extending-the-package)
- [Testing Your App](#testing-your-app)
- [Sandbox Credentials](#sandbox-credentials)
- [FAQ](#faq)
- [Changelog](#changelog)
- [License](#license)

---

Requirements
------------

[](#requirements)

DependencyVersionPHP^8.2Laravel^10.0 | ^11.0 | ^12.0DimePay Account[Sign up at dimepay.net](https://dimepay.net)---

Before You Start
----------------

[](#before-you-start)

Before installing, you need:

1. **A DimePay account** — [Sign up here](https://dimepay.net)
2. **Your API credentials** — Go to your DimePay Dashboard → Developer section and generate:
    - `client_key` — sent with every API request as a header (e.g. `ck_xxxxxxxxxxxx`)
    - `secret_key` — used **server-side only** to sign JWT payloads (e.g. `sk_xxxxxxxxxxxx`)
3. **A webhook URL** — a publicly accessible HTTPS URL on your app that DimePay can POST payment events to (e.g. `https://yourapp.com/dimepay/webhook`)

> **Never expose your `secret_key` in frontend code, mobile apps, or version control.** It signs every payment request and must stay private.

---

Installation
------------

[](#installation)

```
composer require osoobe/dimepay-laravel-sdk
```

The package auto-discovers itself. No manual provider registration needed.

Run the install command:

```
php artisan dimepay:install
```

This publishes:

- `config/dimepay.php` — main SDK config
- `config/webhook-client.php` — webhook handling config
- Webhook database migrations

---

Environment Variables
---------------------

[](#environment-variables)

Add these to your `.env` file:

```
# ─── Required ────────────────────────────────────────────────
DIMEPAY_ENV=sandbox                          # 'sandbox' or 'production'
DIMEPAY_CLIENT_KEY=ck_your_client_key        # from DimePay dashboard
DIMEPAY_SECRET_KEY=sk_your_secret_key        # from DimePay dashboard — KEEP PRIVATE

# ─── Optional — only needed if you want to override defaults ─
DIMEPAY_PRODUCTION_URL=https://api.dimepay.app/dapi/v1
DIMEPAY_SANDBOX_URL=https://sandbox.api.dimepay.app/dapi/v1
DIMEPAY_TIMEOUT=30
DIMEPAY_RETRIES=2
DIMEPAY_RETRY_DELAY=500
DIMEPAY_LOGGING=true
DIMEPAY_LOG_CHANNEL=stack
DIMEPAY_LOG_LEVEL=debug
DIMEPAY_ROUTES_ENABLED=true
DIMEPAY_ROUTES_PREFIX=dimepay
DIMEPAY_WEBHOOK_SECRET=your_webhook_secret   # optional — for signature verification
```

---

Database Setup
--------------

[](#database-setup)

The SDK uses `spatie/laravel-webhook-client` to receive and queue incoming webhooks. This requires one database table.

Run the migrations:

```
php artisan migrate
```

This creates the `webhook_calls` table which stores every incoming webhook from DimePay for reliable queued processing.

> If you don't plan to receive webhooks (e.g. you only use the hosted payment page and don't need payment event callbacks), you can skip this step and set `DIMEPAY_ROUTES_ENABLED=false` in your `.env`.

---

CSRF Setup
----------

[](#csrf-setup)

DimePay POSTs webhooks to your app. Since webhooks come from an external server they don't have a CSRF token and will be rejected by Laravel's CSRF middleware.

Exclude the webhook route in `bootstrap/app.php`:

```
->withMiddleware(function (Middleware $middleware) {
    $middleware->validateCsrfTokens(except: [
        'dimepay/webhook',
        // or use your custom prefix if you changed DIMEPAY_ROUTES_PREFIX
    ]);
})
```

For older Laravel (pre-11), add to `app/Http/Middleware/VerifyCsrfToken.php`:

```
protected $except = [
    'dimepay/webhook',
];
```

---

Webhook Client Configuration
----------------------------

[](#webhook-client-configuration)

After running `php artisan dimepay:install`, open `config/webhook-client.php` and find or add the `dimepay` entry:

```
'configs' => [
    [
        'name'                  => 'dimepay',
        'signing_secret'        => env('DIMEPAY_WEBHOOK_SECRET'),
        'signature_header_name' => 'X-DimePay-Signature',
        'signature_validator'   => \Osoobe\DimePay\Webhooks\DimePaySignatureValidator::class,
        'webhook_profile'       => \Osoobe\DimePay\Webhooks\DimePayWebhookProfile::class,
        'webhook_response'      => \Spatie\WebhookClient\WebhookResponse\DefaultRespondsTo::class,
        'webhook_model'         => \Spatie\WebhookClient\Models\WebhookCall::class,
        'process_webhook_job'   => \Osoobe\DimePay\Webhooks\ProcessDimePayWebhookJob::class,
    ],
],
```

Set your webhook URL in the DimePay dashboard to:

```
https://yourapp.com/dimepay/webhook

```

---

Configuration Reference
-----------------------

[](#configuration-reference)

All options in `config/dimepay.php`:

```
return [
    // 'sandbox' or 'production'
    'environment' => env('DIMEPAY_ENV', 'sandbox'),

    // Sent as client_key header on every API request
    'client_key' => env('DIMEPAY_CLIENT_KEY'),

    // Used server-side to sign JWT payloads — NEVER expose this
    'secret_key' => env('DIMEPAY_SECRET_KEY'),

    // Override base URLs if needed (e.g. proxies)
    'base_urls' => [
        'production' => env('DIMEPAY_PRODUCTION_URL', 'https://api.dimepay.app/dapi/v1'),
        'sandbox'    => env('DIMEPAY_SANDBOX_URL', 'https://sandbox.api.dimepay.app/dapi/v1'),
    ],

    // HTTP client options
    'timeout'     => 30,    // seconds
    'retries'     => 2,     // auto-retry on server errors
    'retry_delay' => 500,   // milliseconds between retries

    // Log all API responses — useful during development
    'logging' => [
        'enabled' => env('DIMEPAY_LOGGING', true),
        'channel' => env('DIMEPAY_LOG_CHANNEL', 'stack'),
        'level'   => env('DIMEPAY_LOG_LEVEL', 'debug'),
    ],

    // Built-in webhook/callback routes
    'routes' => [
        'enabled'    => env('DIMEPAY_ROUTES_ENABLED', true),
        'prefix'     => env('DIMEPAY_ROUTES_PREFIX', 'dimepay'),
        'middleware' => ['api'],
    ],

    // Webhook signature verification
    'webhook' => [
        'secret'    => env('DIMEPAY_WEBHOOK_SECRET'),
        'tolerance' => 300, // replay-attack window in seconds
    ],

    // JWT signing
    'jwt' => [
        'algorithm' => 'HS256',
        'ttl'       => 3600,
    ],
];
```

---

How It Works
------------

[](#how-it-works)

Every API call to DimePay follows this flow:

```
Your app
  → builds a Data object (e.g. CreateOrderData)
  → SDK signs it as a JWT using your secret_key
  → SDK sends { lang: "en", data: "" } with client_key header
  → DimePay processes it
  → SDK hydrates the response into a typed DTO
  → SDK fires a Laravel event
  → Your app receives a typed response object

```

**Why JWTs?** DimePay uses JWT-signed payloads so the API can verify that the request came from your server and hasn't been tampered with in transit.

**Your `secret_key` never leaves your server.** It only signs the outgoing payload — it never goes in the request itself.

---

Usage
-----

[](#usage)

### Orders

[](#orders)

An order represents a purchase intent. Create one first, then either redirect to the hosted payment page or charge a saved card directly.

```
use Osoobe\DimePay\Facades\DimePay;
use Osoobe\DimePay\Data\Orders\CreateOrderData;

$order = DimePay::orders()->create(new CreateOrderData(
    id: 'ORDER-' . uniqid(),         // your internal order ID
    total: 5000,                      // amount in major units (e.g. 5000 JMD)
    subtotal: 5000,
    currency: 'JMD',                  // USD, JMD, GBP, EUR, TTD, BBD, XCD
    email: 'customer@example.com',
    ipAddress: request()->ip(),
    referenceTransactionId: 'REF-' . uniqid(),
    webhookUrl: 'https://yourapp.com/dimepay/webhook',
    redirectUrl: 'https://yourapp.com/order/success',
    checkoutUrl: 'https://yourapp.com/checkout',
    items: [
        [
            'id'               => 'prod-1',
            'name'             => 'Product Name',
            'price'            => 5000,
            'quantity'         => 1,
            'sku'              => 'SKU-001',
            'shortDescription' => 'Product description',
            'imageUrl'         => 'https://yourapp.com/product.jpg',
        ],
    ],
    taxes: [],  // array of ['name' => '...', 'value' => 12, 'total' => 57.1]
));

// $order->orderUrl — redirect customer here to pay
return redirect($order->orderUrl);
```

**Required fields:**

FieldDescription`id`Your unique order ID`total`Total amount in major units`subtotal`Subtotal before tax/discount`currency`Currency code`email`Customer email`ipAddress`Customer IP address`referenceTransactionId`Your unique reference ID`items`At minimum one item with `id`, `name`, `price`, `quantity`, `sku`, `shortDescription`, `imageUrl``taxes`Array of tax objects (can be empty `[]`)---

### Payments — Hosted Page

[](#payments--hosted-page)

The hosted page is the simplest integration. DimePay handles the card form, 3DS, and everything else. You just redirect.

```
use Osoobe\DimePay\Facades\DimePay;
use Osoobe\DimePay\Data\Orders\CreateOrderData;

$page = DimePay::payments()->hostedPage(new CreateOrderData(
    id: 'ORDER-001',
    total: 5000,
    subtotal: 5000,
    currency: 'JMD',
    email: 'customer@example.com',
    ipAddress: request()->ip(),
    referenceTransactionId: 'REF-001',
    webhookUrl: 'https://yourapp.com/dimepay/webhook',
    redirectUrl: 'https://yourapp.com/order/success',
    checkoutUrl: 'https://yourapp.com/checkout',
    items: [...],
    taxes: [],
));

return redirect($page->orderUrl);
```

The customer pays on DimePay's hosted page. When done, DimePay:

1. Redirects them to your `redirectUrl`
2. POSTs the payment result to your `webhookUrl`

---

### Payments — Auth / Sale / Capture / Void / Refund

[](#payments--auth--sale--capture--void--refund)

These endpoints require a **saved card token** (`card_xxx`). You get one by going through the [Card Tokenization](#card-tokenization) flow first.

#### Sale (charge immediately)

[](#sale-charge-immediately)

```
use Osoobe\DimePay\Data\Payments\DirectPaymentData;
use Osoobe\DimePay\Data\Payments\PaymentParamsData;

$payment = DimePay::payments()->sale(new DirectPaymentData(
    id: 'ORDER-001',
    total: 5000,
    subtotal: 5000,
    currency: 'JMD',
    email: 'customer@example.com',
    ipAddress: request()->ip(),
    referenceTransactionId: 'REF-001',
    paymentParams: new PaymentParamsData(
        source: 'TOKEN',
        token: 'card_xxxxxxxxxxxx',  // saved card token
    ),
    items: [...],
    taxes: [],
));

// $payment->id — transaction ID, save this for capture/void/refund
// $payment->status — 'COMPLETE', 'FAILED', etc.
```

#### Auth (hold funds, capture later)

[](#auth-hold-funds-capture-later)

```
$auth = DimePay::payments()->authorize($directPaymentData);
$transactionId = $auth->id;  // save this
```

#### Capture

[](#capture)

```
$captured = DimePay::payments()->capture($transactionId);
```

#### Void

[](#void)

```
$voided = DimePay::payments()->void($transactionId);
```

#### Refund

[](#refund)

```
$refunded = DimePay::payments()->refund($transactionId);
```

---

### Card Tokenization

[](#card-tokenization)

Tokenization lets you save a customer's card securely without handling raw card numbers. The customer enters their card on DimePay's hosted form. You receive a reusable token via webhook.

**Step 1 — Create a card request:**

```
use Osoobe\DimePay\Data\Cards\CardRequestData;

$cardRequest = DimePay::cards()->requestToken(new CardRequestData(
    id: 'CARD-REQ-' . uniqid(),
    webhookUrl: 'https://yourapp.com/dimepay/webhook',
    redirectUrl: 'https://yourapp.com/card-saved',
));

// Redirect or embed the card form
return redirect($cardRequest->cardUrl);
// or embed:
```

**Step 2 — Customer completes the card form on DimePay's page**

**Step 3 — DimePay POSTs to your webhook with the saved card token:**

```
{
  "type": "card.saved",
  "token": "card_xxxxxxxxxxxx",
  "card_request_token": "cr_xxxxxxxxxxxx",
  "card_scheme": "Visa",
  "last_four_digits": "1111",
  "card_expiry": "12/25"
}
```

**Step 4 — Save the `card_xxx` token to your database and use it for future payments.**

> **Note:** You cannot use raw card numbers directly with the Payments API. All card payments require a pre-tokenized `card_xxx` token obtained through this flow.

---

### Dime Bridge — Split Payments

[](#dime-bridge--split-payments)

Dime Bridge splits a single payment across multiple merchants in your marketplace.

> **Requires Dime Bridge to be enabled on your DimePay account.** Contact  to enable it.

Each item must be tagged with a `merchantId`. The `split` array defines how much each merchant receives and what platform fee you take.

```
$order = DimePay::orders()->create(new CreateOrderData(
    id: 'ORDER-001',
    total: 2000,
    subtotal: 2000,
    currency: 'USD',
    email: 'customer@example.com',
    ipAddress: request()->ip(),
    referenceTransactionId: 'REF-001',
    webhookUrl: 'https://yourapp.com/dimepay/webhook',
    redirectUrl: 'https://yourapp.com/order/success',
    checkoutUrl: 'https://yourapp.com/checkout',
    items: [
        [
            'id'               => 'book-1',
            'name'             => 'PHP Book',
            'price'            => 500,
            'quantity'         => 1,
            'sku'              => 'BOOK-1',
            'shortDescription' => '',
            'imageUrl'         => 'https://example.com/book.jpg',
            'merchantId'       => 'm4D8mQ1wMrdTUIg',  // tag item to merchant
        ],
        [
            'id'               => 'course-1',
            'name'             => 'Laravel Course',
            'price'            => 1500,
            'quantity'         => 1,
            'sku'              => 'COURSE-1',
            'shortDescription' => '',
            'imageUrl'         => 'https://example.com/course.jpg',
            'merchantId'       => 'm7UarSiV9zWxN6v',
        ],
    ],
    split: [
        ['merchantId' => 'm4D8mQ1wMrdTUIg', 'amount' => 500,  'fee' => 10],  // 10% platform fee
        ['merchantId' => 'm7UarSiV9zWxN6v', 'amount' => 1500, 'fee' => 30],  // 30% platform fee
    ],
    taxes: [],
));

return redirect($order->orderUrl);
```

**Rules:**

- Every item must have a `merchantId`
- Every `merchantId` in `items` must have a matching entry in `split`
- `sum(split[].amount)` must equal the total of items grouped by merchant
- `fee` is a percentage (e.g. `10` = 10%)

---

### Recurring / Subscriptions

[](#recurring--subscriptions)

Create a subscription by adding `isSubscription: true` and `subscriptionInstructions` to any order. Include `tokenize: true` to save the card for future billing cycles.

```
use Osoobe\DimePay\Data\Orders\SubscriptionInstructionsData;

$order = DimePay::orders()->create(new CreateOrderData(
    id: 'SUB-' . uniqid(),
    total: 4900,
    subtotal: 4900,
    currency: 'USD',
    email: 'subscriber@example.com',
    ipAddress: request()->ip(),
    referenceTransactionId: 'REF-SUB-' . uniqid(),
    webhookUrl: 'https://yourapp.com/dimepay/webhook',
    redirectUrl: 'https://yourapp.com/subscription/activated',
    checkoutUrl: 'https://yourapp.com/pricing',
    isSubscription: true,
    tokenize: true,               // save card for future cycles
    subscriptionInstructions: new SubscriptionInstructionsData(
        recurringFrequency: 'MONTHLY',  // 'WEEKLY', 'MONTHLY', 'YEARLY'
        billingCycles: 12,              // number of recurring charges
    ),
    items: [
        [
            'id'               => 'plan-basic',
            'name'             => 'Basic Plan — Monthly',
            'price'            => 4900,
            'quantity'         => 1,
            'sku'              => 'PLAN-BASIC',
            'shortDescription' => 'Monthly subscription',
            'imageUrl'         => 'https://yourapp.com/plan.jpg',
        ],
    ],
    taxes: [],
));

return redirect($order->orderUrl);
```

Webhook events to listen for:

- `subscription.created` — after first checkout succeeds
- `invoice.payment_succeeded` — each successful recurring charge
- `invoice.payment_failed` — each failed recurring charge
- `subscription.canceled` / `subscription.paused`

---

Webhooks
--------

[](#webhooks)

DimePay sends payment events to `POST /dimepay/webhook`. The SDK handles receiving, storing, and queued processing automatically via `spatie/laravel-webhook-client`.

**Full setup checklist:**

- `php artisan dimepay:install` run
- `php artisan migrate` run (creates `webhook_calls` table)
- CSRF exception added for `dimepay/webhook`
- `config/webhook-client.php` configured with DimePay classes
- Webhook URL set in DimePay dashboard: `https://yourapp.com/dimepay/webhook`
- Queue worker running: `php artisan queue:work`

**Listen to webhook events in your app:**

```
// app/Providers/AppServiceProvider.php or EventServiceProvider.php

use Osoobe\DimePay\Events\DimePayWebhookReceived;

Event::listen(DimePayWebhookReceived::class, function ($event) {
    $type    = $event->type;    // e.g. 'payment.success', 'card.saved'
    $payload = $event->payload; // full webhook payload array

    match ($type) {
        'payment.success'           => handlePaymentSuccess($payload),
        'payment.failed'            => handlePaymentFailed($payload),
        'card.saved'                => saveCardToken($payload['token'], $payload),
        'subscription.created'      => activateSubscription($payload),
        'invoice.payment_succeeded' => renewSubscription($payload),
        'invoice.payment_failed'    => handleFailedRenewal($payload),
        default                     => null,
    };
});
```

Or use a dedicated listener class:

```
// app/Listeners/HandleDimePayWebhook.php
class HandleDimePayWebhook
{
    public function handle(DimePayWebhookReceived $event): void
    {
        // $event->type, $event->payload
    }
}
```

> **Important:** Webhooks are processed in a queue. Make sure your queue worker is running in production (`php artisan queue:work` or a process manager like Supervisor).

---

Events Reference
----------------

[](#events-reference)

All events fire from service methods — not controllers — so they fire whether you use the built-in routes or call services directly.

EventFired WhenProperties`OrderCreated`Order created via `orders()->create()``$request` (CreateOrderData), `$response` (CreateOrderResponseData)`HostedPaymentPageCreated`Hosted page created`$request`, `$response` (HostedPageResponseData)`PaymentAuthorized`Payment authorized`$request` (DirectPaymentData), `$response` (PaymentResponseData)`PaymentSaleProcessed`Sale completed`$request`, `$response``PaymentCaptured`Payment captured`$transactionId`, `$response``PaymentVoided`Payment voided`$transactionId`, `$response``PaymentRefunded`Payment refunded`$transactionId`, `$response``CardTokenRequested`Card tokenization initiated`$request` (CardRequestData), `$response` (CardRequestResponseData)`DimePayWebhookReceived`Incoming webhook received`$type`, `$payload``DimePayRequestFailed`Any API request fails`$endpoint`, `$payload`, `$exception`---

Exception Handling
------------------

[](#exception-handling)

All DimePay API errors are normalized into typed exceptions.

```
use Osoobe\DimePay\Exceptions\DimePayAuthException;
use Osoobe\DimePay\Exceptions\DimePayValidationException;
use Osoobe\DimePay\Exceptions\DimePayNotFoundException;
use Osoobe\DimePay\Exceptions\DimePayServerException;
use Osoobe\DimePay\Exceptions\DimePayException;

try {
    $order = DimePay::orders()->create($data);
} catch (DimePayAuthException $e) {
    // HTTP 401 — wrong or missing client_key
    // Check DIMEPAY_CLIENT_KEY in .env
} catch (DimePayValidationException $e) {
    // HTTP 400 — bad payload, missing required fields
    // $e->getDetails() returns array of validation error messages
    return back()->withErrors($e->getDetails());
} catch (DimePayNotFoundException $e) {
    // HTTP 404 — order/payment not found
    abort(404);
} catch (DimePayServerException $e) {
    // HTTP 500 — DimePay server error
    // Safe to retry — the SDK automatically retries server errors
    abort(503, 'Payment service temporarily unavailable');
} catch (DimePayException $e) {
    // Catch-all for any other DimePay error
    logger()->error('DimePay error', [
        'status'  => $e->getStatus(),
        'code'    => $e->getErrorCode(),
        'message' => $e->getMessage(),
        'details' => $e->getDetails(),
    ]);
}
```

---

Facade &amp; Dependency Injection
---------------------------------

[](#facade--dependency-injection)

**Via Facade:**

```
use Osoobe\DimePay\Facades\DimePay;

DimePay::orders()    // → OrderServiceInterface
DimePay::payments()  // → PaymentServiceInterface
DimePay::cards()     // → CardServiceInterface
DimePay::jwt()       // → JwtSigner (for manual JWT signing)
```

**Via Dependency Injection:**

```
use Osoobe\DimePay\Contracts\OrderServiceInterface;
use Osoobe\DimePay\Contracts\PaymentServiceInterface;

class CheckoutController extends Controller
{
    public function __construct(
        private readonly OrderServiceInterface $orders,
        private readonly PaymentServiceInterface $payments,
    ) {}

    public function checkout(Request $request)
    {
        $page = $this->payments->hostedPage($orderData);
        return redirect($page->orderUrl);
    }
}
```

---

Multi-tenant / White-label
--------------------------

[](#multi-tenant--white-label)

If you have multiple tenants with different DimePay credentials, use `withConfig()`:

```
$payment = DimePay::withConfig([
    'client_key'  => $tenant->dimepay_client_key,
    'secret_key'  => $tenant->dimepay_secret_key,
    'environment' => $tenant->dimepay_env,
])->payments()->sale($data);
```

This returns a new manager instance with overridden config — the original singleton is unaffected. Safe to use in multi-tenant apps where each request may use different credentials.

---

Extending the Package
---------------------

[](#extending-the-package)

### Override a service

[](#override-a-service)

Bind your own implementation in a service provider:

```
use Osoobe\DimePay\Contracts\OrderServiceInterface;
use App\Services\MyOrderService;

// In AppServiceProvider::register()
$this->app->bind(OrderServiceInterface::class, MyOrderService::class);
```

Extend the base class:

```
use Osoobe\DimePay\Services\OrderService;

class MyOrderService extends OrderService
{
    public function create(CreateOrderData $data): CreateOrderResponseData
    {
        // your logic before/after
        return parent::create($data);
    }
}
```

### Override controllers / routes

[](#override-controllers--routes)

Publish the routes:

```
php artisan vendor:publish --tag=dimepay-routes
```

Edit `routes/dimepay.php` to point to your own controllers.

### Override webhook profile

[](#override-webhook-profile)

Filter which webhook types get processed:

```
use Spatie\WebhookClient\WebhookProfile\WebhookProfile;

class MyWebhookProfile implements WebhookProfile
{
    public function shouldProcess(Request $request): bool
    {
        return in_array($request->input('type'), [
            'payment.success',
            'card.saved',
        ]);
    }
}
```

Register in `config/webhook-client.php`:

```
'webhook_profile' => \App\Webhooks\MyWebhookProfile::class,
```

---

Testing Your App
----------------

[](#testing-your-app)

Use `Http::fake()` to mock DimePay responses in your tests without hitting the real API:

```
use Illuminate\Support\Facades\Http;
use Osoobe\DimePay\Facades\DimePay;
use Osoobe\DimePay\Data\Orders\CreateOrderData;

Http::fake([
    'sandbox.api.dimepay.app/*/orders' => Http::response([
        'order_url' => 'https://pay.sandbox.dimepay.app/e-order/test123',
    ], 201),
]);

$order = DimePay::orders()->create(new CreateOrderData(
    id: 'ORDER-TEST',
    total: 5000,
    subtotal: 5000,
    currency: 'JMD',
    email: 'test@example.com',
    ipAddress: '127.0.0.1',
    referenceTransactionId: 'REF-TEST',
    items: [...],
    taxes: [],
));

expect($order->orderUrl)->toBe('https://pay.sandbox.dimepay.app/e-order/test123');
```

---

Sandbox Credentials
-------------------

[](#sandbox-credentials)

Use these when `DIMEPAY_ENV=sandbox`:

**API Base URL:** `https://sandbox.api.dimepay.app/dapi/v1`

**Test Cards:**

BrandCard NumberExpiryCVVVisa`4111 1111 1111 1111``12/25``123`Mastercard`5555 5555 5555 4444``02/26``265`Amex`3782 822463 10005``03/26``7890`Discover`6445 6464 4564 4564``04/40``123`UnionPay`6279 8862 4809 4966``04/40``123`> **Note on card payments in sandbox:** You cannot pass raw card numbers to the Payments API. You must go through the card tokenization flow to get a `card_xxx` token first, even in sandbox.

---

FAQ
---

[](#faq)

**Q: Do I need to sign JWTs myself?**No. The SDK signs all payloads automatically using your `secret_key`. You only need `DimePay::jwt()->sign([...])` if you're building a custom frontend integration.

**Q: Can I use this without the built-in routes?**Yes. Set `DIMEPAY_ROUTES_ENABLED=false` and call services directly from your own controllers.

**Q: Can I process payments with raw card numbers?**No. DimePay requires card tokenization first. All card payments use a `card_xxx` token obtained through the hosted card form. This is a PCI compliance requirement.

**Q: What's the difference between `orders()->create()` and `payments()->hostedPage()`?**Both create a hosted payment page. `orders()->create()` is the Orders API endpoint (`/orders`), while `payments()->hostedPage()` is the Payments API endpoint (`/payments/hosted-page`). They accept the same payload and return an `order_url`. Use either depending on your flow.

**Q: Do I need a queue for webhooks?**Yes, strongly recommended. Webhooks are processed via a queued job. Without a queue worker running, webhook processing will stall. Use `php artisan queue:work` locally or Supervisor/Horizon in production.

**Q: Does Dime Bridge work in sandbox?**Dime Bridge (split payments) must be enabled on your DimePay account. Contact  to enable it.

**Q: How do I get a saved card token for testing auth/sale?**Run the card tokenization flow — create a card request, open the `card_url` in a browser, enter a test card, and your webhook will receive the `card_xxx` token.

**Q: Is this an official DimePay package?**No. This is a community SDK by Osoobe. For official support, contact .

---

`php artisan about`
-------------------

[](#php-artisan-about)

The SDK registers itself with Laravel's `about` command:

```
php artisan about
```

```
DimePay SDK
  Environment ........ sandbox
  Logging ............ enabled
  Version ............ 1.0.0

```

---

Changelog
---------

[](#changelog)

See [CHANGELOG.md](CHANGELOG.md).

---

License
-------

[](#license)

MIT — see [LICENSE.md](LICENSE.md).

###  Health Score

43

—

FairBetter than 89% of packages

Maintenance94

Actively maintained with recent releases

Popularity10

Limited adoption so far

Community9

Small or concentrated contributor base

Maturity52

Maturing project, gaining track record

 Bus Factor2

2 contributors hold 50%+ of commits

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~9 days

Total

3

Last Release

29d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/1830494?v=4)[Oshane Bailey](/maintainers/b4oshany)[@b4oshany](https://github.com/b4oshany)

---

Top Contributors

[![CrypticHushane](https://avatars.githubusercontent.com/u/61479496?v=4)](https://github.com/CrypticHushane "CrypticHushane (7 commits)")[![b4oshany](https://avatars.githubusercontent.com/u/1830494?v=4)](https://github.com/b4oshany "b4oshany (5 commits)")[![claude](https://avatars.githubusercontent.com/u/81847?v=4)](https://github.com/claude "claude (4 commits)")

---

Tags

laravelsdkpaymentgatewaydimepay

###  Code Quality

TestsPest

Static AnalysisPHPStan

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/osoobe-dimepay-laravel-sdk/health.svg)

```
[![Health](https://phpackages.com/badges/osoobe-dimepay-laravel-sdk/health.svg)](https://phpackages.com/packages/osoobe-dimepay-laravel-sdk)
```

###  Alternatives

[aws/aws-sdk-php

AWS SDK for PHP - Use Amazon Web Services in your PHP project

6.3k543.5M2.6k](/packages/aws-aws-sdk-php)[eslazarev/wildberries-sdk

Wildberries OpenAPI clients (generated).

273.0k](/packages/eslazarev-wildberries-sdk)[civicrm/civicrm-core

Open source constituent relationship management for non-profits, NGOs and advocacy organizations.

751291.4k43](/packages/civicrm-civicrm-core)[danestves/laravel-polar

A package to easily integrate your Laravel application with Polar.sh

8120.4k](/packages/danestves-laravel-polar)[sebdesign/laravel-viva-payments

A Laravel package for integrating the Viva Payments gateway

4851.0k](/packages/sebdesign-laravel-viva-payments)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
