PHPackages                             bankplanet9/milkyway-payments - 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. bankplanet9/milkyway-payments

ActiveLibrary[Payment Processing](/categories/payments)

bankplanet9/milkyway-payments
=============================

Official PHP client for the MilkyWay Payments API (/payments/v1) — initiate, quote, track, and cancel cross-bank payments.

v1.0.1(today)00MITPHPPHP &gt;=8.1CI passing

Since Jun 22Pushed todayCompare

[ Source](https://github.com/bankplanet9/milkyway-php-sdk)[ Packagist](https://packagist.org/packages/bankplanet9/milkyway-payments)[ Docs](https://github.com/bankplanet9/milkyway-php-sdk)[ RSS](/packages/bankplanet9-milkyway-payments/feed)WikiDiscussions main Synced today

READMEChangelog (2)Dependencies (8)Versions (3)Used By (0)

MilkyWay Payments SDK for PHP
=============================

[](#milkyway-payments-sdk-for-php)

[![Packagist](https://camo.githubusercontent.com/43316622d9de0456dc0af741474801d4d0f1e43c354fc5cbfa07374011cbcaa5/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f62616e6b706c616e6574392f6d696c6b797761792d7061796d656e74732e7376673f6c6f676f3d7061636b6167697374)](https://packagist.org/packages/bankplanet9/milkyway-payments)[![Downloads](https://camo.githubusercontent.com/9cd20e968d1193c1f196cf3173445ededb2d5b48334ecb0f9650e27102b0e04c/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f646d2f62616e6b706c616e6574392f6d696c6b797761792d7061796d656e74732e737667)](https://packagist.org/packages/bankplanet9/milkyway-payments)[![CI](https://github.com/bankplanet9/milkyway-php-sdk/actions/workflows/ci.yml/badge.svg)](https://github.com/bankplanet9/milkyway-php-sdk/actions/workflows/ci.yml)[![License: MIT](https://camo.githubusercontent.com/08cef40a9105b6526ca22088bc514fbfdbc9aac1ddbf8d4e6c750e3a88a44dca/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d626c75652e737667)](LICENSE)

Official PHP client for the **MilkyWay Payments API** (`/payments/v1`) — the partner-facing API banks use to initiate, quote, track, and cancel cross-bank payments.

Batteries included:

- **Keycloak client-credentials auth** with in-memory token caching and automatic refresh (plus a one-shot refresh-and-replay on `401`).
- **Retries** with exponential backoff + jitter on transient failures (5xx, 408, network), while deterministic errors (400/401/402/404) are never retried.
- **Typed models &amp; exceptions** — money is [`brick/math`](https://github.com/brick/math)`BigDecimal` (never `float`), status is a backed `enum`, and each HTTP error maps to a specific exception type.
- **Client-agnostic** — built on PSR-18 (HTTP client) and PSR-17 (factories) with [`php-http/discovery`](https://docs.php-http.org/en/latest/discovery.html)auto-discovery, so you can plug in Guzzle, Symfony HttpClient, or anything else.

Requires **PHP 8.1+**.

Install
-------

[](#install)

```
composer require bankplanet9/milkyway-payments
```

You also need a PSR-18 client and PSR-17 factories. If your project does not already provide them, install a pair the SDK can auto-discover, e.g.:

```
composer require guzzlehttp/guzzle nyholm/psr7
```

Quick start
-----------

[](#quick-start)

```
use Bankplanet9\MilkywayPayments\MilkywayOptions;
use Bankplanet9\MilkywayPayments\MilkywayPaymentsClient;
use Bankplanet9\MilkywayPayments\Model\PayRequest;
use Bankplanet9\MilkywayPayments\Model\PrecheckRequest;
use Brick\Math\BigDecimal;

$client = new MilkywayPaymentsClient(new MilkywayOptions(
    baseUrl:      'https://milkyway.stage.planet9.ae',
    tokenUrl:     'https://keycloak.ac8o.planet9.ae/realms/planet9-stage/protocol/openid-connect/token',
    clientId:     'your-client-id',      // issued to your institution
    clientSecret: 'your-client-secret',
));

// 1. Is the recipient bank's service online?
$client->healthcheck('bank-beta', 'card-payout');

// 2. Quote the payment (FX markup + commission applied here).
$quote = $client->precheck(new PrecheckRequest(
    thirdPartyIdDebit: 'bank-beta',
    serviceId:         'card-payout',
    recipientId:       'recipient-9999',
    amountCredit:      BigDecimal::of('100.00'),
    currencyCredit:    'USD',
));
printf("Rate %s, debit %s %s, commission %s\n",
    $quote->rate, $quote->amountDebit, $quote->currencyDebit, $quote->commission);

// 3. Initiate the payment. Pass an idempotency key so retries are safe.
$transactionId = $client->pay(new PayRequest(
    thirdPartyIdDebit: 'bank-beta',
    serviceId:         'card-payout',
    senderId:          'sender-0001',
    recipientId:       'recipient-9999',
    amountCredit:      BigDecimal::of('100.00'),
    currencyCredit:    'USD',
    data:              ['passport' => 'AA1234567'],
), idempotencyKey: bin2hex(random_bytes(16)));

// 4. Poll until the payment reaches a terminal status.
$result = $client->waitForCompletion($transactionId);
printf("Final status: %s\n", $result->status->name);
```

### Bring your own HTTP client

[](#bring-your-own-http-client)

```
$client = new MilkywayPaymentsClient(
    options:    $options,
    httpClient: $myPsr18Client,          // used for API calls
    // tokenHttpClient: a SEPARATE plain client for the token endpoint (must not be
    //                  wrapped in retry/auth); defaults to its own discovered client.
    requestFactory: $myPsr17RequestFactory,
    streamFactory:  $myPsr17StreamFactory,
);
```

Money &amp; precision
---------------------

[](#money--precision)

All monetary fields (`amountCredit`, `amountDebit`, `rate`, `commission`) are `Brick\Math\BigDecimal`. The API encodes money as raw JSON numbers; the SDK reads those values from their **literal textual form** and builds `BigDecimal` from the string, so no binary-float rounding ever touches your amounts — and the original scale (e.g. `100.00`) is preserved on the way out, too.

The `data` field
----------------

[](#the-data-field)

Each service requires extra per-partner fields (sender name, document number, birthday, …) in the `data` array. **Which keys are required depends on your `serviceId` and the recipient bank** — look them up in the Услуги registry at . The server validates `data` against the service's JSON Schema during precheck, so a missing field is rejected before any money moves.

Errors
------

[](#errors)

Every API error throws a subclass of `MilkywayApiException` (carrying `getStatusCode()` and the server's message via `getMessage()` / `getResponseBody()`):

HTTPExceptionMeaning400`MilkywayValidationException`Bad request (invalid amount, missing field, unresolvable FX rate).401`MilkywayAuthException`Token missing/invalid (also thrown if token acquisition fails).402`MilkywayExposureBlockedException`Payment would breach a block-action exposure limit.404`MilkywayNotFoundException`Transaction not found or not owned by your institution.5xx`MilkywayServiceUnavailableException`API or downstream recipient unavailable (retried automatically first).Retries &amp; idempotency
-------------------------

[](#retries--idempotency)

Transient failures (5xx, 408, network exceptions) are retried automatically with exponential backoff + jitter, tunable via `MilkywayOptions`(`maxRetries`, `retryBaseDelay`, `requestTimeout`). Deterministic 4xx responses are never retried.

**`pay()` is only auto-retried when you supply an `idempotencyKey`** — without one, a retry could create a duplicate payment, so the SDK sends it exactly once.

Configuration
-------------

[](#configuration)

OptionDefaultPurpose`baseUrl`— (required)Payments API base URL.`tokenUrl`— (required)Keycloak token endpoint.`clientId` / `clientSecret`— (required)Your institution's credentials.`scope`noneOptional OAuth scope.`tokenRefreshSkew`30sRefresh this long before token expiry.`requestTimeout`30sPer-attempt request timeout (enforce via your HTTP client).`maxRetries`3Max transient-failure retries.`retryBaseDelay`0.5sBase delay for exponential backoff.> Note: per-attempt timeout is honored by the underlying PSR-18 client — configure it on the client you pass in (e.g. Guzzle `timeout`), since PSR-18 has no portable timeout API.

Building from source
--------------------

[](#building-from-source)

```
composer install
composer test        # phpunit
composer lint        # php -l over all sources
```

Releasing
---------

[](#releasing)

Releases are **fully automated** by [semantic-release](https://semantic-release.gitbook.io/)on every push to `main`:

1. Conventional commits are analysed (`feat:` → minor, `fix:`/`perf:` → patch, `!` / `BREAKING CHANGE` → major). No releasable commits → no release.
2. A GitHub release + `vX.Y.Z` tag is created with generated notes.
3. **Packagist** picks up the new tag automatically and publishes the new version.

There is **no build artifact and no registry token** for PHP — the git tag *is* the release, and Packagist syncs from it via a GitHub webhook.

### One-time Packagist setup (maintainers)

[](#one-time-packagist-setup-maintainers)

Do this once so step 3 above works:

1. Sign in at  and **Submit** the repository URL `https://github.com/bankplanet9/milkyway-php-sdk` to register the `bankplanet9/milkyway-payments` package.
2. Enable auto-updates so each tag syncs automatically. Easiest: install the **Packagist** GitHub application (Packagist → your package → *Settings* shows the exact webhook/hook instructions), or add the GitHub service hook with your Packagist API token. Once connected, every `vX.Y.Z` tag pushed by semantic-release appears on Packagist within seconds.

No secrets are stored in CI; the `release` job only needs the default `GITHUB_TOKEN`.

License
-------

[](#license)

MIT — see [LICENSE](LICENSE). Copyright (c) 2026 Planet9.

###  Health Score

39

—

LowBetter than 85% of packages

Maintenance100

Actively maintained with recent releases

Popularity0

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity43

Maturing project, gaining track record

 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

0d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/81781443?v=4)[Bekhruz Iskandarzoda](/maintainers/bekha-io)[@bekha-io](https://github.com/bekha-io)

---

Top Contributors

[![bekha-io](https://avatars.githubusercontent.com/u/81781443?v=4)](https://github.com/bekha-io "bekha-io (3 commits)")

---

Tags

paymentskeycloakfintechsettlementmilkywayplanet9

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/bankplanet9-milkyway-payments/health.svg)

```
[![Health](https://phpackages.com/badges/bankplanet9-milkyway-payments/health.svg)](https://phpackages.com/packages/bankplanet9-milkyway-payments)
```

###  Alternatives

[telnyx/telnyx-php

Official Telnyx PHP SDK — APIs for Voice, SMS, MMS, WhatsApp, Fax, SIP Trunking, Wireless IoT, Call Control, and more. Build global communications on Telnyx's private carrier-grade network.

35729.6k2](/packages/telnyx-telnyx-php)[flow-php/flow

PHP ETL - Extract Transform Load - Data processing framework

84735.1k](/packages/flow-php-flow)[tempest/framework

The PHP framework that gets out of your way.

2.2k31.1k12](/packages/tempest-framework)[cakephp/cakephp

The CakePHP framework

8.8k19.1M1.7k](/packages/cakephp-cakephp)[chargebee/chargebee-php

ChargeBee API client implementation for PHP

758.3M9](/packages/chargebee-chargebee-php)[laudis/neo4j-php-client

Neo4j-PHP-Client is the most advanced PHP Client for Neo4j

185671.3k41](/packages/laudis-neo4j-php-client)

PHPackages © 2026

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