# Payment Widget

div
strong
Key Idea:
br
The Payment Widget is a 
em
pre-built, embeddable payment UI
that a partner drops onto a page with a small JavaScript SDK. It delivers the full payer experience (invoice selection, amount entry, saved methods, auto-pay, receipts), while all sensitive card and bank account data is captured inside a 
strong
separate secure tokenization iframe
the partner page never touches.

The Payment Widget enables partners to accept payments without building a payment UI or handling sensitive card and bank account data. Integration requires only a single script, a short-lived session token, and a call to `mount()`. The widget then renders the appropriate experience for the selected mode and processes the payment through its backend.

## What the Payment Widget Is (and Is Not)

**The Payment Widget *is*:**

- A **frontend SDK + hosted UI** (JavaScript + an iframe) embedded on a partner page
- A **full payer experience** across several modes (one-time, invoice, account balance, payer management, auto-pay)
- A **secure, PCI-focused** integration: card and bank account entry happens inside a dedicated tokenization iframe, not on the partner page


**The Payment Widget is *not*:**

- `payment-widget-service` - that is the **backend-for-frontend (BFF)** the widget talks to (documented separately). The widget never calls core services directly; it only calls the BFF with a session token.
- A payments processor - settlement, gateways, and tokenization happen in downstream services.
- A place where the partner handles `sessionId`, raw card numbers, or bank account numbers - it never sees them.


## How It Works

div
div
div
Partner Page
div
strong
widget-sdk.js
br
Constructs the SDK, supplies a session token, calls 
code
mount()
div
div
Payment Widget (iframe)
div
div
strong
Widget UI
br
Renders the selected mode
      
div
strong
Tokenization iframe
br
Secure card/ACH capture (PCI)
      
div
div
Backend
div
strong
payment-widget-service (BFF)
br
Sessions, config, payment processing
    
div
Orchestrates Profile, Invoice, and Payments behind the scenes.
    
1. The partner page loads `widget-sdk.js` and constructs the SDK with the merchant `entityId` and event callbacks.
2. The partner supplies a short-lived **session token** (obtained server-side) and calls `mount(container, { mode, ... })`.
3. The SDK creates a session through the BFF, loads the widget iframe, and passes the session to it - the partner never handles a `sessionId`.
4. The widget renders the UI for the requested **mode**, capturing any card/ACH data inside the embedded tokenization iframe so raw data never touches the partner page.
5. The widget processes the payment through the BFF and emits lifecycle events (`payment.success`, `payment.failed`, `widget.closed`) back to the partner page.


## Getting Started

Embed and launch a widget in three steps:

```html
<script src="https://cdn.{env}.nelnetpay.com/sdk/{version}/widget-sdk.js"></script>
<div id="payment-container"></div>

<script>
  const widget = new Widget({
    entityId: '1234567890',
    onPaymentComplete: (result) => console.log('paid', result),
    onError: (err) => console.error(err),
    onCancel: () => console.log('closed'),
  });

  // Short-lived token your backend obtains for this payer/session.
  widget.updateSessionToken(sessionToken);

  widget.mount(document.getElementById('payment-container'), {
    mode: 'INVOICE',
    invoiceIds: ['<invoice-id>'],
  });
</script>
```

Call `widget.destroy()` to remove the iframe and listeners when the flow is finished.

div
strong
Important:
The 
code
sessionToken
is short-lived and must be obtained by 
em
your backend
, not embedded in client code. 
code
entityId
is your merchant identifier; the widget never receives or exposes the underlying 
code
sessionId
.

## Widget Modes

The `mode` passed to `mount()` selects the payer experience.

| Mode | What it does | Required inputs |
|  --- | --- | --- |
| **STANDALONE** | One-time payment with optional payer info | none (optional `amount`, `description`, `personId`) |
| **INVOICE** | Pay one or more invoices | at least one of `invoiceIds`, `personId`, or `accountId` |
| **ACCOUNT_BALANCE** | Pay outstanding charges on an account | `accountId` |
| **PAYER_MANAGEMENT** | Manage a payer's saved payment methods | `personId` |
| **AUTOPAY** | Enroll in / manage automatic payments | `personId` + `accountId` |


