forjio-plugipay 0.5.0__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.
- forjio_plugipay-0.5.0/.gitignore +34 -0
- forjio_plugipay-0.5.0/CHANGELOG.md +33 -0
- forjio_plugipay-0.5.0/LICENSE +21 -0
- forjio_plugipay-0.5.0/PKG-INFO +163 -0
- forjio_plugipay-0.5.0/README.md +137 -0
- forjio_plugipay-0.5.0/plugipay/__init__.py +110 -0
- forjio_plugipay-0.5.0/plugipay/api_generated.py +1217 -0
- forjio_plugipay-0.5.0/plugipay/client.py +1844 -0
- forjio_plugipay-0.5.0/plugipay/errors.py +66 -0
- forjio_plugipay-0.5.0/plugipay/resources.py +235 -0
- forjio_plugipay-0.5.0/plugipay/webhooks.py +132 -0
- forjio_plugipay-0.5.0/pyproject.toml +46 -0
- forjio_plugipay-0.5.0/tests/__init__.py +0 -0
- forjio_plugipay-0.5.0/tests/test_api_generated.py +99 -0
- forjio_plugipay-0.5.0/tests/test_client.py +457 -0
- forjio_plugipay-0.5.0/tests/test_fixed_methods.py +90 -0
- forjio_plugipay-0.5.0/tests/test_live_app.py +160 -0
- forjio_plugipay-0.5.0/tests/test_routes_exist.py +176 -0
- forjio_plugipay-0.5.0/tests/test_uploads.py +101 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
node_modules/
|
|
2
|
+
dist/
|
|
3
|
+
.next/
|
|
4
|
+
.env
|
|
5
|
+
.env.local
|
|
6
|
+
.env.*.local
|
|
7
|
+
*.log
|
|
8
|
+
.DS_Store
|
|
9
|
+
*.swp
|
|
10
|
+
coverage/
|
|
11
|
+
playwright-report/
|
|
12
|
+
test-results/
|
|
13
|
+
tsconfig.tsbuildinfo
|
|
14
|
+
sdk/node/node_modules/
|
|
15
|
+
sdk/node/dist/
|
|
16
|
+
sdk/node/*.tgz
|
|
17
|
+
sdk/js/node_modules/
|
|
18
|
+
sdk/js/dist/
|
|
19
|
+
sdk/js/*.tgz
|
|
20
|
+
sdk/python/.venv/
|
|
21
|
+
sdk/python/.pytest_cache/
|
|
22
|
+
sdk/python/dist/
|
|
23
|
+
sdk/python/build/
|
|
24
|
+
sdk/python/*.egg-info/
|
|
25
|
+
**/__pycache__/
|
|
26
|
+
|
|
27
|
+
# Throwaway live-check scripts and their screenshots. Named `_*` on
|
|
28
|
+
# purpose so a stray `git add -A` cannot sweep them into a commit —
|
|
29
|
+
# that has happened twice (ec03d87, 821486a).
|
|
30
|
+
e2e/_*.mjs
|
|
31
|
+
e2e/screenshots/_*.png
|
|
32
|
+
|
|
33
|
+
# apigen scratch copy (scripts/apigen.sh)
|
|
34
|
+
backend/.apigen/
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.5.0
|
|
4
|
+
- `webhook_endpoints.update(endpoint_id, *, url=None, events=None, description=None, active=None)` (`active=True` re-enables an endpoint Plugipay switched off for failing and clears its failure streak), and the delivery log: `webhook_endpoints.list_deliveries(*, limit=None, cursor=None, endpoint_id=None, status=None, type=None)` (a `PageResult`), `get_delivery(delivery_id)`, `retry_delivery(delivery_id)`. New resource `WebhookDelivery`.
|
|
5
|
+
- The `WebhookEndpoint`, `ApiKey` and `BillingTier` docstrings list the fields the API actually returns (endpoint failure streak and `disabledAt` / `disabledReason`; a key's `name`, `keyPrefix`, `environment`, `scopes`, `lastUsedAt`, `key`; a tier's prices, fee and limits).
|
|
6
|
+
- `client.api`: `webhooks_deliveries`, `webhooks_get_deliveries`, `webhooks_deliveries_retry` (regenerated).
|
|
7
|
+
|
|
8
|
+
## 0.4.0
|
|
9
|
+
Hand-written methods that could never succeed against the API now send what their routes require. Some signatures changed (a minor bump in 0.x); every changed call failed before.
|
|
10
|
+
- `customers.update` sends an `Idempotency-Key` (every call was `400`) and takes `external_id`, `tax_id`, `default_payment_token_id`, `metadata`.
|
|
11
|
+
- `plans.create(*, name, interval, prices=None, currency=None, amount=None, portal_features=None, description=None, interval_count=None, trial_days=None, …)`: the API's prices + `portalFeatures`. `currency` + `amount` are the shorthand for one flat price; `portal_features` defaults to cancel + update the payment method.
|
|
12
|
+
- `templates.create(*, kind, name, config, is_default=None)` / `templates.update(id, *, name=None, config=None)` send `config` (the API never took `document`). `templates.preview(*, kind, config)` returns `{"html": …}` from the HTML page the route answers with (it raised `invalid_response`); `sample_data` is gone. `templates.duplicate(id, name=…)` renames the copy with a second request.
|
|
13
|
+
- `adapters.update_xendit` / `update_paypal` / `update_midtrans` / `update_manual` send an `Idempotency-Key` (every call was `400`) and take keyword fields (`secret_key=`, `client_id=`, `server_key=`, `bank_accounts=`, …); the 0.3 dict argument still works. `adapters.list()` returns the adapters (it always returned `[]`: the API answers an object keyed by kind).
|
|
14
|
+
- `adapters.start_managed_onboarding(*, email)`, `simulate_managed_onboarding(*, kyb_status="live", capabilities_status="live", payouts_ready=True)` and `onboarding.provision_managed()` (no input; returns `{"subAccountId": …}`) send the bodies their routes take.
|
|
15
|
+
- `api_keys.create(*, name, environment=None, scopes=None)` (person-only: a key still gets `403`). `invoices.create` and `invoices.finalize` send an `Idempotency-Key`.
|
|
16
|
+
- `request()` returns the text of a `text/html` success response.
|
|
17
|
+
- The routes-exist test now also fails a hand-written method that sends no `Idempotency-Key` where the route takes one, or whose body (every declared parameter filled in) the route's schema refuses.
|
|
18
|
+
|
|
19
|
+
## 0.3.0
|
|
20
|
+
- `uploads.image` sends the image as `multipart/form-data` in the field `file`, which is what `POST /api/v1/uploads/image` takes; it sent JSON before and always got 400. It takes `image(file, *, filename=None, content_type=None)` (`file`: bytes or a binary file object); the 0.2 keywords `base64=`, `filename=`, `mime=` still work. It returns `url`, `fileName`, `fileSize`.
|
|
21
|
+
- `client.api.uploads_image(file=…)` and `client.api.public_checkout_sessions_proof_image(id, file=…)` upload a file; `request()` / `_apigen_request()` take `files=` and `form=` (multipart/form-data, signed over no body as the API checks it).
|
|
22
|
+
- Every request is signed with a fresh `X-Plugipay-Timestamp` when it is sent; the API now refuses a signed time more than 300 s off its clock either way (`401 timestamp_skew`, `401 invalid_timestamp`).
|
|
23
|
+
|
|
24
|
+
## 0.2.0
|
|
25
|
+
- **Published on PyPI as `forjio-plugipay`** (`pip install forjio-plugipay`); the import is still `import plugipay`. The PyPI name `plugipay` belongs to an account Forjio no longer publishes from (like `forjio-linksnap`).
|
|
26
|
+
- `client.api.<area>_<action>(...)`: every Plugipay feature route, one method each, generated from the API spec (`scripts/apigen.sh`). Signed like every other request, with an idempotency key on writes.
|
|
27
|
+
- `api_key=`: a key minted in the dashboard (`pk_live_…` / `pk_test_…`) is sent as `Authorization: Bearer <key>`; `key_id` + `secret` are now optional when `api_key` is given.
|
|
28
|
+
- `webhook_endpoints.list/create/delete` called `/api/v1/webhook-endpoints`, which the API does not serve (every call 404'd); they now call `/api/v1/webhooks`.
|
|
29
|
+
- The API now takes a key on every customer feature. Person-only (403 person_only with a key): `api_keys.*`, `account.*`, `workspaces.create/update/delete`; `admin_portal.*` is for Plugipay's operators.
|
|
30
|
+
- A test checks every hand-written method's route against the API spec (`backend/openapi.json`).
|
|
31
|
+
|
|
32
|
+
## 0.1.0
|
|
33
|
+
- Initial release: HMAC-signed REST client with full resource coverage.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Forjio
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: forjio-plugipay
|
|
3
|
+
Version: 0.5.0
|
|
4
|
+
Summary: Official Python SDK for Plugipay — HMAC-signed REST client with full resource coverage (customers, plans, checkout sessions, invoices, subscriptions, refunds, adapters, payouts, ledger, reports, webhooks).
|
|
5
|
+
Project-URL: Homepage, https://plugipay.com
|
|
6
|
+
Project-URL: Documentation, https://plugipay.com/docs
|
|
7
|
+
Project-URL: Source, https://github.com/hachimi-cat/plugipay-python
|
|
8
|
+
Project-URL: Issues, https://github.com/hachimi-cat/plugipay-python/issues
|
|
9
|
+
Author-email: Forjio <hello@plugipay.com>
|
|
10
|
+
License: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: checkout,forjio,indonesia,midtrans,payments,plugipay,subscriptions,xendit
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
18
|
+
Classifier: Topic :: Office/Business :: Financial
|
|
19
|
+
Classifier: Typing :: Typed
|
|
20
|
+
Requires-Python: >=3.9
|
|
21
|
+
Requires-Dist: httpx>=0.27.0
|
|
22
|
+
Provides-Extra: dev
|
|
23
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
24
|
+
Requires-Dist: respx>=0.21.0; extra == 'dev'
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
# plugipay (Python)
|
|
28
|
+
|
|
29
|
+
Official Python SDK for [Plugipay](https://plugipay.com). HMAC-signed
|
|
30
|
+
REST client; works with Flask, FastAPI, Django, or anything else that
|
|
31
|
+
speaks Python 3.9+.
|
|
32
|
+
|
|
33
|
+
## Install
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install forjio-plugipay
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Quickstart
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from plugipay import PlugipayClient
|
|
43
|
+
|
|
44
|
+
plug = PlugipayClient(key_id="ak_live_...", secret="...")
|
|
45
|
+
|
|
46
|
+
# Create a customer
|
|
47
|
+
customer = plug.customers.create(email="ada@example.com", name="Ada Lovelace")
|
|
48
|
+
|
|
49
|
+
# Open a checkout session
|
|
50
|
+
session = plug.checkout_sessions.create(
|
|
51
|
+
amount=125000,
|
|
52
|
+
currency="IDR",
|
|
53
|
+
methods=["qris", "va", "ewallet"],
|
|
54
|
+
success_url="https://yourapp.com/checkout/success",
|
|
55
|
+
cancel_url="https://yourapp.com/checkout/cancel",
|
|
56
|
+
customer_id=customer["id"],
|
|
57
|
+
)
|
|
58
|
+
print(session["hostedUrl"])
|
|
59
|
+
|
|
60
|
+
# Paginate customers
|
|
61
|
+
page = plug.customers.list(limit=20)
|
|
62
|
+
for c in page.data:
|
|
63
|
+
print(c["id"], c.get("email"))
|
|
64
|
+
if page.has_more:
|
|
65
|
+
next_page = plug.customers.list(limit=20, cursor=page.cursor)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
The client is a context manager — `with PlugipayClient(...) as plug:`
|
|
69
|
+
will close the underlying httpx pool for you.
|
|
70
|
+
|
|
71
|
+
## Resource namespaces
|
|
72
|
+
|
|
73
|
+
Mirrors `@forjio/plugipay-node` 1:1:
|
|
74
|
+
|
|
75
|
+
| Namespace | Methods |
|
|
76
|
+
|---|---|
|
|
77
|
+
| `customers` | `create`, `get`, `list`, `update` |
|
|
78
|
+
| `plans` | `create`, `get`, `list`, `update`, `archive` |
|
|
79
|
+
| `checkout_sessions` | `create`, `get`, `list`, `cancel`, `confirm` |
|
|
80
|
+
| `invoices` | `create`, `get`, `list`, `finalize`, `pay`, `void`, `send_email` |
|
|
81
|
+
| `subscriptions` | `create`, `get`, `list`, `cancel`, `pause`, `resume` |
|
|
82
|
+
| `portal_sessions` | `create` |
|
|
83
|
+
| `receipts` | `list`, `get` |
|
|
84
|
+
| `payouts` | `create`, `get`, `list`, `cancel`, `mark_in_transit`, `mark_paid`, `mark_failed`, `balance`, `get_bank_account`, `update_bank_account` |
|
|
85
|
+
| `ledger` | `list`, `balances` |
|
|
86
|
+
| `reports` | `pnl`, `cash_flow` |
|
|
87
|
+
| `webhook_endpoints` | `list`, `create`, `delete` |
|
|
88
|
+
| `events` | `list`, `get` |
|
|
89
|
+
| `refunds` | `create`, `get`, `list` |
|
|
90
|
+
| `adapters` | `list`, `update_xendit`, `update_paypal`, `update_midtrans`, `update_manual`, `managed_onboarding_state`, `start_managed_onboarding`, `simulate_managed_onboarding` |
|
|
91
|
+
| `api_keys` | `list`, `create`, `revoke` |
|
|
92
|
+
| `billing` | `list_tiers`, `list_plans`, `refresh_tiers` |
|
|
93
|
+
| `onboarding` | `provision_managed` |
|
|
94
|
+
| `checkout_settings` | `get`, `update` |
|
|
95
|
+
| `templates` | `list`, `get`, `create`, `update`, `make_default`, `duplicate`, `preview`, `delete` |
|
|
96
|
+
| `uploads` | `image` |
|
|
97
|
+
| `workspaces` | `list`, `create`, `update`, `delete` |
|
|
98
|
+
| `account` | `get`, `update`, `list_sessions`, `revoke_session`, `revoke_all_sessions`, `list_linked`, `unlink`, `change_email`, `change_password`, `list_members` |
|
|
99
|
+
| `admin_portal` | `me`, `list_billing_accounts`, `update_billing_account`, `list_partners`, `create_partner`, `update_partner`, `delete_partner` |
|
|
100
|
+
| `admin` | `provision_workspace`, `get_workspace`, `partner_usage` |
|
|
101
|
+
|
|
102
|
+
## Webhooks
|
|
103
|
+
|
|
104
|
+
Plugipay signs every webhook with HMAC-SHA256 over `${timestamp}.${rawBody}`.
|
|
105
|
+
Pass the *raw* request body (bytes or str), not the parsed JSON.
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
from flask import Flask, request, abort
|
|
109
|
+
from plugipay import verify_webhook, PlugipaySignatureError
|
|
110
|
+
import os
|
|
111
|
+
|
|
112
|
+
app = Flask(__name__)
|
|
113
|
+
SECRET = os.environ["PLUGIPAY_WEBHOOK_SECRET"]
|
|
114
|
+
|
|
115
|
+
@app.post("/webhooks/plugipay")
|
|
116
|
+
def plugipay_webhook():
|
|
117
|
+
raw = request.get_data()
|
|
118
|
+
sig = request.headers.get("X-Plugipay-Signature", "")
|
|
119
|
+
try:
|
|
120
|
+
event = verify_webhook(raw, sig, SECRET)
|
|
121
|
+
except PlugipaySignatureError:
|
|
122
|
+
abort(400)
|
|
123
|
+
if event.type == "plugipay.invoice.paid.v1":
|
|
124
|
+
invoice = event.object
|
|
125
|
+
# ...
|
|
126
|
+
return "", 204
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Platform keys
|
|
130
|
+
|
|
131
|
+
If you hold a platform-admin key acting across many merchants, scope
|
|
132
|
+
each call with `X-Plugipay-On-Behalf-Of` via `for_merchant()`:
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
plat = PlugipayClient(key_id="ak_live_platform_...", secret="...")
|
|
136
|
+
for_acc = plat.for_merchant("acc_abc123")
|
|
137
|
+
for_acc.invoices.list()
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Errors
|
|
141
|
+
|
|
142
|
+
Every failure raises `PlugipayError` (or a subclass —
|
|
143
|
+
`PlugipayNetworkError`, `PlugipayTimeoutError`, `PlugipaySignatureError`).
|
|
144
|
+
The exception carries `.status`, `.code`, `.message`, and (when present)
|
|
145
|
+
`.request_id`.
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
from plugipay import PlugipayClient, PlugipayError
|
|
149
|
+
|
|
150
|
+
plug = PlugipayClient(key_id="...", secret="...")
|
|
151
|
+
try:
|
|
152
|
+
plug.customers.get("cus_does_not_exist")
|
|
153
|
+
except PlugipayError as e:
|
|
154
|
+
print(e.status, e.code, e.message, e.request_id)
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Source
|
|
158
|
+
|
|
159
|
+
<https://github.com/hachimi-cat/plugipay-python>
|
|
160
|
+
|
|
161
|
+
## License
|
|
162
|
+
|
|
163
|
+
MIT
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# plugipay (Python)
|
|
2
|
+
|
|
3
|
+
Official Python SDK for [Plugipay](https://plugipay.com). HMAC-signed
|
|
4
|
+
REST client; works with Flask, FastAPI, Django, or anything else that
|
|
5
|
+
speaks Python 3.9+.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install forjio-plugipay
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quickstart
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
from plugipay import PlugipayClient
|
|
17
|
+
|
|
18
|
+
plug = PlugipayClient(key_id="ak_live_...", secret="...")
|
|
19
|
+
|
|
20
|
+
# Create a customer
|
|
21
|
+
customer = plug.customers.create(email="ada@example.com", name="Ada Lovelace")
|
|
22
|
+
|
|
23
|
+
# Open a checkout session
|
|
24
|
+
session = plug.checkout_sessions.create(
|
|
25
|
+
amount=125000,
|
|
26
|
+
currency="IDR",
|
|
27
|
+
methods=["qris", "va", "ewallet"],
|
|
28
|
+
success_url="https://yourapp.com/checkout/success",
|
|
29
|
+
cancel_url="https://yourapp.com/checkout/cancel",
|
|
30
|
+
customer_id=customer["id"],
|
|
31
|
+
)
|
|
32
|
+
print(session["hostedUrl"])
|
|
33
|
+
|
|
34
|
+
# Paginate customers
|
|
35
|
+
page = plug.customers.list(limit=20)
|
|
36
|
+
for c in page.data:
|
|
37
|
+
print(c["id"], c.get("email"))
|
|
38
|
+
if page.has_more:
|
|
39
|
+
next_page = plug.customers.list(limit=20, cursor=page.cursor)
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The client is a context manager — `with PlugipayClient(...) as plug:`
|
|
43
|
+
will close the underlying httpx pool for you.
|
|
44
|
+
|
|
45
|
+
## Resource namespaces
|
|
46
|
+
|
|
47
|
+
Mirrors `@forjio/plugipay-node` 1:1:
|
|
48
|
+
|
|
49
|
+
| Namespace | Methods |
|
|
50
|
+
|---|---|
|
|
51
|
+
| `customers` | `create`, `get`, `list`, `update` |
|
|
52
|
+
| `plans` | `create`, `get`, `list`, `update`, `archive` |
|
|
53
|
+
| `checkout_sessions` | `create`, `get`, `list`, `cancel`, `confirm` |
|
|
54
|
+
| `invoices` | `create`, `get`, `list`, `finalize`, `pay`, `void`, `send_email` |
|
|
55
|
+
| `subscriptions` | `create`, `get`, `list`, `cancel`, `pause`, `resume` |
|
|
56
|
+
| `portal_sessions` | `create` |
|
|
57
|
+
| `receipts` | `list`, `get` |
|
|
58
|
+
| `payouts` | `create`, `get`, `list`, `cancel`, `mark_in_transit`, `mark_paid`, `mark_failed`, `balance`, `get_bank_account`, `update_bank_account` |
|
|
59
|
+
| `ledger` | `list`, `balances` |
|
|
60
|
+
| `reports` | `pnl`, `cash_flow` |
|
|
61
|
+
| `webhook_endpoints` | `list`, `create`, `delete` |
|
|
62
|
+
| `events` | `list`, `get` |
|
|
63
|
+
| `refunds` | `create`, `get`, `list` |
|
|
64
|
+
| `adapters` | `list`, `update_xendit`, `update_paypal`, `update_midtrans`, `update_manual`, `managed_onboarding_state`, `start_managed_onboarding`, `simulate_managed_onboarding` |
|
|
65
|
+
| `api_keys` | `list`, `create`, `revoke` |
|
|
66
|
+
| `billing` | `list_tiers`, `list_plans`, `refresh_tiers` |
|
|
67
|
+
| `onboarding` | `provision_managed` |
|
|
68
|
+
| `checkout_settings` | `get`, `update` |
|
|
69
|
+
| `templates` | `list`, `get`, `create`, `update`, `make_default`, `duplicate`, `preview`, `delete` |
|
|
70
|
+
| `uploads` | `image` |
|
|
71
|
+
| `workspaces` | `list`, `create`, `update`, `delete` |
|
|
72
|
+
| `account` | `get`, `update`, `list_sessions`, `revoke_session`, `revoke_all_sessions`, `list_linked`, `unlink`, `change_email`, `change_password`, `list_members` |
|
|
73
|
+
| `admin_portal` | `me`, `list_billing_accounts`, `update_billing_account`, `list_partners`, `create_partner`, `update_partner`, `delete_partner` |
|
|
74
|
+
| `admin` | `provision_workspace`, `get_workspace`, `partner_usage` |
|
|
75
|
+
|
|
76
|
+
## Webhooks
|
|
77
|
+
|
|
78
|
+
Plugipay signs every webhook with HMAC-SHA256 over `${timestamp}.${rawBody}`.
|
|
79
|
+
Pass the *raw* request body (bytes or str), not the parsed JSON.
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
from flask import Flask, request, abort
|
|
83
|
+
from plugipay import verify_webhook, PlugipaySignatureError
|
|
84
|
+
import os
|
|
85
|
+
|
|
86
|
+
app = Flask(__name__)
|
|
87
|
+
SECRET = os.environ["PLUGIPAY_WEBHOOK_SECRET"]
|
|
88
|
+
|
|
89
|
+
@app.post("/webhooks/plugipay")
|
|
90
|
+
def plugipay_webhook():
|
|
91
|
+
raw = request.get_data()
|
|
92
|
+
sig = request.headers.get("X-Plugipay-Signature", "")
|
|
93
|
+
try:
|
|
94
|
+
event = verify_webhook(raw, sig, SECRET)
|
|
95
|
+
except PlugipaySignatureError:
|
|
96
|
+
abort(400)
|
|
97
|
+
if event.type == "plugipay.invoice.paid.v1":
|
|
98
|
+
invoice = event.object
|
|
99
|
+
# ...
|
|
100
|
+
return "", 204
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Platform keys
|
|
104
|
+
|
|
105
|
+
If you hold a platform-admin key acting across many merchants, scope
|
|
106
|
+
each call with `X-Plugipay-On-Behalf-Of` via `for_merchant()`:
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
plat = PlugipayClient(key_id="ak_live_platform_...", secret="...")
|
|
110
|
+
for_acc = plat.for_merchant("acc_abc123")
|
|
111
|
+
for_acc.invoices.list()
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Errors
|
|
115
|
+
|
|
116
|
+
Every failure raises `PlugipayError` (or a subclass —
|
|
117
|
+
`PlugipayNetworkError`, `PlugipayTimeoutError`, `PlugipaySignatureError`).
|
|
118
|
+
The exception carries `.status`, `.code`, `.message`, and (when present)
|
|
119
|
+
`.request_id`.
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
from plugipay import PlugipayClient, PlugipayError
|
|
123
|
+
|
|
124
|
+
plug = PlugipayClient(key_id="...", secret="...")
|
|
125
|
+
try:
|
|
126
|
+
plug.customers.get("cus_does_not_exist")
|
|
127
|
+
except PlugipayError as e:
|
|
128
|
+
print(e.status, e.code, e.message, e.request_id)
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Source
|
|
132
|
+
|
|
133
|
+
<https://github.com/hachimi-cat/plugipay-python>
|
|
134
|
+
|
|
135
|
+
## License
|
|
136
|
+
|
|
137
|
+
MIT
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"""Official Python SDK for Plugipay.
|
|
2
|
+
|
|
3
|
+
Mirrors the public surface of ``@forjio/plugipay-node``:
|
|
4
|
+
|
|
5
|
+
- :class:`PlugipayClient` — HMAC-signed client with every resource
|
|
6
|
+
namespace (customers, plans, checkout_sessions, invoices,
|
|
7
|
+
subscriptions, refunds, adapters, api_keys, workspaces, templates,
|
|
8
|
+
uploads, account, admin, ...).
|
|
9
|
+
- :func:`verify_webhook` — verify ``X-Plugipay-Signature`` and parse the
|
|
10
|
+
event envelope.
|
|
11
|
+
- :class:`PlugipayError` + subclasses — the only thing the SDK raises.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from .client import PlugipayClient
|
|
15
|
+
from .errors import (
|
|
16
|
+
PlugipayError,
|
|
17
|
+
PlugipayNetworkError,
|
|
18
|
+
PlugipaySignatureError,
|
|
19
|
+
PlugipayTimeoutError,
|
|
20
|
+
)
|
|
21
|
+
from .resources import (
|
|
22
|
+
AccountProfile,
|
|
23
|
+
AdapterConfig,
|
|
24
|
+
AdminPortalIdentity,
|
|
25
|
+
ApiKey,
|
|
26
|
+
AvailableBalance,
|
|
27
|
+
BankAccount,
|
|
28
|
+
BillingTier,
|
|
29
|
+
BrowserSession,
|
|
30
|
+
CashFlowReport,
|
|
31
|
+
CheckoutSession,
|
|
32
|
+
CheckoutSettings,
|
|
33
|
+
Customer,
|
|
34
|
+
EventRecord,
|
|
35
|
+
Invoice,
|
|
36
|
+
LedgerBalance,
|
|
37
|
+
LedgerEntry,
|
|
38
|
+
LinkedAccount,
|
|
39
|
+
ManagedOnboardingState,
|
|
40
|
+
PageResult,
|
|
41
|
+
PartnerUsageSummary,
|
|
42
|
+
PartnerWorkspace,
|
|
43
|
+
Payout,
|
|
44
|
+
Plan,
|
|
45
|
+
PnLReport,
|
|
46
|
+
PortalSession,
|
|
47
|
+
ReceiptSummary,
|
|
48
|
+
Refund,
|
|
49
|
+
Resource,
|
|
50
|
+
Subscription,
|
|
51
|
+
Template,
|
|
52
|
+
UploadedFile,
|
|
53
|
+
WebhookDelivery,
|
|
54
|
+
WebhookEndpoint,
|
|
55
|
+
WebhookEvent,
|
|
56
|
+
Workspace,
|
|
57
|
+
WorkspaceMember,
|
|
58
|
+
)
|
|
59
|
+
from .webhooks import verify_webhook
|
|
60
|
+
|
|
61
|
+
__all__ = [
|
|
62
|
+
# client
|
|
63
|
+
"PlugipayClient",
|
|
64
|
+
# errors
|
|
65
|
+
"PlugipayError",
|
|
66
|
+
"PlugipayNetworkError",
|
|
67
|
+
"PlugipaySignatureError",
|
|
68
|
+
"PlugipayTimeoutError",
|
|
69
|
+
# webhooks
|
|
70
|
+
"verify_webhook",
|
|
71
|
+
# resources
|
|
72
|
+
"AccountProfile",
|
|
73
|
+
"AdapterConfig",
|
|
74
|
+
"AdminPortalIdentity",
|
|
75
|
+
"ApiKey",
|
|
76
|
+
"AvailableBalance",
|
|
77
|
+
"BankAccount",
|
|
78
|
+
"BillingTier",
|
|
79
|
+
"BrowserSession",
|
|
80
|
+
"CashFlowReport",
|
|
81
|
+
"CheckoutSession",
|
|
82
|
+
"CheckoutSettings",
|
|
83
|
+
"Customer",
|
|
84
|
+
"EventRecord",
|
|
85
|
+
"Invoice",
|
|
86
|
+
"LedgerBalance",
|
|
87
|
+
"LedgerEntry",
|
|
88
|
+
"LinkedAccount",
|
|
89
|
+
"ManagedOnboardingState",
|
|
90
|
+
"PageResult",
|
|
91
|
+
"PartnerUsageSummary",
|
|
92
|
+
"PartnerWorkspace",
|
|
93
|
+
"Payout",
|
|
94
|
+
"Plan",
|
|
95
|
+
"PnLReport",
|
|
96
|
+
"PortalSession",
|
|
97
|
+
"ReceiptSummary",
|
|
98
|
+
"Refund",
|
|
99
|
+
"Resource",
|
|
100
|
+
"Subscription",
|
|
101
|
+
"Template",
|
|
102
|
+
"UploadedFile",
|
|
103
|
+
"WebhookDelivery",
|
|
104
|
+
"WebhookEndpoint",
|
|
105
|
+
"WebhookEvent",
|
|
106
|
+
"Workspace",
|
|
107
|
+
"WorkspaceMember",
|
|
108
|
+
]
|
|
109
|
+
|
|
110
|
+
__version__ = "0.5.0"
|