> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/zkp2p/zkp2p-contracts/llms.txt
> Use this file to discover all available pages before exploring further.

# UnifiedPaymentVerifier

> Unified payment verification contract for multiple payment methods

## Overview

The `UnifiedPaymentVerifier` contract verifies payment proofs for multiple payment methods (Venmo, PayPal, Wise, etc.) using a unified, configurable architecture. This contract replaces individual payment verifiers with a single contract that can be easily swapped without affecting critical state.

**Key Features:**

* Supports multiple payment methods with custom configuration
* Uses EIP-712 signature validation for payment attestations
* Validates offchain payment attestations through the AttestationVerifier
* Prevents double-spending via nullifier registry
* Ensures trust anchor integrity for off-chain verification

**Contract Location:** `contracts/unifiedVerifier/UnifiedPaymentVerifier.sol`

## Architecture

The UnifiedPaymentVerifier inherits from `BaseUnifiedPaymentVerifier` and implements the `IPaymentVerifier` interface. It coordinates with several other contracts:

* **AttestationVerifier**: Validates witness signatures on payment attestations
* **NullifierRegistry**: Prevents payment reuse (double-spending)
* **OrchestratorRegistry**: Authorizes orchestrators to call verification
* **Orchestrator**: Provides intent data for validation

## Payment Attestation Structure

Payment attestations are EIP-712 signed messages containing:

```solidity theme={null}
struct PaymentAttestation {
    bytes32 intentHash;       // Binds the payment to the intent on Orchestrator
    uint256 releaseAmount;    // Final token amount to release on-chain after FX
    bytes32 dataHash;         // Hash of the additional data to verify integrity
    bytes[] signatures;       // Array of signatures from witnesses
    bytes data;               // Data for verification
    bytes metadata;           // Additional metadata; isn't signed by the witnesses
}
```

The `data` field contains:

```solidity theme={null}
struct PaymentDetails {
    bytes32 method;           // Payment method hash (e.g., "venmo", "paypal", "wise")
    bytes32 payeeId;          // Payment recipient ID (hashed payee details)
    uint256 amount;           // Payment amount in smallest currency unit (cents)
    bytes32 currency;         // Payment currency hash (e.g., "USD", "EUR")
    uint256 timestamp;        // Payment timestamp in UTC in milliseconds
    bytes32 paymentId;        // Hashed payment identifier from the service
}

struct IntentSnapshot {
    bytes32 intentHash;
    uint256 amount;
    bytes32 paymentMethod;
    bytes32 fiatCurrency;
    bytes32 payeeDetails;
    uint256 conversionRate;
    uint256 signalTimestamp;
    uint256 timestampBuffer;
}
```

## EIP-712 Signature Validation

The contract uses EIP-712 typed structured data hashing and signing. The domain separator is computed at deployment:

```solidity theme={null}
DOMAIN_SEPARATOR = keccak256(
    abi.encode(
        DOMAIN_TYPEHASH,
        keccak256(bytes("UnifiedPaymentVerifier")), // name
        keccak256(bytes("1")),                      // version
        block.chainid,                              // chainId
        address(this)                               // verifyingContract
    )
);
```

The PaymentAttestation type hash is:

```solidity theme={null}
bytes32 private constant PAYMENT_ATTESTATION_TYPEHASH = keccak256(
    "PaymentAttestation(bytes32 intentHash,uint256 releaseAmount,bytes32 dataHash)"
);
```

### Verification Process

The `_verifyAttestation` function:

1. Constructs the struct hash from attestation fields
2. Creates the EIP-712 digest: `keccak256("\x19\x01" || DOMAIN_SEPARATOR || structHash)`
3. Verifies data integrity by checking `keccak256(attestation.data) == attestation.dataHash`
4. Calls the AttestationVerifier to validate witness signatures

**Source:** `contracts/unifiedVerifier/UnifiedPaymentVerifier.sol:183-214`

## Payment Verification Flow

The `verifyPayment` function executes the following steps:

### 1. Decode Attestation

```solidity theme={null}
PaymentAttestation memory attestation = _decodeAttestation(_verifyPaymentData.paymentProof);
```

### 2. Decode Payment Details and Intent Snapshot

```solidity theme={null}
(
    PaymentDetails memory paymentDetails, 
    IntentSnapshot memory intentSnapshot
) = _decodeAttestationPayload(attestation.data);
```

### 3. Validate Payment Method

```solidity theme={null}
require(isPaymentMethod[paymentDetails.method], "UPV: Invalid payment method");
```

### 4. Validate Intent Snapshot

Reads the intent from the Orchestrator and validates all fields match:

* Intent hash
* Payee details
* Amount
* Payment method
* Fiat currency
* Conversion rate
* Signal timestamp
* Timestamp buffer (must be ≤ 48 hours)

**Source:** `contracts/unifiedVerifier/UnifiedPaymentVerifier.sol:220-234`

### 5. Verify Attestation Signatures

```solidity theme={null}
bool isValid = _verifyAttestation(attestation);
require(isValid, "UPV: Invalid attestation");
```

