Skip to main content

FPSF-SS-001 — Stablecoin Stack

Document Metadata

FieldValue
Spec IDFPSF-SS-001
TitleStablecoin Stack
Version1.0.0
StatusDraft
Date2026-03-25
AuthorAdalton Reis — reis@fabricpaymentstandards.org
OrganizationFabric Payment Standards Foundation
Contactspecs@fabricpaymentstandards.org>
LicenseApache-2.0
AdoptsFPSF-CPD-001 v1.0.0

Table of Contents

  1. Abstract
  2. Introduction
  3. Vision and Design Philosophy
  4. Relationship to FPSF-CPD-001
  5. System Architecture
  6. Component Specifications
  7. Cryptographic Conventions
  8. Data Structures
  9. Submission Payloads
  10. The Acquiring Model
  11. Settlement Contract — State Variables
  12. Settlement Contract — Events
  13. Settlement Contract — Fee Model
  14. Settlement Contract — Functions
  15. Validation Rules
  16. Submission Flow
  17. End-to-End Payment Flow
  18. Error Handling
  19. Security Model
  20. Conformance
  21. Future Work
  22. Normative References

1. Abstract

This document is the foundational specification of the Stablecoin Stack — an open architecture for processing stablecoin payments on Ethereum-compatible networks. It establishes the design philosophy, system architecture, component responsibilities, cryptographic conventions, data structures, submission protocols, on-chain contract interface, end-to-end payment flow, security model, and conformance requirements for the full stack.

The Stablecoin Stack is built on the proposition that cryptographic signatures are a superior foundation for payment authorization compared to identity-based delegation models, and that the tooling to make this practical for everyday users is both achievable and urgently needed. All architectural decisions follow directly from this proposition, and from the foundational model defined in FPSF-CPD-001.


2. Introduction

2.1 Purpose

This specification provides the normative definitions required to implement, deploy, audit, or assess conformance of any component of the Stablecoin Stack. It is intended for:

  • developers integrating a wallet or checkout front-end with the payment processor API
  • smart-contract engineers implementing or auditing a conformant Settlement Contract
  • payment processor operators who need to understand the on-chain guarantees upon which their off-chain service depends
  • auditors, regulators, and reviewers assessing correctness, security, and compliance of an existing implementation

2.2 Scope

This specification covers: design principles; system architecture; cryptographic conventions; all data structures; the two primary submission flows (payment transfer and acquirer registration); the full on-chain Settlement Contract interface; validation rules; the end-to-end payment flow; and the security model.

This specification does not cover: the Checkout Engine API exposed to merchant servers (FPSF-SS-003, planned); the Broadcast Layer WebSocket protocol (FPSF-SS-002); individual microservice interface specifications; key management and custody procedures; token issuance and redemption mechanics; and regulatory compliance guidance.

2.3 Normative References

See Section 22.

2.4 Conventions and Terminology

The key words MUST, MUST NOT, REQUIRED, SHALL, SHALL NOT, SHOULD, SHOULD NOT, RECOMMENDED, MAY, and OPTIONAL are to be interpreted as described in RFC 2119.

TermDefinition
Stablecoin StackThe full set of components, protocols, and interfaces specified by this document.
Payment ProcessorAn operator that deploys and runs a conformant instance of the Stablecoin Stack.
RelayerThe account that submits signed transactions on-chain, absorbing gas costs. Operated by the Payment Processor.
MerchantA business or individual that accepts stablecoin payments.
PayerThe individual who holds the stablecoin and signs the payment commitment.
AcquirerA registered third-party participant entitled to a share of the processing fee on payments they referred.
Service ProviderAn Acquirer who has opted in to public discovery via the Basic Data Service.
Settlement ContractThe on-chain Solidity smart contract that verifies signatures, executes token transfers, and distributes fees.
Permit SignatureAn ERC-2612 off-chain signature authorizing the Settlement Contract to call permit() on the token contract.
Binding SignatureAn EIP-712 signature over the full operation parameters, authorizing the processor to submit the operation.
ChargeA processor-issued record representing an expected payment.
Order ReferenceA 16-byte reference created by the merchant for tracking. Embedded in PermittedTransfer events.
Payload IDA client-generated identifier for idempotency.
Acquirer IDA bytes16 UUID identifying a registered Acquirer within the Settlement Contract.
Zero-UUIDThe Acquirer ID consisting entirely of zero bytes. Indicates absence of an Acquirer.
Ephemeral TokenA short-lived, single-use token issued by the checkout widget for wallet session retrieval.
Principal AmountThe token amount intended to reach the beneficiary before fee deduction.
Total With FeesPrincipal Amount plus all applicable fees. The value the payer must hold and authorize.
Base FeeAn absolute minimum fee in token units, charged on every transfer. Per-token.
Basis Points FeeA percentage-based fee component of the operator fee, expressed in basis points. Per-token.
Operator FeeThe combined per-transfer fee earned by the Payment Processor: Base Fee plus Basis Points Fee. Both components are per-token.
Acquiring FeeAn additional percentage-based fee charged on behalf of a registered Acquirer. Per-token.
AdministratorThe privileged account that controls Settlement Contract operational parameters. MUST NOT have ability to move user funds.
Hex stringA UTF-8 string with 0x prefix followed by lowercase hexadecimal digits.
EVM AddressA 20-byte account identifier: 0x + 40 lowercase hex digits.

