Skip to content

Query Status API

Monitor the progress of cross-chain payments in real-time. This API provides detailed status information and transaction history for orders.

Endpoint

GET /v1/payment/status/{orderId}

Authentication

This endpoint requires API key authentication:

Authorization: Bearer YOUR_API_KEY

Path Parameters

ParameterTypeRequiredDescription
orderIdstringOrder identifier to query

Query Parameters

ParameterTypeRequiredDescription
includeTransactionsbooleanInclude transaction details (default: false)
includeEventsbooleanInclude event history (default: false)

Example Request

GET /v1/payment/status/order_1759231185333_ghi789?includeTransactions=true&includeEvents=true
Authorization: Bearer YOUR_API_KEY

Response Format

Success Response

{
  "success": true,
  "data": {
    "orderId": "order_1759231185333_ghi789",
    "checkoutId": "checkout_1759231185333_abc123",
    "status": "completed",
    "substatus": "settled",
    "progress": {
      "currentStep": 3,
      "totalSteps": 3,
      "percentage": 100,
      "estimatedCompletion": null
    },
    "payment": {
      "paymentAmount": "100.50",
      "settlementAmount": "100.00",
      "actualReceived": "100.00",
      "paymentToken": {
        "symbol": "USDC.e",
        "address": "0x7F5c764cBc14f9669B88837ca1490cCa17c31607",
        "chainId": 10
      },
      "settlementToken": {
        "symbol": "USDC",
        "address": "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
        "chainId": 137
      }
    },
    "timing": {
      "createdAt": 1759231185,
      "startedAt": 1759231200,
      "completedAt": 1759231605,
      "totalDuration": 420,
      "estimatedDuration": 420
    },
    "fees": {
      "total": {
        "amount": "0.50",
        "amountUsd": "0.50"
      },
      "breakdown": {
        "networkFees": "0.20",
        "bridgeFees": "0.15",
        "platformFee": "0.15"
      }
    },
    "transactions": [
      {
        "stepId": 1,
        "type": "approval",
        "status": "confirmed",
        "txHash": "0x1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b",
        "chainId": 10,
        "blockNumber": 118234567,
        "gasUsed": "46000",
        "gasCost": "0.001",
        "timestamp": 1759231215
      },
      {
        "stepId": 2,
        "type": "swap",
        "status": "confirmed",
        "txHash": "0x2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c",
        "chainId": 10,
        "blockNumber": 118234580,
        "gasUsed": "299230",
        "gasCost": "0.006",
        "amountIn": "100.50",
        "amountOut": "100.25",
        "timestamp": 1759231245
      },
      {
        "stepId": 3,
        "type": "bridge",
        "status": "confirmed",
        "txHash": "0x3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d",
        "chainId": 10,
        "blockNumber": 118234590,
        "destinationTxHash": "0x4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e",
        "destinationChainId": 137,
        "destinationBlockNumber": 52847291,
        "gasUsed": "304000",
        "gasCost": "0.007",
        "amountIn": "100.25",
        "amountOut": "100.00",
        "bridgeFee": "0.15",
        "timestamp": 1759231605
      }
    ],
    "events": [
      {
        "type": "order_created",
        "timestamp": 1759231185,
        "message": "Order created successfully"
      },
      {
        "type": "payment_started",
        "timestamp": 1759231200,
        "message": "User initiated payment"
      },
      {
        "type": "approval_confirmed",
        "timestamp": 1759231215,
        "message": "Token approval confirmed",
        "txHash": "0x1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b"
      },
      {
        "type": "swap_completed",
        "timestamp": 1759231245,
        "message": "Token swap completed on source chain",
        "txHash": "0x2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c"
      },
      {
        "type": "bridge_initiated",
        "timestamp": 1759231250,
        "message": "Cross-chain bridge initiated"
      },
      {
        "type": "bridge_completed",
        "timestamp": 1759231605,
        "message": "Cross-chain bridge completed",
        "txHash": "0x4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e"
      },
      {
        "type": "payment_completed",
        "timestamp": 1759231605,
        "message": "Payment successfully completed"
      }
    ],
    "metadata": {
      "customerNote": "Express delivery requested",
      "referralCode": "FRIEND20"
    }
  },
  "timestamp": "2024-01-01T00:00:00Z"
}

Status Values

Primary Status

StatusDescription
pending_paymentWaiting for user to initiate payment
processingPayment is being processed
bridgingCross-chain transfer in progress
settlingFinal settlement in progress
completedPayment successfully completed
failedPayment failed
expiredOrder expired without payment
cancelledOrder cancelled

Substatus Values

SubstatusDescription
awaiting_approvalWaiting for token approval
swappingToken swap in progress
bridgingCross-chain bridge in progress
confirmingWaiting for block confirmations
settledFinal settlement confirmed
refundingRefund in progress
refundedRefund completed

Transaction Status

