# Melio Web SDK

The Melio Web SDK allows partners to embed core Melio workflows directly into the partner UI with minimal effort. It provides a high-level API for launching flows such as onboarding and pay flow, while managing authentication, lifecycle, and secure communication under the hood.

## 📚 Documentation

- **[Pay Flow Integration Guide](./guides/pay-flow-integration.md)** — the full end-to-end guide for embedding Melio payments (backend `prepare` call, auth code, SDK setup, events, testing checklist). **Start here.**
- **[SDK Reference](https://melio.github.io/web-sdk/)** — auto-generated API reference for every class, config, and event.

## 🚀 Quick Start

### Installation

```bash
npm install @melio-eng/web-sdk
```

### Basic Usage

```typescript
import { MelioSDK } from "@melio-eng/web-sdk";

// Create SDK instance
const melioSDK = new MelioSDK();

// Initialize the SDK with your authorization code
const init = melioSDK.init("AUTH_CODE_FROM_YOUR_BACKEND", {
  partnerName: "your-partner-name",   // required
  environment: "production",          // optional — defaults to 'production'
  keepAlive: true,                    // optional — keeps the session warm
});

init.on("authenticationSucceeded", () => {
  // Open the Pay Flow with the Melio bill IDs returned by /pay-flow/prepare
  const payFlow = melioSDK.openPayFlow({
    containerId: "melio-payflow-container",
    billIds: ["bill_abc123"],
    billIdType: "melio",
  });

  payFlow.on("completed", (data) => {
    console.log("Payment completed:", data);
  });
});

init.on("authenticationFailed", () => {
  // Re-mint the auth code on your backend and retry
});
```

> The full integration (backend `POST /pay-flow/prepare`, minting the auth code, and handling every event) is documented in the **[Pay Flow Integration Guide](./guides/pay-flow-integration.md)**.

## 📖 API Reference

### Initialization

```typescript
melioSDK.init(authenticationCode: string, options: InitOptions): InitFlowInstance
```

**Parameters:**
- `authenticationCode` (string, required): short-lived, user-scoped code minted on your backend
- `options` (InitOptions, required): Configuration options
  - `partnerName` (string, required): The partner name for the SDK instance
  - `keepAlive` (boolean): If true, the session will be kept alive in the background via an invisible iframe
  - `environment` (Environment): The environment to use for API endpoints (defaults to `'production'`)
  - `branchOverride` (string): Advanced/testing only

`init` returns an `InitFlowInstance` you can listen on for `authenticationSucceeded` / `authenticationFailed`.

### Environment Configuration

The SDK supports multiple environments to facilitate development and testing:

```typescript
// Production (default)
melioSDK.init("auth_code", { partnerName: "your-partner-name", environment: "production" });

// Staging
melioSDK.init("auth_code", { partnerName: "your-partner-name", environment: "staging01" });

// Public QA
melioSDK.init("auth_code", { partnerName: "your-partner-name", environment: "public-qa" });

// Certification
melioSDK.init("auth_code", { partnerName: "your-partner-name", environment: "certification" });

// Localhost (for local development)
melioSDK.init("auth_code", { partnerName: "your-partner-name", environment: "localhost" });
```

Each environment uses a different base URL:

| Environment | SDK `environment` | API base URL |
|---|---|---|
| Production | `production` (default) | `https://partnerships.melioservices.com` |
| Staging | `staging01` | `https://partnerships.staging01.melioservices.com` |
| Public QA | `public-qa` | `https://partnerships.public-qa.melioservices.com` |
| Certification | `certification` | `https://partnerships.certification.melioservices.com` |
| Localhost | `localhost` | `http://localhost:3005` |

### Flow Methods

#### Pay Flow
Pay one or more bills. Pass the **Melio bill IDs** returned by `POST /pay-flow/prepare` together with `billIdType: "melio"`.
```typescript
melioSDK.openPayFlow(config: PayFlowConfig): FlowInstance
```

`billIdType` controls how the platform resolves `billIds` (defaults to `"accounting"`):
- `"melio"` — `billIds` are Melio bill IDs from `/pay-flow/prepare`; the payment screen opens directly. **Use this for the prepare-based pay-flow integration.**
- `"accounting"` — `billIds` are external accounting-software IDs, imported on demand (for accounting-software partners such as Xero / QuickBooks).

#### Just Pay Flow
Vendor-based payment (start from a vendor rather than a specific bill).
```typescript
melioSDK.openJustPayFlow(config: JustPayFlowConfig): FlowInstance
```

#### Payments Dashboard Flow
Let users view and manage their payments inside your product.
```typescript
melioSDK.openPaymentsDashboard(config: PaymentsDashboardConfig): FlowInstance
```

#### Onboarding Flow
Collect any missing account / KYC information directly in Melio.
```typescript
melioSDK.openOnboarding(config: OnboardingConfig): FlowInstance
```

#### Settings Flow
Host Melio account settings within your product.
```typescript
melioSDK.openSettings(config: SettingsConfig): FlowInstance
```

#### Entry Point (`entryPoint`)

`PayFlowConfig`, `JustPayFlowConfig` and `OnboardingConfig` accept an optional `entryPoint` — which
page of your product the user came from. Melio uses it to word the button that ends the flow:

| `entryPoint` | return button reads |
|---|---|
| `"bills"` | "Back to bills" |
| `"contacts"` | "Back to contacts" |
| `"quickpayment"` | "Back to quick payment" |
| `"managepayments"` | "Back to manage payments" |
| `"home"` | "Back to homepage" |
| `"generic"` | "Finish and return" |
| _omitted_ | today's default wording |

It's wording only — the `completed` event carries no destination, so your app still handles
navigation. On the Pay Flow, it has no effect when `billIdType` is `"melio"`. See
[Entry point](./guides/pay-flow-integration.md#entry-point) in the full guide for a worked example.

### Event Handling

Each flow method returns a `FlowInstance` that allows you to register event listeners:

```typescript
const flow = melioSDK.openPayFlow({
  containerId: "melio-payflow-container",
  billIds: ["bill_abc123"],
  billIdType: "melio",
});

flow.on("loaded",    ()     => console.log("Flow rendered"));
flow.on("completed", (data) => console.log("Payment completed:", data));
flow.on("error",     (err)  => console.warn("Flow error:", err.errorCode));
flow.on("exit",      ()     => console.log("User exited the flow"));
flow.on("navigated", (p)    => console.log("Navigated to:", p.target));
```

**Available Events:**
- `loaded`: The flow iframe has rendered
- `completed`: The user successfully finished a flow
- `error`: A flow error occurred (e.g. `billsSyncFailed`)
- `exit`: The user exited the iframe
- `navigated`: Navigation occurred inside the iframe
- `buttonClicked`: The user clicked a button whose destination is in your product (see below)
- `authenticationSucceeded` / `authenticationFailed`: emitted on the `init` instance

#### Button clicks (`buttonClicked`)

Some Melio screens contain buttons whose destination lives in **your** product, not in Melio —
today the "+ Quick payment" button on the payments dashboard, which should take the user to
your own supplier selection page. Melio cannot navigate the host page, so it reports the click
and waits for you:

```typescript
const dashboard = melioSDK.openPaymentsDashboard({
  containerId: "melio-dashboard-container",
});

dashboard.on("buttonClicked", ({ type }) => {
  if (type === "quickPayment") {
    window.location.assign("/suppliers/select");
  }
});
```

| `type` | Emitted when | What you should do |
|---|---|---|
| `quickPayment` | The user clicks the quick payment button on the payments dashboard | Take them to your supplier selection page, then open a pay flow |

Without a listener the button does nothing at all. Only buttons whose destination is in your
product are reported here — buttons that end a flow keep their existing events, so "Back to
bills" still arrives as `completed`. Further button types ship in minor releases, so switch on
`type` and treat an unrecognised value as a no-op.

## 💡 Examples

### Pay Flow

```typescript
const payFlow = melioSDK.openPayFlow({
  containerId: "melio-payflow-container",
  billIds: ["bill_abc123", "bill_def456"],   // Melio bill IDs from /pay-flow/prepare
  billIdType: "melio",
});

payFlow.on("completed", (data) => {
  console.log("Payment completed:", data);
});

payFlow.on("exit", () => {
  console.log("User exited the payment flow");
});
```

### Development with Staging Environment

```typescript
const init = melioSDK.init("your_auth_code", {
  partnerName: "your-partner-name",
  environment: "staging01",
  keepAlive: true,
});

init.on("authenticationSucceeded", () => {
  melioSDK.openPayFlow({
    containerId: "payment-container",
    billIds: ["bill_abc123"],
    billIdType: "melio",
  });
});
```

### Onboarding

```typescript
const onboarding = melioSDK.openOnboarding({
  containerId: "onboarding-container",
});

onboarding.on("completed", (data) => {
  console.log("User completed onboarding:", data);
});
```

## 🧪 Running the Examples Locally

The repository includes interactive examples that demonstrate the SDK functionality. They render a partner-style accounting UI (navigation, tabs, a bills table with single and batch selection) and wire the SDK into it end-to-end.

```bash
# Serve all examples in the /examples directory
npm run examples
```

Then open the example HTML files (e.g. `http://localhost:3009/examples/basic-usage.html` or `http://localhost:3009/examples/bill-pay-example.html`).

The bill-pay example demonstrates:
- **Automatic SDK initialization** on page load
- **Interactive bill management**: checkbox selection for batch payments, individual "Pay bill" buttons, real-time totals, and status updates after completion
- **Full integration**: how the SDK slots into an existing partner interface
- **Event logging** for every flow lifecycle event

## 🔄 Session Keep-Alive

When `keepAlive: true` is passed to `init()`, the SDK renders a hidden 1x1 pixel iframe that points to Melio's authentication endpoint. This iframe:

- Periodically sends `USER_ACTIVE_PING` postMessages to Melio
- Prevents session expiration during long-lived host app sessions
- Is invisible to the user and does not impact the layout

This mechanism avoids redundant SSO handshakes and ensures seamless flow launches without delays.

## 🏗️ How It Works

### Session Initialization
1. SDK uses the authorization code to perform a token exchange with Melio
2. Melio receives the user's identity from the partner
3. Melio generates a new access token and initializes the session used for iframe-based flows
4. A valid session is stored in local/session storage
5. All future flows reuse this session if it is still valid

### iframe Injection
1. SDK creates and injects an iframe into the specified `containerId`
2. It builds an `authUrl` with token, target, entrypoint, and context (e.g., billIds)

### Communication
- SDK and Melio iframe communicate using `postMessage`
- Navigation, height, session, and errors are synchronized

## 🛠️ Development

### Building

```bash
npm run build
```

### Documentation

```bash
npm run docs        # generate the SDK reference + guides
npm run docs:serve  # generate docs and start a local server
npm run docs:view   # view already-generated docs
```

### Testing

```bash
npm test
```

## 📋 Requirements

- Modern browser with ES2020 support
- HTTPS environment (required for iframe communication)
- Valid Melio partner authorization code

## 🔒 Security

- All communication is done over HTTPS
- Origin validation for postMessage communication
- Session tokens are managed securely
- Your API key is used server-to-server only — never in the browser

## 🤝 Support

For support, please contact the Melio team or create an issue in this repository.

## 📄 License

MIT
