Skip to main content

Nylon Pay SDK for merchant integrations

Project description

nylonpay-py

Server-side SDK for integrating Nylon Pay payment operations into your Python application.

Full documentation

Install

pip install nylonpay-py

Requires Python 3.10+.

Quick Start

Create a client, initiate a payment, subscribe to events, and wait for completion.

from nylonpay import create_nylon_pay
import secrets

nylonpay = create_nylon_pay(
    api_key="npk_test_...",
    api_secret="nps_test_...",
)

payment = nylonpay.collect_payment(
    amount=10000,
    currency="UGX",
    customer={"name": "Jane", "phone_number": "+256700000000"},
    description="Order #1234",
    reference=secrets.token_hex(7),
)

def on_success(data):
    print(f"Paid: {data.transaction.reference}")

def on_failed(data):
    print(f"Failed: {data.error}")

payment.on("success", on_success)
payment.on("failed", on_failed)

tx = payment.wait()
if tx is not None:
    print(f"Transaction {tx.reference} completed")

Configuration

Create the SDK instance with create_nylon_pay(). All options are keyword arguments.

Your API key determines the mode — npk_sandbox_... for test mode, npk_live_... for production. There is no separate mode option.

Field Required Default Description
api_key Yes Must start with npk_
api_secret Yes Must start with nps_
base_url No https://api.nylonpay.nilesquad.com/api/services Override for a custom endpoint
timeout_ms No 30000 Request timeout in milliseconds
max_retries No 3 Retry count for failed requests
max_poll_interval_ms No 2000 Interval between status checks
max_poll_duration_ms No 300000 Maximum wait time for wait() (about 5 minutes)
max_poll_attempts No 150 Maximum status check attempts
force No False Bypass instance cache and create a fresh instance
hooks No None Lifecycle hooks (SdkHooks) for cross-cutting concerns
http_client No None httpx.Client for testing injection
nylonpay = create_nylon_pay(
    api_key="npk_test_...",
    api_secret="nps_test_...",
    timeout_ms=15000,
    max_retries=5,
)

The factory caches instances by api_key + base_url + sha256(api_secret). Rotating the secret produces a different cache key and a fresh instance. Pass force=True to bypass caching.

Operations

All operations accept keyword arguments. Nested types (customer, destination, items) accept plain dicts — no need to import dataclasses.

collect_payment

Initiate a payment collection. Returns a PaymentInstance with event-driven updates.

payment = nylonpay.collect_payment(
    amount=10000,
    currency="UGX",
    customer={"name": "Jane", "phone_number": "+256700000000"},
    description="Order #1234",
    method="mobileMoney",
    reference="ORDER-2026-001",
)

def on_success(data):
    print(f"Paid: {data.transaction.reference}")

def on_failed(data):
    print(f"Failed: {data.error}")

payment.on("success", on_success)
payment.on("failed", on_failed)

reference is optional and auto-generated if omitted. A supplied reference must be 13 to 15 characters; the SDK raises SdkException with category validation otherwise. A raw UUID is 36 characters and will be rejected — use a short id of your own or omit the field.

collect_payment_and_resolve

Block until the collection reaches a terminal state. Single request/response — the server checks status internally, no client-side waiting.

result = nylonpay.collect_payment_and_resolve(
    amount=5000,
    currency="UGX",
    customer={"name": "Jane", "phone_number": "+256700000000"},
    description="Quick payment",
)

if result.is_ok:
    print("Paid:", result.value.reference)

make_payout

Disburse funds to a destination account. Returns a PaymentInstance with event-driven updates.

payout = nylonpay.make_payout(
    amount=50000,
    currency="UGX",
    customer={"name": "Jane", "phone_number": "+256700000000"},
    destination={
        "account_holder_name": "Jane Doe",
        "account_number": "123456",
    },
    description="Refund for order #1234",
)

tx = payout.wait()

make_payout_and_resolve

Block until the payout reaches a terminal state. Single request/response.

result = nylonpay.make_payout_and_resolve(
    amount=50000,
    currency="UGX",
    customer={"name": "Jane", "phone_number": "+256700000000"},
    destination={
        "account_holder_name": "Jane Doe",
        "account_number": "123456",
    },
    description="Refund",
)

if result.is_ok:
    print("Payout completed:", result.value.reference)

get_status

One-shot status check for a transaction. Does not wait — returns the current server-side state.

result = nylonpay.get_status(reference="ORDER-2026-001")
if result.is_ok:
    print(result.value.status)

get_transaction

Look up a full transaction record by id or reference. At least one must be provided.

result = nylonpay.get_transaction(reference="ORDER-2026-001")
if result.is_ok:
    print(result.value.failure_reason)

verify_phone

Pre-validate a phone number and get the registered name.

result = nylonpay.verify_phone(phone_number="+256700000000")
if result.is_ok and result.value.verified:
    print("Registered to:", result.value.customer_name)

Phone numbers are normalized automatically — any common format works: +256 700 000 000, 0700000000, 256700000000 are all accepted.

create_invoice

Generate a hosted payment link. Card payments are only supported via this hosted flow — card details never reach your servers.

