gopaycommunity/gopay-php-api-v4
Composer 安装命令:
composer require gopaycommunity/gopay-php-api-v4
包简介
GoPay PHP SDK for server-side use — wraps the GoPay Payments API v4.0
README 文档
README
Server-side PHP SDK for new GoPay Payments API v4.
Requires PHP ≥ 8.1. Transport-agnostic — works with any PSR-18 HTTP client.
v3 → v4 migration
See MIGRATION.md for a full breakdown. The SDK is v4-only — not source-level compatible with gopay/payments-sdk v1.
Key changes:
- Gateway URL changed — update your
Configinitialization - Legacy
gw_urlredirect removed — replaceheader('Location: gw_url')withchargePayment(). 3DS challenges still redirect viagetAction()->getRedirectUrl() - Recurrences redesigned — now standalone entities, not attached to a payment
- 11 v3 methods removed — pre-auth capture/void, EET, account statement, payment instruments
Installation
composer require gopaycommunity/gopay-php-api-v4
For the HTTP client you need a PSR-18 implementation. Guzzle 7 is the most common choice:
composer require guzzlehttp/guzzle
Any PSR-18-compatible client works (Symfony HttpClient, Buzz, …).
Quick start
use GoPay\Payments\GoPayClient; use GoPay\Payments\Config; use GoPay\Payments\Environment; // 1. Initialize the client $sdk = new GoPayClient(new Config( environment: Environment::Sandbox, shareableKey: 'YOUR_SHAREABLE_KEY', // optional — for browser SDK initialisation )); // 2. Authenticate (stored internally; token refreshes automatically) $sdk->authenticate('YOUR_CLIENT_ID', 'YOUR_CLIENT_SECRET', 'payment:write payment:read'); // 3. Create a payment $payment = $sdk->createPayment('YOUR_GOID', [ 'amount' => 1000, // 10.00 CZK (in minor units / haléře) 'currency' => 'CZK', 'order_number' => 'ORDER-001', 'customer' => ['email' => 'customer@example.com'], 'callback' => [ 'notification_url' => 'https://yourshop.com/notify', 'return_url' => 'https://yourshop.com/return', ], ]); // 4. Charge using a card token from the browser iframe // (browser SDK's mountCardForm() → user enters card → iframe returns token) $charge = $sdk->chargePayment($payment->getId(), [ 'payment_instrument' => [ 'payment_instrument' => 'PAYMENT_CARD', 'input' => [ 'input_type' => 'CARD_TOKEN', 'card_token' => $cardToken, // from the browser SDK iframe ], ], ]); // 5a. No 3DS needed — poll for final state if ($charge->getAction() === null) { $final = $sdk->awaitChargeState($payment->getId()); echo $final->getState(); // 'SUCCEEDED' } // 5b. 3DS required — redirect the customer if ($charge->getAction()?->getRedirectUrl() !== null) { header('Location: ' . $charge->getAction()->getRedirectUrl()); exit; }
gw_url— backward compatibility. ThePaymentDetailsobject contains agw_urlfield. Do not redirect the customer to it by default. It exists for backward-compat with old redirect-based flows for payment methods not yet implemented on v4. This SDK's flow is always:createPayment()→chargePayment().
Configuration
use GoPay\Payments\Config; use GoPay\Payments\Environment; $config = new Config( environment: Environment::Production, // Environment::Sandbox (default) baseUrl: null, // override base URL (e.g. staging); null = use environment debugLoggingEnabled: false, // log request/response to error_log (default false) onError: null, // callable(\Throwable): void — called before throwing shareableKey: null, // shareable key for browser SDK initialisation );
| Parameter | Type | Default | Description |
|---|---|---|---|
environment |
Environment |
Sandbox |
API environment |
baseUrl |
?string |
null |
Override the resolved URL (e.g. for staging) |
debugLoggingEnabled |
bool |
false |
Log to error_log |
onError |
?callable |
null |
Invoked before every thrown exception |
shareableKey |
?string |
null |
Shareable key for getBrowserKeys() |
Environments
| Environment | Base URL |
|---|---|
Environment::Sandbox |
https://api.sandbox.gopay.com/api/merchant/payments/4.0 |
Environment::Production |
https://api.gopay.com/api/merchant/payments/4.0 |
PSR-18 HTTP client injection
By default, php-http/discovery auto-discovers an installed PSR-18 client. You can inject your own:
use GoPay\Payments\GoPayClient; use GoPay\Payments\Config; $sdk = new GoPayClient( config: new Config(), httpClient: $myPsr18Client, // Psr\Http\Client\ClientInterface requestFactory: $myRequestFactory, // Psr\Http\Message\RequestFactoryInterface streamFactory: $myStreamFactory, // Psr\Http\Message\StreamFactoryInterface );
API reference
Authentication
// Authenticate (client_credentials grant) $sdk->authenticate(string $clientId, string $clientSecret, string $scope): void // Check if a token is stored $sdk->isAuthenticated(): bool // Clear tokens $sdk->logout(): void // Store shareable key for browser SDK $sdk->setShareableKey(string $key): void // Return shareable_key + client_id for browser SDK init (safe to expose to the browser) $sdk->getBrowserKeys(): array{shareable_key: string, client_id: string}
Payments
// Create a payment session $sdk->createPayment(string $goid, array $params): PaymentDetails // Get payment status $sdk->getPaymentStatus(string $paymentId): PaymentDetails // Charge a payment (card token / Google Pay / Apple Pay) $sdk->chargePayment(string $paymentId, array $params): PaymentChargeResponse // Get charge state (poll manually) $sdk->getChargeState(string $paymentId): PaymentChargeStatusResponse // Poll charge state until terminal (throws on FAILED / timeout) // WARNING: blocks the PHP process — see "Production deployment" below. $sdk->awaitChargeState( string $paymentId, int $timeoutSeconds = 30, int $pollIntervalMs = 1_000, ): PaymentChargeStatusResponse // Google Pay configuration (pre-filled paymentDataRequest) $sdk->getGooglePayInfo(string $paymentId): array // Apple Pay configuration (applepayVersion, applePayPaymentRequest) $sdk->getApplePayInfo(string $paymentId): array // Apple Pay merchant validation (server-side; forward validationURL from browser) $sdk->validateApplePayMerchant(string $paymentId, ?array $body = null, ?string $origin = null): array // QR payment information (recipient details + base64 QR image) $sdk->getQrPaymentInfo(string $paymentId, ?string $format = null): QRPaymentDetails
Cards
// Get stored card details $sdk->getCardDetails(string $cardId): PermanentCardTokenDetails // Delete a stored card $sdk->deleteCard(string $cardId): void // Tokenize JWE payload from the browser iframe (returns permanent token) $sdk->tokenizeEncryptedCard(string $payload): PermanentCardTokenDetails
Recurrences (server-side API not available yet — SDK methods are present but calls will fail)
// Create a recurring payment agreement $sdk->createRecurrence(string $goid, array $params): RecurrenceDetails // Get recurrence status $sdk->recurrenceStatus(string $recId): RecurrenceDetails // Stop a recurrence permanently $sdk->stopRecurrence(string $recId): void // Start a recurrence (triggers first charge) $sdk->startRecurrence(string $recId, ?array $params = null): PaymentDetails // Create the next instalment $sdk->recurrenceNext(string $recId, ?array $params = null): PaymentDetails
Refunds (server-side API not available yet — SDK methods are present but calls will fail)
// Refund a payment (full or partial) $sdk->refundPayment(string $paymentId, array $params): RefundDetails // List all refunds for a payment $sdk->listRefunds(string $paymentId): list<RefundDetails> // Get a single refund $sdk->getRefund(string $refundId): RefundDetails
Payment links (server-side API not available yet — SDK methods are present but calls will fail)
// Create a shareable payment link $sdk->createPaymentLink(string $goid, array $params): LinkDetails // Get link status $sdk->linkStatus(string $linkId): LinkDetails // Disable a link $sdk->disableLink(string $linkId): void
Response objects
All API methods return typed objects. Use the provided getters to access fields:
$payment = $sdk->createPayment($goid, [...]); echo $payment->getId(); // unique payment ID echo $payment->getState(); // 'CREATED', 'PAID', etc. echo $payment->getAmount(); // amount in minor units (int) $charge = $sdk->chargePayment($payment->getId(), [...]); echo $charge->getState(); // 'SUCCEEDED', 'AUTHENTICATION_PENDING', etc. echo $charge->getAction()?->getRedirectUrl(); // 3DS URL (null if no redirect needed) $card = $sdk->tokenizeEncryptedCard($jwePayload); echo $card->getToken(); // permanent card token for future charges echo $card->getMaskedPan(); // '411111******1111'
Charge flow for 3DS cards
$charge = $sdk->chargePayment($paymentId, $params); if ($charge->getAction()?->getRedirectUrl() !== null) { // 3DS authentication required — redirect the customer header('Location: ' . $charge->getAction()->getRedirectUrl()); exit; } // No 3DS — charge is complete or in processing; poll for result $final = $sdk->awaitChargeState($paymentId); echo $final->getState(); // 'SUCCEEDED'
Polling payment state after a redirect
After 3DS the customer is redirected to your return_url. At that point use
getPaymentStatus() and PaymentPoller to determine the outcome:
use GoPay\Payments\PaymentPoller; // On your return_url handler: $paymentId = $_GET['payment_id']; do { sleep(2); $payment = $sdk->getPaymentStatus($paymentId); } while (PaymentPoller::isPending($payment->getState())); if (PaymentPoller::isSuccessful($payment->getState())) { // PAID or AUTHORIZED echo 'Payment succeeded: ' . $payment->getState(); } else { // CANCELED or TIMEOUTED echo 'Payment did not complete: ' . $payment->getState(); }
PaymentPoller groups payment states into three buckets:
| Group | States | Meaning |
|---|---|---|
| Pending | CREATED, PAYMENT_METHOD_CHOSEN |
Still in progress — keep polling |
| Successful | PAID, AUTHORIZED |
Completed successfully |
| Failed | CANCELED, TIMEOUTED |
Did not complete |
Post-success states (REFUNDED, PARTIALLY_REFUNDED) are terminal — isTerminal() returns true for them.
QR payment flow
// 1. Create the payment session (same as any other payment) $payment = $sdk->createPayment($goid, [ 'amount' => 1990, 'currency' => 'CZK', 'order_number' => 'ORDER-001', 'customer' => ['email' => 'customer@example.com'], 'callback' => [ 'notification_url' => 'https://yourshop.com/notify', 'return_url' => 'https://yourshop.com/return', ], ]); // 2. Retrieve QR code and recipient details $qr = $sdk->getQrPaymentInfo($payment->getId()); // 'png' (default) or 'svg' $imageBase64 = $qr->getQrCode(); // base64-encoded image // 3. Render to the customer echo '<img src="data:image/png;base64,' . $imageBase64 . '" alt="QR payment">'; echo 'Amount: ' . $qr->getAmount() . ' ' . $qr->getCurrency(); // 4. Poll until the customer pays (webhook-preferred; polling shown for completeness) use GoPay\Payments\PaymentPoller; do { sleep(3); $status = $sdk->getPaymentStatus($payment->getId()); } while (PaymentPoller::isPending($status->getState())); echo PaymentPoller::isSuccessful($status->getState()) ? 'Paid' : 'Not paid';
Error handling
All SDK methods throw on error:
| Exception | When |
|---|---|
GoPaySdkException |
Config errors, auth failures, timeout, argument errors |
GoPayHttpException |
Non-2xx API responses (status + body available) |
use GoPay\Payments\Exception\GoPaySdkException; use GoPay\Payments\Exception\GoPayHttpException; use GoPay\Payments\Exception\ErrorCode; try { $payment = $sdk->createPayment($goid, $params); } catch (GoPayHttpException $e) { echo $e->status; // e.g. 422 var_dump($e->body); // decoded JSON or raw string } catch (GoPaySdkException $e) { echo $e->errorCode->value; // e.g. 'AUTH_TOKEN_MISSING' echo $e->getMessage(); }
Error codes (ErrorCode enum)
| Code | Meaning |
|---|---|
AuthTokenMissing |
No token; call authenticate() first |
AuthRefreshFailed |
Token refresh HTTP error |
AuthInvalidResponse |
Token response missing required fields |
AuthCredentialsMissing |
No stored client credentials |
AuthUnauthorized |
Still 401 after token refresh |
NetworkError |
Transport-level failure, including timeouts |
ChargeTimeout |
awaitChargeState() timed out |
ChargeFailed |
Charge reached FAILED state |
UnexpectedResponse |
API responded with an unexpected body shape |
InvalidConfig |
Bad configuration |
InvalidArgument |
Empty required argument |
onError callback
$sdk = new GoPayClient(new Config( onError: function (\Throwable $e): void { // Fires before every throw — use for logging/monitoring $logger->error('GoPay error', ['exception' => $e]); }, ));
Production deployment
Token caching
GoPayClient stores the OAuth2 access token in memory inside a single instance.
In conventional PHP-FPM / mod_php deployments each HTTP request is a new process,
so every new GoPayClient(...) + authenticate() call makes a fresh round-trip to
the GoPay token endpoint (typically 50–200 ms).
Tokens are valid for several minutes. To avoid re-fetching on every request, store the raw token in a shared cache (APCu, Redis, Memcached) and restore it before making API calls:
use GoPay\Payments\GoPayClient; use GoPay\Payments\Config; use GoPay\Payments\Environment; function getGoPayClient(): GoPayClient { $cacheKey = 'gopay_token_' . md5(CLIENT_ID . SCOPE); $sdk = new GoPayClient(new Config(environment: Environment::Production)); $cached = apcu_fetch($cacheKey, $success); if ($success && is_array($cached)) { // pseudo-code — getHttp() is not yet public; see note below // $sdk->getHttp()->getTokenStore()->setToken($cached['token'], $cached['expires_in']); // $sdk->getHttp()->getTokenStore()->setClientCredentials(CLIENT_ID, CLIENT_SECRET, SCOPE); } else { $sdk->authenticate(CLIENT_ID, CLIENT_SECRET, SCOPE); } return $sdk; }
A first-class
TokenCacheInterface(injectable intoConfig) is planned for a future release. Until then, the pattern above or a singleton per worker process is the recommended approach.
awaitChargeState in web contexts
awaitChargeState() uses usleep() and blocks the PHP worker process for up to
$timeoutSeconds (default 30 s). Under concurrent load this can exhaust the worker
pool. Prefer the webhook-driven pattern for production web servers:
- GoPay POSTs a notification to your
notification_url. - Your handler calls
getChargeState()once and records the result. - Return HTTP 200 immediately.
Use awaitChargeState() only in CLI scripts or environments with ample worker headroom.
Browser SDK compatibility
The PHP SDK handles the server side; the GoPay browser SDK handles the card form in an iframe.
- Browser:
mountCardForm()→ user enters card → iframe submits → returns{ token, card_id }. - Server:
chargePayment($paymentId, ['payment_instrument' => ['payment_instrument' => 'PAYMENT_CARD', 'input' => ['input_type' => 'CARD_TOKEN', 'card_token' => $token]]]).
For browser SDK initialisation, pass the result of getBrowserKeys() to the page:
// Server-side (PHP) $keys = $sdk->getBrowserKeys(); // $keys = ['shareable_key' => '...', 'client_id' => '...']
<!-- Browser page --> <script> GoPayBrowserSDK.init({ clientId: '<?= htmlspecialchars($keys['client_id']) ?>', shareableKey: '<?= htmlspecialchars($keys['shareable_key']) ?>' }); </script>
JWE tokenization flow (return-payload mode)
When the iframe is configured in return-payload mode, it returns a JWE compact serialization string instead of calling /cards/tokens itself. Forward it from the browser to your server, then exchange it for a permanent token:
// Browser posts the JWE payload to your server $jwePayload = $_POST['payload']; $card = $sdk->tokenizeEncryptedCard($jwePayload); // Now charge with the permanent token $sdk->chargePayment($paymentId, [ 'payment_instrument' => [ 'payment_instrument' => 'PAYMENT_CARD', 'input' => ['input_type' => 'CARD_TOKEN', 'card_token' => $card->getToken()], ], ]);
Examples
Runnable scripts in examples/ demonstrate the full payment flow against the GoPay
sandbox. See examples/README.md for setup and usage.
Development
# Run all checks (code style + PHPStan + tests) composer ci # Individual steps composer cs # php-cs-fixer check composer cs:fix # auto-fix code style composer phpstan # PHPStan level 10 composer test # PHPUnit
Regenerating model classes
The PHP model classes in src/Generated/ are auto-generated from the GoPay OpenAPI spec. To regenerate them:
# Requires Docker (no local Java/Node needed) and an internet connection
composer codegen
This fetches the latest spec from https://api-docs.gopay.com/spec/en/payments.yaml, runs the OpenAPI generator in Docker, and copies the output into src/Generated/. Review the diff before committing — in particular check that the namespace (GoPay\Payments\Generated\Model) and ModelInterface compatibility are preserved.
Do not edit files in src/Generated/ by hand. If a model class is wrong, fix the upstream spec.
License
MIT
统计信息
- 总下载量: 0
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 1
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-07-09