H2H Response Structure

Host-to-Host API responses provide essential information for tracking and completing payment processes. This guide covers response formats, status handling, and implementation patterns.

Response Overview

The API returns a payment response containing essential information for tracking and completing the payment process.

Response Structure

The payment response includes:
  • Payment Request Identifier: A unique identifier for the created payment request that can be used to pull payment request status
  • Redirect URL: A URL to complete the payment if necessary (for payment methods requiring customer interaction)

Response Fields

FieldDescriptionType
paymentRequestIdCreated payment request identifierstring/integer
redirectUrlURL to complete payment (if required)string
statusInitial payment statusstring

Response Usage

1. Status Tracking

Use the payment request identifier to check payment status via status endpoints:
// Track payment status
async function checkPaymentStatus(paymentRequestId) {
  const response = await fetch(`/api/payment-status/${paymentRequestId}`, {
    headers: {
      'X-API-Key': apiKey
    }
  });
  
  const status = await response.json();
  return status;
}

2. Payment Completion

Redirect customers to the provided URL for payment methods requiring user interaction:
// Handle redirect if required
if (paymentResponse.redirectUrl) {
  // Redirect customer to complete payment
  window.location.href = paymentResponse.redirectUrl;
} else {
  // Payment completed directly
  handlePaymentComplete(paymentResponse);
}

3. Transaction Management

Store the identifier for future reference and reconciliation:
// Store payment reference
const paymentRecord = {
  paymentRequestId: paymentResponse.paymentRequestId,
  merchantReference: orderData.referenceId,
  amount: orderData.amount,
  currency: orderData.unit,
  status: paymentResponse.status,
  createdAt: new Date().toISOString()
};

await savePaymentRecord(paymentRecord);

Implementation Examples

Basic H2H Payment Processing

// H2H Payment Processing
async function processH2HPayment(paymentData) {
  const apiEndpoint = "https://api.cyrexa.com/h2h/payments"; // From dashboard
  const apiKey = "your-api-key-from-dashboard";
  
  try {
    const response = await fetch(apiEndpoint, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-API-Key': apiKey
      },
      body: JSON.stringify({
        amount: paymentData.amount,
        currency: paymentData.currency,
        paymentMethod: {
          type: "credit_card",
          cardNumber: paymentData.cardNumber,
          expiryMonth: paymentData.expiryMonth,
          expiryYear: paymentData.expiryYear,
          cvv: paymentData.cvv,
          cardholderName: paymentData.cardholderName
        },
        customer: {
          email: paymentData.customerEmail,
          phone: paymentData.customerPhone
        },
        billingAddress: paymentData.billingAddress,
        merchantReference: paymentData.orderId,
        notificationUrl: "https://yoursite.com/webhooks/payment-notification"
      })
    });
    
    const result = await response.json();
    
    if (response.ok) {
      console.log('Payment processed successfully:', result);
      return {
        success: true,
        transactionId: result.transactionId,
        status: result.status,
        data: result
      };
    } else {
      console.error('Payment failed:', result);
      return {
        success: false,
        error: result.error,
        message: result.message
      };
    }
  } catch (error) {
    console.error('Network error:', error);
    return {
      success: false,
      error: 'NETWORK_ERROR',
      message: error.message
    };
  }
}

Multiple Provider Handling

// Handle multiple API endpoints for different providers
const providerEndpoints = {
  'provider1': 'https://api1.cyrexa.com/h2h/payments',
  'provider2': 'https://api2.cyrexa.com/h2h/payments',
  'provider3': 'https://api3.cyrexa.com/h2h/payments'
};

async function processPaymentWithFailover(paymentData, preferredProvider = 'provider1') {
  const providers = [preferredProvider, ...Object.keys(providerEndpoints).filter(p => p !== preferredProvider)];
  
  for (const provider of providers) {
    try {
      const endpoint = providerEndpoints[provider];
      const result = await processH2HPayment({
        ...paymentData,
        endpoint: endpoint
      });
      
      if (result.success) {
        console.log(`Payment successful with ${provider}`);
        return result;
      }
    } catch (error) {
      console.warn(`Provider ${provider} failed, trying next...`);
      continue;
    }
  }
  
  throw new Error('All payment providers failed');
}

Error Handling

