Skip to main content

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


Download files

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

Source Distribution

pykaspi-0.1.0.tar.gz (17.8 kB view details)

Uploaded Source

Built Distribution

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

pykaspi-0.1.0-py3-none-any.whl (25.9 kB view details)

Uploaded Python 3

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

Hashes for pykaspi-0.1.0.tar.gz
Algorithm Hash digest
SHA256 379a06a35ae8333b82ca45a33f2e594da418d21e422e7829c6409781f542a3c8
MD5 cc233b6a68585ca54eb1a28d45ef77b9
BLAKE2b-256 b1e18f4cd8b1b0637f9bfe0dbdd2cac535c84eb3e8e0a6f953f3b086163e1d23

See more details on using hashes here.

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

Hashes for pykaspi-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 983e366f4cc1ea5701b1d6f4ce84ef72ffcbd893a4b406d33ab70e8d4bc57772
MD5 bf8aa8dccdccf004be4b467f29654a5d
BLAKE2b-256 d8733b1382c7bffd506c7c7a539d9754c8a584dbdf733da826efe0587345cbef

See more details on using hashes here.

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