Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
bdbb38e
feat (account balance): added async support in account balance API.
RafaelJohn9 Jan 18, 2026
b583631
feat (B2B Express Checkout): added async support in B2B Express Check…
RafaelJohn9 Jan 18, 2026
5ac511c
feat (B2C Account Top up): added async support in B2C Account Top Up …
RafaelJohn9 Jan 18, 2026
ffd1f8d
feat (B2C): added async support for B2C API with unit tests for test…
RafaelJohn9 Jan 18, 2026
4f9d473
feat (Bill Manager): added async support for Bill Manager API and w…
RafaelJohn9 Jan 18, 2026
e1a15d3
fix (tests): removed bill integration unit tests since its a duplicat…
RafaelJohn9 Jan 18, 2026
822a827
feat (Business PayBill): added async support for Business PayBill AP…
RafaelJohn9 Jan 18, 2026
9cf406a
feat (Business Buy Goods): added async support for Business Buy Goods…
RafaelJohn9 Jan 18, 2026
986aa2a
feat (C2B): added async support for C2B API with unit tests
RafaelJohn9 Jan 18, 2026
95ff7bf
feat (Dynamic QR Code): added async support for Dynamic QR Code API …
RafaelJohn9 Jan 18, 2026
8b5c47d
feat (Mpesa Express): added async support for Mpesa Express API with…
RafaelJohn9 Jan 18, 2026
262cc03
feat (Mpesa Ratiba): added async support for Mpesa Ratiba API with u…
RafaelJohn9 Jan 18, 2026
a046398
feat (Reversal): added async support for Reversal API with unit tests.
RafaelJohn9 Jan 18, 2026
ae31069
feat (C2B): added async support for C2B API with unit tests.
RafaelJohn9 Jan 18, 2026
52bee65
feat (Transaction Status): added async support for Transaction Status…
RafaelJohn9 Jan 18, 2026
b043ff8
fix (AsyncBillManager): Added 'Async' in 'BillManager' error message …
RafaelJohn9 Jan 18, 2026
9a56ae3
fix (B2B Express Checkout): Updated async tests to use AsyncMock inst…
RafaelJohn9 Jan 18, 2026
e18390d
feat (Async Dynamic QR Code): Updated tests to assert await instead o…
RafaelJohn9 Jan 18, 2026
6a713a8
fix (Account Balance): Updated async tests to use AsyncMock instead o…
RafaelJohn9 Jan 18, 2026
782314f
feat (Reversal): Updated tests to assert await instead of called and …
RafaelJohn9 Jan 18, 2026
da67b14
fix (B2C): Updated tests misleading name.
RafaelJohn9 Jan 18, 2026
e11a25a
feat (Mpesa Express): Updated tests to assert await instead of called…
RafaelJohn9 Jan 18, 2026
4ea020f
tests (Account Balance): updated assertion to assert await instead of…
RafaelJohn9 Jan 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion mpesakit/account_balance/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from .account_balance import AccountBalance, AsyncAccountBalance
from .schemas import (
AccountBalanceIdentifierType,
AccountBalanceRequest,
Expand All @@ -7,9 +8,9 @@
AccountBalanceTimeoutCallback,
AccountBalanceTimeoutCallbackResponse,
)
from .account_balance import AccountBalance

