|
|
package com.yj.utils;
|
|
|
|
|
|
/**
|
|
|
* @author yj
|
|
|
* @create 2020-08-30 9:00
|
|
|
*/
|
|
|
|
|
|
import java.io.UnsupportedEncodingException;
|
|
|
import java.security.MessageDigest;
|
|
|
import java.security.NoSuchAlgorithmException;
|
|
|
import java.util.Arrays;
|
|
|
|
|
|
public class PaymentUtil {
|
|
|
|
|
|
// 定义字符编码
|
|
|
private static String encodingCharset = "UTF-8";
|
|
|
|
|
|
/**
|
|
|
* 生成hmac方法,用于构建支付请求的参数签名
|
|
|
*
|
|
|
* @param p0_Cmd 业务类型
|
|
|
* @param p1_MerId 商户编号
|
|
|
* @param p2_Order 商户订单号
|
|
|
* @param p3_Amt 支付金额
|
|
|
* @param p4_Cur 交易币种
|
|
|
* @param p5_Pid 商品名称
|
|
|
* @param p6_Pcat 商品种类
|
|
|
* @param p7_Pdesc 商品描述
|
|
|
* @param p8_Url 商户接收支付成功数据的地址
|
|
|
* @param p9_SAF 送货地址
|
|
|
* @param pa_MP 商户扩展信息
|
|
|
* @param pd_FrpId 银行编码
|
|
|
* @param pr_NeedResponse 应答机制
|
|
|
* @param keyValue 商户密钥
|
|
|
* @return 生成的hmac签名
|
|
|
*/
|
|
|
public static String buildHmac(String p0_Cmd, String p1_MerId,
|
|
|
String p2_Order, String p3_Amt, String p4_Cur, String p5_Pid, String p6_Pcat,
|
|
|
String p7_Pdesc, String p8_Url, String p9_SAF, String pa_MP, String pd_FrpId,
|
|
|
String pr_NeedResponse, String keyValue) {
|
|
|
StringBuilder sValue = new StringBuilder();
|
|
|
// 依次追加所有参数到StringBuilder
|
|
|
sValue.append(p0_Cmd);
|
|
|
sValue.append(p1_MerId);
|
|
|
sValue.append(p2_Order);
|
|
|
sValue.append(p3_Amt);
|
|
|
sValue.append(p4_Cur);
|
|
|
sValue.append(p5_Pid);
|
|
|
sValue.append(p6_Pcat);
|
|
|
sValue.append(p7_Pdesc);
|
|
|
sValue.append(p8_Url);
|
|
|
sValue.append(p9_SAF);
|
|
|
sValue.append(pa_MP);
|
|
|
sValue.append(pd_FrpId);
|
|
|
sValue.append(pr_NeedResponse);
|
|
|
|
|
|
// 调用hmacSign方法生成签名
|
|
|
return PaymentUtil.hmacSign(sValue.toString(), keyValue);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 验证支付网关回调的hmac签名
|
|
|
*
|
|
|
* @param hmac 支付网关发来的加密验证码
|
|
|
* @param p1_MerId 商户编号
|
|
|
* @param r0_Cmd 业务类型
|
|
|
* @param r1_Code 支付结果
|
|
|
* @param r2_TrxId 易宝支付交易流水号
|
|
|
* @param r3_Amt 支付金额
|
|
|
* @param r4_Cur 交易币种
|
|
|
* @param r5_Pid 商品名称
|
|
|
* @param r6_Order 商户订单号
|
|
|
* @param r7_Uid 易宝支付会员ID
|
|
|
* @param r8_MP 商户扩展信息
|
|
|
* @param r9_BType 交易结果返回类型
|
|
|
* @param keyValue 密钥
|
|
|
* @return 验证结果,true表示验证通过
|
|
|
*/
|
|
|
public static boolean verifyCallback(String hmac, String p1_MerId,
|
|
|
String r0_Cmd, String r1_Code, String r2_TrxId, String r3_Amt,
|
|
|
String r4_Cur, String r5_Pid, String r6_Order, String r7_Uid,
|
|
|
String r8_MP, String r9_BType, String keyValue) {
|
|
|
StringBuilder sValue = new StringBuilder();
|
|
|
// 依次追加所有回调参数到StringBuilder
|
|
|
sValue.append(p1_MerId);
|
|
|
sValue.append(r0_Cmd);
|
|
|
sValue.append(r1_Code);
|
|
|
sValue.append(r2_TrxId);
|
|
|
sValue.append(r3_Amt);
|
|
|
sValue.append(r4_Cur);
|
|
|
sValue.append(r5_Pid);
|
|
|
sValue.append(r6_Order);
|
|
|
sValue.append(r7_Uid);
|
|
|
sValue.append(r8_MP);
|
|
|
sValue.append(r9_BType);
|
|
|
// 调用hmacSign方法生成签名并与支付网关的签名比较
|
|
|
String sNewString = PaymentUtil.hmacSign(sValue.toString(), keyValue);
|
|
|
return sNewString.equals(hmac);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 使用HMAC-MD5算法生成签名
|
|
|
*
|
|
|
* @param aValue 待签名的字符串
|
|
|
* @param aKey 密钥
|
|
|
* @return 生成的签名
|
|
|
*/
|
|
|
public static String hmacSign(String aValue, String aKey) {
|
|
|
// 初始化内部填充字节数组
|
|
|
byte k_ipad[] = new byte[64];
|
|
|
byte k_opad[] = new byte[64];
|
|
|
byte keyb[];
|
|
|
byte value[];
|
|
|
try {
|
|
|
// 使用指定的字符编码获取字节数组
|
|
|
keyb = aKey.getBytes(encodingCharset);
|
|
|
value = aValue.getBytes(encodingCharset);
|
|
|
} catch (UnsupportedEncodingException e) {
|
|
|
// 如果不支持指定的字符编码,则使用默认字符编码
|
|
|
keyb = aKey.getBytes();
|
|
|
value = aValue.getBytes();
|
|
|
}
|
|
|
|
|
|
// 填充k_ipad和k_opad数组
|
|
|
Arrays.fill(k_ipad, keyb.length, 64, (byte) 54);
|
|
|
Arrays.fill(k_opad, keyb.length, 64, (byte) 92);
|
|
|
for (int i = 0; i < keyb.length; i++) {
|
|
|
k_ipad[i] = (byte) (keyb[i] ^ 0x36);
|
|
|
k_opad[i] = (byte) (keyb[i] ^ 0x5c);
|
|
|
}
|
|
|
|
|
|
// 获取MD5算法的MessageDigest实例
|
|
|
MessageDigest md = null;
|
|
|
try {
|
|
|
md = MessageDigest.getInstance("MD5");
|
|
|
} catch (NoSuchAlgorithmException e) {
|
|
|
// 如果MD5算法不可用,则返回null
|
|
|
return null;
|
|
|
}
|
|
|
// 对k_ipad和待签名数据进行更新
|
|
|
md.update(k_ipad);
|
|
|
md.update(value);
|
|
|
byte dg[] = md.digest(); // 进行第一次摘要
|
|
|
md.reset(); // 重置MessageDigest实例
|
|
|
// 对k_opad和第一次摘要结果进行更新
|
|
|
md.update(k_opad);
|
|
|
md.update(dg, 0, 16);
|
|
|
dg = md.digest(); // 进行第二次摘要,得到最终的签名
|
|
|
return toHex(dg); // 将字节数组转换为十六进制字符串
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 将字节数组转换为十六进制字符串
|
|
|
*
|
|
|
* @param input 字节数组
|
|
|
* @return 十六进制字符串
|
|
|
*/
|
|
|
public static String toHex(byte input[]) {
|
|
|
if (input == null)
|
|
|
return null;
|
|
|
StringBuffer output = new StringBuffer(input.length * 2);
|
|
|
for (int i = 0; i < input.length; i++) {
|
|
|
int current = input[i] & 0xff;
|
|
|
if (current < 16)
|
|
|
output.append("0");
|
|
|
output.append(Integer.toString(current, 16));
|
|
|
}
|
|
|
return output.toString();
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 根据参数数组和密钥生成HMAC签名
|
|
|
*
|
|
|
* @param args 参数数组
|
|
|
* @param key 密钥
|
|
|
* @return 生成的HMAC签名
|
|
|
*/
|
|
|
public static String getHmac(String[] args, String key) {
|
|
|
if (args == null || args.length == 0) {
|
|
|
return (null);
|
|
|
}
|
|
|
StringBuffer str = new StringBuffer();
|
|
|
for (int i = 0; i < args.length; i++) {
|
|
|
str.append(args[i]);
|
|
|
}
|
|
|
return (hmacSign(str.toString(), key));
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 使用SHA算法对字符串进行摘要
|
|
|
*
|
|
|
* @param aValue 待摘要的字符串
|
|
|
* @return 摘要结果的十六进制字符串
|
|
|
*/
|
|
|
public static String digest(String aValue) {
|
|
|
aValue = aValue.trim();
|
|
|
byte value[];
|
|
|
try {
|
|
|
value = aValue.getBytes(encodingCharset);
|
|
|
} catch (UnsupportedEncodingException e) {
|
|
|
value = aValue.getBytes();
|
|
|
}
|
|
|
MessageDigest md = null;
|
|
|
try {
|
|
|
md = MessageDigest.getInstance("SHA");
|
|
|
} catch (NoSuchAlgorithmException e) {
|
|
|
e.printStackTrace();
|
|
|
return null;
|
|
|
}
|
|
|
return toHex(md.digest(value));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
} |