Developer Introduction

Welcome to the SarfiPay Developer Portal. SarfiPay represents the premium financial operating layer designed explicitly for scale, developer speed, and bulletproof security in the Algerian digital market.

Our REST APIs allow you to interact with double-entry ledgers, manage multi-currency user wallets (primary DZD), process secure e-commerce gateway transactions, execute automated payouts, and receive immediate event alerts through custom webhook channels.

API Environments:
- Sandbox Base URL: https://api.sarfipay.com/v1 (Set sandbox headers or test keys)
- Production Base URL: https://api.sarfipay.com/v1

Create a Merchant Account

Before you can write active API code, you need a verified Merchant profile. The setup takes less than two minutes:

  1. Navigate to the SarfiPay Signup Page.
  2. Select the Merchant option during signup.
  3. Verify your business email address using the secure code sent to your inbox.
  4. Enable Multi-Factor Authentication (MFA) via Google Authenticator in your profile settings for high-value operations.

Once logged in, you will be in Sandbox mode automatically, letting you play and simulate integration features with zero credit risks.

Applications

SarfiPay structures business operations via the concept of Applications. An Application represents a distinct payment environment—for instance, a specific mobile app, an e-commerce website, or an in-store physical POS terminal.

Within your dashboard, you can provision separate applications, allowing you to:

  • Isolate transaction reporting and logs for different stores.
  • Create unique API credentials for each interface.
  • Configure dedicated webhook endpoints depending on checkout locations.

Test and Live Modes

The SarfiPay portal has a strict separation between Test (Sandbox) and Live environments. This structure guarantees that code changes and simulations never impact real money or live compliance rails.

Safety First: Data is completely partitioned. A user profile, ledger balance, or API key created in Test mode does not exist and is entirely invalid in Live mode.
Feature Test (Sandbox) Mode Live Mode
Base URL https://api.sarfipay.com/v1/... https://api.sarfipay.com/v1/...
Key Prefixes sk_test_... / pk_test_... sk_live_... / pk_live_...
Ledger Balances Simulated, infinite refill Real Algerian DZD funds
CCP Recharge Simulated cards & vouchers Real Algérie Poste vouchers

API Keys

Your API requests are authorized using secret key pairs. Every application receives two key pairs:

  • Public Key (pk_...): Suitable for client-side JavaScript or mobile clients. It only grants authorization to initiate checkout links.
  • Secret Key (sk_...): Intended solely for secure backend servers. It grants access to execute transfers, sign payouts, and manage wallets. Never expose this key in frontend code!

To authenticate requests, pass your secret key in the request headers:

curl -X GET https://api.sarfipay.com/v1/auth/profile \
  -H "Authorization: Bearer sk_test_51MzZ7xSrfyP"
const fetch = require('node-fetch');

async function getProfile() {
  const response = await fetch('https://api.sarfipay.com/v1/auth/profile', {
    method: 'GET',
    headers: {
      'Authorization': 'Bearer sk_test_51MzZ7xSrfyP',
      'Content-Type': 'application/json'
    }
  });
  const profile = await response.json();
  console.log(profile);
}
import requests

headers = {
    "Authorization": "Bearer sk_test_51MzZ7xSrfyP",
    "Content-Type": "application/json"
}

response = requests.get("https://api.sarfipay.com/v1/auth/profile", headers=headers)
profile = response.json()
print(profile)

Account Verification (KYC)

To comply with the Bank of Algeria regulations and anti-money laundering policies, all merchants must undergo identity verification. Our automated KYC (Know Your Customer) framework splits limits into tiers:

Tier Level Requirements Single Transaction Limit Monthly Volume Limit
Tier 1 (Instant) Email, verified Phone number, and full name. 10,000 DZD 50,000 DZD
Tier 2 (Basic) National Identity Card (NID/Biometric passport) + Selfie verification. 100,000 DZD 500,000 DZD
Tier 3 (Enterprise) Commercial Registry (Registre du Commerce), Tax ID (NIF), and bank rib statement. Unlimited Unlimited

Webhooks System

