Skip to content

通用接口说明

通用请求是商户侧调用平台各业务接口都需要的参数,当前版本采用HTTP请求头部信息的方式。

回调IP地址

  • 54.169.54.48
  • 3.0.143.201
  • 52.76.5.66

金额单位

Rupiah(元)

请求测试域名

参数是否必须内容
API endpointhttps://test-merchant.hzpay.net/prex/

请求正式域名

参数是否必须内容
API endpointhttps://merchant.hzpay.net/prex/

请求头部信息

WARNING

接口请求和回调中均包含请求头数据

参数是否必须内容
Content-Typeapplication/json
Api-Key由平台分配的商户唯一标识
Request-Id商户请求ID,由商户侧保证唯一,建议采用UUID、NanoID 或者长度在8位以上32位以下的无特殊符号字符串
TimestampLong类型,当前时间与世界统一时间UTC(1970年1月1日)的差值,以毫秒为单位
Sign用于核实身份的签名信息(签名计算方法见下面的签名说明)

签名计算方法

代收签名计算 ✅

签名采用对【请求体】采用 SHA-256 加密算法进行加密计算,

通过分隔符”&”将 【商户唯一标识】、【请求体】、【商户请求 ID】、【时间戳】排列为一个组件,

通过分配给商户的加密密钥从上述组件中计算 HMAC-SHA256,并将加密结果转换为 BASE64 字符串的方式来生成。

假设参数如下:

  • 商户唯一标识(api-key) : 934ns90d
  • 商户加密密钥(api-secret): 90oa4dowox00o3cd
  • 商户请求(requestId): d123455678892238729
  • 请求时间戳(timestamp): 1687227487329
  • 请求体(body): 如下所示

DANGER

注意:请求数据传过来的部分均需要加密,下述加密数据中的 body 仅为示例

签名计算示例:

java
public static String sha256(String body) throws Exception {
  MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
  byte[] hash = messageDigest.digest(body.getBytes("UTF-8"));
  return Base64.getEncoder().encodeToString(hash);
}

public static String hmacSha256(String component, String secret) throws Exception {
  byte[] decodedKey = secret.getBytes(StandardCharsets.UTF_8);
  SecretKey originalKey = new SecretKeySpec(decodedKey, 0, decodedKey.length, "HmacSHA256");
  Mac hmacSha256 = Mac.getInstance("HmacSHA256");
  hmacSha256.init(originalKey);
  hmacSha256.update(component.getBytes(StandardCharsets.UTF_8));
  byte[] HmacSha256DigestBytes = hmacSha256.doFinal();
  String signature = Base64.getEncoder().encodeToString(HmacSha256DigestBytes);
  return signature;
}

加密数据:
String body = {"order":{"amount":10000,"callback":"http://yourdomain.com/callback _success","id":"9bddcb4114b24aceb49daa506b7b406d"}}
String component = Api-Key={yourApiKey}&Body-Hash={sha256(body)}&Request- Id={requestId}&Timestamp={timestamp};
String api-secret = 90oa4dowox00o3cd;
String sign = hmacSha256(component, api-secret);
// component: Api-Key=934ns90d&Body-Hash=VodvE2oJFTVS9AE6vRD+hFA8agUgEvkGxsY+QQys4uc=&Request-Id=123455678892238729&Timestamp=1687227487329
// sign: Oa6V892jbd3BovnCCug7UJ+RUcz1HvjK1WfhwVLOztI=
php
<?php
function sha256($str) {
  try {
    $hash = hash('sha256', $str, true);
    $base64Encoded = base64_encode($hash);
    return $base64Encoded;
  } catch (Exception $e) {
    throw new Exception("SHA-256 加密失败", 0, $e);
  }
}
function hmacSha256($component, $secret) {
  try {
    $decodedKey = utf8_decode($secret);
    $originalKey = hash_hmac('sha256', $component, $decodedKey, true);
    $signature = base64_encode($originalKey);
    return $signature;
  } catch (Exception $e) {
    throw new Exception("HMAC SHA-256 计算失败", 0, $e);
  }
}
try {
  $body = '{"order":{"amount":10000,"callback":"http://yourdomain.com/callback_success", "id":"9bddcb4114b24aceb49daa506b7b406d"}}';
  $bodyHash = sha256($body);
  $list = [
    "Api-Key=934ns90d",
    "Body-Hash=" . $bodyHash,
    "Request-Id=123455678892238729",
    "Timestamp=1687227487329"
  ];
  // 使用 implode 连接列表中的字符串
  $before = implode("&", $list);
  $sign = hmacSha256($before, "90oa4dowox00o3cd");
  echo "Before: " . $before . "\n";
  echo "Signature: " . $sign . "\n";
} catch (Exception $e) {
  echo $e->getMessage() . "\n";
}
?>
c#
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;

