mpesakit 2.0.0__tar.gz → 2.0.2__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.
Files changed (78) hide show
  1. {mpesakit-2.0.0 → mpesakit-2.0.2}/PKG-INFO +35 -49
  2. {mpesakit-2.0.0 → mpesakit-2.0.2}/README.md +31 -48
  3. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/account_balance/account_balance.py +1 -1
  4. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/account_balance/schemas.py +3 -3
  5. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/b2b_express_checkout/schemas.py +1 -1
  6. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/b2c/b2c.py +1 -1
  7. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/b2c/schemas.py +3 -3
  8. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/b2c_account_top_up/schemas.py +4 -4
  9. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/business_buy_goods/business_buy_goods.py +1 -1
  10. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/business_buy_goods/schemas.py +2 -2
  11. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/business_paybill/business_paybill.py +1 -1
  12. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/business_paybill/schemas.py +3 -3
  13. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/c2b/c2b.py +1 -1
  14. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/c2b/schemas.py +2 -2
  15. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/dynamic_qr_code/dynamic_qr_code.py +1 -1
  16. mpesakit-2.0.2/mpesakit/http_client/__init__.py +5 -0
  17. mpesakit-2.0.2/mpesakit/http_client/http_client.py +53 -0
  18. mpesakit-2.0.2/mpesakit/http_client/mpesa_async_http_client.py +159 -0
  19. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/mpesa_express/schemas.py +4 -4
  20. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/mpesa_express/stk_push.py +1 -1
  21. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/reversal/reversal.py +1 -1
  22. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/reversal/schemas.py +2 -2
  23. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/services/transaction.py +15 -9
  24. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/tax_remittance/schemas.py +2 -2
  25. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/tax_remittance/tax_remittance.py +1 -1
  26. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/transaction_status/schemas.py +4 -4
  27. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/transaction_status/transaction_status.py +1 -1
  28. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/utils/phone.py +1 -1
  29. {mpesakit-2.0.0 → mpesakit-2.0.2}/pyproject.toml +7 -3
  30. mpesakit-2.0.0/mpesakit/http_client/__init__.py +0 -4
  31. mpesakit-2.0.0/mpesakit/http_client/http_client.py +0 -28
  32. {mpesakit-2.0.0 → mpesakit-2.0.2}/.gitignore +0 -0
  33. {mpesakit-2.0.0 → mpesakit-2.0.2}/AUTHORS.md +0 -0
  34. {mpesakit-2.0.0 → mpesakit-2.0.2}/LICENSE +0 -0
  35. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/__init__.py +0 -0
  36. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/account_balance/__init__.py +0 -0
  37. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/auth/__init__.py +0 -0
  38. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/auth/access_token.py +0 -0
  39. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/auth/token_manager.py +0 -0
  40. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/b2b_express_checkout/__init__.py +0 -0
  41. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/b2b_express_checkout/b2b_express_checkout.py +0 -0
  42. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/b2c/__init__.py +0 -0
  43. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/b2c_account_top_up/__init__.py +0 -0
  44. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/b2c_account_top_up/b2c_account_top_up.py +0 -0
  45. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/bill_manager/__init__.py +0 -0
  46. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/bill_manager/bill_manager.py +0 -0
  47. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/bill_manager/schemas.py +0 -0
  48. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/business_buy_goods/__init__.py +0 -0
  49. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/business_paybill/__init__.py +0 -0
  50. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/c2b/__init__.py +0 -0
  51. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/dynamic_qr_code/__init__.py +0 -0
  52. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/dynamic_qr_code/schemas.py +0 -0
  53. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/errors.py +0 -0
  54. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/http_client/mpesa_http_client.py +0 -0
  55. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/mpesa_client.py +0 -0
  56. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/mpesa_express/__init__.py +0 -0
  57. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/mpesa_ratiba/__init__.py +0 -0
  58. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/mpesa_ratiba/mpesa_ratiba.py +0 -0
  59. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/mpesa_ratiba/schemas.py +0 -0
  60. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/reversal/__init__.py +0 -0
  61. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/security/__init__.py +0 -0
  62. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/security/certs/ProductionCertificate.cer +0 -0
  63. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/security/certs/SandboxCertificate.cer +0 -0
  64. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/security/get_credential.py +0 -0
  65. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/security/ip_whitelist.py +0 -0
  66. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/services/__init__.py +0 -0
  67. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/services/b2b.py +0 -0
  68. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/services/b2c.py +0 -0
  69. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/services/balance.py +0 -0
  70. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/services/bill.py +0 -0
  71. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/services/c2b.py +0 -0
  72. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/services/dynamic_qr.py +0 -0
  73. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/services/express.py +0 -0
  74. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/services/ratiba.py +0 -0
  75. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/services/reversal.py +0 -0
  76. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/services/tax.py +0 -0
  77. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/tax_remittance/__init__.py +0 -0
  78. {mpesakit-2.0.0 → mpesakit-2.0.2}/mpesakit/transaction_status/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mpesakit
