Api Documentation

Introduction

This section describes the Dzwallet payment gateway API.


The Dzwallet API is simple to integrate into your company's software. Our API takes cURL requests, has well-formatted URLs, and produces JSON responses.

The API can be used in test mode without affecting your real data. The request is authenticated using the API key, which also establishes whether the payment is legitimate or not. For test mode just use the sandbox URL and In case of live mode use the live URL from section Initiate Payment .

Supported Currencies

The supported currencies by Dzwallet are given below.


Currency Currency Symbol
DZD DZD

Supported Payment Gateway

Accept CIB and EDAHABIA payments in DZD through SATIM.


Gateway Name gateway_methods value Currency Description
CIB / EDAHABIA Satim DZD Algerian CIB and EDAHABIA card payments via SATIM gateway

Example - Use CIB only:

'gateway_methods[]' => 'Satim', 'currency' => 'DZD'

Get The Api Key

How to obtain the api key is explained in this part.


To access your Dzwallet merchant account, please log in. In case you don't have an account, you can click here.

Now go to the Account > Settings > API Key from the merchant panel.

The api keys can be found there which is Public key and Secret key. Use these keys to initiate the API request. Every time you can generate new API key by clicking Generate Api Key button. Remember do not share these keys with anyone.

Domains and URL Rules

Register your website before accepting payments.


Before you can initiate a payment you must register the domain(s) of your website in Profile Setting > Your website domains. Enter one domain per line, for example example.com. You can register up to 10 domains. Subdomains are accepted automatically (shop.example.com is covered by example.com).

Every one of ipn_url, success_url and cancel_url must:

  • start with http:// or https:// (https is strongly recommended);
  • belong to one of your registered domains (the host equals the domain or is a subdomain of it);
  • be publicly reachable: localhost, .local, .internal and private or reserved IP addresses are rejected;
  • not contain a username or password, and use only the ports 80, 443, 8080 or 8443;
  • be at most 500 characters long.

The same rules apply to site_logo (public http/https url, but it does not have to be on your domain).

Related error messages:

MessageMeaning / fix
No website domain is registered for this merchant. Add your domain in Profile Setting first.Register your domain in Profile Setting.
ipn_url must belong to one of your registered domainsThe url host is not one of your registered domains. The same message exists for success_url and cancel_url.
Invalid ipn_url: must be a public http(s) URLThe url is local/private, uses a blocked port, contains credentials or is not http/https.

Initiate Payment

In this section, the procedure for initiating the payment is explained.


To begin the payment process, use the sample code provided, and pay close attention to the parameters. The API endpoints mentioned below will need to be used to make the request.

Live End Point: https://dzwallet.app/payment/initiate

Test End Point: https://dzwallet.app/test/payment/initiate

Request to the end point with the following parameters below.

Parameter Type Is Required Description
public_key string (255) required Your Public API key
identifier string (20) required Identifier is basically for identify payment at your end
currency string (20) required Currency Code, Must be in Upper Case. e.g. DZD
gateway_methods[] array optional For this SATIM integration, send gateway_methods as an array containing Satim and use DZD as the currency.
amount decimal required Payment amount
details string (100) required Details of your payment or transaction
ipn_url string required Server-to-server notification url (POST). Must be a public http/https url on one of your registered domains. See Domains and URL Rules.
success_url string required Customer is sent here after a successful payment. Must be a public http/https url on one of your registered domains.
cancel_url string required Customer is sent here when the payment is cancelled or fails. Must be a public http/https url on one of your registered domains.
site_name string required Your business site name
site_logo string/url optional Public http/https url of your logo image (max 500 characters)
checkout_theme string optional Checkout form theme dark/light. Default theme is light
Customer
customer[] array required customer must be an array
customer.first_name string required Customer's first name
customer.last_name string required Customer's last name
customer.email string required Customer's valid email
customer.mobile string required Customer's valid mobile
Shipping info
shipping_info[] array optional shipping_info must be an array
shipping_info.address_one string optional Customer's address one
shipping_info.address_two string optional Customer's address two
shipping_info.area string optional Shipping area of customer
shipping_info.city string optional Shipping city of customer
shipping_info.sub_city string optional Shipping sub city of customer
shipping_info.state string optional Shipping state
shipping_info.postcode string optional Shipping postcode
shipping_info.country string optional Shipping country
shipping_info.others string optional Others info
Billing info
billing_info[] array optional billing_info must be an array
billing_info.address_one string optional Customer's address one
billing_info.address_two string optional Customer's address two
billing_info.area string optional Billing area of customer
billing_info.city string optional Billing city of customer
billing_info.sub_city string optional Billing sub city of customer
billing_info.state string optional Billing state
billing_info.postcode string optional Billing postcode
billing_info.country string optional Billing country
billing_info.others string optional Others info

