Skip to main content

Official Python SDK for the Jokoor API

Project description

Jokoor Python SDK

Official Python SDK for the Jokoor API. Send SMS messages, accept payments, manage payouts, and more.

PyPI version Python Support License: MIT

Features

  • SMS: Send messages, manage campaigns, templates, and contacts
  • Payments: Accept payments via Wave, card, Afrimoney, and QMoney
  • Invoices: Create, send, and track invoices with automatic tax calculation
  • Payouts: Withdraw funds to bank accounts and Wave B2P recipients
  • Webhooks: Configure webhook endpoints to receive event notifications
  • Type-safe: Full type hints for better IDE support and type checking
  • Tuple return pattern: Returns (data, error) tuples - no exceptions raised
  • Automatic retries: Built-in retry logic with exponential backoff
  • Connection pooling: Efficient HTTP connection management

Installation

pip install jokoor
poetry add jokoor
pipenv install jokoor

Quick Start

from jokoor import Jokoor
from datetime import datetime, timezone  # For scheduled operations

# Initialize the client
client = Jokoor('sk_test_your_api_key')

# Send an SMS message
sms, error = client.sms.send(
    recipient_phone='+2207654321',
    message_body='Hello from Jokoor!'
)

if error:
    print(f'Error: {error}')
else:
    print(f'SMS sent: {sms["id"]}')

Note on DateTime Parameters: For scheduled operations, you can use either ISO 8601 strings or Python datetime objects:

# Option 1: ISO 8601 string (recommended)
scheduled_at='2024-12-25T10:00:00Z'

# Option 2: Python datetime object
from datetime import datetime, timezone
scheduled_at=datetime(2024, 12, 25, 10, 0, tzinfo=timezone.utc)

Authentication

Get your API keys from the Jokoor Dashboard.

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 testing and development, no real charges
  • Live keys (_live_) - For production use with real transactions
# Test environment
test_client = Jokoor('sk_test_your_api_key')

# Production environment
live_client = Jokoor('sk_live_your_api_key')

Configuration

from jokoor import Jokoor

client = Jokoor(
    api_key='sk_test_xxx',
    base_url='https://api.jokoor.com/v1',  # Optional: custom API URL
    timeout=30,                             # Optional: request timeout (seconds)
    max_retries=3,                          # Optional: max retry attempts
    debug=False,                            # Optional: enable debug logging
)

Error Handling

The SDK uses a tuple return pattern (data, error) instead of raising exceptions:

# Success case
sms, error = client.sms.send(
    recipient_phone='+2207654321',
    message_body='Hello!'
)

if error:
    print(f'Error: {error}')
    return

# Use data
print(f'SMS sent: {sms["id"]}')

API Reference

SMS

Send SMS

sms, error = client.sms.send(
    recipient_phone='+2207123456',
    message_body='Your verification code is 123456',
    sender_id='MyApp',              # Optional
    scheduled_at='2024-12-25T10:00:00Z',  # Optional
    is_draft=False,                 # Optional
)

Get SMS Message

sms, error = client.sms.get('msg_123')

List SMS Messages

result, error = client.sms.list(
    offset=0,
    limit=20,
    status='delivered',
    start_date='2024-01-01T00:00:00Z',
    end_date='2024-12-31T23:59:59Z',
)

if result:
    print(f"Total: {result['count']}")
    for sms in result['items']:
        print(sms['id'])

Payment Links

Create Payment Link

link, error = client.payment_links.create(
    title='Premium Subscription',
    amount='500.00',
    currency='GMD',
    description='Monthly premium features',
    success_url='https://example.com/success',
    failure_url='https://example.com/cancel',
)

if link:
    # Share this URL with customers
    print(f"Payment URL: {link['payment_url']}")
    # Example: https://pay.jokoor.com/pay/pl_abc123

Note: Payment links have hosted payment pages. Customers visit the payment_url to complete payment - no need to call the initialize endpoint.

List Payment Links

result, error = client.payment_links.list(limit=20, status='active')

Checkouts

Checkouts support both hosted pages and custom integration.

Option 1: Hosted Checkout Page (Simple)

checkout, error = client.checkouts.create(
    amount='100.00',
    currency='GMD',
    description='Service payment',
)

if checkout:
    # Share payment_url with customer
    print(f"Send customer to: {checkout['payment_url']}")
    # https://pay.jokoor.com/checkout/chk_abc123
    # Customer completes payment on hosted page

Option 2: Custom SDK Integration (Full Control)

# Step 1: Create checkout
checkout, error = client.checkouts.create(
    amount='100.00',
    currency='GMD',
    description='Service payment',
)

if error:
    print(f'Error: {error}')
    exit()

# Step 2: Initialize with client_secret
session, error = client.payments.initialize(
    client_secret=checkout['client_secret'],
    payment_method='wave',
    customer_phone='+2207654321',
    customer_email='customer@example.com',
)

if session:
    # Redirect customer to payment provider
    print(f"Redirect to: {session['payment_url']}")

Initialize Payment (Embedded Checkout)

Use this endpoint when building a custom payment UI with checkouts.

