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

# React

> Embed secure payment checkout experiences directly into your React application — modal or inline

## Overview

The **Moment React SDK** enables seamless payment integration into any React application. Whether you need a **modal overlay** or an **inline embedded checkout**, the SDK provides a simple, declarative API with React hooks to handle the entire payment flow.

Built with **React** and modern hooks, the SDK integrates naturally with your React workflow and state management.

#### Embedded Modal :

<video autoPlay loop muted playsInline style={{ width: '100%', borderRadius: '8px' }}>
  <source src="https://mintcdn.com/momentholdingslimited/ADxHxgRJd2L3EWcV/libraries-and-sdks/resources/web-sdk-mp4.mp4?fit=max&auto=format&n=ADxHxgRJd2L3EWcV&q=85&s=7b598eef65d191370915958b1146c87a" type="video/mp4" data-path="libraries-and-sdks/resources/web-sdk-mp4.mp4" />
</video>

***

## Key Features

* **React Hooks API** – Declarative interface with `useCheckout` hook
* **Flexible Display Options** – Modal or inline embedded checkout
* **TypeScript Support** – Full type definitions included
* **Flexible Callbacks** – Handle success, failure, cancellation, and retry events
* **Cross-Origin Safe** – Secure iframe communication via `postMessage`
* **Environment Detection** – Automatic sandbox / production switching
* **PCI Compliant** – Sensitive payment data handled entirely in Moment's secure iframe

***

## Display Variants

| Variant  | Availability | Description                                           |
| -------- | ------------ | ----------------------------------------------------- |
| `modal`  | Available    | Opens checkout in a centered modal overlay (default)  |
| `inline` | Coming Soon  | Renders checkout inside a specified container element |

***

## How It Works

```mermaid theme={"system"}
sequenceDiagram
    participant Customer
    participant React App
    participant Your Backend
    participant Moment API
    participant Moment Checkout
    participant 3DS / Bank Page

    Note over React App: Page loads — MomentProvider + MomentCheckoutProvider mount, fetchClientToken & fetchSessionStatus registered

    Customer->>React App: Clicks "Pay Now"
    React App->>Moment Checkout: checkout.launch()
    Moment Checkout->>Your Backend: fetchClientToken() fires
    Your Backend->>Moment API: POST /collect/payment_sessions (with success_url)
    Moment API-->>Your Backend: Returns client_token (JWT)
    Your Backend-->>Moment Checkout: Returns client_token
    Moment Checkout-->>Customer: Displays checkout UI (modal/inline)
    Customer->>Moment Checkout: Submits payment
    Moment Checkout->>3DS / Bank Page: Redirects customer (full page)
    Customer->>3DS / Bank Page: Completes authentication
    3DS / Bank Page-->>React App: Redirects to your success_url?session_id=abc123
    Note over React App: Page loads — MomentCheckoutProvider detects session_id in URL
    React App->>Your Backend: fetchSessionStatus('abc123') fires
    Your Backend->>Moment API: GET /collect/payment_sessions/abc123
    Moment API-->>Your Backend: Returns session status
    Your Backend-->>React App: Returns { status: 'completed', ... }
    React App->>Customer: onSuccess(result) fires — shows confirmation
```

1. **Page loads** — `MomentProvider` mounts (SDK init), then `MomentCheckoutProvider` mounts, registering your `fetchClientToken`, `fetchSessionStatus`, and event callbacks (`onSuccess`, `onFailure`, etc.)
2. **Customer clicks Pay** — `checkout.launch()` is called from your component
3. **SDK calls `fetchClientToken`** — your callback creates a session on your backend (with a `success_url`) and returns the `client_token`
4. **Checkout opens** in your chosen variant (modal or inline)
5. **Customer submits payment** — Moment takes over the full page and redirects to the authentication page
6. **Customer completes authentication** on the external page
7. **Customer is redirected back** to your `success_url` with `?session_id=abc123` appended
8. **Page loads again** — `MomentCheckoutProvider` detects the `session_id` in the URL automatically and calls your `fetchSessionStatus('abc123')` callback
9. **Your callback fetches the status** from your backend, which queries Moment API
10. **SDK routes to the right handler** — `onSuccess`, `onFailure`, `onExpired`, or `onCancel` — and removes `session_id` from the URL silently
11. **Webhooks** are delivered to your backend regardless of outcome

***

## Installation

### **NPM**

```bash theme={"system"}
npm install momentpay
```

### **Yarn**

```bash theme={"system"}
yarn add momentpay
```

### **pnpm**

```bash theme={"system"}
pnpm add momentpay
```

***

