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

# E-commerce Checkout

> Verify customers during payment to prevent fraud

# E-commerce Checkout Verification

Protect your e-commerce platform from payment fraud by verifying customers before processing transactions.

## Business Scenario

During checkout, you want to:

1. Verify that the customer's mobile number is genuine
2. Check if the payment VPA belongs to the customer
3. Screen the VPA against blocklists
4. Make a risk-based decision on whether to proceed

## Architecture

```mermaid theme={null}
sequenceDiagram
    participant Customer
    participant YourApp as Your App
    participant TxnCheck as TxnCheck
    participant Payment as Payment Gateway

    Customer->>YourApp: Enter mobile + VPA
    YourApp->>TxnCheck: POST /full-check
    TxnCheck-->>YourApp: Verification result
    
    alt Risk Score < 50
        YourApp->>Payment: Process payment
        Payment-->>YourApp: Payment success
        YourApp-->>Customer: Order confirmed
    else Risk Score >= 50
        YourApp-->>Customer: Additional verification required
    end
```

## Implementation

### Step 1: Create Verification Service

```typescript theme={null}
// services/checkout-verification.ts
import { TxnCheckClient } from './txncheck';

export interface VerificationInput {
  mobile: string;
  paymentVpa: string;
  orderAmount: number;
}

export interface VerificationResult {
  approved: boolean;
  riskScore: number;
  riskLevel: 'low' | 'medium' | 'high';
  customerName?: string;
  requiresOTP: boolean;
  blockedReason?: string;
}

export class CheckoutVerificationService {
  private client: TxnCheckClient;
  
  // Risk thresholds
  private readonly LOW_RISK_THRESHOLD = 30;
  private readonly HIGH_RISK_THRESHOLD = 70;
  private readonly HIGH_VALUE_ORDER = 50000; // INR

  constructor(apiKey: string) {
    this.client = new TxnCheckClient({ apiKey });
  }

  async verifyCheckout(input: VerificationInput): Promise<VerificationResult> {
    const result: VerificationResult = {
      approved: false,
      riskScore: 0,
      riskLevel: 'low',
      requiresOTP: false,
    };

    try {
      // Use sync mode for real-time checkout
      const verification = await this.client.fullCheck(input.mobile, { sync: true });

      if (verification.status !== 'COMPLETED') {
        // If verification fails, require OTP for security
        result.riskScore = 100;
        result.riskLevel = 'high';
        result.requiresOTP = true;
        return result;
      }

      // Calculate risk score based on multiple factors
      result.riskScore = this.calculateRiskScore(verification, input);
      result.riskLevel = this.getRiskLevel(result.riskScore);
      
      // Extract customer name for display
      result.customerName = this.extractCustomerName(verification);
      
      // Decision logic
      if (result.riskScore >= this.HIGH_RISK_THRESHOLD) {
        result.approved = false;
        result.requiresOTP = true;
      } else if (result.riskScore >= this.LOW_RISK_THRESHOLD) {
        // Medium risk: approve but require OTP for high-value orders
        result.approved = true;
        result.requiresOTP = input.orderAmount >= this.HIGH_VALUE_ORDER;
      } else {
        // Low risk: approve without additional verification
        result.approved = true;
        result.requiresOTP = false;
      }

      return result;

    } catch (error) {
      console.error('Verification error:', error);
      // On error, require OTP as fallback
      result.riskScore = 75;
      result.riskLevel = 'high';
      result.requiresOTP = true;
      return result;
    }
  }

  private calculateRiskScore(verification: any, input: VerificationInput): number {
    let score = 0;
    const result = verification.result || {};
    
    // Check 1: VPA ownership (50 points if not linked)
    const userVpas = (result.upiByMobile?.upi || []).map((v: string) => v.toLowerCase());
    const paymentVpaLower = input.paymentVpa.toLowerCase();
    
    if (!userVpas.includes(paymentVpaLower)) {
      score += 50;
    }

    // Check 2: Blocklist status (100 points if blocklisted)
    const blocklisted = (result.vpaChargebackCheck?.blocklisted || [])
      .map((v: any) => v.vpa.toLowerCase());
    
    if (blocklisted.includes(paymentVpaLower)) {
      return 100; // Immediate rejection
    }

    // Check 3: KYC availability (20 points if no KYC)
    if (!result.kycByMobile?.pan) {
      score += 20;
    }

    // Check 4: Few linked VPAs might indicate new/suspicious account
    if (userVpas.length < 2) {
      score += 10;
    }

    return Math.min(score, 100);
  }

  private getRiskLevel(score: number): 'low' | 'medium' | 'high' {
    if (score < this.LOW_RISK_THRESHOLD) return 'low';
    if (score < this.HIGH_RISK_THRESHOLD) return 'medium';
    return 'high';
  }

  private extractCustomerName(verification: any): string | undefined {
    const result = verification.result || {};
    return result.kycByMobile?.fullName || result.upiByMobile?.name;
  }
}
```

