GoLang

Without Router - Current Approach
package main

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"time"
)

func main() {

	urlStr := "https://fip-1.dev.sahamati.org.in/v2/Accounts/discover"

	body := []byte(fmt.Sprintf(`{
        "ver": "2.0.0",
        "timestamp": "%s",
        "txnid": "f35761ac-4a18-11e8-96ff-0277a9fbfedc2",
        "Customer": {
            "id": "customer_identifier@AA_identifier",
            "Identifiers": [
                {
                    "category": "STRONG",
                    "type": "AADHAAR",
                    "value": "XXXXXXXXXXXXXXXX"
                }
            ]
        },
        "FITypes": [
            "DEPOSIT"
        ]
    }`, time.Now().UTC().Format(time.RFC3339)))

	client := &http.Client{
		Timeout: 30 * time.Second,
	}

	req, err := http.NewRequest("POST", urlStr, bytes.NewBuffer(body))
	if err != nil {
		fmt.Printf("Error creating request: %v\n", err)
		return
	}

	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("x-jws-signature", "eyJhbGciOiJSUzI1NiIsImtpZCI6IlRlZ1FhMms3MUlFWlotaEhxcm1ueWFFc3ZvSWloNWdrVUx2SjFfTEhibGsiLCJjcml0IjpbImI2NCJdLCJiNjQiOmZhbHNlfQ..Dux_bx7X-q1YSvyNmZiyPM60ZgaK3MshW...")
	req.Header.Set("x-simulate-res", "Ok")
	req.Header.Set("Authorization", "Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJsRlByWU4wR3dBQ3YyUzJFVFZyRkVvZVVlc2VzelppQ2xIaVY1M3hrU3JNIn0.eyJleHAiOjE3NDQ5NjgwNDAsImlhdCI6MTc0NDg4MTY0MCwianRpIjoiYzZiZTcyNDAtOWU5Ny00MzYwLTk3OGUtZWI5MTY0MWZiMmMwIiwiaXNzIjoiaHR0cHM6Ly9hcGkuZGV2LnNhaGFtYXRpLm9yZy5pbi9hdXRoL3JlYWxtcy9zYWhhbWF0aSIsInN1YiI6ImIyMzE1MTU3LWRmNzYtNGQzZS04ZjM2LWQ4NzZmY2ViOWFlZiIsInR5cCI6IkJlYXJlciIsImF6cCI6IkFBLVNJTVVMQVRPUiIsImFjciI6IjEiLCJzY29wZSI6ImVtYWlsIG1pY3JvcHJvZmlsZS1qd3QgcHJvZmlsZSBhZGRyZXNzIHBob25lIiwidXBuIjoic2VydmljZS1hY2NvdW50LWFhLXNpbXVsYXRvciIsImNsaWVudElkIjoiQUEtU0lNVUxBVE9SIiwiYWRkcmVzcyI6e30sImNsaWVudEhvc3QiOiIxMC4yMjQuMC4xODIiLCJyb2xlcyI6IkFBIiwic2VjcmV0LWV4cGlyeS10cyI6IjIwMjUtMTItMDRUMTU6MzM6MzQuMDU5MTgzIiwiY2xpZW50QWRkcmVzcyI6IjEwLjIyNC4wLjE4MiJ9.G-ErvIeZUtKkqN4CbaH09Nwzy9fUjKSD18xpNh6y74AQdB8YFfNuhC8m6nxMpDCLY2fUj8sIcQ--Jp-EYDxChSR8kQTbKDmYJzPGRGunO-hkLxPK83R3Q7Byc6KZot1lZlj-Dsv-l5JD0Ay0KpPr4bKqIas5FEZTx2qoA3p6J1CyNbiQ81t4_KxVoO44hmVKPe0FIVNLw9MK04bkHOwO-WMB9DoUX5Y8bBREYgRv_W3QEEC8gcI5vZLnHuBXSZSXQ3MDPSLOq7lGnMsxh5A0YF1Wvfqg3LJjXizMfIfRNMyq2M0eMwHQEWfjLNTEqS6Bd6qkjPREVdhRTTnHaggq9A")

	resp, err := client.Do(req)
	if err != nil {
		fmt.Printf("Error executing request: %v\n", err)
		return
	}
	defer resp.Body.Close()

	respBody, err := io.ReadAll(resp.Body)
	if err != nil {
		fmt.Printf("Error reading response body: %v\n", err)
		return
	}

	fmt.Printf("Status: %s\n", resp.Status)
	fmt.Printf("Headers: %v\n", resp.Header)
	fmt.Printf("Response Body: %s\n", string(respBody))

	if resp.StatusCode >= 400 {
		fmt.Printf("Error response received - Status Code: %d\n", resp.StatusCode)
	}
}