## **Quick Start**

### **1. Initialize the SDK**

Set up two providers at the root of your app: `MomentProvider` initializes the SDK instance (equivalent to `Moment(clientKey)` in vanilla JS), and `MomentCheckoutProvider` configures the `checkout` product (equivalent to `moment.checkout(config)`). You provide two fetch callbacks — the SDK calls them automatically at the right moments, so you never manage tokens or session IDs manually.

**Backend endpoints:**

<Tabs>
  <Tab title="Node.js">
    ```javascript server.js theme={"system"}
    // Called by fetchClientToken when checkout.launch() is triggered
    app.post('/api/create-payment-session', async (req, res) => {
      const response = await fetch('https://api.momentpay.net/collect/payment_sessions', {
        method: 'POST',
        headers: {
          'Authorization': 'Bearer sk_test_your_secret_key',
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          amount: 10000, // Amount in cents (e.g., R100.00)
          currency: 'ZAR',
          type: 'one_time',
          options: {
            checkout_options: {
              presentation_mode: { mode: 'embedded' },
              success_url: 'https://yoursite.com/checkout?session_id={SESSION_ID}', // required for redirect payments
            },
          },
        }),
      });
      const { client_token } = await response.json();
      res.json({ client_token });
    });

    // Called by fetchSessionStatus after customer returns from a redirect payment.
    // The SDK extracts session_id from the URL (?session_id=abc123) and passes it here.
    app.get('/api/session-status', async (req, res) => {
      const sessionId = req.query.session_id;
      const response = await fetch(
        `https://api.momentpay.net/collect/payment_sessions/${sessionId}`,
        { headers: { 'Authorization': 'Bearer sk_test_your_secret_key' } }
      );
      const session = await response.json();
      res.json({ status: session.status, ...session });
    });
    ```
  </Tab>

  <Tab title="Python">
    ```python server.py theme={"system"}
    import requests
    from flask import Flask, request, jsonify

    app = Flask(__name__)
    SECRET_KEY = 'sk_test_your_secret_key'

    # Called by fetchClientToken when checkout.launch() is triggered
    @app.route('/api/create-payment-session', methods=['POST'])
    def create_payment_session():
        response = requests.post(
            'https://api.momentpay.net/collect/payment_sessions',
            headers={
                'Authorization': f'Bearer {SECRET_KEY}',
                'Content-Type': 'application/json',
            },
            json={
                'amount': 10000,  # Amount in cents (e.g., R100.00)
                'currency': 'ZAR',
                'type': 'one_time',
                'options': {
                    'checkout_options': {
                        'presentation_mode': {'mode': 'embedded'},
                        'success_url': 'https://yoursite.com/checkout?session_id={SESSION_ID}',  # required for redirect payments
                    },
                },
            },
        )
        data = response.json()
        return jsonify({'client_token': data['client_token']})

    # Called by fetchSessionStatus after customer returns from a redirect payment.
    # The SDK extracts session_id from the URL (?session_id=abc123) and passes it here.
    @app.route('/api/session-status', methods=['GET'])
    def get_session_status():
        session_id = request.args.get('session_id')
        response = requests.get(
            f'https://api.momentpay.net/collect/payment_sessions/{session_id}',
            headers={'Authorization': f'Bearer {SECRET_KEY}'},
        )
        session = response.json()
        return jsonify({'status': session['status'], **session})
    ```
  </Tab>

  <Tab title="Go">
    ```go server.go theme={"system"}
    package main

    import (
        "bytes"
        "encoding/json"
        "fmt"
        "io"
        "net/http"
    )

    const secretKey = "sk_test_your_secret_key"
    const momentAPI = "https://api.momentpay.net"

    // Called by fetchClientToken when checkout.launch() is triggered
    func createPaymentSession(w http.ResponseWriter, r *http.Request) {
        payload, _ := json.Marshal(map[string]any{
            "amount":   10000, // Amount in cents (e.g., R100.00)
            "currency": "ZAR",
            "type":     "one_time",
            "options": map[string]any{
                "checkout_options": map[string]any{
                    "presentation_mode": map[string]string{"mode": "embedded"},
                    "success_url":       "https://yoursite.com/checkout?session_id={SESSION_ID}", // required for redirect payments
                },
            },
        })

        req, _ := http.NewRequest("POST", momentAPI+"/collect/payment_sessions", bytes.NewBuffer(payload))
        req.Header.Set("Authorization", "Bearer "+secretKey)
        req.Header.Set("Content-Type", "application/json")

        resp, _ := http.DefaultClient.Do(req)
        defer resp.Body.Close()

        var result map[string]any
        json.NewDecoder(resp.Body).Decode(&result)

        w.Header().Set("Content-Type", "application/json")
        json.NewEncoder(w).Encode(map[string]any{"client_token": result["client_token"]})
    }

    // Called by fetchSessionStatus after customer returns from a redirect payment.
    // The SDK extracts session_id from the URL (?session_id=abc123) and passes it here.
    func getSessionStatus(w http.ResponseWriter, r *http.Request) {
        sessionID := r.URL.Query().Get("session_id")

        req, _ := http.NewRequest("GET", fmt.Sprintf("%s/collect/payment_sessions/%s", momentAPI, sessionID), nil)
        req.Header.Set("Authorization", "Bearer "+secretKey)

        resp, _ := http.DefaultClient.Do(req)
        defer resp.Body.Close()

        body, _ := io.ReadAll(resp.Body)
        w.Header().Set("Content-Type", "application/json")
        w.Write(body)
    }

    func main() {
        http.HandleFunc("/api/create-payment-session", createPaymentSession)
        http.HandleFunc("/api/session-status", getSessionStatus)
        http.ListenAndServe(":8080", nil)
    }
    ```
  </Tab>

  <Tab title="Firebase">
    ```javascript functions/index.js theme={"system"}
    const { onCall } = require('firebase-functions/v2/https');
    const axios = require('axios');

    // Called by fetchClientToken — creates a payment session
    exports.createPaymentSession = onCall(async (request) => {
      const { amount, currency } = request.data;
      const { data } = await axios.post(
        'https://api.momentpay.net/collect/payment_sessions',
        {
          amount,
          currency,
          type: 'one_time',
          options: {
            checkout_options: {
              presentation_mode: { mode: 'embedded' },
              success_url: 'https://yoursite.com/checkout?session_id={SESSION_ID}',
            },
          },
        },
        {
          headers: {
            Authorization: `Bearer ${process.env.MOMENT_SECRET_KEY}`,
            'Content-Type': 'application/json',
          },
        }
      );
      return { client_token: data.client_token };
    });

    // Called by fetchSessionStatus — retrieves session status after redirect
    exports.getSessionStatus = onCall(async (request) => {
      const { sessionId } = request.data;
      const { data } = await axios.get(
        `https://api.momentpay.net/collect/payment_sessions/${sessionId}`,
        { headers: { Authorization: `Bearer ${process.env.MOMENT_SECRET_KEY}` } }
      );
      return { status: data.status, ...data };
    });
    ```
  </Tab>

  <Tab title="Supabase">
    ```typescript supabase/functions/create-payment-session/index.ts theme={"system"}
    import { serve } from 'https://deno.land/std/http/server.ts';

    // Invoked by fetchClientToken
    serve(async () => {
      const response = await fetch('https://api.momentpay.net/collect/payment_sessions', {
        method: 'POST',
        headers: {
          Authorization: `Bearer ${Deno.env.get('MOMENT_SECRET_KEY')}`,
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          amount: 10000,
          currency: 'ZAR',
          type: 'one_time',
          options: {
            checkout_options: {
              presentation_mode: { mode: 'embedded' },
              success_url: 'https://yoursite.com/checkout?session_id={SESSION_ID}',
            },
          },
        }),
      });
      const { client_token } = await response.json();
      return new Response(JSON.stringify({ client_token }), {
        headers: { 'Content-Type': 'application/json' },
      });
    });
    ```

    ```typescript supabase/functions/get-session-status/index.ts theme={"system"}
    import { serve } from 'https://deno.land/std/http/server.ts';

    // Invoked by fetchSessionStatus
    serve(async (req) => {
      const { session_id } = await req.json();
      const response = await fetch(
        `https://api.momentpay.net/collect/payment_sessions/${session_id}`,
        { headers: { Authorization: `Bearer ${Deno.env.get('MOMENT_SECRET_KEY')}` } }
      );
      const session = await response.json();
      return new Response(JSON.stringify({ status: session.status, ...session }), {
        headers: { 'Content-Type': 'application/json' },
      });
    });
    ```
  </Tab>

  <Tab title="Lambda">
    ```javascript handler.js theme={"system"}
    // Called by fetchClientToken — POST /api/create-payment-session
    exports.createPaymentSession = async (event) => {
      const response = await fetch('https://api.momentpay.net/collect/payment_sessions', {
        method: 'POST',
        headers: {
          Authorization: `Bearer ${process.env.MOMENT_SECRET_KEY}`,
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          amount: 10000,
          currency: 'ZAR',
          type: 'one_time',
          options: {
            checkout_options: {
              presentation_mode: { mode: 'embedded' },
              success_url: 'https://yoursite.com/checkout?session_id={SESSION_ID}',
            },
          },
        }),
      });
      const { client_token } = await response.json();
      return {
        statusCode: 200,
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ client_token }),
      };
    };

    // Called by fetchSessionStatus — GET /api/session-status?session_id=abc123
    exports.getSessionStatus = async (event) => {
      const sessionId = event.queryStringParameters?.session_id;
      const response = await fetch(
        `https://api.momentpay.net/collect/payment_sessions/${sessionId}`,
        { headers: { Authorization: `Bearer ${process.env.MOMENT_SECRET_KEY}` } }
      );
      const session = await response.json();
      return {
        statusCode: 200,
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ status: session.status, ...session }),
      };
    };
    ```
  </Tab>
</Tabs>

**Frontend — wrap your app with `MomentProvider` + `MomentCheckoutProvider`:**

<Tabs>
  <Tab title="Fetch (Native)">
    ```jsx App.jsx theme={"system"}
    import { MomentProvider, MomentCheckoutProvider } from 'momentpay';

    function App() {
      return (
        <MomentProvider clientKey="pk_your_key_here">
          <MomentCheckoutProvider
            // SDK calls this when checkout.launch() is invoked to get a fresh session token
            fetchClientToken={async () => {
              const response = await fetch('/api/create-payment-session', { method: 'POST' });
              const data = await response.json();
              return data.client_token;
            }}

            // SDK calls this automatically when ?session_id=... is detected in the URL.
            // Happens after a customer returns from a redirect-based payment (3DS, bank redirect, EFT).
            // Must return: { status: 'completed' | 'failed' | 'expired' | 'cancelled', ... }
            fetchSessionStatus={async (sessionId) => {
              const response = await fetch('/api/session-status?session_id=' + sessionId);
              return response.json();
            }}

            onSuccess={(result) => {
              console.log('Payment successful!', result);
              // Redirect to success page or update UI
            }}
            onFailure={(result) => {
              console.log('Payment failed', result);
              // Show error message
            }}
            onCancel={(result) => {
              console.log('Payment cancelled', result);
              // Allow user to retry
            }}
            onAttemptFailed={(result) => {
              console.log('Payment attempt failed', result);
              // User can retry within the checkout
            }}
            onExpired={(result) => {
              console.log('Payment session expired', result);
              // Request a new session and retry
            }}
          >
            <YourApp />
          </MomentCheckoutProvider>
        </MomentProvider>
      );
    }

    export default App;
    ```
  </Tab>

  <Tab title="Axios">
    ```jsx App.jsx theme={"system"}
    import { MomentProvider, MomentCheckoutProvider } from 'momentpay';
    import axios from 'axios';

    function App() {
      return (
        <MomentProvider clientKey="pk_your_key_here">
          <MomentCheckoutProvider
            // SDK calls this when checkout.launch() is invoked to get a fresh session token
            fetchClientToken={async () => {
              const { data } = await axios.post('/api/create-payment-session');
              return data.client_token;
            }}

            // SDK calls this automatically when ?session_id=... is detected in the URL.
            // Happens after a customer returns from a redirect-based payment (3DS, bank redirect, EFT).
            // Must return: { status: 'completed' | 'failed' | 'expired' | 'cancelled', ... }
            fetchSessionStatus={async (sessionId) => {
              const { data } = await axios.get('/api/session-status', {
                params: { session_id: sessionId },
              });
              return data;
            }}

            onSuccess={(result) => {
              console.log('Payment successful!', result);
              // Redirect to success page or update UI
            }}
            onFailure={(result) => {
              console.log('Payment failed', result);
              // Show error message
            }}
            onCancel={(result) => {
              console.log('Payment cancelled', result);
              // Allow user to retry
            }}
            onAttemptFailed={(result) => {
              console.log('Payment attempt failed', result);
              // User can retry within the checkout
            }}
            onExpired={(result) => {
              console.log('Payment session expired', result);
              // Request a new session and retry
            }}
          >
            <YourApp />
          </MomentCheckoutProvider>
        </MomentProvider>
      );
    }

    export default App;
    ```
  </Tab>

  <Tab title="Firebase">
    ```jsx App.jsx theme={"system"}
    import { MomentProvider, MomentCheckoutProvider } from 'momentpay';
    import { getFunctions, httpsCallable } from 'firebase/functions';

    const functions = getFunctions();
    const createPaymentSession = httpsCallable(functions, 'createPaymentSession');
    const getSessionStatus = httpsCallable(functions, 'getSessionStatus');

    function App() {
      return (
        <MomentProvider clientKey="pk_your_key_here">
          <MomentCheckoutProvider
            // SDK calls this when checkout.launch() is invoked to get a fresh session token
            fetchClientToken={async () => {
              const { data } = await createPaymentSession({ amount: 10000, currency: 'ZAR' });
              return data.client_token;
            }}

            // SDK calls this automatically when ?session_id=... is detected in the URL.
            // Happens after a customer returns from a redirect-based payment (3DS, bank redirect, EFT).
            // Must return: { status: 'completed' | 'failed' | 'expired' | 'cancelled', ... }
            fetchSessionStatus={async (sessionId) => {
              const { data } = await getSessionStatus({ sessionId });
              return data;
            }}

            onSuccess={(result) => {
              console.log('Payment successful!', result);
              // Redirect to success page or update UI
            }}
            onFailure={(result) => {
              console.log('Payment failed', result);
              // Show error message
            }}
            onCancel={(result) => {
              console.log('Payment cancelled', result);
              // Allow user to retry
            }}
            onAttemptFailed={(result) => {
              console.log('Payment attempt failed', result);
              // User can retry within the checkout
            }}
            onExpired={(result) => {
              console.log('Payment session expired', result);
              // Request a new session and retry
            }}
          >
            <YourApp />
          </MomentCheckoutProvider>
        </MomentProvider>
      );
    }

    export default App;
    ```
  </Tab>

  <Tab title="Supabase">
    ```jsx App.jsx theme={"system"}
    import { MomentProvider, MomentCheckoutProvider } from 'momentpay';
    import { createClient } from '@supabase/supabase-js';

    const supabase = createClient('https://your-project.supabase.co', 'your-anon-key');

    function App() {
      return (
        <MomentProvider clientKey="pk_your_key_here">
          <MomentCheckoutProvider
            // SDK calls this when checkout.launch() is invoked to get a fresh session token.
            // Invokes a Supabase Edge Function named 'create-payment-session'.
            fetchClientToken={async () => {
              const { data, error } = await supabase.functions.invoke('create-payment-session');
              if (error) throw error;
              return data.client_token;
            }}

            // SDK calls this automatically when ?session_id=... is detected in the URL.
            // Happens after a customer returns from a redirect-based payment (3DS, bank redirect, EFT).
            // Must return: { status: 'completed' | 'failed' | 'expired' | 'cancelled', ... }
            fetchSessionStatus={async (sessionId) => {
              const { data, error } = await supabase.functions.invoke('get-session-status', {
                body: { session_id: sessionId },
              });
              if (error) throw error;
              return data;
            }}

            onSuccess={(result) => {
              console.log('Payment successful!', result);
              // Redirect to success page or update UI
            }}
            onFailure={(result) => {
              console.log('Payment failed', result);
              // Show error message
            }}
            onCancel={(result) => {
              console.log('Payment cancelled', result);
              // Allow user to retry
            }}
            onAttemptFailed={(result) => {
              console.log('Payment attempt failed', result);
              // User can retry within the checkout
            }}
            onExpired={(result) => {
              console.log('Payment session expired', result);
              // Request a new session and retry
            }}
          >
            <YourApp />
          </MomentCheckoutProvider>
        </MomentProvider>
      );
    }

    export default App;
    ```
  </Tab>

  <Tab title="Lambda">
    ```jsx App.jsx theme={"system"}
    import { MomentProvider, MomentCheckoutProvider } from 'momentpay';

    const API_BASE = 'https://your-api-id.execute-api.us-east-1.amazonaws.com/prod';

    function App() {
      return (
        <MomentProvider clientKey="pk_your_key_here">
          <MomentCheckoutProvider
            // SDK calls this when checkout.launch() is invoked to get a fresh session token.
            // Calls your Lambda function via API Gateway.
            fetchClientToken={async () => {
              const response = await fetch(`${API_BASE}/create-payment-session`, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
              });
              const data = await response.json();
              return data.client_token;
            }}

            // SDK calls this automatically when ?session_id=... is detected in the URL.
            // Happens after a customer returns from a redirect-based payment (3DS, bank redirect, EFT).
            // Must return: { status: 'completed' | 'failed' | 'expired' | 'cancelled', ... }
            fetchSessionStatus={async (sessionId) => {
              const response = await fetch(
                `${API_BASE}/session-status?session_id=${sessionId}`
              );
              return response.json();
            }}

            onSuccess={(result) => {
              console.log('Payment successful!', result);
              // Redirect to success page or update UI
            }}
            onFailure={(result) => {
              console.log('Payment failed', result);
              // Show error message
            }}
            onCancel={(result) => {
              console.log('Payment cancelled', result);
              // Allow user to retry
            }}
            onAttemptFailed={(result) => {
              console.log('Payment attempt failed', result);
              // User can retry within the checkout
            }}
            onExpired={(result) => {
              console.log('Payment session expired', result);
              // Request a new session and retry
            }}
          >
            <YourApp />
          </MomentCheckoutProvider>
        </MomentProvider>
      );
    }

    export default App;
    ```
  </Tab>
</Tabs>

### **2. Open Checkout**

Add `useCheckout` to any component and call `checkout.launch()` to trigger checkout:

```jsx Checkout.jsx theme={"system"}
import { useCheckout } from 'momentpay';

