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:
ListPaymentspagination API- optional
RetrieveCustomerenrichment API
ingestion.ts
Responsibilities:
- idempotent ingest orchestration
- customer identity resolution
- transaction storage and reward application
Processing Outcomes
Ingestion reports one of:
creditedstored_anonymousduplicateno_rule_no_credit
These outcomes make provider noise and business impact explicit.
Idempotency Strategy
Two layers are used:
- application-level idempotency key (
idempotency_keystable) - 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.
Related Docs
Written byDhruv Doshi