π POS Device Signing (Ed25519)
Every production call toPOST /api/v1/sdk/payments/process must include a cryptographic signature. This prevents spoofing β no request can be fabricated without access to the physical deviceβs private key.
How Signing Works
Generate a Key Pair on First Launch
When the POS app starts for the first time on a new device, generate an Ed25519 key pair and store the private key in the deviceβs hardware Keystore or Keychain. Never expose the private key outside the secure enclave.
Register the Device
Send the hex-encoded public key and a stable
device_uuid to TapRails when onboarding the device:Verification (Server Side)
Our backend performs the following checks in sequence:X-Device-SignatureandX-Device-Uuidheaders are present.- The
device_uuidmaps to an ACTIVE registered device for the paymentβs merchant. - The Ed25519 signature is verified against the stored public key using the raw request body.
- If any check fails β
401 Unauthorizedis returned. The payment is not processed.
π NDEF Fallback URL Security
The Dual-Path Flow introduces a web fallback for customers without a native app. To prevent URL tampering, the fallback URL is signed with the same Ed25519 private key used for native HCE invoice signing. The backend verifies the signature using the merchantβs pre-registered device public key.Signed URL Structure
| Parameter | Description |
|---|---|
id | The unique payment ID from the backend (e.g. pay_...) |
sig | Hex-encoded Ed25519 signature of the deterministic signing string |
t | Unix timestamp (seconds) from the NFC payload β matches the t field signed in the native HCE payload |
Signing Format
The value signed is the same deterministic string used for the native NFC payload. This means one signing operation covers both paths β no extra signing step is required:Backend Verification Steps
When a customer opens the fallback URL, the backend validates it in this order before returning any payment data:- Parameter validation β
sigandtare present andtis a valid integer. - Time window check β The timestamp
tmust be no more than 35 minutes in the past. This covers the 30-minute payment window plus 5 minutes of clock skew grace. Older URLs are rejected as expired regardless of signature validity. - Payment lookup β The payment record is fetched using
id. - Payload reconstruction β The backend reconstructs the exact
stringToSignusing the paymentβs storedamount,expiresAt,merchantWallet, and the URLβstparameter. - Signature verification β The backend retrieves all
ACTIVEregistered devices for the merchant and verifies thesigagainst each deviceβs public key. A match on any device is sufficient. - Auto-expiry β If the payment is
PENDINGbut pastexpiresAt, it is atomically markedEXPIREDbefore the response is returned.
Multi-device support is built in. If a merchant operates multiple POS terminals, the signature will be verified against all active registered devices β whichever device signed the URL is accepted.
π Atomic Financial Ledger
TapRails maintains a double-entry bookkeeping ledger to ensure complete financial integrity. Not a single cent of USDC can be moved without a corresponding audit record.Ledger Entry Structure
Every balance-changing event creates aMerchantTransaction record simultaneously with the payment update, inside a single atomic database transaction:
| Field | Description |
|---|---|
id | Unique ledger entry ID |
type | PAYMENT, DEBIT, CREDIT, WITHDRAWAL |
amount | Amount in USDC (decimal string) |
balance_before | Merchant balance before this event |
balance_after | Merchant balance after this event |
payment_id | Linked payment ID (if applicable) |
status | COMPLETED, PENDING, FAILED |
description | Human-readable description |
created_at | ISO 8601 timestamp |
Querying the Ledger
If a ledger entry creation fails, the entire payment confirmation is rolled back. Payments are never confirmed without a corresponding ledger record.
π‘οΈ Compliance & Sanction Screening
Every customer wallet is automatically screened for compliance before a payment is confirmed.Screening Process
- Internal Blacklist: The wallet address is checked against TapRailsβ
BlacklistedWallettable. - External Integration (optional): A hook is available to connect to 3rd-party providers like TRM Labs or Chainalysis for broader sanctions coverage.
- Rejection: If the wallet is flagged β
403 Forbiddenwith error codeCOMPLIANCE_REJECTEDis returned. No funds move.
Merchant Status Enforcement
Merchants that are notACTIVE are blocked from receiving payments at the API level:
| Merchant Status | Payment Request Result |
|---|---|
ACTIVE | β Processed |
PAUSED | β 403 Forbidden |
FROZEN | β 403 Forbidden |
BLACKLISTED | β 403 Forbidden |
SUSPENDED | β 403 Forbidden |
βοΈ High-Availability RPC
TapRails prevents single-provider failures on the Base network by supporting a multi-provider failover rail:- Configuration: Set
BASE_RPC_URLas a comma-separated list of endpoints. - Automatic Failover: Built on Viemβs
fallbacktransport. If the primary RPC is slow or down, the system cycles through backups automatically. - Strict Transfer Decoding: We decode ERC-20
Transferevent logs from the block data (not just transaction receipts) to confirm the exact amount arrived at the correct merchant wallet.
π Webhook Reliability & Signature Verification
Automatic Retries
Failed webhook deliveries are retried with exponential backoff for up to 24 hours. You can also view logs and trigger manual retries via the dashboard.Verifying Signatures
Every TapRails webhook payload includes a signature header:POST /api/v1/dashboard/webhooks/config with { "rollSecret": true }.