3. Vision and Design Philosophy

3.1 User-Centric by Design

Technical complexity MUST NOT be a barrier to participation. The payer encounters a flow as simple as any mobile payment. All complexity — signature construction, nonce management, fee calculation, gas payment — MUST be absorbed by the infrastructure.

3.2 Payer Sovereignty

The authority to authorize a payment belongs exclusively to the payer, expressed through cryptographic signature. No component can move funds without a valid signed authorization. The system is payer-agnostic: the identity of the account that submits a transaction is irrelevant to its validity.

3.3 Gas Abstraction as a Baseline

Gas is always paid by the Relayer, which recovers costs through the Operator Fee. The user holds only the stablecoin they wish to spend.

3.4 Complementary Rails, Not Replacement

The Stablecoin Stack is a set of additional payment rails, particularly suited to cross-border transfers, privacy-preserving payments, contexts without a shared banking relationship, and scenarios where programmable settlement logic adds value. It is designed to coexist with existing payment infrastructure.

3.5 Open by Default

All specifications, reference implementations, and supporting materials are published under open licenses. There are no proprietary extensions, no closed conformance test suites, and no tollgates on the information required to build a compliant implementation.


4. Relationship to FPSF-CPD-001

This specification adopts the Canonical Payment Definition (FPSF-CPD-001 v1.0.0) as its foundational payment model. Each stablecoin payment processed by the Stablecoin Stack is a Digital Payment in the sense of CPD-001. The mapping is as follows:

CPD-001 ConceptStablecoin Stack Equivalent
PayerThe token holder whose Permit Signature and Binding Signature authorize the transfer
PayeeThe beneficiary address receiving the principal amount
Valueprincipal amount in the specified ERC-2612 stablecoin
AuthorizationProofThe dual-signature structure: permitSig + payWithPermitSig
ExecutionContextThe Settlement Contract address, chain ID, and network
CREATEDTransferRequest constructed and signed by the payer
AUTHORIZEDTransferRequest validated by the Payment Processor
IN_FLIGHTTransaction submitted to the network by the Relayer
SETTLEDPermittedTransfer event confirmed by transfer-history
FAILEDTransaction reverted or validation failure

All CPD-001 invariants (uniqueness, integrity, authorization binding, deterministic state, finality) MUST be preserved by conformant implementations.


5. System Architecture

5.1 Principal Actors

ActorRole
MerchantCreates charges and receives settlement confirmation. Integrates with the Checkout Engine over mTLS.
PayerHolds the stablecoin, signs the payment commitment, submits the payload via a compliant wallet. Never submits transactions directly to the network.
Payment ProcessorOperates the off-chain infrastructure (Checkout Engine, Broadcast Layer, Relayer).
AcquirerA registered third party who distributes the payment service and earns a share of the processing fee.

5.2 Component Overview

ComponentSubsystemKey Requirement
Settlement ContractOn-chainMUST satisfy all requirements of Sections 11–14.
core-checkout-engineCheckout EngineMUST expose merchant API over mTLS. MUST reconcile charges on confirmed events.
checkout-public-widgetCheckout EngineMUST issue single-use Ephemeral Tokens.
ca-serverCheckout EngineMUST issue mTLS certificates for merchant sessions.
login-serverCheckout EngineMUST issue JWT tokens within mTLS sessions.
credentials-managerCheckout EngineMUST manage certificate lifecycle.
merchant-dashboardCheckout EngineSHOULD provide real-time order monitoring and fee reporting.
wallet-gatewayBroadcast LayerMUST accept TransferRequest and BuyAcquiringPackRequest payloads. MUST maintain WebSocket connections.
broadcast-serviceBroadcast LayerMUST manage submission state transitions. MUST NOT treat broadcast without revert as final settlement.
broadcast-submitterBroadcast LayerMUST be the sole component that issues on-chain transactions.
balance-and-historyBroadcast LayerSHOULD maintain real-time wallet balance views and deliver transfer notifications.
transfer-historyEvent IndexingMUST monitor the Settlement Contract for PermittedTransfer events. MUST wait for configurable confirmation depth before publishing.
basic-data-serverShared InfrastructureMUST expose a publicly accessible read-only API: supported token list, Service Provider registry, processor configuration.
Client WalletClientMUST satisfy all wallet requirements of Section 6.6.