### Step 2: Integrate into Checkout Flow

```typescript theme={null}
// routes/checkout.ts
import express from 'express';
import { CheckoutVerificationService } from '../services/checkout-verification';

const router = express.Router();
const verificationService = new CheckoutVerificationService(process.env.FRAUD_BUSTER_API_KEY!);

router.post('/verify-payment', async (req, res) => {
  const { mobile, paymentVpa, orderAmount, orderId } = req.body;

  // Validate input
  if (!mobile || !paymentVpa || !orderAmount) {
    return res.status(400).json({ error: 'Missing required fields' });
  }

  try {
    const verification = await verificationService.verifyCheckout({
      mobile,
      paymentVpa,
      orderAmount,
    });

    // Log for audit
    console.log(`Order ${orderId}: Risk=${verification.riskScore}, Level=${verification.riskLevel}`);

    if (!verification.approved && !verification.requiresOTP) {
      // Hard block
      return res.status(403).json({
        success: false,
        message: 'Payment method not accepted',
        reason: verification.blockedReason || 'Security check failed',
      });
    }

    res.json({
      success: true,
      approved: verification.approved,
      customerName: verification.customerName,
      requiresOTP: verification.requiresOTP,
      riskLevel: verification.riskLevel,
    });

  } catch (error) {
    console.error('Checkout verification error:', error);
    // Fail open with OTP requirement
    res.json({
      success: true,
      approved: true,
      requiresOTP: true,
      riskLevel: 'high',
    });
  }
});

export default router;
```

### Step 3: Frontend Integration

```typescript theme={null}
// components/Checkout.tsx
import React, { useState } from 'react';

interface CheckoutFormData {
  mobile: string;
  paymentVpa: string;
}

export function CheckoutPage({ orderAmount }: { orderAmount: number }) {
  const [formData, setFormData] = useState<CheckoutFormData>({ mobile: '', paymentVpa: '' });
  const [verification, setVerification] = useState<any>(null);
  const [showOTP, setShowOTP] = useState(false);
  const [loading, setLoading] = useState(false);

  const handleVerify = async () => {
    setLoading(true);
    
    try {
      const response = await fetch('/api/checkout/verify-payment', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          ...formData,
          orderAmount,
          orderId: 'ORDER-123',
        }),
      });

      const result = await response.json();
      setVerification(result);

      if (!result.success) {
        alert(result.message);
        return;
      }

      if (result.requiresOTP) {
        setShowOTP(true);
      } else {
        // Proceed to payment
        handlePayment();
      }

    } catch (error) {
      console.error('Verification failed:', error);
      // Show OTP as fallback
      setShowOTP(true);
    } finally {
      setLoading(false);
    }
  };

  const handlePayment = () => {
    // Integrate with your payment gateway
    console.log('Processing payment...');
  };

  return (
    <div className="checkout-form">
      <h2>Payment Details</h2>
      
      <input
        type="tel"
        placeholder="Mobile Number (+91...)"
        value={formData.mobile}
        onChange={(e) => setFormData({ ...formData, mobile: e.target.value })}
      />
      
      <input
        type="text"
        placeholder="UPI ID (user@bank)"
        value={formData.paymentVpa}
        onChange={(e) => setFormData({ ...formData, paymentVpa: e.target.value })}
      />

      {verification?.customerName && (
        <p className="verified-name">
          ✓ Verified: {verification.customerName}
        </p>
      )}

      {showOTP ? (
        <OTPVerification onVerified={handlePayment} />
      ) : (
        <button onClick={handleVerify} disabled={loading}>
          {loading ? 'Verifying...' : 'Proceed to Pay'}
        </button>
      )}

      {verification?.riskLevel === 'high' && (
        <p className="risk-warning">
          Additional verification required for your security
        </p>
      )}
    </div>
  );
}
```