function CheckoutButton() {
  const checkout = useCheckout({
    onSuccess: (result) => {
      console.log('Payment successful!', result);
      // Redirect to success page or update UI
    },
    onFailure: (result) => {
      console.log('Payment failed', result);
      // Show error message
    },
    onCancel: (result) => {
      console.log('Payment cancelled', result);
      // Allow user to retry
    },
    onAttemptFailed: (result) => {
      console.log('Payment attempt failed', result);
      // User can retry within the checkout
    },
  });

  return (
    <button onClick={() => checkout.launch()}>
      Pay Now
    </button>
  );
}

export default CheckoutButton;
```

***

## **API Reference**

### **MomentProvider** *(SDK level)*

Initializes the Moment SDK instance — equivalent to `Moment(clientKey)` in vanilla JS. Provides the SDK context to all product providers nested below it. Must be the outermost Moment wrapper in your app.

| Prop        | Type     | Required | Description                                                                                           |
| ----------- | -------- | -------- | ----------------------------------------------------------------------------------------------------- |
| `clientKey` | `string` | Yes      | Your publishable key (`pk_test_...` or `pk_live_...`). Determines sandbox vs. production environment. |

**Example:**

```jsx theme={"system"}
<MomentProvider clientKey="pk_test_abc123">
  <MomentCheckoutProvider ...>
    <App />
  </MomentCheckoutProvider>
