Python SDK for the KosovoPay payment API
Project description
KosovoPay Python SDK
Official Python client for the KosovoPay payment API.
Installation
pip install kosovopay
Requires Python 3.10+ and no additional system dependencies. The SDK uses httpx for HTTP, Pydantic v2 for data parsing, and tenacity for retry logic.
Quick start
import kosovopay
kp = kosovopay.KosovoPay("sk_test_YOUR_KEY")
Use the client as a context manager to ensure the connection pool is closed:
with kosovopay.KosovoPay("sk_test_YOUR_KEY") as kp:
me = kp.me()
print(me.team.name, me.mode)
Hosted checkout
The simplest integration: KosovoPay renders a payment page and redirects the customer on success.
from kosovopay import KosovoPay, CreatePaymentParams, CurrencyCode, CheckoutMode
kp = KosovoPay("sk_test_YOUR_KEY")
params = CreatePaymentParams(
amount=4990, # minor units (cents) — €49.90
currency=CurrencyCode.EUR,
success_url="https://example.com/thanks",
cancel_url="https://example.com/cart",
mode=CheckoutMode.Hosted,
description="Order #1234",
)
payment = kp.payments.create(params)
print(payment.id) # pi_…
print(payment.hosted_url) # redirect the customer here
Direct (bank-embedded) checkout
Bypass the hosted page and send the customer straight to their bank's UI.
Requires specifying the bank_code.
from kosovopay import KosovoPay, CreatePaymentParams, CurrencyCode, CheckoutMode, BankCode
kp = KosovoPay("sk_test_YOUR_KEY")
params = CreatePaymentParams(
amount=1500,
currency=CurrencyCode.EUR,
success_url="https://example.com/thanks",
mode=CheckoutMode.Direct,
bank_code=BankCode.Procredit,
)
payment = kp.payments.create(params)
print(payment.redirect_url) # send the customer to this URL
Retrieving a payment
payment = kp.payments.retrieve("pi_abc123")
print(payment.status) # PaymentStatus.Captured
print(payment.amount_captured)
Cancelling a payment
cancelled = kp.payments.cancel("pi_abc123", reason="customer request")
Refunds
from kosovopay import CreateRefundParams, RefundReason
params = CreateRefundParams(
payment="pi_abc123",
amount=500, # partial refund — €5.00
reason=RefundReason.RequestedByCustomer,
)
refund = kp.refunds.create(params)
print(refund.id, refund.status)
To refund the full amount, omit amount:
full_refund_params = CreateRefundParams(payment="pi_abc123")
refund = kp.refunds.create(full_refund_params)
Cursor pagination
All list() methods return a lazy generator that transparently follows
cursor pages until has_more is false.
from kosovopay import ListPaymentsParams, PaymentStatus
params = ListPaymentsParams(status=PaymentStatus.Captured, limit=50)
for payment in kp.payments.list(params):
print(payment.id, payment.amount)
for refund in kp.refunds.list():
print(refund.id, refund.status)
Webhooks
Register an endpoint
from kosovopay import CreateWebhookEndpointParams, WebhookEventType
we_params = CreateWebhookEndpointParams(
url="https://example.com/webhooks/kosovopay",
enabled_events=[
WebhookEventType.PaymentCaptured,
WebhookEventType.RefundSucceeded,
],
description="Production webhook",
)
endpoint = kp.webhook_endpoints.create(we_params)
print(endpoint.secret) # whsec_… — store this securely
Verifying and consuming events
Always verify using the raw request body bytes (before any JSON decoding).
The Webhook.construct_event call returns a typed Event or raises
WebhookSignatureError.
from kosovopay import Webhook, WebhookSignatureError
# In a Flask/Django/FastAPI view:
raw_body: str = request.body.decode("utf-8") # or request.data
sig_header: str = request.headers["Kosovopay-Signature"]
secret: str = "whsec_…" # from endpoint.secret
try:
event = Webhook.construct_event(raw_body, sig_header, secret)
except WebhookSignatureError as exc:
print("Bad signature:", exc)
return 400
if event.type.value == "payment.captured":
payment = event.as_payment()
print("Captured:", payment.id, payment.amount_captured)
elif event.type.value == "refund.succeeded":
refund = event.as_refund()
print("Refunded:", refund.id)
Rotating a webhook secret
updated = kp.webhook_endpoints.rotate_secret("we_abc123")
print(updated.secret) # new secret — update your environment variable
Error handling
Every network or API error raised by this SDK extends KosovoPayError.
from kosovopay import (
KosovoPayError,
AuthenticationError,
PermissionError,
ValidationError,
RateLimitError,
PaymentError,
AmountBelowMinimumError,
BankNotEnabledError,
ApiError,
)
try:
payment = kp.payments.retrieve("pi_nonexistent")
except AuthenticationError:
print("Invalid API key")
except PermissionError as exc:
print("Not allowed:", exc.error_code)
except ValidationError as exc:
print("Bad param:", exc.param)
except RateLimitError as exc:
print(f"Rate limited — retry after {exc.retry_after}s")
except AmountBelowMinimumError as exc:
print("Amount too small:", exc)
except PaymentError as exc:
print("Payment error:", exc.error_code)
except ApiError as exc:
print(f"Server error {exc.status_code}:", exc)
except KosovoPayError as exc:
print("SDK error:", exc)
Exception hierarchy
KosovoPayError
├── AuthenticationError — invalid / missing API key (401)
├── PermissionError — key lacks scope for this operation (403)
├── ValidationError — invalid request parameters (422)
├── IdempotencyError — idempotency key conflict
├── RateLimitError — too many requests (429); has .retry_after
├── ApiError — unexpected server error (5xx)
├── WebhookSignatureError — webhook HMAC verification failed
└── PaymentError — payment-domain errors
├── AmountBelowMinimumError — amount < bank minimum
├── AmountStepInvalidError — amount not a multiple of bank step
├── BankNotEnabledError — bank not enabled on this key
├── BankUnreachableError — bank temporarily unavailable
├── PaymentNotCancelableError
├── PaymentNotRefundableError
├── RefundExceedsRemainingError
└── PartialRefundUnsupportedError
Money helpers
Amounts are always minor units (e.g. 4990 = €49.90). The SDK ships pure-integer money helpers with no float precision risk.
from kosovopay import format_amount, format_currency, convert
# Format minor units
print(format_amount(4990, decimals=2, symbol="€")) # "€49.90"
print(format_amount(500, decimals=0, symbol="¥")) # "¥500"
print(format_amount(-1250, decimals=2, symbol="$")) # "-$12.50"
# Convert between currencies using a rate string
print(convert(10000, "1.12")) # 11200
# Format using a Currency DTO from the API
currencies = kp.currencies.list()
eur = next(c for c in currencies if c.code.value == "EUR")
print(format_currency(4990, eur)) # "€49.90"
Client-side amount validation
Pre-validate amounts without a round-trip to the server:
from kosovopay import BankCode, CurrencyCode
result = kp.validate_amount(173, CurrencyCode.EUR, BankCode.Onefor)
if not result.valid:
print(result.code) # "amount_step_invalid"
print(result.nearest_valid) # [150, 200]
FX rates
from kosovopay import CurrencyCode
rate = kp.rates.retrieve(CurrencyCode.EUR, CurrencyCode.USD)
print(rate.rate) # "1.0850"
print(rate.stale) # False
Idempotency
Every create / cancel call automatically generates a UUID v4 idempotency
key. You can supply your own to safely retry from application code:
import uuid
idem_key = str(uuid.uuid4())
payment = kp.payments.create(params, idempotency_key=idem_key)
# Retrying with the same key returns the original payment, never a duplicate.
payment_again = kp.payments.create(params, idempotency_key=idem_key)
assert payment.id == payment_again.id
Configuration
kp = KosovoPay(
api_key="sk_live_…",
base_url="https://api.kosovo.sh", # default
api_version="2026-06-01", # default
connect_timeout=10.0, # seconds
request_timeout=30.0, # seconds
max_retries=3, # network + 429 + 5xx are retried
)
Typing
The package ships a py.typed marker file. All public APIs are fully type
annotated and compatible with mypy strict mode.
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file kosovopay-1.0.0.tar.gz.
File metadata
- Download URL: kosovopay-1.0.0.tar.gz
- Upload date:
- Size: 23.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3e243fe7445b502df049c33e9db3f5899f87dfc30f005e3bed19cb0b231cae47
|
|
| MD5 |
4e47c2419d00b075e8817a7d958f3eaa
|
|
| BLAKE2b-256 |
1edf8b467e8279b4622aaa902048b97ca4df62ec9c935097aa368386e5590f17
|
Provenance
The following attestation bundles were made for kosovopay-1.0.0.tar.gz:
Publisher:
release.yml on kosovopay/python-sdk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
kosovopay-1.0.0.tar.gz -
Subject digest:
3e243fe7445b502df049c33e9db3f5899f87dfc30f005e3bed19cb0b231cae47 - Sigstore transparency entry: 1782904472
- Sigstore integration time:
-
Permalink:
kosovopay/python-sdk@901475288a8f320ba5edc5f60e8fbe059032d145 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/kosovopay
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@901475288a8f320ba5edc5f60e8fbe059032d145 -
Trigger Event:
push
-
Statement type:
File details
Details for the file kosovopay-1.0.0-py3-none-any.whl.
File metadata
- Download URL: kosovopay-1.0.0-py3-none-any.whl
- Upload date:
- Size: 22.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
919605b22925b4ed8b30ba473521b46c043cf03e67bdee5c20caa0cf1c0ffa5f
|
|
| MD5 |
42f15d5778834beaee970806697e754e
|
|
| BLAKE2b-256 |
c4745e73f2563d639581510b5ec280ba0080e8f006bc02927fa3b7eabfc12838
|
Provenance
The following attestation bundles were made for kosovopay-1.0.0-py3-none-any.whl:
Publisher:
release.yml on kosovopay/python-sdk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
kosovopay-1.0.0-py3-none-any.whl -
Subject digest:
919605b22925b4ed8b30ba473521b46c043cf03e67bdee5c20caa0cf1c0ffa5f - Sigstore transparency entry: 1782904551
- Sigstore integration time:
-
Permalink:
kosovopay/python-sdk@901475288a8f320ba5edc5f60e8fbe059032d145 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/kosovopay
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@901475288a8f320ba5edc5f60e8fbe059032d145 -
Trigger Event:
push
-
Statement type: