UNPKG

@jokoor/sdk

Version:

Official Jokoor API SDK for JavaScript/TypeScript - SMS, Payments, and more

669 lines (516 loc) 15.3 kB
# Jokoor SDK for TypeScript/JavaScript Official TypeScript/JavaScript SDK for the [Jokoor API](https://jokoor.com) - Send SMS, accept payments, manage payouts, and more. [![npm version](https://badge.fury.io/js/%40jokoor%2Fsdk.svg)](https://www.npmjs.com/package/@jokoor/sdk) [![TypeScript](https://img.shields.io/badge/TypeScript-5.7-blue)](https://www.typescriptlang.org/) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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.