Skip to main content

POS Adapter Architecture

SalesArck currently implements Square-first normalization and ingestion in apps/api/src/services.

Canonical Transaction Contract

Provider payloads are normalized to NormalizedTransaction (packages/types/src/pos.ts):

{
providerTransactionId: string,
provider: "square" | "clover",
amountMinorUnits: number,
currency: string,
transactionAt: string,
customerRef?: {
mobile?: string,
email?: string,
externalCustomerId?: string
},
lineItems?: [...]
}

Current Normalization Components

square-adapter.ts

Responsibilities:

  • read webhook/list-payment record variants (snake_case and camelCase)
  • ignore non-completed payment records
  • map money fields and timestamps
  • extract customer references when available

square-api.ts

Responsibilities:

  • ListPayments pagination API
  • optional RetrieveCustomer enrichment API

ingestion.ts

Responsibilities:

  • idempotent ingest orchestration
  • customer identity resolution
  • transaction storage and reward application

Processing Outcomes

Ingestion reports one of:

  • credited
  • stored_anonymous
  • duplicate
  • no_rule_no_credit

These outcomes make provider noise and business impact explicit.

Idempotency Strategy

Two layers are used:

  1. application-level idempotency key (idempotency_keys table)
  2. schema-level transaction unique constraint (transactions_provider_dedup)

This protects against retries and race conditions.

Clover Integration Status

Clover path is scaffolded but not yet feature-complete.

Current code base does not yet include a full Clover adapter equivalent to Square processing.

Why This Pattern Works

  • Provider-specific payload complexity is isolated.
  • Reward and wallet services remain provider-agnostic.
  • New providers can be added by implementing normalize + ingest integration without rewriting reward logic.
Written byDhruv Doshi