PHPackages                             pongsit/scb - 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. pongsit/scb

ActiveLibrary

pongsit/scb
===========

SCB Partners API (Thai QR bill payment) — framework agnostic

v0.1.2(yesterday)03↑2900%MITPHPPHP &gt;=7.4

Since Jul 23Pushed yesterdayCompare

[ Source](https://github.com/ppppongsit/scb)[ Packagist](https://packagist.org/packages/pongsit/scb)[ RSS](/packages/pongsit-scb/feed)WikiDiscussions main Synced today

READMEChangelogDependenciesVersions (3)Used By (0)

pongsit/scb
===========

[](#pongsitscb)

SCB Partners API (Thai PromptPay bill-payment QR) for PHP 7.4+. No framework, no dependencies beyond `ext-curl` and `ext-json` — drop it into a plain-PHP app, a Laravel app, or anything else.

Why this exists
---------------

[](#why-this-exists)

Every project that takes money re-implements the same four calls, and the same mistake keeps coming with it: trusting the webhook body. The callback endpoint is public and SCB signs nothing, so anyone can POST `{"amount": "5000.00"}`. This package never reads the amount from the request — it re-fetches the transaction from SCB over an authenticated call and compares that amount to what your application says is due.

Install
-------

[](#install)

```
composer require pongsit/scb
```

Configure
---------

[](#configure)

```
use Pongsit\Scb\Config;

$config = Config::fromArray([
    'clientId'     => '...',
    'clientSecret' => '...',
    'merchantId'   => '...',   // biller id
    'ppId'         => '...',   // PromptPay id on the QR
    'ref3'         => 'RAB',   // as registered with SCB
    'env'          => 'sandbox',   // sandbox | uat | production
]);
```

`env` decides the host. Nothing else changes between environments, so a project can run against `sandbox` until the day it goes live.

Show a QR
---------

[](#show-a-qr)

```
use Pongsit\Scb\{Client, Qr};

$qr = new Qr(new Client($config));

$result = $qr->create(
    365.00,          // amount due
    'AJNUNU',        // ref1 — appears on the payer's slip
    (string) $orderId // ref2 — comes back in the webhook, must identify your row
);

echo '';
```

`ref1`/`ref2`/`ref3` must be 1–20 characters of `A-Z0-9`; the package uppercases and validates them instead of letting SCB reject the call later.

Creating a QR moves no money — safe to call while testing.

Take the callback
-----------------

[](#take-the-callback)

Implement three methods so the package can check the payment without knowing anything about your schema:

```
use Pongsit\Scb\{SettlementStore, Transaction};

class OrderStore implements SettlementStore
{
    public function expectedAmount($ref2)
    {
        $row = /* SELECT price FROM orders WHERE id = $ref2 */;
        return $row ? (float) $row['price'] : null;   // null = I don't know this reference
    }

    public function isSettled($ref2)
    {
        return /* SELECT confirm FROM orders WHERE id = $ref2 */;
    }

    public function settle($ref2, Transaction $transaction)
    {
        // money is confirmed and the amount is right — do your thing
    }
}
```

Then the endpoint SCB calls:

```
use Pongsit\Scb\{Client, Webhook};

$webhook = new Webhook(new Client($config), function ($message, $context) {
    error_log($message . ' ' . json_encode($context));
});

$result = $webhook->handle(null, new OrderStore());
$webhook->acknowledge($result);

if ($result->needsAttention()) {
    // underpaid, or a reference we don't recognise — worth an alert
}
```

`handle()` returns a `WebhookResult` with one of:

statusmeaning`settle()` called`settled`amount confirmed and sufficientyes`already_settled`SCB retried a callbackno`underpaid`SCB confirms less than is dueno`unknown_reference``expectedAmount()` returned nullno`not_found`SCB has no record of the transactionno`invalid_payload`body was not usable JSONnoThe package acknowledges every case with SCB's expected `resCode 00`. Retrying will not turn an underpayment into a full one, and an unacknowledged callback is retried forever.

Verify a payment yourself
-------------------------

[](#verify-a-payment-yourself)

```
use Pongsit\Scb\Verify;

$verify = new Verify(new Client($config));

$txn = $verify->byReference('2026-07-23', 'AJNUNU', '142');   // bank agnostic
$txn = $verify->byTransactionId($transactionId, $sendingBank); // bank code required
```

Prefer `byReference()`. `byTransactionId()` needs the payer's bank code, which is **not** always `014` — a payer on another bank will not be found if you hardcode it.

Rules the package enforces
--------------------------

[](#rules-the-package-enforces)

- The amount always comes from SCB, never from the request body.
- Less than the expected amount is never settled.
- A reference the application does not recognise is never settled.
- A repeated callback settles once.
- `requestUId` is a fresh UUID per call, not a user id.
- Credentials are never written to the log callback.

Tests
-----

[](#tests)

```
php tests/offline-test.php
```

No network, no database — covers the money rules above.

###  Health Score

34

—

LowBetter than 75% of packages

Maintenance100

Actively maintained with recent releases

Popularity4

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity24

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% 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.

###  Release Activity

Cadence

Every ~0 days

Total

2

Last Release

1d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/2c922244b98429f93d976ead8431298571624e01db8ae9f0a4ac692528ad982b?d=identicon)[pongsit](/maintainers/pongsit)

---

Top Contributors

[![ppppongsit](https://avatars.githubusercontent.com/u/173520948?v=4)](https://github.com/ppppongsit "ppppongsit (2 commits)")

### Embed Badge

![Health badge](/badges/pongsit-scb/health.svg)

```
[![Health](https://phpackages.com/badges/pongsit-scb/health.svg)](https://phpackages.com/packages/pongsit-scb)
```

PHPackages © 2026

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