ylb-支付服务pay

news2024/11/25 2:41:57

总览:
在这里插入图片描述
在这里插入图片描述
在pay模块util包下,创建签名工具类Pkipair和http工具类HttpUtil:

package com.bjpowernode.util;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;


public class Pkipair {


    /*生成签名*/
    public String signMsg( String signMsg) {

        String base64 = "";
        try {
            KeyStore ks = KeyStore.getInstance("PKCS12");
            //商户私钥文件
            String file = Pkipair.class.getResource("10012140356.pfx").getPath().replaceAll("%20", " ");
            System.out.println(file);

            FileInputStream ksfis = new FileInputStream(file);
            BufferedInputStream ksbufin = new BufferedInputStream(ksfis);

            char[] keyPwd = "123456".toCharArray();
            ks.load(ksbufin, keyPwd);

            PrivateKey priK = (PrivateKey) ks.getKey("test-alias", keyPwd);
            Signature signature = Signature.getInstance("SHA256withRSA");
            signature.initSign(priK);
            signature.update(signMsg.getBytes("utf-8"));
            sun.misc.BASE64Encoder encoder = new sun.misc.BASE64Encoder();
            base64 = encoder.encode(signature.sign());

        } catch(FileNotFoundException e){
            e.printStackTrace();
        }catch (Exception ex) {
            ex.printStackTrace();
        }
        return base64;
    }

    /*验签的方法*/
    public boolean enCodeByCer( String val, String msg) {
        boolean flag = false;
        try {

            //快钱的公钥文件
            String file = Pkipair.class.getResource("99bill[1].cert.rsa.20140803.cer").toURI().getPath();
            System.out.println(file);                       //  99bill.cert.rsa.20140803.cer
            FileInputStream inStream = new FileInputStream(file);

            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            X509Certificate cert = (X509Certificate) cf.generateCertificate(inStream);

            PublicKey pk = cert.getPublicKey();

            Signature signature = Signature.getInstance("SHA256withRSA");
            signature.initVerify(pk);
            signature.update(val.getBytes());

            sun.misc.BASE64Decoder decoder = new sun.misc.BASE64Decoder();
            System.out.println(new String(decoder.decodeBuffer(msg)));
            flag = signature.verify(decoder.decodeBuffer(msg));
            System.out.println(flag);
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("no");
        }
        return flag;
    }

}
package com.bjpowernode.util;

import java.net.SocketTimeoutException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpStatus;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;

/**
 * HttpUtil 工具类
 */
public class HttpUtil {

    private static final Log LOGGER = LogFactory.getLog(HttpUtil.class);

    private static PoolingHttpClientConnectionManager connManager;
    private static RequestConfig requestConfig;

    static{
        try {
            SSLContext sslcontext = createIgnoreVerifySSL();
            SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
            sslContext.init(null, null, null);
            SSLContext.setDefault(sslContext);
            Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder
                    .<ConnectionSocketFactory> create().register("http", PlainConnectionSocketFactory.INSTANCE)
                    .register("https", new SSLConnectionSocketFactory(sslcontext)).build();
            connManager = new PoolingHttpClientConnectionManager(
                    socketFactoryRegistry);
            // 连接池超时时间使用connect超时时间
            requestConfig = RequestConfig.custom()
                    .setConnectionRequestTimeout(1000)
                    .setConnectTimeout(1000)
                    .setSocketTimeout(5000).build();
        } catch (Exception e) {
            LOGGER.error(" [XPAY-SDK] init connectionManager or requestConfig error !!! ",e);
            e.printStackTrace();
        }
    }

