Amwal Tech logoDocs

Web-View Integration

Integrate Amwal Installment Links directly into your native iOS, Android, or React Native mobile applications using WebViews without redirecting users out to external mobile browsers.


1. Integration Architecture

Rendering diagram...

Generate a single-use installment payment URL from your server:

const createPaymentLink = async (orderData) => {
  const response = await fetch('https://backend.sa.amwal.tech/payment_links/YOUR_STORE_ID/create', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': 'YOUR_SECRET_KEY',
    },
    body: JSON.stringify({
      amount: orderData.amount,
      title: orderData.title,
      singleUse: true,
      client_phone_number: orderData.customerPhone,
      client_email: orderData.customerEmail,
      language: 'en', // 'en' or 'ar'
      callback_url: 'https://mystore.sa/webhook/payment-complete',
      metadata: {
        order_id: orderData.orderId,
        platform: 'mobile_app'
      }
    })
  });

  return await response.json();
};

3. Mobile Implementation

iOS (Swift - WKWebView)

import UIKit
import WebKit

class PaymentViewController: UIViewController, WKNavigationDelegate {
    @IBOutlet weak var webView: WKWebView!
    var paymentURL: String?
    var onPaymentComplete: ((Bool, String?) -> Void)?

    override func viewDidLoad() {
        super.viewDidLoad()
        webView.navigationDelegate = self
        webView.configuration.preferences.javaScriptEnabled = true
        webView.customUserAgent = "StoreMobileApp/1.0 Mobile"

        if let urlString = paymentURL, let url = URL(string: urlString) {
            webView.load(URLRequest(url: url))
        }
    }

    func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationPolicy) -> Void) {
        guard let url = navigationAction.request.url else {
            decisionHandler(.cancel)
            return
        }

        // Intercept completion redirects
        if url.absoluteString.contains("payment-success") {
            onPaymentComplete?(true, nil)
            dismiss(animated: true)
            decisionHandler(.cancel)
            return
        } else if url.absoluteString.contains("payment-failed") {
            onPaymentComplete?(false, "Payment cancelled or declined")
            dismiss(animated: true)
            decisionHandler(.cancel)
            return
        }

        decisionHandler(.allow)
    }
}

React Native (react-native-webview)

import React, { useRef } from 'react';
import { View, StyleSheet, Alert } from 'react-native';
import { WebView } from 'react-native-webview';

interface PaymentWebViewProps {
  paymentURL: string;
  onSuccess: () => void;
  onError: (error: string) => void;
}

export const PaymentWebView = ({ paymentURL, onSuccess, onError }: PaymentWebViewProps) => {
  const handleNavigationStateChange = (navState: { url: string }) => {
    const { url } = navState;

    if (url.includes('payment-success')) {
      onSuccess();
    } else if (url.includes('payment-failed') || url.includes('payment-cancelled')) {
      onError('Payment was cancelled or failed.');
    }
  };

  return (
    <View style={styles.container}>
      <WebView
        source={{ uri: paymentURL }}
        style={styles.webView}
        onNavigationStateChange={handleNavigationStateChange}
        javaScriptEnabled={true}
        domStorageEnabled={true}
        startInLoadingState={true}
      />
    </View>
  );
};

const styles = StyleSheet.create({
  container: { flex: 1 },
  webView: { flex: 1 },
});

4. Best Practices for Mobile WebViews

  1. Always Verify Server-Side: Never trust the mobile client alone to mark an order as paid. Confirm final status through backend webhooks (order.success) or POST /payment_links/{payment_link_id}/details.
  2. Single Use Links: Set singleUse: true to prevent replay attempts on completed payment links.
  3. Session Cookies: Enable domStorageEnabled = true and javaScriptEnabled = true so biometric and 3DS verification frames load without issues.

On this page