Go Integration
This guide shows how to integrate TxnCheck API into your Go application.Installation
No external dependencies required. Uses standard librarynet/http.
For structured JSON handling:
Copy
go get github.com/json-iterator/go # Optional, for faster JSON
Basic Client Setup
Copy
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)
Copy
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
Copy
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
Copy
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
Copy
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
Copy
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
Copy
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
Copy
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")
}
}
}
