This page lists the official Danipa Pay SDKs and shows the minimum "make an authenticated call" snippet for each. Pick the SDK for your language; the calls and behaviour are identical to the REST API documented elsewhere.
If your language isn't listed, fall back to the REST API directly — every SDK is a thin wrapper.
Available SDKs
| Language | Package | Repo |
|---|---|---|
| Node.js / TypeScript | @danipa/sdk | Danipa/node-sdk |
| PHP | danipa/sdk | Danipa/php-sdk |
| Python | danipa | (in the platform monorepo, mirrored at PyPI) |
| Java | com.danipa:danipa-java-sdk | Danipa/java-sdk |
All four SDKs are published from the same OpenAPI source — model shapes are guaranteed in sync with the API.
Common shape
Every SDK:
- Accepts your API key at construction time.
- Exposes a method per endpoint, with typed request and response.
- Sends an
Idempotency-Keyautomatically when you call a mutating method (you can override). - Surfaces errors as the standard envelope (status code +
errors[].code).
Node.js
npm install @danipa/sdk
import { Danipa } from '@danipa/sdk';
const danipa = new Danipa({ apiKey: process.env.DANIPA_API_KEY });
const balance = await danipa.wallets.balance();
console.log(balance);
const transfer = await danipa.transfers.create({
amount: '10.00',
currency: 'USD',
recipient: '+233500000000',
});
console.log(transfer.id);
The SDK is fully typed. Hover any method in your IDE for the shape of the request and response.
PHP
composer require danipa/sdk
use Danipa\Client;
$danipa = new Client(['api_key' => getenv('DANIPA_API_KEY')]);
$balance = $danipa->wallets->balance();
echo json_encode($balance);
$transfer = $danipa->transfers->create([
'amount' => '10.00',
'currency' => 'USD',
'recipient' => '+233500000000',
]);
echo $transfer->id;
Python
pip install danipa
import os
from danipa import Danipa
danipa = Danipa(api_key=os.environ['DANIPA_API_KEY'])
balance = danipa.wallets.balance()
print(balance)
transfer = danipa.transfers.create(
amount='10.00',
currency='USD',
recipient='+233500000000',
)
print(transfer.id)
Java
<dependency>
<groupId>com.danipa</groupId>
<artifactId>danipa-java-sdk</artifactId>
<version>1.0.0</version>
</dependency>
import com.danipa.sdk.Danipa;
import com.danipa.sdk.transfers.CreateTransferRequest;
var danipa = Danipa.builder()
.apiKey(System.getenv("DANIPA_API_KEY"))
.build();
var balance = danipa.wallets().balance();
System.out.println(balance);
var transfer = danipa.transfers().create(
CreateTransferRequest.builder()
.amount("10.00")
.currency("USD")
.recipient("+233500000000")
.build()
);
System.out.println(transfer.id());
Common operation patterns
The snippets below mirror the most common merchant flows across languages. They use the same package names introduced above.
Create a payment link
PaymentLink link = danipa.paymentLinks().create(
CreatePaymentLinkRequest.builder()
.title("Widget Purchase")
.amount(new BigDecimal("50.00"))
.currency("GHS")
.amountFixed(true)
.build()
);
System.out.println("Share: " + link.getUrl());
const link = await danipa.paymentLinks.create({
title: 'Widget Purchase',
amount: 50.00,
currency: 'GHS',
amountFixed: true,
});
console.log(`Share: ${link.url}`);
$link = $danipa->paymentLinks->create([
'title' => 'Widget Purchase',
'amount' => 50.00,
'currency' => 'GHS',
'amountFixed' => true,
]);
echo "Share: " . $link->url;
link = danipa.payment_links.create(
title="Widget Purchase",
amount=50.00,
currency="GHS",
amount_fixed=True,
)
print(f"Share: {link.url}")
Send an invoice
Invoice invoice = danipa.invoices().create(
CreateInvoiceRequest.builder()
.customerName("Jane Customer")
.customerEmail("jane@customer.com")
.lineItems(List.of(LineItem.of("Widget A", 2, new BigDecimal("10.00"))))
.currency("GHS")
.dueDate(LocalDate.of(2026, 4, 1))
.build()
);
danipa.invoices().send(invoice.getId());
const invoice = await danipa.invoices.create({
customerName: 'Jane Customer',
customerEmail: 'jane@customer.com',
lineItems: [{ description: 'Widget A', quantity: 2, unitPrice: 10.00 }],
currency: 'GHS',
dueDate: '2026-04-01',
});
await danipa.invoices.send(invoice.id);
Verify a webhook
All SDKs include a Webhook.verify() helper that checks HMAC-SHA256
signatures and enforces the 300-second replay window. See
Webhooks for the full algorithm if you'd rather
hand-roll it.
boolean valid = Webhook.verify(
request.getHeader("X-Danipa-Signature"),
request.getHeader("X-Danipa-Timestamp"),
requestBody,
webhookSecret
);
const event = Webhook.verify(req.rawBody, {
signature: req.headers['x-danipa-signature'],
timestamp: req.headers['x-danipa-timestamp'],
secret: process.env.WEBHOOK_SECRET,
});
from danipa import Webhook
event = Webhook.verify(
payload=request.body,
signature=request.headers["X-Danipa-Signature"],
timestamp=request.headers["X-Danipa-Timestamp"],
secret=os.environ["WEBHOOK_SECRET"],
)
Sandbox vs production
Every SDK accepts a baseUrl override:
new Danipa({
apiKey: process.env.DANIPA_API_KEY,
baseUrl: 'https://api.sandbox.danipa.com', // default in the sandbox SDK distribution
});
Default is sandbox — the environment available today. Production
(https://api.danipa.com) is reserved for general availability and
is not yet live; set the base URL explicitly for production builds
once it opens. Don't switch at runtime based on heuristics — use a
build-time / env-var-driven config.
Versioning
SDKs follow semver against the platform's API version:
- Major bump — backward-incompatible API change (rare; we ship parallel versions when possible).
- Minor bump — additive (new endpoints, new fields).
- Patch bump — bug fixes, doc updates.
The SDK tracks the API version at the vN level (v1 today).
A v2 release of the API would ship a parallel SDK major
version; v1 would continue to work for a deprecation period
(currently 18 months minimum).
What's not in the SDKs
- Webhook signature verification — that's HMAC of a request body keyed by your secret; the SDKs include a helper, but the pattern is small enough to hand-roll. See Webhooks.
- OAuth flows for sub-merchant onboarding — separate package surface; ship in the dedicated marketplace SDK if/when needed.
- Real-time streaming — the platform doesn't expose a streaming API today; SDKs don't have a streaming surface.
Error handling
Every SDK throws a typed error for the standard error envelope,
giving you access to both the HTTP status and the errors[].code.
try {
danipa.paymentLinks().create(request);
} catch (DanipaValidationException e) {
// 400: Invalid request parameters
log.error("Validation error: {}", e.getErrors());
} catch (DanipaAuthException e) {
// 401/403: Authentication or authorization error
log.error("Auth error: {}", e.getMessage());
} catch (DanipaRateLimitException e) {
// 429: Rate limit exceeded
log.error("Retry after: {}s", e.getRetryAfterSeconds());
} catch (DanipaApiException e) {
// 5xx: Server error
log.error("API error: {} {}", e.getStatusCode(), e.getMessage());
}
Catch the error type and branch on code — never string-match on
the human-readable message.
Common pitfalls
What's next
- Postman collection — for ad-hoc exploration without writing code.
- Errors & limits — the error envelope every SDK surfaces.
- Authentication — the API-key model the SDKs use.
Community libraries
We welcome community-contributed libraries. If you've built a Danipa client library, contact us to get it listed here.
| Language | Library | Maintainer | Status |
|---|---|---|---|
| Go | Coming soon | — | Planned |
| Ruby | Coming soon | — | Planned |