Skip to main content

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

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.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

kosovopay-1.1.0.tar.gz (24.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

kosovopay-1.1.0-py3-none-any.whl (23.1 kB view details)

Uploaded Python 3

File details

Details for the file kosovopay-1.1.0.tar.gz.

File metadata

  • Download URL: kosovopay-1.1.0.tar.gz
  • Upload date:
  • Size: 24.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for kosovopay-1.1.0.tar.gz
Algorithm Hash digest
SHA256 f0b7cdfc30ad0a5f0ef7742def8aca78f6949c935ae112ec35769e2d7ec0ba95
MD5 2ca3bb33478035e2cf12e511cc7a9a8f
BLAKE2b-256 f158478083a44af822134d275cd2f892ba4231e5c41b7e751baccb8a3a78da13

See more details on using hashes here.

Provenance

The following attestation bundles were made for kosovopay-1.1.0.tar.gz:

Publisher: release.yml on kosovopay/python-sdk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file kosovopay-1.1.0-py3-none-any.whl.

File metadata

  • Download URL: kosovopay-1.1.0-py3-none-any.whl
  • Upload date:
  • Size: 23.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for kosovopay-1.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 576ef603fadb0377cf921df1a93e49ef54a88b9849ca94b02559e3a9469ec978
MD5 4a7e8213f29fb314faf5f56a83b23f53
BLAKE2b-256 64038bfef1b2f5f6f3d5dfcf350b3f1845b14903d465bf0ed74c2c8f6b2d99cb

See more details on using hashes here.

Provenance

The following attestation bundles were made for kosovopay-1.1.0-py3-none-any.whl:

Publisher: release.yml on kosovopay/python-sdk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page