Concepts
This guide will walk you through the steps needed to transfer funds using Flutterwave’s API with Go. You’ll learn how to:
- Set up your server app: Create a Go project and install the required dependencies.
- Get bank code:
Retrieve a list of bank codes from Flutterwave’s API for initiating transactions.
- Get Account Details:
Use a bank code to fetch and confirm bank account details.
- Initiate a transfer:
Send money to the resolved bank account via Flutterwave.
- Fetch the transfer:
Retrieve the status of a transfer to ensure it was processed.
- Verify a transaction is valid:
Confirm the authenticity of a transaction using Flutterwave’s verification endpoint.
Step 1: Setting up your Server App
To initiate a transfer successfully, you need to whitelist your current IP address. Here is a quick guide on how to do that.
You can also use the universal address 0.0.0.0 to allow transfers from all
IP addresses. However, this method offers less control compared to
specifying a list of acceptable IP addresses.
Create a new Go project and initialize a Go module to manage dependencies.
mkdir flutterwave-go && cd flutterwave-go
go mod init flutterwave-go
Installing your Dependencies
Next, install the required dependency.
go get github.com/joho/godotenv
Configuring your Environment
Log into your Flutterwave dashboard to get your API keys and add the keys to your .env file.
FLW_SECRET_KEY=<your_secret_key_here>
FLW_PUBLIC_KEY=<your_public_key_here>
Create the Project Entry Point
Create a main.go file in the root directory and initialize the project entry point.
package main
import (
"fmt"
"log"
"os"
"github.com/joho/godotenv"
)
// Load environment variables
var flwSecretKey string
func init() {
err := godotenv.Load(".env")
if err != nil {
log.Fatal("Error loading .env file")
}
secretKey := os.Getenv("FLW_SECRET_KEY")
fmt.Println("Secret Key Loaded:", secretKey)
}
Step 2: Fetch Bank Code
Before you make a transfer to any bank, you need the bank code. It is a unique identifier that every bank has. You will use this to identify the bank you want to transfer to.
type Bank struct {
Code string `json:"code"`
Name string `json:"name"`
}
// Fetch bank code from Flutterwave API
func getBankCode(country, accountBank string) (Bank, error) {
url := fmt.Sprintf("https://api.flutterwave.com/v3/banks/%s", country)
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer "+flwSecretKey)
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return Bank{}, err
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
if resp.StatusCode != 200 {
return Bank{}, fmt.Errorf("failed to fetch bank list: %s", string(body))
}
var result struct {
Data []Bank `json:"data"`
}
if err := json.Unmarshal(body, &result); err != nil {
return Bank{}, fmt.Errorf("failed to unmarshal response: %w", err)
}
// Loop through the result to find the matching bank code
for _, bank := range result.Data {
if bank.Code == accountBank {
fmt.Printf("Selected Bank: %s\n", bank.Name)
return bank, nil
}
}
return Bank{}, fmt.Errorf("bank not found for code: %s", accountBank)
}
Step 3: Resolve Bank Account
Next, validate the account before making the transfer.
type AccountDetails struct {
AccountNumber string `json:"account_number"`
AccountName string `json:"account_name"`
}
// Resolve bank account details
func resolveAccount(accountNumber, accountBank string) (AccountDetails, error) {
url := "https://api.flutterwave.com/v3/accounts/resolve"
payload := map[string]string{
"account_number": accountNumber,
"account_bank": accountBank,
}
jsonPayload, _ := json.Marshal(payload)
req, _ := http.NewRequest("POST", url, bytes.NewBuffer(jsonPayload))
req.Header.Add("Authorization", "Bearer "+flwSecretKey)
req.Header.Add("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return AccountDetails{}, err
}
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
if resp.StatusCode != 200 {
return AccountDetails{}, fmt.Errorf("failed to resolve account: %s", string(body))
}
var result struct {
Data AccountDetails `json:"data"`
}
json.Unmarshal(body, &result)
fmt.Printf("Account Verified: %+v\n", result.Data)
return result.Data, nil
}
Step 4: Initiate Transfer
Initiate the transfer once the bank account details have been verified.
type TransferResponse struct {
ID string `json:"id"`
}
// Initiate a bank transfer
func initiateTransfer(dummyData map[string]interface{}, selectedBank Bank) (TransferResponse, error) {
url := "https://api.flutterwave.com/v3/transfers"
payload := map[string]interface{}{
"account_bank": selectedBank.Code,
"account_number": dummyData["account_number"],
"amount": dummyData["amount"],
"narration": dummyData["narration"],
"currency": dummyData["currency"],
"reference": dummyData["reference"],
"callback_url": "https://example.com/callback",
}
jsonPayload, _ := json.Marshal(payload)
req, _ := http.NewRequest("POST", url, bytes.NewBuffer(jsonPayload))
req.Header.Add("Authorization", "Bearer "+flwSecretKey)
req.Header.Add("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return TransferResponse{}, err
}
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
if resp.StatusCode != 200 {
return TransferResponse{}, fmt.Errorf("failed to initiate transfer: %s", string(body))
}
var result struct {
Data TransferResponse `json:"data"`
}
json.Unmarshal(body, &result)
fmt.Printf("Transfer Initiated: %+v\n", result.Data)
return result.Data, nil
}
Step 5: Fetch Transfer Status
Check the status of the transfer using the transfer ID returned from the previous step.
type TransferStatus struct {
Status string `json:"status"`
}
// Fetch the transfer status
func fetchTransferStatus(transferID string) (TransferStatus, error) {
url := fmt.Sprintf("https://api.flutterwave.com/v3/transfers/%s", transferID)
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer "+flwSecretKey)
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return TransferStatus{}, err
}
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
if resp.StatusCode != 200 {
return TransferStatus{}, fmt.Errorf("failed to fetch transfer status: %s", string(body))
}
var result struct {
Data TransferStatus `json:"data"`
}
json.Unmarshal(body, &result)
fmt.Printf("Transfer Status: %+v\n", result.Data)
return result.Data, nil
}
Step 6: Transaction Verification
Verify the transaction to confirm the transfer is successful.
type TransactionVerification struct {
Status string `json:"status"`
Amount float64 `json:"amount"`
}
// Verify a transaction
func verifyTransfer(transferID int) (TransactionVerification, error) {
url := fmt.Sprintf("https://api.flutterwave.com/v3/transfers/%s", strconv.Itoa(transferID))
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer "+flwSecretKey)
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return TransactionVerification{}, err
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
if resp.StatusCode != 200 {
return TransactionVerification{}, fmt.Errorf("failed to verify transfer: %s", string(body))
}
var result struct {
Data TransactionVerification `json:"data"`
}
json.Unmarshal(body, &result)
fmt.Printf("Trasfer Verified: %+v\n", result.Data)
return result.Data, nil
}
Calling the Requests
Make the requests to simulate the bank transfer using all the functions created above.
package main
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"os"
"github.com/joho/godotenv"
)
func main() {
// Example: Fetch bank code
country := "NG" // Nigeria as an example
accountBank := "044" // Example bank code
accountNumber := "1234567890" // Example account number
// Step 1: Get the bank code
selectedBank, err := getBankCode(country, accountBank)
if err != nil {
fmt.Printf("Error fetching bank code: %v\n", err)
return
}
// Step 2: Resolve account details
accountDetails, err := resolveAccount(accountNumber, selectedBank.Code)
if err != nil {
fmt.Printf("Error resolving account: %v\n", err)
return
}
// Step 3: Initiate transfer
dummyData := map[string]interface{}{
"account_number": accountDetails.AccountNumber,
"amount": 1000,
"narration": "Test transfer",
"currency": "NGN",
"reference": "tx-1234567890", //you can use a helper function or library to generate unique references
}
transferResponse, err := initiateTransfer(dummyData, selectedBank)
if err != nil {
fmt.Printf("Error initiating transfer: %v\n", err)
return
}
// Step 4: Fetch transfer status
transferStatus, err := fetchTransferStatus(transferResponse.ID)
if err != nil {
fmt.Printf("Error fetching transfer status: %v\n", err)
return
}
// Step 5: Verify transaction
transactionVerification, err := verifyTransaction(transferResponse.ID)
if err != nil {
fmt.Printf("Error verifying transaction: %v\n", err)
return
}
fmt.Printf("Transfer Complete, Transaction Status: %s, Amount: %.2f\n", transactionVerification.Status, transactionVerification.Amount)
}
Use the command below to run your application:
Next Step
Check out other API endpoints you can integrate.
You can also checkout our best practices and error handling guides to learn how to build more robust applications.