### 6. Nullify Payment

Prevents double-spending by creating a unique nullifier:

```solidity theme={null}
bytes32 nullifier = keccak256(abi.encodePacked(paymentMethod, paymentId));
_validateAndAddNullifier(nullifier);
```

The nullifier combines payment method and payment ID to prevent collisions across different payment systems.

**Source:** `contracts/unifiedVerifier/UnifiedPaymentVerifier.sol:242-245`

### 7. Calculate Release Amount

Caps the release amount to the intent amount:

```solidity theme={null}
uint256 releaseAmount = _calculateReleaseAmount(attestation.releaseAmount, intentSnapshot.amount);
```

### 8. Emit Payment Details

Emits the `PaymentVerified` event for off-chain reconciliation:

```solidity theme={null}
event PaymentVerified(
    bytes32 indexed intentHash,
    bytes32 indexed method,
    bytes32 indexed currency,
    uint256 amount,
    uint256 timestamp,
    bytes32 paymentId,
    bytes32 payeeId
);
```

**Source:** `contracts/unifiedVerifier/UnifiedPaymentVerifier.sol:53-61`

## Access Control

The `verifyPayment` function can only be called by authorized orchestrators:

```solidity theme={null}
modifier onlyOrchestrator() {
    require(orchestratorRegistry.isOrchestrator(msg.sender), "Only orchestrator can call");
    _;
}
```

This prevents griefing attacks since the verifier has write permissions on the nullifier registry.

## Configuration Management

Inherited from `BaseUnifiedPaymentVerifier`:

### Adding Payment Methods

```solidity theme={null}
function addPaymentMethod(bytes32 _paymentMethod) external onlyOwner
```

Adds a new supported payment method. The payment method should be the keccak256 hash of the lowercase method name (e.g., `keccak256("venmo")`).

### Removing Payment Methods

```solidity theme={null}
function removePaymentMethod(bytes32 _paymentMethod) external onlyOwner
```

### Updating Attestation Verifier

```solidity theme={null}
function setAttestationVerifier(address _newVerifier) external onlyOwner
```

Allows swapping the attestation verifier implementation.

**Source:** `contracts/unifiedVerifier/BaseUnifiedPaymentVerifier.sol:74-111`

## Security Considerations

### Double-Spend Prevention

The nullifier combines both payment method and payment ID to create a unique identifier:

```solidity theme={null}
bytes32 nullifier = keccak256(abi.encodePacked(paymentMethod, paymentId));
```

This prevents collisions where the same payment ID could exist across different payment methods.

### Data Integrity

The contract verifies that the data hash in the attestation matches the actual data:

```solidity theme={null}
require(
    keccak256(attestation.data) == attestation.dataHash,
    "UPV: Data hash mismatch"
);
```

This ensures witnesses signed the exact payment details being verified.

### Intent Validation

All intent fields are validated against the on-chain intent state to prevent attestation reuse or manipulation:

* Prevents attestations from being used for different intents
* Ensures payment details match what was agreed upon
* Validates timestamp buffer is within acceptable range (≤ 48 hours)

### Release Amount Capping

The release amount is always capped to the intent amount:

```solidity theme={null}
if (releaseAmount > intentAmount) {
    return intentAmount;
}
```

This prevents over-release of tokens even if the attestation specifies a higher amount.

## Example Usage

```solidity theme={null}
// Called by Orchestrator contract
IPaymentVerifier.VerifyPaymentData memory verifyData = IPaymentVerifier.VerifyPaymentData({
    intentHash: intentHash,
    paymentProof: encodedAttestation, // ABI-encoded PaymentAttestation
    data: "" // Additional data if needed
});

IPaymentVerifier.PaymentVerificationResult memory result = 
    unifiedPaymentVerifier.verifyPayment(verifyData);

if (result.success) {
    // Release result.releaseAmount tokens to the user
}
```

## Events

### PaymentVerified

```solidity theme={null}
event PaymentVerified(
    bytes32 indexed intentHash,
    bytes32 indexed method,
    bytes32 indexed currency,
    uint256 amount,
    uint256 timestamp,
    bytes32 paymentId,
    bytes32 payeeId
);
```

Emitted when a payment is successfully verified. Used for off-chain reconciliation and monitoring.

### PaymentMethodAdded

```solidity theme={null}
event PaymentMethodAdded(bytes32 indexed paymentMethod);
```

### PaymentMethodRemoved

```solidity theme={null}
event PaymentMethodRemoved(bytes32 indexed paymentMethod);
```

### AttestationVerifierUpdated

```solidity theme={null}
event AttestationVerifierUpdated(address indexed oldVerifier, address indexed newVerifier);
```

## Related Contracts

* [AttestationVerifier](/contracts/attestation-verifier) - Validates witness signatures
* [BaseUnifiedPaymentVerifier](/contracts/unified-payment-verifier) - Base configuration contract
* NullifierRegistry - Prevents double-spending
* OrchestratorRegistry - Authorizes orchestrators
