> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dodopayments.com/llms.txt
> Use this file to discover all available pages before exploring further.

# React Native

> Mở checkout được lưu trữ trên Dodo Payments từ ứng dụng React Native hoặc Expo trong cửa sổ trình duyệt hệ thống và nhận kết quả checkout có kiểu dữ liệu từ một lần gọi.

> [!NOTE]
>
> Trang này trình bày SDK checkout React Native chính thức của Dodo Payments, `@dodopayments/react-native-checkout`. SDK mở checkout được lưu trữ trên Dodo Payments trong cửa sổ trình duyệt native và trả về kết quả có kiểu dữ liệu. Một package cũ hơn, `dodopayments-react-native-sdk` (không có scope), có API khác. Trang này chỉ dokument package có scope.

> **[Checkout Sessions API](/developer-resources/checkout-session)**
>
> Tạo `checkout_url` mà SDK này sẽ mở từ backend của bạn.

> **[Mobile Integration Guide](/developer-resources/mobile-integration)**
>
> Xem SDK này phù hợp như thế nào trong toàn bộ quy trình thanh toán trên mobile.

React Native SDK là một Turbo Module bao bọc các checkout SDK native cho [iOS](/developer-resources/sdks/ios) và [Android](/developer-resources/sdks/android). SDK mở `SFSafariViewController` trên iOS và Custom Tab trên Android. SDK không chứa API key và không có logic checkout riêng, vì vậy không bao giờ gọi Dodo Payments API. Checkout chạy trong cửa sổ trình duyệt. SDK hiển thị và đóng cửa sổ đó, đồng thời đọc kết quả từ return URL.

> [!WARNING]
>
> SDK này chỉ hỗ trợ **New Architecture**. SDK yêu cầu React Native 0.77 trở lên, iOS 16 trở lên và Android `minSdk` 24. Ứng dụng Android của bạn phải được build với `compileSdk` 34 trở lên.

## Cài đặt

**1. Install the Package**

**Android**

Package được autolink và lấy `com.dodopayments.api:checkout-android` từ Maven Central.

```sh theme={null}
npm i @dodopayments/react-native-checkout
```

Dependency native được phân giải tự động, vì vậy không cần thêm bước cài đặt nào khác.

**iOS**

```sh theme={null}
npm i @dodopayments/react-native-checkout
cd ios && pod install
```

Package tích hợp Swift core và CocoaPods sẽ cài đặt nó.

**Expo**

SDK chỉ hoạt động trong development build, không hoạt động trong Expo Go.

```sh theme={null}
npm i @dodopayments/react-native-checkout
```

Sau đó, cấu hình plugin trong `app.json` như minh họa ở bước tiếp theo.

