不想努力的程序员 2022-07-21 17:17 采纳率: 52.5%
浏览 560
已结题

微信支付时报支付验证签名失败,怎么解决

前端是uniapp,后端是springboot,后端统一下单后,返回信息,前端调用uni.requestPayment,提示支付验证签名失败,控制台没有返回错误信息,这是哪里的问题啊,求解。
这是统一下单的方法

package com.xjc.vueapi.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.xjc.common.config.WechatPayConfig;
import com.xjc.common.config.WechatPayUrlEnum;
import com.xjc.common.request.WechatPayRequest;
import com.xjc.common.untils.ResultUtils;
import com.xjc.entity.RechargeRecord;
import com.xjc.service.RechargeRecordService;
import com.xjc.service.WeChatUserService;
import com.xjc.vo.WeChatPayVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.impl.client.CloseableHttpClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * @ClassName WeChatPayController
 * @Description
 * @Author bill
 * @Date 2022-07-20 16:19
 **/
@RestController
@RequestMapping("wx/pay")
@RequiredArgsConstructor
@Slf4j
public class WeChatPayController {
    final WechatPayConfig wechatPayConfig;
    final WechatPayRequest wechatPayRequest;

    /**
     * 无需应答签名
     */
    final CloseableHttpClient wxPayNoSignClient;

    /**
     * 交易
     * @param type h5、jsapi、app、native、sub_jsapi
     * @return
     */
    @RequestMapping("transactions")
    public ResultUtils transactions(@RequestBody WeChatPayVO weChatPayVO){
      
        // 统一参数封装
        Map<String, Object> params = new HashMap<>(8);
        params.put("appid", wechatPayConfig.getAppId());
        params.put("mchid", wechatPayConfig.getMerchantId());
        params.put("description", "充值信息");
        int outTradeNo = new Random().nextInt(999999999);
        params.put("out_trade_no", outTradeNo+"");
        params.put("notify_url", wechatPayConfig.getNotifyOrderUrl());

        Map<String, Object> amountMap = new HashMap<>(4);
        // 金额单位为分
        amountMap.put("total", 1);
        amountMap.put("currency", "CNY");
        params.put("amount", amountMap);

        // 场景信息
        Map<String, Object> sceneInfoMap = new HashMap<>(4);
        // 客户端IP
        sceneInfoMap.put("payer_client_ip", "127.0.0.1");
        // 商户端设备号(门店号或收银设备ID)
        sceneInfoMap.put("device_id", "127.0.0.1");

        // 除H5与JSAPI有特殊参数外,其他的支付方式都一样
        if (weChatPayVO.getType().equals(WechatPayUrlEnum.H5.getType())) {

            Map<String, Object> h5InfoMap = new HashMap<>(4);
            // 场景类型:iOS, Android, Wap
            h5InfoMap.put("type", "IOS");
            sceneInfoMap.put("h5_info", h5InfoMap);
        } else if (weChatPayVO.getType().equals(WechatPayUrlEnum.JSAPI.getType()) || weChatPayVO.getType().equals(WechatPayUrlEnum.SUB_JSAPI.getType())) {
            Map<String, Object> payerMap = new HashMap<>(4);
            payerMap.put("openid", weChatPayVO.getOpenid());
            params.put("payer", payerMap);
        }

        params.put("scene_info", sceneInfoMap);

        String paramsStr = JSON.toJSONString(params);
        log.info("请求参数 ===> {}" + paramsStr);

        // 重写type值,因为小程序会多一个下划线(sub_type)
        String[] split = weChatPayVO.getType().split("_");
        String  newType = split[split.length - 1];

        String resStr = wechatPayRequest.wechatHttpPost(wechatPayConfig.getBaseUrl().concat(WechatPayUrlEnum.PAY_TRANSACTIONS.getType().concat(newType)), paramsStr);
        Map<String, Object> resMap = JSONObject.parseObject(resStr, new TypeReference<Map<String, Object>>(){});

        Map<String, Object> signMap = paySignMsg(resMap, weChatPayVO.getType());
        resMap.put("type",weChatPayVO.getType());
        resMap.put("signMap",signMap);
        return new ResultUtils(resMap);
    }
    private  Map<String, Object>  paySignMsg(Map<String, Object> map,String type){
        // 设置签名信息,Native与H5不需要
        if(type.equals(WechatPayUrlEnum.H5.getType()) || type.equals(WechatPayUrlEnum.NATIVE.getType()) ){
            return null;
        }

        long timeMillis = System.currentTimeMillis();
        String appId = wechatPayConfig.getAppId();
        String timeStamp = timeMillis/1000+"";
        String nonceStr = timeMillis+"";
        String prepayId = map.get("prepay_id").toString();
        String packageStr = "prepay_id="+prepayId;

        // 公共参数
        Map<String, Object> resMap = new HashMap<>();
        resMap.put("nonceStr",nonceStr);
        resMap.put("timeStamp",timeStamp);

        // JSAPI、SUB_JSAPI(小程序)
        if(type.equals(WechatPayUrlEnum.JSAPI.getType()) || type.equals(WechatPayUrlEnum.SUB_JSAPI.getType()) ) {
            resMap.put("appId",appId);
            resMap.put("package", packageStr);
            // 使用字段appId、timeStamp、nonceStr、package进行签名
            String paySign = createSign(resMap);
            resMap.put("paySign", paySign);
            resMap.put("signType", "RSA");
        }
        // APP
        if(type.equals(WechatPayUrlEnum.APP.getType())) {
            resMap.put("appid",appId);
            resMap.put("prepayid", prepayId);
            // 使用字段appId、timeStamp、nonceStr、prepayId进行签名
            String sign = createSign(resMap);
            resMap.put("package", "Sign=WXPay");
            resMap.put("partnerid", wechatPayConfig.getMerchantId());
            resMap.put("sign", sign);
            resMap.put("signType", "HMAC-SHA256");
        }
        return resMap;
    }
    /**
     * 获取加密数据
     */
    private  String createSign(Map<String, Object> params){
        try {
            Map<String, Object> treeMap = new TreeMap<>(params);
            List<String> signList = new ArrayList<>(5);
            for (Map.Entry<String, Object> entry : treeMap.entrySet())
            {
                signList.add(entry.getKey() + "=" + entry.getValue());
            }
            String signStr = String.join("&", signList);

            signStr = signStr+"&key="+wechatPayConfig.getV3Key();
            System.out.println(signStr);

            Mac sha = Mac.getInstance("HmacSHA256");
            SecretKeySpec secretKey = new SecretKeySpec(wechatPayConfig.getV3Key().getBytes(StandardCharsets.UTF_8), "HmacSHA256");
            sha.init(secretKey);
            byte[] array = sha.doFinal(signStr.getBytes(StandardCharsets.UTF_8));
            StringBuilder sb = new StringBuilder();
            for (byte item : array) {
                sb.append(Integer.toHexString((item & 0xFF) | 0x100), 1, 3);
            }
            signStr = sb.toString().toUpperCase();
            System.out.println(signStr);

            return signStr;
        }catch (Exception e){
            throw new RuntimeException("加密失败!");
        }
    }
}