With Router Integration - Request
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"time"
)

const (
	baseURL                    = "http://fip-1.sandbox.sahamati.org.in/fip-simulate"
	routerIntegrationHelperURL = "https://api.sandbox.sahamati.org.in/router-helper/v1/request/header"
	recipientID                = "FIP-SIMULATOR"
)

var httpClient = &http.Client{Timeout: 30 * time.Second}

func main() {
	// Step 1: Execute discovery request
	response, err := executeDiscoveryRequest()
	if err != nil {
		fmt.Printf("Account discovery failed. Error: %v\n", err)
		return
	}

	fmt.Println("Account discovery successful:", response)

	// Step 2: Do something with response
	performAnotherOperations(response)
}

/**
 * Executes account discovery:
 *  - Builds request
 *  - Adds router headers via Integration Helper
 *  - Sends request
 */
func executeDiscoveryRequest() (string, error) {
	route := "/Accounts/discover"

	// ReBIT defined request body
	rebitRequestBody := map[string]interface{}{
		"ver":      "2.0.0",
		"timestamp": time.Now().UTC().Format(time.RFC3339),
		"txnid":    "f35761ac-4a18-11e8-96ff-0277a9fbfedc2",
		"Customer": map[string]interface{}{
			"id": "9766334467@aa_simulator",
			"Identifiers": []map[string]interface{}{
				{
					"category": "STRONG",
					"type":     "AADHAAR",
					"value":    "XXXXXXXXXXXXXXXX",
				},
			},
		},
		"FITypes": []string{"DEPOSIT"},
	}

	// Step 1: Build initial request (with base URL)
	url := baseURL + route
	req, err := buildHttpRequest(url, map[string]string{}, rebitRequestBody)
	if err != nil {
		return "", fmt.Errorf("failed to build request: %w", err)
	}

	// Step 2: Add Sahamati router configuration (helper API call)
	req, err = addSahamatiConfiguration(req, route, rebitRequestBody)
	if err != nil {
		return "", fmt.Errorf("failed to add router configuration: %w", err)
	}

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

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

	return string(respBody), nil
}

/**
 * Builds a basic POST request with headers + JSON body
 */
func buildHttpRequest(url string, headers map[string]string, body map[string]interface{}) (*http.Request, error) {
	jsonData, err := json.Marshal(body)
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
	if err != nil {
		return nil, err
	}

	req.Header.Set("Content-Type", "application/json")
	for k, v := range headers {
		req.Header.Set(k, v)
	}
	return req, nil
}

/**
 * Adds Sahamati Router headers by calling the Router Integration Helper API
 */
func addSahamatiConfiguration(req *http.Request, route string, rebitRequestBody map[string]interface{}) (*http.Request, error) {
	// Call router integration helper
	headerResponse, err := routerIntegrationHelper(route, rebitRequestBody)
	if err != nil {
		return nil, err
	}

	targetHost, ok := headerResponse["targetHost"].(string)
	if !ok || targetHost == "" {
		return nil, fmt.Errorf("Router Helper response missing required field 'targetHost'")
	}

	// Build new URL
	newURL := targetHost + route

	// Clone body (req.Body cannot be reused after read, so re-marshal)
	var originalBody map[string]interface{}
	json.NewDecoder(req.Body).Decode(&originalBody)
	req.Body.Close()
	jsonBody, _ := json.Marshal(rebitRequestBody)

	newReq, err := http.NewRequest(req.Method, newURL, bytes.NewBuffer(jsonBody))
	if err != nil {
		return nil, err
	}

	// Copy headers from old request
	for k, v := range req.Header {
		for _, vv := range v {
			newReq.Header.Add(k, vv)
		}
	}

	// Add x-request-meta header only if it exists
	if xRequestMeta, ok := headerResponse["x-request-meta"].(string); ok && xRequestMeta != "" {
		newReq.Header.Set("x-request-meta", xRequestMeta)
	}

	return newReq, nil
}

/**
 * Calls Router Integration Helper Service
 */
func routerIntegrationHelper(route string, rebitRequestBody map[string]interface{}) (map[string]interface{}, error) {
	payload := map[string]interface{}{
		"rebitAPIEndpoint": route,
		"recipientId":      recipientID,
		"customerId":       "9766334467@aa_simulator",
		"requestBody":      rebitRequestBody,
	}

	payloadBytes, _ := json.Marshal(payload)
	req, err := http.NewRequest("POST", routerIntegrationHelperURL, bytes.NewBuffer(payloadBytes))
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", "application/json")

	resp, err := httpClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	respBody, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	var responseMap map[string]interface{}
	if err := json.Unmarshal(respBody, &responseMap); err != nil {
		return nil, err
	}
	return responseMap, nil
}

