Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
3e8f14a
Bump package version
gray-adeyi Feb 18, 2026
690fcd4
Add missing enum variants
gray-adeyi Feb 18, 2026
6492244
Add missing terminal_id params
gray-adeyi Feb 18, 2026
bac9283
Fix typo
gray-adeyi Feb 18, 2026
4ebcb5c
Implement virtual terminal client
gray-adeyi Feb 18, 2026
29c47ff
Implement async virtual terminal client
gray-adeyi Feb 18, 2026
a2ac3a3
Register new sync and async clients
gray-adeyi Feb 18, 2026
9c76654
Rename method from list to all
gray-adeyi Feb 18, 2026
c7c4929
Add authorization related methods to customers clients
gray-adeyi Feb 18, 2026
25d44c9
Rename method
gray-adeyi Feb 18, 2026
1393797
Fix typo
gray-adeyi Feb 18, 2026
56acbf4
Implement direct debit client
gray-adeyi Feb 18, 2026
1510c86
Implement async direct debit client
gray-adeyi Feb 18, 2026
82d4821
Register direct debit clients
gray-adeyi Feb 18, 2026
af372ad
Bump version
gray-adeyi Feb 18, 2026
d9886ee
Add missing optional parameters in client
gray-adeyi Feb 19, 2026
48af75b
Deprecate obsolete params
gray-adeyi Feb 19, 2026
687f46d
Add RefundAccount model
gray-adeyi Feb 19, 2026
f6bef99
Add missing optional parameter for miscellaneous clients
gray-adeyi Feb 19, 2026
07e4c9c
Add missing optional parameters for charge clients
gray-adeyi Feb 19, 2026
3d4b649
Add retry_refund methods to refund clients
gray-adeyi Feb 19, 2026
0cd9820
Update changelog
gray-adeyi Feb 19, 2026
5d91217
Fix error handling
gray-adeyi Feb 19, 2026
d07ed63
Update changelog
gray-adeyi Feb 19, 2026
7076502
Add api reference for new clients
gray-adeyi Feb 19, 2026
aeaa1a8
Add API reference for virtual terminals
gray-adeyi Feb 19, 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
34 changes: 32 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,42 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## unreleased

## [3.1.0] - (8th December 2025)
## [3.2.0] - (19th February 2026)

### Added

- `VirtualTerminalClient` and `AsyncVirtualTerminalClient`
- `DirectDebitClient` and `AsyncDirectDebitClient`
- `initialize_authorization`,`verify_authorization`,`initialize_direct_debit`,`direct_debit_activation_charge` methods to `CustomerClient` and `AsyncCustomerClient`
- `retry_refund` methods to `RefundClient` and `AsyncRefundClient`

### Fixed
- Missing `Channel` enum variants `APPLE_PAY`, `EFT`, `PAYATTITUDE`
- Add missing `terminal_id` parameter to `AsyncTransactionClient.get_transactions` and `TransactionClient.get_transactions`
- Add missing optional parameters `currency`,`type_`,`plan`, `fixed_amount`, `redirect_url`, `success_message`, `notification_email`, `collect_phone` to
`PaymentPageClient` and `AsyncPaymentPageClient` `create` methods.
- Add missing optional parameter `account_reference` to `TransferClient` and
`AsyncTransferClient` `initiate` methods.
- Add missing optional parameters `split_code`, `subaccount`, `transaction_charge`, `bearer`, `qr` to `ChargeClient` and `AsyncChargeClient` `charge` methods.
- Add missing optional parameter `enabled_for_verificaton` to `MiscellaneousClient`
and `enabled_for_verificaton` `get_banks` method.
- Client error handling.

### Changed

- Endpoint for `CustomerClient` and `AsyncCustomerClient` method `deactivate` from `/customer/deactivate_authorization`
to `/customer/authorization/deactivate`
- Deprecate `customer` parameter in `TransferClient` and `AsyncTransferClient` `get_tranfers` methods. It no longer exists in Paystack's API reference.
It is now `recipeint`
- Deprecate `reference` parameter in `RefundClient` and `AsyncRefundClient`
`get_refunds` methods. It no longer exists in Paystack's API reference.

