Skip to content

Commit 9c89be9

Browse files
authored
Merge pull request #38 from RafaelJohn9/dev
Dev
2 parents 3d681d7 + bf66198 commit 9c89be9

44 files changed

Lines changed: 2838 additions & 33 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/code-quality.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ jobs:
3636
run: uv pip install -e '.[dev]'
3737

3838
- name: Run Ruff
39-
run: uv run ruff check mpesa_sdk
39+
run: uv run ruff check mpesa_sdk && uv run ruff check tests
4040

4141
mypy:
4242
runs-on: ubuntu-latest

.github/workflows/publish.yml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
name: Publish Python Package
2+
3+
on:
4+
push:
5+
tags:
6+
- "v*" # publish only on version tags like v0.1.0
7+
8+
jobs:
9+
build:
10+
runs-on: ubuntu-latest
11+
12+
steps:
13+
- name: Checkout code
14+
uses: actions/checkout@v4
15+
16+
- name: Set up Python
17+
uses: actions/setup-python@v5
18+
with:
19+
python-version: "3.12"
20+
21+
- name: Install Hatch
22+
run: pip install hatch
23+
24+
- name: Build package
25+
run: hatch build
26+
27+
- name: Publish to PyPI
28+
env:
29+
HATCH_INDEX_USER: __token__
30+
HATCH_INDEX_AUTH: ${{ secrets.PYPI_API_TOKEN }}
31+
run: hatch publish

.github/workflows/tests.yml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
name: Run Unit Tests
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
pull_request:
7+
workflow_dispatch: # Allows manual triggering of the workflow
8+
9+
jobs:
10+
unit-tests:
11+
runs-on: ubuntu-latest
12+
13+
steps:
14+
- name: Checkout code
15+
uses: actions/checkout@v4
16+
17+
- name: Set up Python
18+
uses: actions/setup-python@v4
19+
with:
20+
python-version: '3.12'
21+
22+
- name: Install dependencies
23+
run: |
24+
python -m pip install --upgrade pip
25+
pip install ".[dev,test]"
26+
pip install pytest
27+
pip install -e .
28+
29+
- name: Run unit tests
30+
run: |
31+
pytest tests/unit

mpesa_sdk/B2C/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
B2CResultParameter,
66
B2CResultMetadata,
77
B2CResultCallback,
8-
B2CTimeoutCallbackResponse,
98
B2CTimeoutCallback,
109
B2CTimeoutCallbackResponse,
1110
)

mpesa_sdk/C2B/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55
C2BValidationResponse,
66
C2BConfirmationResponse,
77
C2BValidationResultCodeType,
8+
C2BResponseType,
89
)
910
from .C2B import C2B
1011

1112
__all__ = [
13+
"C2BResponseType",
1214
"C2BRegisterUrlRequest",
1315
"C2BRegisterUrlResponse",
1416
"C2BValidationRequest",

mpesa_sdk/bill_manager/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
BillManagerPaymentNotificationResponse,
1515
BillManagerPaymentAcknowledgmentRequest,
1616
BillManagerPaymentAcknowledgmentResponse,
17+
InvoiceItem,
1718
)
1819

1920
from .bill_manager import BillManager
@@ -35,4 +36,5 @@
3536
"BillManagerPaymentAcknowledgmentRequest",
3637
"BillManagerPaymentAcknowledgmentResponse",
3738
"BillManager",
39+
"InvoiceItem",
3840
]

mpesa_sdk/bill_manager/schemas.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ class BillManagerOptInResponse(BaseModel):
5656
}
5757
)
5858

59+
def is_successful(self) -> bool:
60+
"""Checks if the response indicates success."""
61+
return self.rescode == "200"
62+
5963

6064
# Update Opt-in Details API
6165
class BillManagerUpdateOptInRequest(BaseModel):
@@ -98,6 +102,10 @@ class BillManagerUpdateOptInResponse(BaseModel):
98102
json_schema_extra={"example": {"resmsg": "Success", "rescode": "200"}}
99103
)
100104