</MomentProvider>
```

***

### **MomentCheckoutProvider** *(checkout product)*

Initializes the `checkout` product — equivalent to `moment.checkout(config)` in vanilla JS. Must be a child of `MomentProvider` and a parent of any component using `useCheckout`.

| Prop                 | Type       | Required | Description                                                                                                                                                                                                         |
| -------------------- | ---------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `fetchClientToken`   | `function` | Yes      | Async function returning `Promise<string>` — a fresh client token. Called by the SDK when `checkout.launch()` is invoked.                                                                                           |
| `fetchSessionStatus` | `function` | No       | Async function receiving a `sessionId` string, returning `Promise<{ status: string, ... }>`. Called when a `session_id` is found in the URL after a redirect-based payment. Required only if you use redirect mode. |
| `onSuccess`          | `function` | No       | Global callback fired when payment succeeds (both embedded and redirect flows)                                                                                                                                      |
| `onFailure`          | `function` | No       | Global callback fired when payment fails                                                                                                                                                                            |
| `onCancel`           | `function` | No       | Global callback fired when user cancels                                                                                                                                                                             |
| `onAttemptFailed`    | `function` | No       | Global callback fired when a payment attempt fails (user can retry)                                                                                                                                                 |
| `onExpired`          | `function` | No       | Global callback fired when the payment session expires                                                                                                                                                              |

**Example:**

```jsx theme={"system"}
<MomentProvider clientKey="pk_test_abc123">
  <MomentCheckoutProvider
    fetchClientToken={async () => {
      const res = await fetch('/api/create-payment-session', { method: 'POST' });
      const data = await res.json();
      return data.client_token;
    }}
    // Only needed if using redirect mode
    fetchSessionStatus={async (sessionId) => {
      const res = await fetch('/api/session-status?session_id=' + sessionId);
      return res.json();
      // Must return: { status: 'completed' | 'failed' | 'expired' | 'cancelled', ... }
    }}
    onSuccess={(result) => console.log('Success:', result)}
    onFailure={(result) => console.log('Failed:', result)}
    onCancel={(result) => console.log('Cancelled:', result)}
    onAttemptFailed={(result) => console.log('Attempt failed:', result)}
    onExpired={(result) => console.log('Expired:', result)}
  >
    <App />
  </MomentCheckoutProvider>
