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

# TypeScript / JavaScript SDK

> Official SDK for Node.js, Bun, Deno, and edge runtimes.

## Installation

<CodeGroup>
  ```bash npm theme={null}
  npm install @paybridge-np/sdk
  ```

  ```bash yarn theme={null}
  yarn add @paybridge-np/sdk
  ```

  ```bash bun theme={null}
  bun add @paybridge-np/sdk
  ```
</CodeGroup>

## Initialization

```typescript theme={null}
import { PayBridgeNP } from "@paybridge-np/sdk";

const paybridgenp = new PayBridgeNP({
  apiKey: process.env.PAYBRIDGENP_API_KEY!,  // sk_test_... or sk_live_...
  baseUrl: "https://api.paybridgenp.com",  // optional, this is the default
  timeout: 30_000,                         // optional, ms. default: 30000
  maxRetries: 2,                           // optional. default: 2
});
```

***

## `paybridgenp.checkout`

### `checkout.create(params)`

Creates a checkout session.

```typescript theme={null}
const session = await paybridgenp.checkout.create({
  amount: 10000,                              // required - paisa (NPR × 100)
  returnUrl: "https://yoursite.com/success", // required
  cancelUrl: "https://yoursite.com/cart",    // optional
  provider: "khalti",                        // optional - omit to let customer pick
  currency: "NPR",                           // optional, default: "NPR"
  metadata: { orderId: "ORD-001" },          // optional
});

console.log(session.checkout_url);  // redirect customer here
console.log(session.id);            // cs_...
console.log(session.expires_at);    // ISO timestamp
```

**Parameters:**

| Name        | Type                               | Required | Description                                    |
| ----------- | ---------------------------------- | -------- | ---------------------------------------------- |
| `amount`    | `number`                           | Yes      | In paisa. NPR 100.00 = `10000`                 |
| `returnUrl` | `string`                           | Yes      | Redirect URL after payment                     |
| `cancelUrl` | `string`                           | No       | Redirect URL on cancellation                   |
| `provider`  | `"esewa" \| "khalti" \| "fonepay"` | No       | Pre-select provider                            |
| `currency`  | `string`                           | No       | Default: `"NPR"`                               |
| `metadata`  | `object`                           | No       | Passed through to webhooks and payment records |

### `checkout.expire(id)`

Marks a session as expired so it can no longer accept payment. Use this when you mint a fresh session for a logical purchase that already had one outstanding (e.g. a customer requesting a new payment link), so the old URL stops being payable immediately rather than waiting for its 30-minute TTL.

```typescript theme={null}
await paybridgenp.checkout.expire("cs_6f2jHn2…");
```

Idempotent: calling on an already-terminal session is a no-op that returns the current row state without error.

### `checkout.retrieve(id)`

Fetches a checkout session by ID: its current status, amount, customer, and any collected address. Read-only (sessions are created with `checkout.create`).

```typescript theme={null}
const session = await paybridgenp.checkout.retrieve("cs_6f2jHn2…");
console.log(session.status);        // "pending" | "initiated" | "success" | ...
console.log(session.amount);        // paisa
console.log(session.customerName);  // camelCase on the read shape
```

Note: the read shape uses camelCase keys (`customerName`, `expiresAt`, ...), unlike the snake\_case `checkout.create` response.

### `checkout.list(params?)`

Lists checkout sessions for the project, newest first.

```typescript theme={null}
const { data, meta } = await paybridgenp.checkout.list({
  limit: 20,          // optional, 1 to 100 (default 25)
  offset: 0,          // optional
  status: "success",  // optional filter
});
console.log(meta.total, data.length);
```

***

## `paybridgenp.paymentLinks`

Reusable hosted payment pages. Methods need an API key with the `links:read` or `links:write` scope.

### `paymentLinks.create(params)`

```typescript theme={null}
const link = await paybridgenp.paymentLinks.create({
  title: "Donation",  // required
  amount: 50000,      // paisa. Omit for a customer-entered amount (use minAmount/maxAmount)
  maxUses: 100,       // optional
});
console.log(link.url); // public hosted payment page
```

### `paymentLinks.list(params?)`

```typescript theme={null}
const { data, meta } = await paybridgenp.paymentLinks.list({
  active: true, // optional filter
  limit: 20,    // optional
});
```

### `paymentLinks.retrieve(id)`

Returns the link plus aggregated view and conversion stats.

```typescript theme={null}
const link = await paybridgenp.paymentLinks.retrieve("lnk_…");
console.log(link.stats); // { views, used_count, conversion_rate }
```

### `paymentLinks.update(id, params)`

Updates editable fields. Only the keys you pass are changed.

```typescript theme={null}
await paybridgenp.paymentLinks.update("lnk_…", { active: false });
```

### `paymentLinks.cancel(id)`

Deactivates a link so it can no longer accept payments, while keeping it and its history for your records. The recommended way to retire a link that has already been used.

```typescript theme={null}
await paybridgenp.paymentLinks.cancel("lnk_…");
```

