@jokoor/sdk
Version:
Official Jokoor API SDK for JavaScript/TypeScript - SMS, Payments, and more
669 lines (516 loc) • 15.3 kB
Markdown
# Jokoor SDK for TypeScript/JavaScript
Official TypeScript/JavaScript SDK for the [Jokoor API](https://jokoor.com) - Send SMS, accept payments, manage payouts, and more.
[](https://www.npmjs.com/package/@jokoor/sdk)
[](https://www.typescriptlang.org/)
[](https://opensource.org/licenses/MIT)
## Features
- 🚀 **Type-safe** - Full TypeScript support with comprehensive type definitions
- 📦 **Tree-shakeable** - Only bundle what you use
- 🔄 **Automatic retries** - Built-in retry logic with exponential backoff
- ✅ **Result pattern** - No exceptions thrown, explicit error handling with `{data, error}`
- 🎯 **Modern** - ES2022+, ESM and CommonJS support
- 🔀 **Auto case conversion** - Seamless camelCase (JS) ↔ snake_case (API) conversion
- 📝 **Well-documented** - Comprehensive JSDoc comments and examples
## Installation
```bash
npm install @jokoor/sdk
```
```bash
yarn add @jokoor/sdk
```
```bash
pnpm add @jokoor/sdk
```
## Quick Start
```typescript
import { Jokoor } from "@jokoor/sdk";
// Initialize the client with your API key
const jokoor = new Jokoor("sk_test_your_api_key_here");
// Send an SMS
const { data, error } = await jokoor.sms.send({
recipientPhone: "+2207123456",
messageBody: "Hello from Jokoor!",
});
if (error) {
console.error("Error:", error);
} else {
console.log("SMS sent:", data.id);
}
```
## Authentication
Get your API keys from the [Jokoor Dashboard](https://dashboard.jokoor.com/settings/api-keys).
### API Key Types
- **Secret keys** (`sk_test_xxx`, `sk_live_xxx`) - Full API access, use server-side only
- **Publishable keys** (`pk_test_xxx`, `pk_live_xxx`) - Limited access for client-side operations
### Test vs Live Mode
- **Test keys** (`_test_`) - For development and testing, no real charges
- **Live keys** (`_live_`) - For production use with real transactions
```typescript
// Test mode
const jokoor = new Jokoor("sk_test_xxx");
console.log(jokoor.isTestMode()); // true
// Live mode
const jokoorLive = new Jokoor("sk_live_xxx");
console.log(jokoorLive.isTestMode()); // false
```
## Configuration
```typescript
import { Jokoor } from "@jokoor/sdk";
const jokoor = new Jokoor("sk_test_xxx", {
baseURL: "https://api.jokoor.com/v1", // API base URL
timeout: 30000, // Request timeout (30s default)
maxRetries: 3, // Max retry attempts (3 default)
debug: false, // Enable debug logging
});
```
## API Reference
### SMS
#### Send SMS
```typescript
const { data, error } = await jokoor.sms.send({
recipientPhone: "+2207123456",
messageBody: "Your verification code is 123456",
senderId: "MyApp", // Optional
scheduledAt: "2024-12-25T10:00:00Z", // Optional
isDraft: false, // Optional
});
```
#### Get SMS Message
```typescript
const { data, error } = await jokoor.sms.get("msg_123");
```
#### List SMS Messages
```typescript
const { data, error } = await jokoor.sms.list({
offset: 0,
limit: 20,
status: "delivered",
startDate: "2024-01-01T00:00:00Z",
endDate: "2024-12-31T23:59:59Z",
});
if (data) {
console.log(`Total: ${data.count}`);
data.items.forEach((sms) => console.log(sms.id));
}
```
### Payment Links
#### Create Payment Link
```typescript
const { data, error } = await jokoor.paymentLinks.create({
title: "Premium Subscription",
description: "Monthly premium features",
amount: "500.00",
currency: "GMD",
successUrl: "https://example.com/success",
failureUrl: "https://example.com/cancel",
});
if (data) {
// Share this URL with customers
console.log("Payment URL:", data.paymentUrl);
// Example: https://pay.jokoor.com/pay/pl_abc123
}
```
**Note:** Payment links have hosted payment pages. Customers visit the `paymentUrl` to complete payment - no need to call the initialize endpoint.
#### List Payment Links
```typescript
const { data, error } = await jokoor.paymentLinks.list({
limit: 20,
status: "active",
});
```
### Checkouts
Checkouts support **both hosted pages and custom integration**.
#### Option 1: Hosted Checkout Page (Simple)
```typescript
const { data, error } = await jokoor.checkouts.create({
amount: "100.00",
currency: "GMD",
description: "Service payment",
});
if (data) {
// Share paymentUrl with customer
console.log("Send customer to:", data.paymentUrl);
// https://pay.jokoor.com/checkout/chk_abc123
// Customer completes payment on hosted page
}
```
#### Option 2: Custom SDK Integration (Full Control)
```typescript
// Step 1: Create checkout
const { data: checkout, error: createError } = await jokoor.checkouts.create({
amount: "100.00",
currency: "GMD",
description: "Service payment",
});
if (createError) return;
// Step 2: Initialize with client_secret
const { data: session, error } = await jokoor.payments.initialize({
clientSecret: checkout.clientSecret,
paymentMethod: "wave",
customerPhone: "+2207654321",
customerEmail: "customer@example.com",
});
if (error) return;
// Step 3: Redirect to payment provider
window.location.href = session.paymentUrl;
```
### Initialize Payment (Embedded Checkout)
Use this endpoint when building a custom payment UI with checkouts.
```typescript
// After creating a checkout with client_secret
const { data, error } = await jokoor.payments.initialize({
clientSecret: checkout.clientSecret, // From checkout creation
paymentMethod: "wave",
customerPhone: "+2207654321",
customerEmail: "customer@example.com",
customerName: "John Doe",
});
if (data) {
// Redirect customer to payment provider
window.location.href = data.paymentUrl;
}
```
**When to use:**
- ✅ Embedded checkout integrations (custom payment UI)
- ❌ NOT for payment links, donations, or invoices (they have hosted pages)
### Invoices
#### Create Invoice
```typescript
const { data, error } = await jokoor.invoices.create({
customerEmail: "customer@example.com",
customerName: "John Doe",
items: [
{
description: "Consulting Services",
quantity: 10,
unitPrice: "50.00",
},
],
currency: "GMD",
dueDate: "2024-12-31T23:59:59Z",
taxRate: 15, // Optional: 15% tax
});
if (data) {
console.log("Invoice Number:", data.invoiceNumber);
console.log("Payment URL:", data.paymentUrl); // Customer pays here
console.log("PDF URL:", data.pdfUrl); // Download PDF
}
```
#### Record Offline Payment
For cash, bank transfers, checks, etc. (NOT Wave/Afrimoney/card):
```typescript
const { data, error } = await jokoor.invoices.recordPayment("inv_123", {
amount: "1150.00",
paymentMethod: "bank_transfer",
transactionId: "BANK-REF-123456",
notes: "Received via wire transfer",
});
```
**Two ways to pay invoices:**
- **Online:** Customer visits `paymentUrl` (Wave, Afrimoney, card)
- **Offline:** Use `recordPayment()` for cash, bank transfers, checks
### Donation Campaigns
```typescript
const { data, error } = await jokoor.donations.create({
title: "Help Build a School",
description: "Raising funds to build a new school",
targetAmount: "50000.00", // Optional goal
currency: "GMD",
slug: "school-building-fund", // Custom URL slug
});
if (data) {
console.log("Donation URL:", data.donationUrl);
// Example: https://donate.jokoor.com/school-building-fund
console.log("Slug:", data.slug);
console.log("Progress:", `${data.progressPercentage}%`);
}
```
### Customers
```typescript
const { data, error } = await jokoor.customers.create({
email: "customer@example.com",
phone: "+2207123456",
name: "John Doe",
});
```
### Products
```typescript
const { data, error } = await jokoor.products.create({
name: "Premium Subscription",
description: "Monthly premium features",
price: "29.99",
currency: "GMD",
active: true,
});
```
### Transactions
```typescript
const { data, error } = await jokoor.transactions.list({
offset: 0,
limit: 20,
status: "completed",
startDate: "2024-01-01T00:00:00Z",
endDate: "2024-12-31T23:59:59Z",
});
if (data) {
data.items.forEach((txn) => {
console.log(`${txn.id}: ${txn.amount} ${txn.currency}`);
});
}
```
### Refunds
```typescript
// Full refund
const { data, error } = await jokoor.refunds.create("txn_123", {
reason: "Customer request",
});
// Partial refund
const { data, error } = await jokoor.refunds.create("txn_123", {
amount: "50.00",
reason: "Partial refund for damaged item",
});
```
### Subscriptions
```typescript
const { data, error } = await jokoor.subscriptions.create({
customerId: "cus_123",
amount: "29.99",
currency: "GMD",
interval: "month",
intervalCount: 1,
startDate: "2024-12-01",
items: [
{
description: "Premium Subscription",
quantity: 1,
unitPrice: "29.99",
},
],
});
```
### Payouts
#### Get Balance
```typescript
const { data, error } = await jokoor.payouts.getBalance();
if (data) {
console.log(`Available: ${data.availableBalance} ${data.currency}`);
console.log(`Pending: ${data.pendingBalance} ${data.currency}`);
}
```
#### List Bank Accounts
```typescript
const { data, error } = await jokoor.bankAccounts.list();
```
**Note:** Creating/updating bank accounts requires OTP and must be done via the web dashboard.
#### Send Payout to Recipient (Wave B2P)
```typescript
const { data, error } = await jokoor.payoutRecipients.sendPayout({
recipientId: "recip_123",
amount: "500.00",
reference: "Salary payment",
// Note: OTP required - must be obtained via dashboard
});
```
### Webhooks
#### Create Webhook Endpoint
```typescript
const { data, error } = await jokoor.webhooks.create({
url: "https://myapp.com/webhooks/jokoor",
enabledEvents: [
"payment.succeeded",
"payment.failed",
"sms.delivered",
"sms.failed",
],
});
if (data) {
// Secret is only shown once - store it securely
console.log("Webhook Secret:", data.secret);
}
```
#### List Webhook Events
```typescript
const { data, error } = await jokoor.webhookEvents.list({
offset: 0,
limit: 20,
type: "payment.succeeded",
startDate: "2024-01-01T00:00:00Z",
});
```
## Error Handling
The SDK uses a **Result pattern** instead of throwing exceptions. All methods return `{data, error}`:
```typescript
// Success case
const { data, error } = await jokoor.sms.send({...});
if (error) {
console.error('Error:', error); // error is a string
return;
}
console.log('Success:', data); // data is the response
```
## TypeScript Support
The SDK is written in TypeScript and provides comprehensive type definitions:
```typescript
import type {
SMS,
PaymentLink,
Checkout,
Invoice,
Transaction,
DonationCampaign,
} from "@jokoor/sdk";
// All response types are fully typed
const handlePaymentLink = (link: PaymentLink) => {
console.log(link.paymentUrl); // TypeScript knows this field exists
console.log(link.livemode); // Full autocomplete support
};
```
## Complete Examples
### Accept Payment with Hosted Page
```typescript
import { Jokoor } from "@jokoor/sdk";
const jokoor = new Jokoor("sk_test_xxx");
// Create a payment link
const { data, error } = await jokoor.paymentLinks.create({
title: "Product Purchase",
amount: "500.00",
currency: "GMD",
});
if (error) {
console.error("Error:", error);
} else {
// Share paymentUrl with customer
console.log("Send customer to:", data.paymentUrl);
// Customer visits URL and completes payment on hosted page
}
```
### Custom Payment Integration
```typescript
import { Jokoor } from "@jokoor/sdk";
const jokoor = new Jokoor("sk_test_xxx");
// 1. Create checkout
const checkout = await jokoor.checkouts.create({
amount: "100.00",
currency: "GMD",
description: "Service payment",
});
if (checkout.error) return;
// 2. Initialize payment with custom UI
const payment = await jokoor.payments.initialize({
clientSecret: checkout.data.clientSecret,
paymentMethod: "wave",
customerPhone: "+2207654321",
customerEmail: "customer@example.com",
});
if (payment.error) return;
// 3. Redirect to payment provider
window.location.href = payment.data.paymentUrl;
```
### Create and Send Invoice
```typescript
// Create invoice
const { data: invoice, error } = await jokoor.invoices.create({
customerEmail: "customer@example.com",
customerName: "John Doe",
items: [
{
description: "Web Development",
quantity: 1,
unitPrice: "1000.00",
},
],
currency: "GMD",
dueDate: "2024-12-31T23:59:59Z",
taxRate: 15,
});
if (error) return;
// Send invoice to customer
await jokoor.invoices.send(invoice.id);
// Customer can pay online at:
console.log("Payment URL:", invoice.paymentUrl);
// Or record offline payment (cash, bank transfer):
await jokoor.invoices.recordPayment(invoice.id, {
amount: "1150.00",
paymentMethod: "bank_transfer",
transactionId: "BANK-123",
notes: "Received via wire transfer",
});
```
### Bulk SMS Campaign
```typescript
// 1. Create contact group
const { data: group } = await jokoor.smsContactGroups.create({
name: "Campaign Recipients",
});
// 2. Add contacts
await jokoor.smsContactGroups.addContacts(group.id, [
"contact_1",
"contact_2",
"contact_3",
]);
// 3. Create and send campaign
const { data: campaign } = await jokoor.smsCampaigns.create({
name: "Product Launch",
messageBody: "Check out our new product!",
groupIds: [group.id],
});
// Send immediately
await jokoor.smsCampaigns.send(campaign.id);
// Or send asynchronously (recommended for large campaigns)
await jokoor.smsCampaigns.sendAsync(campaign.id);
```
## Rate Limiting
The Jokoor API implements rate limiting. When rate limited, the SDK will:
1. Automatically retry with exponential backoff (up to `maxRetries`)
2. Return an error if all retries fail
```typescript
const jokoor = new Jokoor("sk_test_xxx", {
maxRetries: 5, // Increase retries for rate-limited endpoints
});
```
## Debugging
Enable debug logging to see detailed request/response information:
```typescript
const jokoor = new Jokoor("sk_test_xxx", {
debug: true, // Logs all requests and responses
});
```
## Migration from v1.x
### Field Name Changes
The SDK now automatically converts between camelCase (JavaScript) and snake_case (API). Update your code:
```typescript
// OLD (v1.x)
sms.send({ recipient_phone: "...", message_body: "..." });
// NEW (v2.0)
sms.send({ recipientPhone: "...", messageBody: "..." });
// OLD
link.url;
// NEW
link.paymentUrl; // Clearer naming
```
### Payment Links
```typescript
// OLD
const link = await jokoor.paymentLinks.create({...});
console.log(link.url);
// NEW
const { data } = await jokoor.paymentLinks.create({...});
console.log(data.paymentUrl); // Now includes full hosted page URL
```
### Donation Campaigns
```typescript
// OLD
campaign.goalAmount;
campaign.raisedAmount;
// NEW
campaign.targetAmount; // Renamed for API consistency
campaign.currentAmount; // Renamed for API consistency
campaign.donationUrl; // New: Public donation page URL
campaign.slug; // New: SEO-friendly URL slug
```
## Support
- **Documentation**: https://docs.jokoor.com
- **API Reference**: https://docs.jokoor.com/api
- **Email**: hello@jokoor.com
## License
MIT License - see [LICENSE](LICENSE) file for details.