Skip to main content

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.

Go Integration

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

Installation

No external dependencies required. Uses standard library net/http. For structured JSON handling:
go get github.com/json-iterator/go  # Optional, for faster JSON

Basic Client Setup

package txncheck

import (
	"bytes"
	"context"
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"strconv"
	"time"
)

// Config holds client configuration
type Config struct {
	APIKey    string
	SecretKey string // Optional, for request signing
	BaseURL   string
	Timeout   time.Duration
}

// DefaultConfig returns config with default values
func DefaultConfig(apiKey string) Config {
	return Config{
		APIKey:  apiKey,
		BaseURL: "https://api.txncheck.in/api/v1",
		Timeout: 30 * time.Second,
	}
}

// Client is the TxnCheck API client
type Client struct {
	config     Config
	httpClient *http.Client
}

// NewClient creates a new TxnCheck client
func NewClient(config Config) *Client {
	if config.BaseURL == "" {
		config.BaseURL = "https://api.txncheck.in/api/v1"
	}
	if config.Timeout == 0 {
		config.Timeout = 30 * time.Second
	}

	return &Client{
		config: config,
		httpClient: &http.Client{
			Timeout: config.Timeout,
		},
	}
}

// RequestStatus represents the status of a verification request
type RequestStatus string

const (
	StatusQueued     RequestStatus = "QUEUED"
	StatusProcessing RequestStatus = "PROCESSING"
	StatusCompleted  RequestStatus = "COMPLETED"
	StatusFailed     RequestStatus = "FAILED"
	StatusPartial    RequestStatus = "PARTIAL"
)

// AcceptedResponse is returned for async requests
type AcceptedResponse struct {
	StatusCode int           `json:"statusCode"`
	Message    string        `json:"message"`
	RequestID  string        `json:"requestId"`
	Status     RequestStatus `json:"status"`
}

// VerificationResult is returned for sync requests or status polling
type VerificationResult struct {
	StatusCode   int                    `json:"statusCode,omitempty"`
	RequestID    string                 `json:"requestId"`
	Method       string                 `json:"method,omitempty"`
	Status       RequestStatus          `json:"status"`
	Result       map[string]interface{} `json:"result,omitempty"`
	Error        map[string]interface{} `json:"error,omitempty"`
	StepStatuses map[string]string      `json:"stepStatuses,omitempty"`
	CreatedAt    string                 `json:"createdAt,omitempty"`
	CompletedAt  string                 `json:"completedAt,omitempty"`
}

// APIError represents an API error response
type APIError struct {
	StatusCode int    `json:"statusCode"`
	Message    string `json:"message"`
	Error      string `json:"error"`
}

func (e *APIError) Error() string {
	return fmt.Sprintf("%d %s: %s", e.StatusCode, e.Error, e.Message)
}

// signRequest generates HMAC-SHA256 signature
func (c *Client) signRequest(method, path string, body []byte) (timestamp, signature string) {
	if c.config.SecretKey == "" {
		return "", ""
	}

	timestamp = strconv.FormatInt(time.Now().UnixMilli(), 10)
	message := fmt.Sprintf("%s.%s.%s.%s", timestamp, method, path, string(body))

	h := hmac.New(sha256.New, []byte(c.config.SecretKey))
	h.Write([]byte(message))
	signature = hex.EncodeToString(h.Sum(nil))

	return timestamp, signature
}

// request makes an HTTP request to the API
func (c *Client) request(ctx context.Context, method, endpoint string, body interface{}) ([]byte, error) {
	var bodyBytes []byte
	var err error

	if body != nil {
		bodyBytes, err = json.Marshal(body)
		if err != nil {
			return nil, fmt.Errorf("failed to marshal body: %w", err)
		}
	}

	url := c.config.BaseURL + endpoint
	req, err := http.NewRequestWithContext(ctx, method, url, bytes.NewReader(bodyBytes))
	if err != nil {
		return nil, fmt.Errorf("failed to create request: %w", err)
	}

	req.Header.Set("X-API-Key", c.config.APIKey)
	req.Header.Set("Content-Type", "application/json")

	// Sign request if secret key is configured
	if timestamp, signature := c.signRequest(method, endpoint, bodyBytes); signature != "" {
		req.Header.Set("X-Timestamp", timestamp)
		req.Header.Set("X-Signature", signature)
	}

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return nil, fmt.Errorf("request failed: %w", err)
	}
	defer resp.Body.Close()

	respBody, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("failed to read response: %w", err)
	}

	if resp.StatusCode >= 400 {
		var apiErr APIError
		if json.Unmarshal(respBody, &apiErr) == nil {
			apiErr.StatusCode = resp.StatusCode
			return nil, &apiErr
		}
		return nil, &APIError{
			StatusCode: resp.StatusCode,
			Message:    string(respBody),
			Error:      "Unknown Error",
		}
	}

	return respBody, nil
}