### STANDALONE

A one-time payment when no invoice is involved. Amount and description come from the session. When `personId` is absent, the widget collects payer details (name, email, phone, address); when present, saved payment methods are offered.

### INVOICE

Pay one or more invoices. If `invoiceIds` are provided, the widget pays only those invoices. Otherwise, the widget retrieves and pays all outstanding invoices for the specified payer or account based on the supplied `personId` and/or `accountId`. Supports per-invoice include/exclude, partial amounts (when enabled), past-due indicators, and a line-item details drawer.

### ACCOUNT_BALANCE

Pay against the outstanding balance on an account (for clients who use billing without formal invoices). Supports full or partial payment with live remaining-balance recalculation; the backend settles charges oldest-first.

### PAYER_MANAGEMENT

Manage a payer's stored payment methods: list, add (card or ACH, with set-as-default and use-for-auto-pay), edit, set default, and delete.

### AUTOPAY

Enroll a payer in automatic payments for an account, choose the payment method and schedule, and manage or disable enrollment.

## Mount Options

| Option | Required | Description |
|  --- | --- | --- |
| `mode` | Yes | One of the modes above |
| `invoiceIds` | INVOICE¹ | Array of invoice IDs to pay |
| `accountId` | ACCOUNT_BALANCE, AUTOPAY (INVOICE¹) | Target account |
| `personId` | PAYER_MANAGEMENT, AUTOPAY (INVOICE¹) | Enables saved payment methods |
| `amount` | No | Pre-set amount (STANDALONE) |
| `description` | No | Payment description (STANDALONE) |
| `width` / `height` | No | Iframe sizing (defaults `100%` / `600px`; height auto-resizes) |


`entityId` comes from the SDK constructor; the session token from `updateSessionToken()`.

¹ INVOICE mode requires **at least one** of `invoiceIds`, `personId`, or `accountId`.

## Events

Register event callbacks when constructing the SDK.

| Callback | Fires when | Payload |
|  --- | --- | --- |
| `onReady` | The widget has loaded and is ready | - |
| `onPaymentComplete` | A payment succeeds | `{ paymentId, transactionId, amount, paymentMethod, lastFour, receiptId, receiptUrl }` |
| `onError` | An error occurs | `{ code, message, category, retryable }` |
| `onCancel` | The payer closes or dismisses the widget | `{ reason }` |


The `retryable` flag on errors indicates transient failures the payer can safely retry.

## Payment Methods and Authorization

- **Saved methods** are available whenever a `personId` is supplied; the payer can pick a saved card or bank account or add a new one.
- **New methods** are captured inside the tokenization iframe (card or ACH), keeping the integration out of PCI scope.
- **ACH authorization:** when a bank account is selected, the widget surfaces the required Nacha-compliant authorization before the payment can proceed.
- **Save for later / auto-pay:** surfaced when enabled in merchant configuration and a `personId` is present.


## Theming

The widget accepts CSS custom properties so it can match the host portal's branding - primary color and hover/focus states, background/foreground, borders, and error/warning/success tokens. Merchant-level branding (logo, accepted payment methods, convenience-fee display, terms URL) is applied automatically from the widget's configuration.

## Versioning and Delivery

The SDK and widget are static assets served from the CDN, versioned in the path:

- **SDK:** `https://cdn.{env}.nelnetpay.com/sdk/{version}/widget-sdk.js`
- **Widget UI:** `https://cdn.{env}.nelnetpay.com/widget/{version}/`


Pin a specific `{version}` for stability; the SDK automatically resolves the matching widget build from its own script URL.

div
strong
Common misconception:
strong
Payment Widget
and 
strong
payment-widget-service
are not the same component. The 
strong
Payment Widget
(this page) is the embeddable UI and JavaScript SDK that renders in partner portals. 
strong
payment-widget-service
is the backend-for-frontend (BFF) that the widget communicates with. Keep these two components distinct when searching the documentation or filing issues.