class Program
{
  static void Main()
  {
    string apiKey = "your-api-key";
    string body = "your-request-body";
    string requestId = "your-request-id";
    string timestamp = "your-timestamp";
    string secret = "your-secret";

	try
    {
      string body = "{\n" +
          "    \"order\":{\n" +
          "        \"amount\":10000,\n" +
          "        \"id\":\"12345678932\",\n" +
          "    }\n" +
          "}";
      string sha256Hash = Sha256(body);

      Console.WriteLine("Original String: " + body);
      Console.WriteLine("SHA-256 Hash: " + sha256Hash);

      string signature = HmacSha256(component, secret);

      Console.WriteLine("Signature: " + signature);
    }
    catch (Exception e)
    {
      Console.WriteLine(e.Message);
    }

    List<string> list = new List<string>
    {
      "Api-Key=" + apiKey,
      "Body-Hash=" + sha256Hash,
      "Request-Id=" + requestId,
      "Timestamp=" + timestamp
    };
    // 使用 string.Join 连接列表中的字符串
    string before = string.Join("&", list);
    // 输出结果
    Console.WriteLine("Concatenated String: " + before);
  }

	// 生成 body-hash
	static string Sha256(string str)
  {
    using (SHA256 sha256 = SHA256.Create())
    {
      byte[] hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(str));
      return Convert.ToBase64String(hashBytes);
    }
  }

	// 生成签名
	static string HmacSha256(string component, string secret)
  {
    byte[] keyBytes = Encoding.UTF8.GetBytes(secret);
    using (HMACSHA256 hmac = new HMACSHA256(keyBytes))
    {
      byte[] componentBytes = Encoding.UTF8.GetBytes(component);
      byte[] hashBytes = hmac.ComputeHash(componentBytes);

      // 将字节数组转换为 Base64 编码的字符串
      string signature = Convert.ToBase64String(hashBytes);

      return signature;
    }
  }
}
python
import hashlib
import hmac
import base64

def sha256(data):
  """Compute SHA-256 hash of the given data and return it as a Base64 encoded string."""
  # Create a new sha256 hash object
  message_digest = hashlib.sha256()
  # Update the hash object with the bytes of the data
  message_digest.update(data.encode('utf-8'))
  # Get the hash bytes
  hash_bytes = message_digest.digest()
  # Encode the hash bytes to a Base64 encoded string
  hash_base64 = base64.b64encode(hash_bytes).decode('utf-8')
  
  return hash_base64

def hmac_sha256(data, key):
  return base64.b64encode(hmac.new(key.encode('utf-8'), data.encode('utf-8'), hashlib.sha256).digest()).decode('utf-8')

def merchant_sign(api_key, request_id, timestamp, body, api_secret):
  try:
    list_to_join = [
        f"Api-Key={api_key}",
        f"Body-Hash={sha256(body)}",
        f"Request-Id={request_id}",
        f"Timestamp={timestamp}"
    ]
    before = "&".join(list_to_join)
    print("before:", before)
    return hmac_sha256(before, api_secret)
  except Exception as e:
    print(f"Error: {str(e)}")
    return None

示例:
api_key = "ABCDWER12"
request_id = "123455678892238729"
timestamp = 1687227487329
body = """{"order":{"amount":10000,"id":"2024070410329301","ewallet_phone":"111111111"},"payment":{"payment_method_types":["DANA"]}}"""
api_secret = "AEKRIU1254838DJK"

signature = merchant_sign(api_key, request_id, timestamp, body, api_secret)
print("Signature:", signature)
#  before: Api-Key=ABCDWER12&Body-Hash=gEomqJpTFfGEEQgJu+MaB+NIYfOMmSCyR8tH2qOIJAI=&Request-Id=123455678892238729&Timestamp=1687227487329
# Sign: 8U0AtOVcgRMWEGiu3hCDCuhKMUaqLh9TFg0urRTvujw=

代付签名计算 ✅

签名采用对【APIKey】+【请求唯一标识】+【时间戳】+【APISecret】(平台分配给商户的加密密钥) 采用AES加密算法进行加密计算, 并将加密结果转换为BASE64字符串的方式来生成。

假设参数如下:

  • APIKey:M12345
  • APISecret:abcdef1234567890
  • 请求唯一标识(Request-Id):11223344-5566-7788-9900-abcdabcdabcd
  • 时间戳(Timestamp):1687227487329

签名计算示例:

java
import org.apache.commons.codec.binary.Base64; import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.security.Key;

private static final String ALGORITHM = "AES";
public static String encrypt(String valueToEnc, String encKey) throws Exception {
  Key key = new SecretKeySpec(encKey.getBytes(), ALGORITHM); // 使用 APISecret 生成加密密钥
  Cipher c = Cipher.getInstance(ALGORITHM); // 获取 Cipher 实例
  c.init(Cipher.ENCRYPT_MODE, key); // 初始化 Cipher 为加密模式
  byte[] encValue = c.doFinal(valueToEnc.getBytes()); // 对 valueToEnc 字符串加密
  return new String(Base64.encodeBase64(encValue)); // 返回加密结果的 Base64 编码
}