Validate The Payment and IPN

This section describes the process to get your instant payment notification.


To initiate the payment follow the example code and be careful with the perameters. You will need to make request with these following API end points.

End Point: Your business application ipn url.

Request Method: POST

You will get following parameters below.

Parameter Description
status Payment success status.
identifier Identifier is basically for identify payment at your end.
signature A hash signature to verify your payment at your end.
data Data contains some basic information with charges, amount, currency, payment transaction id etc.

IPN Security and Delivery

How to trust and handle the notification.


Every notification is signed. Verify the signature on the raw request body before you trust anything in it.

Headers
HeaderDescription
X-DzWallet-Signaturet=<unix time>,v1=<hex signature>
X-DzWallet-Event-IdUnique id of the event. It is the same on every retry, so you can ignore duplicates.
X-DzWallet-TimestampUnix time of this delivery attempt.
X-DzWallet-Delivery-Attempt1 for the first delivery, then 2, 3 and so on for each retry.

The body also contains event (payment.succeeded), event_id and timestamp, next to the fields described above.

How to verify a notification
  1. Read the raw body exactly as received, before any parsing.
  2. Compute HMAC-SHA256 of the timestamp, a dot and the raw body, with your secret key (the test secret for test payments, the live secret for live payments). Compare it with v1 using a constant-time comparison.
  3. Reject the notification if its timestamp is more than 5 minutes away from your server time.
  4. Read the event_id field of the body (it is signed; do not use the header for this). If you already processed it, answer 200 and do nothing. Otherwise store it, with a unique constraint, before you fulfil the order.
  5. Check that the identifier and the amount match your own order, then fulfil the order once.
  6. Answer with a 2xx status quickly.
Delivery and retries
  • The notification is sent only after the payment gateway has confirmed the payment. It is a POST request to your ipn_url with a 15 second timeout; redirects are not followed.
  • If your endpoint does not answer with a 2xx status, we retry with increasing delays: 1, 2, 4, 8, 16 and 32 minutes, then every hour, for up to 15 attempts (about 12 hours). Every retry has a new timestamp and signature but the same event id.
  • If all attempts fail, confirm the payment with the Payment Status endpoint below.
  • Every delivery attempt (status code, time, errors) is listed in your merchant dashboard under Webhook Log, where you can also send a notification again with the Resend button. A resent notification has the same event id.
  • Never rely on the customer reaching your success_url. Redirects can be reached or faked by anyone; only the signed notification, or a call to the Payment Status endpoint, proves a payment.
  • Use a valid HTTPS certificate on your ipn_url so the notification is delivered securely.

Older signature field. The signature field in the body (uppercase HMAC-SHA256 of data.amount and identifier, with your secret key) is still sent so existing integrations keep working. It does not cover the timestamp and cannot stop replays, so use the header in new integrations.

PHP
<?php
$raw       = file_get_contents('php://input');          // exact body, before any parsing
$header    = $_SERVER['HTTP_X_DZWALLET_SIGNATURE'] ?? '';
$eventId   = $_SERVER['HTTP_X_DZWALLET_EVENT_ID'] ?? '';
$secret    = 'YOUR_SECRET_KEY';                         // test secret for test payments

parse_str(str_replace(',', '&', $header), $h);       // t=..., v1=...
$t = (int) ($h['t'] ?? 0);
$expected = hash_hmac('sha256', $t . '.' . $raw, $secret);

