Download the PHP package gobtcpay/php-merchant-sdk without Composer
On this page you can find all versions of the php package gobtcpay/php-merchant-sdk. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Informations about the package php-merchant-sdk
gobtcpay/php-merchant-sdk
PHP client for the GoBTC Pay API — accept Bitcoin payments and track them until they settle on-chain. Two clients share one toolkit:
GoBTCPay— POS terminals. Every request is signed with a fresh HMAC-SHA256 signature + timestamp (per-terminal key).GoBTCPayServer— server-side shop integrations. Authenticates with the merchant's secret key (sk_live_…): create a payment for an order, track it, cancel it, reconcile.
Both share webhook verification and payment polling helpers.
Requirements
- PHP 8.1+
- A PSR-18 HTTP client and PSR-17 factories. Install Guzzle and the SDK auto-discovers it — or pass your own implementation.
Installation
guzzlehttp/guzzle is a suggestion, not a hard dependency: any PSR-18 client
works. With Guzzle installed you don't need to wire anything up.
⚠️ The POS
apiKeyand the serversk_live_…key are secrets. Keep them server-side or on a controlled POS device. Never ship them to a browser.
Quick start
POS terminal (GoBTCPay)
Server-side shop (GoBTCPayServer)
Bring your own HTTP client
Any PSR-18 client + PSR-17 factories work. Pass them explicitly to skip auto-discovery (or to reuse a configured client):
Configuration
GoBTCPay (POS)
| Option | Required | Default | Description |
|---|---|---|---|
apiKey |
yes | — | Per-terminal secret used to sign every request. |
posTerminalId |
no | — | Default terminal for createPayment. |
baseUrl |
no | https://api.gobtcpay.com/public/api/v1.1 |
Override for staging/dev environments. |
timeoutMs |
no | 30000 |
Per-request timeout. |
httpClient |
no | auto-discovered Guzzle | Your PSR-18 client. |
requestFactory |
no | auto-discovered Guzzle/Nyholm | Your PSR-17 request factory. |
streamFactory |
no | auto-discovered Guzzle/Nyholm | Your PSR-17 stream factory. |
GoBTCPayServer
| Option | Required | Default | Description |
|---|---|---|---|
apiKey |
yes | — | Merchant secret key (sk_live_…). pk_live_… rejected. |
baseUrl |
no | https://api.gobtcpay.com/public/api/v1.2 |
Override for staging/dev environments. |
timeoutMs |
no | 30000 |
Per-request timeout. |
maxRetries |
no | 2 |
Retries after the first attempt (network / 429 / 5xx). |
onEvent |
no | — | callable(array $event) called once per attempt. |
httpClient / requestFactory / streamFactory |
no | auto-discovered | Same as above. |
Methods
GoBTCPay
GoBTCPayServer
listPayments() is a generator that pages transparently, newest first:
Auto-polling
watchPayment() returns a PaymentPoller. Because PHP request handlers are
synchronous, poll() is a blocking loop: it calls getPayment on an
interval until the payment reaches one of the statuses it stops at (paid /
cleared / expired / canceled / failed). The interval defaults to and is
clamped to a minimum of 3 seconds.
Options (array keys): paymentId (required), intervalMs, timeoutMs,
immediate (default true), until (list of PaymentStatus), stopOnError.
paidvscleared:paidmeans the funds are confirmed on-chain — the success state for external wallet payments. To stop as soon as that happens, pass'until' => [PaymentStatus::Paid], or react toonPaidwhile polling continues.Stopping is not the same as deciding. The default stop set is "no longer worth polling", not "nothing can change".
expiredin particular is not terminal on the server: the window closing does not close the payment, and funds arriving within the grace period still move it topaidafterwards — so cancelling an order onexpiredcan strand a payment that later succeeds. When your decision is irreversible, read$payment->paidAt(settlement time,nulluntil the payment settles) and$payment->transactionsrather thanPaymentStatus::isFinal().
Webhooks
Register a webhook URL in the merchant dashboard to receive
payment.status.updated events. The handler verifies the X-GoBTCPay-Signature
header (t={timestamp},v1={hmac_hex}), enforces a replay window, and
de-duplicates on eventId.
Guard on hasPaymentData(). A test delivery (see below) arrives correctly
signed, through the same listener, with no payment behind it — $event->test is
true and $event->data is empty. Calling payment() on one throws a
GoBTCPayException; answering 2xx anyway is the right behaviour, and it is what
tells the sender the endpoint works.
handle() returns the parsed WebhookEvent, or null if it was a duplicate.
Use constructEvent() to only verify + parse without dispatching. Always feed
the raw request body — do not decode and re-encode it, or the signature will
not match.
Inspecting and testing endpoints
listWebhooks() returns the endpoints configured for the merchant, and
testWebhook() asks the platform to send a test delivery to one of them:
The test delivery is a normal signed delivery of type payment.status.updated
carrying test: true and no payment data, so your existing handler receives it —
guard with hasPaymentData() as shown above and answer 2xx.
Three things worth knowing before you build a "test my webhook" button on this:
testWebhook()returning normally means the delivery was queued, not that it arrived. Deliveries are dispatched by a scheduled job, so the test lands at your endpoint some time later — and can still fail there. Whether the webhook works is answered at the receiving end, by observing the delivery.- Both calls are merchant-level, so
listWebhooks()doubles as the cheapest probe of whether a key can manage webhooks at all — an empty list is a perfectly successful answer, while a key restricted to a single store is refused with HTTP 403 (AuthException). The refusal does not say why: the same 403 covers a store-scoped key, a revoked key, an unknown key, a publishable key and an inactive merchant. Show the server's message rather than guessing the cause. listWebhooks()returns one page. Compare the page againsttotalCountand page withskipbefore concluding that a URL is not registered.
Plain PHP
Laravel
Symfony
Error handling
Every error extends GoBTCPay\PosApiSdk\Exception\GoBTCPayException:
| Exception | When |
|---|---|
ApiException |
Any API error envelope / non-2xx (->httpStatus, ->body, ->requestId, ->type()). Base for the ones below. |
AuthException |
401 / 403 — key missing, malformed, revoked, not permitted. |
ValidationException |
400 / 402 — request rejected; retrying unchanged won't help. |
NotFoundException |
404 — no such payment or webhook endpoint. |
RateLimitException |
429 — too many requests (->retryAfterMs). |
ServerException |
5xx — the API failed; safe to retry idempotent calls. |
NetworkException |
No response: connection / DNS / TLS / timeout (->isTimeout). |
WebhookSignatureException |
A webhook signature failed verification. |
The server client retries network failures, 429 and 5xx automatically
(maxRetries, default 2) with exponential backoff + jitter. Reads and
idempotent create calls (those with an externalId) are retried; a
create without an externalId is not, so a retry can't double-charge. The
POS client does not retry — a human at the terminal sees the failure and taps
again.
Development
Docker
A Dockerfile is included so that all checks run in an identical environment
locally and in CI:
CI builds this image per pipeline and runs lint, phpstan, and test jobs
against it.
Testing
Unit tests — pure logic, no network. They gate every MR and run in CI:
request signing (HMAC cross-checked against hash_hmac), the envelope
transport (retries, typed exceptions), webhook verification (signature / replay
window / de-dup), and the poller lifecycle.
Integration tests — exercise the SDK end-to-end against a live POS API on the test contour. Opt-in and self-skipping unless credentials are present:
Use test-contour credentials only — never a production key.
Versioning
The API version is pinned inside each client (POS v1.1, server v1.2),
exported as GoBTCPay::API_VERSION / GoBTCPayServer::SERVER_API_VERSION — you
don't put it in a URL. Need a different version or environment? Override
baseUrl.
Publishing
How it works
- A merge into
maintriggers thelint,phpstan, andtestGitHub Actions jobs. - If they pass, the
releasejob runs semantic-release, which reads the Conventional Commits since the last release:fix:bumps a patch version,feat:bumps minor, aBREAKING CHANGE:footer bumps major. Anything else (docs:,chore:,ci:, …) does not trigger a release. - On a release, semantic-release updates
CHANGELOG.md, bumpsSDK_VERSIONinsrc/GoBTCPay.php/src/GoBTCPayServer.php, commitschore(release): x.y.z [skip ci], tags it, and creates a GitHub release. - That push fires a GitHub webhook (Settings → Webhooks, configured on
this repo) that pings
https://packagist.org/api/github. Packagist re-reads the tag from GitHub and publishes it as a new version — no manual "Update" click, no separate publish step.
Distribution is through the public Packagist
package gobtcpay/php-merchant-sdk.
Giving this to a merchant
Nothing special — it's a public Composer package like any other. Point them
at this repo or the Packagist page;
they run composer require gobtcpay/php-merchant-sdk in their own project and
follow Quick start. No token,
no invite, no access request. Their apiKey / sk_live_… credentials are
issued separately and are unrelated to installing the package.
Exception — WordPress/WooCommerce/Shopify plugins: those merchants typically don't run Composer at all, and bundling this SDK's dependencies (Guzzle, PSR interfaces) as-is risks class name collisions with other plugins on the same site. A plugin that embeds this SDK should vendor it at build time with a namespace-prefixing tool (php-scoper or Strauss) and ship the prefixed code inside the plugin's own zip — the merchant never touches Composer or this package directly.
По-русски
**Как это работает.** 1. Мерж в `main` запускает джобы `lint`, `phpstan`, `test` в GitHub Actions. 2. Если они прошли — джоба `release` запускает [semantic-release](https://semantic-release.gitbook.io/), которая читает [Conventional Commits](https://www.conventionalcommits.org/) с прошлого релиза: `fix:` поднимает patch-версию, `feat:` — minor, футер `BREAKING CHANGE:` — major. Остальное (`docs:`, `chore:`, `ci:` и т.п.) релиз не создаёт. 3. При релизе semantic-release обновляет `CHANGELOG.md`, поднимает `SDK_VERSION` в `src/GoBTCPay.php` / `src/GoBTCPayServer.php`, коммитит `chore(release): x.y.z [skip ci]`, ставит тег и создаёт GitHub release. 4. Этот пуш триггерит GitHub webhook (настроен в **Settings → Webhooks** этого репозитория), который стучится в `https://packagist.org/api/github`. Packagist сам подтягивает новый тег с GitHub и публикует версию — без ручного нажатия Update, без отдельного шага публикации. Дистрибуция — через публичный [Packagist](https://packagist.org/), пакет `gobtcpay/php-merchant-sdk`. **Как передать этот пакет мерчанту.** Ничего особенного делать не нужно — это обычный публичный Composer-пакет. Дайте ссылку на этот репозиторий или на [страницу пакета на Packagist](https://packagist.org/packages/gobtcpay/php-merchant-sdk); у себя в проекте мерчант просто выполняет `composer require gobtcpay/php-merchant-sdk` и дальше следует разделам [Installation](#installation) / [Quick start](#quick-start). Токен, приглашение или согласование доступа не требуются. Ключи `apiKey` / `sk_live_…` выдаются отдельно и никак не связаны с установкой пакета. **Исключение — плагины для WordPress/WooCommerce/Shopify.** Такие мерчанты обычно вообще не используют Composer, а прямое встраивание зависимостей SDK (Guzzle, PSR-интерфейсы) рискует конфликтом имён классов с другими плагинами на том же сайте. Плагин, встраивающий этот SDK, должен вендорить его на этапе сборки инструментом с префиксацией неймспейсов ([php-scoper](https://github.com/humbug/php-scoper) или [Strauss](https://github.com/BrianHenryIE/strauss)) и поставлять уже префиксированный код внутри zip-архива плагина — мерчант напрямую с Composer или этим пакетом не взаимодействует.License
MIT — see LICENSE. You may freely use, copy, modify, merge, publish, distribute, sublicense, and sell the software, including in closed-source and commercial products. Keep the copyright notice and license text in copies. Provided "as is", without warranty.
All versions of php-merchant-sdk with dependencies
ext-json Version *
psr/http-client Version ^1.0
psr/http-factory Version ^1.0
psr/http-message Version ^1.1 || ^2.0