Modernize SDK: full API, 54-FZ receipts, webhook IPs, and QA - #25
Merged
Conversation
…, subscriptions, payouts, account
…reliability fixes (IPv6 normalization, CIDR prefix range, REMOTE_ADDR guard, api docblock)
…malization/prefix-range regression (117 tests)
…er split, renames; review guards (initPaymentApi, webhook)
listopad
force-pushed
the
feature/update-sdk
branch
from
July 23, 2026 13:54
82a5546 to
9b1d870
Compare
…ll-switch, error codes
…witch, handler, API_UNREACHABLE skip, no-propagate)
…complexity caps for the feature
Add native param/return/property type declarations to CashItem, UnitpayIpAllowlist and UnitPay within the PHP 7.4 subset (no union types, no mixed, no strict_types). Money/quantity params (form() $sum, CashItem $count/$price, getCount) and httpGet()'s string|false stay untyped by design — their types remain in PHPDoc. Redundant type-only @param/@return tags dropped; prose, unions and element types kept. Raise PHPStan from level 5 to 6: UnitPay.php is clean; test methods gained : void and data-provider value types. No API or behavior change.
Translate every code comment across the SDK from Russian to English — docblocks and inline comments in UnitPay.php, examples/ and tests/. Comments only: code, string literals (incl. example data 'Доставка'), README/CHANGELOG and docs/ are unchanged. Unit-of-measure glosses use American spelling for consistency. php -l, phpunit (126 tests), php-cs-fixer and phpstan all pass.
…erprint only Handler pre-flight errors are already observable to Unitpay via the webhook error response it receives; api() pre-flight errors are dev-time integration mistakes. So the separate /sdk/telemetry endpoint, opt-in flag, reporter, env kill-switch, error codes and wire-in carried little value — removed. Keeps Layer A (User-Agent + X-Unitpay-Client on api(), sdk token in form()), matching the Stripe/AWS/MongoDB first-party piggyback pattern. Reverts the phpmd threshold bumps; folds the UA/client helpers into fingerprintHeaders.
User-Agent now carries the targeted Unitpay API surface (api/<v>) instead of the PHP version; X-Unitpay-Client reports sdk_version, api_version, lang, lang_version, platform (OS family only) and publisher. Adds UnitPay::API_VERSION; CHANGELOG and the telemetry test updated to match.
- Read the verified getHandlerMethod()/getHandlerParams() instead of $_GET after checkHandlerRequest() - Handle the preauth webhook (now accepted by the handler) and add a default branch, so an unknown method never returns an empty response — which Unitpay treats as a failed callback - Translate a stray Russian comment in the initPayment sample to English - Sync the Telemetry section with the new fingerprint header format
- Extract immutable api() method-param and webhook-method dictionaries into private const (REQUIRED_UNITPAY_METHODS_PARAMS, SUPPORTED_PARTNER_METHODS); they were never reassigned. - Type the $unitPay test properties natively (private UnitPay $unitPay). - Rename caught exception variable $e -> $exception in examples to match the project convention.
…ilure - api(): clear the accumulated fluent-setter params in a finally (on both success and transport failure), symmetric with form(), so a stale receipt/customer no longer leaks into an unrelated later call on a reused instance; a retry after a failure must re-apply the setters - getSignature(): reject a null/empty secret up front — a public call must not silently hash with an empty secret (the appended null coerces to '' and drops out, yielding a plausible but secret-less signature) - setAllowedIps([]): document as fail-closed (an empty allowlist rejects every webhook, not a no-op) - transport seam: document the $headers argument the transport actually receives - add regression tests (empty-secret signing, fail-closed allowlist, cleared params after a failed call); record the api() behavior change in CHANGELOG
Trim README to a ~90-line landing page and move the detailed sections into docs/: getting-started, receipts, api-methods, webhooks, telemetry. Each page has prev/next navigation and a See Also footer; no content was lost. README keeps badges, quick start, key features and a documentation table.
Add a concurrency group with cancel-in-progress, least-privilege permissions (contents: read) and a workflow_dispatch trigger. Install and cache dependencies via ramsey/composer-install and add a non-blocking composer audit step. The PHP 7.4-8.4 matrix and job names are unchanged.
Pin the unordered-list style to asterisk (MD004) and disable MD060 and MD013 to match the repository's existing markdown style (padded tables, long lines). Export-ignore the config from the Composer dist.
listopad
force-pushed
the
feature/update-sdk
branch
from
July 24, 2026 12:58
fef47b7 to
086fbd2
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Brings the single-file SDK up to the current Unitpay backend: 54-FZ receipts,
the full REST API surface (incl. mass payouts), a dynamic webhook IP allowlist,
passive version telemetry, native type declarations, and a first PHPUnit suite +
QA tooling. No runtime dependencies added; still
php >=7.4,ext-json, cURLoptional.
47 files changed (+4095 / −578).
Features
CashItemdictionaries synced with the backend (new VATrates incl. calculated ones; new payment objects; deprecated the ones the
public API rejects) and optional fields (
sum,currency,measure,nomenclatureCode,markCode,markQuantity,pre_text,post_text),serialized only when set.
api()— payments, refunds, confirm/cancel,subscriptions, account/reference calls, and mass payouts, each validating its
required params; an explicit
secretKeyoverrides the instance key foraccount-level methods.
PAYMENT_TYPE_*constants (card, cardInvoice, sbp, sberpay, tinkoffpay,paypal, webmoney) — convenience/typo-guard only, still passed unvalidated.
setAllowedIps()/addAllowedIps()/getAllowedIps()andrefreshAllowedIps()(pulls the published/ips/ips_webhooks.jsonfeed, TLS-verified, fail-safe); CIDR matching forIPv4/IPv6 via
UnitpayIpAllowlist.User-Agent+X-Unitpay-Clientfingerprint onapi()and ansdktoken in theform()URL; no extra requests, no PII.Adds
UnitPay::VERSION/UnitPay::API_VERSION.Security & hardening
getSignature():PHP_INT_MAXguard retained; rejects a null/empty secret upfront (a public call must not silently hash without the secret).
checkHandlerRequest(): constant-timehash_equals()+is_string()guard;rejects an empty secret; accepts the
preauthwebhook.api(): fluent-setter params are cleared once the request has been attempted(success and transport failure), symmetric with
form(), so a stalereceipt/customer can't leak into an unrelated later call.
REMOTE_ADDR(not the spoofableX-Forwarded-For);setAllowedIps([])documented as fail-closed.Reliability & DX
UnitpaySignatureException,UnitpayIpException,UnitpayTransportException,UnitpayUnsupportedMethodException,UnitpayValidationException, all implementingUnitpayExceptionInterfaceandstill extending their former SPL classes (existing
catchblocks keep working).allow_url_fopendependency; falls back to
file_get_contents(). Locale-independent floatformatting so a comma-locale can't corrupt the signature/amount.
CashItem/UnitpayIpAllowlist/UnitPaywithin the PHP 7.4 subset; money/quantity params stay soft-typed bydesign (documented in PHPDoc). PHPStan raised to level 6.
$transport/$request/$clientIp) for testingwithout the network or superglobals.
Tests & QA
PHP_INT_MAXregression), handler verification,api()via injectedtransport, forms, CashItems, IP allowlist (CIDR/IPv6/fail-safe), floats.
parallel-lint,php-cs-fixer(PSR-12),phpstan,phpmd;composer checkruns them all.cache and a non-blocking
composer audit.Docs
docs/(getting-started, receipts, api-methods, webhooks, telemetry).
paymentForm,initPaymentApi,receipt,webhook,paymentInfo,refund,twoStagePayment,subscriptions,payout,accountInfo,offsetAdvance) reading secrets from the environment;config.php/order.phpsplit. All code comments in English.Compatibility (SemVer)
Effectively a minor at the API level (a clean bc-check against
v2.0.6):@deprecated(removal in 3.0).initPaymentrequired params aligned withthe backend;
CashItemrejects non-numeric/non-positivecountand negativeprice) — practically BC: it only rejects input the backend already refused.5.6 → 7.4(Composer gates installation, so widely treatedas minor).
api()no longer retains fluent-setter params after atransport failure — a retry must re-apply the setters.
Verification
composer checkgreen: parallel-lint, php-cs-fixer (0 findings), PHPStan level 6(no errors), PHPMD, PHPUnit (121 tests).