105+
def is_successful(self) -> bool:
106+
"""Checks if the response indicates success."""
107+
return self.rescode == "200"
108+
101109

102110
# Invoice Item
103111
class InvoiceItem(BaseModel):
@@ -324,6 +332,10 @@ class BillManagerBulkInvoiceResponse(BaseModel):
324332
}
325333
)
326334

335+
def is_successful(self) -> bool:
336+
"""Checks if the response indicates success."""
337+
return self.rescode == "200"
338+
327339

328340
# Cancel Single Invoice API
329341
class BillManagerCancelSingleInvoiceRequest(BaseModel):

mpesa_sdk/dynamic_qr_code/schemas.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
"""
66

77
from enum import Enum
8-
from typing import Optional
98
from pydantic import BaseModel, Field, ConfigDict
109
from pydantic import model_validator
1110
from mpesa_sdk.utils.phone import normalize_phone_number

mpesa_sdk/mpesa_client.py

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
"""MpesaClient: A unified client for M-PESA services."""
2+
3+
from mpesa_sdk.auth import TokenManager
4+
from mpesa_sdk.http_client import MpesaHttpClient
5+
from mpesa_sdk.services import (
6+
B2BService,
7+
B2CService,
8+
BalanceService,
9+
BillService,
10+
C2BService,
11+
DynamicQRCodeService,
12+
StkPushService,
13+
RatibaService,
14+
ReversalService,
15+
TaxService,
16+
TransactionService,
17+
)
18+
19+
20+
class MpesaClient:
21+
"""Unified client for all M-PESA services."""
22+
23+
def __init__(self, consumer_key: str, consumer_secret: str) -> None:
24+
"""Initialize the MpesaClient with all service facades."""
25+
self.http_client = MpesaHttpClient()
26+
self.token_manager = TokenManager(
27+
http_client=self.http_client,
28+
consumer_key=consumer_key,
29+
consumer_secret=consumer_secret,
30+
)
31+
32+
# express => M-PESA STK Push
33+
self.express = StkPushService(
34+
http_client=self.http_client, token_manager=self.token_manager
35+
)
36+
37+
# b2c => M-PESA Business to Customer services
38+
self.b2c = B2CService(
39+
http_client=self.http_client, token_manager=self.token_manager
40+
)
41+
42+
# b2b => M-PESA Business to Business services
43+
self.b2b = B2BService(
44+
http_client=self.http_client, token_manager=self.token_manager
45+
)
46+
47+
# transaction => M-PESA Transaction status services
48+
self.transactions = TransactionService(
49+
http_client=self.http_client, token_manager=self.token_manager
50+
)
51+
52+
# tax => M-PESA Tax services
53+
self.tax = TaxService(
54+
http_client=self.http_client, token_manager=self.token_manager
55+
)
56+
57+
# balance => M-PESA Account balance services
58+
self.balance = BalanceService(
59+
http_client=self.http_client, token_manager=self.token_manager
60+
)
61+
62+
# reversal => M-PESA Transaction reversal services
63+
self.reversal = ReversalService(
64+
http_client=self.http_client, token_manager=self.token_manager
65+
)
66+
67+
# bill => M-PESA Bill services
68+
self.bill = BillService(
69+
http_client=self.http_client, token_manager=self.token_manager
70+
)
71+
72+
# dynamic_qr => M-PESA Dynamic QR services
73+
self.dynamic_qr = DynamicQRCodeService(
74+
http_client=self.http_client, token_manager=self.token_manager
75+
)
76+
77+
# c2b => M-PESA Customer to Business services
78+
self.c2b = C2BService(
79+
http_client=self.http_client, token_manager=self.token_manager
80+
)
81+
82+
# ratiba => M-PESA Ratiba services
83+
self.ratiba = RatibaService(
84+
http_client=self.http_client, token_manager=self.token_manager
85+
)

mpesa_sdk/reversal/schemas.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
It includes models for reversal requests, responses, and result notifications.
44
"""
55

6-
from enum import Enum
76
from pydantic import BaseModel, Field, ConfigDict, model_validator
87
from typing import Optional, List
98

0 commit comments

Comments
 (0)