微信小程序使用建行支付

微信小程序使用建行支付

一、交易流程说明

正在上传…重新上传取消

二、接口说明

字段 说明 类型 必传
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

三、微信相关调起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(字符金额),当然还有其他的信息。一般会在反馈的处理方法里面进行金额校验与修改订状态的操作

方法1:商户后台设置

正在上传…重新上传取消

方法2:访问开发接口

查阅接口文档
建行开放接口文档

项目中遇到的有关反馈的问题

反馈地址这一块相对来说是比较难调试,也会遇到很多问题。我就说一下我遇到的问题以及我是如何解决的。
在测试版本(测试版本使用的反馈地址的协议是http)测试时,可正常接收到建行发送过来的反馈信息。但是切换到正式版本(正式版本使用的反馈地址的协议是https)后,发现支付完成后怎么都接收不到建行的反馈信息,建行那边查询原因后说是向反馈地址发送请求后返回的是403(服务器禁止访问),尝试了很多种方法,最后是通过开启springboot项目的双协议配置,即项目可同时通过http协议与https协议进行访问(不同协议端口不一致),回调地址使用的是http协议的。这是退而求其次的方法,这个问题并没有完全解决,今后如果找到解决方法,会更新博客。哪位大佬彻底解决了这个问题,也欢迎分享。
springboot开启双协议的配置

五、访问建行开发接口

建行开放接口文档

1. 商户反馈地址查询(示例,其他的接口都类似)

1)请求地址:http://120.77.236.53:9999

2)请求参数

  
<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> 

3)响应信息

   
  <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> 

正在上传…重新上传取消

4)Springboot使用RestTemplate请求实例

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());
    }

六、Demo项目代码

1.服务层接口

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);
}

2.服务层实现

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();
    }
}

3.MD5工具类

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());
    }

}

4.参数封装类

1)访问网银接口返回封装类

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;

}

2)建行返回封装对象

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;

}

3)建行退款返回封装类

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;

}

5.获取唯一随机序列工具类

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);
    }
}

你可能感兴趣的

猜你喜欢

转载自blog.csdn.net/zxl2016/article/details/126645043