Skip to main content

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 your success_url: use the signed IPN, or call payment_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 older signature field (uppercase HMAC of data.amount + identifier) otherwise. Pass require_v2=True to refuse the older one. Django: request.body and request.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) and verify_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).
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)

Source distribution for dzwallet 0.2.0
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)

Table of built distributions (wheels) for dzwallet 0.2.0
File Interpreter ABI Platform Actions
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.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page