# React Native

> Accept Devzity 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](https://docs.devzity.com/v1/android.md) and [iOS](https://docs.devzity.com/v1/ios.md), 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 ──(Devzity secret key)──▶ Devzity
        ▲                    │
        └──── checkout_url ──┘
app opens checkout_url ──▶ customer pays ──▶ myapp://payment-return?… ──▶ app
app ──▶ your backend ──▶ "is my order paid?"
```

**The Devzity 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](https://docs.devzity.com/v1/mobile-backend.md).

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

```tsx
// React Native: the app never holds a Devzity 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?devzity_payment_id=…&devzity_status=PAID
    const subscription = Linking.addEventListener("url", ({ url }) => {
      if (!url.includes("devzity_status")) return;
      // devzity_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>
  );
}
```

> **Warning:** `devzity_status` in the return link is a **hint**, not proof. Never fulfil or unlock anything from it — refresh the order from your backend.

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.