# After creating a checkout with client_secret
session, error = client.payments.initialize(
    client_secret=checkout['client_secret'],
    payment_method='wave',
    customer_phone='+2207654321',
    customer_email='customer@example.com',
    customer_name='John Doe',
)

if session:
    # Redirect customer to payment provider
    print(f"Redirect to: {session['payment_url']}")

When to use:

  • ✅ Embedded checkout integrations (custom payment UI)
  • ❌ NOT for payment links, donations, or invoices (they have hosted pages)

Invoices

Create Invoice

invoice, error = client.invoices.create(
    customer_email='customer@example.com',
    customer_name='John Doe',
    items=[
        {
            'description': 'Consulting Services',
            'quantity': 10,
            'unit_price': '50.00',
        },
    ],
    currency='GMD',
    due_date='2024-12-31T23:59:59Z',
    tax_rate=15,  # Optional: 15% tax
)

if invoice:
    print(f"Invoice Number: {invoice['invoice_number']}")
    print(f"Payment URL: {invoice['payment_url']}")  # Customer pays here
    print(f"PDF URL: {invoice['pdf_url']}")          # Download PDF

Record Offline Payment

For cash, bank transfers, checks, etc. (NOT Wave/Afrimoney/card):

invoice, error = client.invoices.record_payment(
    'inv_123',
    amount='1150.00',
    payment_method='bank_transfer',
    transaction_id='BANK-REF-123456',
    notes='Received via wire transfer',
)

Two ways to pay invoices:

  • Online: Customer visits payment_url (Wave, Afrimoney, card)
  • Offline: Use record_payment() for cash, bank transfers, checks

Donation Campaigns

campaign, error = client.donations.create(
    title='Help Build a School',
    description='Raising funds to build a new school',
    target_amount='50000.00',  # Optional goal
    currency='GMD',
    slug='school-building-fund',  # Custom URL slug
)

if campaign:
    print(f"Donation URL: {campaign['donation_url']}")
    # Example: https://donate.jokoor.com/school-building-fund
    print(f"Slug: {campaign['slug']}")
    print(f"Progress: {campaign['progress_percentage']}%")

Customers

customer, error = client.customers.create(
    email='customer@example.com',
    phone='+2207123456',
    name='John Doe',
)

Products

product, error = client.products.create(
    name='Premium Subscription',
    description='Monthly premium features',
    price='29.99',
    currency='GMD',
    active=True,
)

Transactions

result, error = client.transactions.list(
    offset=0,
    limit=20,
    status='completed',
    start_date='2024-01-01T00:00:00Z',
    end_date='2024-12-31T23:59:59Z',
)

if result:
    for txn in result['items']:
        print(f"{txn['id']}: {txn['amount']} {txn['currency']}")

Refunds

# Full refund
refund, error = client.refunds.create(
    'txn_123',
    reason='Customer request',
)

# Partial refund
refund, error = client.refunds.create(
    'txn_123',
    amount='50.00',
    reason='Partial refund for damaged item',
)

Subscriptions

subscription, error = client.subscriptions.create(
    customer_id='cus_123',
    amount='29.99',
    currency='GMD',
    interval='monthly',
)

Payouts

Get Balance

balance, error = client.payouts.get_balance()
if balance:
    print(f"Available: {balance['available_balance']} {balance['currency']}")
    print(f"Pending: {balance['pending_balance']} {balance['currency']}")

List Bank Accounts

accounts, error = client.bank_accounts.list()

Note: Creating/updating bank accounts requires OTP and must be done via the web dashboard.

Send Payout to Recipient (Wave B2P)

payout, error = client.recipients.send_payout(
    recipient_id='recip_123',
    amount='500.00',
    reference='Salary payment',
    # Note: OTP required - must be obtained via dashboard
)

Webhooks

Create Webhook Endpoint

webhook, error = client.webhooks.create(
    url='https://myapp.com/webhooks/jokoor',
    enabled_events=[
        'payment.succeeded',
        'payment.failed',
        'sms.delivered',
        'sms.failed',
    ],
)

if webhook:
    # Secret is only shown once - store it securely
    print(f"Webhook Secret: {webhook['secret']}")

List Webhook Events

result, error = client.webhook_events.list(
    offset=0,
    limit=20,
    type='payment.succeeded',
    start_date='2024-01-01T00:00:00Z',
)

Complete Examples

Accept Payment with Hosted Page

from jokoor import Jokoor

client = Jokoor('sk_test_xxx')

# Create a payment link
link, error = client.payment_links.create(
    title='Product Purchase',
    amount='500.00',
    currency='GMD',
)

if error:
    print(f'Error: {error}')
else:
    # Share payment_url with customer
    print(f'Send customer to: {link["payment_url"]}')
    # Customer visits URL and completes payment on hosted page

Custom Payment Integration

from jokoor import Jokoor

client = Jokoor('sk_test_xxx')

# 1. Create checkout
checkout, error = client.checkouts.create(
    amount='100.00',
    currency='GMD',
    description='Service payment',
)

if error:
    print(f'Error: {error}')
    exit()