result = nylonpay.create_invoice(
    amount=25000,
    currency="UGX",
    description="Monthly subscription",
    items=[{"name": "Pro Plan", "quantity": 1, "unit_price": 25000}],
    redirect_url="https://myapp.com/thank-you",
)

if result.is_ok:
    print("Invoice URL:", result.value.url)

verify_webhook_signature

Verify incoming webhook payloads before processing. Operates on raw payload bytes or string — never re-serialize parsed JSON, which would alter the signed content.

from nylonpay import verify_webhook_signature

is_valid = verify_webhook_signature(
    payload=raw_payload_bytes,
    signature=signature_header,
    secret="nps_...",
)

if not is_valid:
    # Reject — payload did not originate from Nylon Pay
    ...

The verification checks authenticity and freshness. Pass tolerance_seconds=0 to disable the freshness check. Returns True only when both checks pass. Never raises.

PaymentInstance Events

collect_payment and make_payout return a PaymentInstance with event-driven updates.

Event Description
processing Transaction is being processed
success Transaction completed successfully
failed Transaction failed
cancelled Transaction was cancelled
error Network or server error
def on_success(data):
    print(f"Paid: {data.transaction.reference}")

payment.on("success", on_success)
payment.once("success", on_success)  # fires at most once
payment.off("success", on_success)   # remove handler

tx = payment.wait()

wait()

Block until terminal state. Returns Transaction on success, None on failure, cancellation, or error. Never raises.

tx = payment.wait()
if tx is not None:
    print("paid:", tx.reference)
else:
    print("failed or timed out")

Error Handling

Operations that return Result[T, str] use the SDK's Result type. Check .is_ok / .is_err before accessing .value / .error.

from nylonpay import parse_error

result = nylonpay.get_status(reference="ORDER-2026-001")
if result.is_err:
    error = parse_error(result.error)
    if error.retryable:
        # Retry the operation
        ...
    print(f"Category: {error.category}, Message: {error.message}")

SdkException

Operations that throw on initiation failure (invalid input, missing fields) raise SdkException. It carries structured error information.

from nylonpay import SdkException

try:
    payment = nylonpay.collect_payment(
        amount=100,
        currency="UGX",
        customer={"name": "Jane", "phone_number": "+256700000000"},
        description="Test",
    )
except SdkException as e:
    print(f"Category: {e.category}")
    print(f"Retryable: {e.retryable}")
    print(f"Message: {e}")

Error Categories

Category Description Retryable
auth Invalid credentials No
validation Invalid input No
limit Account limit exceeded No
rate_limit Rate limited Yes
account Account issue (suspended, etc.) No
provider Provider rejected the transaction No
duplicate Reference already used No
not_found Resource not found No
internal Server error Varies
network Network error Yes
timeout Request timed out Yes

Hooks

Lifecycle hooks fire on every matching operation. Use them for cross-cutting concerns like logging, audit trails, and payload enrichment.

Each hook is wrapped in SdkHook which provides a safe boundary — if the hook's fn raises an exception, it is routed to on_error instead of crashing the payment flow.

from nylonpay import create_nylon_pay, SdkHook, SdkHooks

def log_before_collect(input):
    print(f"About to collect: {input.reference}")
    return input  # can mutate and return, or return None to skip

def log_after_collect(result, after_input):
    if result.is_ok:
        print(f"Collected: {result.value.reference}")
    else:
        print(f"Failed: {result.error}")

def on_hook_error(exc):
    print(f"Hook failed: {exc}")

hooks = SdkHooks(
    before_collect=SdkHook(fn=log_before_collect, on_error=on_hook_error),
    after_collect=SdkHook(fn=log_after_collect, on_error=on_hook_error),
)

nylonpay = create_nylon_pay(
    api_key="npk_test_...",
    api_secret="nps_test_...",
    hooks=hooks,
)

Available hooks: before_collect, after_collect, before_payout, after_payout. Each can be None (no-op).

Supported Currencies

USD, EUR, GBP, KES, UGX, TZS, RWF

Links

License

MIT

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

nylonpay_py-0.2.0.tar.gz (68.9 kB view details)

Uploaded Source

Built Distribution

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

nylonpay_py-0.2.0-py3-none-any.whl (39.4 kB view details)

Uploaded Python 3

File details

Details for the file nylonpay_py-0.2.0.tar.gz.

File metadata

  • Download URL: nylonpay_py-0.2.0.tar.gz
  • Upload date:
  • Size: 68.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.5

File hashes

Hashes for nylonpay_py-0.2.0.tar.gz
Algorithm Hash digest
SHA256 6a8ece7c37541d77cc4a645b42608cfebe27f1c4b587a29fab279cc530f21de9
MD5 0177a408fc7b07c26e8827188e01293b
BLAKE2b-256 830f1714c50c422c3f6f273404b49f9e44c7b3872e8b437130be800f59c76429

See more details on using hashes here.

File details

Details for the file nylonpay_py-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: nylonpay_py-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 39.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.5

File hashes

Hashes for nylonpay_py-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 5ffaa9106b541a4ae338d34ee3094f42f8e5002ee241402a6d9715a9dde98f44
MD5 0df29f8668fcff03a9f615169587d653
BLAKE2b-256 d6d1687da8b583c5d7ba603a95d6f0cd7e03dbfae4b28becaeede0e675574b27

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