# Scheme: `pob-escrow` on `PoB`

## Summary

The `pob-escrow` scheme authorizes a machine payment by opening a bounded escrow hold on a PoB chain and proving that hold to the Resource Server or its Facilitator.

Unlike x402 `exact` on EVM, which is optimized for one-shot transfer settlement, `pob-escrow` is designed for merchant flows where the final amount is not known at the start of service:

- fueling
- parking
- metered mobility services
- other preauthorization / capture flows

The x402 payment exchange completes when the hold is verified on-chain. Final merchant settlement happens later when the merchant backend settles or releases the hold.

This scheme is intentionally designed around two PoB properties:

1. **Scalable merchant-side settlement path**

   PoB escrow routes the high-frequency authorization path through per-transaction escrow state and merchant operational accounts such as `merchantWallet` and `feeWallet`, with later redemption to merchant custody. This avoids making the merchant treasury address the direct write target for every payment authorization.

2. **Payment privacy**

   PoB is intended for deployments where payment proofs and public execution traces should not expose the full merchant revenue graph to competitors or outside observers. This includes exact payment values, timing patterns, and customer-to-merchant linkage.

## Core Payment Model

`pob-escrow` is a **proof-of-execution** scheme.

The client does one of the following:

- submits a PoB hold transaction directly and returns its proof, or
- provides a signed PoB transaction so the Facilitator can broadcast it

The proof must correspond to an on-chain call that opens a hold:

```solidity
MerchantBase(merchantContract).hold(holdAmount, merchantWallet)
```

The hold is then verified against escrow state:

```solidity
PoBERC20Escrow(tokenAddress).getHeldBalance(txId)
```

The x402 exchange is successful when the hold exists and matches the original payment requirements.

## PaymentRequirements

The `accepted` object in `PAYMENT-REQUIRED` MUST contain:

- `scheme: "pob-escrow"`
- `network`: the PoB network identifier
- `amount`: the hold amount in base units
- `asset`: the PoB escrow token address
- `payTo`: the merchant contract address that receives the `hold(...)` call
- `maxTimeoutSeconds`

The `extra` object MUST contain:

- `merchantWallet`
- `merchantOwner`
- `serviceType`
- `sessionId`

It SHOULD contain:

- `decimals`
- `currencyCode`
- `description`
- `centAligned`

Example:

```json
{
  "x402Version": 2,
  "resource": {
    "url": "https://api.example.com/parking/extend",
    "description": "Extend parking session",
    "mimeType": "application/json"
  },
  "accepted": {
    "scheme": "pob-escrow",
    "network": "eip155:42431",
    "amount": "10000000",
    "asset": "0xTokenAddress",
    "payTo": "0xMerchantContract",
    "maxTimeoutSeconds": 60,
    "extra": {
      "merchantWallet": "0xMerchantSettlementWallet",
      "merchantOwner": "0xMerchantOwner",
      "serviceType": "parking",
      "sessionId": "parking_123",
      "decimals": 6,
      "currencyCode": "USD",
      "description": "Parking extension preauthorization",
      "centAligned": true
    }
  }
}
```

### Network Identifier

If the PoB chain exposes an EVM-compatible chain ID, implementations SHOULD use a CAIP-2 network identifier of the form:

- `eip155:<chainId>`

Implementations MAY expose a PoB-specific alias internally, but interoperable x402 deployments SHOULD prefer the CAIP-2 form.

## Phase 1: `PAYMENT-SIGNATURE` Header Payload

The `payload` field MUST contain a proof that a PoB hold has been opened for the challenge.

Two payload forms are defined.

### Payload Form A: Signed Transaction

```json
{
  "x402Version": 2,
  "accepted": {
    "scheme": "pob-escrow",
    "network": "eip155:42431",
    "amount": "10000000",
    "asset": "0xTokenAddress",
    "payTo": "0xMerchantContract",
    "maxTimeoutSeconds": 60,
    "extra": {
      "merchantWallet": "0xMerchantSettlementWallet",
      "merchantOwner": "0xMerchantOwner",
      "serviceType": "parking",
      "sessionId": "parking_123"
    }
  },
  "payload": {
    "proofType": "signedTransaction",
    "signedTx": "0x...",
    "txType": "hold"
  }
}
```

### Payload Form B: Transaction Hash Proof

```json
{
  "x402Version": 2,
  "accepted": {
    "scheme": "pob-escrow",
    "network": "eip155:42431",
    "amount": "10000000",
    "asset": "0xTokenAddress",
    "payTo": "0xMerchantContract",
    "maxTimeoutSeconds": 60,
    "extra": {
      "merchantWallet": "0xMerchantSettlementWallet",
      "merchantOwner": "0xMerchantOwner",
      "serviceType": "parking",
      "sessionId": "parking_123"
    }
  },
  "payload": {
    "proofType": "txHash",
    "txHash": "0xPaymentTxHash",
    "txId": "0xHoldTxId",
    "txType": "hold"
  }
}
```

### Preferred Form

`txHash` SHOULD be preferred when the client is already a PoB transaction sender and wants the Resource Server to remain a verifier only.

`signedTransaction` SHOULD be preferred when the deployment wants the Facilitator to broadcast the transaction on behalf of the client.

## Phase 2: Verification Logic

The Resource Server or Facilitator MUST verify all of the following.

### Basic x402 Checks

1. `accepted.scheme == "pob-escrow"`
2. `accepted.network` is a supported PoB network
3. `accepted.amount`, `accepted.asset`, and `accepted.payTo` match the original `PAYMENT-REQUIRED`
4. `maxTimeoutSeconds` has not expired
5. the request has not already been satisfied for the same internal payment identifier

### PoB Transaction Checks

The proof MUST correspond to a transaction that:

1. targets `accepted.payTo` as the transaction destination
2. decodes to:

```solidity
hold(uint256 holdAmount, address merchantWallet)
```

3. uses `holdAmount == accepted.amount`
4. uses `merchantWallet == accepted.extra.merchantWallet`
5. executes successfully on-chain

If `payload.proofType == "signedTransaction"`:

1. decode the raw transaction
2. verify the call shape above
3. broadcast or confirm the transaction
4. wait for PoB confirmation according to local policy

If `payload.proofType == "txHash"`:

1. fetch the transaction and receipt by hash
2. verify the call shape above
3. confirm success and PoB confirmation according to local policy

### Escrow State Checks

After transaction success, the implementation MUST recover or verify `txId`.

The implementation MUST then query:

```solidity
getHeldBalance(txId)
```

and verify:

1. `merchantContract == accepted.payTo`
2. `merchantWallet == accepted.extra.merchantWallet`
3. `merchantOwner == accepted.extra.merchantOwner`
4. `amount == accepted.amount`
5. `customer` is non-zero

The x402 payment MUST NOT be treated as successful until the escrow state matches these checks.

## Phase 3: Settlement Logic

For `pob-escrow`, the x402 settlement step is **authorization settlement**, not final merchant capture.

That means:

- if a signed transaction payload is supplied, the Facilitator MAY broadcast it
- if a transaction hash payload is supplied, the Facilitator verifies that the hold already exists
- once the hold is verified, the payment is considered authorized for x402 purposes

The x402 settlement result SHOULD return:

- `success`
- `txHash`
- `txId`
- `sessionId`
- `merchantContract`
- `merchantWallet`
- `holdAmount`

Example:

```json
{
  "success": true,
  "network": "eip155:42431",
  "scheme": "pob-escrow",
  "txHash": "0xPaymentTxHash",
  "txId": "0xHoldTxId",
  "sessionId": "parking_123",
  "merchantContract": "0xMerchantContract",
  "merchantWallet": "0xMerchantSettlementWallet",
  "holdAmount": "10000000"
}
```

## Merchant-Side Finalization

The x402 flow completes after hold verification, but the merchant flow does not.

Final merchant-side completion happens later through:

```solidity
settle(txId, finalAmount)
```

or:

```solidity
release(txId)
```

This finalization step is outside the initial x402 request-response pair.

In a typical deployment:

- the public x402 Resource Server authorizes the hold
- the merchant service performs the real-world service
- a merchant backend later settles or releases the hold

## Why `pob-escrow` Exists

### 1. Variable-final-amount services

Fueling, parking, charging, and similar services usually require:

- payment authorization before service begins
- unknown final amount at service start
- later capture or release

These flows are escrow-shaped, not one-shot exact-transfer shaped.

### 2. Merchant hotspot reduction

Naive on-chain payment systems send every transaction directly to a small set of merchant treasury addresses. For machine commerce, this creates a high-conflict write pattern because millions of payments converge on a small merchant set.

PoB escrow reduces this problem by:

- separating customer authorization from final merchant redemption
- writing authorization state into per-transaction escrow records
- routing operational flows through `merchantWallet` and `feeWallet`
- redeeming later to merchant custody

This makes the hot path materially different from direct exact-transfer-to-treasury models.

### 3. Deterministic settlement path

PoB is intended for deployments where transaction validity is established cryptographically before publication and blocks are advanced by a deterministic publisher rather than mining or stake-based leader election. This avoids the probabilistic confirmation model commonly assumed by ordinary public-chain payment rails.

### 4. Merchant privacy

Public exact-transfer systems expose merchant revenue patterns directly through destination accounts and payment traces.

`pob-escrow` is intended for PoB deployments where:

- customer-to-merchant relationships are not exposed as a simple public transfer graph
- competitors cannot trivially reconstruct merchant sales from exact transfer streams
- payment authorization and final redemption are separated

This scheme SHOULD be used only on PoB deployments that preserve those privacy properties at the chain and proof layers.

## Security Considerations

### Authorization vs final settlement

Implementations MUST distinguish:

- x402 success for the initial request
- later merchant capture or release

An authorized hold is not the same as final merchant settlement.

### Replay protection

The Resource Server MUST ensure the same internal service request is not fulfilled twice from the same payment proof.

Implementations SHOULD bind authorization state to:

- `sessionId`
- `txId`
- `txHash`
- an internal idempotency key

### Signed transaction handling

If `signedTransaction` mode is supported:

- raw signed transactions are bearer-value payment artifacts
- they MUST be handled over TLS
- they MUST NOT be logged
- they SHOULD have narrow validity windows where possible

### Privacy downgrade

Implementations MUST NOT silently downgrade `pob-escrow` to transparent exact-transfer mechanisms such as ordinary EIP-3009-style payment authorization if privacy is part of the merchant or payer policy.

## Non-Goals

This scheme does not define:

- voucher-based streaming sessions
- cumulative off-chain payment channels
- a generic escrow scheme for all chains
- a direct replacement for x402 `exact`

It is a PoB-specific escrow authorization scheme for x402 transport.

## Reference Contract Mapping

The scheme assumes a PoB deployment with contract semantics equivalent to:

- `MerchantBase.hold(uint256 holdAmount, address merchantWallet)`
- `MerchantBase.settle(bytes32 txId, uint256 finalAmount)`
- `MerchantBase.release(bytes32 txId)`
- `PoBERC20Escrow.getHeldBalance(bytes32 txId)`

The authorization phase opens the hold through the merchant contract.

The later merchant completion phase settles or releases it.

The privacy and hotspot-reduction claims of this scheme assume a PoB settlement design where:

- the operational authorization path uses escrow state rather than direct treasury transfer
- merchant operational accounts and final custody accounts are separated
- final redemption may occur after service completion rather than on every authorization
