> For the complete documentation index, see [llms.txt](https://docs.loomgate.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.loomgate.io/integrations/react.md).

# React

Component và hook cho React 18/19, chạy được với StrictMode và Next.js.

`@loompay/loomgate-react-sdk` bọc `loomgate.js` thành component và hook cho React 18 và 19.

```bash
pnpm add @loompay/loomgate-react-sdk react
```

## Ví dụ

```tsx
import { useState } from 'react';
import {
  BrandingElement,
  LoomgatePayment,
  LoomgateProvider,
  PaymentElement,
  loadLoomgate,
  useLoomgatePayment,
  type BillingDetails,
} from '@loompay/loomgate-react-sdk';

// Gọi loadLoomgate() MỘT lần, ngoài render, và chỉ trong trình duyệt (trên server truyền null).
const loomgatePromise =
  typeof window === 'undefined'
    ? null
    : loadLoomgate('pk_live_…', { apiBaseUrl: 'https://api.loomgate.io' });

export function Checkout({ clientSecret, billing }: { clientSecret: string; billing: BillingDetails }) {
  return (
    <LoomgateProvider loomgate={loomgatePromise}>
      <LoomgatePayment clientSecret={clientSecret}>
        <PaymentElement />
        <PayButton billingDetails={billing} />
        <BrandingElement /> {/* bắt buộc */}
      </LoomgatePayment>
    </LoomgateProvider>
  );
}

function PayButton({ billingDetails }: { billingDetails: BillingDetails }) {
  const { status, canConfirm, confirm, error, amountTotal, currency } = useLoomgatePayment();
  const [busy, setBusy] = useState(false);
  const [message, setMessage] = useState<string | null>(null);

  if (status === 'error') return <p role="alert">{error?.message}</p>;

  const price =
    amountTotal !== null && currency
      ? new Intl.NumberFormat('en-US', { style: 'currency', currency }).format(amountTotal / 100)
      : null;

  function handleClick() {
    setBusy(true);
    // Không await gì trước confirm().
    confirm({ billingDetails }).then((result) => {
      setBusy(false);
      if (result.error) return setMessage(result.error.message);
      window.location.assign(`/orders/thanks?pi=${result.paymentIntentId}`);
    });
  }

  return (
    <>
      <button type="button" disabled={!canConfirm || busy} onClick={handleClick}>
        {price ? `Pay ${price}` : 'Pay'}
      </button>
      {message && <p role="alert">{message}</p>}
    </>
  );
}
```

## API

| Thành phần                       | Ghi chú                                                                                                                                                                                                                                         |
| -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `<LoomgateProvider loomgate>`    | `loomgate` là kết quả `loadLoomgate()` (promise hoặc giá trị) hoặc `null` khi chưa tải. Giá trị khác `null` đầu tiên được giữ nguyên: đừng gọi `loadLoomgate()` ngay trong JSX. Sau khi `loadLoomgate()` lỗi, truyền một promise mới để thử lại |
| `<LoomgatePayment clientSecret>` | Sở hữu một session cho mỗi `clientSecret`. Đổi `clientSecret` thì session cũ bị hủy và tạo session mới                                                                                                                                          |
| `<PaymentElement className? />`  | Chỗ hiện form thẻ                                                                                                                                                                                                                               |
| `<BrandingElement className? />` | Phần thương hiệu của đối tác thanh toán, **bắt buộc**                                                                                                                                                                                           |
| `useLoomgatePayment()`           | `{ status: 'loading' \| 'ready' \| 'error', canConfirm, confirm, error, amountTotal, currency }`                                                                                                                                                |

* `confirm()` giống `session.confirm()` của [loomgate.js](/integrations/browser.md): không throw, trả `{ status, paymentIntentId }` hoặc `{ error }`.
* `amountTotal` và `currency` là số tiền form thẻ sẽ trừ (`null` trước lần tải đầu). Khi `confirm()` trả `payment_intent_updated`, `amountTotal` đã là số mới và `status` vẫn là `'ready'`: cho người mua bấm lại.
* Chạy đúng dưới `<StrictMode>`: sau mount → unmount → mount chỉ còn một form thẻ.

## Next.js và SSR

Package có sẵn `"use client"`, import được từ Server Component. Component vẫn được render trên server, nên chỉ gọi `loadLoomgate()` trong trình duyệt (như ví dụ trên) và truyền `null` khi render trên server. Tạo payment (lấy `clientSecret`) ở Route Handler hoặc Server Action bằng [SDK máy chủ](/integrations/node-server.md).

## Ví dụ đầy đủ

* [`examples/react`](https://github.com/loomgroup/loomgate-examples/tree/main/examples/react) trong [kho ví dụ loomgate-examples](https://github.com/loomgroup/loomgate-examples): trang checkout hoàn chỉnh dùng React SDK và shadcn/ui, kèm phần máy chủ tạo payment.
* [Playground](https://examples.loomgate.io) cũng được xây bằng React SDK: chạy thử từng bước (báo giá phí, tạo payment, thanh toán, hoàn tiền) bằng key merchant của chính bạn và xem mã của từng bước. Thanh toán trên playground dùng tiền thật và phí không được hoàn.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.loomgate.io/integrations/react.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