### `paymentLinks.delete(id)`

Permanently deletes a link. Allowed only when the link has never been used. Otherwise it returns `422`; cancel it instead.

```typescript theme={null}
await paybridgenp.paymentLinks.delete("lnk_…");
```

***

## `paybridgenp.payments`

### `payments.list(params?)`

```typescript theme={null}
const { data, meta } = await paybridgenp.payments.list({
  limit: 20,   // optional, default 10, max 100
  offset: 0,   // optional, default 0
});

console.log(meta.total);  // total count
data.forEach((payment) => {
  console.log(payment.id, payment.amount, payment.status);
});
```

### `payments.retrieve(id)`

```typescript theme={null}
const payment = await paybridgenp.payments.retrieve("pay_6f2jHn2...");

console.log(payment.status);      // "success" | "failed"
console.log(payment.amount);      // paisa
console.log(payment.provider);    // "esewa" | "khalti" | "fonepay"
console.log(payment.providerRef); // provider's transaction reference
console.log(payment.metadata);    // whatever you passed at checkout
```

***

## `paybridgenp.webhooks`

### `webhooks.create(params)`

```typescript theme={null}
const endpoint = await paybridgenp.webhooks.create({
  url: "https://yoursite.com/webhooks/paybridgenp",
  events: ["payment.succeeded", "payment.failed"], // optional - omit for all events
});

console.log(endpoint.signing_secret); // save this! shown only once
```

### `webhooks.list()`

```typescript theme={null}
const { data } = await paybridgenp.webhooks.list();
data.forEach((wh) => console.log(wh.id, wh.url, wh.enabled));
```

### `webhooks.delete(id)`

```typescript theme={null}
await paybridgenp.webhooks.delete("wh_...");
```

### `PayBridgeNP.webhooks.constructEvent(body, signature, secret)` (static)

Verifies a webhook signature and parses the event. Use this in your webhook handler.

```typescript theme={null}
import { PayBridgeNP } from "@paybridge-np/sdk";

// This is a static method - no instance needed
const event = await PayBridgeNP.webhooks.constructEvent(
  rawBodyString,           // raw request body - do NOT parse as JSON first
  signatureHeader,         // value of X-PayBridgeNP-Signature header
  process.env.PAYBRIDGENP_WEBHOOK_SECRET!,
);

switch (event.type) {
  case "payment.succeeded":
    // event.data: { id, amount, currency, provider, provider_ref, session_id, metadata }
    await fulfillOrder(event.data.metadata?.orderId);
    break;

  case "payment.failed":
    await notifyOrderFailed(event.data.session_id);
    break;
}
```

Throws `SignatureVerificationError` if:

* The signature header is missing or malformed
* The HMAC doesn't match
* The timestamp is more than 5 minutes old (replay attack protection)

***

## `paybridgenp.billing`

The billing namespace provides access to plans, customers, subscriptions, and invoices. Billing API access requires the Growth plan or higher.

### `billing.plans`

```typescript theme={null}
// Create a plan
const plan = await paybridgenp.plans.create({
  name: "Pro Monthly",
  amount: 99900,         // paisa - NPR 999/month
  intervalUnit: "month",
  intervalCount: 1,
  trialDays: 14,         // optional
});

// List plans
const { data } = await paybridgenp.plans.list();

// Get a plan
const plan = await paybridgenp.plans.get("plan_...");

// Update a plan
const updated = await paybridgenp.plans.update("plan_...", { name: "Pro Monthly v2" });
```

### `billing.customers`

```typescript theme={null}
// Create a customer
const customer = await paybridgenp.customers.create({
  name: "Aarav Sharma",
  email: "aarav@example.com",
  externalCustomerId: "user_123",   // optional - your internal user ID
});

// List customers
const { data } = await paybridgenp.customers.list();

// Get a customer
const customer = await paybridgenp.customers.get("cus_...");

// Update a customer
const updated = await paybridgenp.customers.update("cus_...", { email: "new@example.com" });

// Delete a customer
await paybridgenp.customers.delete("cus_...");
```

### `billing.subscriptions`

```typescript theme={null}
// Create a subscription
const subscription = await paybridgenp.subscriptions.create({
  customerId: "cus_...",
  planId: "plan_...",
});

// List subscriptions
const { data } = await paybridgenp.subscriptions.list();

// Get a subscription
const sub = await paybridgenp.subscriptions.get("sub_...");

// Lifecycle actions
await paybridgenp.subscriptions.pause("sub_...");
await paybridgenp.subscriptions.resume("sub_...");
await paybridgenp.subscriptions.cancel("sub_...");

// Change plan
await paybridgenp.subscriptions.changePlan("sub_...", { newPlanId: "plan_..." });
```

### `billing.invoices`

```typescript theme={null}
// List invoices (optionally filter by subscription or customer)
const { data } = await paybridgenp.invoices.list({
  subscriptionId: "sub_...",   // optional
});

// Get an invoice
const invoice = await paybridgenp.invoices.get("inv_...");

console.log(invoice.status);     // "open" | "paid" | "overdue" | "void" | "uncollectible" | "write_off"
console.log(invoice.amount_due); // paisa
console.log(invoice.due_at);     // ISO timestamp
console.log(invoice.hosted_invoice_url); // hosted payment link sent to customer
```

