Wechat mini program uses CCB payment

Wechat mini program uses CCB payment

1. Description of transaction process

Uploading... Reupload Cancel

2. Interface description

field illustrate type must pass
MERCHANTID Merchant code, uniformly assigned by CCB CHAR(15) yes
POSID Merchant counter code, uniformly assigned by CCB CHAR(9) yes
BRANCHID Branch code, uniformly assigned by CCB CHAR(9) yes
ORDERS Order number, provided by the merchant, up to 30 digits (digits) CHAR(30) yes
PAYMENT The payment amount, provided by the merchant, is given according to the actual amount. After the payment is completed, the merchant should compare the payment amount with the payment amount in the notification received from the merchant to confirm that the two amounts are consistent; NUMBER(16,2) yes
CURCODE Currency, the default is 01-RMB (currently only supports RMB payment) CHAR(2) yes
REMARK1 Remark 1 is generally used as a merchant's custom remark information, which can be displayed in the statement. When the main merchant of the group initiates a sub-merchant transaction, the information of the main merchant of the group needs to be filled in this field. The format is as follows: JTSH: main merchant number + main counter For example: JTSH: 105000000000000123456789 where 105000000000000 is the main merchant number and 123456789 is the main counter CHAR(30) no
REMARK2 Remark 2 is generally used as a merchant's custom remark information, which can be displayed in the statement. CHAR(30) no
TXCODE Transaction code, uniformly assigned by CCB, is 530590 CHAR(6) yes
MAC MAC check field, using standard MD5 algorithm, implemented by merchants CHAR(32) yes
TYPE Interface type, branch business personnel set the anti-phishing switch in the background of the P2 employee channel. 1-Anti-phishing interface CHAR(1) yes
PUB The last 30 digits of the public key are downloaded by the merchant from the CCB merchant service platform, and the last 30 digits are intercepted. Only participate in the MD5 digest as a source string, not passed as a parameter CHAR(30) yes
GATEWAY Gateway type, default is 0 VARCHAR(100) yes
CLIENTIP Client IP, the IP of the customer in the merchant system, that is, the ip used by the customer to log in (visit) the merchant system) CHAR(40) no
REGINFO Customer registration information, customer registration information in the merchant system, Chinese needs to use escape encoding CHAR(256) no
PROINFO Product information, the product purchased by the customer needs to use escape code in Chinese CHAR(256) no
REFER Merchant URL, the merchant can send an empty value; please refer to the REFERER setting instructions for details CHAR(100) no
TIMEOUT Order timeout time, format: YYYYMMDDHHMMSS For example: 20120214143005 bank system time > TIMEOUT, the transaction will be rejected, and no timeout will be judged if an empty value is sent. When this field has a value, it participates in MAC verification, otherwise it does not participate in MAC verification. CHAR(14) no
TRADE_TYPE Transaction type, JSAPI-official account payment, MINIPRO-small program CHAR(16) yes
SUB_APPID The APPID of the Mini Program/Official Account, the APPID of the Mini Program/Official Account currently calling the payment CHAR(32) yes
SUB_OPENID The user sub-identification is the unique identification of the user under the appid of the Mini Program/Official Account. The Mini Program is obtained through wx.login. The interface document address: https://developers.weixin.qq.com/miniprogram/dev/api/api-login. html?t=20161122 CHAR(128) yes
WX_CHANNELID Channel business name, for the channel business name customized by the merchant, when this field has a value, it will participate in MAC verification, otherwise it will not participate in MAC verification. CHAR(20) no
RETURN_FIELD Return information bitmap, a total of 20 bits, the merchant notifies whether to return a bitmap of a field, 0 or empty - no return, 1 - return. Digit 1: Whether to return OPENID and SUB_OPENID Digit 2: Reserved, default to 0 Digit 3: Reserved, default to 0 Fourth digit: Whether to return payment details Field example: 10000000000000000000 CHAR(20) no
USERPARAM Real-name payment, real-name payment function, including three subfields of type, ID number, and name (if this field appears, then all three subfields contained in this field must appear. For details, see the description of the USERPARAM field in 5) below). When this field has a value, it participates in MAC verification, otherwise it does not participate in MAC verification. Not online yet, please ignore VARCHAR(2000) no

Secondary Merchant Information

If the second-level merchant information is sent, the eight second-level merchant information fields must all send values. When the field has a value, it participates in MAC verification, otherwise it does not participate in MAC verification.

field illustrate type must pass
SMEARS Secondary merchant code, uniformly assigned by CCB CHAR(15) no
SMERNAME The name of the second-level merchant, Chinese needs to use escape code Chinese (33), English CHAR (100) no
SMERTYPEID Secondary Merchant Category Code CHAR(15) no
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. Merchant feedback address query (example, other interfaces are similar)

1) Request address: http://120.77.236.53:9999

2) Request parameters

  
<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) Response information

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

Uploading... Reupload Cancel

4) Springboot uses the RestTemplate request instance

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

6. Demo project code

1. Service layer interface

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. Service layer implementation

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 tools

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. Parameter encapsulation class

1) Access the online banking interface and return the encapsulation class

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) CCB returns the encapsulated object

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) CCB refund return package class

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. Obtain a unique random sequence tool class

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

you may be interested

Guess you like

Origin blog.csdn.net/zxl2016/article/details/126645043