# 2. Initialize payment with custom UI
session, error = client.payments.initialize(
    client_secret=checkout['client_secret'],
    payment_method='wave',
    customer_phone='+2207654321',
    customer_email='customer@example.com',
)

if session:
    # Redirect customer to payment provider
    print(f'Redirect to: {session["payment_url"]}')

Create and Send Invoice

# Create invoice
invoice, error = client.invoices.create(
    customer_email='customer@example.com',
    customer_name='John Doe',
    items=[
        {
            'description': 'Web Development',
            'quantity': 1,
            'unit_price': '1000.00',
        },
    ],
    currency='GMD',
    due_date='2024-12-31T23:59:59Z',
    tax_rate=15,
)

if error:
    print(f'Error: {error}')
    exit()

# Send invoice to customer
client.invoices.send(invoice['id'])

# Customer can pay online at:
print(f"Payment URL: {invoice['payment_url']}")

# Or record offline payment (cash, bank transfer):
client.invoices.record_payment(
    invoice['id'],
    amount='1150.00',
    payment_method='bank_transfer',
    transaction_id='BANK-123',
    notes='Received via wire transfer',
)

Bulk SMS Campaign

# 1. Create contact group
group, error = client.contact_groups.create(name='Campaign Recipients')

# 2. Add contacts
contact_ids = ['contact_1', 'contact_2', 'contact_3']
client.contact_groups.add_contacts(group['id'], contact_ids)

# 3. Create and send campaign
campaign, error = client.campaigns.create(
    name='Product Launch',
    message_body='Check out our new product!',
    group_ids=[group['id']],
)

# Send immediately
client.campaigns.send(campaign['id'])

# Or send asynchronously (recommended for large campaigns)
client.campaigns.send_async(campaign['id'])

Type Hints

The SDK includes comprehensive type hints for better IDE support:

from typing import Tuple, Optional
from jokoor import Jokoor
from jokoor.types import PaymentLink, Checkout, Invoice

def create_payment(client: Jokoor) -> Tuple[Optional[PaymentLink], Optional[str]]:
    return client.payment_links.create(
        name='Test Payment',
        amount='100.00',
        currency='GMD',
    )

# IDE will provide autocomplete and type checking
link, error = create_payment(client)
if link:
    print(link['payment_url'])  # IDE knows this field exists
    print(link['livemode'])     # Full autocomplete support

Migration from v1.x

Field Name Changes

# Invoice items field renamed
# OLD
invoice['line_items']

# NEW
invoice['items']  # Now matches API field name

# Donation campaign fields renamed
# OLD
campaign['goal_amount']
campaign['raised_amount']

# NEW
campaign['target_amount']   # Renamed for API consistency
campaign['current_amount']  # Renamed for API consistency

# Payment link URL field renamed
# OLD
link['url']

# NEW
link['payment_url']  # Now includes full hosted page URL

New Fields Available

Checkouts:

  • payment_url - Hosted payment page URL
  • client_secret - For SDK integration
  • livemode - Payment mode indicator

Payment Links:

  • payment_url - Hosted payment page URL
  • livemode - Payment mode indicator

Invoices:

  • payment_url - Public payment URL
  • pdf_url - PDF download URL
  • remaining_amount - Balance remaining
  • receipts - Payment receipts

Donation Campaigns:

  • slug - SEO-friendly URL slug
  • donation_url - Public donation page URL
  • donor_count - Number of donors
  • progress_percentage - Campaign progress
  • organizer_details - Organizer information

Rate Limiting

client = Jokoor('sk_test_xxx', max_retries=5)

Debugging

client = Jokoor('sk_test_xxx', debug=True)
# Logs all requests and responses

Support

License

MIT License - see LICENSE file for details.

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

jokoor-1.0.2.tar.gz (31.8 kB view details)

Uploaded Source

Built Distribution

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

jokoor-1.0.2-py3-none-any.whl (41.8 kB view details)

Uploaded Python 3

File details

Details for the file jokoor-1.0.2.tar.gz.

File metadata

  • Download URL: jokoor-1.0.2.tar.gz
  • Upload date:
  • Size: 31.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.6

File hashes

Hashes for jokoor-1.0.2.tar.gz
Algorithm Hash digest
SHA256 b8b7452cec323d18d0ceabd0776ae7103aec4529f4c118f548d5692ffa9bfbd1
MD5 da5758fa8c52aaf7f2d9e142a1c8d9f1
BLAKE2b-256 45b5db1500df6834acb971e127e9063ff26bc04ae651e9550199ffea31944634

See more details on using hashes here.

File details

Details for the file jokoor-1.0.2-py3-none-any.whl.

File metadata

  • Download URL: jokoor-1.0.2-py3-none-any.whl
  • Upload date:
  • Size: 41.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.6

File hashes

Hashes for jokoor-1.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 637befc1a195c4b2005dcbb3fa3f8b2ddcac26d1209e32995bbd54e2029325ab
MD5 1c72ca0c6b5d5a9ae577e6d4031372e3
BLAKE2b-256 9d65ae8160bb25aba4ee12f95b8edef1c762e26f70557d84407ec18163e0eb8c

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