Async Python client for Kaspi Pay POS private HTTP API
Project description
pykaspi
pykaspi is an async Python client for the Kaspi Pay POS private HTTP API.
The project is extracted from kaspi-pos-automation, but it is a library, not an HTTP server. Your application owns storage, scheduling, polling, webhooks, and security boundaries.
This API is unofficial and may change or stop working without notice. Keep device/session credentials private.
Install
pip install pykaspi
poetry add pykaspi
uv add pykaspi
For local development:
uv sync
Quick Start
import asyncio
from pykaspi import DeviceIdentity, KaspiClient, KaspiSession
async def main() -> None:
device = DeviceIdentity.generate()
async with KaspiClient(device=device) as client:
init = await client.auth.init()
# Kaspi entrance expects local KZ mobile digits, without +7/8 prefix.
await client.auth.send_phone(init["process_id"], "7001234567")
otp = input("SMS code: ")
session = await client.auth.verify_otp(init["process_id"], otp)
# Persist session.token_sn, session.vtoken_secret_b64,
# session.ecdh_private_key_b64, and org fields in secure storage.
qr = await client.qr.create(session, amount=1000)
print(qr.data.qr_operation_id)
print(qr.data.qr_token)
asyncio.run(main())
Restore a saved session:
from pykaspi import KaspiSession
session = KaspiSession.from_base64(
token_sn="...",
vtoken_secret_b64="...",
profile_id=12345,
)
API Surface
client.auth.init()client.auth.send_phone(process_id, phone_number)client.auth.verify_otp(process_id, otp)client.auth.refresh(session)client.qr.create(session, amount)client.qr.status(session, qr_operation_id)client.invoice.client_info(session, phone_number)client.invoice.create(session, phone_number, amount, comment="")client.invoice.details(session, operation_id)client.invoice.cancel(session, operation_id)client.invoice.history(session)client.history.operations(session, end_date)client.history.details(session, operation_id)client.refund.create(session, qr_operation_id, return_amount)client.session.check(session)
Pydantic Models
Payment methods return flexible Pydantic models:
qr = await client.qr.create(session, amount=1000)
print(qr.ok)
print(qr.status_code)
print(qr.data.qr_operation_id)
print(qr.data.qr_token)
Kaspi can add fields without warning, so pykaspi models use extra="allow". Unknown fields are preserved in model_extra and can be accessed as attributes when their names are valid Python identifiers:
raw = qr.raw()
extra = qr.data.model_extra
Known Kaspi PascalCase fields are exposed as Pythonic snake_case attributes. For example, QrOperationId becomes qr.data.qr_operation_id.
Device Identity
DeviceIdentity.generate() does not write files. If you want stable Kaspi device identity, save it explicitly:
device = DeviceIdentity.generate()
device.save("device.json")
device = DeviceIdentity.load("device.json")
device.json is not enough to call payment methods without SMS. It only stores the virtual mobile device identity and signing key. To skip SMS on later runs, also persist the Kaspi session values returned by auth.verify_otp():
{
"token_sn": "...",
"vtoken_secret_b64": "...",
"ecdh_private_key_b64": "...",
"profile_id": 12345,
"organization_id": 67890
}
The local examples store this as session.json, which is ignored by git. Treat both device.json and session.json as secrets. ecdh_private_key_b64 is needed for SignInLite refresh after your process restarts.
Session Lifecycle
Kaspi does not expose a reliable expires_at value for this private session. Treat a session as valid only while client.session.check(session) succeeds.
Recommended backend flow:
from pykaspi import KaspiReauthRequiredError
async def ensure_session(client, session, save_session, require_sms_reauth):
check = await client.session.check(session)
if check.active:
return session
try:
refreshed = await client.auth.refresh(session)
except KaspiReauthRequiredError:
await require_sms_reauth()
raise
await save_session(refreshed)
return refreshed
auth.refresh() uses Kaspi SignInLite and can often recover an inactive session, but it is not guaranteed forever. If Kaspi rejects refresh, pykaspi raises KaspiReauthRequiredError; your app should mark the merchant as requiring SMS re-auth and notify the responsible user.
Live Examples
Create a remote invoice:
PYKASPI_AUTH_PHONE="+77001234567" \
PYKASPI_CLIENT_PHONE="+77007654321" \
uv run python examples/live_invoice.py
Create a QR payment token:
PYKASPI_AUTH_PHONE="+77001234567" \
uv run python examples/live_qr.py
The example values are placeholders. Use a real Kaspi Pay cashier phone for
PYKASPI_AUTH_PHONE and a real Kaspi customer phone for PYKASPI_CLIENT_PHONE.
Development
uv run pytest
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 pykaspi-0.1.0.tar.gz.
File metadata
- Download URL: pykaspi-0.1.0.tar.gz
- Upload date:
- Size: 17.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.17 {"installer":{"name":"uv","version":"0.11.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Arch Linux","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
379a06a35ae8333b82ca45a33f2e594da418d21e422e7829c6409781f542a3c8
|
|
| MD5 |
cc233b6a68585ca54eb1a28d45ef77b9
|
|
| BLAKE2b-256 |
b1e18f4cd8b1b0637f9bfe0dbdd2cac535c84eb3e8e0a6f953f3b086163e1d23
|
File details
Details for the file pykaspi-0.1.0-py3-none-any.whl.
File metadata
- Download URL: pykaspi-0.1.0-py3-none-any.whl
- Upload date:
- Size: 25.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.17 {"installer":{"name":"uv","version":"0.11.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Arch Linux","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
983e366f4cc1ea5701b1d6f4ce84ef72ffcbd893a4b406d33ab70e8d4bc57772
|
|
| MD5 |
bf8aa8dccdccf004be4b467f29654a5d
|
|
| BLAKE2b-256 |
d8733b1382c7bffd506c7c7a539d9754c8a584dbdf733da826efe0587345cbef
|