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
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.inputcan be a URL, a URL string,URLSearchParamsor 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 throwsThruSignatureError(withreason:missing_signature,bad_format,bad_signatureorbad_payload).isCheckoutSessionEvent(event), a type guard for thecheckout.session.*events.ThruApiError(withstatusandpayload), thrown by the client on a non-2xx answer.hmacHex,safeEqualHex, and the constantsSIGNATURE_HEADER,EVENT_TYPE_HEADERandRETURN_PARAM_SESSION/STATUS/TIMESTAMP/SIGNATURE.- The subpaths
@thru-payment/server/checkoutand@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:
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:
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.
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.