Accept payments with Square in DaloyJS
Square gives you one API across in-person, online, and recurring payments — useful when the same merchant takes both Tap to Pay at the counter and Apple Pay through your web app. This guide uses the modern square TypeScript SDK (v40+, currently v44.x — a full rewrite from the pre-v40 line), Square's Web Payments SDK on the client for tokenisation, and WebhooksHelper.verifySignature on the server for webhook auth.
What you should know up front
- v40+ is a full rewrite. If you see
squareConnect,new Client({ bearerAuthCredentials }), orpaymentsApi.createPaymentin tutorials, you're looking at the legacy SDK. The new client isSquareClient, calls areclient.payments.create(...), and parameters are camelCase. The legacy surface is still shipped assquare/legacyfor migration only — don't start there in 2026. - Money is
BigInt, in the smallest unit.{ amount: BigInt("1000"), currency: "USD" }means $10.00. Pass a plainnumberand TypeScript will (correctly) yell at you;JSON.stringifying a BigInt without a replacer will throw at runtime. See the serialisation note below. - You don't charge a card — you charge a source ID. The Web Payments SDK on the client returns a single-use token (
cnon:...for cards,cash:, Apple Pay nonces, etc.) that your server passes assourceId. Raw PANs never touch your code. - Always send an
idempotencyKey. Required on every mutating call (payments.create,refunds.refundPayment, orders, etc.). A UUID per logical attempt is the right shape — re-use it on retries. - Webhook verification needs three things, not two.
WebhooksHelper.verifySignaturewants the raw body, the signature header, the signature key, and the exactnotificationUrlyou registered in the dashboard. Get the URL even slightly wrong (trailing slash, http vs https, behind a proxy that strips the host) and every event will look invalid.
1. Provision
- Sign in at developer.squareup.com and create an application.
- Grab a Sandbox access token and your Application ID from Credentials. Also note a Location ID from Locations — every payment needs one.
- Under Webhooks → Subscriptions, add an endpoint pointing at your DaloyJS route, subscribe to at least
payment.updated,payment.created, andrefund.updated, and copy the Signature Key. Save the full URL exactly as Square shows it.
2. Install
3. Environment variables
4. Plugin
5. Create a payment
The client uses Square's Web Payments SDK with your SQUARE_APPLICATION_ID + SQUARE_LOCATION_ID, tokenises the card, and posts the sourceId to this endpoint.
BigInt + JSON gotcha:JavaScript's default JSON serialiser throws on BigInt. Map money to strings at the response edge (amountMinor.toString()) or use a custom replacer. DaloyJS's Zod responses already coerce BigInt to string when you declare the response as z.string(); declare a z.bigint() only when both ends agree on it.
6. Webhook
Square retries non-2xx responses with backoff for up to 72 hours. Once the signature checks out, ack with 200 even for event types you don't handle, and do the slow work asynchronously.
7. Refunds
Runtimes
The v40+ SDK is Fern-generated and uses the platform fetch when available, falling back to node-fetch. Square officially supports Node.js 18+, Vercel (Edge and Node), Cloudflare Workers, Deno 1.25+, Bun 1.0+, and React Native — so the same plugin runs on Edge runtimes unchanged. The only thing to watch is reading the raw body: on Edge, use await request.text() instead of readRawBodyif your adapter doesn't expose Node streams.
Errors
Non-2xx responses throw SquareError. Inspect err.statusCode, err.body, and the structured err.errors[] array — each entry has category (e.g. PAYMENT_METHOD_ERROR), code (e.g. CARD_DECLINED, CVV_FAILURE), detail, and an optional field. Map them through problem+json with type: square:<category>:<code> so reconciliation tools can join them later.
Modernisation notes
- Use the new SDK, not
square/legacy.The legacy export exists so v39 codebases can migrate piecemeal — there's no reason to start a new integration on it in 2026. New features ship to the new client first (or only). - Pin a Square API version in production. The SDK is tied to a Square API version per release, and a new SDK major (v40 → v41 → ...) can be a breaking change. Pin
squareto a caret range you control and read the changelog before bumping. - Iterate paginated endpoints with
for await. List responses are async-iterable:for (const item of pageable)(synchronously) only gives you the first page; usefor await (const item of pageable)to walk all pages without manual cursor juggling. - Verify webhooks; don't trust the source IP.Square's IP ranges change. The HMAC + the registered notification URL together prove authenticity and that the request hit the right endpoint.
See also the payments overview, Braintree guide, Authorize.Net guide, and problem+json errors.