// UPIByMobileRequest is the request body for UPI by mobile
type UPIByMobileRequest struct {
	Mobile string `json:"mobile"`
	Async  *bool  `json:"async,omitempty"`
}

// UPIByMobile gets UPI VPAs linked to a mobile number
func (c *Client) UPIByMobile(ctx context.Context, mobile string, sync bool) (*VerificationResult, error) {
	asyncVal := !sync
	body := UPIByMobileRequest{
		Mobile: mobile,
		Async:  &asyncVal,
	}

	respBody, err := c.request(ctx, "POST", "/upi-by-mobile", body)
	if err != nil {
		return nil, err
	}

	var result VerificationResult
	if err := json.Unmarshal(respBody, &result); err != nil {
		return nil, fmt.Errorf("failed to unmarshal response: %w", err)
	}

	return &result, nil
}

// KYCByMobile gets KYC data by mobile number
func (c *Client) KYCByMobile(ctx context.Context, mobile string, sync bool) (*VerificationResult, error) {
	asyncVal := !sync
	body := map[string]interface{}{
		"mobile": mobile,
		"async":  asyncVal,
	}

	respBody, err := c.request(ctx, "POST", "/kyc-by-mobile", body)
	if err != nil {
		return nil, err
	}

	var result VerificationResult
	if err := json.Unmarshal(respBody, &result); err != nil {
		return nil, fmt.Errorf("failed to unmarshal response: %w", err)
	}

	return &result, nil
}

// VPAChargebackCheck checks VPAs against blocklist
func (c *Client) VPAChargebackCheck(ctx context.Context, vpas []string, sync bool) (*VerificationResult, error) {
	asyncVal := !sync
	body := map[string]interface{}{
		"vpas":  vpas,
		"async": asyncVal,
	}

	respBody, err := c.request(ctx, "POST", "/vpa-chargeback-check", body)
	if err != nil {
		return nil, err
	}

	var result VerificationResult
	if err := json.Unmarshal(respBody, &result); err != nil {
		return nil, fmt.Errorf("failed to unmarshal response: %w", err)
	}

	return &result, nil
}

// FullCheck performs full verification: UPI + KYC + Chargeback check
func (c *Client) FullCheck(ctx context.Context, mobile string, sync bool) (*VerificationResult, error) {
	asyncVal := !sync
	body := map[string]interface{}{
		"mobile": mobile,
		"async":  asyncVal,
	}

	respBody, err := c.request(ctx, "POST", "/full-check", body)
	if err != nil {
		return nil, err
	}

	var result VerificationResult
	if err := json.Unmarshal(respBody, &result); err != nil {
		return nil, fmt.Errorf("failed to unmarshal response: %w", err)
	}

	return &result, nil
}

// BulkVPACheck checks multiple VPAs against blocklist
func (c *Client) BulkVPACheck(ctx context.Context, vpas []string, sync bool) (*VerificationResult, error) {
	asyncVal := !sync
	body := map[string]interface{}{
		"vpas":  vpas,
		"async": asyncVal,
	}

	respBody, err := c.request(ctx, "POST", "/bulk/vpa-chargeback-check", body)
	if err != nil {
		return nil, err
	}

	var result VerificationResult
	if err := json.Unmarshal(respBody, &result); err != nil {
		return nil, fmt.Errorf("failed to unmarshal response: %w", err)
	}

	return &result, nil
}

// GetRequestStatus gets the status of a request
func (c *Client) GetRequestStatus(ctx context.Context, requestID string) (*VerificationResult, error) {
	respBody, err := c.request(ctx, "GET", "/requests/"+requestID, nil)
	if err != nil {
		return nil, err
	}

	var result VerificationResult
	if err := json.Unmarshal(respBody, &result); err != nil {
		return nil, fmt.Errorf("failed to unmarshal response: %w", err)
	}

	return &result, nil
}