    public static String doPostJsonRequest(String reqeustString, String url,
                                           int connectTimeout, int socketTimeOut) throws Exception {

        CloseableHttpResponse response = null;
        try {

            changeRequestConfig(connectTimeout,socketTimeOut);
            CloseableHttpClient httpclient = HttpClients.createDefault();
            HttpPost httpPost = new HttpPost(url);
            httpPost.addHeader("Content-Type", "application/json;charset=UTF-8");
            httpPost.setConfig(requestConfig);
            httpPost.setEntity(new StringEntity(reqeustString, ContentType.APPLICATION_JSON));
            response = httpclient.execute(httpPost);
            // get http status code
            int resStatu = response.getStatusLine().getStatusCode();
            String responseString = null;
            if (resStatu == HttpStatus.SC_OK) {
                responseString = EntityUtils.toString(response.getEntity());
            } else {
                throw new Exception(url + ",the statusCode is " + resStatu);
            }
            return responseString;
        } catch (ConnectTimeoutException e) {
            e.printStackTrace();
        } catch (SocketTimeoutException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (response != null) {
                try {
                    EntityUtils.consume(response.getEntity());
                    response.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return url;
    }

    /**
     * 绕过验证
     *
     * @return
     * @throws NoSuchAlgorithmException
     * @throws KeyManagementException
     */
    private static SSLContext createIgnoreVerifySSL() throws NoSuchAlgorithmException, KeyManagementException {
        SSLContext sc = SSLContext.getInstance("TLSv1.2");
        // 实现一个X509TrustManager接口,用于绕过验证,不用修改里面的方法
        X509TrustManager trustManager = new X509TrustManager() {

            public void checkClientTrusted(java.security.cert.X509Certificate[] paramArrayOfX509Certificate,
                                           String paramString) throws CertificateException {}


            public void checkServerTrusted(java.security.cert.X509Certificate[] paramArrayOfX509Certificate,
                                           String paramString) throws CertificateException {}


            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                return null;
            }
        };
        sc.init(null, new TrustManager[] { trustManager }, null);
        return sc;
    }

    public static String doPostJsonRequestByHttps(String reqeustString, String url,
                                                  int connectTimeout, int socketTimeOut) {
        long startTime = System.currentTimeMillis();
        CloseableHttpResponse response = null;
        String responseString = null;
        try {

            changeRequestConfig(connectTimeout,socketTimeOut);
            CloseableHttpClient httpsClient = HttpClients.custom().setConnectionManager(connManager).build();
            HttpPost httpPost = new HttpPost(url);
            httpPost.addHeader("Content-Type", "application/json;charset=UTF-8");
            httpPost.setConfig(requestConfig);
            httpPost.setEntity(new StringEntity(reqeustString, ContentType.APPLICATION_JSON));
            response = httpsClient.execute(httpPost);
            // get http status code
            int resStatu = response.getStatusLine().getStatusCode();
            responseString = null;
            if (resStatu == HttpStatus.SC_OK) {
                responseString = EntityUtils.toString(response.getEntity());
            } else {
                throw new Exception(url + ",the statusCode is " + resStatu);
            }
            LOGGER.info(String.format("response data : [ %s ] , time consuming : [ %s ] ms !! ",responseString
                    ,(System.currentTimeMillis()- startTime)));
            return responseString;
        }catch (ConnectTimeoutException e) {
            e.printStackTrace();
        } catch (SocketTimeoutException e) {
            e.printStackTrace();
        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            if (response != null) {
                try {
                    EntityUtils.consume(response.getEntity());
                    response.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return responseString;
    }

    /**
     * 修改默认超时时间
     * @param connectionTime
     * @param soTimeout
     */
    private static void changeRequestConfig(int connectionTime,int soTimeout){
        if(connectionTime != requestConfig.getConnectionRequestTimeout()
                || soTimeout != requestConfig.getSocketTimeout()){
            requestConfig = RequestConfig.custom()
                    .setConnectionRequestTimeout(connectionTime)
                    .setConnectTimeout(connectionTime)
                    .setSocketTimeout(soTimeout).build();
        }
    }
}

在pay模块service包下,创建KuaiQianService:
1、获取用户信息(queryUser)
2、生成快钱支付接口的数据 Map是发送给快钱的所有请求参数(generateFormData)
3、创建充值记录(addRecharge)
4、生成订单号orderId(generateOrderId)
5、把订单号,存放到redis(addOrderIdToRedis)
6、处理异步通知(kqNotify)
7、调用快钱的查询接口(handleQueryOrder)

package com.bjpowernode.pay.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.bjpowernode.api.model.RechargeRecord;
import com.bjpowernode.api.model.User;
import com.bjpowernode.api.service.RechargeService;
import com.bjpowernode.api.service.UserService;
import com.bjpowernode.common.constants.RedisKey;
import com.bjpowernode.common.constants.YLBConstant;
import com.bjpowernode.util.HttpUtil;
import com.bjpowernode.util.Pkipair;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

@Service
public class KuiQianService {

    @DubboReference(interfaceClass = UserService.class,version = "1.0")
    private UserService userService;

    @DubboReference(interfaceClass = RechargeService.class,version = "1.0")
    private RechargeService rechargeService;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    /*获取用户信息*/
    public User queryUser(Integer uid){
        User user = userService.queryById(uid);
        return user;
    }

    /*生成快钱支付接口的数据  Map是发送给快钱的所有请求参数*/
    public Map<String,String> generateFormData(Integer uid, String phone, BigDecimal rechargeMoney) {
        Map<String,String> data = new HashMap<>();

        //人民币网关账号,该账号为11位人民币网关商户编号+01,该参数必填。
        String merchantAcctId = "1001214035601";//
        //编码方式,1代表 UTF-8; 2 代表 GBK; 3代表 GB2312 默认为1,该参数必填。
        String inputCharset = "1";
        //接收支付结果的页面地址,该参数一般置为空即可。
        String pageUrl = "";
        //服务器接收支付结果的后台地址,该参数务必填写,不能为空。
        String bgUrl = "http://451627p2o2.qicp.vip/pay/kq/rece/notify";
        //网关版本,固定值:v2.0,该参数必填。
        String version =  "v2.0";
        //语言种类,1代表中文显示,2代表英文显示。默认为1,该参数必填。
        String language =  "1";
        //签名类型,该值为4,代表PKI加密方式,该参数必填。
        String signType =  "4";
        //支付人姓名,可以为空。
        String payerName= "";
        //支付人联系类型,1 代表电子邮件方式;2 代表手机联系方式。可以为空。
        String payerContactType =  "2";
        //支付人联系方式,与payerContactType设置对应,payerContactType为1,则填写邮箱地址;payerContactType为2,则填写手机号码。可以为空。
        String payerContact =  phone;
        //指定付款人,可以为空
        String payerIdType =  "3";
        //付款人标识,可以为空
        String payerId =  String.valueOf(uid);
        //付款人IP,可以为空
        String payerIP =  "";
        //商户订单号,以下采用时间来定义订单号,商户可以根据自己订单号的定义规则来定义该值,不能为空。
        String orderId = "KQ"+generateOrderId();

        //订单金额,金额以“分”为单位,商户测试以1分测试即可,切勿以大金额测试。该参数必填。
        //0.01 * 100 == 1.00  100000*100 = 10000000 , 科学计数法  1 * 10^7
        String orderAmount = rechargeMoney.multiply(new BigDecimal("100")).stripTrailingZeros().toPlainString();

        //订单提交时间,格式:yyyyMMddHHmmss,如:20071117020101,不能为空。
        String orderTime = new java.text.SimpleDateFormat("yyyyMMddHHmmss").format(new java.util.Date());
        //快钱时间戳,格式:yyyyMMddHHmmss,如:20071117020101, 可以为空
        String orderTimestamp = orderTime;
        //商品名称,可以为空。
        String productName= "动力理财产品";
        //商品数量,可以为空。
        String productNum = "1";
        //商品代码,可以为空。
        String productId = "10000";
        //商品描述,可以为空。
        String productDesc = "购买产品";
        //扩展字段1,商户可以传递自己需要的参数,支付完快钱会原值返回,可以为空。
        String ext1 = "";
        //扩展自段2,商户可以传递自己需要的参数,支付完快钱会原值返回,可以为空。
        String ext2 = "";
        //支付方式,一般为00,代表所有的支付方式。如果是银行直连商户,该值为10-1或10-2,必填。
        String payType = "00";
        //银行代码,如果payType为00,该值可以为空;如果payType为10-1或10-2,该值必须填写,具体请参考银行列表。
        String bankId = "";
        //同一订单禁止重复提交标志,实物购物车填1,虚拟产品用0。1代表只能提交一次,0代表在支付不成功情况下可以再提交。可为空。
        String redoFlag = "0";
        //快钱合作伙伴的帐户号,即商户编号,可为空。
        String pid = "";

        // signMsg 签名字符串 不可空,生成加密签名串
        String signMsgVal = "";
        signMsgVal = appendParam(signMsgVal, "inputCharset", inputCharset,data);
        signMsgVal = appendParam(signMsgVal, "pageUrl", pageUrl,data);
        signMsgVal = appendParam(signMsgVal, "bgUrl", bgUrl,data);
        signMsgVal = appendParam(signMsgVal, "version", version,data);
        signMsgVal = appendParam(signMsgVal, "language", language,data);
        signMsgVal = appendParam(signMsgVal, "signType", signType,data);
        signMsgVal = appendParam(signMsgVal, "merchantAcctId",merchantAcctId,data);
        signMsgVal = appendParam(signMsgVal, "payerName", payerName,data);
        signMsgVal = appendParam(signMsgVal, "payerContactType",payerContactType,data);
        signMsgVal = appendParam(signMsgVal, "payerContact", payerContact,data);
        signMsgVal = appendParam(signMsgVal, "payerIdType", payerIdType,data);
        signMsgVal = appendParam(signMsgVal, "payerId", payerId,data);
        signMsgVal = appendParam(signMsgVal, "payerIP", payerIP,data);
        signMsgVal = appendParam(signMsgVal, "orderId", orderId,data);
        signMsgVal = appendParam(signMsgVal, "orderAmount", orderAmount,data);
        signMsgVal = appendParam(signMsgVal, "orderTime", orderTime,data);
        signMsgVal = appendParam(signMsgVal, "orderTimestamp", orderTimestamp,data);
        signMsgVal = appendParam(signMsgVal, "productName", productName,data);
        signMsgVal = appendParam(signMsgVal, "productNum", productNum,data);
        signMsgVal = appendParam(signMsgVal, "productId", productId,data);
        signMsgVal = appendParam(signMsgVal, "productDesc", productDesc,data);
        signMsgVal = appendParam(signMsgVal, "ext1", ext1,data);
        signMsgVal = appendParam(signMsgVal, "ext2", ext2,data);
        signMsgVal = appendParam(signMsgVal, "payType", payType,data);
        signMsgVal = appendParam(signMsgVal, "bankId", bankId,data);
        signMsgVal = appendParam(signMsgVal, "redoFlag", redoFlag,data);
        signMsgVal = appendParam(signMsgVal, "pid", pid,data);

        System.out.println(signMsgVal);
        Pkipair pki = new Pkipair();
        //生成签名串
        String signMsg = pki.signMsg(signMsgVal);
        //需要signMsg
        data.put("signMsg",signMsg);

        return data;
    }
    
    /*创建充值记录*/
    public boolean addRecharge(Integer uid, BigDecimal rechargeMoney, String orderId) {

        RechargeRecord record = new RechargeRecord();
        record.setChannel("KQ");
        record.setRechargeDesc("快钱充值");
        record.setRechargeMoney(rechargeMoney);
        record.setRechargeNo(orderId);
        record.setRechargeStatus(YLBConstant.RECHARGE_STATUS_PROCESSING);
        record.setRechargeTime(new Date());
        record.setUid(uid);
        int rows = rechargeService.addRechargeRecord(record);

        return rows > 0;

    }

    /**************辅助工具方法***********************************/
    private String appendParam(String returns, String paramId, String paramValue,Map<String,String> data) {
        if (returns != "") {
            if (paramValue != "" && paramValue != null) {

                returns += "&" + paramId + "=" + paramValue;
            }

        } else {

            if (paramValue != "" && paramValue != null) {
                returns = paramId + "=" + paramValue;
            }
        }
        if( data != null ){
            data.put(paramId,paramValue);
        }

        return returns;
    }

    //生成orderId
    private String generateOrderId(){
        //唯一值。1. 使用数据库的自增主键
        //       2. 使用redis的自增
        // orderId = 时间戳 +  redis的自增
        String key = RedisKey.KEY_RECHARGE_ORDERID;
        Long incr = stringRedisTemplate.boundValueOps(key).increment();

        String orderId = DateFormatUtils.format(new Date(),"yyyyMMddHHmmssSSS") + incr;
        return orderId;
    }

    /*把订单号,存放到redis*/
    public void addOrderIdToRedis(String orderId) {
        String key  = RedisKey.KEY_ORDERID_SET;
        stringRedisTemplate.boundZSetOps(key).add(orderId, new Date().getTime());
    }

    /*处理异步通知*/
    public void kqNotify(HttpServletRequest request) {

        String merchantAcctId = request.getParameter("merchantAcctId");
        String version = request.getParameter("version");
        String language = request.getParameter("language");
        String signType = request.getParameter("signType");
        String payType = request.getParameter("payType");
        String bankId = request.getParameter("bankId");
        String orderId = request.getParameter("orderId");
        String orderTime = request.getParameter("orderTime");
        String orderAmount = request.getParameter("orderAmount");
        String bindCard = request.getParameter("bindCard");
        if(request.getParameter("bindCard")!=null){
            bindCard = request.getParameter("bindCard");}
        String bindMobile="";
        if(request.getParameter("bindMobile")!=null){
            bindMobile = request.getParameter("bindMobile");}

        String dealId = request.getParameter("dealId");
        String bankDealId = request.getParameter("bankDealId");
        String dealTime = request.getParameter("dealTime");
        String payAmount = request.getParameter("payAmount");
        String fee = request.getParameter("fee");
        String ext1 = request.getParameter("ext1");
        String ext2 = request.getParameter("ext2");
        String payResult = request.getParameter("payResult");
        String aggregatePay = request.getParameter("aggregatePay");
        String errCode = request.getParameter("errCode");
        String signMsg = request.getParameter("signMsg");

        //拼接签名计算的串
        String merchantSignMsgVal = "";
        merchantSignMsgVal = appendParam(merchantSignMsgVal,"merchantAcctId", merchantAcctId,null);
        merchantSignMsgVal = appendParam(merchantSignMsgVal, "version",version,null);
        merchantSignMsgVal = appendParam(merchantSignMsgVal, "language",language,null);
        merchantSignMsgVal = appendParam(merchantSignMsgVal, "signType",signType,null);
        merchantSignMsgVal = appendParam(merchantSignMsgVal, "payType",payType,null);
        merchantSignMsgVal = appendParam(merchantSignMsgVal, "bankId",bankId,null);
        merchantSignMsgVal = appendParam(merchantSignMsgVal, "orderId",orderId,null);
        merchantSignMsgVal = appendParam(merchantSignMsgVal, "orderTime",orderTime,null);
        merchantSignMsgVal = appendParam(merchantSignMsgVal, "orderAmount",orderAmount,null);
        merchantSignMsgVal = appendParam(merchantSignMsgVal, "bindCard",bindCard,null);
        merchantSignMsgVal = appendParam(merchantSignMsgVal, "bindMobile",bindMobile,null);
        merchantSignMsgVal = appendParam(merchantSignMsgVal, "dealId",dealId,null);
        merchantSignMsgVal = appendParam(merchantSignMsgVal, "bankDealId",bankDealId,null);
        merchantSignMsgVal = appendParam(merchantSignMsgVal, "dealTime",dealTime,null);
        merchantSignMsgVal = appendParam(merchantSignMsgVal, "payAmount",payAmount,null);
        merchantSignMsgVal = appendParam(merchantSignMsgVal, "fee", fee,null);
        merchantSignMsgVal = appendParam(merchantSignMsgVal, "ext1", ext1,null);
        merchantSignMsgVal = appendParam(merchantSignMsgVal, "ext2", ext2,null);
        merchantSignMsgVal = appendParam(merchantSignMsgVal, "payResult",payResult,null);
        merchantSignMsgVal = appendParam(merchantSignMsgVal, "aggregatePay",aggregatePay,null);
        merchantSignMsgVal = appendParam(merchantSignMsgVal, "errCode",errCode,null);

        Pkipair pki = new Pkipair();
        boolean flag = pki.enCodeByCer(merchantSignMsgVal, signMsg);

        System.out.println("flag==="+flag);
        if( flag ){
            /**
             * 可以处理业务逻辑
             * 1.判断商户是商家自己的吗
             * 2.判断订单在商家是否存在,是否处理过
             * 3.判断金额是否一致
             * 4.如果成功,更新用户的资金
             * 5.修改充值表的记录状态
             * 6.删除redis中的处理过的订单号
             */
            if("1001214035601".equals(merchantAcctId)){
               int rechargeResult  =  rechargeService.handleKQNotify(orderId,payAmount,payResult);
               System.out.println("订单"+orderId+",充值处理结果:"+rechargeResult);
            } else {
                System.out.println("订单"+orderId+",充值处理结果:商家号不正确");
            }
        } else {
            System.out.println("订单"+orderId+"验签失败,不能处理");
        }
        //删除redis中的订单记录
        stringRedisTemplate.boundZSetOps(RedisKey.KEY_ORDERID_SET).remove(orderId);
    }

    /*调用快钱的查询接口*/
    public void handleQueryOrder() {

        //1.从redis获取订单号
        Set<String> orders = stringRedisTemplate
                    .boundZSetOps(RedisKey.KEY_ORDERID_SET).range(0, -1);

        //2.循环订单号
        orders.forEach( orderId -> {
            //3.每个订单,调用查询接口
            doQuery(orderId);
        });
    }

    private void doQuery(String orderId){
        Map<String, Object> request = new HashMap<String, Object>();
        //固定值:1代表UTF-8;
        String inputCharset = "1";
        //固定值:v2.0 必填
        String version = "v2.0";
        //1代表Md5,2 代表PKI加密方式  必填
        String signType = "2";
        //人民币账号 membcode+01  必填
        String merchantAcctId = "1001214035601";
        //固定值:0 按商户订单号单笔查询,1 按交易结束时间批量查询必填
        String queryType = "0";
        //固定值:1	代表简单查询 必填
        String queryMode = "1";
        //数字串,格式为:年[4 位]月[2 位]日[2 位]时[2 位]分[2 位]秒[2位],例如:20071117020101
        String startTime = "";
        数字串,格式为:年[4 位]月[2 位]日[2 位]时[2 位]分[2 位]秒[2位],例如:20071117020101
        String endTime = "";
        String requestPage = "";
        String key = "27YKWKBKHT2IZSQ4";

        request.put("inputCharset", inputCharset);
        request.put("version", version);
        request.put("signType", signType);
        request.put("merchantAcctId", merchantAcctId);
        request.put("queryType", queryType);
        request.put("queryMode", queryMode);
        request.put("startTime", startTime);
        request.put("endTime", endTime);
        request.put("requestPage", requestPage);
        request.put("orderId", orderId);

        String message="";
        message = appendParam(message,"inputCharset",inputCharset,null);
        message = appendParam(message,"version",version,null);
        message = appendParam(message,"signType",signType,null);
        message = appendParam(message,"merchantAcctId",merchantAcctId,null);
        message = appendParam(message,"queryType",queryType,null);
        message = appendParam(message,"queryMode",queryMode,null);
        message = appendParam(message,"startTime",startTime,null);
        message = appendParam(message,"endTime",endTime,null);
        message = appendParam(message,"requestPage",requestPage,null);
        message = appendParam(message,"orderId",orderId,null);
        message = appendParam(message,"key",key,null);

        Pkipair pki = new Pkipair();
        String sign = pki.signMsg(message);
        request.put("signMsg", sign);

        System.out.println("请求json串===" + JSON.toJSONString(request));

        //sandbox提交地址
        String reqUrl = "https://sandbox.99bill.com/gatewayapi/gatewayOrderQuery.do";
        String response = "";
        try {
            response = HttpUtil.doPostJsonRequestByHttps(JSON.toJSONString(request), reqUrl, 3000, 8000);
            //解析response
            if(StringUtils.isNotBlank(response)){
                Object detailObject = JSONObject.parseObject(response).get("orderDetail");
                System.out.println("detailObject===="+detailObject);
                if( detailObject != null){
                    //把查询的Object转为JSONArray
                    JSONArray array = (JSONArray)detailObject;
                    JSONObject detailJsonObject = array.getJSONObject(0);
                    if( detailJsonObject != null){
                        //处理充值结果,和异步通知一样
                        int result = rechargeService.handleKQNotify(
                                detailJsonObject.getString("orderId"),
                                detailJsonObject.getString("payAmount"),
                                detailJsonObject.getString("payResult")
                        );
                        System.out.println("处理的订单号是:"+orderId+",处理结果:"+result);
                    }
                }
            }
            //删除redis中的订单
            stringRedisTemplate.boundZSetOps(RedisKey.KEY_ORDERID_SET).remove(orderId);
        } catch (Exception e) {
            e.printStackTrace();
            return;
        }
    }
}

其中:
1、在api模块下的service包,充值接口RechargeService添加:(创建充值记录方法addRechargeRecord 和 处理后续充值方法handleKQNotify)

package com.bjpowernode.api.service;

import com.bjpowernode.api.model.RechargeRecord;

import java.util.List;

/**
 * 充值接口
 */
public interface RechargeService {

    /*根据userID查询它的充值记录*/
    List<RechargeRecord> queryByUid(Integer uid,Integer pageNo, Integer pageSize);

    /*创建充值记录*/
    int addRechargeRecord(RechargeRecord record);

    /*处理后续充值*/
    int handleKQNotify(String orderId, String payAmount, String payResult);
}

2、实现这个接口方法,在dataservice模块service包下,实现类RechargeServiceImpl添加:(创建充值记录addRechargeRecord 和 处理后续充值handleKQNotify):

package com.bjpowernode.dataservice.service;

import com.bjpowernode.api.model.RechargeRecord;
import com.bjpowernode.api.service.RechargeService;
import com.bjpowernode.common.constants.YLBConstant;
import com.bjpowernode.common.util.CommonUtil;
import com.bjpowernode.dataservice.mapper.FinanceAccountMapper;
import com.bjpowernode.dataservice.mapper.RechargeRecordMapper;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;

@DubboService(interfaceClass = RechargeService.class,version = "1.0")
public class RechargeServiceImpl implements RechargeService {

    @Resource
    private RechargeRecordMapper rechargeMapper;

    @Resource
    private FinanceAccountMapper accountMapper;

    /*根据userID查询它的充值记录*/
    @Override
    public List<RechargeRecord> queryByUid(Integer uid, Integer pageNo, Integer pageSize) {
        List<RechargeRecord> records  = new ArrayList<>();
        if( uid != null && uid > 0 ){
            pageNo  = CommonUtil.defaultPageNo(pageNo);
            pageSize = CommonUtil.defaultPageSize(pageSize);
            int offset = (pageNo -1 ) * pageSize;
            records = rechargeMapper.selectByUid(uid, offset, pageSize);
        }
        return records;
    }

    /*创建充值记录*/
    @Override
    public int addRechargeRecord(RechargeRecord record) {
        return rechargeMapper.insertSelective(record);
    }

    /*处理后续充值*/
    @Transactional(rollbackFor = Exception.class)
    @Override
    public synchronized int handleKQNotify(String orderId, String payAmount, String payResult) {
        int result = 0;//订单不存在
        int rows =  0;
        //1.查询订单
        RechargeRecord record = rechargeMapper.selectByRechargeNo(orderId);
        if(record != null ){
            if( record.getRechargeStatus() == YLBConstant.RECHARGE_STATUS_PROCESSING){
                //2.判断金额是否一致
                String fen = record.getRechargeMoney().multiply(new BigDecimal("100"))
                                                     .stripTrailingZeros().toPlainString();
                if( fen.equals(payAmount)){
                    //金额一致
                    if("10".equals(payResult)){
                        //成功
                        rows = accountMapper.updateAvailableMoneyByRecharge(record.getUid(),record.getRechargeMoney());
                        if(rows < 1 ){
                            throw new RuntimeException("充值更新资金账号失败");
                        }
                        //更新充值记录的状态
                        rows = rechargeMapper.updateStatus(record.getId(),YLBConstant.RECHARGE_STATUS_SUCCESS);
                        if( rows < 1) {
                            throw new RuntimeException("充值更新充值记录状态失败");
                        }
                        result  = 1;//成功
                    } else {
                        //充值失败
                        //更新充值记录的状态
                        rows = rechargeMapper.updateStatus(record.getId(),YLBConstant.RECHARGE_STATUS_FAIL);
                        if( rows < 1) {
                            throw new RuntimeException("充值更新充值记录状态失败");
                        }
                        result = 2;//充值结果是失败的
                    }
                } else {
                    result = 4;//金额不一样
                }
            } else {
                result = 3;//订单已经处理过了
            }
        }
        return result;
    }
}

其中:
1、根据订单号,查询记录selectByRechargeNo(需要在dataservice模块mapper包下的RechargeRecordMapper接口添加方法,并在resources/mappers/RechargeRecordMapper.xml编写SQL语句):

    RechargeRecord selectByRechargeNo(@Param("rechargeNo") String orderId);
  <!--根据订单号,查询记录-->
  <select id="selectByRechargeNo" resultMap="BaseResultMap">
    select <include refid="Base_Column_List" />
    from b_recharge_record
    where recharge_no = #{rechargeNo} for update
  </select>

2、充值更新金额updateAvailableMoneyByRecharge(需要在dataservice模块mapper包下的FinanceAccountMapper接口添加方法,并在resources/mappers/FinanceAccountMapper.xml编写SQL语句):

    /*充值更新金额*/
    int updateAvailableMoneyByRecharge(@Param("uid") Integer uid, @Param("rechargeMoney") BigDecimal rechargeMoney);
  <!--充值更新金额-->
  <update id="updateAvailableMoneyByRecharge">
     update  u_finance_account set  available_money = available_money + #{rechargeMoney}
     where uid = #{uid}
  </update>

3、更新充值记录状态updateStatus(需要在dataservice模块mapper包下的RechargeRecordMapper接口添加方法,并在resources/mappers/RechargeRecordMapper.xml编写SQL语句):

    /*更新状态*/
    int updateStatus(@Param("id") Integer id, @Param("newStatus") int rechargeStatusSuccess);
  <!--更新状态-->
  <update id="updateStatus">
    update b_recharge_record set  recharge_status = #{newStatus} where id = #{id}
  </update>

在pay模块controller包下,创建接受请求的KuaiQianController:
一、(接收来自vue项目的支付充值请求)
1、检查uid是否是有效的用户(用户存在,且充值金额充足),并查询用户(kQService.queryUser(uid))
2、创建快钱支付接口需要的请求参数(kQService.generateFormData(uid,user.getPhone(),rechargeMoney))
3、创建充值记录(kQService.addRecharge(uid,rechargeMoney,data.get(“orderId”)))
4、把订单号存放到redis(kQService.addOrderIdToRedis(data.get(“orderId”)))
5、提交支付请求给快钱的form页面(thymeleaf)(view = “kqForm”;)
二、(接收快钱给商家的支付结果 , 快钱以get方式,发送请求给商家)
三、从定时任务,调用的接口

package com.bjpowernode.pay.controller;

import com.bjpowernode.api.model.User;
import com.bjpowernode.pay.service.KuiQianService;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.Map;

@Controller
@RequestMapping("/kq")
public class KuaiQianController {

    @Resource
    private KuiQianService kQService;

    /*接收来自vue项目的支付充值请求*/
    @GetMapping("/rece/recharge")
    public String receFrontRechargeKQ(Integer uid,
                                      BigDecimal rechargeMoney,
                                      Model model){
        //默认是错误视图
        String view="err";
        if(uid != null && uid > 0 &&
                rechargeMoney != null && rechargeMoney.doubleValue() > 0 ){
            //1.检查uid是否是有效的用户
            try{
                User user = kQService.queryUser(uid);
                if(user != null ){
                    //创建快钱支付接口需要的请求参数
                    Map<String,String> data = kQService.generateFormData(uid,user.getPhone(),rechargeMoney);
                    model.addAllAttributes(data);
                    //创建充值记录
                    kQService.addRecharge(uid,rechargeMoney,data.get("orderId"));
                    //把订单号存放到redis
                    kQService.addOrderIdToRedis(data.get("orderId"));
                    //提交支付请求给快钱的form页面(thymeleaf)
                    view  = "kqForm";
                }
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        return view;

    }


    //接收快钱给商家的支付结果 , 快钱以get方式,发送请求给商家
    @GetMapping("/rece/notify")
    @ResponseBody
    public String payResultNotify(HttpServletRequest request){
        System.out.println("=================接收快钱的异步通知=============");
        kQService.kqNotify(request);
        return "<result>1</result><redirecturl>http://localhost:8080/</redirecturl>";
    }

    //从定时任务,调用的接口
    @GetMapping("/rece/query")
    @ResponseBody
    public String queryKQOrder(){
        kQService.handleQueryOrder();
        return "接收了查询的请求";

    }
}

其中:
1、在pay模块,resources/template下,创建两个页面:err.html和kqForm.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>请求错误</h1>
</body>
</html>
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

    <form name="kqPay" action="https://sandbox.99bill.com/gateway/recvMerchantInfoAction.htm" method="get">
        <input type="hidden" name="inputCharset" th:value="${inputCharset}" />
        <input type="hidden" name="pageUrl" th:value="${pageUrl}" />
        <input type="hidden" name="bgUrl" th:value="${bgUrl}" />
        <input type="hidden" name="version" th:value="${version}" />
        <input type="hidden" name="language" th:value="${language}" />
        <input type="hidden" name="signType" th:value="${signType}" />
        <input type="hidden" name="signMsg" th:value="${signMsg}" />
        <input type="hidden" name="merchantAcctId" th:value="${merchantAcctId}" />
        <input type="hidden" name="payerName" th:value="${payerName}" />
        <input type="hidden" name="payerContactType" th:value="${payerContactType}" />
        <input type="hidden" name="payerContact" th:value="${payerContact}" />
        <input type="hidden" name="payerIdType" th:value="${payerIdType}" />
        <input type="hidden" name="payerId" th:value="${payerId}" />
        <input type="hidden" name="payerIP" th:value="${payerIP}" />
        <input type="hidden" name="orderId" th:value="${orderId}" />
        <input type="hidden" name="orderAmount" th:value="${orderAmount}" />
        <input type="hidden" name="orderTime" th:value="${orderTime}" />
        <input type="hidden" name="orderTimestamp" th:value="${orderTimestamp}" />
        <input type="hidden" name="productName" th:value="${productName}" />
        <input type="hidden" name="productNum" th:value="${productNum}" />
        <input type="hidden" name="productId" th:value="${productId}" />
        <input type="hidden" name="productDesc" th:value="${productDesc}" />
        <input type="hidden" name="ext1" th:value="${ext1}" />
        <input type="hidden" name="ext2" th:value="${ext2}" />
        <input type="hidden" name="payType" th:value="${payType}" />
        <input type="hidden" name="bankId" th:value="${bankId}" />
        <input type="hidden" name="redoFlag" th:value="${redoFlag}" />
        <input type="hidden" name="pid" th:value="${pid}" />

       <!-- <input type="submit" name="submit" th:value="提交到快钱">-->
    </form>
    <script>
        document.forms[0].submit();
    </script>
</body>
</html>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/760708.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

centos7 docker 安装sql server 2019

contos7安装sql server docker最低1.8或更高 卸载旧的docker sudo yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine 装docker依赖包 #安装所需资源包 sudo yum install -…

mac 下 geoserver 安装

一、去官网下载geoserver https://geoserver.org/ 选择一个版本&#xff0c;然后点进去 二、需要配置java环境和设置geoserver 环境变量 1&#xff09;、java 环境安装 Java Downloads | Oracle 中国 2&#xff09;、环境变量设置 1.打开终端&#xff1a;command 空格键 2…

全卷积网络(FCN)

一&#xff1a;全卷积网络 义分割是对图像中的每个像素分类。 全卷积网络&#xff08;fully convolutional network&#xff0c;FCN&#xff09;采用卷积神经网络实现了从图像像素到像素类别的变换。与我们之前在图像分类或目标检测部分介绍的卷积神经网络不同&#xff0c;全卷…

融合自适应惯性权重和柯西变异的秃鹰搜索算法(CBES) - 附代码

融合自适应惯性权重和柯西变异的秃鹰搜索算法(CBES) 文章目录 融合自适应惯性权重和柯西变异的秃鹰搜索算法(CBES)1.秃鹰优化算法2.改进秃鹰优化算法2.1 Tent 映射2.2 自适应惯性权重2.3 柯西变异 3.实验结果4.参考文献5.Matlab代码6.python代码 摘要&#xff1a;针对基本秃鹰搜…

复习HashMap-2

在Java集合中&#xff0c;Map是一种特殊的集合&#xff0c;原因在于这种集合容器并不是保存单个元素&#xff0c;而是保存一个一个的Key-Vaue键值对.HashMap是基于哈希表的Map接口的实现,在项目开发中使用广泛,下面就对HashMap的源码进行解析. Hashmap的特点 1.HashMap是基于…

【Tauri + React 实战】VCluster - 配置应用图标与启动闪屏

本节我们将为 Tauri 应用自定义应用图标的启动闪屏 起步 通过 npm create tauri-applatest 我们成功创建了一个空白的 Tauri 项目&#xff0c;npm install安装好依赖后&#xff0c;通过 npm run tauri dev 即可开启热加载&#xff1a; 配置图标 官方示例很酷&#xff0c;但…

数仓报表数据导出——Hive数据导出至Clickhouse

1. Clickhouse建表 创建database create database ad_report; use ad_report;创建table drop table if exists dwd_ad_event_inc; create table if not exists dwd_ad_event_inc (event_time Int64 comment 事件时间,event_type String comment 事件…

运营实操,如何寻找自己产品的精准关键词

在进行Listing优化时&#xff0c;许多卖家感到困惑&#xff0c;不知从何入手。尽管他们听过许多关于优化的文章&#xff0c;但实际操作时仍不清楚何为好的标准。 亚马逊产品列表的另一个重要元素是图片。图像体验很大程度上决定了客户是否会继续浏览&#xff0c;仅仅依靠文字故…

Linux--查看man手册中某个函数的库函数

第一步&#xff1a;man 函数名 第二步&#xff1a;查看是否存在 第三步&#xff1a;如果不存在&#xff0c;则输入man 2 exit依次类推3、4.....

STM32开发笔记:中断

一、中断系统 中断&#xff1a;在主程序运行过程中&#xff0c;出现了特定的中断触发条件&#xff08;中断源&#xff09;&#xff0c;使得CPU暂停当前正在运行的程序&#xff0c;转而去处理中断程序&#xff0c;处理完成后又返回原来被暂停的位置继续运行。 中断优先级&#x…

c++11 标准模板(STL)(std::basic_istream)(九)

定义于头文件 <istream> template< class CharT, class Traits std::char_traits<CharT> > class basic_istream : virtual public std::basic_ios<CharT, Traits> 类模板 basic_istream 提供字符流上的高层输入支持。受支持操作包含带格式的…

静态数码管显示

学习芯片&#xff1a; EP4CE6F17C8 本次学习使用的为共阴极数码管&#xff0c;即用低电平点亮数码管&#xff0c;同样可知&#xff0c;共阳极数码管的阳极连在一起&#xff0c;即用高电平点亮数码管。 八段数码管示意图&#xff1a; a,b,c,d,e,f,g,dg表示八段数码管时&#…

分类预测 | MATLAB实现基于Attention-LSTM的数据分类预测多特征分类预测(长短期记忆网络融合注意力机制分类预测,含混淆矩阵图、分类图)

分类预测 | MATLAB实现基于Attention-LSTM的数据分类预测多特征分类预测(长短期记忆网络融合注意力机制分类预测&#xff0c;含混淆矩阵图、分类图) 目录 分类预测 | MATLAB实现基于Attention-LSTM的数据分类预测多特征分类预测(长短期记忆网络融合注意力机制分类预测&#xff…

mybatis例子,以及静态资源过滤问题解决

第一步&#xff1a;编写一个工具类 package com.heerlin.utils;import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder;imp…

Spring Boot 源码学习之@SpringBootApplication注解

SpringBootApplication 注解 引言主要内容1. 创建 Spring Boot 项目2. Spring Boot 入口类3. SpringBootApplication 介绍 总结 引言 在 Huazie 前面的博文 《Spring Boot 核心运行原理介绍》中&#xff0c;我们初步了解了 Spring Boot 核心运行原理&#xff0c;知道了 Enable…

#挑战Open AI!马斯克宣布成立xAI,你怎么看?# 马斯克的xAI:充满困难与希望

文章目录 1.什么是xAI公司&#xff1f;2.xAI公司的图标3.“反AI斗士”马斯克进军AI&#xff1a;期待与挑战并存3.1 关于马斯克……3.2 这位“反AI斗士”……3.3 我的看法3.4 可能会遇到的困难与优势3.5 蓄谋已久的马斯克……3.6 xAI“全明星阵容”3.7 总结 4.百模大战&#xff…

git第一次拉取远程分支项目(ssh的方式)

一.生成SSH keys,并将生成的key复制到远程库 1.本地用命令生成密钥对。 ssh-keygen -t rsa -C "yourEmailAddress" 或 ssh-keygen -t ed25519 -C "yourEmailAddress" 按三次enter直接生成密钥对。 2.切换至ssh目录下&#xff0c;复制key&#xff08;公…

软件测试值不值得学,2023软件测试行情分析

目录 1、人们的生活离不开软件&#xff0c;有软件的地方就有测试 2、测试工程师特别是自动化测试工程师的需求会越来越大 3、软件测试经验越丰富越受欢迎&#xff0c;不存在35岁限制。 4、所有新兴行业比如chat-gtp&#xff0c;车载系统等都需要测试工程师 薪资 就业 软…

设计模式——桥梁模式

桥梁模式 定义 桥梁模式&#xff08;Bridge Pattern&#xff09;也叫做桥接模式。 将抽象和显示解耦&#xff0c;使得两者可以独立地变化。 优缺点、应用场景 优点 抽象和实现的解耦。 这是桥梁模式的主要特点&#xff0c;它完全是为了解决继承的缺点而提出的设计模式。优…

从C到C++ | C++入门(二)

目录 缺省参数 1.)全缺省 2.)半缺省 函数重载 1.) 参数类型不同 2.) 参数个数不同 3.) 参数顺序不同 函数重载的原理&#xff1a; &#xff01;&#xff01;&#xff01;注意 &#xff01;&#xff01;&#xff01; 引用 1.) 引用做参数 2.) 引用做返回值 引用和…