StatusDescription
pendingTransaction submitted, waiting for confirmation
confirmedTransaction confirmed on blockchain
failedTransaction failed
replacedTransaction was replaced (higher gas)

Error Responses

Order Not Found

{
  "success": false,
  "error": {
    "code": "ORDER_NOT_FOUND",
    "message": "Order not found",
    "details": {
      "orderId": "order_1759231185333_ghi789"
    }
  },
  "timestamp": "2024-01-01T00:00:00Z"
}

Access Denied

{
  "success": false,
  "error": {
    "code": "ACCESS_DENIED",
    "message": "You don't have permission to access this order",
    "details": {
      "orderId": "order_1759231185333_ghi789"
    }
  },
  "timestamp": "2024-01-01T00:00:00Z"
}

Code Examples

JavaScript/TypeScript

import { CyberPaySDK } from '@cyberpay/sdk';
 
const sdk = new CyberPaySDK({
  apiKey: 'YOUR_API_KEY',
  environment: 'production'
});
 
async function queryPaymentStatus(orderId: string) {
  try {
    const status = await sdk.payment.getStatus(orderId, {
      includeTransactions: true,
      includeEvents: true
    });
    
    console.log('Payment status:', status);
    
    // Check if payment is complete
    if (status.status === 'completed') {
      console.log('Payment completed successfully!');
      console.log('Amount received:', status.payment.actualReceived);
      return status;
    }
    
    // Check if payment failed
    if (status.status === 'failed') {
      console.log('Payment failed:', status.failureReason);
      return status;
    }
    
    // Payment still in progress
    console.log(`Payment in progress: ${status.progress.percentage}%`);
    console.log('Current step:', status.progress.currentStep, 'of', status.progress.totalSteps);
    
    return status;
  } catch (error) {
    console.error('Error querying payment status:', error);
    throw error;
  }
}
 
// Monitor payment with polling
async function monitorPayment(orderId: string, onUpdate?: (status: any) => void) {
  const maxAttempts = 120; // 10 minutes with 5-second intervals
  let attempts = 0;
  
  while (attempts < maxAttempts) {
    try {
      const status = await queryPaymentStatus(orderId);
      
      if (onUpdate) {
        onUpdate(status);
      }
      
      // Check if payment is in final state
      if (['completed', 'failed', 'expired', 'cancelled'].includes(status.status)) {
        return status;
      }
      
      // Wait before next check
      await new Promise(resolve => setTimeout(resolve, 5000));
      attempts++;
      
    } catch (error) {
      console.error('Error monitoring payment:', error);
      attempts++;
    }
  }
  
  throw new Error('Payment monitoring timeout');
}

React Hook Example

import { useState, useEffect, useCallback } from 'react';
import { CyberPaySDK } from '@cyberpay/sdk';
 
export function usePaymentStatus(orderId: string, pollInterval: number = 5000) {
  const [status, setStatus] = useState<any>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  
  const fetchStatus = useCallback(async () => {
    try {
      const sdk = new CyberPaySDK({ apiKey: process.env.REACT_APP_CYBERPAY_API_KEY });
      const result = await sdk.payment.getStatus(orderId, {
        includeTransactions: true,
        includeEvents: true
      });
      
      setStatus(result);
      setError(null);
      
      // Stop polling if payment is in final state
      if (['completed', 'failed', 'expired', 'cancelled'].includes(result.status)) {
        setLoading(false);
        return false; // Stop polling
      }
      
      return true; // Continue polling
    } catch (err: any) {
      setError(err.message);
      return false; // Stop polling on error
    }
  }, [orderId]);
  
  useEffect(() => {
    let intervalId: NodeJS.Timeout;
    
    const startPolling = async () => {
      // Initial fetch
      const shouldContinue = await fetchStatus();
      
      if (shouldContinue) {
        // Set up polling
        intervalId = setInterval(async () => {
          const shouldContinue = await fetchStatus();
          if (!shouldContinue) {
            clearInterval(intervalId);
          }
        }, pollInterval);
      }
    };
    
    startPolling();
    
    return () => {
      if (intervalId) {
        clearInterval(intervalId);
      }
    };
  }, [fetchStatus, pollInterval]);
  
  return { status, loading, error, refetch: fetchStatus };
}
 
