FastAPI
The Python SDK source includes a runnable FastAPI 0.162.0 / Python 3.12+ application. It is a reference integration with real native tests, not a separately registered framework plugin. Adapt the existing store's authenticated orders/fulfillment model while preserving the transaction contract.
Source and full guide (opens in a new tab) · Server setup (opens in a new tab)
SDK 0.2.1 is published on PyPI (opens in a new tab); install it in an existing application with pip install lunixi-sdk==0.2.1. To run this reference application, use the matching v0.2.1 source tag (opens in a new tab) and locked environment in the shared setup; no separate framework package is published.
Install and configure
Check out the matching example source and install its locked environment:
git clone --branch v0.2.1 --depth 1 https://github.com/Lunixi-Payment/lunixi-python.git
cd lunixi-python
python3.12 -m venv .venv
.venv/bin/pip install -r requirements-dev.lock
.venv/bin/pip install --no-deps -e .Set protected server environment variables: LUNIXI_PUBLIC_ORIGIN (trusted HTTPS origin), LUNIXI_MERCHANT_ID, LUNIXI_KEY_ID, LUNIXI_PRIVATE_KEY, independent LUNIXI_WEBHOOK_SECRET (16+ characters), independent LUNIXI_SESSION_SECRET (32+ characters), LUNIXI_ENCRYPTION_KEY (base64 random 32 bytes) and LUNIXI_DATABASE (persistent SQLite file outside web root; existing parent directory). Generate/backup keys separately and never commit real values.
Optional: LUNIXI_CHECKOUT_MODE=embedded|hosted (default: embedded), LUNIXI_PREAUTH=true|false, LUNIXI_LOCALE=en|tr. Default gateway is https://api-gateway.lunixi.com; any override is a trusted operator setting. Register <LUNIXI_PUBLIC_ORIGIN>/webhook in Lunixi, preserving raw bytes and delivery headers. Configure the public origin/port and provide a trusted TLS certificate for the commands below.
python -m integrations.cli init
python -m integrations.cli seed --customer-id TRUSTED_CUSTOMER_ID --amount 19.90 --currency TRY
uvicorn --factory integrations.fastapi_app:create_app --host 127.0.0.1 --port 8000 --ssl-keyfile /secure/tls/key.pem --ssl-certfile /secure/tls/cert.pemThe server CLI prints a one-use invitation valid for 15 minutes. Give it only to the intended customer and redact invitation paths in access logs. It is atomically consumed into a native signed Secure/HttpOnly/SameSite session. Checkout POST validates the session CSRF header and persisted customer/order ownership. There is no HTTP endpoint that lets a caller select a customer ID or basket amount. SQLite uses disk-backed per-transaction connections, WAL/FULL synchronization and BEGIN IMMEDIATE.
Payment, worker and recovery
Run reconciliation every minute; capture/refund/void are server-operator commands with existing durable operation IDs:
python -m integrations.cli reconcile --limit 100
python -m integrations.cli operation refund --order-id ORDER_ID --operation-id PERSISTED_REFUND_ID --amount 1.00Pending/uncertain is not success. Reconcile/retry the same ID and amount; a fresh ID cannot bypass an unresolved operation.23 hour replay fences block stale financial POSTs, while authenticated GET remains available. FAILED without certainty stays review. Only authenticated settlement changes/releases confirm capture/refund/void.
Webhook HMAC/freshness/signed identity, authenticated merchant/order/currency/amount/split/fee evidence and native order/stock/inbox/outbox commit atomically. Browser returns never mark paid. Partial capture/incomplete multi-card parents stay on hold; native manual holds and changed snapshots are respected. Outbox rows are retained for the application's existing post-commit notification worker; the sample does not send external emails or ship goods.
The UI uses original Lunixi branding, en/tr text and a per-response CSP nonce. Only the official Lunixi browser bundle renders the form. Available methods remain in the Lunixi panel; PAN/CVV is never collected by these servers.
Native verification
pytest -q tests/frameworks uses actual framework clients, native sessions/users/CSRF and real databases, including wrong-customer/Host denial, raw tamper/replay, rollback after native writes, concurrency, manual holds and changed-order denial. Full checks include SDK canonical/HTTPS tests, Ruff, Mypy, Bandit and dependency audit. External gateway transport is synthetic; no live charge or marketplace approval is claimed. Build and test (opens in a new tab).
Provider browser return
Lunixi hosted, split and 3DS flows POST to /return/<order-key>. A cross-site POST may omit the SameSite=Lax cookie. This informational route therefore ignores the entire body and sends 303 to the same relative /return/<order-key> GET without accessing the session, order, database or gateway. Only that subsequent GET restores normal customer ownership checks and authenticated reconciliation, then redirects to the protected checkout page. Posted status, payment IDs and redirect URLs have no authority.
The return POST has a 64 KiB declared-body bound; enforce that wire limit at the HTTPS proxy too, including chunked requests. The signed webhook retains its separate 1 MiB bound. Only the informational return and signed webhook are exempt from checkout CSRF; all financial customer requests still require CSRF and ownership. Real loopback WSGI/Uvicorn HTTP tests prove no-session303, ignored forged fields and zero database/gateway calls on this hop.
Embedded default and optional backend payment page
The framework examples default to LUNIXI_CHECKOUT_MODE=embedded: the existing form mounts on the merchant website using only https://cdn.lunixi.com/lunixi-checkout.min.js and the official object/token/mount API. Explicit hosted remains supported. IntentResult.payment_page_url reads the optional backend paymentPageUrl; hosted_url validates and returns that exact URL, whose opaque page token can differ from the embedded token. Missing/invalid URLs fail closed; no URL is built from the browser token.
The shared form owns enabled methods, multi-card, installments and insurance in either mode; none requires a redirect. Persisted tokens and page URLs are encrypted with distinct authenticated contexts. The original callback/mode/body/hash/key remain bound to an existing attempt: changing configuration does not create a replacement charge. An older hosted record without a backend URL requires reconciliation. Strict raw webhooks, owner/session/CSRF checks, authenticated settlement and informational 3DS/split returns remain required.
Current source 0.2.1 focused checks: 27 SDK/native Django/FastAPI/Flask cases passed; prior full 0.2.0 verification remains the baseline. No live merchant charge or broad new merchant acceptance was run.
The checkout page’s CSP permits form POSTs only to 'self' and the configured gateway origin (default https://api-gateway.lunixi.com). This preserves wallet/provider and legacy POST handoffs supported by the browser SDK. For another reviewed provider, set server-only LUNIXI_FORM_ACTION_ORIGINS to a comma-separated list of exact HTTPS origins, without spaces, paths, trailing slashes, credentials, query, fragment or wildcards. Invalid configuration fails closed; gateway responses and browser fields never expand this list. Script nonces, frame/connect restrictions and informational return handling remain in force.