字段 | 说明 | 类型 | 必传 |
---|---|---|---|
MERCHANTID | 商户代码,由建行统一分配 | CHAR(15) | 是 |
POSID | 商户柜台代码,由建行统一分配 | CHAR(9) | 是 |
BRANCHID | 分行代码,由建行统一分配 | CHAR(9) | 是 |
ORDERID | 订单号,由商户提供,最长30位(数字) | CHAR(30) | 是 |
PAYMENT | 付款金额,由商户提供,按实际金额给出。支付完成后,请商户与收到的商户通知中的付款金额比对,确认两者金额一致; | NUMBER(16,2) | 是 |
CURCODE | 币种,缺省为01-人民币(目前只支持人民币支付) | CHAR(2) | 是 |
REMARK1 | 备注1,一般作为商户自定义备注信息使用,可在对账单中显示。集团主商户代理发起子商户交易时,需将集团主商户信息填入该字段,格式如下:JTSH:主商户号+主柜台例如:JTSH:105000000000000123456789其中105000000000000为主商户号,123456789为主柜台 | CHAR(30) | 否 |
REMARK2 | 备注2,一般作为商户自定义备注信息使用,可在对账单中显示。 | CHAR(30) | 否 |
TXCODE | 交易码,由建行统一分配 ,为530590 | CHAR(6) | 是 |
MAC | MAC校验域,采用标准MD5算法,由商户实现 | CHAR(32) | 是 |
TYPE | 接口类型,分行业务人员在P2员工渠道后台设置防钓鱼的开关。1-防钓鱼接口 | CHAR(1) | 是 |
PUB | 公钥后30位,商户从建行商户服务平台下载,截取后30位。仅作为源串参加MD5摘要,不作为参数传递 | CHAR(30) | 是 |
GATEWAY | 网关类型,默认为0 | VARCHAR(100) | 是 |
CLIENTIP | 客户端IP,客户在商户系统中的IP,即客户登陆(访问)商户系统时使用的ip) | CHAR(40) | 否 |
REGINFO | 客户注册信息,客户在商户系统中注册的信息,中文需使用escape编码 | CHAR(256) | 否 |
PROINFO | 商品信息,客户购买的商品中文需使用escape编码 | CHAR(256) | 否 |
REFERER | 商户URL,商户送空值即可;具体请看REFERER设置说明 | CHAR(100) | 否 |
TIMEOUT | 订单超时时间,格式:YYYYMMDDHHMMSS如:20120214143005银行系统时间> TIMEOUT时拒绝交易,若送空值则不判断超时。当该字段有值时参与MAC校验,否则不参与MAC校验。 | CHAR(14) | 否 |
TRADE_TYPE | 交易类型,JSAPI–公众号支付、MINIPRO–小程序 | CHAR(16) | 是 |
SUB_APPID | 小程序/公众号的APPID,当前调起支付的小程序/公众号APPID | CHAR(32) | 是 |
SUB_OPENID | 用户子标识用户在小程序/公众号appid下的唯一标识,小程序通过wx.login获取,接口文档地址:https://developers.weixin.qq.com/miniprogram/dev/api/api-login.html?t=20161122 | CHAR(128) | 是 |
WX_CHANNELID | 渠道商号,对于商户自定义的渠道商号当该字段有值时参与MAC校验,否则不参与MAC校验。 | CHAR(20) | 否 |
RETURN_FIELD | 返回信息位图,共20位,商户通知是否返回某个字段的位图,0或空-不返回,1-返回。第1位:是否返回OPENID和SUB_OPENID第2位:保留位,默认送0第3位:保留位,默认送0 第4位:是否返回支付详细信息字段示例:10000000000000000000 | CHAR(20) | 否 |
USERPARAM | 实名支付,实名支付功能,包含类型、证件号、姓名三个子域(如果本字段出现,那么本字段包含的三个子域均需出现。详见下文说明5)USERPARAM字段说明)。当该字段有值时参与MAC校验,否则不参与MAC校验。暂未上线,请忽略 | VARCHAR(2000) | 否 |
若上送二级商户信息则八个二级商户信息字段必须都送值,当该字段有值时参与MAC校验,否则不参与MAC校验。
字段 | 说明 | 类型 | 必传 |
---|---|---|---|
SMERID | 二级商户代码,由建行统一分配 | CHAR(15) | 否 |
SMERNAME | 二级商户名称,中文需使用escape编码 | 中文(33)、英文CHAR(100) | 否 |
SMERTYPEID | 二级商户类别代码 | CHAR(15) | 否 |
SMERTYPE | 二级商户类别名称,汉字最长27个,中文需使用escape编码 | 中文(27)、英文CHAR(81) | 否 |
TRADECODE | 交易类型代码 | CHAR(15) | 否 |
TRADENAME | 交易类型名称,如消费、投资理财、信用卡还款等,中文需使用escape编码 | 中文(10),英文CHAR(30) | 否 |
SMEPROTYPE | 商品类别代码 | CHAR(24) | 否 |
PRONAME | 商品类别名称,中文需使用escape编码 | 中文(15)英文CHAR(50) | 否 |
注:字符串中变量名必须是大写字母。
1)参与摘要运算的字符串及其顺序如下:
请注意:加粗的字段请根据需要上送,有值时才参与MAC,否则无需参与MAC。
MERCHANTID=123456789&POSID=000000000&BRANCHID=110000000&ORDERID=19991101234&PAYMENT=0.01&CURCODE=01&TXCODE=530590&REMARK1=&REMARK2=&TYPE=1&PUB=30819d300d06092a864886f70d0108&GATEWAY=0&CLIENTIP=172.0.0.1®INFO=%u5C0F%u98DE%u4FA0&PROINFO=%u5145%u503C%u5361&REFERER=&SMERID=111&SMERNAME=%u5DE5%u827A%u7F8E%u672F%u5546%u5E97&SMERTYPEID=112&SMERTYPE=%u5BBE%u9986%u9910%u5A31%u7C7B&TRADECODE=001&TRADENAME=%u6D88%u8D39&SMEPROTYPE=1&PRONAME=%u5DE5%u827A%u54C1&TIMEOUT=20161028101226&TRADE_TYPE=MINIPRO&SUB_APPID=wx8888888888888888&SUB_OPENID=oUpF8uMuAJO_M2pxb1Q9zNjWeS6o &WX_CHANNELID=wx902937628837&USERPARAM=1U2xb%2FdMepRIs0KcM53xns%2Chdg2xBh3qwJ%2F%2F%2FHi%2FjMfPcbUYjQdxJKe%2CnoHnBgXppyQqPVPdDf8p%0D%0AEwxoLdkWjvdj2QUXJ5Hb
2)使用js的escape()方法对“客户注册信息”和“商品信息”进行转码,数字字母信息不需转码。
例:escape(小飞侠)= %u5C0F%u98DE%u4FA0
escape(充值卡)= %u5145%u503C%u5361
3)使用js的escape()方法对“二级商户名称”、“二级商户类别名称”、“交易类型名称”、“商品类别名称”进行转码,数字字母信息不需转码。
例: escape (工艺美术商店)=%u5DE5%u827A%u7F8E%u672F%u5546%u5E97
escape (宾馆餐娱类)= %u5BBE%u9986%u9910%u5A31%u7C7B
escape (消费)= %u6D88%u8D39
escape (工艺品)= %u5DE5%u827A%u54C1
4)完整的URL:
第三方商户提交给网银的网关地址:注意用post方式提交参数。
https://ibsbjstar.ccb.com.cn/CCBIS/ccbMain?CCB_IBSVersion=V6
请注意:加粗的字段请根据需要上送
https://ibsbjstar.ccb.com.cn/CCBIS/ccbMainCCB_IBSVersion=V6&MERCHANTID=105320148140002&POSID=100001135&BRANCHID=320000000&ORDERID=88487&PAYMENT=0.01&CURCODE=01&TXCODE=530590&REMARK1=&REMARK2=&TYPE=1&GATEWAY=0&CLIENTIP=128.128.80.125®INFO=xiaofeixia&PROINFO=digital&REFERER=&SMERID=111&SMERNAME=%u5DE5%u827A%u7F8E%u672F%u5546%u5E97&SMERTYPEID=112&SMERTYPE=%u5BBE%u9986%u9910%u5A31%u7C7B&TRADECODE=001&TRADENAME=%u6D88%u8D39&SMEPROTYPE=1&PRONAME=%u5DE5%u827A%u54C1&TIMEOUT=20161028101226&TRADE_TYPE=MINIPRO&SUB_APPID=wx8888888888888888&SUB_OPENID=oUpF8uMuAJO_M2pxb1Q9zNjWeS6o&WX_CHANNELID=wx902937628837&USERPARAM=1U2xb%2FdMepRIs0KcM53xns%2Chdg2xBh3qwJ%2F%2F%2FHi%2FjMfPcbUYjQdxJKe%2CnoHnBgXppyQqPVPdDf8p%0D%0AEwxoLdkWjvdj2QUXJ5Hb&RETURN_FIELD=10000000000000000000&MAC=b2a1adfc9f9a44b57731440e31710740
5)访问上述请求后,获得数据
{“SUCCESS”:“true”,“PAYURL”:“https://ibsbjstar.ccb.com.cn/CCBIS/B2CMainPlat_02?CCB_IBSVersion=V6&MERCHANTID=105320148140002&POSID=100001135&BRANCHID=320000000&ORDERID=88487&PAYMENT=0.01&CURCODE=01&TXCODE=530590&REMARK1=&REMARK2=&TYPE=1&GATEWAY=0&CLIENTIP=128.128.80.125®INFO=xiaofeixia&PROINFO=digital&REFERER=&SMERID=111&SMERNAME=%u5DE5%u827A%u7F8E%u672F%u5546%u5E97&SMERTYPEID=112&SMERTYPE=%u5BBE%u9986%u9910%u5A31%u7C7B&TRADECODE=001&TRADENAME=%u6D88%u8D39&SMEPROTYPE=1&PRONAME=%u5DE5%u827A%u54C1&TIMEOUT=20161028101226&TRADE_TYPE=MINIPRO&SUB_APPID=wx8888888888888888&SUB_OPENID=oUpF8uMuAJO_M2pxb1Q9zNjWeS6o&MAC=b2a1adfc9f9a44b57731440e31710740&QRCODE=1&CHANNEL=1” }
6)使用GET请求,直接访问PAYURL中的地址
7)获得JSON数据,
{
"appId": "wxad35f06adfdsgre3"
"errcode": "000000"
"errmsg": ""
"mweb_url": ""
"mypackage": "prepay_id=wx15155254732131244559e3cb82f0000"
"nonceStr": "wYggZEgyfdfdsfasdsaJpPOSw61sG"
"partnerid": "54523121"
"paySign": "Hn/e4XM7gOnfhADoN6ccVh2BnAX09zs2IjlqPs5PfckIkUXFRSwprCd9g94FU4NwoZd58tjtwFjiI/7z2qaXhMwNKlxthjgasavUWhhHd3Nb1JPIORiRXlN/lyElmDj4RQ/6+bheixrQmT7NlIX/gCcpRxJbIw+lmoNMbgJWB8nNZ4YOIkS8B9ybBjluNa4bqePwKxSfLJnDJmlm95IDIVcJ/+uuTED97peHPbEI39t966wFbibXxUi6cbeOtYieW7TkwDIt3LGX6SqvLlMybXDyuKGseyY0wG80UNOFShvOt60iFiFJhAuE0OXHFw=="
"prepayid": ""
"signType": "RSA"
"success": true
"timeStamp": "1605426774"
"txcode": "530590"
}
该参数为小程序/公众号调起微信的支付参数,具体参数说明如下:
字段 | 名称 | 说明 |
---|---|---|
SUCCESS | 返回状态码 | 此字段是通信标识,表示通信成功 |
ERRCODE | 错误码 | 000000表示交易成功,非000000表示交易失败,错误信息可以查看ERRMSG字段 |
ERRMSG | 错误信息 | 错误信息描述 |
TXCODE | 交易码 | 530590 |
appId | 微信分配的APPID | 参考微信对应的调起支付API |
timeStamp | 时间戳 | 参考微信对应的调起支付API |
nonceStr | 随机串 | 参考微信对应的调起支付API |
package | 数据包 | 参考微信对应的调起支付API |
signType | 签名方式 | 参考微信对应的调起支付API |
paySign | 签名数据 | 参考微信对应的调起支付API |
partnerid | 子商户的商户号 | 参考微信对应的调起支付API |
prepayid | 预支付交易会话ID | 参考微信对应的调起支付API |
mweb_url | 微信H5支付中间页面URL | 参考微信对应的调起支付API |
小程序:https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_sl_api.php?chapter=7_7&index=5
公众号:https://pay.weixin.qq.com/wiki/doc/api/jsapi_sl.php?chapter=7_7&index=6
说明:反馈地址,就是支付完成后,建行会向你设置的反馈地址发送信息,类似纯微信支付的反馈地址。
信息里面会有ORDERID(订单号)与PAYMENT(字符金额),当然还有其他的信息。一般会在反馈的处理方法里面进行金额校验与修改订状态的操作
查阅接口文档
建行开放接口文档
反馈地址这一块相对来说是比较难调试,也会遇到很多问题。我就说一下我遇到的问题以及我是如何解决的。
在测试版本(测试版本使用的反馈地址的协议是http)测试时,可正常接收到建行发送过来的反馈信息。但是切换到正式版本(正式版本使用的反馈地址的协议是https)后,发现支付完成后怎么都接收不到建行的反馈信息,建行那边查询原因后说是向反馈地址发送请求后返回的是403(服务器禁止访问),尝试了很多种方法,最后是通过开启springboot项目的双协议配置,即项目可同时通过http协议与https协议进行访问(不同协议端口不一致),回调地址使用的是http协议的。这是退而求其次的方法,这个问题并没有完全解决,今后如果找到解决方法,会更新博客。哪位大佬彻底解决了这个问题,也欢迎分享。
springboot开启双协议的配置
建行开放接口文档
<TX>
<REQUEST_SN>请求序列号REQUEST_SN>
<CUST_ID>商户号CUST_ID>
<USER_ID>操作员号USER_ID>
<PASSWORD>操作员号密码PASSWORD>
<TX_CODE>5W1019TX_CODE>
<LANGUAGE>CNLANGUAGE>
<TX_INFO>
<POS_ID>终端编号POS_ID>
TX_INFO>
TX>
<TX>
<REQUEST_SN>请求序列码REQUEST_SN>
<CUST_ID>商户号CUST_ID>
<TX_CODE>5W1019TX_CODE>
<RETURN_CODE>响应码RETURN_CODE>
<RETURN_MSG>响应信息RETURN_MSG>
<LANGUAGE>CNLANGUAGE>
<TX_INFO>
<POS_ACCDATE_FLAG>是否返回记账日期POS_ACCDATE_FLAG>
<LIST>
<EC_CgyCd>电子渠道类别代码EC_CgyCd>
<Rsrv_Fld_1>网页反馈地址Rsrv_Fld_1>
<Rsrv_Fld_2>服务器反馈地址Rsrv_Fld_2>
<Rsrv_Fld_3>实时反馈标志Rsrv_Fld_3>
LIST>
<NOTICE>提示信息NOTICE>
TX_INFO>
TX>
public Response test(){
RestTemplate restTemplate = new RestTemplateBuilder().build();
String urlXML =
"" +
"" +
"" + KeyUtil.uniqueKey() + "" +
"" + "" + "" +
"" + "" + "" +
"" + "" + "" +
"" + "5W1019" + "" +
"" + "CN"+ "" +
"" +
"" + "" + "" +
"" +
"";
String params = null;
params = "requestXml=" + urlXML;
String parameter = params;
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<String> requestEntity = new HttpEntity<String>(parameter, headers);
ResponseEntity<String> response = restTemplate.exchange("http://120.77.236.53:9999", HttpMethod.POST, requestEntity, String.class);
return Response.success(response.getBody());
}
import java.math.BigDecimal;
/**
* 建行支付服务类
*/
public interface JHPayService {
/**
* 发起支付
* @param orderNo 订单号
* @param payMoney 支付金额
* @param openid 微信用户openid
* @param appId 微信小程序appId
* @return
*/
JHPayResult JHPay(String orderNo, BigDecimal payMoney, String openid, String appId);
/**
* 发起退款
* @param orderNo 订单号
* @param refundMoney 退款金额
* @param payMoney 支付金额
* @return
*/
JHRefundResponse JHRefund(String orderNo, BigDecimal refundMoney, BigDecimal payMoney);
}
import com.google.gson.Gson;
import com.zengmi.config.JHPayProperties;
import com.zengmi.enums.ErrorEnum;
import com.zengmi.exception.MyException;
import com.zengmi.utils.KeyUtil;
import com.zengmi.utils.MD5Util;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.math.BigDecimal;
import java.util.Date;
/**
* 建行支付退款服务层
* @author IT00ZYQ
* @Date 2020/10/22 19:14
**/
@Service
@Slf4j
public class JHPayServiceImpl implements JHPayService{
private static final String successCode = "000000";
@Autowired
private JHPayProperties jhPayProperties;
@Autowired
private RestTemplate restTemplate;
@Override
public JHPayResult JHPay(String orderNo, BigDecimal payMoney, String openid, String appId) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<String> requestEntity = new HttpEntity<>(getPayRequestParam(orderNo, payMoney, openid, appId), headers);
//POST方式访问网银获取PAYURL
ResponseEntity<String> response = restTemplate.exchange(jhPayProperties.getURL(), HttpMethod.POST, requestEntity, String.class);
HttpEntity<String> requestEntity2 = new HttpEntity<>("", headers);
JHPayDTO dto = new Gson().fromJson(response.getBody(), JHPayDTO.class);
//GET方式访问PAYURL获取唤起微信支付的参数
ResponseEntity<String> response2 = restTemplate.exchange(dto.getPayUrl(), HttpMethod.GET, requestEntity2, String.class);
//将json字符串转化为WxPayment对象
JHPayResult result = new Gson().fromJson(response2.getBody(), JHPayResult.class);
result.setOrderNo(orderNo);
return result;
}
@Override
public JHRefundResponse JHRefund(String orderNo, BigDecimal refundMoney, BigDecimal payMoney) {
if (refundMoney.compareTo(payMoney) > 0 ){
throw new MyException(ErrorEnum.REFUND_MONEY_HAVE_ERROR);
}
log.info("【进入建行退款】订单号:{}, 退款金额:{}, 时间:{}", orderNo, refundMoney, new Date());
//请求外联
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<String> requestEntity = new HttpEntity<String>(getRefundRequestXml(orderNo, refundMoney), headers);
System.out.println(requestEntity);
ResponseEntity<String> response = this.restTemplate.exchange(jhPayProperties.getREFUND_URL(),
HttpMethod.POST,
requestEntity,
String.class);
if (ObjectUtils.isEmpty(response) || ObjectUtils.isEmpty(response.getBody())){
throw new MyException(ErrorEnum.REFUND_REQUEST_ERROR);
}
String str = response.getBody();
log.info("【建行退款返回信息】:{}", str);
//分割得到返回信息
String code = str.substring(str.indexOf("" )+13, str.lastIndexOf(""));
if (successCode.equals(code)){
return JHRefundResponse.builder()
.orderNo(str.substring(str.indexOf("" ) + 11, str.lastIndexOf("")))
.payMoney(new BigDecimal(str.substring(str.indexOf("" ) + 12, str.lastIndexOf(""))))
.refundMoney(new BigDecimal(str.substring(str.indexOf("" ) + 8, str.lastIndexOf(""))))
.success(true)
.build();
}else {
throw new MyException(ErrorEnum.REFUND_ERROR);
}
}
/**
* 建行支付请求参数处理
* @param orderNo
* @param payMoney
* @param openid
* @param appId
* @return
*/
private String getPayRequestParam(String orderNo, BigDecimal payMoney, String openid, String appId) {
//md5加密 MD5加密后生成32位(小写字母 + 数字)字符串
String sb1 = "MERCHANTID=" + jhPayProperties.getMERCHANTID() + "&" +
"POSID=" + jhPayProperties.getPOSID() + "&" +
"BRANCHID=" + jhPayProperties.getBRANCHID() + "&" +
"ORDERID=" + orderNo.trim() + "&" +
"PAYMENT=" + payMoney + "&" +
"CURCODE=" + jhPayProperties.getCURCODE() + "&" +
"TXCODE=530590&" +
"REMARK1=&REMARK2=&" +
"TYPE=" + jhPayProperties.getTYPE() + "&" +
"PUB=" + jhPayProperties.getPUB() + "&" +
"GATEWAY=" + jhPayProperties.getGATEWAY() + "&" +
"CLIENTIP=®INFO=&PROINFO=&REFERER=&" +
"TRADE_TYPE=" + jhPayProperties.getTRADE_TYPE() + "&" +
"SUB_APPID=" + appId + "&" +
"SUB_OPENID=" + openid;
String sb2 = "MERCHANTID=" + jhPayProperties.getMERCHANTID() + "&" +
"POSID=" + jhPayProperties.getPOSID() + "&" +
"BRANCHID=" + jhPayProperties.getBRANCHID() + "&" +
"ORDERID=" + orderNo.trim() + "&" +
"PAYMENT=" + payMoney + "&" +
"CURCODE=" + jhPayProperties.getCURCODE() + "&" +
"TXCODE=530590&" +
"REMARK1=&REMARK2=&" +
"TYPE=" + jhPayProperties.getTYPE() + "&" +
"GATEWAY=" + jhPayProperties.getGATEWAY() + "&" +
"CLIENTIP=®INFO=&PROINFO=&REFERER=&" +
"TRADE_TYPE=" + jhPayProperties.getTRADE_TYPE() + "&" +
"SUB_APPID=" + appId + "&" +
"SUB_OPENID=" + openid;
return sb2 + "&MAC=" + MD5Util.MD5Lower(sb1);
}
/**
* 建行退款请求参数处理
* @param orderNo
* @param refundMoney
* @return
*/
private String getRefundRequestXml(String orderNo, BigDecimal refundMoney){
//请求序列号
String requestSN = KeyUtil.uniqueKey();
StringBuffer sb = new StringBuffer();
sb.append("requestXml=")
.append("")
.append("" )
.append("" ).append(requestSN).append("")
.append("" ).append(jhPayProperties.getCUST_ID()).append("")
.append("" ).append(jhPayProperties.getUSER_ID()).append("")
.append("" ).append(jhPayProperties.getPASSWORD()).append("")
.append("" ).append("5W1004").append("")
.append("" ).append(jhPayProperties.getLANGUAGE()).append("")
.append("" )
.append("" ).append(refundMoney).append("")
.append("" ).append(orderNo).append("")
.append(" ")
.append("")
.append(" ")
.append(" ")
.append("");
return sb.toString();
}
}
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Objects;
public class MD5Util {
static final char[] hexDigits = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
static final char[] hexDigitsLower = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
public static String makeMD5(String password) {
String result = null;
MessageDigest messageDigest;
try {
messageDigest = MessageDigest.getInstance("MD5");
messageDigest.update(password.getBytes());
result = new BigInteger(1, messageDigest.digest()).toString(16);
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* 对字符串 MD5 无盐值加密
*
* @param plainText 传入要加密的字符串
* @return MD5加密后生成32位(小写字母 + 数字)字符串
*/
public static String MD5Lower(String plainText) {
try {
// 获得MD5摘要算法的 MessageDigest 对象
MessageDigest md = MessageDigest.getInstance("MD5");
// 使用指定的字节更新摘要
md.update(plainText.getBytes());
// digest()最后确定返回md5 hash值,返回值为8位字符串。因为md5 hash值是16位的hex值,实际上就是8位的字符
// BigInteger函数则将8位的字符串转换成16位hex值,用字符串来表示;得到字符串形式的hash值。1 固定值
return new BigInteger(1, md.digest()).toString(16);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
}
/**
* 对字符串 MD5 加密
*
* @param plainText 传入要加密的字符串
* @return MD5加密后生成32位(大写字母 + 数字)字符串
*/
public static String MD5Upper(String plainText) {
try {
// 获得MD5摘要算法的 MessageDigest 对象
MessageDigest md = MessageDigest.getInstance("MD5");
// 使用指定的字节更新摘要
md.update(plainText.getBytes());
// 获得密文
byte[] mdResult = md.digest();
// 把密文转换成十六进制的字符串形式
int j = mdResult.length;
char str[] = new char[j * 2];
int k = 0;
for (int i = 0; i < j; i++) {
byte byte0 = mdResult[i];
str[k++] = hexDigits[byte0 >>> 4 & 0xf];// 取字节中高 4 位的数字转换, >>> 为逻辑右移,将符号位一起右移
str[k++] = hexDigits[byte0 & 0xf]; // 取字节中低 4 位的数字转换
}
return new String(str);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 对字符串 MD5 加盐值加密
*
* @param plainText 传入要加密的字符串
* @param saltValue 传入要加的盐值
* @return MD5加密后生成32位(小写字母 + 数字)字符串
*/
public static String MD5Lower(String plainText, String saltValue) {
try {
// 获得MD5摘要算法的 MessageDigest 对象
MessageDigest md = MessageDigest.getInstance("MD5");
// 使用指定的字节更新摘要
md.update(plainText.getBytes());
md.update(saltValue.getBytes());
// digest()最后确定返回md5 hash值,返回值为8位字符串。因为md5 hash值是16位的hex值,实际上就是8位的字符
// BigInteger函数则将8位的字符串转换成16位hex值,用字符串来表示;得到字符串形式的hash值。1 固定值
return new BigInteger(1, md.digest()).toString(16);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
}
/**
* 对字符串 MD5 加盐值加密
*
* @param plainText 传入要加密的字符串
* @param saltValue 传入要加的盐值
* @return MD5加密后生成32位(大写字母 + 数字)字符串
*/
public static String MD5Upper(String plainText, String saltValue) {
try {
// 获得MD5摘要算法的 MessageDigest 对象
MessageDigest md = MessageDigest.getInstance("MD5");
// 使用指定的字节更新摘要
md.update(plainText.getBytes());
md.update(saltValue.getBytes());
// 获得密文
byte[] mdResult = md.digest();
// 把密文转换成十六进制的字符串形式
int j = mdResult.length;
char str[] = new char[j * 2];
int k = 0;
for (int i = 0; i < j; i++) {
byte byte0 = mdResult[i];
str[k++] = hexDigits[byte0 >>> 4 & 0xf];
str[k++] = hexDigits[byte0 & 0xf];
}
return new String(str);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* MD5加密后生成32位(小写字母+数字)字符串
* 同 MD5Lower() 一样
*/
public static String MD5(String plainText) {
try {
MessageDigest mdTemp = MessageDigest.getInstance("MD5");
mdTemp.update(plainText.getBytes("UTF-8"));
byte[] md = mdTemp.digest();
int j = md.length;
char str[] = new char[j * 2];
int k = 0;
for (int i = 0; i < j; i++) {
byte byte0 = md[i];
str[k++] = hexDigitsLower[byte0 >>> 4 & 0xf];
str[k++] = hexDigitsLower[byte0 & 0xf];
}
return new String(str);
} catch (Exception e) {
return null;
}
}
/**
* 校验MD5码
*
* @param text 要校验的字符串
* @param md5 md5值
* @return 校验结果
*/
public static boolean valid(String text, String md5) {
return md5.equals(MD5(text)) || md5.equals(Objects.requireNonNull(MD5(text)).toUpperCase());
}
}
import com.google.gson.annotations.SerializedName;
import lombok.Data;
/**
* @author IT00ZYQ
* @Date 2020/11/13 10:36
* PARURL封装类
**/
@Data
public class JHPayDTO {
@SerializedName("SUCCESS")
private String success;
@SerializedName("PAYURL")
private String payUrl;
}
import com.google.gson.annotations.SerializedName;
import lombok.Data;
import java.io.Serializable;
/**
* 建行返回参数封装对象
*/
@Data
public class JHPayResult implements Serializable {
/**
* 此字段是通信标识,表示通信成功
*/
@SerializedName("SUCCESS")
private boolean success;
/**
* 000000表示交易成功,非000000表示交易失败,错误信息可以查看ERRMSG字段
*/
@SerializedName("ERRCODE")
private String errcode;
/***
* 错误信息描述
*/
@SerializedName("ERRMSG")
private String errmsg;
/**
* 交易码
*/
@SerializedName("TXCODE")
private String txcode;
/**
*微信分配的APPID
*/
private String appId;
/**
* 时间戳
*/
private String timeStamp;
/**
* 随机串
*/
private String nonceStr;
/**
* 数据包
*/
@SerializedName("package")
private String Mypackage;
/**
* 签名方式
*/
private String signType;
/**
* 签名数据
*/
private String paySign;
/**
* 子商户的商户号
*/
private String partnerid;
/**
* 预支付交易会话ID 建行没有
*/
private String prepayid ;
/**
* 微信H5支付中间页面URL
*/
private String mweb_url;
/**
* 订单号
*/
private String orderNo;
}
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
/**
* @author IT00ZYQ
* @Date 2020/11/13 12:48
* 建行退款返回封装类
**/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class JHRefundResponse {
/**
* 是否成功退款,成功true,失败false
*/
private Boolean success;
/**
* 订单号
*/
private String orderNo;
/**
* 订单支付金额,单位元
*/
private BigDecimal payMoney;
/**
* 退款金额,单位元
*/
private BigDecimal refundMoney;
}
import java.util.Random;
/**
* @author IT00ZYQ
* @Date 2020/5/25 18:02
* 获取唯一序列工具类
**/
public class KeyUtil {
/*唯一键,当前毫秒数+三位随机数*/
public static synchronized String uniqueKey(){
Random random = new Random();
Integer number = random.nextInt(900)+100;
return System.currentTimeMillis()+String.valueOf(number);
}
/*唯一键,当前毫秒数后9位+三位随机数*/
public static synchronized String uniqueKey2(){
Random random = new Random();
Integer number = random.nextInt(900)+100;
return String.valueOf(System.currentTimeMillis()).substring(6)+String.valueOf(number);
}
}