[Tùy chỉnh giao diện](#appearance-customization) yêu cầu version 1.2.0 trở lên.

**2. Register a Callback URL Scheme**

Đăng ký một URL scheme để hệ điều hành định tuyến return URL của checkout trở lại ứng dụng của bạn.

**Android (Gradle)**

Đặt scheme làm manifest placeholder trong `android/app/build.gradle`:

```groovy android/app/build.gradle theme={null}
android {
    defaultConfig {
        manifestPlaceholders["dodoCallbackScheme"] = "myapp"
    }
}
```

Thay `"myapp"` bằng scheme của ứng dụng.

**iOS (Info.plist)**

Thêm một URL type vào `ios/YourApp/Info.plist`:

```xml Info.plist theme={null}
<key>CFBundleURLTypes</key>
<array>
  <dict>
    <key>CFBundleURLName</key>
    <string>myapp</string>
    <key>CFBundleURLSchemes</key>
    <array>
      <string>myapp</string>
    </array>
  </dict>
</array>
```

Bạn cũng có thể thêm URL type trong Xcode tại **Info → URL Types**.

**Expo (both platforms)**

Config plugin của package đăng ký scheme cho cả hai platform trong quá trình `prebuild`. Truyền scheme dưới dạng tùy chọn `scheme` của plugin trong `app.json`:

```json app.json theme={null}
{
  "expo": {
    "scheme": "myapp",
    "plugins": [
      [
        "@dodopayments/react-native-checkout",
        { "scheme": "myappcheckout" }
      ]
    ]
  }
}
```

`scheme` của plugin phải khớp với scheme của `returnUrl` mà bạn truyền vào, chẳng hạn `myappcheckout://return`. Sử dụng scheme khác với `expo.scheme`. Expo đã đăng ký `expo.scheme` trên `MainActivity`, vì vậy việc sử dụng lại scheme này có thể gửi checkout return đến activity không đúng trên Android và plugin sẽ cảnh báo bạn. Plugin từ chối `http`, `https` và các system scheme khác.

Build lại native project sau khi chỉnh sửa `app.json`:

```sh theme={null}
npx expo prebuild --clean
```

Plugin chỉ hoạt động trong development build, không hoạt động trong Expo Go. Trên iOS, bạn vẫn cần listener `Linking` trong [Chuyển tiếp Return URL](#forwarding-the-return-url).

Nếu `android/app/build.gradle` không có block `defaultConfig { }`, plugin sẽ dừng `prebuild` với một lỗi. Trong trường hợp đó, hãy tự thêm placeholder như minh họa trong tab Android.

Trên mọi platform, hãy đặt cùng một URL với `return_url` của checkout session khi backend tạo session. SDK đối chiếu return URL theo scheme, host và path. URL không cần tải một trang thực tế.

## Cách sử dụng

Gọi `DodoCheckout.start` với `checkout_url` từ backend của bạn:

```typescript theme={null}
import { Linking } from 'react-native';
import { DodoCheckout } from '@dodopayments/react-native-checkout';

// Required for iOS's return-URL handling.
Linking.addEventListener('url', ({ url }) => DodoCheckout.handleOpenURL(url));

// showSuccess(), showFailure(), showExpired(), and reconcileAbandonedSession()
// are your own functions. See Abandoned Sessions for what to reconcile.
const result = await DodoCheckout.start({
  checkoutUrl,                          // from your backend's checkout session
  returnUrl: 'myapp://checkout/return', // scheme must be registered (see Installation)
  onEvent: (e) => console.log(e.type),  // logging only
});

switch (result.status) {
  case 'succeeded': showSuccess(result.paymentId); break;
  case 'failed':    showFailure(); break;
  case 'cancelled': await reconcileAbandonedSession(); break; // outcome unknown, not a failure
  case 'pending':   await reconcileAbandonedSession(); break;
  case 'expired':   showExpired(); break;
}
```

`onEvent` nhận các event có `type` là `checkout.opened`, `checkout.return_received` hoặc `checkout.closed`. Chỉ sử dụng chúng để logging, không bao giờ dùng để quyết định kết quả.

## Chuyển tiếp Return URL

iOS cần listener `Linking` để xử lý return URL, vì `SFSafariViewController` không thể tự bắt return URL của nó. Trên Android, `handleOpenURL` không làm gì và resolve `false`, vì Android SDK bắt redirect ở native. Bạn có thể đăng ký listener trên cả hai platform.

```typescript theme={null}
import { Linking } from 'react-native';
import { DodoCheckout } from '@dodopayments/react-native-checkout';

Linking.addEventListener('url', ({ url }) => {
  DodoCheckout.handleOpenURL(url);
});
```

Trên iOS, `handleOpenURL` resolve `true` khi URL thuộc về checkout đang thực hiện và `false` đối với mọi URL khác.

## Ý nghĩa của kết quả

SDK xây dựng kết quả từ các query parameter trên return URL.

> [!WARNING]
>
> `result.status` là một UI hint, không phải bằng chứng thanh toán. Xác nhận mọi khoản thanh toán từ backend bằng webhook `payment.succeeded` hoặc `subscription.active`.

**status** `CheckoutStatus` *required*

Một trong năm giá trị:

* `succeeded`: return URL có `status=succeeded` (thanh toán một lần) hoặc `status=active` (subscription).
* `failed`: khoản thanh toán bị từ chối (`status=failed`).
* `cancelled`: customer đã đóng cửa sổ trình duyệt trước khi return URL đến. SDK không biết kết quả và khoản thanh toán có thể đã thành công, vì vậy không hiển thị màn hình thất bại. Thay vào đó, hãy đối soát [abandoned session](#abandoned-sessions).
* `pending`: khoản thanh toán được settlement sau (`status=processing` hoặc bất kỳ giá trị `requires_*` nào), hoặc parameter `status` bị thiếu hoặc không được nhận diện. Hãy đối soát như với `cancelled`.
* `expired`: checkout session đã hết hạn (`status=expired`).

**paymentId** `string`

Query parameter `payment_id`, khi return URL có chứa parameter này. Hiển thị parameter trong UI, nhưng không dùng nó để cấp quyền truy cập. Xem [Xác minh khoản thanh toán](#verify-the-payment).

**subscriptionId** `string`

Query parameter `subscription_id`. Được thiết lập cho subscription checkout.

**licenseKeys** `string[]`

Query parameter `license_key`. Được thiết lập khi checkout bao gồm các sản phẩm có license key.

**customerEmail** `string`

Query parameter `email`. Được thiết lập khi checkout thu thập địa chỉ email.

**raw** `Record\<string, string\>`

Mọi query parameter từ return URL, giữ nguyên văn.

## Xác minh khoản thanh toán

> **[Webhooks](/developer-resources/webhooks)**
>
> Dodo Payments gọi backend của bạn khi một khoản thanh toán thành công hoặc một subscription được kích hoạt.

> **[Get Payment Detail](/api-reference/payments/get-payments-1)**
>
> Tra cứu `paymentId` bằng secret key để kiểm tra trạng thái.

Chỉ cấp quyền truy cập sau khi một trong các cách này xác nhận khoản thanh toán. Không chỉ dựa vào `result.status`.

## Tùy chỉnh giao diện

Để thay đổi toolbar, button và color scheme của trình duyệt checkout, truyền `customization` vào `start(...)`. Android Custom Tabs và `SFSafariViewController` trên iOS cung cấp các native control khác nhau, vì vậy các tùy chọn được nhóm thành object `android` và object `ios`. Mỗi platform chỉ đọc object của chính nó. Mọi field đều là tùy chọn. Khi bạn bỏ qua một field, platform sẽ áp dụng giá trị mặc định riêng.

> **Android — Custom Tab**
>
> **toolbarColor** `string`
>
> Màu nền toolbar, dưới dạng chuỗi hex: `"#RRGGBB"` hoặc `"#AARRGGBB"`.
>
> **navigationBarColor** `string`
>
> Màu navigation bar, dưới dạng chuỗi hex.
>
> **navigationBarDividerColor** `string`
>
> Màu của đường phân cách phía trên navigation bar, dưới dạng chuỗi hex.
>
> **closeButtonStyle** `'default' | 'back'`
>
> `default` hiển thị icon "X" của hệ thống. `back` hiển thị mũi tên quay lại do SDK vẽ.
>
> **closeButtonPosition** `'start' | 'end'`
>
> Mặt của toolbar nơi close button xuất hiện.
>
> **shareButtonEnabled** `boolean`
>
> Hiển thị share icon của toolbar. `false` ẩn icon này.
>
> **showTitleEnabled** `boolean`
>
> Hiển thị tiêu đề trang bên dưới URL trong toolbar.
>
> **urlBarHidingEnabled** `boolean`
>
> Tự động ẩn toolbar khi trang được cuộn.
>
> **bookmarksButtonEnabled** `boolean`
>
> Hiển thị "Bookmark this page" trong menu overflow.
>
> **downloadsButtonEnabled** `boolean`
>
> Hiển thị "Download page" trong menu overflow.
>
> **colorScheme** `'system' | 'light' | 'dark'`
>
> `light` hoặc `dark` buộc giao diện đó bất kể cài đặt hệ thống của thiết bị. `system` tuân theo cài đặt hệ thống.

> **iOS — SFSafariViewController**
>
> **dismissButtonStyle** `'done' | 'close' | 'cancel'`
>
> Kiểu của dismiss button. iOS quyết định hiển thị dưới dạng label hay icon.
>
> **presentationStyle** `'pageSheet' | 'fullScreen'`
>
> `pageSheet` (mặc định) hiển thị một card mà customer có thể vuốt xuống để đóng. `fullScreen` bao phủ toàn bộ màn hình.
>
> **barCollapsingEnabled** `boolean`
>
> Cho phép toolbar thu gọn khi trang được cuộn. Chỉ có hiệu lực khi `presentationStyle` là `fullScreen`. Với `pageSheet`, các thanh luôn được ghim bất kể cài đặt này.
>
> **colorScheme** `'system' | 'light' | 'dark'`
>
> `light` hoặc `dark` buộc giao diện đó bất kể cài đặt hệ thống của thiết bị. `system` tuân theo cài đặt hệ thống.

iOS không có tùy chọn màu toolbar vì các thuộc tính tint của `SFSafariViewController` bên dưới đã bị deprecated kể từ iOS 26.

```typescript theme={null}
const result = await DodoCheckout.start({
  checkoutUrl,
  returnUrl: 'myapp://checkout/return',
  customization: {
    android: { toolbarColor: '#6366F1', closeButtonStyle: 'back' },
    ios: { presentationStyle: 'fullScreen', colorScheme: 'dark' },
  },
});
```

## Lỗi

`start` chỉ reject với `CheckoutError` khi sử dụng sai hoặc xảy ra lỗi platform. Đọc lý do từ `error.code`. Customer hủy hoặc khoản thanh toán bị từ chối luôn là một result, không phải rejection.

* `INVALID_CHECKOUT_URL`: `checkoutUrl` không phải là URL checkout session `https` (path bắt đầu bằng `/session/`) trên `checkout.dodopayments.com` hoặc `test.checkout.dodopayments.com`.
* `INVALID_RETURN_URL`: `returnUrl` không phải là absolute URL có scheme và host.
* `ALREADY_IN_PROGRESS`: một checkout khác đang chạy. Mỗi lần chỉ có thể chạy một checkout.
* `PLATFORM_ERROR`: lỗi platform không mong đợi. SDK cũng báo cáo mọi native error không được nhận diện bằng code này.

## Abandoned Session

Native SDK ghi lại checkout session khi checkout bắt đầu và chỉ xóa bản ghi khi checkout kết thúc với `succeeded`, `failed` hoặc `expired`. Bản ghi vẫn tồn tại khi ứng dụng hoặc JavaScript bundle bị kill trong lúc checkout, làm mất promise `start`, và sau result `cancelled` hoặc `pending`. Kiểm tra bản ghi này khi mount tiếp theo và sau mỗi result `cancelled` hoặc `pending`:

```typescript theme={null}
import { DodoCheckout } from '@dodopayments/react-native-checkout';

const abandoned = await DodoCheckout.getAbandonedSession();
if (abandoned) {
  // Ask your backend for the outcome of abandoned.sessionId, and show it.
  // Clear the record only after the outcome is final:
  await DodoCheckout.clearAbandonedSession();
}
```

`abandoned.sessionId` là ID của checkout session, bắt đầu bằng `cks_`. `abandoned.createdAt` là thời điểm `Date` checkout bắt đầu. Backend của bạn có thể tra cứu session bằng [Get Checkout Session](/api-reference/checkout-sessions/get-checkouts), endpoint này trả về `payment_id` và `payment_status`. Cho đến khi khoản thanh toán đạt trạng thái cuối cùng, hãy xử lý như đang pending, không phải failed.

## Liên quan

> **[Mobile Integration Guide](/developer-resources/mobile-integration)**
>
> Cùng một contract cho Android, iOS và Flutter.

> **[Expo Boilerplate](/developer-resources/expo-boilerplate)**
>
> Ví dụ Expo hoàn chỉnh với tích hợp checkout.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.