Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
374573a
Implement is_verified_webhook_payload utility method
gray-adeyi Dec 4, 2025
5fe4e50
Replace make with task
gray-adeyi Dec 7, 2025
938343d
Change package build system from hatch to uv_build
gray-adeyi Dec 7, 2025
966b172
Upgrade project dependencies
gray-adeyi Dec 7, 2025
75f5591
Update project dev dependencies
gray-adeyi Dec 7, 2025
0e9c1fd
Add webhook dependency group and remove mypy configs
gray-adeyi Dec 7, 2025
bbfea26
Sync project
gray-adeyi Dec 7, 2025
cb4c5ff
Add logging config
gray-adeyi Dec 7, 2025
09fd5d3
Implement is_verified_webhook_payload on the clients
gray-adeyi Dec 7, 2025
256732b
Implement utilities for dynamic import of modules and parsing endpoin…
gray-adeyi Dec 7, 2025
9e5a36b
Implement webhook proxy server
gray-adeyi Dec 7, 2025
585c16b
Fix incorrect type annotation
gray-adeyi Dec 7, 2025
0c9c284
Add some webhook model
gray-adeyi Dec 7, 2025
ce4fe7b
Implement cli webhook subcommand
gray-adeyi Dec 7, 2025
b5fa4f7
Implement cli root
gray-adeyi Dec 7, 2025
dff0d8b
Expose script entry point
gray-adeyi Dec 7, 2025
fa4ab39
Add dev setup to task
gray-adeyi Dec 7, 2025
c037b68
Move webhook related enum to separate module
gray-adeyi Dec 7, 2025
d296545
Implement some webhook data as concrete types
gray-adeyi Dec 7, 2025
7b87345
Set unset field and add missing annotation
gray-adeyi Dec 7, 2025
5cfe753
Use rich for error print
gray-adeyi Dec 7, 2025
6ebfd6c
Ensure tunnel server forward endpoint is http
gray-adeyi Dec 7, 2025
53c58f2
Refactor tests
gray-adeyi Dec 7, 2025
40a04d6
Update readme
gray-adeyi Dec 7, 2025
f4acefe
Grouped dependencies
gray-adeyi Dec 7, 2025
195dae6
Add tasks for tests
gray-adeyi Dec 7, 2025
93972e0
Update serialization warning messages
gray-adeyi Dec 7, 2025
775ec46
Update dev-ci
gray-adeyi Dec 7, 2025
8abd46c
Update doc-ci
gray-adeyi Dec 7, 2025
c8e3ba0
Update publish-ci
gray-adeyi Dec 7, 2025
1283bca
Lock dependencies
gray-adeyi Dec 7, 2025
ef06889
Add ChargeStep model
gray-adeyi Dec 7, 2025
ae91144
Add install guide for webhook variant
gray-adeyi Dec 7, 2025
f9671c0
Polish document guide
gray-adeyi Dec 7, 2025
fe7598b
Fix typo
gray-adeyi Dec 7, 2025
66869f2
Test is_verified_webhook_payload
gray-adeyi Dec 7, 2025
1350e1f
Update docs dependencies
gray-adeyi Dec 7, 2025
91bf1e1
Fix bug in is_verified_webhook_payload
gray-adeyi Dec 7, 2025
9303e5f
Update readme
gray-adeyi Dec 7, 2025
81daf8a
Update docs
gray-adeyi Dec 7, 2025
f91950d
Update changelog
gray-adeyi Dec 7, 2025
fdb63b3
Bump package version
gray-adeyi Dec 7, 2025
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
48 changes: 48 additions & 0 deletions .github/workflows/dev-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python

name: Development Continuous Integration

permissions:
contents: write

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]

env:
PAYSTACK_SECRET_KEY: ${{ secrets.PAYSTACK_SECRET_KEY }}

jobs:
dev:
name: Format, lint & test project
if: github.actor != 'github-actions[bot]'
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
fetch-depth: 0
- name: Install the latest version of uv
uses: astral-sh/setup-uv@v7
- name: Install dependencies with uv
run: uv sync --all-groups
- name: Format with ruff
run: uv run ruff format .
- name: Lint with ruff
run: uv run ruff check . --fix
- name: Test package
run: uv run python -m unittest discover tests/unit
- name: Commit changes
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"
git add -A
git commit -m "Apply auto-formatting" || echo "No changes to commit"
- name: Push changes
run: |
git push
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,21 @@ jobs:
name: Deploy docs to GitHub Pages
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
- name: Checkout
uses: actions/checkout@v4
with:
python-version: "3.10"
fetch-depth: 0
- uses: astral-sh/setup-uv@v1
with:
version: "latest"
- name: Install the latest version of uv
uses: astral-sh/setup-uv@v7
- name: Install dependencies
run: uv sync -p 3.10
run: uv sync --group doc
- name: Setup doc deploy
run: |
git config --global user.name Docs deploy
git config --global user.name "Docs deploy"
git config --global user.email docs@dummy.bot.com
- name: Set release notes tag
run: |
export RELEASE_TAG_VERSION="${{ github.event.release.tag_name }}"
echo RELEASE_TAG_VERSION="${RELEASE_TAG_VERSION:1}" >> "$GITHUB_ENV"
- name: Build docs website
run: uv run mike deploy "${RELEASE_TAG_VERSION}" --push
run: uv run mike deploy "${RELEASE_TAG_VERSION}" --push
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Publish Pypaystack2 [version] to PYPI
name: Publish Pypaystack2 to PYPI

on:
release:
Expand All @@ -10,16 +10,14 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install uv
uses: astral-sh/setup-uv@v3
- name: Install the latest version of uv
uses: astral-sh/setup-uv@v7
with:
enable-cache: true
cache-dependency-glob: uv.lock
- name: Set up Python
run: uv python install 3.12 # Or whatever version I want to use.