5.3 Trust Boundaries

Fully trusted by the payer: the payer's own private key and the wallet software that manages it.

Trusted by the merchant within a verified mTLS session: the core-checkout-engine.

Trusted to relay faithfully, not trusted with funds: the Payment Processor and Relayer. They cannot forge payments — valid signatures are required.

Trusted as a shared public resource: the basic-data-server. No private state; public reference data only.

Trust-minimized — verified on-chain: the Settlement Contract. Its behavior is determined by deployed bytecode, verified by the blockchain network.


6. Component Specifications

6.1 Settlement Contract

The on-chain trust anchor. Responsibilities: verifying both signatures; preventing replay attacks via usedHashes; transferring payment amounts; computing and distributing Operator Fees and Acquiring Fees (all per-token); crediting principal to the beneficiary; registering Acquirers. Fully specified in Sections 11–14.

6.2 Checkout Engine

Manages the lifecycle of payment sessions from charge creation through reconciliation. The core-checkout-engine exposes an API to merchants over mTLS. The checkout-public-widget issues Ephemeral Tokens and delivers session parameters to wallets.

6.3 Broadcast Layer

Receives, validates, queues, submits, and provides real-time status for signed payment payloads. wallet-gateway is the external entry point. broadcast-service manages submission lifecycle. broadcast-submitter holds the Relayer account and is the sole component that issues on-chain transactions. balance-and-history maintains wallet balance views.

6.4 Event Indexing

transfer-history monitors the Settlement Contract for PermittedTransfer events. After configurable block confirmations, it publishes confirmed events to the Checkout Engine for charge reconciliation. It has no write authority over other components.

6.5 Basic Data Service

basic-data-server is a publicly accessible, read-only service providing: supported ERC-2612 stablecoins; registered Acquirers who opted in to public discovery; and Payment Processor public configuration including the Settlement Contract address.

6.6 Client Wallet

A conformant wallet MUST be able to: retrieve session details from the checkout widget via Ephemeral Token; read the payer's ERC-2612 nonce; construct and sign PermitParams using EIP-712; construct and sign PayWithPermitParams using EIP-712; assemble and submit a TransferRequest; maintain a WebSocket connection for status updates; present payment status clearly.

The wallet MUST NOT transmit the payer's private key or seed phrase to any remote service. All signing MUST be performed locally on the user's device.


7. Cryptographic Conventions

7.1 Signature Algorithm

All signatures use ECDSA over secp256k1, as specified in FIPS 186-4 — the same scheme used natively by the Ethereum protocol.

7.2 Typed Data Signing (EIP-712)

Every signature MUST be computed over a structured hash per EIP-712:

digest = keccak256(
0x1901
|| domainSeparator
|| hashStruct(message)
)

The Permit Signature MUST use the token contract's domain separator. The Binding Signature MUST use the Settlement Contract's domain separator. The two domains MUST always be distinct.

7.3 Signature Representation

FieldFormatRequiredDescription
hashbytes32 — 66-char hex stringREQUIREDEIP-712 digest that was signed.
vuint8 — integerREQUIREDRecovery identifier. MUST be 27 or 28. Values 0 or 1 MUST be normalized by adding 27.
rbytes32 — 66-char hex stringREQUIREDECDSA r component.
sbytes32 — 66-char hex stringREQUIREDECDSA s component.

All hex strings MUST use 0x prefix and lowercase digits. Leading zero bytes MUST be preserved.


8. Data Structures

8.1 Signature Object — ERC20RelayerSig

Reused as permitSig and payWithPermitSig across all payload types.

FieldFormatRequiredConstraints
hashbytes32 hex stringREQUIRED0x + 64 lowercase hex chars.
vintegerREQUIRED27 or 28. Normalize 0→27, 1→28. Reject all other values.
rbytes32 hex stringREQUIREDFull 32 bytes, zero-padded.
sbytes32 hex stringREQUIREDFull 32 bytes, zero-padded.

8.2 ERC-2612 Permit Parameters — PermitParams