</MomentProvider>
```

***

### **useCheckout(config)**

React hook that returns a `checkout` object for triggering checkout from any component. Must be used inside `MomentCheckoutProvider`. Callbacks defined here are component-level and fire alongside any global callbacks on `MomentCheckoutProvider`.

| Parameter         | Type       | Required | Description                                                                  |
| ----------------- | ---------- | -------- | ---------------------------------------------------------------------------- |
| `onSuccess`       | `function` | No       | Component-level callback fired when payment succeeds                         |
| `onFailure`       | `function` | No       | Component-level callback fired when payment fails                            |
| `onCancel`        | `function` | No       | Component-level callback fired when user cancels                             |
| `onAttemptFailed` | `function` | No       | Component-level callback fired when a payment attempt fails (user can retry) |
| `onExpired`       | `function` | No       | Component-level callback fired when the payment session expires              |

Returns a `checkout` object — `checkout.launch()`, `checkout.close()`, and `checkout.submit()`.

***

### **checkout.launch(options)**

Opens the payment checkout. Internally calls `fetchClientToken` from `MomentCheckoutProvider` to retrieve a fresh token, then opens checkout. The display variant (`modal` or `inline`) is determined from the JWT payload `variant` claim, which is set when creating the session on your backend.

| Parameter           | Type                  | Required | Description                                           |
| ------------------- | --------------------- | -------- | ----------------------------------------------------- |
| `options.container` | `string` or `Element` | No       | CSS selector or DOM element — for inline variant only |

Returns a `Promise` that resolves with `{ checkoutUrl, variant }` once the checkout is open.

**Examples:**

```jsx theme={"system"}
// Open checkout — SDK fetches token via fetchClientToken from MomentCheckoutProvider
checkout.launch();

