npm package: @paytrigo-net/sdk
Description: payTrigo SDK client and OpenAPI types
Latest version: 0.1.4

# @paytrigo-net/sdk

Official PayTrigo SDK (runtime client + TypeScript types). PayTrigo is non-custodial: funds go directly to the merchant wallet. The SDK helps you create invoices, fetch status/intent, and submit payment intents with full type safety.

## Install

```bash
npm install @paytrigo-net/sdk
# or
pnpm add @paytrigo-net/sdk
# or
yarn add @paytrigo-net/sdk
```

## Requirements

- A runtime with `fetch` (Node 18+ or a polyfill)
- TypeScript recommended (types are included)

## Quickstart (Merchant)

```ts
import { createPaytrigoMerchantClient } from "@paytrigo-net/sdk";

const client = createPaytrigoMerchantClient({
  auth: { type: "apiKey", token: process.env.PAYTRIGO_API_KEY! },
});

const invoice = await client.invoices.create({
  amount: "29.00",
  // options omitted -> all enabled Address Book entries are exposed
  metadata: { orderId: "order_123" },
});

console.log(invoice.invoiceId, invoice.payUrl);
```

## Configuration

### Environment Variables

```bash
PAYTRIGO_API_KEY=sk_live_your_api_key_here
PAYTRIGO_PLATFORM_KEY=sk_live_your_platform_key_here
PAYTRIGO_API_URL=https://api.paytrigo.net
PAYTRIGO_WEBHOOK_SECRET=whsec_...
```

### Base URL

The SDK defaults to `https://api.paytrigo.net` and automatically calls `/v1/*`. Do not include `/v1` in `baseUrl`.

```ts
const client = createPaytrigoMerchantClient({
  baseUrl: process.env.PAYTRIGO_API_URL,
  auth: { type: "apiKey", token: process.env.PAYTRIGO_API_KEY! },
});
```

### Timeouts, User-Agent, Retry

```ts
const client = createPaytrigoMerchantClient({
  auth: { type: "apiKey", token: process.env.PAYTRIGO_API_KEY! },
  timeoutMs: 10000,
  userAgent: "my-app/1.2.3",
  retry: { max: 2, backoffMs: 500 },
});
```

## Hosted Checkout (Recommended)

Create the invoice on your server, then redirect the user to `payUrl`.

### Server: Create Invoice

```ts
import { createPaytrigoMerchantClient } from "@paytrigo-net/sdk";

const client = createPaytrigoMerchantClient({
  auth: { type: "apiKey", token: process.env.PAYTRIGO_API_KEY! },
});

export async function createPayment(orderId: string, amount: string) {
  const invoice = await client.invoices.create({
    amount,
    metadata: { orderId },
  });

  return {
    invoiceId: invoice.invoiceId,
    payUrl: invoice.payUrl,
    checkoutToken: invoice.checkoutToken,
  };
}
```

### Client: Redirect

```ts
const response = await fetch("/api/create-payment", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ amount: "29.00" }),
});

const { payUrl } = await response.json();
window.location.href = payUrl;
```

## Merchant vs Platform Keys

Merchant keys are for checkout integrations. They cannot set `recipientAddress` and rely on Address Book entries configured in the dashboard.

Platform keys are for server-side marketplace flows. They must set `recipientAddress` for each invoice (or per option).

Security note: never expose API keys in the browser. Only use `checkoutToken` client-side.

### Merchant: Multi-option (Multi-token)

```ts
await client.invoices.create({
  amount: "100.00",
  options: [
    { chain: "base", token: "usdc", isDefault: true },
    { chain: "base", token: "usdt" },
  ],
});
```

### Platform: Single Option (Server-side only)

```ts
import { createPaytrigoPlatformClient } from "@paytrigo-net/sdk";

const platformClient = createPaytrigoPlatformClient({
  auth: { type: "apiKey", token: process.env.PAYTRIGO_PLATFORM_KEY! },
});

await platformClient.invoices.create({
  amount: "49.99",
  chain: "base",
  token: "usdc",
  recipientAddress: "0xSellerWallet...",
});
```

### Platform: Multi-option (Each option needs recipientAddress)

```ts
await platformClient.invoices.create({
  amount: "49.99",
  options: [
    { chain: "base", token: "usdc", recipientAddress: "0xSellerWallet..." },
    { chain: "base", token: "usdt", recipientAddress: "0xSellerWallet..." },
  ],
});
```

## Checkout Token Flow (Pay Page)

Use the checkout token to build a pay page that only has access to the specific invoice.