3
- Version: 2.0.0
3
+ Version: 2.0.2
4
4
  Summary: A Python SDK for integrating with M-Pesa APIs
5
5
  Project-URL: Homepage, https://github.com/rafaeljohn9/mpesakit
6
6
  Project-URL: Repository, https://github.com/rafaeljohn9/mpesakit
@@ -21,6 +21,7 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
21
  Classifier: Typing :: Typed
22
22
  Requires-Python: >=3.12
23
23
  Requires-Dist: cryptography>=41.0.7
24
+ Requires-Dist: httpx<1.0.0,>=0.27.0
24
25
  Requires-Dist: pydantic<3.0.0,>=2.10.6
25
26
  Requires-Dist: pydantic[email]<3.0.0,>=2.10.6
26
27
  Requires-Dist: requests<3.0.0,>=2.32.3
@@ -34,6 +35,7 @@ Requires-Dist: hatch-vcs; extra == 'dev'
34
35
  Requires-Dist: hatchling; extra == 'dev'
35
36
  Requires-Dist: mypy; extra == 'dev'
36
37
  Requires-Dist: pytest; extra == 'dev'
38
+ Requires-Dist: pytest-asyncio<1.0.0,>=0.23.6; extra == 'dev'
37
39
  Requires-Dist: pytest-cov; extra == 'dev'
38
40
  Requires-Dist: ruff; extra == 'dev'
39
41
  Requires-Dist: types-requests; extra == 'dev'
@@ -42,18 +44,20 @@ Requires-Dist: bandit; extra == 'test'
42
44
  Requires-Dist: mypy; extra == 'test'
43
45
  Requires-Dist: pyngrok; extra == 'test'
44
46
  Requires-Dist: pytest; extra == 'test'
47
+ Requires-Dist: pytest-asyncio<1.0.0,>=0.23.6; extra == 'test'
45
48
  Requires-Dist: pytest-cov; extra == 'test'
46
49
  Requires-Dist: types-requests; extra == 'test'
47
50
  Description-Content-Type: text/markdown
48
51
 
49
- # mpesa-daraja-sdk
52
+ # mpesakit
50
53
 
51
54
  > ⚡ **Effortless M-Pesa integration** using Safaricom's Daraja API — built for developers, by developers.
52
55
 