// Inline checkout with custom container
checkout.launch({ container: '#checkout-container' });
```

***

### **checkout.close()**

Manually closes the modal or inline checkout.

```jsx theme={"system"}
checkout.close();
```

***

### **checkout.submit()**  `Coming soon`

Triggers payment submission from outside the checkout UI — useful when you want an external "Pay" button rather than relying on the checkout's built-in submit. Only available for the `inline` variant.

```jsx theme={"system"}
// Wire up an external Pay button
<button onClick={() => checkout.submit()}>Pay</button>
```

***

## Callback Strategies

The Moment React SDK offers flexible callback handling at both the **global** (`MomentCheckoutProvider`) and **component** (`useCheckout` hook) levels. Understanding when to use each approach helps you write cleaner, more maintainable code.

### Global Callbacks (MomentCheckoutProvider Level)

Define callbacks once at the `MomentCheckoutProvider` level to apply consistent behavior across your entire application.

**✅ Best for:**

* App-wide analytics tracking
* Global error logging
* Consistent redirect patterns
* Shared notification systems

```jsx App.jsx theme={"system"}
import { MomentProvider, MomentCheckoutProvider } from 'momentpay';
import { useRouter } from 'next/router';
import { trackEvent } from './analytics';
import { showNotification } from './notifications';