FieldFormatRequiredDescription
ownerEVM addressREQUIREDToken holder granting approval.
spenderEVM addressREQUIREDAddress authorized to transfer tokens. Typically the Settlement Contract.
valueuint256REQUIREDTotal token amount approved, including all fees.
nonceuint256REQUIREDCurrent ERC-2612 nonce of the owner on the token contract.
deadlineuint256REQUIREDPermit expiry Unix timestamp (seconds).

8.3 Payment Transfer Parameters — PayWithPermitParams

FieldFormatRequiredDescription
tokenEVM addressREQUIREDERC-2612-compliant stablecoin contract address.
beneficiaryEVM addressREQUIREDMerchant's receiving address.
orderReferencebytes16 — 34-char hex stringREQUIRED16-byte Order Reference. 0x + 32 hex chars. Zero bytes if absent.
acquirerIdbytes16 — 34-char hex stringREQUIRED16-byte Acquirer ID. Use Zero-UUID if no acquirer. Always present.
permitParamsPermitParamsREQUIREDowner MUST match address recovered from payWithPermitSig.

Note: orderReference and acquirerId are separate fields. They MUST NOT be concatenated. The Settlement Contract uses them independently — orderReference for event emission and charge reconciliation, acquirerId for fee routing.

8.4 Acquirer Registration Parameters — BuyAcquiringPackPermitParams

FieldFormatRequiredDescription
tokenEVM addressREQUIREDERC-2612-compliant token for registration fee. Must be in acquiringAllowedTokens.
feeValueuint256REQUIREDRegistration fee. MUST equal permitParams.value and current acquiringPrice.
acquiringEVM addressREQUIREDWallet to register as Acquirer. Must not already be registered.
acquiringFeeBpsuint256REQUIREDAcquiring fee in basis points. Must not exceed maxAcquiringFeeBps for the token.
permitParamsPermitParamsREQUIREDvalue MUST equal feeValue.

9. Submission Payloads

9.1 Two-Signature Pattern

Every submission carries exactly two signature objects:

SignatureSignsVerified by
permitSigERC-2612 Permit typed-dataThe ERC-2612 token contract on-chain
payWithPermitSigFull operation parametersThe Payment Processor off-chain and the Settlement Contract on-chain

Neither signature alone is sufficient to execute a payment.

9.2 Payment Transfer Payload — TransferRequest

FieldRequiredDescription
payWithPermitParamsREQUIREDPayment parameters as defined in Section 8.3.
payWithPermitSigREQUIREDBinding Signature over payWithPermitParams. Recovered signer MUST equal permitParams.owner.
permitSigREQUIREDPermit Signature. Forwarded verbatim to the Settlement Contract.
payloadIdREQUIREDClient-generated UUID- Idempotency key

9.3 Acquirer Registration Payload — BuyAcquiringPackRequest

FieldRequiredDescription
buyAcquiringPackParamsREQUIREDRegistration parameters as defined in Section 8.4.
payWithPermitSigREQUIREDBinding Signature over buyAcquiringPackParams.
permitSigREQUIREDPermit Signature. Signed value MUST equal buyAcquiringPackParams.feeValue.

10. The Acquiring Model

10.1 Overview

The Stablecoin Stack incorporates a first-class acquiring model allowing third-party participants — Acquirers — to distribute the payment service and earn a share of the processing fee. Acquirers are identified by a bytes16 UUID Acquirer ID. When a payer includes a non-zero Acquirer ID in a payment, the Settlement Contract distributes the Acquiring Fee to the Acquirer's balance atomically with the payment.

10.2 Acquirer Registration

Registration is performed by submitting a BuyAcquiringPackRequest. Requirements: payment of the acquiringPrice in an accepted stablecoin; selection of an Acquiring Fee in basis points, subject to the per-token maxAcquiringFeeBps ceiling; and the wallet address to be registered.

10.3 Fee Distribution

Every payment is subject to:

FeeTypeApplicabilityPer-Token
Base Fee (baseFeeAmount[token])Absolute, in token unitsEvery transferYes
Basis Points Fee (operatorBps[token])Percentage of principalEvery transferYes
Acquiring Fee (acquiringFeeBps[acquirer][token])Percentage of principalOnly when non-zero Acquirer IDYes

The Operator Fee is the sum of the Base Fee and the Basis Points Fee, both evaluated for the specific token being transferred. The Basis Points Fee is applied to the principal amount.

When no Acquirer is involved, the payer MUST pass Zero-UUID as the acquirerId. This suppresses the Acquiring Fee without reverting.

10.4 Service Provider Discovery

An Acquirer MAY publish their information to the basic-data-server to appear as a Service Provider, enabling wallets to present them to payers.


11. Settlement Contract — State Variables

A conformant Settlement Contract MUST maintain the following state:

/// @notice Base fee per token, in token units. Charged on every transfer.
mapping(address => uint256) public baseFeeAmount;

/// @notice Operator fee in basis points per token.
/// Applied to the principal amount on every transfer.
mapping(address => uint256) public operatorFeeBps;

/// @notice Maximum acquiring fee in basis points per token.
/// Enforced at acquirer registration and update.
mapping(address => uint256) public maxAcquiringFeeBps;

/// @notice Acquiring fee in basis points per acquirer per token.
mapping(address => mapping(address => uint256)) public acquiringFeeBps;

/// @notice Internal token balances per participant.
/// Mapping: token address => participant address => amount.
mapping(address => mapping(address => uint256)) public balances;

/// @notice Registry of consumed Binding Signature hashes.
/// A hash present here MUST cause the transaction to revert.
mapping(bytes32 => bool) public usedHashes;

/// @notice Registry mapping Acquirer IDs to wallet addresses.
mapping(bytes16 => address) public acquirerWallets;

/// @notice Set of ERC-2612-compliant tokens accepted for acquirer registration.
mapping(IERC20Permit => bool) public acquiringAllowedTokens;

/// @notice Price in token units required to register as an Acquirer.
/// Per-token, keyed by token address.
mapping(address => uint256) public acquiringPrice;

Key design notes:

  • baseFeeAmount and operatorFeeBps are both per-token. A transfer's Operator Fee is baseFeeAmount[token] + (principal * operatorFeeBps[token] / 10000).
  • acquiringFeeBps is per-acquirer per-token. An Acquirer may have different fee rates on different tokens.
  • maxAcquiringFeeBps is per-token. The ceiling for Acquirer fee rates on that token.
  • acquiringPrice is per-token. The registration cost may differ by token.

12. Settlement Contract — Events

A conformant Settlement Contract MUST emit the following events:

/// @notice Emitted upon successful execution of a permitted token transfer.
event PermittedTransfer(
bytes32 indexed domainSeparator,
address indexed token,
address indexed payer,
address beneficiary,
uint256 value,
uint256 operatorFee,
uint256 acquiringFee,
bytes16 orderReference,
bytes16 acquirerId
);

/// @notice Emitted when a new Acquirer is registered.
event AcquirerCreated(
bytes16 indexed acquirerId,
address indexed wallet,
address indexed token,
uint256 feeBps
);

/// @notice Emitted when an Acquirer updates their fee.
event AcquiringFeeUpdated(
address indexed acquiring,
address indexed token,
uint256 feeBps
);

/// @notice Emitted when an acquiring fee is generated on a payment.
event CommissionGenerated(
bytes16 indexed acquirerId,
address indexed token,
uint256 amount
);

/// @notice Emitted when a participant withdraws their internal balance.
event Withdrawal(
address indexed owner,
address indexed beneficiary,
address indexed token,
uint256 amount
);

Notes:

  • PermittedTransfer emits orderReference and acquirerId as separate bytes16 fields, not concatenated. The transfer-history service uses orderReference for charge reconciliation; acquirerId is retained for audit.
  • operatorFee and acquiringFee are emitted separately to enable transparent fee attribution.

13. Settlement Contract — Fee Model

13.1 Operator Fee Structure

The Operator Fee for a given transfer is:

operatorFee = baseFeeAmount[token] + floor(principal * operatorFeeBps[token] / 10000)

Both components are configured per token. baseFeeAmount[token] provides a floor ensuring at minimum the cost of on-chain execution is recovered. operatorFeeBps[token] adds a percentage component scaled to the transfer value.

13.2 Acquiring Fee

The Acquiring Fee for a given transfer is:

acquiringFee = floor(principal * acquiringFeeBps[acquirerWallet][token] / 10000)

This is zero when acquirerId is Zero-UUID.

13.3 Total With Fees

totalWithFees = principal + operatorFee + acquiringFee

The PermitParams.value MUST equal totalWithFees.

13.4 calculateFees

function calculateFees(
address token,
uint256 principal,
bytes16 acquirerId
) public view returns (
uint256 operatorFee,
uint256 acquiringFee,
uint256 totalWithFees
)
ParameterDescription
tokenThe ERC-2612 stablecoin address.
principalThe amount intended to reach the beneficiary, in token units.
acquirerIdThe Acquirer ID. Pass Zero-UUID if no acquirer.

Returns the operatorFee, acquiringFee, and totalWithFees for the specified token and acquirer.

13.5 breakdownTransferAmount

function breakdownTransferAmount(
address token,
uint256 totalWithFees,
bytes16 acquirerId
) public view returns (
uint256 principal,
uint256 operatorFee,
uint256 acquiringFee
)