if (!hash_equals($expected, strtolower($h['v1'] ?? ''))) { http_response_code(400); exit; }
if (abs(time() - $t) > 300)                              { http_response_code(400); exit; }
parse_str($raw, $p);                                     // the event id comes from the signed body
$eventId = $p['event_id'] ?? '';
if (already_processed($eventId))                         { http_response_code(200); exit; }

// check $p['identifier'] and $p['data']['amount'] against your order
mark_processed($eventId);
fulfil_order($p['identifier']);
http_response_code(200);
Node.js
// Express: keep the raw body
app.post('/dzwallet/ipn', express.raw({ type: '*/*' }), (req, res) => {
  const raw = req.body.toString('utf8');
  const header = req.get('X-DzWallet-Signature') || '';
  const parts = Object.fromEntries(header.split(',').map(s => s.trim().split('=')));
  const t = parseInt(parts.t, 10);
  const expected = require('crypto').createHmac('sha256', SECRET)
    .update(t + '.' + raw).digest('hex');
  const ok = parts.v1 && parts.v1.length === expected.length &&
    require('crypto').timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected));
  if (!ok || Math.abs(Date.now() / 1000 - t) > 300) return res.sendStatus(400);
  const eventId = new URLSearchParams(raw).get('event_id');   // from the signed body, not the header
  if (alreadyProcessed(eventId)) return res.sendStatus(200);
  markProcessed(eventId);          // unique constraint on event_id
  // parse raw with new URLSearchParams(raw), check identifier and amount, fulfil once
  res.sendStatus(200);
});
Python
import hmac, hashlib, time

def verify(raw_body: bytes, header: str, secret: str, tolerance=300) -> bool:
    parts = dict(p.strip().split('=', 1) for p in header.split(','))
    t = int(parts['t'])
    expected = hmac.new(secret.encode(), (str(t) + '.').encode() + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, parts['v1'].lower()) and abs(time.time() - t) <= tolerance

# Flask: raw = request.get_data(); header = request.headers['X-DzWallet-Signature']
# Then: parse the body (urllib.parse.parse_qs), skip if its event_id was already stored, check identifier and amount, fulfil once.

The official SDKs do all of this for you: see the Developers page.

Payment Status

Check a payment at any time from your server.


End Point: https://dzwallet.app/api/payment/status   Method: POST

Use it to confirm a payment if your IPN did not arrive. A live public key returns only live payments; a test public key returns only test payments. Use a unique identifier for each order within its mode. If you reuse an identifier, the latest checkout attempt in that mode is returned.

ParameterTypeDescription
public_keystringYour public API key
identifierstringThe identifier you sent when initiating the payment

Possible payment_status values: initiated, pending, success, rejected, cancelled. Only success means the money was received. A successful response also returns identifier, amount, currency, trx and created_at. Errors return {"status":"error","message":"Invalid API key"} or "Payment not found".

Refunds

How refunds work.


  • Refunds are requested from your merchant dashboard (Refunds). There is no refund API call.
  • For CIB / EDAHABIA payments our commission (gateway charge and payment charge) is not refunded: the customer receives the payment amount minus these fees, and your balance is debited by the same refunded amount.
  • After the request is approved by our team it is processed immediately through the same gateway that took the payment: CIB / EDAHABIA payments are refunded through SATIM.
  • If the payment was already released to your balance, the refunded amount is debited from your merchant balance, so your balance must cover it. A payment can be refunded only once.
  • After a refund the payment status becomes refunded and the customer is credited by the card network according to the bank timelines.

Fees and Settlement

What you receive and when.


  • Your balance is credited with the full payment amount, then the gateway fee and service charge are deducted as separate transactions, so each one is visible in your transaction history.
  • Funds are released to your balance after the hold period configured on your account.

Rate Limits

Keep your requests reasonable.


Initiate Payment (live and test) and Payment Status are limited to 60 requests per minute per IP address. Exceeding it returns HTTP 429 Too Many Requests; wait a minute and retry. Always send API requests from your server, never from the visitor browser, so your secret key stays private.