public static void main(String[] args) throws Exception {
  String valueToEnc = "M12345" + "11223344-5566-7788-9900-abcdabcdabcd" + "1687227487329"; // APIKey + Request-Id + Timestamp
  String encKey= "abcdef1234567890"; // APISecret
  String sign = encrypt(valueToEnc, encKey);
// sign: XWtW50jBF1a3t8UiYoMO5JUZz5PO4mGdLXBybLgSi5FLx+rga286c0Y5Dr9lgSz3HGZauWLiIb7Vzv0JB z8+5Q==
}
php
<?php
  $apiKey = "M12345";
  $requestId = "11223344-5566-7788-9900-abcdabcdabcd";
  $timestamp = "1687227487329";
  $valueToEnc = $apiKey . $requestId . $timestamp;
  $encKey = "abcdef1234567890";
  $algorithm = "aes-128-ecb";
  try {
    // OpenSSL 加密函数
    $encValue = openssl_encrypt($valueToEnc, $algorithm, $encKey, OPENSSL_RAW_DATA);
    // Base64 编码
    $encValueBase64 = base64_encode($encValue);
    echo $encValueBase64;
  } catch (Exception $e) {
    error_log($e->getMessage());
  }
?>
c#
using System;
using System.Security.Cryptography;
using System.Text;

class Program {
  private const string ALGORITHM = "AES";
  public static void Main(string[] args) {
    string valueToEnc = "M1234511223344-5566-7788-9900-abcdabcdabcd1687227487329"; // APIKey + Request-Id + Timestamp
    string encKey = "abcdef1234567890"; // APISecret
    try {
      // Create a new AES instance
      using (Aes aes = Aes.Create()) {
        aes.Key = Encoding.UTF8.GetBytes(encKey);
        aes.Mode = CipherMode.ECB;
        aes.Padding = PaddingMode.PKCS7;
        // Create an encryptor
        ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
        // Encrypt the data
        byte[] encValue = encryptor.TransformFinalBlock(Encoding.UTF8.GetBytes(valueToEnc), 0, valueToEnc.Length);
        // Convert to Base64
        string encValueBase64 = Convert.ToBase64String(encValue);
        Console.WriteLine(encValueBase64);
      }
    } catch (Exception e) {
      Console.Error.WriteLine(e.ToString());
    }
  }
}
python
安装库实现AES加密 pip install pycryptodome

from Crypto.Cipher import AES
from Crypto.Util.Padding import pad import base64

def main():
  value_to_enc = " M1234511223344-5566-7788-9900-abcdabcdabcd1687227487329" # APIKey + Request-Id + Timestamp
  enc_key = "abcdef1234567890" # APISecret
  try:
    # Ensure the key is 16 bytes long
    key = enc_key.encode('utf-8')
    # Initialize cipher with AES/ECB/PKCS5Padding
    cipher = AES.new(key, AES.MODE_ECB)

    # Pad the value to be encrypted
    padded_value_to_enc = pad(value_to_enc.encode('utf-8'), AES.block_size)
    # Perform encryption
    enc_value = cipher.encrypt(padded_value_to_enc)
    # Encode the encrypted value with Base64
    encrypted_value_base64 = base64.b64encode(enc_value).decode('utf-8')
    print(encrypted_value_base64)
  except Exception as e:
    print(f"Error: {str(e)}")

if __name__ == "__main__":
  main()

通用返回

当接口请求处理被支付平台接收后,无论是处理成功还是失败,接口均会返回包含如下内容的通用状态信息。

代收通用状态信息

json
{
  "status": 1,
  "data": {}
}

代收状态代码说明

代码说明可能的原因
1成功成功
其他失败失败

代付通用状态信息

json
{
  "status": 1,
  "message": "PROCESSING SUCCESS",
  "data": {}
}

代付状态代码说明

代码说明可能的原因
0PROCESSING FAILED不能确定的外部原因造成的失败
1PROCESSING SUCCESS处理成功
10IP ILLEGALIP地址不合法,发起请求的服务器地址与备案的不符合
11IDENTITY ERROR身份验证不通过,用于核实身份的信息不正确
12PARAMETER FORMAT ERROR参数格式错误,JSON数据不正确
13NECESSARY PARAMETER MISS缺少必要的参数
14ORDER_ID REPEAT交易流水号重复
15BANK_ID ERROR代付银行编码错误
20REQUEST REJECTED请求被拒绝,请求过于频繁或者被限制
21PROCESSING TIMEOUT处理超时
22INQUIRY ACCOUNT INFORMATION ERROR查询收款人账户信息错误,收款人账户信息有误或者被限制
23LOW_BALANCE余额不足
24ORDER_NOT_EXIST订单不存在
99UNKNOWN ERROR平台未知异常