Skip to content
SDK preview. Packages are not publicly published yet — use REST / cURL, or the local development artifacts.

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.

Text
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#

  1. The app sends an order id to your backend; the backend decides the amount and returns checkout_url.
  2. The app opens it with Linking.openURL.
  3. Your backend set the payment's return_url to your app's deep link (myapp://payment-return). Configure that scheme in your app (scheme in Expo's app.json, or native URL types).
  4. When the app receives the link, ask your backend for the order's status.
PayScreen.tsx · TypeScript
// 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.