DEV Community

Cover image for How to integrate UAE payment gateways (Telr, PayTabs) into an app
Erum Qamar
Erum Qamar

Posted on

How to integrate UAE payment gateways (Telr, PayTabs) into an app

Integrating UAE Payment Gateways into Your App: A Practical Guide to Telr and PayTabs

If you're building a commercial app for the UAE market, you will eventually hit the payment gateway question.

Stripe works globally but isn't the default choice here. The UAE has its own dominant gateways — Telr and PayTabs are the two you'll encounter most — and while both have documentation, the docs leave a lot of gaps that only show up when you're actually integrating.

This is the guide I wish existed before we built our first payment flow for a UAE client.

Why not just use Stripe?

Stripe does operate in the UAE, but there are real reasons local gateways get chosen instead:

  • Local acquiring banks — Telr and PayTabs process through UAE banks, which means lower decline rates on local cards and faster settlement
  • Network International and Mashreq support — common UAE card networks that local gateways handle natively
  • Arabic interface support — important for consumer-facing checkouts
  • Compliance familiarity — local gateways understand UAE Central Bank (CBUAE) requirements out of the box
  • Client preference — many UAE businesses specifically request a local gateway in their contracts

For most UAE-market apps, you'll be integrating one of these two. Here's how they differ in practice.

Telr vs PayTabs — the practical difference

Telr

  • SDKs: iOS, Android, Web
  • Hosted payment page: yes
  • Direct API (card data): yes, requires PCI compliance
  • Recurring billing: yes
  • Multi-currency: yes
  • Sandbox: yes
  • Documentation: patchy — expect to fill gaps yourself
  • Best for: enterprise clients who specifically request it, established UAE merchants

PayTabs

  • SDKs: iOS, Android, Web, Flutter (official package)
  • Hosted payment page: yes
  • Direct API (card data): yes, requires PCI compliance
  • Recurring billing: yes
  • Multi-currency: yes
  • Sandbox: yes
  • Documentation: better structured, still has gaps
  • Best for: Flutter apps, faster integrations, Arabic checkout out of the box

The Flutter SDK is the deciding factor for most mobile projects — PayTabs has one, Telr doesn't. If your client hasn't specified a gateway, start with PayTabs.

Setting up Telr

1. Get your credentials

After creating a merchant account, you'll receive:

  • store_id — your merchant identifier
  • authkey — your API authentication key

These go in your server-side environment variables. Never expose them client-side.

2. Create a payment order (server-side)

Telr uses a hosted payment page approach for most integrations. You create an order from your backend and redirect the user to Telr's checkout.

// Node.js example
const axios = require('axios');

async function createTelrOrder({ amount, currency, orderId, customerEmail, returnUrl, cancelUrl }) {
  const payload = {
    method: 'create',
    store: process.env.TELR_STORE_ID,
    authkey: process.env.TELR_AUTH_KEY,
    order: {
      cartid: orderId,
      test: process.env.NODE_ENV === 'production' ? '0' : '1', // 1 = test mode
      amount: amount.toFixed(2),
      currency: currency || 'AED',
      description: `Order ${orderId}`,
    },
    return: {
      authorised: returnUrl,
      declined: cancelUrl,
      cancelled: cancelUrl,
    },
    customer: {
      email: customerEmail,
    },
  };

  const response = await axios.post('https://secure.telr.com/gateway/order.json', payload);
  return response.data;
}
Enter fullscreen mode Exit fullscreen mode

The response includes an order.url — redirect the user there to complete payment.

3. Handle the return

Telr redirects back to your authorised or declined URL with an order_ref parameter. You then verify the payment status server-side:

async function verifyTelrPayment(orderRef) {
  const payload = {
    method: 'check',
    store: process.env.TELR_STORE_ID,
    authkey: process.env.TELR_AUTH_KEY,
    order: {
      ref: orderRef,
    },
  };

  const response = await axios.post('https://secure.telr.com/gateway/order.json', payload);
  const status = response.data?.order?.status?.code;

  // 3 = authorised, -1 = declined, -2 = cancelled
  return {
    success: status === 3,
    status,
    data: response.data,
  };
}
Enter fullscreen mode Exit fullscreen mode

Important: Always verify server-side. Never trust a client-side redirect parameter to confirm payment.

4. Sandbox gotchas

  • Use test card 4111 1111 1111 1111 with any future expiry and CVV 123
  • Set order.test to 1 — forgetting this is the most common mistake in sandbox
  • Telr sandbox and production use the same endpoint — the test flag controls which environment processes the transaction

Setting up PayTabs

PayTabs has a cleaner API structure and better Flutter support, making it the faster integration for mobile-first apps.

1. Get your credentials

From your PayTabs merchant dashboard:

  • profile_id — your merchant profile
  • server_key — for server-side API calls
  • client_key — safe to use client-side (in their SDK)

2. Create a payment page (server-side)

