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

4.5 KiB

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

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:

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_<payment-id> 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.