__all__ = [
"AsyncAccountBalance",
"AccountBalance",
"AccountBalanceRequest",
"AccountBalanceResponse",
Expand Down
42 changes: 39 additions & 3 deletions mpesakit/account_balance/account_balance.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
"""

from pydantic import BaseModel, ConfigDict
from mpesakit.auth import TokenManager
from mpesakit.http_client import HttpClient

from mpesakit.auth import AsyncTokenManager, TokenManager
from mpesakit.http_client import AsyncHttpClient, HttpClient

from .schemas import (
AccountBalanceRequest,
Expand Down Expand Up @@ -43,5 +44,40 @@ def query(self, request: AccountBalanceRequest) -> AccountBalanceResponse:
"Authorization": f"Bearer {self.token_manager.get_token()}",
"Content-Type": "application/json",
}
response_data = self.http_client.post(url, json=request.model_dump(by_alias=True), headers=headers)
response_data = self.http_client.post(
url, json=request.model_dump(by_alias=True), headers=headers
)
return AccountBalanceResponse(**response_data)


class AsyncAccountBalance(BaseModel):
"""Represents the async Account Balance API client for M-Pesa operations.

Attributes:
http_client (AsyncHttpClient): Async HTTP client for making requests to the M-Pesa API.
token_manager (AsyncTokenManager): Async token manager for authentication.
"""

http_client: AsyncHttpClient
token_manager: AsyncTokenManager

model_config = ConfigDict(arbitrary_types_allowed=True)

async def query(self, request: AccountBalanceRequest) -> AccountBalanceResponse:
"""Queries the account balance asynchronously.

Args:
request (AccountBalanceRequest): The account balance query request.

Returns:
AccountBalanceResponse: Response from the M-Pesa API.
"""
url = "/mpesa/accountbalance/v1/query"
headers = {
"Authorization": f"Bearer {await self.token_manager.get_token()}",
"Content-Type": "application/json",
}
response_data = await self.http_client.post(
url, json=request.model_dump(by_alias=True), headers=headers
)
return AccountBalanceResponse(**response_data)
8 changes: 4 additions & 4 deletions mpesakit/b2b_express_checkout/__init__.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
from .b2b_express_checkout import AsyncB2BExpressCheckout, B2BExpressCheckout
from .schemas import (
B2BExpressCallbackResponse,
B2BExpressCheckoutCallback,
B2BExpressCheckoutRequest,
B2BExpressCheckoutResponse,
B2BExpressCheckoutCallback,
B2BExpressCallbackResponse,
)

from .b2b_express_checkout import B2BExpressCheckout

__all__ = [
"AsyncB2BExpressCheckout",
"B2BExpressCheckout",
"B2BExpressCheckoutRequest",
"B2BExpressCheckoutResponse",
Expand Down
40 changes: 38 additions & 2 deletions mpesakit/b2b_express_checkout/b2b_express_checkout.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
"""

from pydantic import BaseModel, ConfigDict
from mpesakit.auth import TokenManager
from mpesakit.http_client import HttpClient

from mpesakit.auth import AsyncTokenManager, TokenManager
from mpesakit.http_client import AsyncHttpClient, HttpClient

from .schemas import (
B2BExpressCheckoutRequest,
Expand Down Expand Up @@ -49,3 +50,38 @@ def ussd_push(
url, json=request.model_dump(mode="json"), headers=headers
)
return B2BExpressCheckoutResponse(**response_data)


class AsyncB2BExpressCheckout(BaseModel):
"""Represents the async B2B Express Checkout API client for M-Pesa operations.

Attributes:
http_client (AsyncHttpClient): Async HTTP client for making requests to the M-Pesa API.
token_manager (AsyncTokenManager): Async token manager for authentication.
"""

http_client: AsyncHttpClient
token_manager: AsyncTokenManager

model_config = ConfigDict(arbitrary_types_allowed=True)

async def ussd_push(
self, request: B2BExpressCheckoutRequest
) -> B2BExpressCheckoutResponse:
"""Initiates a B2B Express Checkout USSD Push transaction asynchronously.

Args:
request (B2BExpressCheckoutRequest): The B2B Express Checkout request data.

Returns:
B2BExpressCheckoutResponse: Response from the M-Pesa API.
"""
url = "/v1/ussdpush/get-msisdn"
headers = {
"Authorization": f"Bearer {await self.token_manager.get_token()}",
"Content-Type": "application/json",
}
response_data = await self.http_client.post(
url, json=request.model_dump(mode="json"), headers=headers
)
return B2BExpressCheckoutResponse(**response_data)
7 changes: 4 additions & 3 deletions mpesakit/b2c/__init__.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
from .b2c import B2C, AsyncB2C
from .schemas import (
B2CCommandIDType,
B2CRequest,
B2CResponse,
B2CResultParameter,
B2CResultMetadata,
B2CResultCallback,
B2CResultMetadata,
B2CResultParameter,
B2CTimeoutCallback,
B2CTimeoutCallbackResponse,
)
from .b2c import B2C

__all__ = [
"AsyncB2C",
"B2C",
"B2CCommandIDType",
"B2CRequest",
Expand Down
42 changes: 39 additions & 3 deletions mpesakit/b2c/b2c.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
"""

from pydantic import BaseModel, ConfigDict
from mpesakit.auth import TokenManager
from mpesakit.http_client import HttpClient

from mpesakit.auth import AsyncTokenManager, TokenManager
from mpesakit.http_client import AsyncHttpClient, HttpClient

from .schemas import (
B2CRequest,
Expand Down Expand Up @@ -43,5 +44,40 @@ def send_payment(self, request: B2CRequest) -> B2CResponse:
"Authorization": f"Bearer {self.token_manager.get_token()}",
"Content-Type": "application/json",
}
response_data = self.http_client.post(url, json=request.model_dump(by_alias=True), headers=headers)
response_data = self.http_client.post(
url, json=request.model_dump(by_alias=True), headers=headers
)
return B2CResponse(**response_data)


class AsyncB2C(BaseModel):
"""Represents the async B2C API client for M-Pesa Business to Customer operations.

Attributes:
http_client (AsyncHttpClient): Async HTTP client for making requests to the M-Pesa API.
token_manager (AsyncTokenManager): Async token manager for authentication.
"""

http_client: AsyncHttpClient
token_manager: AsyncTokenManager

model_config = ConfigDict(arbitrary_types_allowed=True)

async def send_payment(self, request: B2CRequest) -> B2CResponse:
"""Initiates a B2C payment request asynchronously.

Args:
request (B2CRequest): The payment request details.

Returns:
B2CResponse: Response from the M-Pesa API after payment initiation.
"""
url = "/mpesa/b2c/v3/paymentrequest"
headers = {
"Authorization": f"Bearer {await self.token_manager.get_token()}",
"Content-Type": "application/json",
}
response_data = await self.http_client.post(
url, json=request.model_dump(by_alias=True), headers=headers
)
return B2CResponse(**response_data)
7 changes: 4 additions & 3 deletions mpesakit/b2c_account_top_up/__init__.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
from .b2c_account_top_up import AsyncB2CAccountTopUp, B2CAccountTopUp
from .schemas import (
B2CAccountTopUpRequest,
B2CAccountTopUpResponse,
B2CAccountTopUpCallback,
B2CAccountTopUpCallbackResponse,
B2CAccountTopUpRequest,
B2CAccountTopUpResponse,
B2CAccountTopUpTimeoutCallback,
B2CAccountTopUpTimeoutCallbackResponse,
)
from .b2c_account_top_up import B2CAccountTopUp

__all__ = [
"AsyncB2CAccountTopUp",
"B2CAccountTopUp",
"B2CAccountTopUpRequest",
"B2CAccountTopUpResponse",
Expand Down
39 changes: 37 additions & 2 deletions mpesakit/b2c_account_top_up/b2c_account_top_up.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
"""

from pydantic import BaseModel, ConfigDict
from mpesakit.auth import TokenManager
from mpesakit.http_client import HttpClient

from mpesakit.auth import AsyncTokenManager, TokenManager
from mpesakit.http_client import AsyncHttpClient, HttpClient

from .schemas import (
B2CAccountTopUpRequest,
Expand Down Expand Up @@ -47,3 +48,37 @@ def topup(self, request: B2CAccountTopUpRequest) -> B2CAccountTopUpResponse:
url, json=request.model_dump(mode="json"), headers=headers
)
return B2CAccountTopUpResponse(**response_data)


class AsyncB2CAccountTopUp(BaseModel):
"""Represents the async B2C Account TopUp API client for M-Pesa operations.

Attributes:
http_client (AsyncHttpClient): Async HTTP client for making requests to the M-Pesa API.
token_manager (AsyncTokenManager): Async token manager for authentication.
"""

http_client: AsyncHttpClient
token_manager: AsyncTokenManager

model_config = ConfigDict(arbitrary_types_allowed=True)

async def topup(self, request: B2CAccountTopUpRequest) -> B2CAccountTopUpResponse:
"""Initiates a B2C Account TopUp transaction asynchronously.

Args:
request (B2CAccountTopUpRequest): The B2C Account TopUp request data.

Returns:
B2CAccountTopUpResponse: Response from the M-Pesa API.
"""
url = "/mpesa/b2b/v1/paymentrequest"
token = await self.token_manager.get_token()
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json",
}
response_data = await self.http_client.post(
url, json=request.model_dump(mode="json"), headers=headers
)
return B2CAccountTopUpResponse(**response_data)
24 changes: 12 additions & 12 deletions mpesakit/bill_manager/__init__.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
from .bill_manager import AsyncBillManager, BillManager
from .schemas import (
BillManagerOptInRequest,
BillManagerOptInResponse,
BillManagerUpdateOptInRequest,
BillManagerUpdateOptInResponse,
BillManagerSingleInvoiceRequest,
BillManagerSingleInvoiceResponse,
BillManagerBulkInvoiceRequest,
BillManagerBulkInvoiceResponse,
BillManagerCancelSingleInvoiceRequest,
BillManagerCancelBulkInvoiceRequest,
BillManagerCancelInvoiceResponse,
BillManagerPaymentNotificationRequest,
BillManagerPaymentNotificationResponse,
BillManagerCancelSingleInvoiceRequest,
BillManagerOptInRequest,
BillManagerOptInResponse,
BillManagerPaymentAcknowledgmentRequest,
BillManagerPaymentAcknowledgmentResponse,
BillManagerPaymentNotificationRequest,
BillManagerPaymentNotificationResponse,
BillManagerSingleInvoiceRequest,
BillManagerSingleInvoiceResponse,
BillManagerUpdateOptInRequest,
BillManagerUpdateOptInResponse,
InvoiceItem,
)

from .bill_manager import BillManager

__all__ = [
"AsyncBillManager",
"BillManager",
"BillManagerOptInRequest",
"BillManagerOptInResponse",
"BillManagerUpdateOptInRequest",
Expand All @@ -35,6 +36,5 @@
"BillManagerPaymentNotificationResponse",
"BillManagerPaymentAcknowledgmentRequest",
"BillManagerPaymentAcknowledgmentResponse",
"BillManager",
"InvoiceItem",
]
Loading