Skip to main content

PHP Integration

This guide shows how to integrate TxnCheck API into your PHP application.

Installation

composer require guzzlehttp/guzzle

Using cURL

cURL is built into PHP, no installation needed.

Basic Client Setup (Guzzle)

<?php

namespace App\Services;

use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;

class TxnCheckClient
{
    private Client $client;
    private string $apiKey;
    private ?string $secretKey;

    public function __construct(
        string $apiKey,
        ?string $secretKey = null,
        string $baseUrl = 'https://api.txncheck.in/api/v1'
    ) {
        $this->apiKey = $apiKey;
        $this->secretKey = $secretKey;
        
        $this->client = new Client([
            'base_uri' => $baseUrl,
            'timeout' => 30,
            'headers' => [
                'X-API-Key' => $apiKey,
                'Content-Type' => 'application/json',
            ],
        ]);
    }

    /**
     * Sign request with HMAC-SHA256
     */
    private function signRequest(string $method, string $path, string $body): array
    {
        if (!$this->secretKey) {
            return [];
        }

        $timestamp = (string) round(microtime(true) * 1000);
        $message = "{$timestamp}.{$method}.{$path}.{$body}";
        $signature = hash_hmac('sha256', $message, $this->secretKey);

        return [
            'X-Timestamp' => $timestamp,
            'X-Signature' => $signature,
        ];
    }

    /**
     * Make API request
     */
    private function request(string $method, string $endpoint, ?array $data = null, bool $sync = false): array
    {
        if ($data !== null && $sync) {
            $data['async'] = false;
        }

        $body = $data ? json_encode($data) : '';
        $headers = $this->signRequest(strtoupper($method), $endpoint, $body);

        try {
            $response = $this->client->request($method, $endpoint, [
                'headers' => $headers,
                'json' => $data,
            ]);

            return json_decode($response->getBody()->getContents(), true);
        } catch (RequestException $e) {
            if ($e->hasResponse()) {
                $error = json_decode($e->getResponse()->getBody()->getContents(), true);
                throw new TxnCheckException(
                    $error['message'] ?? 'Unknown error',
                    $e->getResponse()->getStatusCode(),
                    $error['error'] ?? 'Error'
                );
            }
            throw $e;
        }
    }

    /**
     * Get UPI VPAs linked to a mobile number
     */
    public function upiByMobile(string $mobile, bool $sync = false): array
    {
        return $this->request('POST', '/upi-by-mobile', ['mobile' => $mobile], $sync);
    }

    /**
     * Get KYC data by mobile number
     */
    public function kycByMobile(string $mobile, bool $sync = false): array
    {
        return $this->request('POST', '/kyc-by-mobile', ['mobile' => $mobile], $sync);
    }

    /**
     * Check VPAs against blocklist
     */
    public function vpaChargebackCheck(array $vpas, bool $sync = false): array
    {
        return $this->request('POST', '/vpa-chargeback-check', ['vpas' => $vpas], $sync);
    }

    /**
     * Full verification: UPI + KYC + Chargeback check
     */
    public function fullCheck(string $mobile, bool $sync = false): array
    {
        return $this->request('POST', '/full-check', ['mobile' => $mobile], $sync);
    }

    /**
     * Bulk check VPAs against blocklist
     */
    public function bulkVpaCheck(array $vpas, bool $sync = false): array
    {
        return $this->request('POST', '/bulk/vpa-chargeback-check', ['vpas' => $vpas], $sync);
    }

    /**
     * Get request status and results
     */
    public function getRequestStatus(string $requestId): array
    {
        return $this->request('GET', "/requests/{$requestId}");
    }

    /**
     * Poll for request completion
     */
    public function waitForResult(string $requestId, int $pollInterval = 2, int $maxWait = 60): array
    {
        $terminalStatuses = ['COMPLETED', 'FAILED', 'PARTIAL'];
        $startTime = time();

        while (time() - $startTime < $maxWait) {
            $result = $this->getRequestStatus($requestId);
            
            if (in_array($result['status'], $terminalStatuses)) {
                return $result;
            }

            sleep($pollInterval);
        }

        throw new \RuntimeException("Request {$requestId} did not complete within {$maxWait}s");
    }
}