function App() {
  const router = useRouter();

  return (
    <MomentProvider clientKey="pk_your_key_here">
      <MomentCheckoutProvider
        fetchClientToken={async () => {
          const res = await fetch('/api/create-payment-session', { method: 'POST' });
          const data = await res.json();
          return data.client_token;
        }}
        onSuccess={(result) => {
          // Global success behavior
          trackEvent('payment_success', { transaction_id: result.transaction_id });
          showNotification('Payment successful!', 'success');
          router.push('/success');
        }}
        onFailure={(result) => {
          // Global failure behavior
          trackEvent('payment_failure', { error: result.error });
          showNotification('Payment failed. Please try again.', 'error');
        }}
        onCancel={() => {
          trackEvent('payment_cancelled');
        }}
      >
        <YourApp />
      </MomentCheckoutProvider>
    </MomentProvider>
  );
}
```

### Component-Level Callbacks (useCheckout Hook)

Override or supplement global callbacks with component-specific behavior.

**✅ Best for:**

* Component-specific state updates
* Different flows for different payment contexts
* Local UI feedback
* Context-aware error handling

```jsx ProductCheckout.jsx theme={"system"}
function ProductCheckout({ product }) {
  const [status, setStatus] = useState('idle');

  const checkout = useCheckout({
    onSuccess: (result) => {
      // Component-specific behavior
      setStatus('success');
      // This will ALSO trigger the global onSuccess from MomentCheckoutProvider
    },
    onFailure: (result) => {
      setStatus('error');
      // This will ALSO trigger the global onFailure from MomentCheckoutProvider
    }
  });

  // ... rest of component
}
```

### Callback Precedence

**Important:** Component-level callbacks **do not override** global callbacks. Both will fire:

1. **Component-level callback** fires first (if defined)
2. **Global callback** fires second (if defined)

This allows you to handle both component-specific and app-wide logic.

```jsx theme={"system"}
// Global callback (in MomentCheckoutProvider)
onSuccess: (result) => {
  console.log('Global: Tracking analytics');
  trackEvent('payment_success', result);
}

