first commit
This commit is contained in:
175
docs/money/payment/payment-provider-square/src/square/square.md
Normal file
175
docs/money/payment/payment-provider-square/src/square/square.md
Normal file
@@ -0,0 +1,175 @@
|
||||
# SquareProvider
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## 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
|
||||
|
||||
```ts
|
||||
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`:
|
||||
|
||||
```ts
|
||||
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.
|
||||
Reference in New Issue
Block a user