# Square payment mapping ## Purpose Translates between Square's `Payment` envelope (as returned by `POST /v2/payments`, `POST /v2/payments/{id}/complete`, `POST /v2/payments/{id}/cancel`, and `GET /v2/payments/{id}`) and `@suaveplan/payment`'s `PaymentIntent` shape. Square's status values are upper-snake-case (`COMPLETED`, `APPROVED`, ...) while uses the lower-case `PaymentStatus` union; the mapping pins this contract in one place so the provider class stays focused on transport. The mapper also captures the two implicit branches Square encodes via optional fields rather than dedicated states: `approved_money` present ↔ manual capture, `card_details` present ↔ card-shaped `PaymentMethod`. ## Features - `mapPaymentStatus(squareStatus)` — exhaustive five-status map with a defensive `"failed"` fallback for forward-rolled API versions. - `mapPayment(p)` — full envelope-to-`PaymentIntent` shape including `capturedAmount` derivation (`p.total_money` on `COMPLETED`, `0n` otherwise), `captureMethod` derivation (`approved_money` ↔ `"manual"`), and optional `PaymentMethod` derivation from `card_details`. - `SquareCard` / `SquareMoney` / `SquarePaymentDto` — typed views of the Square API envelope that downstream code can rely on instead of threading `unknown` through call sites. - Card-brand normalisation — Square returns brands upper-case; the mapper lower-cases and validates against the `CardBrand` union, falling back to `"unknown"` for non-canonical brands (Square gift cards, JCB sub-brands, etc.). ## Usage: Basic Example ```ts import { mapPayment, mapPaymentStatus, } from "@suaveplan/payment-provider-square"; mapPaymentStatus("COMPLETED"); // "captured" const intent = mapPayment({ id: "sq_pay_123", 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", }); console.log(intent.status); // "captured" console.log(intent.capturedAmount.amount); // 5000n ``` ## Usage: Advanced Example Map an authorized-but-not-captured payment with card metadata: ```ts import { mapPayment } from "@suaveplan/payment-provider-square"; const intent = mapPayment({ id: "sq_pay_456", status: "APPROVED", amount_money: { amount: 9999, currency: "EUR" }, total_money: { amount: 9999, currency: "EUR" }, approved_money: { amount: 9999, currency: "EUR" }, customer_id: "cust_42", note: "Pre-authorisation for hold", card_details: { card: { card_brand: "MASTERCARD", last_4: "4242" } }, created_at: "2026-02-01T10:00:00Z", updated_at: "2026-02-01T10:00:00Z", }); console.log(intent.captureMethod); // "manual" console.log(intent.status); // "authorized" console.log(intent.capturedAmount.amount); // 0n console.log(intent.paymentMethod); // { id: "sq_card_sq_pay_456", type: "card", brand: "mastercard", last4: "4242" } ``` ## API Reference ### `mapPaymentStatus(squareStatus: string): PaymentStatus` Maps Square status → suaveplan status. Known mappings: | Square | suaveplan | |---|---| | `APPROVED` | `"authorized"` | | `COMPLETED` | `"captured"` | | `CANCELED` | `"canceled"` | | `FAILED` | `"failed"` | | `PENDING` | `"pending"` | | _other_ | `"failed"` | ### `mapPayment(p: SquarePaymentDto): PaymentIntent` Shape the envelope. `capturedAmount.amount` is `BigInt(p.total_money.amount)` when `p.status === "COMPLETED"`, otherwise `0n`. `captureMethod` is `"manual"` when `p.approved_money` is set. `paymentMethod` is set when `p.card_details` is present. `refundedAmount.amount` is `0n` (refund totals are tracked by `PaymentEngine`, not derived here). `refunds` is an empty array. ### Types - `SquareMoney` — `{ amount: number; currency: string }`. - `SquareCard` — `{ card_brand: string; last_4: string }`. - `SquarePaymentDto` — the subset of Square's `Payment` envelope this package consumes. ## Implementation Notes - The status map is frozen at module init so accidental mutation throws in strict mode. - The card-brand normaliser is intentionally conservative: any brand Square introduces that does not match the `CardBrand` union falls back to `"unknown"` so downstream code never receives an invalid discriminant. - `paymentMethod.id` is synthesised as `sq_card_` because Square does not expose a stable card-fingerprint on the inline `card_details.card` object. Consumers needing a true stable card id should hydrate it from Square's Cards API and overlay it.