Skip to main content

platico — Python SDK

Official Python SDK for the Platico payment API. Stripe-shaped, zero runtime dependencies, Python 3.9+, fully typed (PEP 561).

Status: v0.1.0 — initial release. API surface stable enough to integrate against; minor versions may include breaking changes until v1.0.

Install

pip install platico

Requires Python 3.9 or newer. Zero runtime dependencies — the SDK uses only the standard library (urllib, ssl, hmac, json).

Quickstart

import os
from platico import Platico

client = Platico(os.environ["PLATICO_API_KEY"])

intent = client.payment_intents.create({
    "amount": 999,  # minor units — 9.99 RSD
    "currency": "rsd",
    "description": "Order #4581",
})

print(intent["id"], intent["status"])

Responses are plain dicts (typed as TypedDicts), so unknown new API fields never break your integration — access them as intent["id"].

Customers and saved cards

customer = client.customers.create({
    "email": "jane@example.com",
    "name": "Jane Doe",
})

# Walk every customer with auto-pagination — one page in memory at a time.
for c in client.customers.list():
    print(c["id"], c["email"])

# Or collect a bounded slice (limit is required — prevents accidental OOM):
first50 = client.customers.list().auto_paging_to_list(limit=50)

The same pagination shape works on every list endpoint: payment_intents.list, payment_methods.list, refunds.list, disputes.list, events.list, webhook_endpoints.list. The returned object is also a single page — read .data, .has_more, .url directly if that's all you need.

Webhook signature verification

from flask import Flask, request
from platico import construct_event
from platico import (
    WebhookSignatureInvalidError,
    WebhookTimestampStaleError,
    WebhookMalformedHeaderError,
)

app = Flask(__name__)
WEBHOOK_SECRET = os.environ["PLATICO_WEBHOOK_SECRET"]

@app.post("/webhooks/platico")
def webhook():
    try:
        event = construct_event(
            request.get_data(),  # RAW bytes — NOT request.json
            request.headers.get("Platico-Signature", ""),
            WEBHOOK_SECRET,
        )
    except (
        WebhookSignatureInvalidError,
        WebhookTimestampStaleError,
        WebhookMalformedHeaderError,
    ) as err:
        return str(err), 400

    # event is parsed and verified — safe to act on
    print(event["type"], event["data"]["object"])
    return "", 200

IMPORTANT: webhooks need the raw request body bytes. JSON body parsers re-serialize the payload, which changes the bytes and breaks the HMAC. In Flask use request.get_data(); in Django use request.body; in FastAPI use await request.body().

Configuration

Platico(
    api_key,
    api_base="https://api.platico.rs",  # default
    timeout=30.0,                        # seconds, default
    max_network_retries=3,               # retries on 5xx / 429 / network only
    telemetry=False,                     # opt-in only
)

Environment variables: PLATICO_API_BASE overrides the base URL, PLATICO_CLIENT_TELEMETRY=true opts into telemetry. The API key is passed explicitly (read it from PLATICO_API_KEY yourself — the SDK never reads a key from the environment implicitly).

Error handling

from platico import CardError, RateLimitError, ApiError

try:
    client.payment_intents.create({"amount": 999, "currency": "rsd", "confirm": True})
except CardError as err:
    print("Declined:", err.code, err.decline_code)
except RateLimitError as err:
    print("Slow down, retry after:", err.retry_after)
except ApiError as err:
    print(err.status_code, err.code, err.request_id)

Error objects never carry your API key, the request/response body, headers, or a stack trace — err.to_dict(), repr(err), and logging are all leak-safe.

Security

  • The SDK refuses non-HTTPS api_base URLs unless the host is localhost / 127.0.0.1 / ::1.
  • TLS is always verified (certificate chain + hostname) via the system trust store. There is no option to disable it.
  • API keys are never written to logs or attached to error objects (not even via a traceback or pickle).
  • Webhook signature verification uses hmac.compare_digest (constant time) with replay protection — rejects both stale and future-dated timestamps.
  • Auto-generated Idempotency-Key on every mutating request; the same key is sent on every retry.
  • Retries attempt only on 5xx, 429, and network errors. Never on other 4xx.

Report security issues to security@platico.rs. See SECURITY.md.

Supply-chain trust

  • Zero runtime dependencies — imports only the Python standard library. No transitive surface to audit.
  • No install-time code — declarative hatchling build backend, no setup.py with executable logic, no build/lifecycle hooks. Installing platico cannot run arbitrary code.
  • PyPI Trusted Publishing (OIDC) + PEP 740 attestations — from the first CI-published release, every artifact is linked to the exact GitHub Actions run and commit SHA that built it. No long-lived publish token exists.
  • Artifact file allowlist — the wheel/sdist contain only the source package, README, LICENSE, and CHANGELOG. Asserted in CI before every publish.
  • Pinned GitHub Actions — every workflow uses: pins a full commit SHA.

Detailed posture in SECURITY.md.

License

Proprietary. See LICENSE.

Release files for platico 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for platico 0.1.0
File Size Uploaded
platico-0.1.0.tar.gz 18.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for platico 0.1.0
File Interpreter ABI Platform
platico-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 45.0 kB

Release files / platico-0.1.0.tar.gz

Download URL platico-0.1.0.tar.gz
Size 18.0 kB
Tags Source
SHA-256 checksum
How to use checksums
aa7da1f8686411444e4f0d5499fdc56febac0406a4c5e34a06c302604c1c09d0
BLAKE2b-256 checksum
How to use checksums
37c0e64e1b67427a53b34a191ff5f678adfba96c365b6f4d679fed39b4c5d557
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.12.9

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 12, 2026.

Transparency log

Release files / platico-0.1.0-py3-none-any.whl

Download URL platico-0.1.0-py3-none-any.whl
Size 27.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f3904c4e9952c3dea51b0e0563dc7ba44846e2bc18dc042dd60881f1ff1103f5
BLAKE2b-256 checksum
How to use checksums
f54dd12c91d26fbe94769912ef013d8399675b69749c67cbde5df8a004f47d88
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.12.9

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 12, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page