---
title: "npm packages"
description: "@thru-payment/server (checkout sessions, return and webhook verification), checkout-core (headless, React or plain JS), pay-sdk (React components) and x402; what each covers and what it does not"
source: https://docs.thru.la/sdks.md
html: https://docs.thru.la/sdks
index: https://docs.thru.la/llms.txt
---
# npm packages

Four packages under the `@thru-payment` scope, MIT licensed; `@thru-payment/server` is at 0.3.1 (the others are 0.2.0). They are ES modules: use `import`, not `require`.

| Package | Runs on | Covers |
|---|---|---|
| `@thru-payment/server` | Your server: Node 20+, Cloudflare Workers, Deno, Bun | Checkout sessions, signed-return verification, webhook verification |
| `@thru-payment/checkout-core` | The browser, with or without React | Reading and polling a payment's public status |
| `@thru-payment/pay-sdk` | The browser, React 18+ | A ready-made payment screen and its parts |
| `@thru-payment/x402` | Your server (Node) | Agent payments over HTTP 402, which are not enabled on api.thru.la yet (`facilitator-x402`) |

**There is no SDK for other languages** (Go, Python, PHP…), and `@thru-payment/server` covers checkout sessions and `payments.retrieve` (0.3.1+), not products, invoices, refunds, settlement or webhook endpoints. For those, call the REST API directly; it is plain JSON with one header. The `webhooks` topic has signature verification code for Node, Go and Python.

The older `@thru/pay-sdk` package is superseded by these; do not use it.

## `@thru-payment/server`

```ts
import { createThruServerClient } from '@thru-payment/server';

const thru = createThruServerClient({ apiKey: process.env.THRU_API_KEY! });

await thru.checkout.sessions.create({ productSlug, reference, successUrl });
// A custom-amount product needs the amount as well: { productSlug, amount: '37', reference, ... }.
// The field takes the same rules as POST /v1/checkout/sessions; use a release of the package that
// declares it (see the package changelog), or call the endpoint directly.
await thru.checkout.sessions.retrieve(sessionId);
await thru.checkout.sessions.list({ createdAfter, status: 'completed', limit: 100, cursor });
await thru.checkout.sessions.expire(sessionId);
```

Also exported:

- `verifyThruReturn(input, checkoutSecret, { toleranceSeconds?, now? })`, **async**. `input` can be a URL, a URL string, `URLSearchParams` or a plain object. It resolves to `{ verified: true, sessionId, status }` or `{ verified: false, sessionId, status: null, reason }`.
- `constructThruEvent(rawBody, headers, webhookSecret)`, **async**. It returns the parsed event or throws `ThruSignatureError` (with `reason`: `missing_signature`, `bad_format`, `bad_signature` or `bad_payload`).
- `isCheckoutSessionEvent(event)`, a type guard for the `checkout.session.*` events.
- `ThruApiError` (with `status` and `payload`), thrown by the client on a non-2xx answer.
- `hmacHex`, `safeEqualHex`, and the constants `SIGNATURE_HEADER`, `EVENT_TYPE_HEADER` and `RETURN_PARAM_SESSION`/`STATUS`/`TIMESTAMP`/`SIGNATURE`.
- The subpaths `@thru-payment/server/checkout` and `@thru-payment/server/webhooks`, if you want to import less.

The full flow is in `checkout-sessions`.

## `@thru-payment/checkout-core`

A read-only client for `GET /v1/public/payments/:id`, with polling that stops once the payment reaches a final status. It never sends a key.

React:

```tsx
import { ThruProvider, usePayment } from '@thru-payment/checkout-core';

function PaymentStatus({ paymentId }: { paymentId: string }) {
  const { data: payment, loading, error } = usePayment(paymentId);
  if (loading) return <Spinner />;
  if (error || !payment) return <ErrorMessage />;
  return <MyPaymentScreen payment={payment} />;
}

<ThruProvider apiBaseUrl="https://api.thru.la/v1">
  <PaymentStatus paymentId={paymentId} />
</ThruProvider>
```

Without React (Vue, Svelte, plain JavaScript), import from the `/core` subpath:

```ts
import { createThruClient, createPaymentStore } from '@thru-payment/checkout-core/core';

const store = createPaymentStore(createThruClient(), paymentId);
const unsubscribe = store.subscribe(({ data, error, loading }) => render(data));
```

## `@thru-payment/pay-sdk`

React components for the payment screen of a direct payment. It includes the React API of `checkout-core`, so install only this one.

```tsx
import { ThruProvider, ThruCheckout } from '@thru-payment/pay-sdk';
import '@thru-payment/pay-sdk/styles.css';

<ThruProvider apiBaseUrl="https://api.thru.la/v1">
  <ThruCheckout paymentId={paymentId} onStatusChange={(payment) => console.log(payment.status)} />
</ThruProvider>
```

`<ThruCheckout>` shows the amount, the address, a QR code and the live status. To build your own layout, use the parts: `ThruRoot`, `PaymentAmount`, `PaymentAddress`, `PaymentQRCode` and `PaymentStatusBadge`. The QR code encodes only the address, so always show the amount as text next to it.

Your server creates the payment with your key and gives the browser only the payment `id`.

## Base URLs differ between package families

`@thru-payment/server` and `@thru-payment/checkout-core` take a base URL that **includes** `/v1` (default `https://api.thru.la/v1`). The `@thru-payment/x402` client takes one **without** it (default `https://api.thru.la`) and adds `/v1` itself. Do not pass one shared variable to both.

---

Related: [checkout-sessions](https://docs.thru.la/checkout-sessions.md) · [webhooks](https://docs.thru.la/webhooks.md) · [credit-topups](https://docs.thru.la/credit-topups.md)