// WaitForResult polls for request completion
func (c *Client) WaitForResult(ctx context.Context, requestID string, pollInterval, maxWait time.Duration) (*VerificationResult, error) {
	if pollInterval == 0 {
		pollInterval = 2 * time.Second
	}
	if maxWait == 0 {
		maxWait = 60 * time.Second
	}

	terminalStatuses := map[RequestStatus]bool{
		StatusCompleted: true,
		StatusFailed:    true,
		StatusPartial:   true,
	}

	deadline := time.Now().Add(maxWait)
	ticker := time.NewTicker(pollInterval)
	defer ticker.Stop()

	for {
		result, err := c.GetRequestStatus(ctx, requestID)
		if err != nil {
			return nil, err
		}

		if terminalStatuses[result.Status] {
			return result, nil
		}

		select {
		case <-ctx.Done():
			return nil, ctx.Err()
		case <-ticker.C:
			if time.Now().After(deadline) {
				return nil, fmt.Errorf("request %s did not complete within %v", requestID, maxWait)
			}
		}
	}
}

Usage Examples

Async Mode (Default)

package main

import (
	"context"
	"fmt"
	"log"

	"yourproject/txncheck"
)

func main() {
	client := txncheck.NewClient(txncheck.DefaultConfig("fb_your_api_key_here"))
	ctx := context.Background()

	// Submit request (async mode)
	result, err := client.UPIByMobile(ctx, "+919876543210", false)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("Request ID: %s\n", result.RequestID)

	// Poll for result
	finalResult, err := client.WaitForResult(ctx, result.RequestID, 0, 0)
	if err != nil {
		log.Fatal(err)
	}
	
	fmt.Printf("Status: %s\n", finalResult.Status)
	if upi, ok := finalResult.Result["upi"].([]interface{}); ok {
		fmt.Printf("UPI addresses: %v\n", upi)
	}
}

Sync Mode

package main

import (
	"context"
	"fmt"
	"log"

	"yourproject/txncheck"
)

func main() {
	client := txncheck.NewClient(txncheck.DefaultConfig("fb_your_api_key_here"))
	ctx := context.Background()

	// Get result immediately (sync mode)
	result, err := client.UPIByMobile(ctx, "+919876543210", true)
	if err != nil {
		log.Fatal(err)
	}

	if result.Status == txncheck.StatusCompleted {
		fmt.Printf("Name: %v\n", result.Result["name"])
		if upi, ok := result.Result["upi"].([]interface{}); ok {
			fmt.Printf("UPI addresses: %v\n", upi)
		}
	}
}

Full Check

result, err := client.FullCheck(ctx, "+919876543210", true)
if err != nil {
	log.Fatal(err)
}

if result.Status == txncheck.StatusCompleted {
	// UPI data
	if upiData, ok := result.Result["upiByMobile"].(map[string]interface{}); ok {
		fmt.Printf("Name: %v\n", upiData["name"])
		fmt.Printf("VPAs: %v\n", upiData["upi"])
	}

	// KYC data
	if kycData, ok := result.Result["kycByMobile"].(map[string]interface{}); ok {
		fmt.Printf("PAN: %v\n", kycData["pan"])
		fmt.Printf("DOB: %v\n", kycData["dob"])
	}

	// Blocklist
	if blocklist, ok := result.Result["vpaChargebackCheck"].(map[string]interface{}); ok {
		if summary, ok := blocklist["summary"].(map[string]interface{}); ok {
			fmt.Printf("Blocklisted: %v\n", summary["blocklisted"])
		}
	}
}

VPA Blocklist Check

vpas := []string{"user1@upi", "user2@paytm", "suspicious@ybl"}
result, err := client.VPAChargebackCheck(ctx, vpas, true)
if err != nil {
	log.Fatal(err)
}

if result.Status == txncheck.StatusCompleted {
	// Blocklisted VPAs
	if blocklisted, ok := result.Result["blocklisted"].([]interface{}); ok {
		for _, v := range blocklisted {
			if vpa, ok := v.(map[string]interface{}); ok {
				fmt.Printf("⚠️ BLOCKED: %v\n", vpa["vpa"])
			}
		}
	}

	// Clean VPAs
	if clean, ok := result.Result["clean"].([]interface{}); ok {
		for _, v := range clean {
			if vpa, ok := v.(map[string]interface{}); ok {
				fmt.Printf("✓ Clean: %v\n", vpa["vpa"])
			}
		}
	}
}

