CLOUD
Guides

SADAD Payment Integration: Complete Technical Guide

CLOUD|March 1, 2026|4 min read|776 views
SADAD Payment Integration: Complete Technical Guide

SADAD = Saudi payment system. Integration requires Saudi server + agreement + security certificates. We explain the complete steps.

SADAD — Background and Importance

SADAD is Saudi Arabia's electronic bill payment system, operated by SADAD Electronic Services Company in partnership with the Saudi Central Bank (SAMA). It enables customers to pay company bills through all Saudi payment channels: online banking, ATMs, payment apps, and MadaPay.

Why does SADAD matter for your business?

  • 30+ Saudi banks are connected to the system
  • 90%+ of Saudis use digital banking services
  • Customers pay without needing a credit card or special account
  • Payment is immediate and confirmation is instant for your system

How SADAD Works

SADAD payment flow:
┌──────────────────────────────────────────────┐
│ 1. Your company creates a bill in SADAD │
│ and receives a Bill Number (Biller Code) │
│ ↓ │
│ 2. Customer opens their banking app │
│ → Bill Payments → SADAD → your company │
│ → enters bill number │
│ ↓ │
│ 3. SADAD queries your API for bill details │
│ (Bill Inquiry Request) │
│ ↓ │
│ 4. Customer confirms payment │
│ ↓ │
│ 5. SADAD sends payment notification to your │
│ system (Bill Payment Advice) │
│ ↓ │
│ 6. Your system acknowledges and activates │
│ the service │
└──────────────────────────────────────────────┘

Registration Requirements

To register as a Biller in SADAD:
- Valid Saudi commercial registration
- Saudi IBAN for transfers
- Saudi server (documented IP)
- API endpoint available 24/7
- Valid SSL/TLS
- Agreement with a SADAD Service Provider
 or directly with SADAD for large companies

Technical Integration with Laravel

Configuration

// config/sadad.php
return [
 'biller_code' => env('SADAD_BILLER_CODE'),
 'merchant_id' => env('SADAD_MERCHANT_ID'),
 'secret_key' => env('SADAD_SECRET_KEY'),
 'base_url' => env('SADAD_BASE_URL'),
];

Bill Inquiry Endpoint

// This endpoint is called by SADAD when a customer wants to pay a bill
class SadadController extends Controller
{
 public function inquiry(Request $request): JsonResponse
 {
 if (!$this->validateRequest($request)) {
 return response->json(['status' => '31', 'message' => 'Invalid signature'], 401);
 }

 $billNumber = $request->input('billNumber');
 $invoice = Invoice::where('sadad_bill_number', $billNumber)->first;

 if (!$invoice) {
 return response->json(['status' => '17', 'message' => 'Bill not found']);
 }

 if ($invoice->isPaid) {
 return response->json(['status' => '16', 'message' => 'Bill already paid']);
 }

 return response->json([
 'status' => '0',
 'billNumber' => $billNumber,
 'amount' => $invoice->total / 100,
 'currency' => 'SAR',
 'dueDate' => $invoice->due_at->format('Y-m-d'),
 'billInfo' => [
 'customerName' => $invoice->user->name,
 'serviceType' => $invoice->service_type,
 ],
 ]);
 }

 public function paymentAdvice(Request $request): JsonResponse
 {
 if (!$this->validateRequest($request)) {
 return response->json(['status' => '31'], 401);
 }

 $billNumber = $request->input('billNumber');
 $transactionId = $request->input('transactionId');
 $amount = $request->input('amount');

 $invoice = Invoice::where('sadad_bill_number', $billNumber)->firstOrFail;

 DB::transaction(function use ($invoice, $transactionId, $amount) {
 $invoice->markAsPaid;

 Payment::create([
 'invoice_id' => $invoice->id,
 'gateway' => 'sadad',
 'transaction_id' => $transactionId,
 'amount' => $amount * 100,
 'currency' => 'SAR',
 'status' => 'completed',
 ]);
 });

 ProvisioningService::handlePaymentCompleted($invoice);

 return response->json([
 'status' => '0',
 'billNumber' => $billNumber,
 'transactionId' => $transactionId,
 ]);
 }

 private function validateRequest(Request $request): bool
 {
 $signature = $request->header('X-SADAD-Signature');
 $expected = hash_hmac('sha256', $request->getContent, config('sadad.secret_key'));
 return hash_equals($expected, $signature ?? '');
 }
}

Routes

// routes/api.php
Route::prefix('sadad')->group(function {
 Route::post('/inquiry', [SadadController::class, 'inquiry'])
 ->middleware('throttle:100,1');
 Route::post('/payment', [SadadController::class, 'paymentAdvice'])
 ->middleware('throttle:100,1');
});

Testing the Integration

# Simulate an Inquiry request from SADAD
SIGNATURE=$(echo -n '{"billNumber":"12345678"}' | openssl dgst -sha256 -hmac "your_secret" -binary | base64)

curl -X POST https://yoursite.com/api/sadad/inquiry \
 -H "Content-Type: application/json" \
 -H "X-SADAD-Signature: $SIGNATURE" \
 -d '{"billNumber":"12345678"}'

Customer Notifications on Payment

class SadadPaymentReceived extends Notification
{
 public function toMail(object $notifiable): MailMessage
 {
 return (new MailMessage)
 ->subject('Payment Received — cloud.net.sa')
 ->line("Payment of {$this->amount} SAR received via SADAD")
 ->line("Invoice ID: {$this->invoice->id}")
 ->action('View Invoice', route('portal.invoices.show', $this->invoice->id));
 }
}

On cloud.net.sa Servers

cloud.net.sa servers in Riyadh and Jeddah are ideal for hosting SADAD integration APIs:

  • Static Saudi IP (primary requirement for SADAD)
  • uptime عالي مع ضمان تعاقدي — SADAD expects near-complete availability
  • Latency very low latency to Saudi banks
  • Free SSL and daily backup included

Summary

SADAD opens a payment channel trusted by 30+ million Saudis. The integration is technically straightforward: one endpoint for inquiry and one for payment notification. The primary requirement is a Saudi server and static IP. Meet these two conditions and the integration becomes simple code.


Get Started

Ready to build your project? Configure your cloud server with the specs your app needs, or view plans and pricing.

Share