53
56
  [![Python 3.12+](https://img.shields.io/badge/python-3.12+-blue.svg)](https://www.python.org/downloads/)
54
57
  [![License: Apache 2.0](https://img.shields.io/badge/License-Apache%202.0-green.svg)](https://opensource.org/licenses/Apache-2.0)
55
- <!-- [![Build Status](https://img.shields.io/badge/build-passing-brightgreen.svg)]() -->
58
+ [![Build Status](https://img.shields.io/badge/build-passing-brightgreen.svg)]()
56
59
 
60
+ [![PyPI version](https://img.shields.io/pypi/v/mpesakit.svg)](https://pypi.org/project/mpesakit) [![Downloads](https://pepy.tech/badge/mpesakit)](https://pepy.tech/project/mpesakit)
57
61
  ---
58
62
 
59
63
  ## The Problem
@@ -73,7 +77,7 @@ For many developers and startups, this becomes a **huge barrier** to adopting M-
73
77
 
74
78
  ## The Solution
75
79
 
76
- **`mpesa-daraja-sdk`** eliminates the complexity with a **clean, developer-friendly Python SDK** that:
80
+ **`mpesakit`** eliminates the complexity with a **clean, developer-friendly Python SDK** that:
77
81
 
78
82
  - **Zero-config setup** — just add your credentials and go
79
83
  - **Handles authentication automatically** — OAuth2, tokens, and security
@@ -86,13 +90,19 @@ For many developers and startups, this becomes a **huge barrier** to adopting M-
86
90
 
87
91
  | Feature | Status | Description |
88
92
  |---------|--------|-------------|
89
- | **STK Push** | Ready | Lipa na M-Pesa Online payments |
90
- | **C2B Payments** | Ready | Customer to Business transactions |
91
- | **B2C Payments** | Ready | Business to Customer payouts |
92
- | **Token Management** | Ready | Automatic OAuth2 handling |
93
- | **Account Balance** | Coming Soon | Check account balances |
94
- | **Transaction Reversal** | Coming Soon | Reverse transactions |
95
- | 🎣 **Webhook Validation** | Coming Soon | Secure callback handling |
93
+ | **STK Push (Lipa na M-Pesa)** | Ready | Initiate customer payment prompts (mpesa-express/stk-push.mdx) |
94
+ | **STK Query** | Ready | Query STK push/payment status (mpesa-express/stk-query.mdx) |
95
+ | **C2B Payments** | Ready | Customer-to-Business payments (c2b.mdx) |
96
+ | **B2C Payments** | Ready | Business-to-Customer payouts (b2c.mdx) |
97
+ | **B2C Account Top-up** | Ready | Account top-up flows for B2C (b2c-account-top-up.mdx) |
98
+ | **Business Paybill** | Ready | Paybill integrations for business collections (business-paybill.mdx) |
99
+ | **Business BuyGoods** | Ready | Till/BuyGoods integrations (business-buygoods.mdx) |
100
+ | **Token Management / Auth** | Ready | Automatic OAuth2 handling and auth utilities (auth.mdx) |
101
+ | **Account Balance** | Ready | Check account balances (account-balance.mdx) |
102
+ | **Transaction Reversal** | Ready | Reverse transactions (reversal.mdx) |
103
+ | **Transaction Status** | Ready | Query transaction status (transaction-status.mdx) |
104
+ | **Dynamic QR** | Ready | Generate and manage dynamic QR payments (dynamic-qr.mdx) |
105
+ | **Tax Remittance** | Ready | Tax remittance flows and docs (tax-remittance.mdx) |
96
106
 
97
107
  > Built on top of [Arlus/mpesa-py](https://github.com/Arlus/mpesa-py) with ❤️ — modernized, cleaned up, and restructured for today's developer needs.
98
108
 
@@ -100,41 +110,17 @@ For many developers and startups, this becomes a **huge barrier** to adopting M-
100
110
 
101
111
  ## Quick Start
102
112
 
103
- ### Installation (coming soon)
113
+ ### Installation (already here :) )
104
114
 
105
115
  ```bash
106
- pip install mpesa-daraja-sdk
116
+ pip install mpesakit
107
117
  ```
108
118
 
109
119
  ---
110
120
 
111
121
  ## 📖 Complete Setup Guide
112
122
 
113
- ### 1. Get Safaricom Developer Account
114
-
115
- 1. Visit [developer.safaricom.co.ke](https://developer.safaricom.co.ke)
116
- 2. **Create account** and verify your email
117
- 3. **Create a new app** to get your credentials:
118
- - Consumer Key
119
- - Consumer Secret
120
-
121
- ### 2. Obtain Test Credentials (Sandbox)
122
-
123
- 1. Navigate to [Test Credentials Page](https://developer.safaricom.co.ke/test_credentials)
124
- 2. Copy the following credentials:
125
- - **Shortcode** (Business number)
126
- - **Initiator Name** (API operator username)
127
- - **Initiator Password** (API operator password)
128
- - **Security Credential** (Encrypted password)
129
-
130
- ### 3. Production Setup
131
-
132
- For production deployment:
133
-
134
- 1. **Get Paybill/Till Number** from Safaricom
135
- 2. **Generate Security Credential** using Safaricom's public certificate
136
- 3. **Switch environment** to `"production"` in your client initialization
137
- 4. **Update callback URLs** to your production domain
123
+ - For the complete setup guide kindly check the documentation at: [https://mpesakit.dev](https://mpesakit.dev)
138
124
 
139
125
  ---
140
126
 
@@ -165,8 +151,8 @@ We welcome contributions from the community! Here's how you can help:
165
151
 
166
152
  ```bash
167
153
  # Clone the repository
168
- git clone https://github.com/rafaeljohn9/mpesa-daraja-sdk.git
169
- cd mpesa-daraja-sdk
154
+ git clone https://github.com/rafaeljohn9/mpesakit.git
155
+ cd mpesakit
170
156
 
171
157
  # Create virtual environment
172
158
  python -m venv venv
@@ -176,7 +162,7 @@ source venv/bin/activate # On Windows: venv\Scripts\activate
176
162
  pip install -e ".[dev]"
177
163
 
178
164
  # Run tests
179
- pytest tests/
165
+ pytest tests/unit
180
166
  ```
181
167
 
182
168
  ### Code Standards
@@ -191,8 +177,8 @@ pytest tests/
191
177
  ## 📞 Support & Community
192
178
 
193
179
  - 📖 **Documentation**: [Full API docs coming soon]
194
- - 🐛 **Issues**: [GitHub Issues](https://github.com/rafaeljohn9/mpesa-daraja-sdk/issues)
195
- - 💬 **Discussions**: [GitHub Discussions](https://github.com/rafaeljohn9/mpesa-daraja-sdk/discussions)
180
+ - 🐛 **Issues**: [GitHub Issues](https://github.com/rafaeljohn9/mpesakit/issues)
181
+ - 💬 **Discussions**: [GitHub Discussions](https://github.com/rafaeljohn9/mpesakit/discussions)
196
182
  - 📧 **Email**: <johnmkagunda@gmail.com>
197
183
 
198
184
  ---
@@ -203,11 +189,11 @@ This project began as a fork of the fantastic [`Arlus/mpesa-py`](https://github.
203
189
 
204
190
  **What we've added:**
205
191
 
206
- - 🏗️ **Modular architecture** for better maintainability
207
- - 🎯 **Developer-first design** with intuitive APIs
208
- - 🧪 **Comprehensive testing** suite
209
- - 📚 **Better documentation** and examples
210
- - 🚀 **Production-ready** features and error handling
192
+ - **Modular architecture** for better maintainability
193
+ - **Developer-first design** with intuitive APIs
194
+ - **Comprehensive testing** suite
195
+ - **Better documentation** and examples
196
+ - **Production-ready** features and error handling
211
197
 
212
198
  Special thanks to the original contributors and the broader Python community in Kenya.
213
199
 
@@ -223,6 +209,6 @@ Licensed under the [Apache 2.0 License](LICENSE) — free for commercial and pri
223
209
 
224
210
  **Made with ❤️ for the Kenyan developer community**
225
211
 
226
- [⭐ Star this repo](https://github.com/rafaeljohn9/mpesa-daraja-sdk) | [🐛 Report Issue](https://github.com/rafaeljohn9/mpesa-daraja-sdk/issues) | [💡 Request Feature](https://github.com/rafaeljohn9/mpesa-daraja-sdk/issues/new)
212
+ [⭐ Star this repo](https://github.com/rafaeljohn9/mpesakit) | [🐛 Report Issue](https://github.com/rafaeljohn9/mpesakit/issues) | [💡 Request Feature](https://github.com/rafaeljohn9/mpesakit/issues/new)
227
213
 
228
214
  </div>
@@ -1,11 +1,12 @@
1
- # mpesa-daraja-sdk
1
+ # mpesakit
2
2
 
3
3
  > ⚡ **Effortless M-Pesa integration** using Safaricom's Daraja API — built for developers, by developers.
4
4
 
5
5
  [![Python 3.12+](https://img.shields.io/badge/python-3.12+-blue.svg)](https://www.python.org/downloads/)
6
6
  [![License: Apache 2.0](https://img.shields.io/badge/License-Apache%202.0-green.svg)](https://opensource.org/licenses/Apache-2.0)
7
- <!-- [![Build Status](https://img.shields.io/badge/build-passing-brightgreen.svg)]() -->
7
+ [![Build Status](https://img.shields.io/badge/build-passing-brightgreen.svg)]()
8
8
 
9
+ [![PyPI version](https://img.shields.io/pypi/v/mpesakit.svg)](https://pypi.org/project/mpesakit) [![Downloads](https://pepy.tech/badge/mpesakit)](https://pepy.tech/project/mpesakit)
9
10
  ---
10
11
 
11
12
  ## The Problem
@@ -25,7 +26,7 @@ For many developers and startups, this becomes a **huge barrier** to adopting M-
25
26
 
26
27
  ## The Solution
27
28
 
28
- **`mpesa-daraja-sdk`** eliminates the complexity with a **clean, developer-friendly Python SDK** that:
29
+ **`mpesakit`** eliminates the complexity with a **clean, developer-friendly Python SDK** that:
29
30
 
30
31
  - **Zero-config setup** — just add your credentials and go
31
32
  - **Handles authentication automatically** — OAuth2, tokens, and security
@@ -38,13 +39,19 @@ For many developers and startups, this becomes a **huge barrier** to adopting M-
38
39
 
39
40
  | Feature | Status | Description |
40
41
  |---------|--------|-------------|
41
- | **STK Push** | Ready | Lipa na M-Pesa Online payments |
42
- | **C2B Payments** | Ready | Customer to Business transactions |
43
- | **B2C Payments** | Ready | Business to Customer payouts |
44
- | **Token Management** | Ready | Automatic OAuth2 handling |
45
- | **Account Balance** | Coming Soon | Check account balances |
46
- | **Transaction Reversal** | Coming Soon | Reverse transactions |
47
- | 🎣 **Webhook Validation** | Coming Soon | Secure callback handling |
42
+ | **STK Push (Lipa na M-Pesa)** | Ready | Initiate customer payment prompts (mpesa-express/stk-push.mdx) |
43
+ | **STK Query** | Ready | Query STK push/payment status (mpesa-express/stk-query.mdx) |
44
+ | **C2B Payments** | Ready | Customer-to-Business payments (c2b.mdx) |
45
+ | **B2C Payments** | Ready | Business-to-Customer payouts (b2c.mdx) |
46
+ | **B2C Account Top-up** | Ready | Account top-up flows for B2C (b2c-account-top-up.mdx) |
47
+ | **Business Paybill** | Ready | Paybill integrations for business collections (business-paybill.mdx) |
48
+ | **Business BuyGoods** | Ready | Till/BuyGoods integrations (business-buygoods.mdx) |
49
+ | **Token Management / Auth** | Ready | Automatic OAuth2 handling and auth utilities (auth.mdx) |
50
+ | **Account Balance** | Ready | Check account balances (account-balance.mdx) |
51
+ | **Transaction Reversal** | Ready | Reverse transactions (reversal.mdx) |
52
+ | **Transaction Status** | Ready | Query transaction status (transaction-status.mdx) |
53
+ | **Dynamic QR** | Ready | Generate and manage dynamic QR payments (dynamic-qr.mdx) |
54
+ | **Tax Remittance** | Ready | Tax remittance flows and docs (tax-remittance.mdx) |
48
55
 
49
56
  > Built on top of [Arlus/mpesa-py](https://github.com/Arlus/mpesa-py) with ❤️ — modernized, cleaned up, and restructured for today's developer needs.
50
57
 
@@ -52,41 +59,17 @@ For many developers and startups, this becomes a **huge barrier** to adopting M-
52
59
 
53
60
  ## Quick Start
54
61
 
55
- ### Installation (coming soon)
62
+ ### Installation (already here :) )
56
63
 
57
64
  ```bash
58
- pip install mpesa-daraja-sdk
65
+ pip install mpesakit
59
66
  ```
60
67
 
61
68
  ---
62
69
 
63
70
  ## 📖 Complete Setup Guide
64
71
 
65
- ### 1. Get Safaricom Developer Account
66
-
67
- 1. Visit [developer.safaricom.co.ke](https://developer.safaricom.co.ke)
68
- 2. **Create account** and verify your email
69
- 3. **Create a new app** to get your credentials:
70
- - Consumer Key
71
- - Consumer Secret
72
-
73
- ### 2. Obtain Test Credentials (Sandbox)
74
-
75
- 1. Navigate to [Test Credentials Page](https://developer.safaricom.co.ke/test_credentials)
76
- 2. Copy the following credentials:
77
- - **Shortcode** (Business number)
78
- - **Initiator Name** (API operator username)
79
- - **Initiator Password** (API operator password)
80
- - **Security Credential** (Encrypted password)
81
-
82
- ### 3. Production Setup
83
-
84
- For production deployment:
85
-
86
- 1. **Get Paybill/Till Number** from Safaricom
87
- 2. **Generate Security Credential** using Safaricom's public certificate
88
- 3. **Switch environment** to `"production"` in your client initialization
89
- 4. **Update callback URLs** to your production domain
72
+ - For the complete setup guide kindly check the documentation at: [https://mpesakit.dev](https://mpesakit.dev)
90
73
 
91
74
  ---
92
75
 
@@ -117,8 +100,8 @@ We welcome contributions from the community! Here's how you can help:
117
100
 
118
101
  ```bash
119
102
  # Clone the repository
120
- git clone https://github.com/rafaeljohn9/mpesa-daraja-sdk.git
121
- cd mpesa-daraja-sdk
103
+ git clone https://github.com/rafaeljohn9/mpesakit.git
104
+ cd mpesakit
122
105
 
123
106
  # Create virtual environment
124
107
  python -m venv venv
@@ -128,7 +111,7 @@ source venv/bin/activate # On Windows: venv\Scripts\activate
128
111
  pip install -e ".[dev]"
129
112
 
130
113
  # Run tests
131
- pytest tests/
114
+ pytest tests/unit
132
115
  ```
133
116
 
134
117
  ### Code Standards
@@ -143,8 +126,8 @@ pytest tests/
143
126
  ## 📞 Support & Community
144
127
 
145
128
  - 📖 **Documentation**: [Full API docs coming soon]
146
- - 🐛 **Issues**: [GitHub Issues](https://github.com/rafaeljohn9/mpesa-daraja-sdk/issues)
147
- - 💬 **Discussions**: [GitHub Discussions](https://github.com/rafaeljohn9/mpesa-daraja-sdk/discussions)
129
+ - 🐛 **Issues**: [GitHub Issues](https://github.com/rafaeljohn9/mpesakit/issues)
130
+ - 💬 **Discussions**: [GitHub Discussions](https://github.com/rafaeljohn9/mpesakit/discussions)
148
131
  - 📧 **Email**: <johnmkagunda@gmail.com>
149
132
 
150
133
  ---
@@ -155,11 +138,11 @@ This project began as a fork of the fantastic [`Arlus/mpesa-py`](https://github.
155
138
 
156
139
  **What we've added:**
157
140
 
158
- - 🏗️ **Modular architecture** for better maintainability
159
- - 🎯 **Developer-first design** with intuitive APIs
160
- - 🧪 **Comprehensive testing** suite
161
- - 📚 **Better documentation** and examples
162
- - 🚀 **Production-ready** features and error handling
141
+ - **Modular architecture** for better maintainability
142
+ - **Developer-first design** with intuitive APIs
143
+ - **Comprehensive testing** suite
144
+ - **Better documentation** and examples
145
+ - **Production-ready** features and error handling
163
146
 
164
147
  Special thanks to the original contributors and the broader Python community in Kenya.
165
148
 
@@ -175,6 +158,6 @@ Licensed under the [Apache 2.0 License](LICENSE) — free for commercial and pri
175
158
 
176
159
  **Made with ❤️ for the Kenyan developer community**
177
160
 
178
- [⭐ Star this repo](https://github.com/rafaeljohn9/mpesa-daraja-sdk) | [🐛 Report Issue](https://github.com/rafaeljohn9/mpesa-daraja-sdk/issues) | [💡 Request Feature](https://github.com/rafaeljohn9/mpesa-daraja-sdk/issues/new)
161
+ [⭐ Star this repo](https://github.com/rafaeljohn9/mpesakit) | [🐛 Report Issue](https://github.com/rafaeljohn9/mpesakit/issues) | [💡 Request Feature](https://github.com/rafaeljohn9/mpesakit/issues/new)
179
162
 
180
163
  </div>
@@ -43,5 +43,5 @@ class AccountBalance(BaseModel):
43
43
  "Authorization": f"Bearer {self.token_manager.get_token()}",
44
44
  "Content-Type": "application/json",
45
45
  }
46
- response_data = self.http_client.post(url, json=dict(request), headers=headers)
46
+ response_data = self.http_client.post(url, json=request.model_dump(by_alias=True), headers=headers)
47
47
  return AccountBalanceResponse(**response_data)
@@ -136,7 +136,7 @@ class AccountBalanceResultMetadata(BaseModel):
136
136
  """Metadata for Account Balance result notification."""
137
137
 
138
138
  ResultType: int = Field(..., description="Type of result (0=Success, 1=Waiting).")
139
- ResultCode: int = Field(..., description="Result code (0=Success).")
139
+ ResultCode: int | str = Field(..., description="Result code (0=Success).")
140
140
  ResultDesc: str = Field(..., description="Result description.")
141
141
  OriginatorConversationID: str = Field(
142
142
  ..., description="Originator conversation ID."
@@ -238,7 +238,7 @@ class AccountBalanceResultCallback(BaseModel):
238
238
  class AccountBalanceResultCallbackResponse(BaseModel):
239
239
  """Schema for response to Account Balance result callback."""
240
240
 
241
- ResultCode: int = Field(
241
+ ResultCode: int | str = Field(
242
242
  default=0, description="Result code (0=Success, other=Failure)."
243
243
  )
244
244
  ResultDesc: str = Field(
@@ -279,7 +279,7 @@ class AccountBalanceTimeoutCallback(BaseModel):
279
279
  class AccountBalanceTimeoutCallbackResponse(BaseModel):
280
280
  """Schema for response to Account Balance timeout callback."""
281
281
 
282
- ResultCode: int = Field(
282
+ ResultCode: int | str = Field(
283
283
  default=0,
284
284
  description="Result code (0=Success, other=Failure).",
285
285
  )
@@ -108,7 +108,7 @@ class B2BExpressCheckoutCallback(BaseModel):
108
108
  class B2BExpressCallbackResponse(BaseModel):
109
109
  """Response schema for B2B Express Checkout callback."""
110
110
 
111
- ResultCode: int = Field(
111
+ ResultCode: int | str = Field(
112
112
  default=0,
113
113
  description="Result code (0=Success, other=Failure).",
114
114
  )
@@ -43,5 +43,5 @@ class B2C(BaseModel):
43
43
  "Authorization": f"Bearer {self.token_manager.get_token()}",
44
44
  "Content-Type": "application/json",
45
45
  }
46
- response_data = self.http_client.post(url, json=dict(request), headers=headers)
46
+ response_data = self.http_client.post(url, json=request.model_dump(by_alias=True), headers=headers)
47
47
  return B2CResponse(**response_data)
@@ -167,7 +167,7 @@ class B2CResultMetadata(BaseModel):
167
167
  """Metadata for B2C result notification."""
168
168
 
169
169
  ResultType: int = Field(..., description="Type of result (0=Success, 1=Failure).")
170
- ResultCode: int = Field(..., description="Result code (0=Success).")
170
+ ResultCode: int | str = Field(..., description="Result code (0=Success).")
171
171
  ResultDesc: str = Field(..., description="Result description.")
172
172
  OriginatorConversationID: str = Field(
173
173
  ..., description="Originator conversation ID."
@@ -279,7 +279,7 @@ class B2CResultCallback(BaseModel):
279
279
  class B2CResultCallbackResponse(BaseModel):
280
280
  """Schema for response to B2C result callback."""
281
281
 
282
- ResultCode: int = Field(
282
+ ResultCode: int | str = Field(
283
283
  default=0, description="Result code (0=Success, other=Failure)."
284
284
  )
285
285
  ResultDesc: str = Field(
@@ -320,7 +320,7 @@ class B2CTimeoutCallback(BaseModel):
320
320
  class B2CTimeoutCallbackResponse(BaseModel):
321
321
  """Schema for response to B2C timeout callback."""
322
322
 
323
- ResultCode: int = Field(
323
+ ResultCode: int | str = Field(
324
324
  default=0,
325
325
  description="Result code (0=Success, other=Failure).",
326
326
  )
@@ -120,7 +120,7 @@ class B2CAccountTopUpCallbackResult(BaseModel):
120
120
  ResultType: int = Field(
121
121
  ..., description="Status code for transaction sent to listener."
122
122
  )
123
- ResultCode: int = Field(
123
+ ResultCode: int | str = Field(
124
124
  ..., description="Transaction result status code. 0 means success."
125
125
  )
126
126
  ResultDesc: str = Field(
@@ -231,13 +231,13 @@ class B2CAccountTopUpCallback(BaseModel):
231
231
 
232
232
  def is_successful(self) -> bool:
233
233
  """Check if the callback indicates a successful transaction."""
234
- return self.Result.ResultCode == 0
234
+ return str(self.Result.ResultCode) == "0"
235
235
 
236
236
 
237
237
  class B2CAccountTopUpCallbackResponse(BaseModel):
238
238
  """Response schema for B2C Account TopUp callback."""
239
239
 
240
- ResultCode: int = Field(
240
+ ResultCode: int | str = Field(
241
241
  default=0, description="Result code of the callback. 0 indicates success."
242
242
  )
243
243
  ResultDesc: str = Field(
@@ -297,7 +297,7 @@ class B2CAccountTopUpTimeoutCallback(BaseModel):
297
297
  class B2CAccountTopUpTimeoutCallbackResponse(BaseModel):
298
298
  """Schema for response to B2C Account TopUp timeout callback."""
299
299
 
300
- ResultCode: int = Field(
300
+ ResultCode: int | str = Field(
301
301
  default=0,
302
302
  description="Result code (0=Success, other=Failure).",
303
303
  )
@@ -43,5 +43,5 @@ class BusinessBuyGoods(BaseModel):
43
43
  "Authorization": f"Bearer {self.token_manager.get_token()}",
44
44
  "Content-Type": "application/json",
45
45
  }
46
- response_data = self.http_client.post(url, json=dict(request), headers=headers)
46
+ response_data = self.http_client.post(url, json=request.model_dump(by_alias=True), headers=headers)
47
47
  return BusinessBuyGoodsResponse(**response_data)
@@ -240,7 +240,7 @@ class BusinessBuyGoodsResultCallback(BaseModel):
240
240
  class BusinessBuyGoodsResultCallbackResponse(BaseModel):
241
241
  """Response schema for Business Buy Goods result callback."""
242
242
 
243
- ResultCode: int = 0
243
+ ResultCode: int | str = 0
244
244
  ResultDesc: str = "Callback received successfully."
245
245
 
246
246
  model_config = ConfigDict(
@@ -289,7 +289,7 @@ class BusinessBuyGoodsTimeoutCallback(BaseModel):
289
289
  class BusinessBuyGoodsTimeoutCallbackResponse(BaseModel):
290
290
  """Response schema for Business Buy Goods timeout callback."""
291
291
 
292
- ResultCode: int = 0
292
+ ResultCode: int | str = 0
293
293
  ResultDesc: str = "Timeout notification received successfully."
294
294
 
295
295
  model_config = ConfigDict(
@@ -43,5 +43,5 @@ class BusinessPayBill(BaseModel):
43
43
  "Authorization": f"Bearer {self.token_manager.get_token()}",
44
44
  "Content-Type": "application/json",
45
45
  }
46
- response_data = self.http_client.post(url, json=dict(request), headers=headers)
46
+ response_data = self.http_client.post(url, json=request.model_dump(by_alias=True), headers=headers)
47
47
  return BusinessPayBillResponse(**response_data)
@@ -229,13 +229,13 @@ class BusinessPayBillResultCallback(BaseModel):
229
229
 
230
230
  def is_successful(self) -> bool:
231
231
  """Check if the result indicates success."""
232
- return self.Result.ResultCode == 0
232
+ return str(self.Result.ResultCode) == "0"
233
233
 
234
234
 
235
235
  class BusinessPayBillResultCallbackResponse(BaseModel):
236
236
  """Response schema for Business PayBill result callback."""
237
237
 
238
- ResultCode: int = 0
238
+ ResultCode: int | str = 0
239
239
  ResultDesc: str = "Callback received successfully."
240
240
 
241
241
  model_config = ConfigDict(
@@ -271,7 +271,7 @@ class BusinessPayBillTimeoutCallback(BaseModel):
271
271
  class BusinessPayBillTimeoutCallbackResponse(BaseModel):
272
272
  """Response schema for Business PayBill timeout callback."""
273
273
 
274
- ResultCode: int = 0
274
+ ResultCode: int | str = 0
275
275
  ResultDesc: str = "Timeout notification received successfully."
276
276
 
277
277
  model_config = ConfigDict(
@@ -41,7 +41,7 @@ class C2B(BaseModel):
41
41
  "Authorization": f"Bearer {self.token_manager.get_token()}",
42
42
  "Content-Type": "application/json",
43
43
  }
44
- response_data = self.http_client.post(url, json=dict(request), headers=headers)
44
+ response_data = self.http_client.post(url, json=request.model_dump(by_alias=True), headers=headers)
45
45
 
46
46
  # Safaricom API Bug: There is a typo in the response field name
47
47
  # "OriginatorCoversationID" should be "OriginatorConversationID"
@@ -152,7 +152,7 @@ class C2BValidationRequest(BaseModel):
152
152
  ThirdPartyTransID: Optional[str] = Field(
153
153
  None, description="Partner transaction ID (optional)."
154
154
  )
155
- MSISDN: int = Field(..., description="Customer mobile number.")
155
+ MSISDN: int | str = Field(..., description="Customer mobile number.")
156
156
  FirstName: Optional[str] = Field(None, description="Customer's first name.")
157
157
  MiddleName: Optional[str] = Field(None, description="Customer's middle name.")
158
158
  LastName: Optional[str] = Field(None, description="Customer's last name.")
@@ -226,7 +226,7 @@ class C2BValidationResponse(BaseModel):
226
226
  class C2BConfirmationResponse(BaseModel):
227
227
  """Schema for confirmation acknowledgment from your ConfirmationURL."""
228
228
 
229
- ResultCode: int = Field(0, description="Always 0 (success).")
229
+ ResultCode: int | str = Field(0, description="Always 0 (success).")
230
230
  ResultDesc: str = Field("Success", description="Usually 'Success'.")
231
231
 
232
232
  model_config = ConfigDict(
@@ -44,6 +44,6 @@ class DynamicQRCode(BaseModel):
44
44
  "Content-Type": "application/json",
45
45
  }
46
46
 
47
- response_data = self.http_client.post(url, json=dict(request), headers=headers)
47
+ response_data = self.http_client.post(url, json=request.model_dump(by_alias=True), headers=headers)
48
48
 
49
49
  return DynamicQRGenerateResponse(**response_data)
@@ -0,0 +1,5 @@
1
+ from .http_client import HttpClient,AsyncHttpClient
2
+ from .mpesa_http_client import MpesaHttpClient
3
+ from .mpesa_async_http_client import MpesaAsyncHttpClient
4
+
5
+ __all__ = ["HttpClient", "MpesaHttpClient","AsyncHttpClient","MpesaAsyncHttpClient"]
@@ -0,0 +1,53 @@
1
+ """http_client.py: Defines an abstract base HTTP client class for making HTTP requests.
2
+
3
+ Provides a reusable interface for GET and POST requests.
4
+ """
5
+
6
+ from typing import Dict, Any, Optional
7
+ from abc import ABC, abstractmethod
8
+
9
+
10
+ class HttpClient(ABC):
11
+ """Abstract base HTTP client for making GET and POST requests."""
12
+
13
+ @abstractmethod
14
+ def post(
15
+ self, url: str, json: Dict[str, Any], headers: Dict[str, str]
16
+ ) -> Dict[str, Any]:
17
+ """Sends a POST request."""
18
+ pass
19
+
20
+ @abstractmethod
21
+ def get(
22
+ self,
23
+ url: str,
24
+ params: Optional[Dict[str, Any]] = None,
25
+ headers: Optional[Dict[str, str]] = None,
26
+ ) -> Dict[str, Any]:
27
+ """Sends a GET request."""
28
+ pass
29
+
30
+ class AsyncHttpClient(ABC):
31
+ """Abstract base HTTP client for making asynchronous GET and POST requests.
32
+
33
+ This is the base class for all asynchronous HTTP client implementations.
34
+ It provides the foundation for making asynchronous HTTP requests, which are
35
+ essential for interacting with the Mpesa API non-blockingly.
36
+ """
37
+
38
+ @abstractmethod
39
+ async def post(
40
+ self, url: str, json: Dict[str, Any], headers: Dict[str, str]
41
+ ) -> Dict[str, Any]:
42
+ """Sends an asynchronous POST request."""
43
+ pass
44
+
45
+ @abstractmethod
46
+ async def get(
47
+ self,
48
+ url: str,
49
+ params: Optional[Dict[str, Any]] = None,
50
+ headers: Optional[Dict[str, str]] = None,
51
+ ) -> Dict[str, Any]:
52
+ """Sends an asynchronous GET request."""
53
+ pass