```ts
const invoice = await client.invoices.create({ amount: "29.00" });

const payClient = createPaytrigoMerchantClient({
  auth: { type: "checkoutToken", token: invoice.checkoutToken },
});

const intent = await payClient.invoices.getIntent(invoice.invoiceId);
const options = await payClient.invoices.getOptions(invoice.invoiceId);
```

### Render Options (Chain/Token Selector)

```ts
const options = await payClient.invoices.getOptions(invoice.invoiceId);

const labels = options.options.map((option) => ({
  id: `${option.chain}-${option.token}`,
  label: `${option.chainName} ${option.tokenMeta.symbol}`,
  routerAddress: option.routerAddress,
  isDefault: option.isDefault,
}));
```

## API-Only Flow (Intent + Submit)

Use this when you build your own wallet UI instead of Hosted Checkout.

```ts
import { createPaytrigoMerchantClient } from "@paytrigo-net/sdk";

const client = createPaytrigoMerchantClient({
  auth: { type: "apiKey", token: process.env.PAYTRIGO_API_KEY! },
});

// 1) Create invoice
const invoice = await client.invoices.create({
  amount: "50.00",
  chain: "base",
  token: "usdc",
});

// 2) Get intent (router, amountAtomic, steps, etc.)
const intent = await client.invoices.getIntent(invoice.invoiceId, {
  chain: "base",
  token: "usdc",
});

// 3) Build and send transaction with your wallet UI...
// 4) Submit txHash for verification
await client.invoices.submitPaymentIntent(invoice.invoiceId, {
  txHash: "0x...",
  chain: "base",
  token: "usdc",
});
```

## Check Invoice Status

```ts
const invoice = await client.invoices.get("inv_123");
console.log(invoice.status, invoice.amount, invoice.chain);
```

## Webhooks (Signature Verification)

Webhooks are the recommended way to confirm payments in production. Always verify the signature and dedupe on `event.id` or `X-Paytrigo-Event-Id`.

```ts
import crypto from "crypto";

type WebhookEvent = {
  id: string;
  type:
    | "invoice.created"
    | "invoice.confirmed"
    | "invoice.expired"
    | "invoice.invalid"
    | "invoice.updated";
  createdAt: string;
  data: {
    invoiceId: string;
    status: string;
    [key: string]: unknown;
  };
};

export async function handleWebhook(request: Request) {
  const rawBody = await request.text();
  const timestamp = request.headers.get("X-Paytrigo-Timestamp") ?? "";
  const signature = request.headers.get("X-Paytrigo-Signature") ?? "";
  const eventId = request.headers.get("X-Paytrigo-Event-Id") ?? "";

  const expected =
    "v1=" +
    crypto
      .createHmac("sha256", process.env.PAYTRIGO_WEBHOOK_SECRET!)
      .update(`${timestamp}.${rawBody}`)
      .digest("hex");

  if (!timingSafeEquals(signature, expected)) {
    return new Response("Invalid signature", { status: 401 });
  }

  const event = JSON.parse(rawBody) as WebhookEvent;

  // Deduplicate by eventId (store in DB)
  if (await alreadyProcessed(eventId)) {
    return new Response("ok");
  }

  await markProcessed(eventId);
  await processEvent(event);
  return new Response("ok");
}

function timingSafeEquals(a: string, b: string) {
  try {
    return crypto.timingSafeEqual(Buffer.from(a), Buffer.from(b));
  } catch {
    return false;
  }
}

async function alreadyProcessed(_eventId: string) {
  return false;
}

async function markProcessed(_eventId: string) {}

async function processEvent(event: WebhookEvent) {
  if (event.type === "invoice.confirmed") {
    // fulfill order
  }
}
```

### Next.js Webhook Route (App Router)

```ts
// app/api/webhooks/paytrigo/route.ts
import { NextRequest, NextResponse } from "next/server";
import crypto from "crypto";

export async function POST(request: NextRequest) {
  const rawBody = await request.text();
  const timestamp = request.headers.get("X-Paytrigo-Timestamp") ?? "";
  const signature = request.headers.get("X-Paytrigo-Signature") ?? "";

  const expected =
    "v1=" +
    crypto
      .createHmac("sha256", process.env.PAYTRIGO_WEBHOOK_SECRET!)
      .update(`${timestamp}.${rawBody}`)
      .digest("hex");

  if (!timingSafeEquals(signature, expected)) {
    return NextResponse.json({ error: "Invalid signature" }, { status: 401 });
  }

  // Parse and process event
  return NextResponse.json({ received: true });
}

function timingSafeEquals(a: string, b: string) {
  try {
    return crypto.timingSafeEqual(Buffer.from(a), Buffer.from(b));
  } catch {
    return false;
  }
}
```

## Polling (Not Recommended)

