Download the PHP package kosovopay/php-sdk without Composer
On this page you can find all versions of the php package kosovopay/php-sdk. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Informations about the package php-sdk
Table of contents
- Why this SDK
- Requirements
- Installation
- Authentication & client setup
- Quickstart
- Core concepts
- Minor units
- Hosted vs. direct checkout
- Idempotency
- API versioning
- Payments
- Refunds
- Banks
- Currencies & FX rates
- Account (
me) - Webhooks
- Verifying a webhook
- Framework integration
- Event types
- Managing webhook endpoints
- Helpers
- Money formatting & conversion
- Local amount validation
- Error handling
- Exception hierarchy
- Error codes
- Type reference
- Retries, timeouts & resilience
- Testing
- Development
- Versioning & support
- License
Why this SDK
| Typed end to end | Every request is a readonly params object; every response is a readonly DTO with enum-typed fields. No associative-array guessing, no stringly-typed statuses. |
| Forward compatible | Unknown enum values โ a bank or event type the platform adds after you ship โ decode to an Unknown case instead of throwing. Old SDK versions keep working. |
| Safe by construction | Mutating calls carry an idempotency key automatically. Retries use exponential backoff, and a mutating 5xx is never retried without an idempotency key โ a network blip can't double-charge a customer. |
| Typed errors | The server error envelope maps to a precise exception class (ValidationException, RateLimitException, PaymentException subclasses, โฆ), each carrying errorCode, errorType, param, requestId and docUrl. |
| Statically verified | PHPStan level max, Pint-clean, 29 tests covering every resource, idempotency, pagination, error mapping and webhook signature verification. |
Requirements
- PHP 8.2 or higher
- ext-bcmath (optional) โ used for exact decimal FX math in
Money::convert(); the SDK falls back to native float math when it is absent - ext-json (bundled with PHP)
Installation
That's it โ no service providers, no config publishing. The client is a plain object you construct with your API key.
Authentication & client setup
Authenticate with a secret key from your KosovoPay dashboard. Keys are environment-scoped โ sk_test_โฆ for the test sandbox, sk_live_โฆ for production.
The constructor accepts the full configuration surface; every argument after the key is optional and shown here with its default:
Never hard-code a live key. Load it from the environment or your secrets manager:
Quickstart
Create a hosted checkout and redirect the buyer to KosovoPay's payment page:
When the buyer finishes, KosovoPay sends a payment.captured webhook and redirects them to your successUrl.
Core concepts
Minor units
All monetary amounts are integers in the currency's minor unit โ cents for EUR/USD, etc. There are no floats anywhere in the money path, which eliminates rounding drift.
| Display | Pass as |
|---|---|
| โฌ49.90 | 4990 |
| $5.00 | 500 |
| ยฅ1200 (JPY, zero-decimal) | 1200 |
Use Money::format() to render a minor-unit integer back to a human string.
Hosted vs. direct checkout
The SDK supports two checkout modes via the CheckoutMode enum:
| Mode | What happens | You use |
|---|---|---|
CheckoutMode::Hosted (default) |
KosovoPay renders the payment page and bank selection. | $payment->hostedUrl |
CheckoutMode::Direct |
You pick the bank up front; KosovoPay returns a bank redirect URL. Requires bankCode. |
$payment->redirectUrl |
Idempotency
Every mutating call (payments->create, refunds->create) accepts an optional idempotency key. If you omit it, the SDK generates a ULID automatically, so an in-flight retry never creates a duplicate charge. Supply your own key (e.g. your order ID) to make the operation idempotent across your retries too:
A key is valid for 24 hours. Reusing it with a different payload raises an IdempotencyException.
API versioning
The API is date-versioned. The SDK pins a version via the Kosovopay-Version header (default 2026-06-01) so the response shape never changes underneath you. Upgrade deliberately by bumping apiVersion in the constructor after reading the changelog.
Payments
Create โ hosted checkout
Create โ direct checkout
You select the bank; KosovoPay returns a redirect straight to it. bankCode is required in this mode.
The params object validates itself on construction. Passing
mode: Directwithout abankCode, a non-positiveamount, or a non-HTTP(S)successUrlthrowsInvalidArgumentExceptionbefore any network call.
Retrieve
List & paginate
payments->all() returns a lazy iterator that transparently walks every page using cursor pagination โ you never touch starting_after:
Filter options on ListPaymentsParams: status, bankCode, currency, merchantReference, createdGte, createdLte, limit, startingAfter, endingBefore.
Timeline
A chronological audit trail for a single payment, returned as a typed Collection<TimelineEvent>:
Cancel
Cancelling a payment that is no longer cancelable raises PaymentNotCancelableException.
Refunds
Create
Omit amount for a full refund; pass a value in minor units for a partial refund.
Not every bank supports partial refunds. Check
$bank->capabilities->refunds->partialfirst (see Banks) โ a partial refund to an unsupported bank raisesPartialRefundUnsupportedException. Refunding more than the remaining balance raisesRefundExceedsRemainingException.
Retrieve
List
Banks
Supported banks: BankCode::Procredit, BankCode::Procard, BankCode::Onefor.
Currencies & FX rates
Pair the rate with Money::convert() for exact minor-unit conversion.
Account (me)
Identify the key in use โ its team, environment, usable banks and default currency:
keyPrefix is the only key material safe to log โ it identifies the key without exposing the secret.
Webhooks
KosovoPay notifies your server of events (a captured payment, a succeeded refund) by POSTing a signed JSON event to your endpoint. Always verify the signature before trusting the payload.
Verifying a webhook
constructEvent() does three things: verifies the HMAC-SHA256 signature in constant time, enforces a 5-minute replay window on the timestamp, and decodes the raw body into a typed Event. The signed payload is "{timestamp}.{raw_body}" โ which is why you must verify against the unmodified request body.
The signature header is exposed as a constant if you need it: Webhook::SIGNATURE_HEADER ("Kosovopay-Signature"). Adjust the replay tolerance with the fourth argument: Webhook::constructEvent($payload, $sig, $secret, tolerance: 600).
The decoded Event exposes id, type, created, livemode, apiVersion, the raw object, and previousAttributes. Use $event->asPayment() / $event->asRefund() to hydrate the affected resource into its typed DTO, and $event->createdAt() for a DateTimeImmutable.
Framework integration
Laravel
Symfony
Event types
WebhookEventType |
Wire value | $event->asโฆ() |
|---|---|---|
PaymentCreated |
payment.created |
asPayment() |
PaymentCaptured |
payment.captured |
asPayment() |
PaymentFailed |
payment.failed |
asPayment() |
PaymentCanceled |
payment.canceled |
asPayment() |
PaymentExpired |
payment.expired |
asPayment() |
RefundSucceeded |
refund.succeeded |
asRefund() |
RefundFailed |
refund.failed |
asRefund() |
Any event type added in the future arrives as WebhookEventType::Unknown โ handle it with a default arm rather than a crash.
Managing webhook endpoints
Register, list, rotate, and delete endpoints programmatically:
Helpers
Money formatting & conversion
Local amount validation
Catch amount_below_minimum / amount_step_invalid before a round-trip by checking an amount against a bank's live capabilities:
Error handling
Every non-2xx response is converted into a typed exception. Catch the specific subclass you care about, or the KosovoPayException base for a catch-all. Every exception carries the full error envelope.
Every KosovoPayException exposes:
| Property | Type | Meaning |
|---|---|---|
getMessage() |
string |
Human-readable summary |
errorCode |
?string |
Stable machine code, e.g. amount_below_minimum |
errorType |
?string |
Error family, e.g. validation_error |
param |
?string |
The offending request field, when applicable |
requestId |
?string |
Correlation id โ always include this in support tickets |
docUrl |
?string |
Link to the docs for this error |
statusCode |
int |
HTTP status |
retryAfter (RateLimitException only) |
?int |
Seconds to wait before retrying |
Exception hierarchy
Resolution order: an exact code match wins; otherwise the error type family is used; otherwise it falls back to ApiException. An unrecognised code from a newer API never crashes the SDK.
Error codes
| Code | Maps to | Notes |
|---|---|---|
missing_key, invalid_key |
AuthenticationException |
|
invalid_request |
ValidationException |
check ->param |
resource_missing |
ValidationException |
404 |
unknown_api_version |
ValidationException |
bad Kosovopay-Version |
currency_not_supported |
ValidationException |
|
rate_unavailable |
ValidationException |
FX feed down |
idempotency_payload_mismatch, idempotency_conflict |
IdempotencyException |
|
rate_limited |
RateLimitException |
honour ->retryAfter |
amount_below_minimum |
AmountBelowMinimumException |
|
amount_step_invalid |
AmountStepInvalidException |
|
bank_not_enabled |
BankNotEnabledException |
|
bank_unreachable |
BankUnreachableException |
transient โ retryable |
payment_not_cancelable |
PaymentNotCancelableException |
|
payment_not_refundable |
PaymentNotRefundableException |
|
refund_exceeds_remaining |
RefundExceedsRemainingException |
|
partial_refund_unsupported |
PartialRefundUnsupportedException |
|
internal_error |
ApiException |
Type reference
Enums
| Enum | Cases |
|---|---|
CheckoutMode |
Hosted, Direct |
BankMode |
Test, Live |
BankCode |
Procredit, Procard, Onefor, Unknown |
CurrencyCode |
The full ISO 4217 circulating set (155 currencies) โ EUR, USD, GBP, JPY, CHF, CNY, AUD, CAD, โฆ ALL, RSD, MKD, plus Unknown. Each case's value is its ISO code. |
PaymentStatus |
Pending, Authorized, Captured, PartiallyRefunded, Refunded, Failed, Canceled, Unknown |
RefundStatus |
Pending, Succeeded, Failed, Unknown |
RefundReason |
RequestedByCustomer, Duplicate, Fraudulent, Other |
WebhookEventType |
PaymentCreated, PaymentCaptured, PaymentFailed, PaymentCanceled, PaymentExpired, RefundSucceeded, RefundFailed, Unknown |
Enums marked with Unknown are forward-compatible: any value the platform introduces later decodes to Unknown rather than throwing. Always include a default/Unknown arm when matching on them.
Key response objects
Payment โ id, status: PaymentStatus, mode: BankMode, amount, amountCaptured, amountRefunded, currency: CurrencyCode, bankCode: ?BankCode, merchantReference, description, payer: ?Payer, lineItems, metadata, fx: ?Fx, lastError, expires, captured, created, refunds, checkoutMode: ?CheckoutMode, hostedUrl, redirectUrl ยท methods: createdAt(): DateTimeImmutable
Refund โ id, payment, amount, status: RefundStatus, reason: ?RefundReason, failureReason, created, succeededAt ยท methods: createdAt(): ?DateTimeImmutable
Bank โ code: BankCode, displayName, logoUrl, enabled, modes, capabilities: BankCapabilities
BankCapabilities โ currencies: list<CurrencyCode>, minAmount, amountStep, refunds: RefundCapability
RefundCapability โ supported: bool, partial: bool
Currency โ code: CurrencyCode, name, symbol, decimals, isDefault
Rate โ from: CurrencyCode, to: CurrencyCode, rate: string, syncedAt, stale
Me โ team: Team, mode: BankMode, keyPrefix, enabledBanks: list<BankCode>, defaultCurrency: ?CurrencyCode
WebhookEndpoint โ id, url, description, enabledEvents: list<WebhookEventType>, status, mode: BankMode, created, secret
Event โ id, type: WebhookEventType, created, livemode, apiVersion, data, object, previousAttributes ยท methods: asPayment(), asRefund(), createdAt()
Single-page lists (banks, currencies, webhookEndpoints, payment timeline) return a typed Collection<T> implementing Countable and IteratorAggregate โ iterate it directly, call ->count(), or grab ->all() / ->data for the array. Paginated lists (payments, refunds) return a lazy iterator instead.
Retries, timeouts & resilience
The connector retries transient failures with exponential backoff, governed by maxRetries (default 3) and an internal 500 ms base interval (โ ~0.5s, 1s, 2s).
| Failure | Retried? |
|---|---|
| Network / connection error | โ always |
429 Too Many Requests |
โ always |
5xx on a GET/HEAD |
โ |
5xx on a mutating call with an idempotency key |
โ (safe โ the key dedupes) |
5xx on a mutating call without an idempotency key |
โ (could double-charge) |
4xx (validation, auth, etc.) |
โ (deterministic โ won't change) |
Because the SDK auto-attaches an idempotency key to every mutating call, your create operations are retried safely out of the box.
Tune timeouts via the constructor (connectTimeout, requestTimeout).
Testing
The client is backed by a Saloon connector, so you can swap in a MockClient and assert against requests โ no network, fully deterministic:
Development
The codebase is held to PHPStan level max with zero suppressions โ no @phpstan-ignore, no assert()-to-silence, no blind casts. Decoded JSON is narrowed through a dedicated coercion layer so types are real, not asserted.
Versioning & support
- The SDK follows semantic versioning. Breaking changes only land in a new major.
- The API is date-versioned independently and pinned via the
Kosovopay-Versionheader โ your integration won't shift under you when the platform evolves. - Found a bug or need help? Include the
requestIdfrom the relevantKosovoPayExceptionโ it lets support trace the exact call.
License
KosovoPay License 1.0 โ free to use, including commercially, at no charge. Modifying, forking, redistributing, or reverse-engineering the SDK is not permitted; it is maintained solely by KosovoPay. See LICENSE.
All versions of php-sdk with dependencies
saloonphp/saloon Version ^3.0
saloonphp/pagination-plugin Version ^2.0
symfony/uid Version ^6.0 || ^7.0