Device features
Web APIs Bridge
Native Polyfills for Notifications, Share, Clipboard & More
AppMintAppwrightBoth builders - the steps below are the same in each.
1What are Web API Bridges?#
Android WebView doesn't support several modern Web APIs that Chrome does. Your generated app automatically polyfills these APIs so websites work just like they do in Chrome:
✅ Web Notifications (Notification API)
✅ Web Share (navigator.share)
✅ Clipboard (navigator.clipboard)
✅ window.close()
✅ target="_blank" links & window.open()
No setup needed - these work automatically in every generated app.
2Web Notifications#
Websites that use Notification.requestPermission() and new Notification() will show real Android notifications.
- On Android 13+, the user gets a system permission dialog
- On Android 12 and below, notifications are allowed by default
- Tapping a notification opens the app
- The 'tag' option replaces previous notifications (matches Web spec)
// Your website JS — works the same as in Chrome:
Notification.requestPermission().then(function(result) {
if (result === 'granted') {
new Notification('Hello!', {
body: 'This is a native Android notification',
tag: 'my-notif'
});
}
});3Web Share API#
navigator.share() opens the Android native share sheet, letting users share content to WhatsApp, Telegram, email, etc.
- Supports title, text, and url
- navigator.canShare() returns true
- Works exactly like Chrome's share dialog
// Share content from your website:
navigator.share({
title: 'Check this out',
text: 'Amazing content!',
url: 'https://example.com/page'
});4Clipboard API#
navigator.clipboard.writeText() and readText() are bridged to Android's native clipboard.
- writeText() copies text to system clipboard
- readText() reads from system clipboard
- Both return Promises (matching the Web spec)
// Copy to clipboard:
await navigator.clipboard.writeText('Hello World');
// Read from clipboard:
const text = await navigator.clipboard.readText();5window.close() & target="_blank"#
Two common WebView gaps are also fixed:
- window.close() - closes/finishes the app activity (useful for payment/auth callback pages)
- target="_blank" links - load inside the WebView instead of being silently dropped
- window.open() calls - handled the same way
No website changes needed - these just work.
6Domain Matching for External Links#
When 'External Links → Open in Browser' is set, the app intelligently matches domains:
- google.com and www.google.com are treated as the same domain
- Only truly different domains open in Chrome
- Redirects within the same site stay in the WebView
This prevents the issue where a site like google.com redirects to www.google.com and accidentally opens in Chrome.
7UPI & Payment App Links (Razorpay, GPay, PhonePe, Paytm)#
Any link whose scheme isn't a web page automatically opens the Android app that owns it - no setup, no scheme list to configure:
- upi://pay - generic UPI intent (the user picks their UPI app)
- paytmmp:// (Paytm), phonepe://, tez:// (Google Pay), gpay://
- tel:, mailto:, sms:, whatsapp://, tg://, intent://
This works everywhere a page can navigate:
✅ A tapped <a href="upi://pay?..."> link
✅ A JS redirect (window.location.href = 'upi://...')
✅ A popup - window.open('paytmmp://...') - which is how Razorpay Checkout hands off to UPI apps
So Razorpay / Cashfree / PhonePe checkout pages work as-is: the customer taps a UPI option, the payment app opens, they approve, and your page's success handler fires when they return. If no app on the phone can handle the scheme, the user sees a clear toast instead of a blank error page.
⚠️ Selling DIGITAL goods in a Play Store app? Google requires Play Billing for those - use the IAP guide instead. UPI/card checkout is for physical goods, services, and apps distributed outside Play.
<!-- Simplest form — a plain UPI intent link. The user's phone
shows every installed UPI app (GPay/PhonePe/Paytm/BHIM): -->
<a href="upi://pay?pa=merchant@bank&pn=MyStore&am=99.00&cu=INR&tn=Order%20123">
Pay ₹99 via UPI
</a>8Complete Example - Razorpay Checkout with UPI, End to End#
A full, copy-pasteable flow using Razorpay Checkout. What happens on the phone:
- Customer taps 'Pay ₹499' → Razorpay Checkout opens in the page
- Customer picks UPI → their UPI app opens automatically (this is the paytmmp:// / phonepe:// hand-off the app handles for you)
- Customer approves in the UPI app and returns - Android brings your app back on its own
- The handler function fires with a payment ID → show your success screen
- dismiss fires if they cancel → let them retry
Replace rzp_test_XXXXXXXX with your key from dashboard.razorpay.com. Test with a key_test key first - Razorpay's test mode simulates UPI approval.
⚠️ For a real store, confirm the payment server-side (Razorpay webhook or Orders API + signature check) before shipping goods - the browser-side handler alone can be faked.
<!doctype html>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://checkout.razorpay.com/v1/checkout.js"></script>
<h2>Premium Plan — ₹499</h2>
<button id="payBtn">Pay ₹499</button>
<p id="result"></p>
<script>
document.getElementById('payBtn').onclick = function () {
var rzp = new Razorpay({
key: 'rzp_test_XXXXXXXX', // your Razorpay Key ID
amount: 49900, // paise — ₹499.00
currency: 'INR',
name: 'My Store',
description: 'Premium Plan',
prefill: { contact: '', email: '' },
theme: { color: '#2563eb' },
// Fires after the customer returns from their UPI app
handler: function (response) {
document.getElementById('result').textContent =
'✅ Paid! Payment ID: ' + response.razorpay_payment_id;
// Real store: send response.razorpay_payment_id to your
// server / webhook flow and verify before unlocking anything.
},
modal: {
ondismiss: function () {
document.getElementById('result').textContent =
'Payment cancelled — tap Pay to try again.';
}
}
});
rzp.open();
};
</script>