Common Errors

Messages returned by Initiate Payment.


MessageMeaning / fix
Invalid api keyThe public_key is wrong or was regenerated.
No website domain is registered for this merchant...Add your domain in Profile Setting.
ipn_url / success_url / cancel_url must belong to one of your registered domainsUse urls on your registered domains.
Invalid ipn_url: must be a public http(s) URLUse a public http/https url without credentials.
HTTP 429Too many requests, slow down.
Initiate Payment
<?php
    $parameters = [
        'identifier' => 'DFU80XZIKS',
        'currency' => 'DZD',
        'amount' => 11.00,
        'gateway_methods' => ['Satim'],
        'details' => 'Purchase T-shirt',
        'ipn_url' => 'https://yourdomain.com/ipn_url.php',
        'cancel_url' => 'https://yourdomain.com/cancel_url.php',
        'success_url' => 'https://yourdomain.com/success_url.php',
        'public_key' => 'your_public_key',
        'site_name' => 'your_site_name',
        'site_logo' => 'https://yourdomain.com/logo.png', // optional
        'checkout_theme' => 'light',
        'customer'=>[
            'first_name'=>'John',
            'last_name'=>'Doe',
            'email'=>'joan@gmail.com',
            'mobile'=>'12345789',
        ],
        'shipping_info'=>[
            'address_one'=>'',
            'address_two'=>'',
            'area'=>'',
            'city'=>'',
            'sub_city'=>'',
            'state'=>'',
            'postcode'=>'',
            'country'=>'',
            'others'=>'',
        ],
        'billing_info'=>[
            'address_one'=>'',
            'address_two'=>'',
            'area'=>'',
            'city'=>'',
            'sub_city'=>'',
            'state'=>'',
            'postcode'=>'',
            'country'=>'',
            'others'=>'',
        ]
    ];

    $parameters = http_build_query($parameters);

    // Start in test mode. Use live keys when switching this value to live.
    $mode = 'test';
    $url = $mode === 'live'
        ? 'https://dzwallet.app/payment/initiate'
        : 'https://dzwallet.app/test/payment/initiate';

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_POSTFIELDS,  $parameters);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $result = curl_exec($ch);
    curl_close($ch);

?>
Example Responses
//Error Response.
{
    "status": "error",
    "message": [
        "Invalid api key"
    ]
}

//Success Response.
{
    "status": "success",
    "message": [
        "Payment initiated"
    ],
    "redirect_url": "https://example.com/payment/checkout?payment_trx=eyJpdiI6IkFyNllSNU1lOFdkYTlPTW52cytPNGc9PSIsInZhbHVlIjoiWWowRTRjdzZ1S1BBRm4ydS81OWR1WjdXeFIxcjE1WkZRVE9BcmZYeXpzND0iLCJtYWMiOiJjNDdhODUzYzY2NmZlZGJjZTI5ODQyMmRkYzdjYjRmM2NiNjg4M2RiMWZjN2EyMzFkODI4OWMwYjk3ZWYwNGQwIiwidGFnIjoiIn0%3D"
}
Validate The Payment and IPN
<?php
    //Receive the response parameter
    $status = $_POST['status'];
    $signature = $_POST['signature'];
    $identifier = $_POST['identifier'];
    $data = $_POST['data'];

    // Generate your signature
    $customKey = $data['amount'].$identifier;
    $secret = 'YOUR_SECRET_KEY';
    $mySignature = strtoupper(hash_hmac('sha256', $customKey , $secret));

    $myIdentifier = 'YOUR_GIVEN_IDENTIFIER';

    if($status == "success" && hash_equals($mySignature, $signature) && $identifier === $myIdentifier){
        //your operation logic
    }
?>
Payment Status
<?php
    $ch = curl_init('https://dzwallet.app/api/payment/status');
    curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
        'public_key' => 'your_public_key',
        'identifier' => 'DFU80XZIKS',
    ]));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $res = json_decode(curl_exec($ch), true);
    curl_close($ch);

    if (($res['payment_status'] ?? '') === 'success') {
        // mark the order as paid
    }
