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 lunipayRequires 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
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 backoffException 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