Files
skills/docs/money/payment/payment-provider-square/src/square/square.md
2026-05-29 08:20:56 +01:00

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 PaymentProvider with literal name: "square".
  • Sends Authorization: Bearer <apiKey> and Square-Version on every request (Square-Version defaults to 2024-01-18; override via SquareConfig.apiVersion).
  • Sandbox toggle (sandbox: true) flips the default base URL to https://connect.squareupsandbox.com/v2; an explicit baseUrl always wins.
  • Idempotency keys filled from the injected randomId seam when the caller omits one — deterministic in tests, crypto.randomUUID() by default in production.
  • Multi-location merchants supported via the required SquareConfig.locationId field, sent as location_id on every CreatePayment body.
  • All errors raised as PaymentProviderError (foundation contract); the package itself never throws Error instances directly.
  • Universal — no node:, bun:, fs, path, crypto imports.

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 / randomId once at construction and passes them through to every httpRequest call so behaviour is deterministic across method invocations.
  • location_id is sent on CreatePayment only — Square does not require it on CompletePayment / CancelPayment / Refund calls.
  • The default randomId is a closure that calls crypto.randomUUID() at call time. Runtimes without a global crypto MUST inject their own RandomIdFn.
  • The class does not duplicate webhook verification logic; Square webhook signatures are verified by @suaveplan/payment's WebhookVerifier, which is provider-agnostic.
  • All formatting flows through the foundation: caller-supplied Content-Type / Accept are preserved by httpRequest's shallow header merge, but the Square-specific Authorization and Square-Version headers always win on collision.