?>
Laravel SDK

Official PHP package for Laravel 10, 11 and 12. It wraps this API: card payments, payment status, payment links and signed notification (IPN) checking.

Never mark an order as paid because the customer reached your success_url. Use the signed IPN or paymentStatus() from your server, and compare the amount with your own order.
1. Install
# Recommended: from Packagist (PHP 8.1+, Laravel 10 / 11 / 12)
composer require dzwallet/laravel-sdk
php artisan vendor:publish --tag=dzwallet-config   # optional

# Source code: https://github.com/dzwallet/laravel-sdk

# Alternative without Packagist: download the zip (button above), put it in
# a folder "packages/" of your project (do not unzip) and add to composer.json:
"repositories": [
    { "type": "artifact", "url": "packages/" }
]
# then run:
composer require dzwallet/laravel-sdk:0.1.0
2. Configure
# .env  (keys: merchant dashboard > Setting > API Key)
DZWALLET_MODE=test              # test | live
DZWALLET_PUBLIC_KEY=your_public_key   # TEST key in test mode, LIVE key in live mode
DZWALLET_SECRET_KEY=your_secret_key   # server side only, never in JavaScript or a repository
DZWALLET_IPN_ENABLED=true       # optional: registers POST /dzwallet/ipn
3. Take a payment
<?php
use DzWallet\Facades\DzWallet;
use DzWallet\DzWalletClient;

$order->update(['identifier' => DzWalletClient::newIdentifier('ORD')]); // max 20 chars, unique

$payment = DzWallet::initiatePayment([
    'identifier'  => $order->identifier,
    'amount'      => $order->total,              // DZD
    'details'     => "Order #{$order->id}",
    'ipn_url'     => route('dzwallet.ipn'),
    'success_url' => route('orders.thanks', $order),
    'cancel_url'  => route('orders.cart'),
    'site_name'   => config('app.name'),
    'customer'    => [
        'first_name' => $user->first_name,
        'last_name'  => $user->last_name,
        'email'      => $user->email,
        'mobile'     => $user->phone,
    ],
]);

return redirect()->away($payment->redirectUrl);
4. Receive the notification (IPN)
<?php
use DzWallet\Events\PaymentNotificationReceived;
use Illuminate\Support\Facades\Event;

// With DZWALLET_IPN_ENABLED=true the package verifies the signature and fires this event.
Event::listen(PaymentNotificationReceived::class, function ($event) {
    $ipn   = $event->ipn;                       // signature already verified
    $order = Order::where('identifier', $ipn->identifier)->first();

    if (!$order || !$ipn->isSuccess()) return;
    if ($order->isPaid()) return;               // the same IPN can arrive twice
    if (abs((float) $ipn->amount() - (float) $order->total) > 0.005) return;

    $order->markPaid($ipn->trx());
});

// Or in your own controller (throws InvalidSignatureException when not valid):
$ipn = DzWallet::verifyIpn($request->all(), $order->identifier, $order->total);

// Or protect a route: ->middleware('dzwallet.signature')
5. Check status and payment links
<?php
// Status (from your server)
$status = DzWallet::paymentStatus($order->identifier);
$status->isPaid();    // true only for "success"
$status->status;      // initiated | pending | success | rejected | cancelled

// Payment link + QR (live keys)
$link = DzWallet::createPaymentLink(2500, [
    'reference' => 'ORD1042', 'days' => 7, 'note' => 'Room 12',
    'ipn_url'   => 'https://yourshop.dz/pay/ipn',
]);
$link->url; $link->printUrl; $link->status; $link->isOpen();

DzWallet::paymentLink('ORD1042');            // read it later
DzWallet::reactivatePaymentLink('ORD1042');  // expired or cancelled link

// Errors: DzWallet\Exceptions\* (ApiException, InvalidSignatureException, ...)

We may use cookies or any other tracking technologies when you visit our website, including any other media form, mobile website, or mobile application related or connected to help customize the Site and improve your experience. learn more

Allow