class TxnCheckException extends \Exception
{
    public string $errorType;

    public function __construct(string $message, int $code, string $errorType)
    {
        parent::__construct($message, $code);
        $this->errorType = $errorType;
    }
}

Basic Client Setup (cURL)

<?php

class TxnCheckCurlClient
{
    private string $apiKey;
    private string $baseUrl;
    private ?string $secretKey;

    public function __construct(
        string $apiKey,
        ?string $secretKey = null,
        string $baseUrl = 'https://api.txncheck.in/api/v1'
    ) {
        $this->apiKey = $apiKey;
        $this->secretKey = $secretKey;
        $this->baseUrl = rtrim($baseUrl, '/');
    }

    private function request(string $method, string $endpoint, ?array $data = null, bool $sync = false): array
    {
        if ($data !== null && $sync) {
            $data['async'] = false;
        }

        $url = $this->baseUrl . $endpoint;
        $body = $data ? json_encode($data) : '';

        $headers = [
            "X-API-Key: {$this->apiKey}",
            'Content-Type: application/json',
        ];

        // Sign request if secret key is configured
        if ($this->secretKey) {
            $timestamp = (string) round(microtime(true) * 1000);
            $message = "{$timestamp}." . strtoupper($method) . ".{$endpoint}.{$body}";
            $signature = hash_hmac('sha256', $message, $this->secretKey);
            $headers[] = "X-Timestamp: {$timestamp}";
            $headers[] = "X-Signature: {$signature}";
        }

        $ch = curl_init();
        curl_setopt_array($ch, [
            CURLOPT_URL => $url,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HTTPHEADER => $headers,
            CURLOPT_TIMEOUT => 30,
        ]);

        if ($method === 'POST') {
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
        }

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $error = curl_error($ch);
        curl_close($ch);

        if ($error) {
            throw new \RuntimeException("cURL error: {$error}");
        }

        $result = json_decode($response, true);

        if ($httpCode >= 400) {
            throw new TxnCheckException(
                $result['message'] ?? 'Unknown error',
                $httpCode,
                $result['error'] ?? 'Error'
            );
        }

        return $result;
    }

    public function upiByMobile(string $mobile, bool $sync = false): array
    {
        return $this->request('POST', '/upi-by-mobile', ['mobile' => $mobile], $sync);
    }

    public function kycByMobile(string $mobile, bool $sync = false): array
    {
        return $this->request('POST', '/kyc-by-mobile', ['mobile' => $mobile], $sync);
    }

    public function vpaChargebackCheck(array $vpas, bool $sync = false): array
    {
        return $this->request('POST', '/vpa-chargeback-check', ['vpas' => $vpas], $sync);
    }

    public function fullCheck(string $mobile, bool $sync = false): array
    {
        return $this->request('POST', '/full-check', ['mobile' => $mobile], $sync);
    }

    public function getRequestStatus(string $requestId): array
    {
        return $this->request('GET', "/requests/{$requestId}");
    }
}

Usage Examples

Async Mode (Default)

<?php

$client = new TxnCheckClient('fb_your_api_key_here');

// Submit request
$response = $client->upiByMobile('+919876543210');
echo "Request ID: {$response['requestId']}\n";

// Poll for result
$result = $client->waitForResult($response['requestId']);
echo "Status: {$result['status']}\n";
echo "UPI addresses: " . implode(', ', $result['result']['upi']) . "\n";

Sync Mode

<?php

// Get result immediately (waits up to 30 seconds)
$result = $client->upiByMobile('+919876543210', sync: true);

if ($result['status'] === 'COMPLETED') {
    echo "Name: {$result['result']['name']}\n";
    echo "UPI addresses: " . implode(', ', $result['result']['upi']) . "\n";
}

Full Check

<?php

$result = $client->fullCheck('+919876543210', sync: true);