Decomposes a total amount into its constituent parts. Inverse of calculateFees.


14. Settlement Contract — Functions

14.1 transferWithPermit

Executes a complete stablecoin payment: verifies both signatures, collects the full amount from the payer, distributes fees, and credits the principal to the beneficiary.

function transferWithPermit(
IERC20Permit token,
address tokenOwner,
uint256 amount,
uint256 deadline,
uint8 v1, bytes32 r1, bytes32 s1,
uint8 v2, bytes32 r2, bytes32 s2,
address beneficiary,
bytes16 orderReference,
bytes16 acquirerId
) external
ParameterDescription
tokenERC-2612-compliant stablecoin.
tokenOwnerToken holder.
amountTotal amount inclusive of all fees. MUST equal PermitParams.value.
deadlinePermit expiry.
v1, r1, s1Permit Signature — validated by the ERC-2612 token contract.
v2, r2, s2Binding Signature — validated by the Settlement Contract.
beneficiaryMerchant's receiving address.
orderReference16-byte order reference for reconciliation.
acquirerId16-byte Acquirer ID. Pass Zero-UUID if no acquirer.

MUST revert if: Binding Signature digest in usedHashes; recovered signer not authorized; deadline <= block.timestamp; token.permit() reverts; tokenOwner balance less than amount.

On success: Binding Signature digest recorded in usedHashes; amount transferred from tokenOwner; Operator Fee distributed to processor fee recipient; Acquiring Fee credited to Acquirer balance (if non-zero acquirerId); principal credited to beneficiary; PermittedTransfer emitted; CommissionGenerated emitted if acquiring fee applies.

14.2 buyAcquiringPack

Registers a new Acquirer. The payer and the wallet being registered may differ.

function buyAcquiringPack(
IERC20Permit token,
address payer,
address acquiring,
uint256 acquiringFeeBps_,
uint256 price,
uint256 deadline,
uint8 v1, bytes32 r1, bytes32 s1,
uint8 v2, bytes32 r2, bytes32 s2
) public
ParameterDescription
tokenToken for fee. Must be in acquiringAllowedTokens.
payerToken holder paying the fee.
acquiringWallet to register. Must not already be registered.
acquiringFeeBps_Acquiring fee in basis points. Must not exceed maxAcquiringFeeBps[token].
priceRegistration fee. MUST equal acquiringPrice[token].
deadlinePermit expiry.
v1, r1, s1Permit Signature.
v2, r2, s2Binding Signature.

MUST revert if: token not in acquiringAllowedTokens; acquiringFeeBps_ exceeds maxAcquiringFeeBps[token]; price != acquiringPrice[token]; acquiring already registered; Binding Signature digest in usedHashes; token.permit() reverts; payer balance less than price.

On success: Acquirer registered with a new bytes16 Acquirer ID; AcquirerCreated emitted.

14.3 calculateFees

See Section 13.4.

14.4 breakdownTransferAmount

See Section 13.5.

14.5 getBalances

function getBalances(
address token,
address[] calldata users
) external view returns (uint256[] memory)

Returns internal balances for multiple participants in a single token. Response array is parallel to users.

function getBalances(
address[] calldata tokens,
address[] calldata users
) external view returns (uint256[][] memory)

Returns balances[i][j] = internal balance of users[j] for tokens[i].

14.6 getAcquiringWallet

function getAcquiringWallet(
bytes16 acquirerId
) public view returns (address)

Returns the wallet address registered under acquirerId, or the zero address if not found.


15. Validation Rules

A conformant Payment Processor MUST enforce all of the following before broadcasting. Any failure MUST result in immediate rejection.

15.1 Structural Validation

  • All REQUIRED fields MUST be present and non-empty.
  • All hex strings MUST have 0x prefix and consist of lowercase hexadecimal digits.
  • Address fields MUST be exactly 42 characters (0x + 40 hex digits).
  • bytes32 fields MUST be exactly 66 characters (0x + 64 hex digits).
  • bytes16 fields MUST be exactly 34 characters (0x + 32 hex digits).
  • v MUST be in {0, 1, 27, 28}. Values 0 and 1 MUST be normalized to 27 and 28. All others MUST be rejected.
  • Numeric fields MUST represent non-negative integers in the uint256 range.

15.2 Semantic Validation

  • deadline MUST be strictly greater than processor clock at time of receipt. Processors SHOULD apply a configurable tolerance (recommended: 30 seconds) for clock skew.
  • nonce MUST match the current on-chain permit nonce of owner on the specified token contract.
  • payloadId MUST be unique. The processor MUST reject otherwise.
  • orderReference and acquirerId MUST each be exactly 16 bytes (bytes16).
  • For BuyAcquiringPackRequest: feeValue MUST equal permitParams.value.

