Metadata-Version: 2.4
Name: kulmipay
Version: 1.0.0
Summary: Python SDK for the KulmiPay API
Author-email: KulmiPay <support@kulmipay.com>
License-Expression: MIT
Project-URL: Homepage, https://www.kulmipay.com
Project-URL: Documentation, https://developers.kulmipay.com
Keywords: payments,mpesa,pesalink,stk push,send money,wallets,kulmipay
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: requests>=2.25.0
Provides-Extra: dev
Requires-Dist: build>=1.2.0; extra == "dev"
Requires-Dist: twine>=5.0.0; extra == "dev"
Dynamic: license-file

# KulmiPay Python SDK

Python SDK for the KulmiPay API. Use it to create hosted checkout sessions, collect M-Pesa and PesaLink payments, send payouts, manage wallets, and create or retrieve refunds.

## Install

```bash
pip install kulmipay
```

## Configure

Use sandbox credentials with `sandbox=True` while testing. Use live credentials with the default live host.

```python
from kulmipay import APIService

service = APIService(
    token="ISSecretKey_live_xxxxxxxxxxxxxxxx",
    publishable_key="ISPubKey_live_xxxxxxxxxxxxxxxx",
)

sandbox_service = APIService(
    token="ISSecretKey_test_xxxxxxxxxxxxxxxx",
    publishable_key="ISPubKey_test_xxxxxxxxxxxxxxxx",
    sandbox=True,
)
```

For self-hosted deployments, pass `base_url`:

```python
service = APIService(
    token="YOUR_SECRET_KEY",
    publishable_key="YOUR_PUBLIC_KEY",
    base_url="https://payments.example.com/api/v1",
)
```

## Checkout

```python
checkout = service.collect.checkout(
    amount=1500,
    currency="KES",
    email="customer@example.com",
    phone_number="254712345678",
    api_ref="ORDER-1001",
    redirect_url="https://merchant.example/thank-you",
    unique_api_ref=True,
)

print(checkout["url"])
```

## M-Pesa STK Push

```python
response = service.collect.mpesa_stk_push(
    phone_number="254712345678",
    amount="1500.00",
    api_ref="ORDER-1001",
    mobile_tarrif="BUSINESS-PAYS",
)

invoice_id = response["invoice"]["invoice_id"]
```

## PesaLink Collection

```python
response = service.collect.pesalink(
    amount="5000.00",
    api_ref="ORDER-BANK-1001",
    email="customer@example.com",
    first_name="Jane",
    last_name="Doe",
    bank_tarrif="BUSINESS-PAYS",
)
```

## Payment Status

```python
status = service.collect.status(invoice_id=invoice_id)
print(status)
```

## Send Money

```python
transactions = [
    {
        "name": "Jane Doe",
        "account": "254712345678",
        "amount": "1000",
        "narrative": "Refund",
        "idempotency_key": "refund-1001",
    }
]

file = service.transfer.mpesa(
    currency="KES",
    transactions=transactions,
    requires_approval="YES",
)

approval = service.transfer.approve({
    "tracking_id": file["tracking_id"],
    "transactions": file["transactions"],
})

status = service.transfer.status(file["tracking_id"])
```

Supported providers:

| Method | Provider |
|--------|----------|
| `service.transfer.mpesa(...)` | `MPESA-B2C` |
| `service.transfer.mpesa_b2b(...)` | `MPESA-B2B` |
| `service.transfer.bank(...)` | `PESALINK` |
| `service.transfer.p2p(...)` | `P2P` |

## Wallets

```python
wallets = service.wallets.retrieve()

wallet = service.wallets.create(
    currency="KES",
    label="Payroll Wallet",
    can_disburse=True,
)

transactions = service.wallets.transactions(wallet["wallet_id"])
```

## Refunds

Refunds use the chargebacks endpoint internally.

```python
refund = service.refunds.create(
    invoice_id="GQ7KZ2XPNM",
    amount="1500.00",
    reason="Duplicate payment",
)

refunds = service.refunds.retrieve()
one_refund = service.refunds.details(refund["chargeback_id"])
```

## Error Handling

The SDK raises `KulmiPayBadRequest`, `KulmiPayUnauthorized`, `KulmiPayNotAllowed`, or `KulmiPayServerError` for common API errors.