Error Handling

package main

import (
	"context"
	"errors"
	"fmt"
	"log"
	"time"

	"yourproject/txncheck"
)

func verifyWithRetry(client *txncheck.Client, mobile string, maxRetries int) (*txncheck.VerificationResult, error) {
	ctx := context.Background()
	var lastErr error

	for attempt := 1; attempt <= maxRetries; attempt++ {
		result, err := client.UPIByMobile(ctx, mobile, true)
		if err == nil {
			return result, nil
		}

		lastErr = err

		// Check if error is retryable
		var apiErr *txncheck.APIError
		if errors.As(err, &apiErr) {
			// Don't retry client errors (except rate limit)
			if apiErr.StatusCode >= 400 && apiErr.StatusCode < 500 && apiErr.StatusCode != 429 {
				return nil, err
			}
		}

		// Exponential backoff
		delay := time.Duration(1<<(attempt-1)) * time.Second
		if delay > 10*time.Second {
			delay = 10 * time.Second
		}
		
		log.Printf("Retry %d/%d in %v...", attempt, maxRetries, delay)
		time.Sleep(delay)
	}

	return nil, lastErr
}

func main() {
	client := txncheck.NewClient(txncheck.DefaultConfig("fb_your_api_key_here"))

	result, err := verifyWithRetry(client, "+919876543210", 3)
	if err != nil {
		var apiErr *txncheck.APIError
		if errors.As(err, &apiErr) {
			fmt.Printf("API Error (%d): %s\n", apiErr.StatusCode, apiErr.Message)
		} else {
			fmt.Printf("Error: %v\n", err)
		}
		return
	}

	fmt.Printf("Verified: %v\n", result.Result["name"])
}

Webhook Signature Verification

package webhooks

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"io"
	"net/http"
	"strconv"
	"time"
)

// VerifyWebhookSignature verifies the webhook signature
func VerifyWebhookSignature(payload []byte, signature, timestamp, secretKey string, maxAgeSeconds int64) bool {
	// Check timestamp freshness
	webhookTime, err := strconv.ParseInt(timestamp, 10, 64)
	if err != nil {
		return false
	}

	currentTime := time.Now().UnixMilli()
	ageMs := currentTime - webhookTime

	if ageMs > maxAgeSeconds*1000 {
		return false // Webhook too old
	}

	// Compute expected signature
	message := timestamp + "." + string(payload)
	h := hmac.New(sha256.New, []byte(secretKey))
	h.Write([]byte(message))
	expected := hex.EncodeToString(h.Sum(nil))

	// Constant-time comparison
	return hmac.Equal([]byte(signature), []byte(expected))
}

// WebhookPayload represents the webhook payload
type WebhookPayload struct {
	Event     string                 `json:"event"`
	Timestamp string                 `json:"timestamp"`
	Data      map[string]interface{} `json:"data"`
}

// WebhookHandler handles incoming webhooks
func WebhookHandler(secretKey string) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		signature := r.Header.Get("X-Webhook-Signature")
		timestamp := r.Header.Get("X-Webhook-Timestamp")

		if signature == "" || timestamp == "" {
			http.Error(w, "Missing signature headers", http.StatusUnauthorized)
			return
		}

		body, err := io.ReadAll(r.Body)
		if err != nil {
			http.Error(w, "Failed to read body", http.StatusBadRequest)
			return
		}

		if !VerifyWebhookSignature(body, signature, timestamp, secretKey, 300) {
			http.Error(w, "Invalid signature", http.StatusUnauthorized)
			return
		}

		var payload WebhookPayload
		if err := json.Unmarshal(body, &payload); err != nil {
			http.Error(w, "Invalid JSON", http.StatusBadRequest)
			return
		}

		switch payload.Event {
		case "request.completed":
			// Handle completed request
			requestID := payload.Data["requestId"].(string)
			log.Printf("Request completed: %s", requestID)

		case "request.failed":
			// Handle failed request
			requestID := payload.Data["requestId"].(string)
			log.Printf("Request failed: %s", requestID)
		}

		w.Header().Set("Content-Type", "application/json")
		json.NewEncoder(w).Encode(map[string]string{"status": "ok"})
	}
}

