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

# Javascript

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

## Overview

The **Moment JavaScript SDK** enables seamless payment integration into any web application. Whether you need a modal overlay or inline embedded checkout — the SDK handles it all with a simple, unified API.

Built with **vanilla JavaScript** (no dependencies), the SDK works with any frontend framework or plain HTML pages.

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

* **Zero Dependencies** – Pure vanilla JavaScript, works everywhere
* **Flexible Display Options** – Choose between modal overlay or inline embedded checkout
* **Flexible Callbacks** – Handle payment success, failure, cancellation, retry, and expiry events
* **Cross-Origin Safe** – Secure iframe communication via `postMessage`
* **Environment Detection** – Automatic sandbox / production switching based on client key
* **PCI Compliant** – All sensitive payment data handled 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 Your App
    participant Your Backend
    participant Moment API
    participant Moment Checkout
    participant 3DS / Bank Page

    Note over Your App: Page loads — Moment(key) + moment.checkout() called
    Your App->>Your App: Registers fetchClientToken, fetchSessionStatus & event callbacks

    Customer->>Your App: Clicks "Pay Now"
    Your 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-->>Your App: Redirects to your success_url?session_id=abc123
    Note over Your App: Page loads — moment.checkout() detects session_id in URL
    Your App->>Your Backend: fetchSessionStatus('abc123') fires
    Your Backend->>Moment API: GET /collect/payment_sessions/abc123
    Moment API-->>Your Backend: Returns session status
    Your Backend-->>Your App: Returns { status: 'completed', ... }
    Your App->>Customer: onSuccess(result) fires — shows confirmation
```

1. **Page loads** — `Moment(clientKey)` creates an SDK instance; `moment.checkout()` is called to create a checkout instance with your `fetchClientToken`, `fetchSessionStatus`, and event callbacks (`onSuccess`, `onFailure`, etc.)
2. **Customer clicks Pay** — `checkout.launch()` is called
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** — `moment.checkout()` 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**

### **Script Tag (Recommended)**

```html theme={"system"}
<script src="https://dy7o1iqfiixsw.cloudfront.net/moment-sdk/js/moment.js"></script>
```

### **Self-Hosted**

Download `moment.js` and include it in your project:

```html theme={"system"}
<script src="/path/to/moment.js"></script>
```

### **Module Import (Coming Soon)**

```javascript theme={"system"}
import Moment from '@momentpay';
```

***

## **Quick Start**

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

Call `Moment(clientKey)` once when your page loads to create an SDK instance. Then call `moment.checkout()` with your fetch callbacks and event handlers — it returns a checkout instance, and the SDK calls your callbacks 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 — initialize once on page load:**

<Tabs>
  <Tab title="Fetch (Native)">
    ```javascript app.js theme={"system"}
    const moment = Moment('pk_your_key_here');

    const checkout = moment.checkout({
      // 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
      },
    });
    ```
  </Tab>

  <Tab title="Axios">
    ```javascript app.js theme={"system"}
    import axios from 'axios';

    const moment = Moment('pk_your_key_here');

    const checkout = moment.checkout({
      // 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
      },
    });
    ```
  </Tab>

  <Tab title="Firebase">
    ```javascript app.js theme={"system"}
    import { getFunctions, httpsCallable } from 'firebase/functions';

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

    const moment = Moment('pk_your_key_here');

    const checkout = moment.checkout({
      // 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
      },
    });
    ```
  </Tab>

  <Tab title="Supabase">
    ```javascript app.js theme={"system"}
    import { createClient } from '@supabase/supabase-js';

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

    const checkout = moment.checkout({
      // 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
      },
    });
    ```
  </Tab>
</Tabs>

### **2. Open Checkout**

```javascript app.js theme={"system"}
// SDK calls fetchClientToken, gets a fresh token, then opens checkout
checkout.launch();
```

***

## **API Reference**

### **Moment(clientKey)**

Factory function — creates an isolated SDK instance scoped to your publishable key. Returns an object with product namespaces (`checkout`, and more in the future).

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

Throws immediately if `clientKey` is invalid.

```javascript theme={"system"}
const moment = Moment('pk_test_abc123');
```

***

### **moment.checkout(config)**

Creates and returns a checkout instance for this SDK. Detects any `?session_id=` in the current URL and automatically calls `fetchSessionStatus` if provided. Returns a `checkout` object with `launch`, `close`, and `submit` methods.

| Parameter            | 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       | Callback fired when payment succeeds (both embedded and redirect flows)                                                                                                                                             |
| `onFailure`          | `function` | No       | Callback fired when payment fails                                                                                                                                                                                   |
| `onCancel`           | `function` | No       | Callback fired when user cancels                                                                                                                                                                                    |
| `onAttemptFailed`    | `function` | No       | Callback fired when a payment attempt fails (user can retry)                                                                                                                                                        |
| `onExpired`          | `function` | No       | Callback fired when the payment session expires                                                                                                                                                                     |

**Example:**

```javascript theme={"system"}
const moment = Moment('pk_test_abc123');

const checkout = moment.checkout({
  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),
});
```

***

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

Opens the payment checkout. Internally calls your `fetchClientToken` function 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:**

```javascript theme={"system"}
// Open checkout — SDK fetches token via your fetchClientToken callback
checkout.launch();

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

***

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

Manually closes the modal or inline checkout.

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

***

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

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

```javascript theme={"system"}
// Wire up an external Pay button
document.getElementById('payBtn').addEventListener('click', () => {
  checkout.submit();
});
```

***

## **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)**