这是WechatPayRequest类

package com.xjc.common.request;

import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.IOException;

/**
 * @ClassName WechatPayRequest
 * @Description 封装统一请求处理
 * @Author bill
 * @Date 2022-07-20 16:03
 **/
@Component
@Slf4j
public class WechatPayRequest {
    @Resource
    private CloseableHttpClient wxPayClient;
    public  String wechatHttpGet(String url) {
        try {
            // 拼接请求参数
            HttpGet httpGet = new HttpGet(url);
            httpGet.setHeader("Accept", "application/json");

            //完成签名并执行请求
            CloseableHttpResponse response = wxPayClient.execute(httpGet);

            return getResponseBody(response);
        }catch (Exception e){
            throw new RuntimeException(e.getMessage());
        }
    }

    public  String wechatHttpPost(String url,String paramsStr) {
        try {
            HttpPost httpPost = new HttpPost(url);
            StringEntity entity = new StringEntity(paramsStr, "utf-8");
            entity.setContentType("application/json");
            httpPost.setEntity(entity);
            httpPost.setHeader("Accept", "application/json");

            CloseableHttpResponse response = wxPayClient.execute(httpPost);
            return getResponseBody(response);
        }catch (Exception e){
            throw new RuntimeException(e.getMessage());
        }
    }

    private String getResponseBody(CloseableHttpResponse response) throws IOException {

        //响应体
        HttpEntity entity = response.getEntity();
        String body = entity==null?"": EntityUtils.toString(entity);
        //响应状态码
        int statusCode = response.getStatusLine().getStatusCode();

        //处理成功,204是,关闭订单时微信返回的正常状态码
        if (statusCode == HttpStatus.SC_OK || statusCode == HttpStatus.SC_NO_CONTENT) {
            log.info("成功, 返回结果 = " + body);
        } else {
            String msg = "微信支付请求失败,响应码 = " + statusCode + ",返回结果 = " + body;
            log.error(msg);
            throw new RuntimeException(msg);
        }
        return body;
    }
}


这是WechatPayConfig类

package com.xjc.common.config;

