@solidus-network/pay
v0.1.0
Published
Solidus Pay SDK: invoices, payments, webhook verification and an embeddable checkout for a non-custodial crypto and stablecoin checkout.
Maintainers
Readme
@solidus-network/pay
The SDK for Solidus Pay: a non-custodial checkout where a buyer pays cryptocurrency or stablecoins straight into a merchant's own wallet. Pay creates the invoice, watches the chain and tells the merchant. It never holds a key that controls buyer or merchant funds, and this package never asks for one: every write either forwards a signature your own wallet produced, or reads what Pay observed on a public chain.
Install
npm install @solidus-network/payZero runtime dependencies. viem is not one of them: this package never recovers a signature or
hashes typed data itself, so it has nothing to hash.
Create a client
import { createPayClient } from '@solidus-network/pay'
const pay = createPayClient({ apiKey: process.env.SOLIDUS_PAY_KEY! })apiKey is an sk_test_... key from the merchant dashboard. Pass baseUrl to point at a local
harness, and fetch to supply your own implementation (Node 22 and every modern browser already
have a global one).
Create an invoice and send a buyer to pay it
const invoice = await pay.invoices.create({
amount: '25.00',
currency: 'USD',
accepted: ['usdc-base-sepolia', 'usdc-sepolia'],
successUrl: 'https://shop.example/thank-you',
})
// invoice.hostedUrl is a page you can redirect the buyer to directly, or open in the embedded
// checkout from a browser (see the ./browser entry point below).Embed the checkout in your own page
import { openCheckout } from '@solidus-network/pay/browser'
const handle = openCheckout({
invoiceId: invoice.id,
onEvent(event) {
if (event.type === 'paid') handle.close()
},
})openCheckout opens a sandboxed iframe over your page and only accepts messages from the origin
it loaded the checkout from. Pass mode: 'redirect' to send the buyer to the hosted checkout page
instead of opening a modal.
Verify a webhook
import { constructEvent, PayWebhookError } from '@solidus-network/pay'
/** A Web-standard handler: works on Node 20+, Bun, Deno, Workers, and inside any framework. */
export async function handleWebhook(request: Request): Promise<Response> {
// ⚠ The RAW body. Re-serializing an object changes the bytes and the signature will not match.
const payload = await request.text()
try {
const event = constructEvent({
payload,
header: request.headers.get('solidus-signature') ?? '',
secret: process.env.SOLIDUS_PAY_WEBHOOK_SECRET!,
})
if (event.type === 'invoice.paid') {
// fulfil the order
}
return Response.json({ received: true })
} catch (err) {
// A bad signature or a stale timestamp is a 400. Anything else is yours to handle.
if (err instanceof PayWebhookError) return Response.json({ code: err.code }, { status: 400 })
throw err
}
}constructEvent needs the raw request body, not a re-serialized object: the signature covers
the exact bytes Pay sent. The header is Solidus-Signature, not X-Solidus-Signature.
Response key stability
Every response type this package exports has an exact key set, and RESPONSE_KEYS is that set at
runtime, re-exported from @solidus-network/pay-core:
import { RESPONSE_KEYS } from '@solidus-network/pay'
console.log(RESPONSE_KEYS.Invoice) // every key an Invoice response can carry, and no othersStatus
Testnet only. Every chain, asset and price this package's types describe is a testnet fact; there
is no mainnet endpoint yet. ./x402 is reserved for a future release and throws if imported today.
License
Apache-2.0
