PHPackages                             qredit-payment-gateway/laravel-qredit - 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. qredit-payment-gateway/laravel-qredit

ActiveLibrary[Payment Processing](/categories/payments)

qredit-payment-gateway/laravel-qredit
=====================================

Laravel integration for Qredit payment gateway using Saloon PHP

0134PHPCI failing

Since Apr 21Pushed 3w agoCompare

[ Source](https://github.com/PaltechHub/QreditPaymentGateway)[ Packagist](https://packagist.org/packages/qredit-payment-gateway/laravel-qredit)[ RSS](/packages/qredit-payment-gateway-laravel-qredit/feed)WikiDiscussions master Synced 1w ago

READMEChangelogDependenciesVersions (2)Used By (0)

Qredit Laravel SDK
==================

[](#qredit-laravel-sdk)

[![Latest Version on Packagist](https://camo.githubusercontent.com/7f0f3b29c59747cb897e42ccd3e3b787f3d8eb7609e4a4e879a07cac0ff56178/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f7172656469742d7061796d656e742d676174657761792f6c61726176656c2d7172656469742e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/qredit-payment-gateway/laravel-qredit)[![Tests](https://camo.githubusercontent.com/772481c06520cf7ea05b2b9ad4c590c0eba52b3496262dc5ebe3af4c2c7db73d/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f7172656469742d7061796d656e742d676174657761792f6c61726176656c2d7172656469742f74657374732e796d6c3f6272616e63683d6d61696e266c6162656c3d7465737473267374796c653d666c61742d737175617265)](https://github.com/PaltechHub/QreditPaymentGateway/actions/workflows/tests.yml)[![Total Downloads](https://camo.githubusercontent.com/877c0728440521f46713f9e3d6ae1e384a312c6be49b0ec66a79fd5baee22301/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f7172656469742d7061796d656e742d676174657761792f6c61726176656c2d7172656469742e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/qredit-payment-gateway/laravel-qredit)[![License](https://camo.githubusercontent.com/538c894263e8e11797e094db4070ed8c81244ab38ddf06d281cb44056414b806/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f7172656469742d7061796d656e742d676174657761792f6c61726176656c2d7172656469742e7376673f7374796c653d666c61742d737175617265)](LICENSE.md)

Production-ready Laravel SDK for the **Qredit / BlockBuilders payment gateway**. Built with multi-tenant SAAS deployments as a first-class concern.

- ✅ Every Qredit API endpoint wrapped (auth, orders, payment requests, QR, fees, init, customers, transactions, clearing)
- ✅ Automatic HMAC-SHA512 signing — you never compute a signature yourself
- ✅ Per-tenant credentials — swap API keys per-request via a pluggable `CredentialProvider`
- ✅ Ready-made `/sign` and `/webhook` endpoints (one-line route macros)
- ✅ Per-tenant token cache (95% fewer auth calls), transparent refresh on 401
- ✅ `FakeQredit` test double + `qredit:call` CLI (the Postman replacement)
- ✅ Built on [Saloon](https://docs.saloon.dev/) v3 — full middleware / mock-client support

> **Status:** verified live against Qredit UAT — `auth/token` returns a valid JWT end-to-end through the SDK. See [CHANGELOG.md](CHANGELOG.md) for the latest release notes.

---

Table of contents
-----------------

[](#table-of-contents)

- [Installation](#installation)
- [Quick start — single tenant](#quick-start--single-tenant)
- [Multi-tenant usage](#multi-tenant-usage)
- [API surface](#api-surface)
- [Webhook handling](#webhook-handling)
- [The checkout widget's `/sign` endpoint](#the-checkout-widgets-sign-endpoint)
- [Testing with `FakeQredit`](#testing-with-fakeqredit)
- [CLI — `qredit:call`](#cli--qreditcall)
- [Configuration reference](#configuration-reference)
- [Troubleshooting](#troubleshooting)
- [Documentation](#documentation)

---

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

[](#installation)

```
composer require qredit-payment-gateway/laravel-qredit
php artisan qredit:install
```

The installer publishes `config/qredit.php` and prints the next-step checklist for your topology (single-tenant by default; pass `--tenancy` for multi-tenant instructions).

**Requirements**

- PHP 8.1 / 8.2 / 8.3 / 8.4
- Laravel 10 / 11 / 12 / 13
- Saloon v3

---

Quick start — single tenant
---------------------------

[](#quick-start--single-tenant)

Add your credentials to `.env`:

```
QREDIT_API_KEY=EdVfej9D...
QREDIT_SECRET_KEY=B9E0236B...
QREDIT_SANDBOX=true
QREDIT_SANDBOX_URL=https://apitest.qredit.tech/gw-checkout/api/v1
QREDIT_PRODUCTION_URL=https://api.qredit.tech/gw-checkout/api/v1
QREDIT_SIGNATURE_CASE=upper     # live UAT accepts only uppercase
```

Register the ready-made endpoints in `routes/web.php`:

```
use Illuminate\Support\Facades\Route;

Route::qreditSign();        // POST /qredit/sign
Route::qreditWebhook();     // POST /qredit/webhook
```

Use the facade anywhere:

```
use Qredit\LaravelQredit\Facades\Qredit;

$order = Qredit::createOrder([
    'amountCents'         => 3200,
    'currencyCode'        => 'ILS',
    'deliveryNeeded'      => 'true',
    'deliveryCostCents'   => 200,
    'shippingProviderCode'=> 'DELV2',
    'clientReference'     => 'ORDER-2026-001',
    'customerInfo'        => [
        'name'  => 'Jane Doe',
        'phone' => '+970599785833',
        'email' => 'jane@example.com',
    ],
    'shippingData' => [
        'countryCode' => 'PSE',
        'cityCode'    => '50',
        'areaCode'    => '50',
        'street'      => "Jemma'in",
        'postalCode'  => '970',
    ],
    'items' => [
        ['name' => 'Widget', 'amountCents' => 2000, 'quantity' => 1, 'sku' => 'W-001'],
        ['name' => 'Gadget', 'amountCents' => 1200, 'quantity' => 1, 'sku' => 'G-002'],
    ],
]);

$orderReference = $order['records'][0]['orderReference'];

$payment = Qredit::createPayment([
    'orderReference'    => $orderReference,
    'amountCents'       => 3200,
    'currencyCode'      => 'ILS',
    'lockOrderWhenPaid' => true,
    'paymentChannels'   => [['code' => 'CSAB'], ['code' => 'NC-QR']],
    'customerInfo'      => [/* ... */],
    'billingData'       => [/* ... */],
]);

$checkoutUrl = $payment['records'][0]['url'];
return redirect($checkoutUrl);
```

Every outgoing request is automatically signed with the correct `Authorization: HmacSHA512_O ` header per merchant guide §7. You don't compute anything by hand.

---

Multi-tenant usage
------------------

[](#multi-tenant-usage)

The SDK ships with two contracts you bind to your app's tenancy layer:

ContractResponsibility[`CredentialProvider`](src/Contracts/CredentialProvider.php)Given a tenant id, return that tenant's Qredit credentials.[`TenantResolver`](src/Contracts/TenantResolver.php)Given an HTTP request, return the current tenant id.### 1. Implement `CredentialProvider`

[](#1-implement-credentialprovider)

```
namespace App\Qredit;

use Qredit\LaravelQredit\Contracts\CredentialProvider;
use Qredit\LaravelQredit\Tenancy\QreditCredentials;
use App\Models\Tenant;

class DbCredentialProvider implements CredentialProvider
{
    public function credentialsFor(?string $tenantId = null): QreditCredentials
    {
        $tenantId = $tenantId ?? app('current.tenant.id');
        $tenant   = Tenant::findOrFail($tenantId);

        return new QreditCredentials(
            apiKey:    $tenant->qredit_api_key,
            secretKey: decrypt($tenant->qredit_secret_key),
            sandbox:   $tenant->qredit_sandbox,
            language:  $tenant->language_code,
            tenantId:  (string) $tenantId,
        );
    }

    public function isConfiguredFor(?string $tenantId = null): bool
    {
        $tenant = Tenant::find($tenantId ?? app('current.tenant.id'));

        return $tenant && filled($tenant->qredit_api_key) && filled($tenant->qredit_secret_key);
    }
}
```

### 2. Pick (or write) a `TenantResolver`

[](#2-pick-or-write-a-tenantresolver)

Built-in resolvers cover the common cases:

```
use Qredit\LaravelQredit\Tenancy\SubdomainTenantResolver;  // "shop-b.example.com" → "shop-b"
use Qredit\LaravelQredit\Tenancy\HeaderTenantResolver;      // X-Tenant-Id header
use Qredit\LaravelQredit\Tenancy\CallbackTenantResolver;    // closure escape hatch
```

### 3. Bind both in a service provider

[](#3-bind-both-in-a-service-provider)

```
use Qredit\LaravelQredit\Contracts\CredentialProvider;
use Qredit\LaravelQredit\Contracts\TenantResolver;
use Qredit\LaravelQredit\Tenancy\SubdomainTenantResolver;

public function register(): void
{
    $this->app->bind(CredentialProvider::class, \App\Qredit\DbCredentialProvider::class);
    $this->app->bind(TenantResolver::class, fn () => new SubdomainTenantResolver('example.com'));
}
```

### 4. Use the facade — tenancy is transparent

[](#4-use-the-facade--tenancy-is-transparent)

```
// HTTP request — uses the bound TenantResolver automatically.
Qredit::createOrder([...]);

// Queue job — ALWAYS pass the tenant id explicitly.
class SettleCartJob implements ShouldQueue
{
    public function __construct(public string $tenantId, public string $orderReference) {}

    public function handle(): void
    {
        Qredit::forTenant($this->tenantId)->calculateFees([...]);
    }
}
```

See [`docs/MULTITENANCY.md`](docs/MULTITENANCY.md) for deep-dive examples including Bagisto, Stancl Tenancy, and Spatie Multitenancy.

---

API surface
-----------

[](#api-surface)

GroupMethodQredit endpointAuth`authenticate()``POST /auth/token`Orders`createOrder($data)``POST /orders`Orders`getOrder($ref)``GET /orders?orderReference=…`Orders`updateOrder($ref, $data)``PUT /orders`Orders`cancelOrder($ref, $reason)``DELETE /orders`Orders`listOrders($query)``GET /orders`Payment req.`createPayment($data)``POST /paymentRequests`Payment req.`getPayment($ref)``GET /paymentRequests?reference=…`Payment req.`updatePayment($ref, $data)``PUT /paymentRequests`Payment req.`deletePayment($ref, $reason)``DELETE /paymentRequests`Payment req.`listPayments($query)``GET /paymentRequests`Payment req.`generateQR($query)``GET /paymentRequests/generateQR`Payment req.`calculateFees($data)``POST /paymentRequests/calculateFees`Payment req.`initPayment($data)``POST /paymentRequests/initPayment`Customers`listCustomers($filters)``GET /customers`Transactions`listTransactions($filters)``GET /payments`Transactions`changeClearingStatus($data)``POST /payments/changeClearingStatus`Webhook`verifyWebhookSignature($p, $a)`—Webhook`processWebhook($p, $a)`—Full request/response shapes are in [`docs/API_REFERENCE.md`](docs/API_REFERENCE.md).

---

Webhook handling
----------------

[](#webhook-handling)

The SDK ships a ready-made webhook controller. Register it with the route macro:

```
Route::qreditWebhook('/qredit/webhook');                  // single-tenant
Route::qreditWebhook('/qredit/webhook/{tenant}');         // multi-tenant (tenant id in URL)
```

Listen for typed events in your `EventServiceProvider`:

```
use Qredit\LaravelQredit\Events\PaymentCompleted;
use Qredit\LaravelQredit\Events\PaymentFailed;
use Qredit\LaravelQredit\Events\OrderCancelled;
use Qredit\LaravelQredit\Events\WebhookReceived;

protected $listen = [
    PaymentCompleted::class => [\App\Listeners\FulfillOrder::class],
    PaymentFailed::class    => [\App\Listeners\NotifyCustomerOfFailure::class],
    OrderCancelled::class   => [\App\Listeners\ReleaseStock::class],
];
```

Each event's `$data` payload carries `_tenant_id` so listeners can scope their work correctly in background jobs.

Signature verification uses the per-tenant `secret_key` — no separate webhook secret needed, matching merchant guide §6/§7.

See [`docs/WEBHOOKS.md`](docs/WEBHOOKS.md) for payload shapes and full event documentation.

---

The checkout widget's `/sign` endpoint
--------------------------------------

[](#the-checkout-widgets-sign-endpoint)

BlockBuilders' hosted checkout widget runs in the customer's browser and needs signed gateway calls — but the secret key must **never** be shipped to the browser. The widget solves this by POSTing `{ body: "..." }` to a merchant-owned `/sign` endpoint and receiving `{ signature: "..." }` back.

The SDK ships that endpoint as `SignController`. Wire it with one line:

```
Route::qreditSign();   // POST /qredit/sign
```

Pass that URL to `PaymentWidget.init`:

```

  PaymentWidget.init({
    containerId: 'payment-widget',
    reference:   '{{ $paymentReference }}',
    token:       '{{ $accessToken }}',
    url:         '{{ route("qredit.sign") }}',
    lang:        app()->getLocale(),
  });

```

The controller pulls the current tenant's secret via your `CredentialProvider`, signs the payload with `HmacSigner`, and returns the hex. The secret never leaves your server.

---

Testing with `FakeQredit`
-------------------------

[](#testing-with-fakeqredit)

```
use Qredit\LaravelQredit\Facades\Qredit;
use Qredit\LaravelQredit\Testing\FakeQredit;

it('creates an order when checkout starts', function () {
    $fake = new FakeQredit([
        'createOrder' => [
            'status'  => true,
            'code'    => '00',
            'records' => [['orderReference' => 'ORDER-1']],
        ],
    ]);

    Qredit::fake($fake);

    $this->post('/checkout/place-order', [/* ... */])->assertOk();

    $fake->assertCalled('createOrder');
    $fake->assertCalledWith('createOrder', fn ($args) => $args[0]['amountCents'] === 3200);
});
```

Full testing guide: [`docs/TESTING.md`](docs/TESTING.md).

---

CLI — `qredit:call`
-------------------

[](#cli--qreditcall)

Because every request needs an HMAC signature, Postman / Insomnia aren't practical. The SDK ships a signed-request CLI:

```
# Every supported endpoint
php artisan qredit:call --list

# Live auth call
php artisan qredit:call auth \
  --api-key=... --secret-key=... --sandbox

# Create order from an inline payload
php artisan qredit:call create-order \
  --payload='{"amountCents":3200,"currencyCode":"ILS",...}'

# From a JSON file
php artisan qredit:call create-payment \
  --payload-file=./tests/fixtures/payment.json

# Dry-run — prints signature + payload without sending
php artisan qredit:call create-order --dry-run \
  --secret-key=... --payload='{...}'

# Flip signature hex case (gateway is strict)
php artisan qredit:call auth --case=upper ...
```

---

Configuration reference
-----------------------

[](#configuration-reference)

Full [`config/qredit.php`](config/qredit.php) options:

KeyEnvDefaultPurpose`api_key``QREDIT_API_KEY``''`Public API key (single-tenant default)`secret_key``QREDIT_SECRET_KEY``''`HMAC secret (single-tenant default)`sandbox``QREDIT_SANDBOX``true`UAT vs production`sandbox_url``QREDIT_SANDBOX_URL``https://apitest.qredit.tech/gw-checkout/api/v1``production_url``QREDIT_PRODUCTION_URL``https://api.qredit.tech/gw-checkout/api/v1``language``QREDIT_LANGUAGE``EN``Accept-Language` header`client.type`— (hardcoded)`TP``Client-Type` header — fixed; don't override`client.version``QREDIT_CLIENT_VERSION``ccc``Client-Version` header — derived from SDK version at runtime; set only to pin`signing.scheme``QREDIT_AUTH_SCHEME``HmacSHA512_O`Authorization prefix`signing.case``QREDIT_SIGNATURE_CASE``upper`Hex case — live UAT accepts only `upper``token_storage.strategy``QREDIT_TOKEN_STRATEGY``cache``cache`, `database`, or `hybrid``debug``QREDIT_DEBUG``false`Log every request + response---

Troubleshooting
---------------

[](#troubleshooting)

### `code 1004 "Bad Signature"`

[](#code-1004-bad-signature)

Signature compared against a stored secret but mismatched. Most common causes:

1. **Credentials not provisioned** — apiKey not in gateway's user database. Verify via a second host: if one returns `1004` and another returns `1705 "User Not Found"`, the account isn't set up.
2. **Signature case** — live UAT rejects lowercase; leave `QREDIT_SIGNATURE_CASE=upper`.

### `code 1005 "Bad Signature"`

[](#code-1005-bad-signature)

The Authorization header is missing or the scheme prefix is wrong. Send `HmacSHA512_O ` (not `HMAC-SHA512` or `Bearer`).

### `code 1012 "Bad Signature"`

[](#code-1012-bad-signature)

Usually a `Client-Type` / `Client-Version` header mismatch — the SDK hardcodes `TP` and derives the version dynamically, so this only fires if something upstream (proxy, CDN, WAF) rewrites those headers.

### `code 1904 "Operation not allowed"`

[](#code-1904-operation-not-allowed)

**Signature validated** but the apiKey lacks permission for the endpoint. Ask Qredit to grant the relevant role (e.g. `ROLE_ORDER_MANAGEMENT`).

### `QreditException: Qredit credentials missing`

[](#qreditexception-qredit-credentials-missing)

The default `ConfigCredentialProvider` couldn't find `QREDIT_API_KEY` / `QREDIT_SECRET_KEY` in config. Either set them in `.env`, or bind a custom `CredentialProvider` for multi-tenant use.

See [`docs/TROUBLESHOOTING.md`](docs/TROUBLESHOOTING.md) for the full diagnostic playbook.

---

Documentation
-------------

[](#documentation)

DocTopic[`docs/API_REFERENCE.md`](docs/API_REFERENCE.md)Every wrapped endpoint — full request / response shapes[`docs/MULTITENANCY.md`](docs/MULTITENANCY.md)Deep-dive with Bagisto / Stancl / Spatie examples[`docs/SIGNING.md`](docs/SIGNING.md)HMAC SHA512 algorithm — step-by-step with the §7 worked example[`docs/WEBHOOKS.md`](docs/WEBHOOKS.md)Event payloads + listener patterns[`docs/TESTING.md`](docs/TESTING.md)`FakeQredit`, Saloon mock clients, feature tests[`docs/TROUBLESHOOTING.md`](docs/TROUBLESHOOTING.md)Every error code, every diagnostic step[`docs/LLM_IMPLEMENTATION_GUIDE.md`](docs/LLM_IMPLEMENTATION_GUIDE.md)Structured reference for AI agents[`docs/QREDIT_SIGNATURE_ISSUE.md`](docs/QREDIT_SIGNATURE_ISSUE.md)Current known issue with UAT credentials[`examples/BasicUsage.php`](examples/BasicUsage.php)Copy-paste recipes for every endpoint[`examples/MultiTenantUsage.php`](examples/MultiTenantUsage.php)Full multi-tenant integration[`examples/WebhookHandler.php`](examples/WebhookHandler.php)Signed-callback handler---

Contributing
------------

[](#contributing)

Contributions are welcome — bug fixes, new endpoint wrappers, tenant resolvers, docs, the lot. See [CONTRIBUTING.md](CONTRIBUTING.md) for the full workflow (fork → branch → test → pint → PR). For security issues, please don't open a public issue; follow [SECURITY.md](SECURITY.md) instead.

Good first issues are labeled [`good first issue`](https://github.com/PaltechHub/QreditPaymentGateway/labels/good%20first%20issue) on the tracker. Questions? Open a [GitHub Discussion](https://github.com/PaltechHub/QreditPaymentGateway/discussions).

Community
---------

[](#community)

- **Bug reports / feature requests:** [GitHub Issues](https://github.com/PaltechHub/QreditPaymentGateway/issues)
- **Open-ended questions, show-and-tell:** [GitHub Discussions](https://github.com/PaltechHub/QreditPaymentGateway/discussions)
- **Security vulnerabilities:** email `shakerawad@paltechhub.com` — see [SECURITY.md](SECURITY.md)
- **Changelog:** [CHANGELOG.md](CHANGELOG.md)
- **Code of Conduct:** [CODE\_OF\_CONDUCT.md](.github/CODE_OF_CONDUCT.md)

Credits
-------

[](#credits)

- Built on [Saloon](https://docs.saloon.dev/) by Sam Carré
- HMAC signing algorithm confirmed against Qredit UAT (2026-04-16)
- All [contributors](https://github.com/PaltechHub/QreditPaymentGateway/graphs/contributors)

License
-------

[](#license)

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

###  Health Score

25

—

LowBetter than 36% of packages

Maintenance62

Regular maintenance activity

Popularity14

Limited adoption so far

Community9

Small or concentrated contributor base

Maturity13

Early-stage or recently created project

 Bus Factor1

Top contributor holds 60% of commits — single point of failure

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.

### Community

Maintainers

![](https://www.gravatar.com/avatar/38d6cb0e176cfbd0300f9390b442d6cddf005bc1fa68b7a012414d4be326e4ec?d=identicon)[ShakerAwad74](/maintainers/ShakerAwad74)

---

Top Contributors

[![majdghithan](https://avatars.githubusercontent.com/u/54972678?v=4)](https://github.com/majdghithan "majdghithan (15 commits)")[![ShakerAwad74](https://avatars.githubusercontent.com/u/121998317?v=4)](https://github.com/ShakerAwad74 "ShakerAwad74 (8 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (2 commits)")

### Embed Badge

![Health badge](/badges/qredit-payment-gateway-laravel-qredit/health.svg)

```
[![Health](https://phpackages.com/badges/qredit-payment-gateway-laravel-qredit/health.svg)](https://phpackages.com/packages/qredit-payment-gateway-laravel-qredit)
```

###  Alternatives

[omnipay/coinbase

Coinbase driver for the Omnipay payment processing library

18570.2k1](/packages/omnipay-coinbase)[yenepay/php-sdk

YenePay SDK for PHP

112.7k](/packages/yenepay-php-sdk)

PHPackages © 2026

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