15.3 Cryptographic Validation

  • The signer recovered from payWithPermitSig MUST equal permitParams.owner.
  • The processor SHOULD verify permitSig locally before broadcasting to avoid on-chain reversion.
  • The hash field in each ERC20RelayerSig MUST equal the digest recomputed from the accompanying parameters.

16. Submission Flow

16.1 Payment Transfer — Step by Step

  1. Checkout Engine issues a payment session: orderReference, beneficiary, token, principal amount, acquirerId, and deadline.
  2. Client Wallet fetches the ERC-2612 nonce for owner from the token contract or via wallet-gateway.
  3. Client Wallet calls calculateFees(token, principal, acquirerId) to determine the Operator Fee and Acquiring Fee. Computes permitValue = principal + operatorFee + acquiringFee.
  4. Client Wallet constructs PermitParams: owner, spender (Settlement Contract), value = permitValue, nonce, deadline.
  5. Client Wallet signs the ERC-2612 Permit typed-data using EIP-712 against the token contract's domain separatorpermitSig.
  6. Client Wallet constructs PayWithPermitParams: token, beneficiary, orderReference, acquirerId, permitParams.
  7. Client Wallet signs PayWithPermitParams using EIP-712 against the Settlement Contract's domain separatorpayWithPermitSig.
  8. Client Wallet generates a payloadId (UUID) and assembles the TransferRequest.
  9. Client Wallet submits the TransferRequest to wallet-gateway over HTTPS.
  10. Payment Processor validates per Section 15 and broadcasts the transaction on success.
  11. Settlement Contract executes permit(), transferFrom(), and fee distribution atomically.

16.2 Acquirer Registration — Step by Step

  1. Client Wallet fetches acquiringPrice[token] and maxAcquiringFeeBps[token] from the Settlement Contract or via wallet-gateway.
  2. Client Wallet fetches the ERC-2612 nonce.
  3. Client Wallet constructs PermitParams: value = acquiringPrice[token].
  4. Client Wallet signs the Permit typed-data → permitSig.
  5. Client Wallet constructs BuyAcquiringPackPermitParams: token, feeValue, acquiring, acquiringFeeBps_, permitParams.
  6. Client Wallet signs BuyAcquiringPackPermitParamspayWithPermitSig.
  7. Client Wallet assembles and submits BuyAcquiringPackRequest.
  8. Settlement Contract executes registration and emits AcquirerCreated.

17. End-to-End Payment Flow

Phase A — Session Creation

  1. Merchant Server sends charge creation request to the core-checkout-engine over mTLS: amount, token, beneficiary address, order metadata, deadline.
  2. core-checkout-engine generates a 16-byte orderReference. Creates a charge session in awaiting-payment state.
  3. core-checkout-engine returns a Checkout Widget URL and Ephemeral Token to the Merchant Server, which presents it to the payer.

Phase B — Payload Construction and Signing

  1. Client Wallet redeems the Ephemeral Token against the checkout widget to retrieve session parameters. Token is invalidated on first use.
  2. Wallet fetches ERC-2612 nonce and fee breakdown.
  3. Wallet constructs and signs PermitParams (Permit Signature against token contract domain separator).
  4. Wallet constructs and signs PayWithPermitParams (Binding Signature against Settlement Contract domain separator).

Phase C — Submission and Off-Chain Validation

  1. Wallet assembles TransferRequest and submits to wallet-gateway. WebSocket connection maintained for status updates.
  2. broadcast-service enqueues the submission; wallet-gateway acknowledges receipt.
  3. Payment Processor validates per Section 15. Any failure causes immediate rejection.

Phase D — On-Chain Settlement

  1. broadcast-submitter calls transferWithPermit on the Settlement Contract.
  2. Settlement Contract verifies Binding Signature, checks usedHashes, calls permit(), grants allowance.
  3. Settlement Contract calls transferFrom, distributes Operator Fee, distributes Acquiring Fee (if applicable), credits principal to beneficiary, records Binding Signature hash in usedHashes, emits PermittedTransfer. All operations are atomic.
  4. broadcast-service delivers a status update confirming the transaction was broadcast. This is not final settlement.

Phase E — Confirmation and Reconciliation

  1. transfer-history observes PermittedTransfer event. After configurable block confirmations, publishes the confirmed event to core-checkout-engine.
  2. core-checkout-engine matches orderReference against its session registry and marks the charge settled.
  3. core-checkout-engine dispatches settlement webhook to Merchant Server.
  4. balance-and-history delivers final settlement notification to wallet

