Skip to content

Integration Guide

Integration Steps

  1. Register merchant access permissions on the platform
  2. Log in to the platform to view merchant key and iv
  3. Use the platform’s test APIs to complete gateway integration and API development/testing
  4. Contact our team in advance to whitelist your IP address
  5. After completing integration with the test server, contact our team to obtain the production merchant account and keys for replacement
  6. After switching to production, contact our team to whitelist the production IP address

Note: All API interactions must be transmitted over encrypted channels. If you encounter any issues, please contact the platform’s technical support team

Request Format Specification

Test Environment URL: https://t-api.nuvigopay.com
Production Environment URL: https://api.nuvigopay.com

Communication Protocol

The open platform communicates with merchants via HTTP POST requests. All responses are returned in JSON format. All amount-related fields must retain two decimal places.

Message Structure

All request and response character encoding uses UTF-8

  • Request – Common Parameters (HTTP Headers):
Field NameDescriptionTypeRequiredRemarks
X-Merchant-CodeMerchant CodestringYesAssigned by the platform
  • Request – HTTP Body
Field NameDescriptionTypeRequiredRemarks
contentEncrypted datastringYesAPI request data encrypted before sending
timestampTimestampnumberYesCurrent timestamp (milliseconds)
  • Receive response and decrypt content:
Field NameDescriptionTypeRequiredRemarksExample
codeResponse CodeintYes200: Success; others indicate failure
msgMessagestringNoError or status message
dataResponse DatastringNoDetailed response result data

Common Error Codes

Note: For different merchants, the description corresponding to the same code may vary slightly depending on business context.

codeDescription
200Success
9999Payout Failed
6001Insufficient Balance
7001Channel Closed
1001Order Query Failed
2000User Order Number is Required

Signature Algorithm Description

Convert the request parameters into JSON, then encrypt them as the input parameters of the encryption algorithm.

java
import com.exception.BusinessException;
import lombok.experimental.UtilityClass;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.Security;
import java.util.Base64;

@UtilityClass
public class aesutil {
    private static final String AES = "AES";

    private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding";

    private static Base64.Encoder base64Encoder = Base64.getEncoder();
    private static Base64.Decoder base64Decoder = Base64.getDecoder();

    static {
        Security.setProperty("crypto.policy", "unlimited");
    }

    private static final String key = "The key can be viewed in the merchant dashboard.";
    private static final String iv = "The IV can be viewed in the merchant dashboard";

    public String encrypt(String content) {
        try {
            SecretKey secretKey = new SecretKeySpec(key.getBytes(), AES);
            Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(iv.getBytes()));

            byte[] byteEncode = content.getBytes(StandardCharsets.UTF_8);
            byte[] byteAES = cipher.doFinal(byteEncode);
            return base64Encoder.encodeToString(byteAES);
        } catch (Exception e) {
            throw new BusinessException("Encryption failed.");
        }
    }

    public String decrypt(String content) {
        try {
            SecretKey secretKey = new SecretKeySpec(key.getBytes(), AES);
            Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv.getBytes()));

            byte[] byteContent = base64Decoder.decode(content);
            byte[] byteDecode = cipher.doFinal(byteContent);
            return new String(byteDecode, StandardCharsets.UTF_8);
        } catch (Exception e) {
            throw new BusinessException("Decryption failed.");
        }
    }

    public static void main(String[] args) {
        String content = "hello world";
        String encrypt = aesutil.encrypt(content);
        System.out.println(encrypt);
        String decrypt = aesutil.decrypt(encrypt);
        System.out.println(decrypt);
    }
}
php
$key = '********************';
$iv = '**************';

function aes256CbcEncrypt($data)
{
    global $iv, $key;
    return base64_encode(openssl_encrypt($data, 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv));
}

function aes256CbcDecrypt($encryptedData)
{
    global $iv, $key;
    return openssl_decrypt(base64_decode($encryptedData), 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv);
}
go
// Global variables (can be replaced with configuration file loading or other methods).
var (
	host = "https://api.******.com"
)

// AES-256-CBC Encrypt
func aes256CbcEncrypt(data []byte, merchant *ent.Merchant) (string, error) {
	block, err := aes.NewCipher([]byte(merchant.Secret))
	if err != nil {
		return "", err
	}

	data = pkcs7Pad(data, aes.BlockSize)

	cipherText := make([]byte, len(data))
	mode := cipher.NewCBCEncrypter(block, []byte(merchant.Iv))
	mode.CryptBlocks(cipherText, data)

	return base64.StdEncoding.EncodeToString(cipherText), nil
}

// AES-256-CBC Decrypt
func aes256CbcDecrypt(encrypted string, merchant *ent.Merchant) ([]byte, error) {
	cipherData, err := base64.StdEncoding.DecodeString(encrypted)
	if err != nil {
		return nil, err
	}

	block, err := aes.NewCipher([]byte(merchant.Secret))
	if err != nil {
		return nil, err
	}

	if len(cipherData)%aes.BlockSize != 0 {
		return nil, fmt.Errorf("cipher data is not a multiple of the block size")
	}

	decrypted := make([]byte, len(cipherData))
	mode := cipher.NewCBCDecrypter(block, []byte(merchant.Iv))
	mode.CryptBlocks(decrypted, cipherData)

	return pkcs7Unpad(decrypted)
}

