dzwallet (Python)
Python SDK for the DzWallet payment API: accept CIB and Edahabia cards (through SATIM), create payment links and QR codes, check payment status and verify the signed payment notification (IPN). No dependencies (standard library only). Python 3.8+.
Money is only received when the status is
success. Never mark an order as paid because the customer reached yoursuccess_url: use the signed IPN, or callpayment_status()from your server. The secret key is for servers only. Never put it in a browser or mobile app.
Install
pip install dzwallet
Configure
from dzwallet import DzWallet
dz = DzWallet(
mode="test", # test | live
public_key="...", # TEST key in test mode, LIVE key in live mode
secret_key="...", # server side only
)
Keys: merchant dashboard, Setting > API Key. Live keys exist after identity (KYC) verification. Register your domain in Your website domains: ipn_url, success_url and cancel_url must belong to it.
Take a payment
identifier = DzWallet.new_identifier("ORD") # max 20 characters, save with your order first
redirect_url, _ = dz.initiate_payment(
identifier=identifier, amount=2500, details="Order #1042", site_name="My shop",
ipn_url="https://yourshop.dz/dzwallet/ipn",
success_url="https://yourshop.dz/thanks",
cancel_url="https://yourshop.dz/cart",
customer=dict(first_name="Amine", last_name="B.", email="amine@example.dz", mobile="0555000000"),
)
# redirect the customer to redirect_url (test mode: simulated page. Live mode: SATIM card page)
Receive the notification (IPN)
Every notification is signed on the exact raw body (header X-DzWallet-Signature: t=<unix>,v1=<hex>, v1 = HMAC-SHA256(secret, t + "." + raw body)), with a signed timestamp and a unique event id. Use the raw bytes, not a parsed form:
from dzwallet import InvalidSignatureError
# Flask
@app.post("/dzwallet/ipn")
def ipn_view():
try:
ipn, event_id = dz.verify_notification(request.get_data(), request.headers) # signature + 5 minute window
except InvalidSignatureError:
abort(403)
if already_processed(event_id): # unique constraint on event id
return "OK", 200
order = find_order(ipn.identifier) # your own code
if abs(float(ipn.amount) - float(order.total)) > 0.005: # amount must match YOUR order
abort(400)
if ipn.is_success and not order.paid: # fulfil once
order.mark_paid(ipn.trx)
record_event(event_id)
return "OK", 200 # answer quickly with 2xx
verify_notification(raw_body, headers, tolerance=300, require_v2=False)uses the v2 signature when DzWallet sends it and falls back to the oldersignaturefield (uppercase HMAC ofdata.amount + identifier) otherwise. Passrequire_v2=Trueto refuse the older one. Django:request.bodyandrequest.headers.- The event id is read from the signed body, so a replayed request cannot dodge the duplicate check by changing a header. It is the same on every retry and on manual redelivery.
- Lower level:
verify_webhook(raw_body, header)returns{"timestamp": ..., "event_id": ...};parse_ipn_form(text)andverify_ipn(dict)still work. - Retries: DzWallet retries unsuccessful deliveries with increasing delays for about 12 hours. Failed deliveries can be resent from the merchant dashboard (Webhook Log).
Status and payment links
s = dz.payment_status(identifier) # s.is_paid, s.status, s.trx
link = dz.create_payment_link(2500, reference="ORD1042", days=7, note="Room 12") # live keys
dz.payment_link("ORD1042")
dz.reactivate_payment_link("ORD1042")
Errors
ConfigurationError (key missing), ValueError (bad parameter, nothing sent), ApiError (.errors, .http_status, .is_rate_limited), ConnectionFailed (timeout: after initiate_payment the result is unknown, call payment_status() first), InvalidSignatureError. All except ValueError extend DzWalletError.
initiate_payment is never retried automatically; status and link reads retry on connection errors. Limit: 60 requests per minute per IP.
Tests
python -m unittest discover -s tests
They use a fake transport and never call DzWallet.
MIT licence.
Metadata
Release files for dzwallet 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| dzwallet-0.2.0.tar.gz | 12.1 kB | Without provenance | Details for dzwallet-0.2.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| dzwallet-0.2.0-py3-none-any.whl | Python 3 | none | any | Details for dzwallet-0.2.0-py3-none-any.whl |
Total release size: 22.0 kB
Release files / dzwallet-0.2.0.tar.gz
| Download URL | dzwallet-0.2.0.tar.gz |
|---|---|
| Size | 12.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
7747ae6b373475dfd64e75ec1ed1b29b0cba7b1128511180dc546e052339b992
|
|
BLAKE2b-256 checksum How to use checksums |
8a067cdef7e075932cf66f8c1f0b0cec8d1fc1a48f52ef21a0f723617524a686
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.10
|
Release files / dzwallet-0.2.0-py3-none-any.whl
| Download URL | dzwallet-0.2.0-py3-none-any.whl |
|---|---|
| Size | 9.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
87702dbc782b1440db42f8908c54a351fc68cbd56e845fff646c92b69322a538
|
|
BLAKE2b-256 checksum How to use checksums |
612e01f290697e9c1fffc95b64ca20b2dd6b35697a9da30bc14a703c50fbc4fb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.10
|
Security for dzwallet 0.2.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.