// Node.js example
async function createPayTabsPayment({ amount, currency, orderId, customerName, customerEmail, returnUrl, callbackUrl }) {
  const payload = {
    profile_id: process.env.PAYTABS_PROFILE_ID,
    tran_type: 'sale',
    tran_class: 'ecom',
    cart_id: orderId,
    cart_currency: currency || 'AED',
    cart_amount: amount,
    cart_description: `Order ${orderId}`,
    paypage_lang: 'en', // or 'ar' for Arabic checkout
    customer_details: {
      name: customerName,
      email: customerEmail,
    },
    return: returnUrl,
    callback: callbackUrl, // server-to-server notification
  };

  const response = await axios.post(
    'https://secure.paytabs.com/payment/request',
    payload,
    {
      headers: {
        authorization: process.env.PAYTABS_SERVER_KEY,
      },
    }
  );

  return response.data; // includes redirect_url
}
Enter fullscreen mode Exit fullscreen mode

Redirect the user to redirect_url from the response.

3. Handle the callback (IPN)

PayTabs sends a server-to-server callback to your callback URL when a transaction completes. This is more reliable than relying on the return redirect.

// Express.js callback handler
app.post('/payment/callback', async (req, res) => {
  const { tran_ref, payment_result } = req.body;

  // Verify the transaction server-side
  const verifyResponse = await axios.post(
    'https://secure.paytabs.com/payment/query',
    {
      profile_id: process.env.PAYTABS_PROFILE_ID,
      tran_ref,
    },
    {
      headers: { authorization: process.env.PAYTABS_SERVER_KEY },
    }
  );

  const result = verifyResponse.data?.payment_result?.response_status;

  if (result === 'A') {
    // Payment authorised — update your order
  }

  res.sendStatus(200); // Always return 200 to acknowledge receipt
});
Enter fullscreen mode Exit fullscreen mode

4. Flutter SDK integration

PayTabs maintains an official Flutter package: flutter_paytabs_bridge on pub.dev.

// pubspec.yaml
dependencies:
  flutter_paytabs_bridge: ^2.x.x
Enter fullscreen mode Exit fullscreen mode
import 'package:flutter_paytabs_bridge/PaymentSdkConfigurationDetails.dart';
import 'package:flutter_paytabs_bridge/flutter_paytabs_bridge.dart';

void startPayment() {
  PaytabsFlutter.startPayment(
    configuration: PaymentSDKConfiguration(
      profileID: "your_profile_id",
      serverKey: "your_server_key", // use client_key here in mobile
      clientKey: "your_client_key",
      cartID: "order_123",
      cartDescription: "Product description",
      merchantCountryCode: "AE",
      currency: "AED",
      amount: 100.0,
      screenTitle: "Checkout",
      billingDetails: BillingDetails(
        name: "Customer Name",
        email: "customer@email.com",
        countryCode: "AE",
      ),
    ),
    callback: (event) {
      if (event["status"] == "success") {
        // handle success
      } else if (event["status"] == "error") {
        // handle error
      }
    },
  );
}
Enter fullscreen mode Exit fullscreen mode

Things that will catch you out

1. AED is the default but not always the setting
Both gateways support multi-currency but default behaviour varies by account configuration. Confirm your merchant account currency settings before going live — a mismatch causes silent failures in sandbox that only appear in production.

2. 3D Secure is mandatory for UAE-issued cards
UAE banks require 3DS authentication for card-not-present transactions. Both gateways handle this, but your integration needs to account for the additional redirect step in the payment flow. Don't design your UX assuming a single-step checkout.

3. Test with real UAE cards before launch
Sandbox test cards pass through without 3DS. Real UAE-issued cards trigger it. Test your complete flow with an actual card in a staging environment before you go live — the 3DS redirect behaviour can break flows that seemed to work perfectly in sandbox.

4. Callback URLs must be publicly accessible
If you're developing locally, use ngrok or a similar tunnel for your callback URL. PayTabs and Telr can't reach localhost.

5. Idempotency — build it in from the start
Network timeouts can cause duplicate payment attempts. Generate a unique cart_id / order_id per transaction attempt and check for it before processing. Both gateways will process duplicates if you let them.

Which one should you use?

  • Building a Flutter app? Start with PayTabs — the official Flutter SDK saves meaningful integration time.
  • Enterprise client requiring Telr specifically? Telr is fine; just expect to spend more time on the integration and account setup.
  • Need Arabic checkout? Both support it. PayTabs makes it a single parameter (paypage_lang: 'ar').
  • Need recurring billing? Both support it. Telr's implementation is more documented.

For most new projects in the UAE, PayTabs is the faster path to production.

Resources


We build mobile and web apps for the UAE market at Daiyra 360 Communications — payment gateway integration is something we set up on almost every project. If something in this guide is outdated or wrong, drop a comment and we'll update it.

Top comments (0)