// PKCS7 Fill
func pkcs7Pad(data []byte, blockSize int) []byte {
	padding := blockSize - len(data)%blockSize
	padText := bytes.Repeat([]byte{byte(padding)}, padding)
	return append(data, padText...)
}

// PKCS7 Remove padding
func pkcs7Unpad(data []byte) ([]byte, error) {
	length := len(data)
	if length == 0 {
		return nil, fmt.Errorf("data is empty")
	}

	padding := int(data[length-1])
	if padding > length || padding == 0 {
		return nil, fmt.Errorf("invalid padding")
	}

	return data[:length-padding], nil
}

Request Example

java
 private static final String Host = "https://**********.com";

    public static void sendPost() throws IOException {
        String url = "/*******/****";
        String time = new SimpleDateFormat("yyyyMMddHHmmss").format(System.currentTimeMillis());
        HttpRequest post = HttpUtil.createPost(Host + url);

        Map<String, Object> re = new HashMap<String, Object>();
        re.put("submitAmount", "2000");
        re.put("currencyType", "BRL");
        re.put("paymentType", "VA");
        re.put("userOrderNumber", "dy"+time);
        re.put("notifyUrl", "*********");

        String content = aesutil.encrypt(JSONUtil.toJsonStr(re));
        long timestamp = System.currentTimeMillis();

        Map<String, Object> req = new HashMap<String, Object>();
        req.put("content", content);
        req.put("timestamp", timestamp);
        post.header("X-Merchant-Code", "83272331");

        post.body(JSONUtil.toJsonStr(req));
        System.out.println(JSONUtil.toJsonStr(req));

        HttpResponse execute = post.execute();
        System.out.printf("post:" + post);
        System.out.printf("result:" + execute.body());

        ObjectMapper objectMapper = new ObjectMapper();
        HolyPay response = objectMapper.readValue(execute.body(), HolyPay.class);
        System.out.println("Decrypt:");
        System.out.println("Decrypt: "+aesutil.decrypt(response.getData().getContent()));

    }
php
$host = '***********************';
$merchantId = (string)'**********************';
$key = '********************';
$iv = '**************';


function sendPost($action, $param)
{
    global $merchantId, $iv, $key, $host;
    $content = aes256CbcEncrypt(json_encode($param));
    $timestamp = time();

    $req = array(
        "content" => $content,
        "timestamp" => $timestamp
    );

    $ch = curl_init($host . $action);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json', 'X-Merchant-Code: ' . $merchantId,));
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($req));

    $response = curl_exec($ch);
    if ($response === false) {
        echo "Error: " . curl_error($ch);
    } else {
        echo "post: The request information is difficult to simulate and output directly\n";
        echo "result: " . $response . "\n";
    }
    curl_close($ch);
    return $response;
}
go
// Global variables (can be replaced with configuration file loading or other approaches)
var (
	host = "https://api.******.com"
)

type PayRes struct {
	Code int           `json:"code"`
	Data BussinessData `json:"data"`
	Msg  string        `json:"msg"`
}
type BussinessData struct {
	Content   string `json:"content"`
	Timestamp int64  `json:"timestamp"`
}

// Send a POST request
func sendPost(action string, param map[string]interface{}, merchant *ent.Merchant) (*BussinessData, error) {
	jsonData, err := json.Marshal(param)
	if err != nil {
		return nil, err
	}
	log.Info().Msg(string(jsonData))
	content, err := aes256CbcEncrypt(jsonData, merchant)
	if err != nil {
		return nil, err
	}

	reqBody := map[string]interface{}{
		"content":   content,
		"timestamp": time.Now().Unix(),
	}

	bodyBytes, err := json.Marshal(reqBody)
	if err != nil {
		return nil, err
	}

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

	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("X-Merchant-Code", merchant.MerchantsNumber)

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	respBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	log.Info().Msg(string(respBody))
	log.Info().Msg(strconv.Itoa(resp.StatusCode))
	// Parse the response JSON
	var returnData PayRes
	err = json.Unmarshal([]byte(respBody), &returnData)
	if err != nil {
		fmt.Println("Failed to parse the response:", err)
		return nil, err
	}
	log.Info().Interface("returnData", returnData).Msg("<UNK>")
	if returnData.Code != 200 {
		return nil, errors.New(returnData.Msg)
	}

	return &returnData.Data, nil
}

Merchant Balance Inquiry

URL

POST /proxyPayQuery/balance

Request Parameters

Field NameDescriptionTypeMax LengthRequiredRemarksExample
currencyTypeCurrency typestring50YesSee currency codes belowBRL

Response Parameters

Field NameDescriptionTypeMax LengthRequiredRemarksExample
usableAmountAvailable Account Balancestring12YesTwo decimal places100.00

Currency Codes

!!! Do NOT modify letter case of the currency codes !!!

  1. Brazil(BRL)