18. Error Handling

Error CategoryDescription
STRUCTURAL_ERRORRequired field absent or encoding violated. Reject immediately; no on-chain activity.
SEMANTIC_ERRORStructurally valid but a business rule violated (expired deadline, nonce mismatch, repeated payloadId).
CRYPTOGRAPHIC_ERRORSignature cannot be verified or recovered signer does not match owner. MUST NOT disclose details aiding forgery.
BROADCAST_ERRORPayload passed all validation but on-chain transaction reverted. Record failure; MUST NOT mark session settled.

19. Security Model

19.1 Threat Model

ThreatMitigation
Forged paymentECDSA signature; only the payer's private key produces a valid signature
Replay attackusedHashes in Settlement Contract; ERC-2612 nonce on token contract
Parameter substitutionBinding Signature covers all parameters including orderReference and acquirerId separately; modification invalidates the signature
Processor substituting beneficiaryBeneficiary embedded in Binding Signature
Expired permitSettlement Contract enforces block.timestamp <= deadline
Relayer double-spendImpossible without valid Binding Signature; Relayer has no signing authority
Merchant impersonationmTLS between merchant servers and core-checkout-engine

19.2 Dual-Signature Binding

The Permit Signature authorizes a token allowance. The Binding Signature authorizes the specific operation — token, beneficiary, amount, orderReference, and acquirerId. Neither alone is sufficient to execute a payment.

19.3 Replay Protection

Two independent layers: the ERC-2612 nonce (per owner, per token contract) and the usedHashes registry (per Binding Signature digest, in the Settlement Contract). Both MUST be active. They are complementary, not redundant.

19.4 Payer-Agnostic Execution

Any party holding both valid signatures can submit the transaction. This is acceptable because: signatures bind the payment to specific parameters; usedHashes prevents reuse; submitting on behalf of a payer is precisely what they authorized. Clients MUST submit payloads over TLS and MUST NOT persist signed payloads in environments accessible to untrusted parties.

19.5 Administrative Privilege Boundaries

The Administrator MUST be able to: set per-token baseFeeAmount, operatorFeeBps, maxAcquiringFeeBps, acquiringPrice; set the fee recipient address.

The Administrator MUST NOT be able to: transfer tokens held in participant balances; modify usedHashes; alter a registered Acquirer's wallet address or fee rate without their consent.

These constraints MUST be enforced at the contract code level and verified by independent audit.


20. Conformance

20.1 Fully Conformant Stack

A fully conformant deployment MUST include: a deployed Settlement Contract satisfying Sections 11–14; a Checkout Engine satisfying Section 6.2; a Broadcast Layer satisfying Sections 6.3 and 15; a transfer-history service; and a Basic Data Service. A conformant Client Wallet MUST satisfy Section 6.6.

20.2 Partial Conformance

Components MAY be implemented independently. Partial conformance MUST be declared explicitly with clear indication of which sections are and are not satisfied.

20.3 Companion Specifications

Spec IDSubject
FPSF-SS-002wallet-gateway Interface
FPSF-SS-003Checkout Engine API (planned)
FPSF-SS-004Broadcast Layer Protocol (planned)
FPSF-SS-005Client Wallet Certification (planned)

21. Future Work

Offline Transaction Signing. Enabling payers to construct and sign a payment commitment without live network access. Priority for populations with limited connectivity.

Multi-Network Support. Extending conformance to non-EVM networks with equivalent programmable token standards.

Wallet Certification. A conformance test suite and certification programme for third-party wallet implementations.

orderReference field semantics. A future minor version will formalize the orderReference encoding and any extensions the merchant may embed.


22. Normative References

ReferenceDescription
FPSF-CPD-001 v1.0.0Canonical Payment Definition
ERC-20Token Standard — Ethereum Improvement Proposal 20
ERC-2612Permit Extension for EIP-20 Signed Approvals
EIP-712Typed Structured Data Hashing and Signing
ECDSA / FIPS 186-4Digital Signature Standard
RFC 2119Key words for use in RFCs to Indicate Requirement Levels
RFC 4648The Base16, Base32, and Base64 Data Encodings
RFC 8446The Transport Layer Security (TLS) Protocol Version 1.3
RFC 8705OAuth 2.0 Mutual-TLS Client Authentication
Solidity 0.8.xSmart Contract Programming Language
Semantic Versioning 2.0.0https://semver.org

FPSF-SS-001 v1.0.0 · Draft · Fabric Payment Standards Foundation · Apache-2.0