Export as

Orchestrator

Understand OFFER-HUB's core payment orchestration architecture and how it processes transactions.

The Orchestrator is the central engine of OFFER-HUB. It coordinates every step of a transaction — from the moment a buyer deposits USDC to the moment a seller withdraws funds — enforcing rules, managing state, and communicating with the Stellar blockchain so your marketplace never has to.

Note

This guide covers the internal architecture. If you just want to make your first API call, go to Quick Start instead.

What is the Orchestrator

The Orchestrator is a state machine-driven backend service that sits between your marketplace and the Stellar blockchain. It is responsible for:

  • Managing buyer and seller internal balances
  • Locking funds into Soroban smart contracts via Trustless Work
  • Enforcing valid escrow state transitions
  • Emitting real-time events on every state change
  • Handling dispute resolution and refund flows

Your marketplace communicates with the Orchestrator via REST API. The Orchestrator handles everything on-chain.

Note

The Orchestrator is intentionally narrow in scope. It handles payments, escrow, and balances. Authentication, listings, and communication are your marketplace's responsibility.

Architecture Overview

Mermaid
Rendering diagram…

Key Components

Balance Ledger — tracks available and reserved balances for every user. All operations are atomic to prevent double-spending.

Escrow Engine — manages the lifecycle of each escrow from creation to settlement or dispute, enforcing valid state transitions.

SSE Event Stream — emits a domain event on every state change so your marketplace can react in real time.

Idempotency Key Store — caches responses to prevent duplicate operations when requests are retried.

How it Processes Transactions

Every transaction follows the same linear flow:

1. Buyer Deposits

The buyer sends USDC to their assigned Stellar address. The Orchestrator detects the on-chain payment and credits the buyer's available balance.

2. Order Created

The buyer places an order. Funds move from available to reserved in the internal ledger — no longer spendable but not yet on-chain.

typescript
const order = await client.orders.create({
  buyerId: "usr_123",
  sellerId: "usr_456",
  amount: "75.00",
  idempotencyKey: "order_abc_001",
})

3. Escrow Funded On-Chain

The Orchestrator signs and submits a Stellar transaction that locks the USDC into a Soroban smart contract via Trustless Work. The escrow state moves to FUNDED.

Note

At this point funds are held by the smart contract — not by OFFER-HUB. The Orchestrator cannot unilaterally move them. Only valid contract calls can release, dispute, or refund.

4. Work is Delivered

Your marketplace manages communication, deliverables, and reviews. The Orchestrator waits.

5. Settlement

The buyer approves the delivery. The Orchestrator submits transactions to release USDC from the smart contract to the seller's Stellar address.

typescript
const release = await client.orders.release({
  orderId: "ord_789",
  approvedBy: "usr_123",
  idempotencyKey: "release_abc_001",
})

6. Seller Withdraws

The seller requests a withdrawal. The Orchestrator signs a Stellar payment transaction sending USDC to any Stellar address.

$
POST /api/withdrawals

Escrow State Machine

Every escrow moves through a strict set of states. Invalid transitions are rejected with a 409 Conflict error.

Mermaid
Rendering diagram…
StateDescription
CREATEDOrder exists in the ledger, funds reserved but not yet on-chain
FUNDEDUSDC is locked in the Soroban smart contract
RELEASEDFunds sent to seller, escrow is complete
DISPUTEDBuyer opened a dispute, funds frozen pending resolution
REFUNDEDFunds returned to buyer's available balance
SPLITDispute resolved with partial payment to both parties
Note

Once an escrow reaches RELEASED, REFUNDED, or SPLIT it is terminal — no further transitions are allowed.

Error Handling

The Orchestrator uses standard HTTP status codes and structured error responses.

CodeHTTPDescription
INSUFFICIENT_BALANCE422Buyer's available balance is too low
INVALID_STATE_TRANSITION409Transition not allowed from the current state
DUPLICATE_IDEMPOTENCY_KEY200Already processed — cached response returned
STELLAR_TX_FAILED502On-chain transaction rejected by Stellar
ESCROW_NOT_FOUND404No escrow found with the given ID

Retrying Failed Requests

Always include an Idempotency-Key header on state-changing requests. Retrying with the same key is safe — the Orchestrator returns the cached result instead of processing twice.

typescript
const response = await fetch("/api/orders", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Idempotency-Key": "order_abc_001",
  },
  body: JSON.stringify(orderPayload),
})
Note

Never generate a new idempotency key when retrying a failed request. A new key will create a duplicate operation.

Configuration Options

bash
# Blockchain
STELLAR_NETWORK=mainnet
STELLAR_HORIZON_URL=https://horizon.stellar.org
TRUSTLESS_WORK_API_URL=https://api.trustlesswork.com

# Payment Provider
PAYMENT_PROVIDER=crypto

# Escrow timing
ESCROW_DISPUTE_WINDOW_HOURS=72
ESCROW_AUTO_RELEASE_HOURS=168

# Security
WALLET_ENCRYPTION_KEY=your-aes-256-key

# Events
SSE_KEEPALIVE_INTERVAL_MS=30000

Stellar and USDC

USDC Stellar

The Orchestrator is built exclusively for USDC on Stellar. Stellar was chosen for its low fees (fractions of a cent), fast finality (3-5 seconds), and native USDC support via Circle. Smart contracts run on Soroban, managed through the Trustless Work protocol.

Note

For local development, set STELLAR_NETWORK=testnet. You can fund test wallets using the Stellar Friendbot.

Switching to AirTM

bash
PAYMENT_PROVIDER=airtm
AIRTM_API_KEY=your-airtm-key
AIRTM_WEBHOOK_SECRET=your-webhook-secret
Note

AirTM requires users to complete KYC on their platform. Crypto mode has no KYC requirement.