// Component callback (in useCheckout)
onSuccess: (result) => {
  console.log('Component: Updating local state');
  setPaymentComplete(true);
}

// Both will fire when payment succeeds:
// Output:
// "Component: Updating local state"
// "Global: Tracking analytics"
```

### Example: Multi-Level Callback Architecture

```jsx App.jsx theme={"system"}
import { MomentProvider, MomentCheckoutProvider } from 'momentpay';
import { analytics, errorTracking, notifications } from './services';

function App() {
  return (
    <MomentProvider clientKey={process.env.NEXT_PUBLIC_MOMENT_KEY}>
      <MomentCheckoutProvider
        fetchClientToken={async () => {
          const res = await fetch('/api/create-payment-session', { method: 'POST' });
          const data = await res.json();
          return data.client_token;
        }}
        // Global: Cross-cutting concerns
        onSuccess={(result) => {
          analytics.track('payment_completed', {
            transaction_id: result.transaction_id,
            payment_method: result.payment_method
          });
        }}
        onFailure={(result) => {
          errorTracking.capture(result.error);
          notifications.error('Payment failed');
        }}
        onCancel={() => {
          analytics.track('payment_cancelled');
        }}
      >
        <AppContent />
      </MomentCheckoutProvider>
    </MomentProvider>
  );
}
```

```jsx SubscriptionPage.jsx theme={"system"}
function SubscriptionPage() {
  const [subscriptionActive, setSubscriptionActive] = useState(false);
  const router = useRouter();

  // Component: Subscription-specific logic
  const checkout = useCheckout({
    onSuccess: (result) => {
      // Update subscription state
      setSubscriptionActive(true);
      // Navigate to dashboard
      router.push('/dashboard');
      // Show plan-specific message
      notifications.success(`Welcome to ${selectedPlan.name}!`);
    },
    onFailure: () => {
      // Show retry option
      setShowRetryModal(true);
    }
  });

  // ... rest of component
}
```

***

## Advanced Usage

### Custom Hook for Payment Logic

```tsx hooks/usePayment.ts theme={"system"}
import { useState, useCallback } from 'react';
import { useCheckout } from 'momentpay';

export function usePayment() {
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [success, setSuccess] = useState(false);

  const checkout = useCheckout({
    onSuccess: () => {
      setSuccess(true);
      setLoading(false);
      setError(null);
    },
    onFailure: (result) => {
      setError(result.error?.message || 'Payment failed');
      setLoading(false);
    },
    onCancel: () => {
      setLoading(false);
      setError(null);
    }
  });

  const processPayment = useCallback(async () => {
    setLoading(true);
    setError(null);
    setSuccess(false);

    try {
      // SDK calls fetchClientToken from MomentCheckoutProvider automatically
      await checkout.launch();
    } catch (err) {
      setError(err instanceof Error ? err.message : 'Unknown error');
      setLoading(false);
    }
  }, [checkout]);

  return { processPayment, loading, error, success };
}
```

### Using the Custom Hook

```tsx ProductCheckout.tsx theme={"system"}
import { usePayment } from './hooks/usePayment';

function ProductCheckout({ product }) {
  const { processPayment, loading, error, success } = usePayment();

  if (success) {
    return <div className="success">Purchase successful!</div>;
  }

  return (
    <div>
      <h2>{product.name}</h2>
      <p>R{product.price.toFixed(2)}</p>

      {error && <div className="error">{error}</div>}

      <button onClick={processPayment} disabled={loading}>
        {loading ? 'Processing...' : 'Buy Now'}
      </button>
    </div>
  );
}
```

***

## Security Best Practices

1. **Never expose your secret key** (`sk_...`) in frontend code
2. **Always create sessions server-side** using your secret key
3. **Validate webhooks** to confirm payment status server-side
4. **Use HTTPS** for all pages that include the SDK
5. **Handle token expiry** by requesting a new token if needed

***

## Next Steps

* View **[Sample Applications](/libraries-and-sdks/web/samples)** for end-to-end examples
* Create your first **[Payment Session](/api-reference/collect/payment_sessions/create)**
* Configure **[Webhooks](/api-reference/webhooks/overview)**
* Test your integration in **[Sandbox mode](/api-reference/getting-started/testing)**