import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
import com.wechat.pay.contrib.apache.httpclient.exception.HttpCodeException;
import com.wechat.pay.contrib.apache.httpclient.exception.NotFoundException;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.impl.client.CloseableHttpClient;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @ClassName WechatPayConfig
 * @Description
 * @Author bill
 * @Date 2022-07-20 15:08
 **/
@Component
@Data
@Slf4j
@ConfigurationProperties(prefix = "wx")
public class WechatPayConfig {
        /**
         * 小程序ID
         */
        private String appId;
        /**
         * 小程序密钥
         */
        private String appSecret;
        /**
         * 商户id
         */
        private String merchantId;
        /**
         * 证书序列号
         */
        private String merchantSerialNumber;
        /**
         * v3密钥
         */
        private String v3Key;
        /**
         * 下单成功后回调
         */
        private String notifyOrderUrl;
        /**
         * 退款回调url
         */
        private String notifyRefoundUrl;
        private String keyPath;
        private String certPath;
        /**
         * 微信支付V3-url前缀
         */
        private String baseUrl;
        /**
         * // 定义全局容器 保存微信平台证书公钥
         */
        public Map<String, X509Certificate> certificateMap = new ConcurrentHashMap<>();
        /**
         * 获取商户的私钥文件
         * @param keyPemPath
         * @return
         */
        public PrivateKey getPrivateKey(String keyPemPath){

                InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(keyPemPath);
                if(inputStream==null){
                        throw new RuntimeException("私钥文件不存在");
                }
                return PemUtil.loadPrivateKey(inputStream);
        }
        /**
         * 获取证书管理器实例
         * @return
         */
        @Bean
        public Verifier getVerifier() throws GeneralSecurityException, IOException, HttpCodeException, NotFoundException {

                log.info("获取证书管理器实例");

                //获取商户私钥
                PrivateKey privateKey = getPrivateKey(keyPath);

                //私钥签名对象
                PrivateKeySigner privateKeySigner = new PrivateKeySigner(merchantSerialNumber, privateKey);

                //身份认证对象
                WechatPay2Credentials wechatPay2Credentials = new WechatPay2Credentials(merchantId, privateKeySigner);

                // 使用定时更新的签名验证器,不需要传入证书
                CertificatesManager certificatesManager = CertificatesManager.getInstance();
                certificatesManager.putMerchant(merchantId,wechatPay2Credentials,v3Key.getBytes(StandardCharsets.UTF_8));

                return certificatesManager.getVerifier(merchantId);
        }
        /**
         * 获取支付http请求对象
         * @param verifier
         * @return
         */
        @Bean(name = "wxPayClient")
        public CloseableHttpClient getWxPayClient(Verifier verifier)  {

                //获取商户私钥
                PrivateKey privateKey = getPrivateKey(keyPath);

                WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
                        .withMerchant(merchantId, merchantSerialNumber, privateKey)
                        .withValidator(new WechatPay2Validator(verifier));

                // 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签,并进行证书自动更新
                return builder.build();
        }
        /**
         * 获取HttpClient,无需进行应答签名验证,跳过验签的流程
         */
        @Bean(name = "wxPayNoSignClient")
        public CloseableHttpClient getWxPayNoSignClient(){

                //获取商户私钥
                PrivateKey privateKey = getPrivateKey(keyPath);

                //用于构造HttpClient
                WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
                        //设置商户信息
                        .withMerchant(merchantId, merchantSerialNumber, privateKey)
                        //无需进行签名验证、通过withValidator((response) -> true)实现
                        .withValidator((response) -> true);

                // 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签,并进行证书自动更新
                return builder.build();
        }

}


是哪有问题求解!

  • 写回答

1条回答 默认 最新

  • 惠惠软件 惠惠软件官方账号 2022-07-24 19:16
    关注

    重新获取支付秘钥看看~

    评论

报告相同问题?

问题事件

  • 已结题 (查看结题原因) 8月25日
  • 赞助了问题酬金10元 8月25日
  • 创建了问题 7月21日

悬赏问题

  • ¥15 HFSS 中的 H 场图与 MATLAB 中绘制的 B1 场 部分对应不上
  • ¥15 如何在scanpy上做差异基因和通路富集?
  • ¥20 关于#硬件工程#的问题,请各位专家解答!
  • ¥15 关于#matlab#的问题:期望的系统闭环传递函数为G(s)=wn^2/s^2+2¢wn+wn^2阻尼系数¢=0.707,使系统具有较小的超调量
  • ¥15 FLUENT如何实现在堆积颗粒的上表面加载高斯热源
  • ¥30 截图中的mathematics程序转换成matlab
  • ¥15 动力学代码报错,维度不匹配
  • ¥15 Power query添加列问题
  • ¥50 Kubernetes&Fission&Eleasticsearch
  • ¥15 報錯:Person is not mapped,如何解決?