if ($result['status'] === 'COMPLETED') {
    // UPI data
    $upiData = $result['result']['upiByMobile'];
    echo "Name: {$upiData['name']}\n";
    echo "VPAs: " . implode(', ', $upiData['upi']) . "\n";
    
    // KYC data
    $kycData = $result['result']['kycByMobile'];
    echo "PAN: {$kycData['pan']}\n";
    echo "DOB: {$kycData['dob']}\n";
    
    // Blocklist status
    $blocklist = $result['result']['vpaChargebackCheck'];
    echo "Blocklisted: {$blocklist['summary']['blocklisted']}\n";
}

VPA Blocklist Check

<?php

$vpas = ['user1@upi', 'user2@paytm', 'suspicious@ybl'];
$result = $client->vpaChargebackCheck($vpas, sync: true);

if ($result['status'] === 'COMPLETED') {
    // Blocklisted VPAs
    foreach ($result['result']['blocklisted'] as $vpa) {
        echo "⚠️ BLOCKED: {$vpa['vpa']}\n";
    }
    
    // Clean VPAs
    foreach ($result['result']['clean'] as $vpa) {
        echo "✓ Clean: {$vpa['vpa']}\n";
    }
}

Error Handling

<?php

class TxnCheckException extends \Exception
{
    public string $errorType;

    public function __construct(string $message, int $code, string $errorType)
    {
        parent::__construct($message, $code);
        $this->errorType = $errorType;
    }
}

// Usage with retry logic
function verifyWithRetry(TxnCheckClient $client, string $mobile, int $maxRetries = 3): array
{
    $lastError = null;

    for ($attempt = 1; $attempt <= $maxRetries; $attempt++) {
        try {
            return $client->upiByMobile($mobile, sync: true);
        } catch (TxnCheckException $e) {
            $lastError = $e;
            
            // Don't retry client errors (except rate limit)
            if ($e->getCode() >= 400 && $e->getCode() < 500 && $e->getCode() !== 429) {
                throw $e;
            }
            
            // Exponential backoff
            $delay = min(pow(2, $attempt - 1), 10);
            echo "Retry {$attempt}/{$maxRetries} in {$delay}s...\n";
            sleep($delay);
        }
    }

    throw $lastError;
}

// Usage
try {
    $result = verifyWithRetry($client, '+919876543210');
    echo "Verified: {$result['result']['name']}\n";
} catch (TxnCheckException $e) {
    echo "Error ({$e->getCode()}): {$e->getMessage()}\n";
}

Webhook Signature Verification

<?php

function verifyWebhookSignature(
    string $payload,
    string $signature,
    string $timestamp,
    string $secretKey,
    int $maxAgeSeconds = 300
): bool {
    // Check timestamp freshness
    $webhookTime = (int) $timestamp;
    $currentTime = (int) (microtime(true) * 1000);
    $ageMs = $currentTime - $webhookTime;

    if ($ageMs > $maxAgeSeconds * 1000) {
        return false; // Webhook too old
    }

    // Compute expected signature
    $message = "{$timestamp}.{$payload}";
    $expected = hash_hmac('sha256', $message, $secretKey);

    // Constant-time comparison
    return hash_equals($expected, $signature);
}

// Laravel example
Route::post('/webhook/fraud-buster', function (Request $request) {
    $signature = $request->header('X-Webhook-Signature');
    $timestamp = $request->header('X-Webhook-Timestamp');
    $secretKey = config('services.txncheck.webhook_secret');

    if (!verifyWebhookSignature(
        $request->getContent(),
        $signature,
        $timestamp,
        $secretKey
    )) {
        return response()->json(['error' => 'Invalid signature'], 401);
    }

    $data = $request->json();
    $event = $data['event'];

    switch ($event) {
        case 'request.completed':
            // Handle completed request
            $requestId = $data['data']['requestId'];
            Log::info("Request completed: {$requestId}");
            break;
            
        case 'request.failed':
            // Handle failed request
            Log::error("Request failed: {$data['data']['requestId']}");
            break;
    }

    return response()->json(['status' => 'ok']);
});

Laravel Service Provider

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Services\TxnCheckClient;

class TxnCheckServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        $this->app->singleton(TxnCheckClient::class, function ($app) {
            return new TxnCheckClient(
                apiKey: config('services.txncheck.api_key'),
                secretKey: config('services.txncheck.secret_key'),
                baseUrl: config('services.txncheck.base_url', 'https://api.txncheck.in/api/v1')
            );
        });
    }
}

// config/services.php
return [
    'txncheck' => [
        'api_key' => env('FRAUD_BUSTER_API_KEY'),
        'secret_key' => env('FRAUD_BUSTER_SECRET_KEY'),
        'webhook_secret' => env('FRAUD_BUSTER_WEBHOOK_SECRET'),
        'base_url' => env('FRAUD_BUSTER_BASE_URL', 'https://api.txncheck.in/api/v1'),
    ],
];

Complete Integration Example

<?php

/**
 * Complete example: E-commerce checkout verification
 */

class CheckoutVerificationService
{
    private TxnCheckClient $client;

    public function __construct(TxnCheckClient $client)
    {
        $this->client = $client;
    }

    public function verifyCustomerForCheckout(string $mobile, string $paymentVpa): array
    {
        $outcome = [
            'verified' => false,
            'riskScore' => 0,
            'details' => [
                'name' => null,
                'panVerified' => false,
                'vpaLinked' => false,
                'vpaCount' => 0,
                'blocklisted' => false,
            ],
            'error' => null,
        ];

        try {
            // Step 1: Full verification
            error_log("Starting verification for {$mobile}");
            $verification = $this->client->fullCheck($mobile, sync: true);

            if ($verification['status'] !== 'COMPLETED') {
                error_log("Verification incomplete: {$verification['status']}");
                $outcome['riskScore'] = 100;
                return $outcome;
            }

            // Step 2: Extract data
            $upiData = $verification['result']['upiByMobile'] ?? [];
            $kycData = $verification['result']['kycByMobile'] ?? [];
            $blocklist = $verification['result']['vpaChargebackCheck'] ?? [];

            // Step 3: Check if payment VPA belongs to user
            $userVpas = array_map('strtolower', $upiData['upi'] ?? []);
            $vpaLinked = in_array(strtolower($paymentVpa), $userVpas);

            if (!$vpaLinked) {
                error_log("Payment VPA {$paymentVpa} not linked to {$mobile}");
                $outcome['riskScore'] += 50;
            }

            // Step 4: Check blocklist
            $blocklistedVpas = array_map(
                fn($v) => strtolower($v['vpa']),
                $blocklist['blocklisted'] ?? []
            );

            if (in_array(strtolower($paymentVpa), $blocklistedVpas)) {
                error_log("Payment VPA {$paymentVpa} is BLOCKLISTED!");
                $outcome['riskScore'] = 100;
                $outcome['details']['blocklisted'] = true;
                return $outcome;
            }

            // Step 5: Build result
            $outcome['verified'] = $outcome['riskScore'] < 50;
            $outcome['details'] = [
                'name' => $kycData['fullName'] ?? $upiData['name'] ?? null,
                'panVerified' => !empty($kycData['pan']),
                'vpaLinked' => $vpaLinked,
                'vpaCount' => count($userVpas),
                'blocklisted' => false,
            ];

            error_log("Verification complete. Risk score: {$outcome['riskScore']}");
            return $outcome;

        } catch (\Exception $e) {
            error_log("Verification failed: {$e->getMessage()}");
            $outcome['riskScore'] = 100;
            $outcome['error'] = $e->getMessage();
            return $outcome;
        }
    }
}

// Usage
$client = new TxnCheckClient('fb_your_api_key_here');
$service = new CheckoutVerificationService($client);

$result = $service->verifyCustomerForCheckout(
    '+919876543210',
    'user@upi'
);

if ($result['verified']) {
    echo "✓ Customer verified, proceed with payment\n";
    echo "  Name: {$result['details']['name']}\n";
} else {
    echo "⚠️ Verification failed. Risk score: {$result['riskScore']}\n";
    if ($result['details']['blocklisted']) {
        echo "  Reason: VPA is blocklisted\n";
    }
}

Next Steps