Official Kotlin & Java SDK for the Nylon Pay mobile money payment API (Uganda mobile money: MTN MoMo & Airtel Money).
- Collect Payments — Mobile money collections (MTN / Airtel) and bank collections
- Blocking Resolve —
collectPaymentAndResolve&makePayoutAndResolvewith adaptive polling until terminal state - Payouts & Withdrawals — Mobile money and bank disbursement / transfers to customer accounts
- Hosted Invoices — Create payment links for card and mobile money checkout flows
- Transaction Queries & Tag Filtering — Query payments by reference or database ID, list transactions by tags and date range
- Phone Verification — Validate mobile money registered phone numbers and registered customer names
- Webhook Verification — Constant-time HMAC-SHA256 signature check, delivery ID tracking, and timestamp replay protection
- Deterministic Request Signing (RFC 8785 / JCS) — Canonical Unicode code-point key ordering + HMAC-SHA256 headers (
x-nylon-key,x-nylon-nonce,x-nylon-timestamp,x-nylon-signature) - Response Nonce Binding & Integrity — Rejects replayed responses and validates server
_responseSignaturefail-closed - Smart Retries — Automatic exponential backoff + jitter for transient network/server glitches
- Zero External Dependencies — Uses only Java 11+ standard library (
java.net.http,javax.crypto,java.time)
repositories {
mavenCentral()
maven { url = uri("https://jitpack.io") }
}
dependencies {
implementation("com.github.nile-squad:nylonpay-kt:1.0.0")
}repositories {
mavenCentral()
maven { url 'https://jitpack.io' }
}
dependencies {
implementation 'com.github.nile-squad:nylonpay-kt:1.0.0'
}<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
<dependency>
<groupId>com.github.nile-squad</groupId>
<artifactId>nylonpay-kt</artifactId>
<version>1.0.0</version>
</dependency>import com.carthigan.nylonpay.NylonPayClient
val client = NylonPayClient(
apiKey = "npk_your_api_key",
apiSecret = "nps_your_api_secret",
)
// 1. Collect Mobile Money Payment (Fire-and-forget for webhook flows)
val payment = client.collectPayment(
amount = 5000,
phoneNumber = "0700000000", // Automatically normalized to 256700000000
customerName = "John Doe",
description = "Order #1234",
tags = listOf("checkout", "mobile"),
)
println("Status: ${payment.status}") // "pending"
println("Reference: ${payment.reference}") // UUID reference to track payment
// 2. Or Collect & Block Until Terminal State (Success, Failed, Cancelled)
val resolvedTx = client.collectPaymentAndResolve(
amount = 5000,
phoneNumber = "0700000000",
customerName = "John Doe",
description = "Quick checkout",
)
println("Final status: ${resolvedTx.status}") // "successful" / "failed"
// 3. Make a Payout / Disbursement (Min: 5,000 UGX)
val payout = client.makePayout(
amount = 50000,
phoneNumber = "0770000000",
accountHolderName = "Jane Doe",
description = "Reward Payout",
)
println("Payout Reference: ${payout.reference}")
// 4. Create a Hosted Payment Invoice (Card & Mobile Money)
val invoice = client.createInvoice(
amount = 25000,
customerEmail = "customer@example.com",
customerName = "Alice",
description = "Monthly Subscription",
)
println("Send payment link to customer: ${invoice.paymentLink}")import com.carthigan.nylonpay.NylonPayClient;
import com.carthigan.nylonpay.PaymentResponse;
import com.carthigan.nylonpay.TransactionStatus;
public class Main {
public static void main(String[] args) {
NylonPayClient client = new NylonPayClient(
"npk_your_api_key",
"nps_your_api_secret"
);
PaymentResponse response = client.collectPayment(
5000,
"0700000000",
null, // auto-generates UUID
"John Doe",
null, // customerEmail
"Order #1234",
"UGX",
"mobileMoney",
null, // bank details
null, // metadata
null // tags
);
System.out.println("Status: " + response.getStatus());
System.out.println("Reference: " + response.getReference());
}
}Verify webhook callbacks from Nylon Pay safely with constant-time HMAC-SHA256 signature verification and timestamp freshness validation:
import com.carthigan.nylonpay.NylonPayWebhookVerifier
val verifier = NylonPayWebhookVerifier(
webhookSecret = "nps_your_webhook_secret", // from Nylon Pay dashboard
maxTimestampAgeSeconds = 300L, // 5 minutes replay tolerance (pass -1L to disable)
)
// In your HTTP webhook handler:
val signature = request.headers["x-nylon-signature"]
val rawBodyBytes = request.receiveChannel().toByteArray()
val result = verifier.verify(rawBodyBytes, signature)
if (result.valid) {
val deliveryId = result.deliveryId // Use for idempotency / deduplication
// Process the successful payment in your database
} else {
// Reject request: 400 Bad Request or 401 Unauthorized
println("Webhook validation failed: ${result.error}")
}Note on replay tolerance:
maxTimestampAgeSeconds = 0Lenforces zero tolerance (maximum strictness). To explicitly disable the freshness check, passNylonPayWebhookVerifier.DISABLE_FRESHNESS_CHECK(-1L).
val client = NylonPayClient(
apiKey = "npk_...",
apiSecret = "nps_...",
baseUrl = "https://api.nylonpay.nilesquad.com/api/services", // Optional custom endpoint
timeout = Duration.ofSeconds(90), // Optional timeout (covers inline resolve)
maxRetries = 3, // Auto-retry transient errors
maxPollInterval = Duration.ofSeconds(2), // Base polling interval
maxPollDuration = null, // Optional total polling cap
)| Parameter | Type | Default | Description |
|---|---|---|---|
apiKey |
String | Required | API Key starting with npk_ |
apiSecret |
String | Required | API Secret starting with nps_ |
baseUrl |
String | https://api.nylonpay.nilesquad.com/api/services |
Base URL for API calls |
timeout |
Duration | 90s |
HTTP connection & read timeout |
maxRetries |
Int | 3 |
Number of automatic retries on network/server errors (408, 429, 5xx) |
maxPollInterval |
Duration | 2s |
Initial polling interval for resolve operations |
maxPollDuration |
Duration? | null |
Optional timeout cap on resolve polling (null = wait until terminal) |
| Method | Description |
|---|---|
collectPayment(...) |
Initiate a mobile money or bank collection (returns immediate PaymentResponse) |
collectPaymentAndResolve(...) |
Initiate collection and block until terminal state (successful, failed, cancelled) |
makePayout(...) |
Disburse money to customer mobile money or bank account |
makePayoutAndResolve(...) |
Initiate payout and block until terminal state |
getStatus(reference) |
Fetch current lightweight transaction status |
getTransaction(reference?, id?) |
Fetch full transaction details by reference UUID or transaction ID |
createInvoice(...) |
Create a hosted payment invoice link (supports card & mobile money) |
listTransactions(...) |
Query transactions by tags, status, type, and date range with pagination |
getTransactionsByTag(tag, ...) |
Query transactions associated with a specific tag |
verifyPhone(phoneNumber) |
Verify mobile money account validity and registered customer name |
verifyWebhook(rawBody, signature, tolerance?) |
Verify incoming webhook payload authenticity and freshness |
The SDK throws NylonPayException with detailed error categorization:
try {
val res = client.collectPayment(500, "0700000000")
} catch (e: NylonPayException) {
println("Error: ${e.message}")
println("Category: ${e.category}") // "validation", "auth", "limit", "rate_limit", "network", "timeout", "internal"
println("Retryable: ${e.retryable}") // true if safe to retry
}MIT License. Copyright (c) 2026 Nile Squad.