Webhooks are absolute requirements for premium application scaling. Instead of continuously polling our APIs to check if a user finished a transaction, SarfiPay sends an HTTP POST event payload directly to your server as soon as the state transitions.

Supported Webhook Events

Event Name Trigger Point
payment.succeeded A customer successfully completes a checkout link payment. Balance is debited in customer's wallet and credited to merchant's.
payment.failed An attempted charge is declined due to lack of funds, system risk block, or timeouts.
recharge.completed A customer's wallet recharge via CCP or CIB bank card clears successfully.
dispute.created A customer files a transaction challenge, immediately moving the equivalent amount to a frozen escrow account.

System Fees & Commissions

SarfiPay offers a highly transparent, competitive pricing matrix tailored to empower local growth without hidden costs:

  • Wallet-to-Wallet (P2P) transfers: Always 100% Free. No fees for peer transactions.
  • E-commerce Payment Checkout: 1.5% commission per completed invoice.
  • Edahabia / CCP Recharge: Flat 15 DZD fee per successful recharge.
  • Bank Settlement Payouts: Flat 50 DZD transfer fee to local bank RIB accounts.

Start The Integration

SarfiPay's integration engine is designed for flexibility. Whether you want to configure a fast checkout redirect for your shop, or build a complex, deep in-app native wallet transaction ledger, we support your architectural roadmap.

Our team provides official software libraries in various backend technologies, enabling fast integrations with robust transaction validation.

Before You Start

Please complete this checklist before writing checkout scripts:

  • Retrieve your sandbox secret API key (sk_test_...) from the applications settings page.
  • Ensure your development server supports HTTPS (even with localhost self-signed certificates or services like ngrok) to enable testing webhook deliveries locally.
  • Set up your database migrations to store SarfiPay transaction reference IDs securely.

The Quick Guide (Checkout Redirect)

If you want to accept payments quickly, use the simple redirect flow. Your backend initiates a payment request, receiving a secure checkout URL to redirect the customer to. Once paid, the customer returns to your success page.

1. Initiate Payment Request

POST /api/v1/merchants/payment-requests
curl -X POST https://api.sarfipay.com/v1/merchants/payment-requests \
  -H "Authorization: Bearer sk_test_51MzZ7xSrfyP" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 2500,
    "currency": "DZD",
    "description": "Order #4912 - Premium Subscription",
    "returnUrl": "https://yourshop.com/checkout/success"
  }'
const fetch = require('node-fetch');

async function initCheckout() {
  const response = await fetch('https://api.sarfipay.com/v1/merchants/payment-requests', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer sk_test_51MzZ7xSrfyP',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      amount: 2500,
      currency: 'DZD',
      description: 'Order #4912 - Premium Subscription',
      returnUrl: 'https://yourshop.com/checkout/success'
    })
  });
  const session = await response.json();
  console.log("Redirect user to:", session.checkoutUrl);
}
import requests

payload = {
    "amount": 2500,
    "currency": "DZD",
    "description": "Order #4912 - Premium Subscription",
    "returnUrl": "https://yourshop.com/checkout/success"
}

response = requests.post(
    "https://api.sarfipay.com/v1/merchants/payment-requests",
    json=payload,
    headers={"Authorization": "Bearer sk_test_51MzZ7xSrfyP"}
)
session = response.json()
print("Redirect user to:", session["checkoutUrl"])

JSON Response Payload

{
  "status": "success",
  "paymentRequestId": "pr_9Fj8aK29mQ1s",
  "checkoutUrl": "https://pay.sarfipay.com/pay/pr_9Fj8aK29mQ1s",
  "amount": 2500,
  "currency": "DZD"
}

The Full Guide (Advanced Custom Integration)

For premium platforms, you can construct deep e-wallet checkout flows with atomic double-entry verification. This allows your app to process wallet transfers directly, verify credit limits, and check transaction ledger signatures for total immutability.

Double-Entry Wallet Transfer Execution

Use this code on your secure server to deduct funds from a customer's wallet and credit a merchant's wallet atomically, incorporating custom idempotency keys to prevent duplicate transactions:

POST /api/v1/payments/transfer
curl -X POST https://api.sarfipay.com/v1/payments/transfer \
  -H "Authorization: Bearer sk_test_51MzZ7xSrfyP" \
  -H "X-Idempotency-Key: idempotency_tx_921820" \
  -H "Content-Type: application/json" \
  -d '{
    "sourceWalletId": "w_cust_92a18f8c",
    "destinationWalletId": "w_merch_01bb8d99",
    "amount": 4200,
    "currency": "DZD",
    "description": "Custom Double-Entry In-App Purchase"
  }'
const fetch = require('node-fetch');

async function executeAtomicTransfer() {
  const response = await fetch('https://api.sarfipay.com/v1/payments/transfer', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer sk_test_51MzZ7xSrfyP',
      'X-Idempotency-Key': 'idempotency_tx_921820',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      sourceWalletId: 'w_cust_92a18f8c',
      destinationWalletId: 'w_merch_01bb8d99',
      amount: 4200,
      currency: 'DZD',
      description: 'Custom Double-Entry In-App Purchase'
    })
  });
  const transferResult = await response.json();
  console.log("Transfer complete:", transferResult);
}
import requests

headers = {
    "Authorization": "Bearer sk_test_51MzZ7xSrfyP",
    "X-Idempotency-Key": "idempotency_tx_921820",
    "Content-Type": "application/json"
}

payload = {
    "sourceWalletId": "w_cust_92a18f8c",
    "destinationWalletId": "w_merch_01bb8d99",
    "amount": 4200,
    "currency": "DZD",
    "description": "Custom Double-Entry In-App Purchase"
}

response = requests.post(
    "https://api.sarfipay.com/v1/payments/transfer",
    json=payload,
    headers=headers
)
print(response.json())

Handle Webhooks (Signature Verification)

Security is paramount. When your endpoint receives a POST request from our webhook system, you **MUST** verify that the payload is authentic and was sent by SarfiPay. Every application has a unique **Webhook Signing Secret** (visible in your dashboard settings).

SarfiPay includes an X-SarfiPay-Signature header in every webhook POST request. This signature is generated by hashing the raw body JSON with your Webhook Signing Secret using the **HMAC SHA-256** algorithm.

Webhook Signature Verification Code

const express = require('express');
const crypto = require('crypto');
const app = express();

// Webhook Secret from SarfiPay Dashboard
const WEBHOOK_SECRET = 'whsec_yOuR_sIgNiNg_SeCrEt_Key';

// NOTE: Verify using the RAW request body bytes!
app.post('/webhooks', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['x-sarfipay-signature'];
  
  const hmac = crypto.createHmac('sha256', WEBHOOK_SECRET);
  hmac.update(req.body);
  const calculatedSignature = hmac.digest('hex');

  if (signature === calculatedSignature) {
    // Payload is authenticated! Save event.
    const event = JSON.parse(req.body.toString());
    console.log("Verified Event Type:", event.type);
    
    if (event.type === 'payment.succeeded') {
      // Provision premium products immediately.
    }
    
    res.status(200).json({ received: true });
  } else {
    console.warn("Signature verification failed!");
    res.status(400).send("Signature mismatch");
  }
});
from flask import Flask, request, jsonify
import hmac
import hashlib

app = Flask(__name__)

WEBHOOK_SECRET = b"whsec_yOuR_sIgNiNg_SeCrEt_Key"

@app.route("/webhooks", methods=["POST"])
def verify_webhook():
    signature = request.headers.get("X-SarfiPay-Signature")
    raw_body = request.data
    
    calculated = hmac.new(
        WEBHOOK_SECRET,
        raw_body,
        hashlib.sha256
    ).hexdigest()
    
    if hmac.compare_digest(signature, calculated):
        event = request.get_json()
        print("Verified Event Type:", event["type"])
        
        # Handle business logic
        return jsonify({"received": True}), 200
    else:
        print("Signature verification failed!")
        return "Signature mismatch", 400