React Native
Accept PayTaka payments in a React Native app using your own backend, the system browser and a deep link.
No native wrapper package is published for React Native yet — but the integration is fully supported today. It uses the same secure pattern as Android and iOS, with the built-in Linking API, the checkout_url your backend creates, the system browser, and a deep link.
React Native app ──▶ your small backend ──(PayTaka secret key)──▶ PayTaka
▲ │
└──── checkout_url ──┘
app opens checkout_url ──▶ customer pays ──▶ myapp://payment-return?… ──▶ app
app ──▶ your backend ──▶ "is my order paid?"The PayTaka secret key stays on your backend. Never put it in a React Native bundle — the JavaScript ships inside the app and can be read by anyone. No backend yet? See I only have an app.
The flow#
- The app sends an order id to your backend; the backend decides the amount and returns
checkout_url. - The app opens it with
Linking.openURL. - Your backend set the payment's
return_urlto your app's deep link (myapp://payment-return). Configure that scheme in your app (schemein Expo'sapp.json, or native URL types). - When the app receives the link, ask your backend for the order's status.
// React Native: the app never holds a PayTaka secret key. It sends an ORDER ID to YOUR backend (the
// server decides the amount — see examples/mobile-backend), opens the checkout_url it returns, and
// asks YOUR backend for the order's real status when the customer comes back.
import React, { useEffect, useRef, useState } from "react";
import { Button, Linking, Text, View } from "react-native";
const BACKEND = "https://your-backend.example.com";
const ORDER_ID = "order-1001"; // the order being paid
export default function PayScreen() {
const [message, setMessage] = useState("Ready");
const orderId = useRef(ORDER_ID); // keep it so the status check works even if the link never arrives
async function refreshOrder() {
// The authoritative answer — from YOUR backend, never from the return link.
const { status } = await (await fetch(`${BACKEND}/orders/${orderId.current}/status`)).json();
setMessage(`Order status: ${status}`);
}
useEffect(() => {
// Return link: myapp://payment-return?paytaka_payment_id=…&paytaka_status=PAID
const subscription = Linking.addEventListener("url", ({ url }) => {
if (!url.includes("paytaka_status")) return;
// paytaka_status is only a HINT — never fulfil from it. Refresh the order instead.
setMessage("Checking your payment…");
void refreshOrder();
});
return () => subscription.remove();
}, []);
async function pay() {
const response = await fetch(`${BACKEND}/orders/${orderId.current}/pay`, { method: "POST" });
const { checkout_url } = await response.json();
await Linking.openURL(checkout_url); // opens the system browser
}
return (
<View>
<Button title={`Pay for ${ORDER_ID}`} onPress={pay} />
<Text>{message}</Text>
</View>
);
}Closing the browser or backgrounding the app is not a failed payment. Keep the order id (state or storage) so you can ask your backend for the status even if the deep link never arrives.