bazik-sdk 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,10 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ .eggs/
5
+ build/
6
+ dist/
7
+ .venv/
8
+ venv/
9
+ .pytest_cache/
10
+ .DS_Store
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Falandy Jean
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,83 @@
1
+ Metadata-Version: 2.4
2
+ Name: bazik-sdk
3
+ Version: 0.1.0
4
+ Summary: Async Python SDK for the Bazik API — MonCash payments for Haiti.
5
+ Project-URL: Homepage, https://github.com/FalandyJEAN/bazik-sdk-python
6
+ Project-URL: Issues, https://github.com/FalandyJEAN/bazik-sdk-python/issues
7
+ Author: Falandy Jean
8
+ License: MIT
9
+ License-File: LICENSE
10
+ Keywords: bazik,haiti,moncash,natcash,payments
11
+ Requires-Python: >=3.9
12
+ Requires-Dist: httpx>=0.24
13
+ Description-Content-Type: text/markdown
14
+
15
+ # bazik-sdk (Python)
16
+
17
+ Tiny **async** Python client for the [Bazik](https://bazik.io) API — **MonCash** payments for Haiti. Production-tested (powers payments on TradeMakaya). One dependency: `httpx`.
18
+
19
+ ## Install
20
+
21
+ ```bash
22
+ pip install bazik-sdk
23
+ ```
24
+
25
+ ## Quick start
26
+
27
+ ```python
28
+ import asyncio
29
+ from bazik import BazikClient
30
+
31
+ async def main():
32
+ client = BazikClient(user_id="YOUR_USER_ID", secret_key="YOUR_SECRET_KEY")
33
+
34
+ # 1) Create a payment (amount in gourdes, max 75,000 HTG)
35
+ order = await client.create_moncash_payment(
36
+ gdes=500,
37
+ reference_id="INV-1001",
38
+ description="Pro plan",
39
+ customer_first_name="Jean",
40
+ customer_last_name="Pierre",
41
+ customer_email="jean@example.com",
42
+ webhook_url="https://api.yoursite.com/billing/moncash/webhook",
43
+ success_url="https://yoursite.com/paid",
44
+ error_url="https://yoursite.com/canceled",
45
+ )
46
+ print("Send the customer to:", order["redirectUrl"])
47
+
48
+ # 2) Check status (or poll until it resolves)
49
+ result = await client.wait_for_completion(order["orderId"], interval=3, timeout=180)
50
+ print("Status:", result["status"]) # "successful" | "pending" | "failed"
51
+
52
+ asyncio.run(main())
53
+ ```
54
+
55
+ ## API
56
+
57
+ | Method | Description |
58
+ |---|---|
59
+ | `BazikClient(user_id, secret_key, base_url=…, timeout=20)` | Create a client. |
60
+ | `await authenticate()` | Fetch & cache a bearer token (auto-called). |
61
+ | `await create_moncash_payment(*, gdes, reference_id, …)` | Create a MonCash payment → `{orderId, redirectUrl, …}`. |
62
+ | `await verify_payment(order_id)` | Current status → `{status, referenceId, …}`. |
63
+ | `await wait_for_completion(order_id, *, interval=3, timeout=180)` | Poll until resolved. |
64
+
65
+ `create_moncash_payment` optional fields: `webhook_url`, `description`, `customer_first_name`, `customer_last_name`, `customer_email`, `success_url`, `error_url`.
66
+
67
+ Errors raise `bazik.BazikError` (`.status_code`, `.body`).
68
+
69
+ > **Note:** the amount field is `gdes` (gourdes) — not `gourdes`. Sending the wrong key creates a payment with no amount.
70
+
71
+ ## Webhook flow
72
+
73
+ Bazik POSTs `{ orderId }` to your `webhook_url` on a status change. **Don't trust the body** — verify:
74
+
75
+ ```python
76
+ verified = await client.verify_payment(order_id)
77
+ if verified["status"] == "successful":
78
+ ... # activate the order
79
+ ```
80
+
81
+ ## License
82
+
83
+ MIT © Falandy Jean. Unofficial SDK; not affiliated with Bazik.
@@ -0,0 +1,69 @@
1
+ # bazik-sdk (Python)
2
+
3
+ Tiny **async** Python client for the [Bazik](https://bazik.io) API — **MonCash** payments for Haiti. Production-tested (powers payments on TradeMakaya). One dependency: `httpx`.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pip install bazik-sdk
9
+ ```
10
+
11
+ ## Quick start
12
+
13
+ ```python
14
+ import asyncio
15
+ from bazik import BazikClient
16
+
17
+ async def main():
18
+ client = BazikClient(user_id="YOUR_USER_ID", secret_key="YOUR_SECRET_KEY")
19
+
20
+ # 1) Create a payment (amount in gourdes, max 75,000 HTG)
21
+ order = await client.create_moncash_payment(
22
+ gdes=500,
23
+ reference_id="INV-1001",
24
+ description="Pro plan",
25
+ customer_first_name="Jean",
26
+ customer_last_name="Pierre",
27
+ customer_email="jean@example.com",
28
+ webhook_url="https://api.yoursite.com/billing/moncash/webhook",
29
+ success_url="https://yoursite.com/paid",
30
+ error_url="https://yoursite.com/canceled",
31
+ )
32
+ print("Send the customer to:", order["redirectUrl"])
33
+
34
+ # 2) Check status (or poll until it resolves)
35
+ result = await client.wait_for_completion(order["orderId"], interval=3, timeout=180)
36
+ print("Status:", result["status"]) # "successful" | "pending" | "failed"
37
+
38
+ asyncio.run(main())
39
+ ```
40
+
41
+ ## API
42
+
43
+ | Method | Description |
44
+ |---|---|
45
+ | `BazikClient(user_id, secret_key, base_url=…, timeout=20)` | Create a client. |
46
+ | `await authenticate()` | Fetch & cache a bearer token (auto-called). |
47
+ | `await create_moncash_payment(*, gdes, reference_id, …)` | Create a MonCash payment → `{orderId, redirectUrl, …}`. |
48
+ | `await verify_payment(order_id)` | Current status → `{status, referenceId, …}`. |
49
+ | `await wait_for_completion(order_id, *, interval=3, timeout=180)` | Poll until resolved. |
50
+
51
+ `create_moncash_payment` optional fields: `webhook_url`, `description`, `customer_first_name`, `customer_last_name`, `customer_email`, `success_url`, `error_url`.
52
+
53
+ Errors raise `bazik.BazikError` (`.status_code`, `.body`).
54
+
55
+ > **Note:** the amount field is `gdes` (gourdes) — not `gourdes`. Sending the wrong key creates a payment with no amount.
56
+
57
+ ## Webhook flow
58
+
59
+ Bazik POSTs `{ orderId }` to your `webhook_url` on a status change. **Don't trust the body** — verify:
60
+
61
+ ```python
62
+ verified = await client.verify_payment(order_id)
63
+ if verified["status"] == "successful":
64
+ ... # activate the order
65
+ ```
66
+
67
+ ## License
68
+
69
+ MIT © Falandy Jean. Unofficial SDK; not affiliated with Bazik.
@@ -0,0 +1,6 @@
1
+ """bazik — a tiny async Python client for the Bazik API (MonCash payments, Haiti)."""
2
+ from .client import BazikClient, DEFAULT_BASE_URL
3
+ from .errors import BazikError
4
+
5
+ __all__ = ["BazikClient", "BazikError", "DEFAULT_BASE_URL"]
6
+ __version__ = "0.1.0"
@@ -0,0 +1,120 @@
1
+ """Async Bazik client for MonCash payments in Haiti.
2
+
3
+ Battle-tested in production (TradeMakaya). Mirrors the official Bazik API over HTTP:
4
+
5
+ POST /token {userID, secretKey} -> { token, expires_at }
6
+ POST /moncash/token (Bearer) {gdes, referenceId…} -> { orderId, redirectUrl, status }
7
+ GET /order/{orderId} (Bearer) -> { status, referenceId, … }
8
+
9
+ The amount field is **gdes** (gourdes), NOT "gourdes" — sending the wrong key makes Bazik
10
+ create a payment without an amount.
11
+ """
12
+ from __future__ import annotations
13
+
14
+ import asyncio
15
+ import time
16
+ from typing import Any, Optional
17
+
18
+ import httpx
19
+
20
+ from .errors import BazikError
21
+
22
+ DEFAULT_BASE_URL = "https://api.bazik.io"
23
+
24
+
25
+ class BazikClient:
26
+ """A minimal, dependency-light (httpx only) async client for the Bazik API.
27
+
28
+ Example
29
+ -------
30
+ >>> client = BazikClient(user_id="...", secret_key="...")
31
+ >>> order = await client.create_moncash_payment(gdes=500, reference_id="INV-1",
32
+ ... webhook_url="https://api.me/webhook")
33
+ >>> print(order["redirectUrl"]) # send the customer here to pay
34
+ >>> result = await client.wait_for_completion(order["orderId"])
35
+ >>> assert result["status"] == "successful"
36
+ """
37
+
38
+ def __init__(self, user_id: str, secret_key: str, base_url: str = DEFAULT_BASE_URL,
39
+ timeout: float = 20.0):
40
+ if not user_id or not secret_key:
41
+ raise ValueError("BazikClient requires user_id and secret_key.")
42
+ self.user_id = user_id
43
+ self.secret_key = secret_key
44
+ self.base_url = base_url.rstrip("/")
45
+ self.timeout = timeout
46
+ self._token: Optional[str] = None
47
+ self._token_exp: float = 0.0
48
+
49
+ # ---- auth -----------------------------------------------------------------
50
+ async def authenticate(self) -> str:
51
+ """Fetch (and cache) a bearer token. Called automatically by other methods."""
52
+ now = time.time()
53
+ if self._token and now < self._token_exp - 60:
54
+ return self._token
55
+ async with httpx.AsyncClient(timeout=self.timeout) as client:
56
+ r = await client.post(f"{self.base_url}/token",
57
+ json={"userID": self.user_id, "secretKey": self.secret_key})
58
+ if r.status_code >= 400:
59
+ raise BazikError(f"/token failed ({r.status_code})", r.status_code, r.text[:300])
60
+ try:
61
+ data = r.json()
62
+ except Exception:
63
+ raise BazikError("/token: non-JSON response", r.status_code, r.text[:300])
64
+ token = data.get("token") or data.get("access_token") or (data.get("data") or {}).get("token")
65
+ if not token:
66
+ raise BazikError(f"/token: no token in response: {str(data)[:200]}")
67
+ self._token = token
68
+ exp_at = data.get("expires_at")
69
+ self._token_exp = (exp_at / 1000) if isinstance(exp_at, (int, float)) else now + 86400
70
+ return token
71
+
72
+ async def _headers(self) -> dict:
73
+ return {"Authorization": f"Bearer {await self.authenticate()}",
74
+ "Content-Type": "application/json"}
75
+
76
+ # ---- payments -------------------------------------------------------------
77
+ async def create_moncash_payment(self, *, gdes: int, reference_id: str,
78
+ webhook_url: Optional[str] = None,
79
+ description: Optional[str] = None,
80
+ customer_first_name: Optional[str] = None,
81
+ customer_last_name: Optional[str] = None,
82
+ customer_email: Optional[str] = None,
83
+ success_url: Optional[str] = None,
84
+ error_url: Optional[str] = None) -> dict[str, Any]:
85
+ """Create a MonCash payment (max 75,000 HTG). Returns Bazik's raw response,
86
+ including ``orderId`` and ``redirectUrl`` (where you send the customer to pay)."""
87
+ payload: dict[str, Any] = {"gdes": gdes, "referenceId": reference_id}
88
+ for k, v in (("description", description), ("customerFirstName", customer_first_name),
89
+ ("customerLastName", customer_last_name), ("customerEmail", customer_email),
90
+ ("webhookUrl", webhook_url), ("successUrl", success_url), ("errorUrl", error_url)):
91
+ if v:
92
+ payload[k] = v
93
+ async with httpx.AsyncClient(timeout=self.timeout) as client:
94
+ r = await client.post(f"{self.base_url}/moncash/token", json=payload,
95
+ headers=await self._headers())
96
+ if r.status_code >= 400:
97
+ raise BazikError(f"/moncash/token failed ({r.status_code})", r.status_code, r.text[:300])
98
+ return r.json()
99
+
100
+ async def verify_payment(self, order_id: str) -> dict[str, Any]:
101
+ """Fetch a payment's current status. ``status == 'successful'`` once paid
102
+ (other values: ``'pending'``, ``'failed'``)."""
103
+ async with httpx.AsyncClient(timeout=self.timeout) as client:
104
+ r = await client.get(f"{self.base_url}/order/{order_id}", headers=await self._headers())
105
+ if r.status_code >= 400:
106
+ raise BazikError(f"/order failed ({r.status_code})", r.status_code, r.text[:300])
107
+ return r.json()
108
+
109
+ async def wait_for_completion(self, order_id: str, *, interval: float = 3.0,
110
+ timeout: float = 180.0) -> dict[str, Any]:
111
+ """Poll ``verify_payment`` until the order resolves (successful/failed) or the
112
+ timeout elapses. Returns the last status payload."""
113
+ deadline = time.time() + timeout
114
+ last: dict[str, Any] = {}
115
+ while time.time() < deadline:
116
+ last = await self.verify_payment(order_id)
117
+ if (last.get("status") or "").lower() in ("successful", "failed", "canceled", "cancelled"):
118
+ return last
119
+ await asyncio.sleep(interval)
120
+ return last
@@ -0,0 +1,10 @@
1
+ from __future__ import annotations
2
+
3
+
4
+ class BazikError(RuntimeError):
5
+ """Raised when the Bazik API returns an error or an unexpected response."""
6
+
7
+ def __init__(self, message: str, status_code: int | None = None, body: str | None = None):
8
+ super().__init__(message)
9
+ self.status_code = status_code
10
+ self.body = body
@@ -0,0 +1,21 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "bazik-sdk"
7
+ version = "0.1.0"
8
+ description = "Async Python SDK for the Bazik API — MonCash payments for Haiti."
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "Falandy Jean" }]
13
+ keywords = ["bazik", "moncash", "haiti", "payments", "natcash"]
14
+ dependencies = ["httpx>=0.24"]
15
+
16
+ [project.urls]
17
+ Homepage = "https://github.com/FalandyJEAN/bazik-sdk-python"
18
+ Issues = "https://github.com/FalandyJEAN/bazik-sdk-python/issues"
19
+
20
+ [tool.hatch.build.targets.wheel]
21
+ packages = ["bazik"]