// Usage
func main() {
	webhookSecret := "your_webhook_secret"
	
	http.HandleFunc("/webhook/fraud-buster", WebhookHandler(webhookSecret))
	log.Fatal(http.ListenAndServe(":8080", nil))
}

Complete Integration Example

package main

import (
	"context"
	"fmt"
	"log"
	"strings"

	"yourproject/txncheck"
)

// VerificationOutcome represents the verification result
type VerificationOutcome struct {
	Verified  bool
	RiskScore int
	Details   struct {
		Name        string
		PANVerified bool
		VPALinked   bool
		VPACount    int
		Blocklisted bool
	}
	Error string
}

// VerifyCustomerForCheckout verifies a customer before payment
func VerifyCustomerForCheckout(client *txncheck.Client, mobile, paymentVPA string) VerificationOutcome {
	ctx := context.Background()
	outcome := VerificationOutcome{}

	// Step 1: Full verification
	log.Printf("Starting verification for %s", mobile)
	verification, err := client.FullCheck(ctx, mobile, true)
	if err != nil {
		log.Printf("Verification failed: %v", err)
		outcome.RiskScore = 100
		outcome.Error = err.Error()
		return outcome
	}

	if verification.Status != txncheck.StatusCompleted {
		log.Printf("Verification incomplete: %s", verification.Status)
		outcome.RiskScore = 100
		return outcome
	}

	// Step 2: Extract data
	upiData, _ := verification.Result["upiByMobile"].(map[string]interface{})
	kycData, _ := verification.Result["kycByMobile"].(map[string]interface{})
	blocklist, _ := verification.Result["vpaChargebackCheck"].(map[string]interface{})

	// Step 3: Check if payment VPA belongs to user
	var userVPAs []string
	if upi, ok := upiData["upi"].([]interface{}); ok {
		for _, v := range upi {
			if vpa, ok := v.(string); ok {
				userVPAs = append(userVPAs, strings.ToLower(vpa))
			}
		}
	}

	vpaLinked := false
	paymentVPALower := strings.ToLower(paymentVPA)
	for _, vpa := range userVPAs {
		if vpa == paymentVPALower {
			vpaLinked = true
			break
		}
	}

	if !vpaLinked {
		log.Printf("Payment VPA %s not linked to %s", paymentVPA, mobile)
		outcome.RiskScore += 50
	}

	// Step 4: Check blocklist
	if blocklistedList, ok := blocklist["blocklisted"].([]interface{}); ok {
		for _, v := range blocklistedList {
			if item, ok := v.(map[string]interface{}); ok {
				if vpa, ok := item["vpa"].(string); ok {
					if strings.ToLower(vpa) == paymentVPALower {
						log.Printf("Payment VPA %s is BLOCKLISTED!", paymentVPA)
						outcome.RiskScore = 100
						outcome.Details.Blocklisted = true
						return outcome
					}
				}
			}
		}
	}

	// Step 5: Build result
	outcome.Verified = outcome.RiskScore < 50

	if name, ok := kycData["fullName"].(string); ok {
		outcome.Details.Name = name
	} else if name, ok := upiData["name"].(string); ok {
		outcome.Details.Name = name
	}

	if pan, ok := kycData["pan"].(string); ok {
		outcome.Details.PANVerified = pan != ""
	}

	outcome.Details.VPALinked = vpaLinked
	outcome.Details.VPACount = len(userVPAs)

	log.Printf("Verification complete. Risk score: %d", outcome.RiskScore)
	return outcome
}

func main() {
	client := txncheck.NewClient(txncheck.DefaultConfig("fb_your_api_key_here"))

	result := VerifyCustomerForCheckout(
		client,
		"+919876543210",
		"user@upi",
	)

	if result.Verified {
		fmt.Println("✓ Customer verified, proceed with payment")
		fmt.Printf("  Name: %s\n", result.Details.Name)
	} else {
		fmt.Printf("⚠️ Verification failed. Risk score: %d\n", result.RiskScore)
		if result.Details.Blocklisted {
			fmt.Println("  Reason: VPA is blocklisted")
		}
	}
}

Next Steps

Webhooks Guide

Set up webhooks for async notifications

Error Handling

Handle API errors properly

Security Best Practices

Secure your integration

Use Cases

See real-world examples