/**
 * Example: Further operations with the discovery response
 */
func performAnotherOperations(accountDiscoverResponse string) {
	fmt.Println("Next steps with response:", accountDiscoverResponse)
}

With Router Integration - Response
package main

import (
	"bytes"
	"encoding/json"
	"io/ioutil"
	"log"
	"net/http"
	"time"
)

const (
	ROUTER_RESPONSE_HEADER_URL = "https://api.sandbox.sahamati.org.in/router-helper/v1/response/header"
	RECIPIENT_ID               = "AA-SIMULATOR"
)

type Account struct {
	FIType          string `json:"FIType"`
	AccType         string `json:"accType"`
	AccRefNumber    string `json:"accRefNumber"`
	MaskedAccNumber string `json:"maskedAccNumber"`
}

type CustomerIdentifier struct {
	Category string `json:"category"`
	Type     string `json:"type"`
	Value    string `json:"value"`
}

type Customer struct {
	ID          string               `json:"id"`
	Identifiers []CustomerIdentifier `json:"Identifiers"`
}

type DiscoverRequest struct {
	Customer Customer `json:"Customer"`
}

func fetchAccountFromRequestBody(requestBody map[string]interface{}) *Account {
	customerObj, ok := requestBody["Customer"].(map[string]interface{})
	if !ok {
		return nil
	}
	identifiersObj, ok := customerObj["Identifiers"].([]interface{})
	if !ok {
		return nil
	}
	for _, idObj := range identifiersObj {
		idMap, ok := idObj.(map[string]interface{})
		if !ok {
			continue
		}
		category, _ := idMap["category"].(string)
		typeVal, _ := idMap["type"].(string)
		value, _ := idMap["value"].(string)
		if category == "STRONG" && typeVal == "AADHAAR" && value != "" {
			return &Account{
				FIType:          "DEPOSIT",
				AccType:         "SAVINGS",
				AccRefNumber:    "BANK11111111",
				MaskedAccNumber: "XXXXXXX3468",
			}
		}
	}
	return nil
}

func routerIntegrationHelper(requestBody map[string]interface{}) (map[string]interface{}, error) {
	jsonData, _ := json.Marshal(requestBody)
	resp, err := http.Post(ROUTER_RESPONSE_HEADER_URL, "application/json", bytes.NewBuffer(jsonData))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	body, _ := ioutil.ReadAll(resp.Body)
	var result map[string]interface{}
	json.Unmarshal(body, &result)
	return result, nil
}

func handleAccountDiscovery(w http.ResponseWriter, r *http.Request) {
	var incomingRequestBody map[string]interface{}
	if err := json.NewDecoder(r.Body).Decode(&incomingRequestBody); err != nil {
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(`{"error":"Invalid request"}`))
		return
	}
	discoveredAccount := fetchAccountFromRequestBody(incomingRequestBody)
	if discoveredAccount == nil {
		w.WriteHeader(http.StatusNotFound)
		w.Write([]byte(`{"error":"Account not found"}`))
		return
	}
	responseBody := map[string]interface{}{
		"ver":                "2.0.0",
		"timestamp":          time.Now().Format(time.RFC3339),
		"txnid":              "f35761ac-4a18-11e8-96ff-0277a9fbfedcs",
		"DiscoveredAccounts": []Account{*discoveredAccount},
	}
	responseHeaderRequestBody := map[string]interface{}{
		"rebitAPIEndpoint":     "/accounts/discover",
		"customerId":           "9977336577@aa_simulator",
		"recipientId":          RECIPIENT_ID,
		"additionalAttributes": map[string]interface{}{},
		"responseBody":         responseBody,
	}
	responseHeaderResp, err := routerIntegrationHelper(responseHeaderRequestBody)
	if err == nil {
		if xResponseMeta, ok := responseHeaderResp["x-response-meta"].(string); ok && xResponseMeta != "" {
			w.Header().Set("x-response-meta", xResponseMeta)
		}
	}
	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(responseBody)
}

func main() {
	http.HandleFunc("/accounts/discover", handleAccountDiscovery)
	log.Println("Server running on :8080")
	log.Fatal(http.ListenAndServe(":8080", nil))
}

Last updated

Was this helpful?