SDKs

Python

The official LuniPay SDK for Python 3.8 and newer. Stripe-style resource classes, cursor pagination, typed errors, and webhook signature verification.

Install

pip install lunipay

Requires Python 3.8+. Ships with a py.typed marker so mypy and pyright pick up the inline annotations automatically.

Initialize

Set your secret key on the module once at startup, or pass api_key= to each call:

import os
import lunipay

lunipay.api_key = os.environ["LUNIPAY_SECRET_KEY"]

# Optional overrides.
lunipay.api_base = "https://lunipay.io/api"
lunipay.api_version = "2026-04-14"

Server-side only

Secret keys never belong on a user device. Use the SDK from Django, FastAPI, Flask, Celery, or whatever backend framework you prefer.

Resources

Every LuniPay resource is a top-level class on the module. Method names mirror Stripe's conventions: create, retrieve, modify, list, delete.

# Checkout sessions
session = lunipay.CheckoutSession.create(
    amount=5000,
    currency="usd",
    success_url="https://example.com/thanks?session_id={CHECKOUT_SESSION_ID}",
)

# Customers
customer = lunipay.Customer.create(
    email="ada@example.com",
    first_name="Ada",
    last_name="Lovelace",
)
customer = lunipay.Customer.retrieve(customer["id"])
lunipay.Customer.modify(customer["id"], phone="+1-876-555-0100")
lunipay.Customer.delete(customer["id"])

# Invoices
invoice = lunipay.Invoice.create(
    customer=customer["id"],
    currency="usd",
    payment_terms="net_30",
    line_items=[
        {"description": "Consulting", "quantity": 10, "unit_price_cents": 1000},
    ],
)

# Payments
payment = lunipay.Payment.retrieve("pay_01JRZK...")

# Payment links
lunipay.PaymentLink.create(
    amount=2500,
    currency="usd",
    type="MULTI",
    name="Donation drive",
)

# Webhook endpoints
lunipay.WebhookEndpoint.create(
    url="https://example.com/webhooks/lunipay",
    enabled_events=["checkout.session.completed", "payment.succeeded"],
)

Pagination

Every list() method returns a ListObject. For large result sets, use auto_paging_iter() — it yields every row across every page, fetching them lazily.

# One-shot — single page.
page = lunipay.Customer.list(limit=20)
for customer in page:
    print(customer["email"])

# Auto-pagination — walks every page.
for customer in lunipay.Customer.list().auto_paging_iter():
    print(customer["email"])

Errors

Every non-2xx response raises a subclass of lunipay.error.LuniPayError. Branch on the exception class or on e.code.

import lunipay

try:
    lunipay.CheckoutSession.create(
        amount=1, currency="usd", success_url="https://example.com/x"
    )
except lunipay.error.InvalidRequestError as e:
    if e.code == "amount_too_small":
        ...  # show "at least $0.50" in your UI
    else:
        raise
except lunipay.error.RateLimitError:
    ...  # back off and retry
except lunipay.error.APIConnectionError:
    ...  # network issue — retry with backoff

Exception classes: LuniPayError (base), InvalidRequestError, AuthenticationError, PermissionError, RateLimitError, IdempotencyError, APIError, APIConnectionError.

Idempotency

Every mutating method accepts an idempotency_key keyword argument:

import uuid
import lunipay

key = str(uuid.uuid4())

lunipay.CheckoutSession.create(
    amount=5000,
    currency="usd",
    success_url="https://example.com/thanks",
    idempotency_key=key,
)

Webhooks

Use lunipay.Webhook.construct_event to verify signatures and parse the event in one call. Always pass the raw request body — do not decode and re-encode.

import os
import lunipay
from flask import Flask, request

app = Flask(__name__)

@app.post("/webhook")
def webhook():
    payload = request.get_data()  # raw bytes
    sig = request.headers.get("LuniPay-Signature", "")
    try:
        event = lunipay.Webhook.construct_event(
            payload=payload,
            sig_header=sig,
            secret=os.environ["LUNIPAY_WEBHOOK_SECRET"],
        )
    except lunipay.error.SignatureVerificationError:
        return "bad signature", 400

    if event["type"] == "checkout.session.completed":
        # fulfill the order
        ...

    return "", 200