## Edge Cases

### Handle API Timeout

```typescript theme={null}
async verifyWithFallback(input: VerificationInput): Promise<VerificationResult> {
  try {
    // Set a shorter timeout for checkout
    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), 10000); // 10s timeout

    const result = await this.verifyCheckout(input);
    clearTimeout(timeoutId);
    return result;

  } catch (error) {
    if (error.name === 'AbortError') {
      console.warn('Verification timeout, using fallback');
    }
    
    // Fallback: approve with OTP requirement
    return {
      approved: true,
      riskScore: 50,
      riskLevel: 'medium',
      requiresOTP: true,
    };
  }
}
```

### High-Value Order Protection

```typescript theme={null}
// For orders above threshold, always require additional verification
if (input.orderAmount >= 100000) { // 1 lakh INR
  result.requiresOTP = true;
  
  // Also check if this is customer's first large order
  const isFirstLargeOrder = await this.checkOrderHistory(input.mobile);
  if (isFirstLargeOrder) {
    result.riskScore += 20;
  }
}
```

### Returning Customer Optimization

```typescript theme={null}
// Skip verification for trusted returning customers
async verifyWithCache(input: VerificationInput): Promise<VerificationResult> {
  const cachedVerification = await this.cache.get(`verified:${input.mobile}`);
  
  if (cachedVerification && cachedVerification.riskLevel === 'low') {
    // Still check blocklist for returning customers
    const blocklistCheck = await this.client.vpaChargebackCheck(
      [input.paymentVpa], 
      { sync: true }
    );
    
    if (blocklistCheck.result?.blocklisted?.length === 0) {
      return {
        ...cachedVerification,
        customerName: cachedVerification.customerName,
      };
    }
  }
  
  // Full verification for new or risky customers
  return this.verifyCheckout(input);
}
```

## Production Recommendations

<CardGroup cols={2}>
  <Card title="Use Sync Mode" icon="bolt">
    For checkout flows, use `async: false` to get immediate results without polling
  </Card>

  <Card title="Set Timeouts" icon="clock">
    Set appropriate timeouts (10-15s) to avoid blocking checkout
  </Card>

  <Card title="Fail Open" icon="shield-check">
    On API failures, approve with OTP rather than blocking customers
  </Card>

  <Card title="Log Everything" icon="file-lines">
    Log all verifications for audit and chargeback disputes
  </Card>
</CardGroup>

## Metrics to Track

| Metric               | Description                         | Target    |
| -------------------- | ----------------------------------- | --------- |
| Verification Latency | Time to complete verification       | \< 5s p95 |
| Approval Rate        | % of checkouts approved             | > 95%     |
| False Positive Rate  | Legitimate customers blocked        | \< 2%     |
| Fraud Rate           | Fraudulent orders that passed       | \< 0.1%   |
| OTP Conversion       | Users who complete OTP verification | > 80%     |

## Related Guides

<CardGroup cols={2}>
  <Card title="Customer Onboarding" icon="user-plus" href="/use-cases/customer-onboarding">
    KYC verification during registration
  </Card>

  <Card title="Security Best Practices" icon="shield" href="/security/overview">
    Secure your integration
  </Card>
</CardGroup>