Common Error Codes

  • INVALID_API_KEY: API key is missing or invalid
  • INSUFFICIENT_FUNDS: Customer’s account has insufficient funds
  • CARD_DECLINED: Payment method was declined by issuer
  • INVALID_CARD: Card number or details are invalid
  • EXPIRED_CARD: Payment method has expired
  • NETWORK_ERROR: Communication error with payment provider

Retry Logic

async function processPaymentWithRetry(paymentData, maxRetries = 3) {
  let attempt = 0;
  let lastError;
  
  while (attempt < maxRetries) {
    try {
      const result = await processH2HPayment(paymentData);
      
      if (result.success) {
        return result;
      }
      
      // Check if error is retryable
      if (isRetryableError(result.error)) {
        attempt++;
        await delay(Math.pow(2, attempt) * 1000); // Exponential backoff
        continue;
      } else {
        // Non-retryable error, fail immediately
        return result;
      }
    } catch (error) {
      lastError = error;
      attempt++;
      
      if (attempt < maxRetries) {
        await delay(Math.pow(2, attempt) * 1000);
      }
    }
  }
  
  throw new Error(`Payment failed after ${maxRetries} attempts: ${lastError.message}`);
}

function isRetryableError(errorCode) {
  const retryableErrors = [
    'NETWORK_ERROR',
    'TIMEOUT',
    'TEMPORARY_UNAVAILABLE',
    'RATE_LIMITED'
  ];
  
  return retryableErrors.includes(errorCode);
}

function delay(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

Status Management

Payment Status Types

  • pending: Payment is being processed
  • completed: Payment was successful
  • failed: Payment failed
  • cancelled: Payment was cancelled
  • expired: Payment request expired

Status Polling

async function pollPaymentStatus(paymentRequestId, maxAttempts = 30, interval = 2000) {
  let attempts = 0;
  
  while (attempts < maxAttempts) {
    try {
      const status = await checkPaymentStatus(paymentRequestId);
      
      // Terminal states
      if (['completed', 'failed', 'cancelled', 'expired'].includes(status.status)) {
        return status;
      }
      
      // Continue polling for pending status
      await delay(interval);
      attempts++;
      
    } catch (error) {
      console.error('Status check failed:', error);
      attempts++;
      await delay(interval);
    }
  }
  
  throw new Error('Payment status polling timeout');
}

Response Validation

Validate Response Structure

function validatePaymentResponse(response) {
  const requiredFields = ['paymentRequestId', 'status'];
  
  for (const field of requiredFields) {
    if (!response[field]) {
      throw new Error(`Missing required field: ${field}`);
    }
  }
  
  // Validate status values
  const validStatuses = ['pending', 'completed', 'failed', 'cancelled', 'expired'];
  if (!validStatuses.includes(response.status)) {
    throw new Error(`Invalid status: ${response.status}`);
  }
  
  return true;
}

Response Processing

async function handlePaymentResponse(response) {
  try {
    // Validate response structure
    validatePaymentResponse(response);
    
    // Store payment record
    await savePaymentRecord({
      paymentRequestId: response.paymentRequestId,
      status: response.status,
      redirectUrl: response.redirectUrl,
      timestamp: new Date().toISOString()
    });
    
    // Handle based on status
    switch (response.status) {
      case 'completed':
        await handleSuccessfulPayment(response);
        break;
        
      case 'pending':
        await handlePendingPayment(response);
        break;
        
      case 'failed':
        await handleFailedPayment(response);
        break;
        
      default:
        console.warn('Unexpected payment status:', response.status);
    }
    
    return response;
    
  } catch (error) {
    console.error('Response handling error:', error);
    throw error;
  }
}

Best Practices

Response Handling

  1. Immediate Validation: Validate response structure immediately
  2. Status Tracking: Always track payment status changes
  3. Error Logging: Log all errors for debugging and monitoring
  4. Timeout Handling: Implement appropriate timeout values

Performance

  1. Connection Pooling: Use HTTP connection pooling for better performance
  2. Async Processing: Handle responses asynchronously when possible
  3. Caching: Cache non-sensitive response data appropriately
  4. Monitoring: Monitor response times and success rates

Security

  1. Response Validation: Always validate response data
  2. Sensitive Data: Never log sensitive payment information
  3. Error Messages: Sanitize error messages before displaying to users
  4. Audit Trail: Maintain audit trail of all payment responses

Next Steps

Request Structure

Learn about H2H API request format and parameters

Notifications

Set up webhook notifications for real-time status updates

Payment Methods

Explore available payment methods and implementations