Polling is acceptable for testing or UI fallback, but webhooks are strongly recommended in production.

```ts
const terminalStates = new Set(["confirmed", "expired", "invalid"]);

async function pollInvoice(invoiceId: string) {
  while (true) {
    const invoice = await client.invoices.get(invoiceId);
    if (terminalStates.has(invoice.status)) return invoice;
    await new Promise((resolve) => setTimeout(resolve, 5000));
  }
}
```

## Idempotency

For create and submit APIs, you can pass an `idempotencyKey`. If `retry` is enabled, the SDK will automatically set an idempotency key for POST requests.

```ts
await client.invoices.create({
  amount: "29.00",
  idempotencyKey: `pay_attempt_${crypto.randomUUID()}`,
});
```

## Errors

```ts
import { PaytrigoError, PaytrigoValidationError } from "@paytrigo-net/sdk";

try {
  await client.invoices.create({ amount: "29.00" });
} catch (error) {
  if (error instanceof PaytrigoValidationError) {
    console.error(error.issues);
  } else if (error instanceof PaytrigoError) {
    console.error(error.status, error.code, error.message);
  }
}
```

## Types

```ts
import type { components } from "@paytrigo-net/sdk";

type InvoiceDetail = components["schemas"]["InvoiceDetailResponseDto"];
type CreateInvoiceRequest = components["schemas"]["CreateInvoiceDto"];
```

Note: webhook payload types are not part of the OpenAPI spec yet. You can define your own type based on the docs:

```ts
type WebhookEvent = {
  id: string;
  type:
    | "invoice.created"
    | "invoice.confirmed"
    | "invoice.expired"
    | "invoice.invalid"
    | "invoice.updated";
  data: Partial<components["schemas"]["InvoiceDetailResponseDto"]> & {
    invoiceId: string;
    status: string;
  };
};
```

Note: `CreateInvoiceDto.token` is a string token ID (e.g. `"usdc"`), while invoice responses and webhooks return a token descriptor object.

### Utility Type Patterns

```ts
type InvoiceSummary = Pick<
  components["schemas"]["InvoiceDetailResponseDto"],
  "invoiceId" | "status" | "amount" | "chain"
>;

type ClientSafeInvoice = Omit<
  components["schemas"]["InvoiceDetailResponseDto"],
  "metadata"
>;
```

## OpenAPI JSON

```ts
// Requires resolveJsonModule in tsconfig
import openapi from "@paytrigo-net/sdk/openapi.json";
```

## Platform Fee Helpers

```ts
import { getPlatformFeeSummary, expectPlatformFee } from "@paytrigo-net/sdk";

const summary = getPlatformFeeSummary(invoice);
expectPlatformFee(invoice, { bps: 50 });
```

You can also assert platform fees at creation time:

```ts
await client.invoices.create({
  amount: "29.00",
  expectPlatformFee: { bps: 50, recipient: "0xPlatformFeeWallet..." },
});
```

## Escape Hatch (Raw Request)

```ts
const data = await client.request("get", "/v1/invoices/{invoiceId}", {
  params: { path: { invoiceId: "inv_..." } },
});
```

## Next.js API Route Example

```ts
// app/api/create-payment/route.ts
import { NextRequest, NextResponse } from "next/server";
import { createPaytrigoMerchantClient } from "@paytrigo-net/sdk";

export async function POST(request: NextRequest) {
  const { amount } = await request.json();
  const client = createPaytrigoMerchantClient({
    auth: { type: "apiKey", token: process.env.PAYTRIGO_API_KEY! },
  });

  const invoice = await client.invoices.create({ amount });
  return NextResponse.json({
    invoiceId: invoice.invoiceId,
    payUrl: invoice.payUrl,
  });
}
```

### Next.js Pages Router (API Route)

```ts
// pages/api/create-payment.ts
import type { NextApiRequest, NextApiResponse } from "next";
import { createPaytrigoMerchantClient } from "@paytrigo-net/sdk";

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  if (req.method !== "POST") return res.status(405).end();

  const { amount } = req.body as { amount: string };
  const client = createPaytrigoMerchantClient({
    auth: { type: "apiKey", token: process.env.PAYTRIGO_API_KEY! },
  });

  const invoice = await client.invoices.create({ amount });
  res
    .status(200)
    .json({ invoiceId: invoice.invoiceId, payUrl: invoice.payUrl });
}
```

## Development (Repo Maintainers)

- OpenAPI JSON and schema types are generated. Do not edit `openapi.json` or `src/schema.d.ts` manually.
- To regenerate from the API codebase:

```bash
pnpm sdk:sync
```

## Repository

Source: https://github.com/paytrigo/paytrigo-sdk

## License

MIT. See LICENSE.