6.1 KiB
SquareProvider
(no submodules)
Purpose
SquareProvider is the Square Payments API adapter for
@suaveplan/payment. It implements the structural PaymentProvider
contract with name: "square" (narrowed at the type level) and
routes every outbound HTTP call through the
@suaveplan/payment-providers foundation's httpRequest, which
handles JSON serialisation, the per-call timeout signal, external
AbortSignal composition, and typed PaymentProviderError raising.
The provider preserves the upstream square.ts surface verbatim — the
six lifecycle methods (createIntent, confirmIntent, captureIntent,
cancelIntent, refund, getIntent), the canonical request bodies,
and the status mapping — while wiring the previously-hard-coded
globalThis.crypto.randomUUID() call onto the foundation's injectable
RandomIdFn seam. Tests pass a deterministic counter instead of
monkey-patching globalThis.fetch.
Features
- Implements
PaymentProviderwith literalname: "square". - Sends
Authorization: Bearer <apiKey>andSquare-Versionon every request (Square-Versiondefaults to2024-01-18; override viaSquareConfig.apiVersion). - Sandbox toggle (
sandbox: true) flips the default base URL tohttps://connect.squareupsandbox.com/v2; an explicitbaseUrlalways wins. - Idempotency keys filled from the injected
randomIdseam when the caller omits one — deterministic in tests,crypto.randomUUID()by default in production. - Multi-location merchants supported via the required
SquareConfig.locationIdfield, sent aslocation_idon everyCreatePaymentbody. - All errors raised as
PaymentProviderError(foundation contract); the package itself never throwsErrorinstances directly. - Universal — no
node:,bun:,fs,path,cryptoimports.
Usage: Basic Example
import { SquareProvider } from "@suaveplan/payment-provider-square";
const square = new SquareProvider({
apiKey: process.env.SQUARE_ACCESS_TOKEN ?? "",
locationId: process.env.SQUARE_LOCATION_ID ?? "",
});
const intent = await square.createIntent({
amount: { amount: 5000n, currency: "USD" },
description: "Order #123",
});
const captured = await square.captureIntent(intent.id);
console.log(captured.status); // "captured"
Usage: Advanced Example
Inject deterministic seams (FetchLike + RandomIdFn) for testing,
target the sandbox, pin the API version, and compose with an external
AbortSignal:
import type { FetchLike } from "@suaveplan/payment-providers";
import { SquareProvider } from "@suaveplan/payment-provider-square";
const controller = new AbortController();
const fakeFetch: FetchLike = async (url, init) => {
return new Response(
JSON.stringify({
payment: {
id: "sq_pay_1",
status: "COMPLETED",
amount_money: { amount: 5000, currency: "USD" },
total_money: { amount: 5000, currency: "USD" },
created_at: "2026-01-01T00:00:00Z",
updated_at: "2026-01-01T00:00:00Z",
},
}),
{ status: 200 }
);
};
const square = new SquareProvider({
apiKey: "sandbox-token",
locationId: "L1",
sandbox: true,
apiVersion: "2024-09-19",
fetch: fakeFetch,
signal: controller.signal,
randomId: () => "deterministic-idempotency-key",
timeoutMs: 5_000,
});
const intent = await square.createIntent({
amount: { amount: 5000n, currency: "USD" },
});
API Reference
class SquareProvider implements PaymentProvider
constructor(config: SquareConfig)
Accepts a SquareConfig (see ../types/config.md). Required:
apiKey, locationId. Optional: baseUrl, sandbox, apiVersion,
randomId, fetch, signal, timeoutMs, webhookSecret.
readonly name: "square"
Literal — narrows at the type level for discriminated unions.
createIntent(input: CreatePaymentIntentInput): Promise<PaymentIntent>
POSTs <baseUrl>/payments with amount_money, idempotency_key
(falls back to randomId()), autocomplete (true unless
captureMethod === "manual"), location_id, and the optional
customer_id, note, source_id fields. Returns the mapped
PaymentIntent.
confirmIntent(intentId: string): Promise<PaymentIntent>
No-op for Square (payments are auto-confirmed). Delegates to
getIntent(intentId).
captureIntent(intentId: string): Promise<PaymentIntent>
POSTs <baseUrl>/payments/<intentId>/complete with no body.
cancelIntent(intentId: string): Promise<PaymentIntent>
POSTs <baseUrl>/payments/<intentId>/cancel with no body.
refund(input: RefundInput): Promise<Refund>
POSTs <baseUrl>/refunds with payment_id, idempotency_key
(falls back to randomId()), optional amount_money, and optional
reason. Returns the mapped Refund.
getIntent(intentId: string): Promise<PaymentIntent>
GETs <baseUrl>/payments/<intentId>.
Throws
Every method throws PaymentProviderError on HTTP failure, timeout,
or JSON parse failure (raised by the foundation's httpRequest).
err.provider is always "square"; err.httpStatus is set when the
failure originated from a non-ok response.
Implementation Notes
- The provider stores its
FetchLike/AbortSignal/timeoutMs/randomIdonce at construction and passes them through to everyhttpRequestcall so behaviour is deterministic across method invocations. location_idis sent onCreatePaymentonly — Square does not require it onCompletePayment/CancelPayment/Refundcalls.- The default
randomIdis a closure that callscrypto.randomUUID()at call time. Runtimes without a globalcryptoMUST inject their ownRandomIdFn. - The class does not duplicate webhook verification logic; Square
webhook signatures are verified by
@suaveplan/payment'sWebhookVerifier, which is provider-agnostic. - All formatting flows through the foundation: caller-supplied
Content-Type/Acceptare preserved byhttpRequest's shallow header merge, but the Square-specificAuthorizationandSquare-Versionheaders always win on collision.