## [3.1.0] - (8th December 2025)

### Added

- `PaystackClient.is_verified_webhook_payload` and `AsyncPaystackClient.is_verified_webhook_payload` methods for
checking the validity of a webhook payload
checking the validity of a webhook payload
- CLI command interface for webhooks with `pypaystack2 webhook start-tunnel-server`

### Changed
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ $ uv add pypaystack2
# For webhook cli
$ pip install -U "pypaystack2[webhook]"
or install with uv
$ uv add "pypaystack2[webhook"
$ uv add "pypaystack2[webhook]"
```

## Usage Preview
Expand Down
2 changes: 2 additions & 0 deletions docs/reference/direct_debits.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
::: pypaystack2.sub_clients.sync_clients.direct_debits
::: pypaystack2.sub_clients.async_clients.direct_debits
2 changes: 2 additions & 0 deletions docs/reference/virtual_terminals.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
::: pypaystack2.sub_clients.sync_clients.virtual_terminals
::: pypaystack2.sub_clients.async_clients.virtual_terminals
4 changes: 1 addition & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "pypaystack2"
version = "3.1.0"
version = "3.2.0"
description = "A developer-friendly client library for Paystack"
readme = "README.md"
license = "MIT"
Expand Down Expand Up @@ -29,7 +29,6 @@ webhook = [
"fastapi[standard]>=0.123.10",
"ngrok>=1.4.0",
"python-dotenv>=1.2.1",
"typer-slim[standard]>=0.20.0",
]

[build-system]
Expand All @@ -49,7 +48,6 @@ format = [
test = [
"python-dotenv>=1.2.1",
"tomli>=2.3.0",
"typer-slim[standard]>=0.20.0",
]
coverage = []
doc = [
Expand Down
2 changes: 1 addition & 1 deletion src/pypaystack2/_metadata.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
__title__ = "pypaystack2"
__version__ = "3.1.0"
__version__ = "3.2.0"
__author__ = [{"name": "Gbenga Adeyi", "email": "adeyigbenga005@gmail.com"}]
__license__ = "MIT"
__copyright__ = "Copyright 2025."
6 changes: 3 additions & 3 deletions src/pypaystack2/base_clients.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from typing import Any, Type, cast

import httpx
from httpx import NetworkError
from httpx import HTTPError
from pydantic import ValidationError

from pypaystack2._metadata import __version__
Expand Down Expand Up @@ -287,7 +287,7 @@ def _handle_request(

try:
response = http_method_handler(**request_kwargs) # type: ignore
except NetworkError as error:
except HTTPError as error:
raise ClientNetworkError(f"network error occurred: {error}", error)
return self._deserialize_response(
response, response_data_model_class, raise_serialization_exception
Expand Down Expand Up @@ -327,7 +327,7 @@ async def _handle_request( # type: ignore
raise ValueError("HTTP Request method not recognised or implemented")
try:
response = await http_method_handler(**request_kwargs)
except NetworkError as error:
except HTTPError as error:
raise ClientNetworkError(f"network error occurred: {error}", error)

return self._deserialize_response(
Expand Down
3 changes: 3 additions & 0 deletions src/pypaystack2/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,13 @@ class Channel(StrEnum):

CARD = "card"
BANK = "bank"
APPLE_PAY = "apple_pay"
USSD = "ussd"
QR = "qr"
MOBILE_MONEY = "mobile_money"
BANK_TRANSFER = "bank_transfer"
EFT = "eft"
PAYATTITUDE = "payattitude"


class Bearer(StrEnum):
Expand Down
57 changes: 38 additions & 19 deletions src/pypaystack2/main_clients.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
from pypaystack2.sub_clients.async_clients.direct_debits import AsyncDirectDebitClient
from pypaystack2.sub_clients.sync_clients.direct_debits import DirectDebitClient
from pypaystack2.sub_clients.async_clients.virtual_terminals import (
AsyncVirtualTerminalClient,
)
from pypaystack2.sub_clients.sync_clients.virtual_terminals import VirtualTerminalClient
from pypaystack2.base_clients import BaseAPIClient, BaseAsyncAPIClient
from pypaystack2.sub_clients import (
ApplePayClient,
Expand Down Expand Up @@ -102,6 +108,10 @@ class PaystackClient(BaseAPIClient):
Transfers control API e.g. `PaystackClient.transfer_control.check_balance`.
verification: A binding to `VerificationClient` providing methods for interacting Paystack's Verification API
e.g. `PaystackClient.verification.resolve_account_number`.
virtual_terminals: A binding to `VirtualTerminalClient` providing methods for interacting with Paystack's
Virtual Terminal API e.g. `VirtualTerminalClient.list`.
direct_debits: A binding to `DirectDebitClient` providing methods for interacting with Paystack's
Direct Debit API e.g. `DirectDebitClient.trigger_activation_charge`.
"""

def __init__(self, secret_key: str | None = None):
Expand Down Expand Up @@ -157,6 +167,8 @@ def __init__(self, secret_key: str | None = None):
self.verification: VerificationClient = VerificationClient(
secret_key=self._secret_key
)
self.virtual_terminals = VirtualTerminalClient(secret_key=self._secret_key)
self.direct_debits = DirectDebitClient(secret_key=self._secret_key)


class AsyncPaystackClient(BaseAsyncAPIClient):
Expand All @@ -167,51 +179,56 @@ class AsyncPaystackClient(BaseAsyncAPIClient):

Attributes:
apple_pay: A binding to `AsyncApplePayClient` providing methods for interacting Paystack's Apple Pay API
e.g. `PaystackClient.apple_pay.get_domains`.
e.g. `AsyncPaystackClient.apple_pay.get_domains`.
bulk_charges: A binding to `AsyncBulkChargeClient` providing methods for interacting Paystack's Bulk charge API
e.g. `PaystackClient.bulk_charges.initiate`.
e.g. `AsyncPaystackClient.bulk_charges.initiate`.
charge: A binding to `AsyncChargeClient` providing methods for interacting Paystack's Charge API
e.g. `PaystackClient.charge.charge`.
e.g. `AsyncPaystackClient.charge.charge`.
integration: A binding to `AsyncIntegrationClient` providing methods for interacting Paystack's Integration API
e.g. `PaystackClient.integration.get_payment_session_timeout`.
e.g. `AsyncPaystackClient.integration.get_payment_session_timeout`.
customers: A binding to `AsyncCustomerClient` providing methods for interacting Paystack's Customer API
e.g. `PaystackClient.customers.create`.
e.g. `AsyncPaystackClient.customers.create`.
dedicated_accounts: A binding to `AsyncDedicatedAccountClient` providing methods for interacting
Paystack's Dedicated account API e.g. `PaystackClient.dedicated_accounts.create`.
disputes: A binding to `AsyncDisputeClient` providing methods for interacting Paystack's Dispute API
e.g. `PaystackClient.disputes.`.
e.g. `AsyncPaystackClient.disputes.`.
payment_requests: A binding to `AsyncPaymentRequestClient` providing methods for interacting Paystack's Payment
request API e.g. `PaystackClient.payment_requests.create`.
request API e.g. `AsyncPaystackClient.payment_requests.create`.
miscellaneous: A binding to `AsyncApplePayClient` providing methods for interacting Paystack's Apple Pay API
e.g. `PaystackClient.miscellaneous.get_banks`.
e.g. `AsyncPaystackClient.miscellaneous.get_banks`.
payment_pages: A binding to `AsyncPaymentPageClient` providing methods for interacting Paystack's Payment Page
API e.g. `PaystackClient.payment_pages.create`.
plans: A binding to `AsyncPlanClient` providing methods for interacting Paystack's Plan API
e.g. `PaystackClient.plans.get_plans`.
e.g. `AsyncPaystackClient.plans.get_plans`.
products: A binding to `AsyncProductClient` providing methods for interacting Paystack's Product API
e.g. `PaystackClient.products.get_product`.
refunds: A binding to `AsyncRefundClient` providing methods for interacting Paystack's Refund API
e.g. `PaystackClient.refunds.create`.
e.g. `AsyncPaystackClient.refunds.create`.
settlements: A binding to `AsyncSettlementClient` providing methods for interacting Paystack's Settlement API
e.g. `PaystackClient.settlements.get_settlements`.
e.g. `AsyncPaystackClient.settlements.get_settlements`.
splits: A binding to `AsyncTransactionSplitClient` providing methods for interacting Paystack's Transaction
split API e.g. `PaystackClient.splits.create`.
split API e.g. `AsyncPaystackClient.splits.create`.
subaccounts: A binding to `AsyncSubAccountClient` providing methods for interacting Paystack's subaccount API
e.g. `PaystackClient.subaccounts.create`.
e.g. `AsyncPaystackClient.subaccounts.create`.
subscriptions: A binding to `AsyncSubscriptionClient` providing methods for interacting Paystack's Subscription
API e.g. `PaystackClient.subscriptions.create`.
API e.g. `AsyncPaystackClient.subscriptions.create`.
terminals: A binding to `AsyncTerminalClient` providing methods for interacting Paystack's Terminal API
e.g. `PaystackClient.terminals.send_event`.
e.g. `AsyncPaystackClient.terminals.send_event`.
transactions: A binding to `AsyncTransactionClient` providing methods for interacting Paystack's Transaction
API e.g. `PaystackClient.transactions.initiate`.
API e.g. `AsyncPaystackClient.transactions.initiate`.
transfer_recipients: A binding to `AsyncTransferRecipientClient` providing methods for interacting Paystack's
Transfer recipients API e.g. `PaystackClient.transfer_recipients.create`.
Transfer recipients API e.g. `AsyncPaystackClient.transfer_recipients.create`.
transfers: A binding to `AsyncTransferClient` providing methods for interacting Paystack's Transfer API
e.g. `PaystackClient.transfers.finalize`.
transfer_control: A binding to `AsyncTransferControlClient` providing methods for interacting Paystack's
Transfers control API e.g. `PaystackClient.transfer_control.check_balance`.
Transfers control API e.g. `AsyncPaystackClient.transfer_control.check_balance`.
verification: A binding to `AsyncVerificationClient` providing methods for interacting Paystack's Verification
API e.g. `PaystackClient.verification.resolve_account_number`.
API e.g. `AsyncPaystackClient.verification.resolve_account_number`.
virtual_terminals: A binding to `VirtualTerminalClient` providing methods for interacting with Paystack's
Virtual Terminal API e.g. `AsyncVirtualTerminalClient.list`.
direct_debits: A binding to `AsyncDirectDebitClient` providing methods for interacting with Paystack's
Direct Debit API e.g. `AsyncDirectDebitClient.trigger_activation_charge`.

"""

def __init__(self, secret_key: str | None = None):
Expand Down Expand Up @@ -243,3 +260,5 @@ def __init__(self, secret_key: str | None = None):
self.transfers = AsyncTransferClient(secret_key=self._secret_key)
self.transfer_control = AsyncTransferControlClient(secret_key=self._secret_key)
self.verification = AsyncVerificationClient(secret_key=self._secret_key)
self.virtual_terminals = AsyncVirtualTerminalClient(secret_key=self._secret_key)
self.direct_debits = AsyncDirectDebitClient(secret_key=self._secret_key)
1 change: 1 addition & 0 deletions src/pypaystack2/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# ruff: noqa: F401
from pypaystack2.models.payload_models import (
RefundAccount,
BulkChargeInstruction,
LineItem,
Tax,
Expand Down
16 changes: 16 additions & 0 deletions src/pypaystack2/models/payload_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,22 @@
from pypaystack2.enums import RecipientType, Currency


class RefundAccount(BaseModel):
"""A dataclass for representing refund account
details

Attributes:
currency:The currency of the customer's bank account.
It should be the same as the currency the payment was made
account_number: The customer's account number
bank_id: The ID representing the customer's bank.
"""

currency: Currency
account_number: str
bank_id: str


class BulkChargeInstruction(BaseModel):
"""A dataclass for bulk charge instruction.

Expand Down
30 changes: 23 additions & 7 deletions src/pypaystack2/sub_clients/async_clients/charge.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from http import HTTPMethod
from typing import Any
from typing import Any, Literal

from pypaystack2.base_clients import BaseAsyncAPIClient, add_to_payload
from pypaystack2.models import Response
Expand All @@ -19,14 +19,19 @@ async def charge(
self,
email: str,
amount: int,
split_code: str | None = None,
subaccount: str | None = None,
transaction_charge: str | None = None,
bearer: Literal["account", "subaccount"] | None = "account",
bank: dict[str, Any] | None = None,
bank_transfer: dict[str, Any] | None = None,
ussd: dict[str, Any] | None = None,
mobile_money: dict[str, Any] | None = None,
qr: dict[str, Any] | None = None,
auth_code: str | None = None,
pin: str | None = None,
metadata: dict[str, Any] | None = None,
reference: str | None = None,
ussd: dict[str, Any] | None = None,
mobile_money: dict[str, Any] | None = None,
device_id: str | None = None,
alternate_model_class: type[PaystackDataModel] | None = None,
) -> Response[ChargeStep] | Response[PaystackDataModel]:
Expand All @@ -36,15 +41,21 @@ async def charge(
email: Customer's email address
amount: Amount should be in kobo if currency is NGN, pesewas, if currency is GHS,
and cents, if currency is ZAR
split_code: The split code of a previously created split. e.g. `SPL_98WF13Eb3w`
subaccount: The code for the subaccount that owns the payment. e.g. `ACCT_8f4s1eq7ml6rlzj`
transaction_charge: An amount used to override the split configuration for a single split payment.
If set, the amount specified goes to the main account regardless of the split configuration.
bearer: Use this param to indicate who bears the transaction charges.
bank: Bank account to charge (don't send if charging an authorization code)
bank_transfer: Takes the settings for the Pay with Transfer (PwT) channel. Pass in the
account_expires_at param to set the expiry time.
ussd: USSD type to charge (don't send if charging an authorization code, bank or card)
mobile_money: Mobile details (don't send if charging an authorization code, bank or card)
qr: Takes a provider parameter with the value set to: scan-to-pay. Currently supported in South Africa only.
auth_code: An authorization code to charge (don't send if charging a bank account)
pin: 4-digit PIN (send with a non-reusable authorization code)
metadata: A dictionary of data.
reference: Unique transaction reference. Only -, .\\`, = and alphanumeric characters allowed.
ussd: USSD type to charge (don't send if charging an authorization code, bank or card)
mobile_money: Mobile details (don't send if charging an authorization code, bank or card)
device_id: This is the unique identifier of the device a user uses in making payment. Only -, .\\`,
= and alphanumeric characters allowed.
alternate_model_class: A pydantic model class to use instead of the
Expand All @@ -66,14 +77,19 @@ async def charge(

payload = {"email": email, "amount": amount}
optional_params = [
("split_code", split_code),
("subaccount", subaccount),
("transaction_charge", transaction_charge),
("bearer", bearer),
("bank", bank),
("bank_transfer", bank_transfer),
("ussd", ussd),
("mobile_money", mobile_money),
("qr", qr),
("authorization_code", auth_code),
("pin", pin),
("metadata", metadata),
("reference", reference),
("ussd", ussd),
("mobile_money", mobile_money),
("device_id", device_id),
]
payload = add_to_payload(optional_params, payload)
Expand Down
Loading
Loading