# Card & ACH Integration

Accept credit card and ACH payments using the Widget SDK. This guide walks through the full integration from loading the SDK to processing a payment.

## How It Works

The SDK renders a secure payment form on your checkout page. Your customer enters their payment details directly into the payment form; your page never touches sensitive data. When you're ready, call `tokenize()` to exchange those details for a secure token, then send that token to your backend for processing via the Payments API. For complete reference, see [SDK reference](/docs/ui-sdk-reference).

## Step 1: Load the SDK

Include the Widget script from the NPS CDN. This exposes the `Widget` constructor globally.

```html
<script src="https://cdn.<env>.nelnetpay.com/sdk/v1.0.0/widget-sdk.js"></script>
```

## Step 2: Add a Container

Add an empty container element where the payment form will be rendered.

```html
<div id="payment-form"></div>
```

## Step 3: Initialize the SDK

Create a `Widget` instance. The session token is provided separately via `updateSessionToken()` before calling `tokenize()`.

```js
const wg = new Widget({
  // ...additional options; see UI SDK Reference

  onTokenizationComplete: (result) => {
    // Forward result.token to your backend
  },

  onError: (error) => {
  }
});
```

## Step 4: Mount the Payment Form

Call `mount()` to render the payment form. Pass the payment method and any styling or display options.

```js
wg.mount('#payment-form', {
  paymentMethod: 'card',
  styling: {
    theme: 'light',
    labelStyle: 'floating',
    fieldLayout: 'stacked',
    fontFamily: 'Inter',
    borderRadius: 4,
    colors: {
      border: '#C4C8CC',
      focusBorder: '#3B82F6'
    }
  }
});
```

You can switch between card and ACH without remounting the form:

```js
wg.updatePaymentMethod('ach');
wg.updatePaymentMethod('card');
```

## Step 5: Tokenize

When your customer is ready to pay, set a fresh session token via `updateSessionToken()` and then call `tokenize()` with the payment details. The SDK validates the form and returns a secure token.

**Card Example:**

```js
const sessionToken = await fetchSessionToken(); // your server-side endpoint
wg.updateSessionToken(sessionToken);

const result = await wg.tokenize({
  amount: 125.00,
  accountHolderName: 'Jane Smith',
  billingAddress: {
    addressLine1: '123 Main St',
    addressLine2: 'Suite 100',   // optional
    city: 'Omaha',
    state: 'NE',
    postalCode: '68102',
    country: 'US'
  }
});
```

**ACH Example:**

```js
const sessionToken = await fetchSessionToken();
wg.updateSessionToken(sessionToken);

const result = await wg.tokenize({
  amount: 50.00,
  accountHolderName: 'Jane Smith',
  accountType: 'checking',
  billingAddress: {
    addressLine1: '456 Oak Ave',
    addressLine2: 'Apt 2B',      // optional
    city: 'Omaha',
    state: 'NE',
    postalCode: '68102',
    country: 'US'
  }
});
```

On success, the result contains a `token` (plus other details). On failure, `onError` is invoked with an error code and message.

## Step 6: Process the Payment

Send the `token` to your backend. Your server submits it to the [Payments API](https://docs.nelnetpay.com/apis/osi-payments-api/other/processpaymentrequest) to process the payment. The token is the only value required from the SDK.

## Optional: Field Options and Form Events

The card and ACH mounts support additional field options and events for a richer checkout experience. All are optional. See [Card and ACH Field Options](/docs/ui-sdk-reference#card-and-ach-field-options) in the SDK reference for full details.

**Field options** (passed to `mount()`):

- `supportedCardBrands`: Restricts card entry to the brands you accept (e.g. `['visa', 'mastercard']`). Cards of other brands show a localized inline error, and their icons are hidden.
- `requireAccountConfirmation`: For ACH, renders a "Confirm Account Number" field that must match before tokenizing. Paste is blocked, and the confirm value never leaves the payment form.
- `helpIcons`: Renders a keyboard-accessible `?` icon beside chosen fields (e.g. `['securityCode']`). Clicks surface via `onHelpClick`; your page shows the help content.
- `locale`: Sets the payer-facing language of the payment form to `en` (default) or `es`.


**Form events** (passed to the constructor):

- `onFormReady`: Fires with `{ ready: true }` when every field is complete and valid, and `{ ready: false }` if the form regresses. Use it to enable your Pay button.
- `onHelpClick`: Fires with `{ field }` when a help icon is clicked.
- `onFieldErrors`: Fires when `tokenize()` is blocked by validation and returns every errored field with its localized message. Pair with `focusField(field)` to jump the payer to a field; the SDK also auto-scrolls to the first errored field.


```js
const wg = new Widget({
  onTokenizationComplete: (result) => {
    // Forward result.token to your backend
  },

  onFormReady: ({ ready }) => {
    document.getElementById('pay-btn').disabled = !ready;
  },

  onHelpClick: ({ field }) => {
    showHelpModal(field);
  },

  onFieldErrors: ({ errors }) => {
    renderErrorSummary(errors); // [{ field, message }]
  }
});

wg.mount('#payment-form', {
  paymentMethod: 'card',
  supportedCardBrands: ['visa', 'mastercard'],
  helpIcons: ['securityCode'],
  locale: 'es'
});
```

## Complete Example

```html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Checkout</title>
</head>
<body>
  <h1>Checkout</h1>
  <div id="payment-form"></div>
  <button id="pay-btn">Pay $125.00</button>

  <script src="https://cdn.nelnetpay.com/sdk/v1.0.0/widget-sdk.js"></script>
  <script>
    async function fetchSessionToken() {
      const res = await fetch('/api/session-token');
      const data = await res.json();
      return data.sessionToken;
    }

    const wg = new Widget({
      onTokenizationComplete: (result) => {
        // Forward result.token to your backend
      },

      onError: (error) => {
        // Handle errors
      }
    });

    wg.mount('#payment-form', {
      paymentMethod: 'card',
      styling: {
        theme: 'light',
        labelStyle: 'floating',
        fieldLayout: 'stacked',
        borderRadius: 4
      }
    });

    document.getElementById('pay-btn').addEventListener('click', async () => {
      const sessionToken = await fetchSessionToken();
      wg.updateSessionToken(sessionToken);

      await wg.tokenize({
        amount: 125.00,
        accountHolderName: 'Jane Smith',
        billingAddress: {
          addressLine1: '123 Main St',
          addressLine2: 'Suite 100',
          city: 'Omaha',
          state: 'NE',
          postalCode: '68102',
          country: 'US'
        }
      });
    });
  </script>
</body>
</html>
```

## Key Idea

The SDK securely collects payment details in the browser and returns a token. Your backend uses that token to process payments; sensitive data never touches your server.