Louis Innovations has released an open source Shopify integration template that enables Qatar-based merchants to accept SADAD payments directly within their Shopify stores. This Node.js custom payment app connects Shopify's checkout system to the SADAD Payment Gateway, supporting both card payments and QPay, the national Qatar mobile payment scheme.
Building the Custom Payment App
Shopify does not include SADAD as a built-in payment provider, so integration requires a custom payment app built with Shopify's Payment Extensibility framework. The app acts as a bridge between Shopify's checkout and the SADAD API:
const express = require('express');
const { Shopify } = require('@shopify/shopify-api');
const SadadClient = require('@louisinnovations/sadad-node');
const app = express();
const sadad = new SadadClient({
merchantId: process.env.SADAD_MERCHANT_ID,
apiKey: process.env.SADAD_API_KEY,
environment: process.env.SADAD_ENV || 'sandbox',
});
app.post('/payments/initiate', async (req, res) => {
try {
const { amount, currency, orderId, returnUrl } = req.body;
const transaction = await sadad.createTransaction({
amount,
currency: currency || 'QAR',
orderId,
callbackUrl: `${process.env.APP_URL}/payments/callback`,
customerId: req.body.customer.email,
});
res.json({ redirectUrl: transaction.paymentUrl, transactionId: transaction.id });
} catch (error) {
res.status(500).json({ error: error.message });
}
});
Webhook Handling for Payment Callbacks
The SADAD gateway sends asynchronous payment notifications to a callback URL configured in the merchant dashboard. Your Shopify app must handle these webhooks to update order status:
app.post('/payments/callback', async (req, res) => {
const { transactionId, status, signature } = req.body;
if (!sadad.verifySignature(signature, req.body)) {
return res.status(403).json({ error: 'Invalid signature' });
}
const shopifyClient = new Shopify.Clients.Rest(
req.session.shop,
process.env.SHOPIFY_ACCESS_TOKEN
);
if (status === 'SUCCESS') {
await shopifyClient.post({
path: `orders/${req.body.orderId}/transactions`,
data: {
transaction: {
kind: 'sale',
status: 'success',
gateway: 'sadad',
amount: req.body.amount,
},
},
});
}
res.status(200).send('OK');
});
App Configuration and Environment Variables
The integration template reads configuration from environment variables. Create a .env file in the app root directory with the following values:
PORT=3000
SADAD_MERCHANT_ID=your_merchant_id_here
SADAD_API_KEY=your_api_key_here
SADAD_ENV=sandbox
SHOPIFY_API_KEY=your_shopify_api_key
SHOPIFY_API_SECRET=your_shopify_api_secret
SHOPIFY_ACCESS_TOKEN=your_shopify_access_token
APP_URL=https://your-app-domain.com
The SHOPIFY_ACCESS_TOKEN is generated when you install the app on your store. For development, use a private app token from your Shopify admin under Settings > Apps > Develop apps.
Order Fulfillment Flow
Once a payment is confirmed, the Shopify order transitions to the fulfillment_pending state. The app should automatically trigger fulfillment for digital goods or send a notification for manual fulfillment of physical products.
Implement a fulfillment webhook handler in the app:
app.post('/fulfillment/auto', async (req, res) => {
const { orderId, lineItems } = req.body;
const digitalItems = lineItems.filter(item => item.requires_shipping === false);
if (digitalItems.length > 0) {
// Fulfill digital items automatically
await shopifyClient.post({
path: `orders/${orderId}/fulfillments`,
data: {
fulfillment: {
location_id: process.env.SHOPIFY_LOCATION_ID,
tracking_numbers: [],
line_items: digitalItems,
},
},
});
}
res.status(200).send('OK');
});
For cash-on-delivery orders, the flow differs: the order is created in pending state and only marked as paid once the COD amount is confirmed as collected. A separate admin endpoint allows store staff to mark COD orders as paid after confirming collection.
QPay Integration
QPay is the national mobile payment scheme in Qatar, operated by Qatar Central Bank alongside QNB. The integration template supports QPay transactions without additional configuration. When a customer selects QPay at checkout, the app generates a QPay payment request through the SADAD API:
app.post('/qpay/generate', async (req, res) => {
const qrPayload = await sadad.createQPayRequest({
amount: req.body.amount,
merchantId: process.env.SADAD_MERCHANT_ID,
orderId: req.body.orderId,
expiryMinutes: 15,
});
res.json({ qrCode: qrPayload.qrBase64, expiresAt: qrPayload.expiry });
});
The customer scans the generated QR code using any QPay-enabled banking app (QNB, Doha Bank, Commercial Bank, or Qatar Islamic Bank mobile apps all support QPay scanning). The app polls the SADAD API for payment confirmation and completes the order when the QR code is scanned and paid.
Testing in Sandbox
Before going live, test the integration using the SADAD sandbox environment. Create a test merchant account through QNB's developer portal. Use the following test card numbers:
| Card Type | Card Number | Result |
|---|---|---|
| QNB Credit | 4921 8112 3456 7890 | Success |
| QNB Debit | 4921 8112 3456 7891 | Success |
| Insufficient Funds | 4921 8112 3456 7899 | Failed |
| Expired Card | 4921 8112 3456 7888 | Declined |
Configure the sandbox endpoint in your app by setting the environment variable SADAD_ENV=sandbox. All sandbox transactions reset every 24 hours, making it easy to run repeated test cycles during development. For QPay testing, the sandbox environment generates a mock QR code that simulates a successful scan after 10 seconds.
Deployment to Shopify
Deploy the app to any Node.js hosting platform (Heroku, DigitalOcean, or a Qatar-based provider like Ooredoo Cloud or MEEZA). The app requires Node.js 18 or later and an HTTPS endpoint with a valid SSL certificate for production use.
Register the app in your Shopify Partners dashboard, set the app URL and callback URLs, then install it on your store. The app will appear as a custom payment method in your Shopify admin under Settings > Payments > Alternative Payment Methods. From there, you can activate it for your checkout.
For stores that need both SADAD and QPay, the integration template supports both payment methods with a single app installation. Customers select their preferred method at checkout, and the app routes the payment request to the appropriate SADAD API endpoint. The template also supports a unified checkout page that shows both options with clear pricing and estimated processing times.
Frequently Asked Questions
Q: Can I use SADAD with Shopify Payments?
No. Shopify Payments only supports banks in select countries, and Qatar is not currently included. The custom payment app approach is the recommended method for SADAD integration on Shopify.
Q: Does the integration support multi-currency?
Yes. The SADAD gateway processes transactions in QAR by default but supports SAR, AED, and USD for cross-border transactions. You can configure accepted currencies in the app configuration.
Q: How are refunds handled?
Refunds are processed through the Shopify admin. When you issue a refund on an order, the app sends a refund request to the SADAD API. Full and partial refunds are supported. Refunded amounts typically appear in the customers account within 3-5 business days.
Q: What happens if the SADAD gateway is down during checkout?
The app includes a failover mechanism. If the SADAD API does not respond within the configured timeout (default 15 seconds), the checkout will display an error message prompting the customer to try again. For high-availability setups, consider implementing a secondary payment method fallback. Our enterprise software services can help architect a resilient payment infrastructure.