- name: Install documentation dependencies
run: uv sync --group doc
- name: Build
run: uv build

- name: Publish
run: uv publish -t ${{ secrets.PYPI_TOKEN }}
40 changes: 0 additions & 40 deletions .github/workflows/python-package.yml

This file was deleted.

14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## unreleased

## [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
- CLI command interface for webhooks with `pypaystack2 webhook start-tunnel-server`

### Changed

- `PaystackClient.charge.{charge,submit_pin,submit_otp,submit_phone,submit_birthday,set_address}` and
`AsyncPaystackClient.charge.{charge,submit_pin,submit_otp,submit_phone,submit_birthday,set_address}` response
model to `ChargeStep`

## [3.0.2] - (10th October 2025)

### Fixed
Expand Down
4 changes: 0 additions & 4 deletions Makefile

This file was deleted.

88 changes: 34 additions & 54 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,18 @@ projects. It aims at being developer friendly and easy to use.
- Synchronous and Asynchronous clients.
- Pydantic for data modelling.
- Fees Calculation utilities.
- Webhook support & utilities (>= v3.1.0).

## Installation

```bash
$ pip install -U pypaystack2
# or install with uv
$ uv add pypaystack2
# For webhook cli
$ pip install -U "pypaystack2[webhook]"
or install with uv
$ uv add "pypaystack2[webhook"
```

## Usage Preview
Expand All @@ -39,7 +44,7 @@ you to provide a secret key. It also does not handle likely exceptions that call
for network related issues and `ValueError` for validation related issues.

```bash
Python 3.11.11 (main, Feb 12 2025, 14:51:05) [Clang 19.1.6 ] on linux
Python 3.11.13 (main, Sep 2 2025, 14:20:25) [Clang 20.1.4 ] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from pypaystack2 import __version__
>>> print(__version__)
Expand All @@ -65,59 +70,34 @@ Response(
message='Subscription successfully created',
data=Subscription(
customer=87934333,
plan=2237384,
integration=630606,
domain=<Domain.TEST: 'test'>,
start=1741208073,
status='active',
quantity=1,
amount=100000,
subscription_code='SUB_4sje2s7kb30m5bt',
email_token='uqzg0vneparuxtm',
authorization=368220905,
easy_cron_id=None,
cron_expression='54 20 5 3 *',
next_payment_date=datetime.datetime(2026, 3, 5, 20, 54, tzinfo=TzInfo(UTC)),
open_invoice=None,
invoice_limit=0,
id=759264,
split_code=None,
cancelled_at=None,
updated_at=datetime.datetime(2025, 3, 5, 20, 54, 33, tzinfo=TzInfo(UTC)),
payments_count=None,
most_recent_invoice=None,
invoices=None,
invoice_history=None),
meta=None,
type=None,
code=None,
raw={
'status': True,
'message': 'Subscription successfully created',
'data': {
'customer': 87934333,
'plan': 2237384,
'integration': 630606,
'domain': 'test',
'start': 1741208073,
'status': 'active',
'quantity': 1,
'amount': 100000,
'authorization': 368220905,
'invoice_limit': 0,
'split_code': None,
'metadata': None,
'subscription_code': 'SUB_4sje2s7kb30m5bt',
'email_token': 'uqzg0vneparuxtm',
'id': 759264,
'cancelledAt': None,
'createdAt': '2025-03-05T20:54:33.000Z',
'updatedAt': '2025-03-05T20:54:33.000Z',
'cron_expression': '54 20 5 3 *',
'next_payment_date': '2026-03-05T20:54:00.000Z',
'easy_cron_id': None,
'open_invoice': None}
})
plan=2237384,...
```

### Webhook

PyPaystack2 now supports verifying the authenticity of a webhook payload and a CLI to make working with webhooks locally
seamless

#### Verifying a webhook payload

```bash
Python 3.11.13 (main, Sep 2 2025, 14:20:25) [Clang 20.1.4 ] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from pypaystack2 import PaystackClient
>>> client = PaystackClient()
>>> payload = ... # webhook payload e.g., b'{"event": "customeridentification.success", "data": {"customer_id": 324345768, "customer_code": "CUS_e7urjebaoyk1ze2", "email": "jddae8446e-e54c-42ab-bf37-e5abff14527e@example.com", "identification": {"country": "NG", "type": "bank_account", "bvn": "123*****543", "account_number": "342****22", "bank_code": "121"}}}'
>>> signature = ... # x-paystack-signature e.g., "5d049eb93c7c71fa098f5215d7297bda401710b62df8b392b9052adf8d1a02ff308f6ca57a1db14ffeabd5b66264e9c42de029b7067b9c71eb9c231fb2a8e383"
>>> is_verified_webhook_payload = client.is_verified_webhook_payload(payload,signature)
>>> print(is_verified_webhook_payload)
True
```

#### Forward webhook events from paystack to your app running locally

**Note:** This requires that you install `pypaystack2[webhook]`

```bash
pypaystack2 webhook start-tunnel-server --addr localhost:8000 --ngrok-auth-token
```

## Documentation
Expand Down
31 changes: 31 additions & 0 deletions Taskfile.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# https://taskfile.dev

version: '3'

tasks:
default:
cmds:
- task: dev:setup
dev:setup:
cmds:
- uv sync --all-groups
- uv run pre-commit install
dev:lint:
cmds:
- uvx ty check . -W
docs:serve:
cmds:
- uv run mkdocs serve
docs:build:
cmds:
- uv run mkdocs build
test:all:
cmds:
- task: test:unit
- task: test:integration
test:unit:
cmds:
- uv run python -m unittest discover tests/unit
test:integration:
cmds:
- uv run python -m unittest discover tests/integration
Loading
Loading