fazercards-python-sdk 0.1.1__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.
- fazercards_python_sdk-0.1.1/.gitignore +14 -0
- fazercards_python_sdk-0.1.1/CHANGELOG.md +19 -0
- fazercards_python_sdk-0.1.1/LICENSE +21 -0
- fazercards_python_sdk-0.1.1/PKG-INFO +169 -0
- fazercards_python_sdk-0.1.1/README.md +127 -0
- fazercards_python_sdk-0.1.1/examples/fastapi_webhook.py +30 -0
- fazercards_python_sdk-0.1.1/examples/fazercards.toml +5 -0
- fazercards_python_sdk-0.1.1/pyproject.toml +66 -0
- fazercards_python_sdk-0.1.1/src/fazercards_sdk/__init__.py +102 -0
- fazercards_python_sdk-0.1.1/src/fazercards_sdk/_core.py +308 -0
- fazercards_python_sdk-0.1.1/src/fazercards_sdk/_version.py +1 -0
- fazercards_python_sdk-0.1.1/src/fazercards_sdk/client.py +58 -0
- fazercards_python_sdk-0.1.1/src/fazercards_sdk/config.py +104 -0
- fazercards_python_sdk-0.1.1/src/fazercards_sdk/enums.py +90 -0
- fazercards_python_sdk-0.1.1/src/fazercards_sdk/errors.py +104 -0
- fazercards_python_sdk-0.1.1/src/fazercards_sdk/messages.py +60 -0
- fazercards_python_sdk-0.1.1/src/fazercards_sdk/messages.yaml +48 -0
- fazercards_python_sdk-0.1.1/src/fazercards_sdk/models.py +515 -0
- fazercards_python_sdk-0.1.1/src/fazercards_sdk/money.py +41 -0
- fazercards_python_sdk-0.1.1/src/fazercards_sdk/py.typed +0 -0
- fazercards_python_sdk-0.1.1/src/fazercards_sdk/resources.py +313 -0
- fazercards_python_sdk-0.1.1/src/fazercards_sdk/webhooks.py +63 -0
- fazercards_python_sdk-0.1.1/tests/__init__.py +0 -0
- fazercards_python_sdk-0.1.1/tests/conftest.py +131 -0
- fazercards_python_sdk-0.1.1/tests/test_async.py +41 -0
- fazercards_python_sdk-0.1.1/tests/test_catalog.py +100 -0
- fazercards_python_sdk-0.1.1/tests/test_config.py +49 -0
- fazercards_python_sdk-0.1.1/tests/test_orders.py +236 -0
- fazercards_python_sdk-0.1.1/tests/test_safety.py +135 -0
- fazercards_python_sdk-0.1.1/tests/test_webhooks.py +73 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.1
|
|
4
|
+
|
|
5
|
+
- `orders.get` leaves the id format to the API and only refuses an empty id or one that would change the path
|
|
6
|
+
- Steam amounts always go out with two decimals
|
|
7
|
+
|
|
8
|
+
## 0.1.0
|
|
9
|
+
|
|
10
|
+
- Sync `FazerCards` and async `AsyncFazerCards`
|
|
11
|
+
- Top-ups, gift cards, Telegram Stars and Premium, Steam wallet, orders, balance, account and webhook settings
|
|
12
|
+
- Cursor paged categories and numbered order pages, with helpers that read every page
|
|
13
|
+
- Top-up, gift card and Steam orders always carry an Idempotency-Key, with an optional shop prefix
|
|
14
|
+
- Orders are never resent after an ambiguous failure; `OutcomeUnknownError` carries the key
|
|
15
|
+
- A reused key that returns another item raises `KeyCollisionError`
|
|
16
|
+
- Gift card orders that come back completed without codes are read again
|
|
17
|
+
- Reads retry with backoff, 429 waits for `Retry-After`, connection failures retry because nothing was sent
|
|
18
|
+
- Webhook signature check in constant time, with a body size limit
|
|
19
|
+
- goodconf settings, endpoint paths and messages are configurable
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 qiyascc
|
|
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,169 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: fazercards-python-sdk
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Typed Python client for the FazerCards reseller API: game top-ups, gift cards, Telegram Stars and Premium, Steam wallet. Sync and async.
|
|
5
|
+
Project-URL: Homepage, https://theqiyas.cc/projects/fazercards-sdk/
|
|
6
|
+
Project-URL: Source, https://github.com/qiyascc/fazercards-python-sdk
|
|
7
|
+
Project-URL: Author, https://theqiyas.cc
|
|
8
|
+
Project-URL: Issues, https://github.com/qiyascc/fazercards-python-sdk/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/qiyascc/fazercards-python-sdk/blob/main/CHANGELOG.md
|
|
10
|
+
Author-email: qiyascc <qiyasturk@gmail.com>
|
|
11
|
+
License-Expression: MIT
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Keywords: api,fazercards,gift cards,reseller,sdk,steam,telegram stars,top-up
|
|
14
|
+
Classifier: Development Status :: 4 - Beta
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Office/Business :: Financial
|
|
23
|
+
Classifier: Typing :: Typed
|
|
24
|
+
Requires-Python: >=3.10
|
|
25
|
+
Requires-Dist: goodconf>=7.1
|
|
26
|
+
Requires-Dist: httpx<1,>=0.24
|
|
27
|
+
Requires-Dist: pydantic>=2.7
|
|
28
|
+
Requires-Dist: pyyaml>=6
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: goodconf[toml]>=7.1; extra == 'dev'
|
|
31
|
+
Requires-Dist: goodconf[yaml]>=7.1; extra == 'dev'
|
|
32
|
+
Requires-Dist: mypy>=1.8; extra == 'dev'
|
|
33
|
+
Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
|
|
34
|
+
Requires-Dist: pytest>=7; extra == 'dev'
|
|
35
|
+
Requires-Dist: ruff>=0.4; extra == 'dev'
|
|
36
|
+
Requires-Dist: types-pyyaml; extra == 'dev'
|
|
37
|
+
Provides-Extra: toml
|
|
38
|
+
Requires-Dist: goodconf[toml]>=7.1; extra == 'toml'
|
|
39
|
+
Provides-Extra: yaml
|
|
40
|
+
Requires-Dist: goodconf[yaml]>=7.1; extra == 'yaml'
|
|
41
|
+
Description-Content-Type: text/markdown
|
|
42
|
+
|
|
43
|
+
# fazercards-python-sdk
|
|
44
|
+
|
|
45
|
+
[](https://www.python.org)
|
|
46
|
+
[](https://api.fzr.cards/api/v2)
|
|
47
|
+
[](https://mypy-lang.org)
|
|
48
|
+
[](https://www.python-httpx.org)
|
|
49
|
+
[](https://docs.astral.sh/ruff)
|
|
50
|
+
[](https://github.com/qiyascc/fazercards-python-sdk/blob/main/LICENSE)
|
|
51
|
+
|
|
52
|
+
Typed Python client for the FazerCards reseller API. Game top-ups, gift cards, Telegram Stars and Premium, Steam wallet, orders and signed webhooks. Sync and async, with retry rules built so an order is never placed twice.
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
pip install fazercards-python-sdk
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Top up a game
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
from fazercards_sdk import FazerCards
|
|
62
|
+
|
|
63
|
+
with FazerCards("API_KEY") as fc:
|
|
64
|
+
check = fc.topups.validate("free_fire", "123456789")
|
|
65
|
+
if check.valid:
|
|
66
|
+
order = fc.topups.order("free_fire_cis", "110_diamonds", "123456789", idempotency_key="ORDER-1042")
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
`order.status` moves from `created` to `processing` to `completed`, or to `failed` / `refund` with `order.fail_reason`. Follow it by webhook, or with `fc.orders.wait(order.id)`.
|
|
70
|
+
|
|
71
|
+
## Sell a gift card
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
order = fc.giftcards.order("playstation_tr", "250_try", quantity=1, idempotency_key="ORDER-1043")
|
|
75
|
+
order.codes
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
FazerCards can answer a gift card order with `completed` and an empty `cards` list, then fill the list a moment later. The client reads the order again before it returns (`code_checks`, default 3). If the codes still have not arrived, `order.awaiting_codes` is `True`: keep the order open and read it later, never show the customer an empty result.
|
|
79
|
+
|
|
80
|
+
## Telegram and Steam
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
fc.telegram.stars_quote().price(500)
|
|
84
|
+
fc.telegram.buy_stars("@username", 500)
|
|
85
|
+
fc.telegram.buy_premium("username", months=12)
|
|
86
|
+
|
|
87
|
+
if fc.steam.check_login("steam_login").valid:
|
|
88
|
+
fc.steam.order("steam_login", "19.50", "USD", idempotency_key="ORDER-1044")
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Catalog
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
games = fc.topups.all_categories()
|
|
95
|
+
cards = fc.giftcards.all_categories()
|
|
96
|
+
offers = fc.topups.offers("free_fire_cis")
|
|
97
|
+
offers.get("110_diamonds").price
|
|
98
|
+
offers.fields
|
|
99
|
+
fc.giftcards.cards("playstation_tr").offers[0].stock
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Categories come in cursor pages of up to 500 (`fc.topups.categories(limit, cursor)`); `all_categories()` follows the cursor. Orders come in numbered pages of up to 100 (`fc.orders.list(page, limit)`, `fc.orders.recent(count)`).
|
|
103
|
+
|
|
104
|
+
## Webhooks
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
event = fc.verify_webhook(request.body, headers=request.headers)
|
|
108
|
+
if event.needs_lookup:
|
|
109
|
+
order = fc.orders.get(event.order_id)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
The body is checked against `X-Webhook-Signature` (HMAC SHA256 of the raw body, `sha256=` prefix optional) in constant time. Set the address with `fc.account.set_webhook("https://shop.example/webhook/fazercards")`. See [examples/fastapi_webhook.py](https://github.com/qiyascc/fazercards-python-sdk/blob/main/examples/fastapi_webhook.py).
|
|
113
|
+
|
|
114
|
+
## Safety
|
|
115
|
+
|
|
116
|
+
| Situation | What the client does |
|
|
117
|
+
| --- | --- |
|
|
118
|
+
| Connection refused, nothing sent | Retries, then `NotSentError` |
|
|
119
|
+
| Timeout, cut connection or 5xx on an order | No retry, `OutcomeUnknownError` with the `idempotency_key` |
|
|
120
|
+
| Same failure on a read | Retries with backoff, then `TransportError` |
|
|
121
|
+
| 429 | Waits `Retry-After` and retries, the request was not processed |
|
|
122
|
+
| `resend_keyed_orders=True` | An order with an Idempotency-Key is resent once with the same key |
|
|
123
|
+
| Telegram purchase | Takes no key on the API side, so it is never resent |
|
|
124
|
+
| Reused key that returns another item | `KeyCollisionError`, never counted as success |
|
|
125
|
+
| Gift card `completed` without codes | Read again, then `order.awaiting_codes` |
|
|
126
|
+
| Quantity, months, amount like `1.005` or `True` | `InvalidRequestError` before anything is sent |
|
|
127
|
+
| `base_url` over plain http | `ConfigurationError`, except localhost or `allow_insecure_http` |
|
|
128
|
+
| Webhook body over 256 KB | Rejected before hashing |
|
|
129
|
+
|
|
130
|
+
Every top-up, gift card and Steam order carries an `Idempotency-Key`. Pass your own order number, so a double click or a crash can not buy twice. With several shops on one account set `idempotency_prefix`. Prices are `Decimal` end to end. `fc.rate_limit` keeps the last `X-RateLimit-*` headers.
|
|
131
|
+
|
|
132
|
+
## Endpoints
|
|
133
|
+
|
|
134
|
+
| Namespace | Methods |
|
|
135
|
+
| --- | --- |
|
|
136
|
+
| `topups` | `categories` `all_categories` `offers` `validation_games` `validate` `order` |
|
|
137
|
+
| `giftcards` | `categories` `all_categories` `cards` `order` |
|
|
138
|
+
| `orders` | `get` `list` `recent` `wait` |
|
|
139
|
+
| `telegram` | `stars_quote` `premium_quote` `buy_stars` `buy_premium` |
|
|
140
|
+
| `steam` | `rates` `check_login` `order` |
|
|
141
|
+
| `account` | `me` `balance` `webhook` `set_webhook` |
|
|
142
|
+
| client | `balance` `verify_webhook` `request` |
|
|
143
|
+
|
|
144
|
+
`AsyncFazerCards` has the same methods, awaited.
|
|
145
|
+
|
|
146
|
+
## Errors
|
|
147
|
+
|
|
148
|
+
`FazerCardsError` is the base. API answers raise `APIError` subclasses by status: `BadRequestError` (400), `AuthenticationError` (401, 403), `NotFoundError` (404), `ConflictError` (409), `RateLimitError` (429), `ServerError` (5xx). Each carries `status_code`, `message` and the raw `body`.
|
|
149
|
+
|
|
150
|
+
## Config
|
|
151
|
+
|
|
152
|
+
Pass values in code, or set `FAZERCARDS_API_KEY`, `FAZERCARDS_WEBHOOK_SECRET`, `FAZERCARDS_BASE_URL`, `FAZERCARDS_IDEMPOTENCY_PREFIX`, `FAZERCARDS_TIMEOUT`, `FAZERCARDS_MAX_RETRIES`, or put them in `fazercards.toml` ([example](https://github.com/qiyascc/fazercards-python-sdk/blob/main/examples/fazercards.toml)). Every path is a setting (`FAZERCARDS_ENDPOINTS__BALANCE=...`). Messages live in `messages.yaml` and can be replaced with `messages.use(...)`.
|
|
153
|
+
|
|
154
|
+
## Develop
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
pip install -e ".[dev]" && pytest && ruff check . && mypy src
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Links
|
|
161
|
+
|
|
162
|
+
- Source code: [github.com/qiyascc/fazercards-python-sdk](https://github.com/qiyascc/fazercards-python-sdk)
|
|
163
|
+
- PyPI: [pypi.org/project/fazercards-python-sdk](https://pypi.org/project/fazercards-python-sdk/)
|
|
164
|
+
- Project page: [theqiyas.cc/projects/fazercards-sdk](https://theqiyas.cc/projects/fazercards-sdk/)
|
|
165
|
+
- Author: [Qiyas Sadıqov](https://theqiyas.cc), more projects at [theqiyas.cc](https://theqiyas.cc)
|
|
166
|
+
|
|
167
|
+
## License
|
|
168
|
+
|
|
169
|
+
MIT
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# fazercards-python-sdk
|
|
2
|
+
|
|
3
|
+
[](https://www.python.org)
|
|
4
|
+
[](https://api.fzr.cards/api/v2)
|
|
5
|
+
[](https://mypy-lang.org)
|
|
6
|
+
[](https://www.python-httpx.org)
|
|
7
|
+
[](https://docs.astral.sh/ruff)
|
|
8
|
+
[](https://github.com/qiyascc/fazercards-python-sdk/blob/main/LICENSE)
|
|
9
|
+
|
|
10
|
+
Typed Python client for the FazerCards reseller API. Game top-ups, gift cards, Telegram Stars and Premium, Steam wallet, orders and signed webhooks. Sync and async, with retry rules built so an order is never placed twice.
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pip install fazercards-python-sdk
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Top up a game
|
|
17
|
+
|
|
18
|
+
```python
|
|
19
|
+
from fazercards_sdk import FazerCards
|
|
20
|
+
|
|
21
|
+
with FazerCards("API_KEY") as fc:
|
|
22
|
+
check = fc.topups.validate("free_fire", "123456789")
|
|
23
|
+
if check.valid:
|
|
24
|
+
order = fc.topups.order("free_fire_cis", "110_diamonds", "123456789", idempotency_key="ORDER-1042")
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
`order.status` moves from `created` to `processing` to `completed`, or to `failed` / `refund` with `order.fail_reason`. Follow it by webhook, or with `fc.orders.wait(order.id)`.
|
|
28
|
+
|
|
29
|
+
## Sell a gift card
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
order = fc.giftcards.order("playstation_tr", "250_try", quantity=1, idempotency_key="ORDER-1043")
|
|
33
|
+
order.codes
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
FazerCards can answer a gift card order with `completed` and an empty `cards` list, then fill the list a moment later. The client reads the order again before it returns (`code_checks`, default 3). If the codes still have not arrived, `order.awaiting_codes` is `True`: keep the order open and read it later, never show the customer an empty result.
|
|
37
|
+
|
|
38
|
+
## Telegram and Steam
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
fc.telegram.stars_quote().price(500)
|
|
42
|
+
fc.telegram.buy_stars("@username", 500)
|
|
43
|
+
fc.telegram.buy_premium("username", months=12)
|
|
44
|
+
|
|
45
|
+
if fc.steam.check_login("steam_login").valid:
|
|
46
|
+
fc.steam.order("steam_login", "19.50", "USD", idempotency_key="ORDER-1044")
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Catalog
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
games = fc.topups.all_categories()
|
|
53
|
+
cards = fc.giftcards.all_categories()
|
|
54
|
+
offers = fc.topups.offers("free_fire_cis")
|
|
55
|
+
offers.get("110_diamonds").price
|
|
56
|
+
offers.fields
|
|
57
|
+
fc.giftcards.cards("playstation_tr").offers[0].stock
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Categories come in cursor pages of up to 500 (`fc.topups.categories(limit, cursor)`); `all_categories()` follows the cursor. Orders come in numbered pages of up to 100 (`fc.orders.list(page, limit)`, `fc.orders.recent(count)`).
|
|
61
|
+
|
|
62
|
+
## Webhooks
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
event = fc.verify_webhook(request.body, headers=request.headers)
|
|
66
|
+
if event.needs_lookup:
|
|
67
|
+
order = fc.orders.get(event.order_id)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
The body is checked against `X-Webhook-Signature` (HMAC SHA256 of the raw body, `sha256=` prefix optional) in constant time. Set the address with `fc.account.set_webhook("https://shop.example/webhook/fazercards")`. See [examples/fastapi_webhook.py](https://github.com/qiyascc/fazercards-python-sdk/blob/main/examples/fastapi_webhook.py).
|
|
71
|
+
|
|
72
|
+
## Safety
|
|
73
|
+
|
|
74
|
+
| Situation | What the client does |
|
|
75
|
+
| --- | --- |
|
|
76
|
+
| Connection refused, nothing sent | Retries, then `NotSentError` |
|
|
77
|
+
| Timeout, cut connection or 5xx on an order | No retry, `OutcomeUnknownError` with the `idempotency_key` |
|
|
78
|
+
| Same failure on a read | Retries with backoff, then `TransportError` |
|
|
79
|
+
| 429 | Waits `Retry-After` and retries, the request was not processed |
|
|
80
|
+
| `resend_keyed_orders=True` | An order with an Idempotency-Key is resent once with the same key |
|
|
81
|
+
| Telegram purchase | Takes no key on the API side, so it is never resent |
|
|
82
|
+
| Reused key that returns another item | `KeyCollisionError`, never counted as success |
|
|
83
|
+
| Gift card `completed` without codes | Read again, then `order.awaiting_codes` |
|
|
84
|
+
| Quantity, months, amount like `1.005` or `True` | `InvalidRequestError` before anything is sent |
|
|
85
|
+
| `base_url` over plain http | `ConfigurationError`, except localhost or `allow_insecure_http` |
|
|
86
|
+
| Webhook body over 256 KB | Rejected before hashing |
|
|
87
|
+
|
|
88
|
+
Every top-up, gift card and Steam order carries an `Idempotency-Key`. Pass your own order number, so a double click or a crash can not buy twice. With several shops on one account set `idempotency_prefix`. Prices are `Decimal` end to end. `fc.rate_limit` keeps the last `X-RateLimit-*` headers.
|
|
89
|
+
|
|
90
|
+
## Endpoints
|
|
91
|
+
|
|
92
|
+
| Namespace | Methods |
|
|
93
|
+
| --- | --- |
|
|
94
|
+
| `topups` | `categories` `all_categories` `offers` `validation_games` `validate` `order` |
|
|
95
|
+
| `giftcards` | `categories` `all_categories` `cards` `order` |
|
|
96
|
+
| `orders` | `get` `list` `recent` `wait` |
|
|
97
|
+
| `telegram` | `stars_quote` `premium_quote` `buy_stars` `buy_premium` |
|
|
98
|
+
| `steam` | `rates` `check_login` `order` |
|
|
99
|
+
| `account` | `me` `balance` `webhook` `set_webhook` |
|
|
100
|
+
| client | `balance` `verify_webhook` `request` |
|
|
101
|
+
|
|
102
|
+
`AsyncFazerCards` has the same methods, awaited.
|
|
103
|
+
|
|
104
|
+
## Errors
|
|
105
|
+
|
|
106
|
+
`FazerCardsError` is the base. API answers raise `APIError` subclasses by status: `BadRequestError` (400), `AuthenticationError` (401, 403), `NotFoundError` (404), `ConflictError` (409), `RateLimitError` (429), `ServerError` (5xx). Each carries `status_code`, `message` and the raw `body`.
|
|
107
|
+
|
|
108
|
+
## Config
|
|
109
|
+
|
|
110
|
+
Pass values in code, or set `FAZERCARDS_API_KEY`, `FAZERCARDS_WEBHOOK_SECRET`, `FAZERCARDS_BASE_URL`, `FAZERCARDS_IDEMPOTENCY_PREFIX`, `FAZERCARDS_TIMEOUT`, `FAZERCARDS_MAX_RETRIES`, or put them in `fazercards.toml` ([example](https://github.com/qiyascc/fazercards-python-sdk/blob/main/examples/fazercards.toml)). Every path is a setting (`FAZERCARDS_ENDPOINTS__BALANCE=...`). Messages live in `messages.yaml` and can be replaced with `messages.use(...)`.
|
|
111
|
+
|
|
112
|
+
## Develop
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
pip install -e ".[dev]" && pytest && ruff check . && mypy src
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Links
|
|
119
|
+
|
|
120
|
+
- Source code: [github.com/qiyascc/fazercards-python-sdk](https://github.com/qiyascc/fazercards-python-sdk)
|
|
121
|
+
- PyPI: [pypi.org/project/fazercards-python-sdk](https://pypi.org/project/fazercards-python-sdk/)
|
|
122
|
+
- Project page: [theqiyas.cc/projects/fazercards-sdk](https://theqiyas.cc/projects/fazercards-sdk/)
|
|
123
|
+
- Author: [Qiyas Sadıqov](https://theqiyas.cc), more projects at [theqiyas.cc](https://theqiyas.cc)
|
|
124
|
+
|
|
125
|
+
## License
|
|
126
|
+
|
|
127
|
+
MIT
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from fastapi import FastAPI, HTTPException, Request
|
|
2
|
+
|
|
3
|
+
from fazercards_sdk import AsyncFazerCards, SignatureError
|
|
4
|
+
|
|
5
|
+
app = FastAPI()
|
|
6
|
+
fazercards = AsyncFazerCards()
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@app.post("/webhook/fazercards")
|
|
10
|
+
async def fazercards_webhook(request: Request) -> dict:
|
|
11
|
+
try:
|
|
12
|
+
event = fazercards.verify_webhook(await request.body(), headers=request.headers)
|
|
13
|
+
except SignatureError:
|
|
14
|
+
raise HTTPException(status_code=401) from None
|
|
15
|
+
order = await fazercards.orders.get(event.order_id) if event.needs_lookup else event.order
|
|
16
|
+
if order is None:
|
|
17
|
+
return {"message": "OK"}
|
|
18
|
+
if order.delivered and not order.awaiting_codes:
|
|
19
|
+
await deliver(order.id, order.codes)
|
|
20
|
+
elif order.failed:
|
|
21
|
+
await refund(order.id, order.fail_reason)
|
|
22
|
+
return {"message": "OK"}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
async def deliver(order_id: str, codes: tuple[str, ...]) -> None:
|
|
26
|
+
raise NotImplementedError
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
async def refund(order_id: str, reason: str | None) -> None:
|
|
30
|
+
raise NotImplementedError
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.21"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "fazercards-python-sdk"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Typed Python client for the FazerCards reseller API: game top-ups, gift cards, Telegram Stars and Premium, Steam wallet. Sync and async."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
license-files = ["LICENSE"]
|
|
12
|
+
requires-python = ">=3.10"
|
|
13
|
+
authors = [{ name = "qiyascc", email = "qiyasturk@gmail.com" }]
|
|
14
|
+
keywords = ["fazercards", "gift cards", "top-up", "reseller", "telegram stars", "steam", "sdk", "api"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 4 - Beta",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"Operating System :: OS Independent",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.10",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
"Programming Language :: Python :: 3.13",
|
|
24
|
+
"Topic :: Office/Business :: Financial",
|
|
25
|
+
"Typing :: Typed",
|
|
26
|
+
]
|
|
27
|
+
dependencies = ["httpx>=0.24,<1", "goodconf>=7.1", "pydantic>=2.7", "PyYAML>=6"]
|
|
28
|
+
|
|
29
|
+
[project.optional-dependencies]
|
|
30
|
+
toml = ["goodconf[toml]>=7.1"]
|
|
31
|
+
yaml = ["goodconf[yaml]>=7.1"]
|
|
32
|
+
dev = ["fazercards-python-sdk[toml,yaml]", "pytest>=7", "pytest-asyncio>=0.21", "ruff>=0.4", "mypy>=1.8", "types-PyYAML"]
|
|
33
|
+
|
|
34
|
+
[project.urls]
|
|
35
|
+
Homepage = "https://theqiyas.cc/projects/fazercards-sdk/"
|
|
36
|
+
Source = "https://github.com/qiyascc/fazercards-python-sdk"
|
|
37
|
+
Author = "https://theqiyas.cc"
|
|
38
|
+
Issues = "https://github.com/qiyascc/fazercards-python-sdk/issues"
|
|
39
|
+
Changelog = "https://github.com/qiyascc/fazercards-python-sdk/blob/main/CHANGELOG.md"
|
|
40
|
+
|
|
41
|
+
[tool.hatch.version]
|
|
42
|
+
path = "src/fazercards_sdk/_version.py"
|
|
43
|
+
|
|
44
|
+
[tool.hatch.build.targets.wheel]
|
|
45
|
+
packages = ["src/fazercards_sdk"]
|
|
46
|
+
|
|
47
|
+
[tool.hatch.build.targets.sdist]
|
|
48
|
+
include = ["src/fazercards_sdk", "tests", "examples", "README.md", "CHANGELOG.md", "LICENSE"]
|
|
49
|
+
|
|
50
|
+
[tool.pytest.ini_options]
|
|
51
|
+
testpaths = ["tests"]
|
|
52
|
+
asyncio_mode = "auto"
|
|
53
|
+
asyncio_default_fixture_loop_scope = "function"
|
|
54
|
+
|
|
55
|
+
[tool.ruff]
|
|
56
|
+
line-length = 120
|
|
57
|
+
target-version = "py310"
|
|
58
|
+
|
|
59
|
+
[tool.ruff.lint]
|
|
60
|
+
select = ["E", "F", "I", "B", "UP"]
|
|
61
|
+
ignore = ["E731", "UP006", "UP007", "UP035", "UP045"]
|
|
62
|
+
|
|
63
|
+
[tool.mypy]
|
|
64
|
+
python_version = "3.10"
|
|
65
|
+
plugins = ["pydantic.mypy"]
|
|
66
|
+
warn_unused_ignores = true
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
from . import messages, webhooks
|
|
2
|
+
from ._version import __version__
|
|
3
|
+
from .client import AsyncFazerCards, FazerCards
|
|
4
|
+
from .config import Endpoints, Settings, load_settings
|
|
5
|
+
from .enums import PREMIUM_MONTHS, OrderKind, OrderStatus, SteamCurrency
|
|
6
|
+
from .errors import (
|
|
7
|
+
APIError,
|
|
8
|
+
AuthenticationError,
|
|
9
|
+
BadRequestError,
|
|
10
|
+
ConfigurationError,
|
|
11
|
+
ConflictError,
|
|
12
|
+
FazerCardsError,
|
|
13
|
+
InvalidRequestError,
|
|
14
|
+
KeyCollisionError,
|
|
15
|
+
NotFoundError,
|
|
16
|
+
NotSentError,
|
|
17
|
+
OutcomeUnknownError,
|
|
18
|
+
RateLimitError,
|
|
19
|
+
ServerError,
|
|
20
|
+
SignatureError,
|
|
21
|
+
TransportError,
|
|
22
|
+
WaitTimeoutError,
|
|
23
|
+
)
|
|
24
|
+
from .models import (
|
|
25
|
+
Account,
|
|
26
|
+
Balance,
|
|
27
|
+
Category,
|
|
28
|
+
CategoryPage,
|
|
29
|
+
InputField,
|
|
30
|
+
Offer,
|
|
31
|
+
Offers,
|
|
32
|
+
Order,
|
|
33
|
+
OrderPage,
|
|
34
|
+
PlayerCheck,
|
|
35
|
+
PremiumPlan,
|
|
36
|
+
PremiumQuote,
|
|
37
|
+
RateLimit,
|
|
38
|
+
StarsQuote,
|
|
39
|
+
StatusChange,
|
|
40
|
+
SteamLogin,
|
|
41
|
+
SteamRates,
|
|
42
|
+
ValidationGame,
|
|
43
|
+
Webhook,
|
|
44
|
+
WebhookEvent,
|
|
45
|
+
card_code,
|
|
46
|
+
)
|
|
47
|
+
from .webhooks import SIGNATURE_HEADER, parse_webhook, verify_webhook
|
|
48
|
+
|
|
49
|
+
__all__ = [
|
|
50
|
+
"__version__",
|
|
51
|
+
"FazerCards",
|
|
52
|
+
"AsyncFazerCards",
|
|
53
|
+
"Settings",
|
|
54
|
+
"Endpoints",
|
|
55
|
+
"load_settings",
|
|
56
|
+
"messages",
|
|
57
|
+
"webhooks",
|
|
58
|
+
"verify_webhook",
|
|
59
|
+
"parse_webhook",
|
|
60
|
+
"SIGNATURE_HEADER",
|
|
61
|
+
"OrderStatus",
|
|
62
|
+
"OrderKind",
|
|
63
|
+
"SteamCurrency",
|
|
64
|
+
"PREMIUM_MONTHS",
|
|
65
|
+
"Account",
|
|
66
|
+
"Balance",
|
|
67
|
+
"Category",
|
|
68
|
+
"CategoryPage",
|
|
69
|
+
"InputField",
|
|
70
|
+
"Offer",
|
|
71
|
+
"Offers",
|
|
72
|
+
"Order",
|
|
73
|
+
"OrderPage",
|
|
74
|
+
"PlayerCheck",
|
|
75
|
+
"PremiumPlan",
|
|
76
|
+
"PremiumQuote",
|
|
77
|
+
"RateLimit",
|
|
78
|
+
"StarsQuote",
|
|
79
|
+
"StatusChange",
|
|
80
|
+
"SteamLogin",
|
|
81
|
+
"SteamRates",
|
|
82
|
+
"ValidationGame",
|
|
83
|
+
"Webhook",
|
|
84
|
+
"WebhookEvent",
|
|
85
|
+
"card_code",
|
|
86
|
+
"FazerCardsError",
|
|
87
|
+
"ConfigurationError",
|
|
88
|
+
"InvalidRequestError",
|
|
89
|
+
"SignatureError",
|
|
90
|
+
"TransportError",
|
|
91
|
+
"NotSentError",
|
|
92
|
+
"OutcomeUnknownError",
|
|
93
|
+
"APIError",
|
|
94
|
+
"BadRequestError",
|
|
95
|
+
"AuthenticationError",
|
|
96
|
+
"NotFoundError",
|
|
97
|
+
"ConflictError",
|
|
98
|
+
"RateLimitError",
|
|
99
|
+
"ServerError",
|
|
100
|
+
"KeyCollisionError",
|
|
101
|
+
"WaitTimeoutError",
|
|
102
|
+
]
|