// Usage in component
function PaymentStatusComponent({ orderId }: { orderId: string }) {
  const { status, loading, error } = usePaymentStatus(orderId);
  
  if (loading) return <div>Loading payment status...</div>;
  if (error) return <div>Error: {error}</div>;
  if (!status) return null;
  
  return (
    <div className="payment-status">
      <h3>Payment Status: {status.status}</h3>
      
      {status.progress && (
        <div className="progress">
          <div className="progress-bar" style={{ width: `${status.progress.percentage}%` }} />
          <span>Step {status.progress.currentStep} of {status.progress.totalSteps}</span>
        </div>
      )}
      
      {status.status === 'completed' && (
        <div className="success">
Payment completed successfully!
          <br />
          Amount received: {status.payment.actualReceived} {status.payment.settlementToken.symbol}
        </div>
      )}
      
      {status.status === 'failed' && (
        <div className="error">
Payment failed: {status.failureReason}
        </div>
      )}
      
      {status.transactions && (
        <div className="transactions">
          <h4>Transactions:</h4>
          {status.transactions.map((tx: any) => (
            <div key={tx.stepId} className="transaction">
              <span>{tx.type}</span>
              <span>{tx.status}</span>
              {tx.txHash && (
                <a href={`https://etherscan.io/tx/${tx.txHash}`} target="_blank" rel="noopener noreferrer">
                  View on Explorer
                </a>
              )}
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

cURL Example

# Basic status query
curl -X GET "https://api.cyberpay.org/v1/payment/status/order_1759231185333_ghi789" \
  -H "Authorization: Bearer YOUR_API_KEY"
 
# With detailed information
curl -X GET "https://api.cyberpay.org/v1/payment/status/order_1759231185333_ghi789?includeTransactions=true&includeEvents=true" \
  -H "Authorization: Bearer YOUR_API_KEY"

Python Example

import requests
import time
 
def query_payment_status(order_id, include_transactions=False, include_events=False):
    url = f"https://api.cyberpay.org/v1/payment/status/{order_id}"
    headers = {"Authorization": "Bearer YOUR_API_KEY"}
    params = {
        "includeTransactions": include_transactions,
        "includeEvents": include_events
    }
    
    response = requests.get(url, headers=headers, params=params)
    
    if response.status_code == 200:
        return response.json()
    else:
        raise Exception(f"API Error: {response.json()}")
 
def monitor_payment(order_id, max_attempts=120, poll_interval=5):
    """Monitor payment status with polling"""
    attempts = 0
    
    while attempts < max_attempts:
        try:
            status = query_payment_status(order_id, True, True)
            print(f"Payment status: {status['data']['status']}")
            
            # Check if payment is complete
            if status['data']['status'] in ['completed', 'failed', 'expired', 'cancelled']:
                return status
            
            # Show progress
            progress = status['data'].get('progress', {})
            if progress:
                print(f"Progress: {progress.get('percentage', 0)}%")
            
            time.sleep(poll_interval)
            attempts += 1
            
        except Exception as e:
            print(f"Error: {e}")
            attempts += 1
    
    raise Exception("Payment monitoring timeout")
 
# Usage
if __name__ == "__main__":
    order_id = "order_1759231185333_ghi789"
    final_status = monitor_payment(order_id)
    print(f"Final status: {final_status['data']['status']}")

WebSocket Real-Time Updates

For real-time updates without polling, use WebSocket connections:

const ws = new WebSocket('wss://api.cyberpay.org/v1/payment/stream');
 
ws.onopen = function() {
  // Subscribe to order updates
  ws.send(JSON.stringify({
    action: 'subscribe',
    orderId: 'order_1759231185333_ghi789',
    apiKey: 'YOUR_API_KEY'
  }));
};
 
ws.onmessage = function(event) {
  const update = JSON.parse(event.data);
  
  switch (update.type) {
    case 'status_update':
      console.log('Status updated:', update.data.status);
      break;
    case 'transaction_confirmed':
      console.log('Transaction confirmed:', update.data.txHash);
      break;
    case 'payment_completed':
      console.log('Payment completed!');
      ws.close();
      break;
  }
};
 
ws.onerror = function(error) {
  console.error('WebSocket error:', error);
};

Best Practices

Polling Strategy

  • Use reasonable poll intervals (5-10 seconds)
  • Implement exponential backoff on errors
  • Stop polling when payment reaches final state
  • Set maximum polling duration to avoid infinite loops

Error Handling

  • Handle network timeouts gracefully
  • Implement retry logic for temporary failures
  • Provide meaningful error messages to users
  • Log errors for debugging purposes

User Experience

  • Show real-time progress updates
  • Display estimated completion times
  • Provide transaction links for blockchain explorers
  • Handle long-running payments gracefully

Performance

  • Cache status responses when appropriate
  • Use WebSocket for real-time updates when possible
  • Batch multiple status queries when needed
  • Implement proper timeout handling

Rate Limits

  • Standard: 200 requests per minute
  • Premium: 2000 requests per minute
  • Enterprise: Custom limits available

Webhook Alternative

Instead of polling, consider using webhooks for real-time updates:

// Webhook endpoint
app.post('/webhooks/cyberpay/status', (req, res) => {
  const { orderId, status, progress } = req.body.data;
  
  console.log(`Order ${orderId} status: ${status}`);
  
  // Update your database
  updateOrderStatus(orderId, status, progress);
  
  // Notify user via WebSocket, email, etc.
  notifyUser(orderId, status);
  
  res.status(200).send('OK');
});

Next Steps

Support