first commit
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
# 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_<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.
|
||||
Reference in New Issue
Block a user