# App payment — Option A (open Stripe Checkout in an in-app browser)

You chose Option A, so the app reuses the **same** backend checkout. No new
backend endpoint. The app:

1. Calls `POST /api/client-portal/billing/create-checkout-session` (with the
   client's bearer token) → gets `{ url }`.
2. Opens that `url` in an **in-app browser** (not a raw WebView — Apple Pay and
   Google Pay only work in the system browser components below).
3. Stripe shows card + Google Pay + Apple Pay automatically. On success Stripe
   redirects to your `success_url`; the app detects it, closes the browser, and
   re-checks `GET /billing/status` until `access_allowed` is true (the webhook
   is async, so poll a few times).

The gate is enforced on the backend, so even before you build the app screen,
an enabled-but-unpaid client already can't read any protected endpoint (they
get HTTP 402 with `code: "PAYMENT_REQUIRED"`) — the app should route them to the
pay screen when it sees that.

## The important bit: use the RIGHT browser component

- **iOS** → `SFSafariViewController` (Apple Pay works here). NOT `WKWebView`.
- **Android** → Chrome **Custom Tabs** (Google Pay works here).

### React Native
```ts
import { Linking } from "react-native";
// Best: react-native-inappbrowser-reborn (uses SFSafariViewController / Custom Tabs)
import InAppBrowser from "react-native-inappbrowser-reborn";

async function startCheckout(getUrl: () => Promise<string>) {
  const url = await getUrl(); // call create-checkout-session, return data.url
  if (await InAppBrowser.isAvailable()) {
    const result = await InAppBrowser.openAuth(url, "yourapp://billing/return", {
      ephemeralWebSession: false,
      showTitle: true,
      enableUrlBarHiding: true,
    });
    // result.type === "success" when redirected back to yourapp://billing/return
  } else {
    Linking.openURL(url);
  }
  // then re-fetch /billing/status until access_allowed
}
```
Set your Stripe `success_url` to deep-link back (e.g. `yourapp://billing/return`)
or keep the web `success_url` and detect it via the browser's redirect callback.

### Flutter
```dart
import 'package:url_launcher/url_launcher.dart';
// url from create-checkout-session
await launchUrl(Uri.parse(url), mode: LaunchMode.externalApplication);
// (flutter_custom_tabs gives you Custom Tabs / SFSafariViewController explicitly)
```

### Native iOS (Swift)
```swift
import SafariServices
let vc = SFSafariViewController(url: URL(string: checkoutUrl)!)
present(vc, animated: true)
```

### Native Android (Kotlin)
```kotlin
import androidx.browser.customtabs.CustomTabsIntent
CustomTabsIntent.Builder().build().launchUrl(context, Uri.parse(checkoutUrl))
```

## When you're ready for native pay sheets (Option B)
Later, if you want native Apple Pay / Google Pay buttons inside the app instead
of a browser page, that's Option B: a new `create-payment-intent` backend
endpoint + Stripe's mobile SDK PaymentSheet + Apple/Google merchant setup. Ask
and I'll add it — nothing here has to change to move to it.
