Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .php-cs-fixer.dist.php
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
<?php

$finder = (new PhpCsFixer\Finder())
->in([__DIR__ . '/tests', __DIR__ . '/examples'])
->append([__DIR__ . '/UnitPay.php']);
->in([__DIR__ . '/src', __DIR__ . '/tests', __DIR__ . '/examples']);

return (new PhpCsFixer\Config())
->setRiskyAllowed(false)
Expand Down
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
# Changelog

### v3.0.0

**Breaking release.** The SDK is no longer a single file in the global namespace: it is now a PSR-4 package (`Unitpay\` → `src/`) split into layers — Http, Api services, Signature, Webhook, Model/Enum, Exception — behind a thin `Unitpay\Unitpay` facade. Nothing changed on the wire; only the PHP surface you call. Step-by-step renames are in [docs/migration-v3.md](docs/migration-v3.md).

* **No compatibility shim is provided.** A `class_alias` would have restored the old class names without the old methods and constants, so the code would compile and then fail at runtime; a full compat layer would have re-created the god class this release removes. Migration is a mechanical one-time edit instead
* Installation is Composer-only: `UnitPay.php` is gone, so the package needs the PSR-4 autoloader and can no longer be `require`d as a single file
* Classes moved: `UnitPay` → `Unitpay\Unitpay` (note the casing), `CashItem` → `Unitpay\Model\CashItem`, `UnitpayIpAllowlist` → `Unitpay\Webhook\IpAllowlist`, and every exception into `Unitpay\Exception\`. Exception class names and their `InvalidArgumentException` / `UnexpectedValueException` parents are unchanged, so existing catch blocks keep working once the imports are updated
* `api('method', [...])` was replaced by four service objects reached from the facade: `payments()`, `subscriptions()`, `payouts()`, `reference()`. Each method takes its required parameters as arguments and the rest in a trailing options array. `getBinInfo` sits on `payouts()`, next to `getSbpBankList`, rather than on `reference()`
* Required parameters are now enforced by the method signatures instead of the runtime `REQUIRED_UNITPAY_METHODS_PARAMS` dictionary — a missing one is an `ArgumentCountError` caught by static analysis, not a `UnitpayValidationException` at request time. `UnitpayUnsupportedMethodException` is no longer thrown for outbound calls (there is no method-name string to mistype) and now applies only to inbound webhooks
* The fiscal dictionaries left `CashItem` for const-classes in `Unitpay\Model\Enum`, dropping the redundant prefix: `CashItem::NDS_20` → `Nds::VAT20`, `CashItem::PAYMENT_OBJECT_COMMODITY` → `PaymentObject::COMMODITY`, `CashItem::PAYMENT_METHOD_PAYMENT_FULL` → `PaymentMethod::PAYMENT_FULL`, `CashItem::MEASURE_KG` → `Measure::KG`, `UnitPay::PAYMENT_TYPE_CARD` → `PaymentType::CARD`. One irregular case: `CashItem::NDS_NONE` → `Nds::NONE`, without a `VAT` prefix
* Inbound webhook verification and the IP allowlist moved to `Unitpay\Webhook\WebhookVerifier`, reached via `$unitpay->webhook()`: `checkHandlerRequest()`, `getHandlerMethod()`, `getHandlerParams()`, `getSuccessHandlerResponse()`, `getErrorHandlerResponse()`, `setAllowedIps()`, `addAllowedIps()`, `getAllowedIps()`, `refreshAllowedIps()`. `getIp()` and `isAllowedIp()` remain `protected` — subclass the verifier instead of the facade to run behind a proxy
* `getSignature()` is no longer public on the facade; direct signing lives in `Unitpay\Signature\SignatureBuilder::build($params, $secretKey, $method)`
* The transport constructor argument changed from a `callable` to `Unitpay\Http\TransportInterface`. Omitting it still yields the default `CurlTransport` with unchanged behavior (cURL with a `file_get_contents()` fallback, TLS verified). One transport instance now serves both the API calls and the webhook IP-feed fetch, so a custom HTTP stack is injected in a single place
* Deprecated payment objects rejected by the public API — `excise`, `gambling_bet`, `gambling_prize`, `lottery_prize`, `composite` — were announced in v2.1.0 for removal in 3.0, but are **kept in this release** and slated for 4.0 instead. Removing them alongside the namespace break would have added migration friction for no functional gain; they remain available as `PaymentObject::EXCISE` and friends, and should not be used in new code
* Unchanged and verified as such: the signature algorithm and the `{up}` delimiter, the mandatory `PHP_INT_MAX` guard against signature forgery, constant-time comparison via `hash_equals`, the `REMOTE_ADDR`-only IP source, TLS verification on the IP-feed fetch, the fail-safe allowlist refresh, the flat request format, response shapes, PHP >= 7.4 support and the zero-dependency policy
* Test suite ported to the new namespaces and grown from 121 to 150 tests: it now mirrors `src/`, uses a `TransportInterface` double instead of a callable, and covers the new seams (service getters and their memoization, shared transport injection)

### v2.1.0

* Telemetry: passive anonymous version fingerprint (`User-Agent` and `X-Unitpay-Client` headers in `api()`, `sdk` parameter in the `form()` URL) — SDK self-identification with no extra network requests and no PII; there is no dedicated telemetry endpoint. `User-Agent: unitpay-php-sdk/<ver> api/<v>` and a JSON `X-Unitpay-Client` header with fields `sdk_version`, `api_version` (the Unitpay API version the SDK targets), `lang`, `lang_version`, `platform` (OS family only), `publisher`. Added constants `UnitPay::VERSION` and `UnitPay::API_VERSION`
Expand Down
41 changes: 26 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,41 +9,47 @@
> PHP SDK for the [Unitpay.ru](https://unitpay.ru) payment REST API.

A thin, stateless SDK: build a signed redirect to Unitpay's hosted payment page or call
the API server-to-server, attach 54-FZ fiscal receipts, and verify inbound webhooks. The
whole library is a single file in the **global namespace**.
the API server-to-server, attach 54-FZ fiscal receipts, and verify inbound webhooks.
Everything hangs off one entry point, `Unitpay\Unitpay`, which hands out service objects.

Official Unitpay documentation: [help.unitpay.ru](https://help.unitpay.ru)

> **Upgrading from 2.x?** 3.0 moves every class into the `Unitpay\` namespace and replaces
> `api('method', [...])` with typed service methods. There is no compatibility shim —
> see the [v3 Migration Guide](docs/migration-v3.md).

## Requirements

* PHP >= 7.4
* ext-json

No runtime dependencies. The SDK is a single file — [`UnitPay.php`](UnitPay.php) —
exposing two classes in the **global namespace**: `UnitPay` and `CashItem`.
No runtime dependencies. `ext-curl` is optional: the default transport uses it when
present and falls back to `file_get_contents()` otherwise.

## Installation

```sh
composer require unitpay/php-sdk
```

Then load the Composer autoloader — its classmap registers both `UnitPay` and `CashItem`:
Then load the Composer autoloader — the package is PSR-4 (`Unitpay\` → `src/`):

```php
require __DIR__ . '/vendor/autoload.php';
```

See [Getting Started](docs/getting-started.md) for the `dev-master` and direct-download
options.
See [Getting Started](docs/getting-started.md) for the `dev-master` option.

## Quick Start

```php
<?php
require __DIR__ . '/vendor/autoload.php';

$unitpay = new UnitPay('unitpay.ru', $secretKey);
use Unitpay\Model\CashItem;
use Unitpay\Unitpay;

$unitpay = new Unitpay('unitpay.ru', $secretKey);

$unitpay
->setBackUrl('https://domain.com')
Expand All @@ -55,30 +61,35 @@ $redirectUrl = $unitpay->form($publicId, 900, $orderId, 'Payment for item', 'RUB
header('Location: ' . $redirectUrl);
```

Prefer a server-to-server call? Use `$unitpay->api('initPayment', [...])` — see
Prefer a server-to-server call? Use `$unitpay->payments()->initPayment(...)` — see
[Getting Started](docs/getting-started.md).

## Key Features

* **Hosted form or API** — `form()` builds a signed redirect URL; `api('initPayment', ...)`
does a server-to-server call.
* **Hosted form or API** — `form()` builds a signed redirect URL;
`payments()->initPayment(...)` does a server-to-server call.
* **Service objects** — `payments()`, `subscriptions()`, `payouts()`, `reference()`, each
with typed methods, so required parameters are enforced at the call site.
* **54-FZ fiscal receipts** — attach `CashItem` line items to any payment.
* **Secure webhooks** — `checkHandlerRequest()` trusts a callback only when both the
SHA-256 signature **and** the source-IP allowlist pass.
* **Secure webhooks** — `webhook()->checkHandlerRequest()` trusts a callback only when both
the SHA-256 signature **and** the source-IP allowlist pass.
* **Dynamic IP allowlist** — refresh Unitpay's webhook IPs from the published feed,
fail-safe.
* **Swappable transport** — inject any `Unitpay\Http\TransportInterface` to plug in your
own HTTP stack or to test without the network.
* **Typed exceptions** — all implement `UnitpayExceptionInterface`.
* **Zero dependencies** — one file, `ext-json` only (`ext-curl` optional).
* **Zero dependencies** — `ext-json` only (`ext-curl` optional).

## Documentation

| Guide | Description |
|-------|-------------|
| [Getting Started](docs/getting-started.md) | Requirements, installation, first payment (form / API) |
| [Fiscal Receipts](docs/receipts.md) | 54-FZ receipt line items via `CashItem` |
| [API Methods](docs/api-methods.md) | Full `api()` method reference and account-level calls |
| [API Methods](docs/api-methods.md) | Full service reference and account-level calls |
| [Webhooks](docs/webhooks.md) | Payment handler + keeping the IP allowlist fresh |
| [Telemetry](docs/telemetry.md) | Anonymous SDK version fingerprint |
| [v3 Migration Guide](docs/migration-v3.md) | Upgrading from 2.x to 3.0 |

Runnable samples for every method group live in [`examples/`](examples).

Expand Down
Loading
Loading