***

## Error handling

All SDK methods throw typed errors you can catch and inspect:

```typescript theme={null}
import { PayBridgeNP, PayBridgeError, AuthenticationError } from "@paybridge-np/sdk";

try {
  await paybridgenp.checkout.create({ amount: 1000, returnUrl: "..." });
} catch (err) {
  if (err instanceof AuthenticationError) {
    // Invalid API key
    console.error("Check your PAYBRIDGENP_API_KEY");
  } else if (err instanceof PayBridgeError) {
    console.error(err.message);     // human-readable message
    console.error(err.statusCode);  // HTTP status
    console.error(err.code);        // machine-readable code
  }
}
```

### Error classes

| Class                        | Status             | When                                                                 |
| ---------------------------- | ------------------ | -------------------------------------------------------------------- |
| `AuthenticationError`        | 401                | Invalid or missing API key                                           |
| `InvalidRequestError`        | 400, 404, 409, 422 | Malformed request, resource not found, or a business rule blocked it |
| `RateLimitError`             | 429                | Too many requests                                                    |
| `ApiError`                   | 5xx                | Server error - safe to retry                                         |
| `SignatureVerificationError` | -                  | Webhook HMAC mismatch or replay                                      |

The base class is `PayBridgeError`; every error above extends it. See the [Errors reference](/api-reference/errors) for the full list.

***

## TypeScript types

All types are exported from `@paybridge-np/sdk`:

```typescript theme={null}
import type {
  CheckoutSession,
  Payment,
  PaymentStatus,
  Provider,
  WebhookEvent,
  WebhookEventType,
  WebhookEndpoint,
  CreateCheckoutParams,
  PaginatedResponse,
  Plan,
  BillingCustomer,
  Subscription,
  Invoice,
  CreatePlanParams,
  CreateCustomerParams,
  CreateSubscriptionParams,
} from "@paybridge-np/sdk";
```

***

## Framework examples

<AccordionGroup>
  <Accordion title="Next.js App Router">
    ```typescript theme={null}
    // app/api/checkout/route.ts
    import { NextRequest } from "next/server";
    import { PayBridgeNP } from "@paybridge-np/sdk";

    const paybridgenp = new PayBridgeNP({
      apiKey: process.env.PAYBRIDGENP_API_KEY!,
    });

    export async function POST(req: NextRequest) {
      const { orderId, amount } = await req.json();

      const session = await paybridgenp.checkout.create({
        amount,
        returnUrl: `${process.env.NEXT_PUBLIC_URL}/thank-you`,
        cancelUrl: `${process.env.NEXT_PUBLIC_URL}/cart`,
        metadata: { orderId },
      });

      return Response.json({ checkoutUrl: session.checkout_url });
    }
    ```

    ```typescript theme={null}
    // app/api/webhooks/paybridgenp/route.ts
    import { NextRequest } from "next/server";
    import { PayBridgeNP } from "@paybridge-np/sdk";

    export async function POST(req: NextRequest) {
      const body = await req.text();
      const sig = req.headers.get("x-paybridgenp-signature");

      let event;
      try {
        event = await PayBridgeNP.webhooks.constructEvent(
          body, sig, process.env.PAYBRIDGENP_WEBHOOK_SECRET!,
        );
      } catch {
        return Response.json({ error: "Invalid signature" }, { status: 400 });
      }

      if (event.type === "payment.succeeded") {
        await db.orders.update({ status: "paid" }, {
          where: { id: event.data.metadata?.orderId },
        });
      }

      return Response.json({ received: true });
    }
    ```
  </Accordion>

  <Accordion title="Express">
    ```typescript theme={null}
    import express from "express";
    import { PayBridgeNP } from "@paybridge-np/sdk";

    const app = express();
    const paybridgenp = new PayBridgeNP({ apiKey: process.env.PAYBRIDGENP_API_KEY! });

    app.use(express.json());

    app.post("/create-checkout", async (req, res) => {
      const session = await paybridgenp.checkout.create({
        amount: req.body.amount,
        returnUrl: "https://yoursite.com/success",
        metadata: { orderId: req.body.orderId },
      });
      res.json({ checkoutUrl: session.checkout_url });
    });

    // Webhook route - must use raw body
    app.post(
      "/webhooks/paybridgenp",
      express.raw({ type: "application/json" }),
      async (req, res) => {
        const event = await PayBridgeNP.webhooks.constructEvent(
          req.body.toString(),
          req.headers["x-paybridgenp-signature"] as string,
          process.env.PAYBRIDGENP_WEBHOOK_SECRET!,
        );

        if (event.type === "payment.succeeded") {
          // handle success
        }

        res.json({ received: true });
      },
    );
    ```
  </Accordion>
</AccordionGroup>
