paykrypt / paykrypt-php-sdk
Official PHP SDK for the PayKrypt crypto payment gateway API.
Requires
- php: >=8.1
- ext-json: *
- php-http/discovery: ^1.20
- psr/http-client: ^1.0
- psr/http-factory: ^1.1
- psr/http-message: ^1.1
Requires (Dev)
- guzzlehttp/psr7: ^2.7
- phpstan/phpstan: ^1.12
- phpunit/phpunit: ^10.5
- squizlabs/php_codesniffer: ^3.10
Suggests
- guzzlehttp/guzzle: Provides a PSR-18 HTTP client implementation.
- nyholm/psr7: Provides lightweight PSR-7 request/response factories.
- symfony/http-client: Provides a PSR-18 HTTP client implementation.
This package is not auto-updated.
Last update: 2026-08-30 14:24:25 UTC
README
Official PHP SDK for the PayKrypt crypto payment gateway API.
Installation
composer require paykrypt/paykrypt-php-sdk composer require guzzlehttp/guzzle
The SDK is framework-agnostic and uses PSR-18 HTTP clients. If no PSR-18 client can be auto-discovered, install one such as guzzlehttp/guzzle or symfony/http-client.
Requires PHP 8.1 or newer.
Quick Start
use PayKrypt\PayKryptClient; $paykrypt = new PayKryptClient([ 'apiKey' => getenv('PAYKRYPT_API_KEY'), 'baseUrl' => getenv('PAYKRYPT_BASE_URL') ?: 'https://api.paykrypt.io', ]); $intent = $paykrypt->paymentIntents->create([ 'amount' => '100.00', 'currency' => 'USD', 'description' => 'Order #12345', 'customerEmail' => 'customer@example.com', 'allowedChains' => ['ethereum', 'tron'], 'expiresInMinutes' => 60, ]); // Redirect the customer to: // https://gate.paykrypt.io/pay/{$intent['id']}
Configuration
$paykrypt = new PayKryptClient([ 'apiKey' => 'pk_12345678_...', 'baseUrl' => 'https://api.paykrypt.io', 'timeout' => 30_000, 'retries' => 3, 'retryDelayMs' => 250, ]);
All merchant API calls use:
Authorization: Bearer pk_<prefix>_<secret>
The key decides which mode you are in, not baseUrl. A pk_test_<prefix>_<secret> key resolves
to your sandbox merchant and a pk_<prefix>_<secret> key resolves to your live one, against the
same host. The two cannot cross: a test key authenticating against a live merchant is rejected,
and so is the reverse.
baseUrl exists to point the client at a self-hosted or local PayKrypt instance. It is not how
you reach test mode.
if (!$paykrypt->isTestMode()) { throw new RuntimeException('Refusing to run the integration suite against a live key'); }
See Test mode below.
Idempotency
PayKrypt requires an Idempotency-Key on value-creating POST endpoints. The SDK generates one automatically for:
paymentIntents->create()payouts->create()payouts->createWithVerification()refunds->create()refunds->createWithVerification()conversions->convert()
Pass your own stable key when retrying the same app-level action:
$intent = $paykrypt->paymentIntents->create( ['amount' => '100.00', 'currency' => 'USD'], ['idempotencyKey' => 'order:12345'], );
Resources
$paykrypt->paymentIntents->retrieve('pi_...'); $paykrypt->paymentIntents->list(['page' => 1, 'limit' => 20]); $paykrypt->paymentIntents->cancel('pi_...'); $paykrypt->payouts->create([ 'amount' => '95', 'currency' => 'USDT', 'destinationAddress' => 'TXYZ...', 'chainId' => 'tron', ]); $paykrypt->payouts->stats(); $paykrypt->refunds->create([ 'paymentIntentId' => '00000000-0000-0000-0000-000000000000', 'amount' => '50.00', 'reason' => 'Customer requested refund', ]); $paykrypt->webhooks->register([ 'url' => 'https://example.com/webhooks/paykrypt', 'events' => ['payment.confirmed.v1'], ]); // What was actually sent to your endpoints, what they answered, and which attempt it was. $paykrypt->webhooks->deliveries(['limit' => 20, 'paymentIntentId' => 'pi_...']); $paykrypt->addressBook->create([ 'label' => 'Treasury wallet', 'address' => 'TXYZ...', 'chainId' => 'tron', ]); $paykrypt->conversions->preview([ 'fromAssetId' => 1, 'toAssetId' => 2, 'amount' => 10, ]); $paykrypt->currencies->list(); $paykrypt->assets->list(); $paykrypt->assets->listByChain('tron'); $paykrypt->pricing->rates(['currency' => 'USD', 'symbols' => ['BTC', 'ETH', 'USDT']]);
Responses are returned as associative arrays to stay compatible with PayKrypt's evolving API response shapes.
Test mode
A pk_test_ key puts every call into your sandbox merchant. Payments there run through the real
settlement pipeline, so you get the same status transitions, ledger postings and webhooks, with
no blockchain involved and nothing moving on-chain.
Your sandbox key is created with your account and is permanently viewable in the dashboard, so there is no setup step.
$paykrypt = new PayKryptClient(['apiKey' => getenv('PAYKRYPT_TEST_KEY')]); if (!$paykrypt->isTestMode()) { throw new RuntimeException('Refusing to run tests against a live key'); } $intent = $paykrypt->paymentIntents->create(['amount' => '25.00', 'currency' => 'USD']); $paykrypt->test->pay($intent['id'], [ 'scenario' => 'confirm', 'chainId' => 'tron', 'assetSymbol' => 'USDT', ]);
Scenarios: detect, confirm, underpay, overpay, reject. Call pay() twice with the same
txId to simulate a redelivered event and check your handler is idempotent, or twice without one
to build up a multi-transaction payment.
$paykrypt->test->expire($intent['id']); // A sandbox payout waits in `pending` exactly as a real one does. Drive the outcome: $paykrypt->test->completePayout($payout['id'], ['outcome' => 'confirmed']); $paykrypt->test->completeRefund($refund['id'], [ 'outcome' => 'rejected', 'reason' => 'Insufficient hot wallet balance', ]);
Two things to know about these four methods:
- They are unauthenticated, and the gate is the target rather than the caller. Each resolves
the intent, payout or refund and refuses with
403unless the merchant behind it is in test mode. A403therefore means the resource you named belongs to a live business, most often because it was created with a live key. Your key is not sent on these calls. - They are limited to 10 requests per minute per calling IP address. The client retries a
429with backoff and honoursRetry-After, but a suite that fans out will still feel it.
Conversions are refused in test mode with an explanatory error rather than faked, because faking hedge accounting would report numbers that do not mean anything.
Full reference: https://docs.paykrypt.io/api/test-mode
Webhook Verification
PayKrypt signs webhook deliveries with X-PayKrypt-Signature and X-PayKrypt-Timestamp.
Use the secret returned from webhooks->register(...) as PAYKRYPT_WEBHOOK_SECRET.
use PayKrypt\Webhook; use PayKrypt\WebhookVerificationException; $rawBody = file_get_contents('php://input'); $headers = getallheaders() ?: []; try { $event = Webhook::constructEvent( $rawBody, $headers, getenv('PAYKRYPT_WEBHOOK_SECRET') ); if ($event['type'] === 'payment.confirmed.v1') { // Fulfill the order. } } catch (WebhookVerificationException $exception) { http_response_code(400); echo 'Invalid webhook signature'; return; }
The signature payload is:
<millisecond_timestamp>.<raw_body>
The SDK also accepts the older documented aliases Paykrypt-Signature and Paykrypt-Timestamp.
Error Handling
use PayKrypt\PayKryptApiException; try { $paykrypt->paymentIntents->retrieve('pi_missing'); } catch (PayKryptApiException $exception) { error_log($exception->getStatusCode() . ' ' . $exception->getErrorCode() . ' ' . $exception->getMessage()); }
Development
composer validate --strict
composer install
composer lint
composer analyse
composer test
Publishing To Packagist
- Push
maintohttps://github.com/PayKrypt/paykrypt-php-sdk. - Submit the public repository URL to Packagist once under
paykrypt/paykrypt-php-sdk. - Enable Packagist auto-updates through the GitHub app, or configure a GitHub webhook manually:
payload URL
https://packagist.org/api/github?username=PACKAGIST_USERNAME, content typeapplication/json, secret set to your Packagist API token, and push events only. - Tag releases with semantic version tags such as
v1.0.0; Composer reads package versions from VCS tags, so do not add aversionfield tocomposer.json. - Create a GitHub Release from the tag and verify Packagist indexed the new version.
License
MIT