微信V3支付

	<dependency>
			<groupId>com.github.wechatpay-apiv3</groupId>
			<artifactId>wechatpay-apache-httpclient</artifactId>
			<version>0.3.0</version>
		</dependency>

整个下单的类




/**
 * @author xyl
 * @date 2022/7/11 16:39
 * @explain
 */

@Service
@Slf4j
public class WeChatPayServiceImpl implements WeChatPayService {

    @Value("${spring.profiles.active}")
    private String active;


    private static final String PROD = "prod";
    private static final String APP_ID = "appid";
    private static final String MCH_ID = "mchid";
    private static final String GET = "GET";
    private static final String PACKAGE = "Sign=WXPay";
    private static final String SUCCESS = "SUCCESS";
    private static final String TRADE_STATE = "trade_state";
    private static final String OUT_TRADE_NO = "out_trade_no";
    private static final String ACCOUNT_NO = "account_no";
    private static final String TRANSACTION_ID = "transaction_id";
    private static final String TOTAL = "total";

    @Resource
    private CloseableHttpClient wxPayClient;
    @Autowired
    private WxPayConfig wxPayConfig;
    @Autowired
    private ScheduledUpdateCertificatesVerifier verifier;
    @Autowired
    private RefundOrderService refundOrderService;
    @Autowired
    private ScoreOrderService scoreOrderService;
    @Autowired
    private TyGoodsAuthorityService goodsAuthorityService;
    @Autowired
    private MemberOrderService memberOrderService;
    @Autowired
    private StoreSettledOrderService storeSettledOrderService;
    @Autowired
    private ConsignmentSalesService consignmentSalesService;
    @Autowired
    private RechargeOrderService rechargeOrderService;
    @Autowired
    private AfterPayUtil afterPayUtil;
    @Autowired
    private OrderService orderService;

    /**
     * 微信下单
     *
     * @param dto
     * @return {@link {@link HashMap< String, String>}}
     * @author xyl
     * @date 2022/7/12 16:22
     * @explain
     */
    @Override
    public HashMap<String, String> buy(BuyCreateDto dto) {
        //下单map
        BuyUrlMapBo bo = getBuyUrlMapBo(dto);
        HashMap<String, Object> buyMap = bo.getBuyMap();
        //支付金额封装map
        HashMap<String, Object> amountMap = new HashMap<>();
        amountMap.put("total", dto.getTotal());
        amountMap.put("currency", "CNY");
        //加入金额
        buyMap.put("amount", amountMap);
        try {
            //生成签名
            Gson gson = new Gson();
            String json = gson.toJson(buyMap);
            HttpUrl httpurl = HttpUrl.parse(wxPayConfig.getDomain().concat(WeChatApiTypeEnum.GET_CERTIFICATES.getType()));
            HashMap<String, String> token = getToken(GET, httpurl, json);
            HttpPost httpPost = new HttpPost(bo.getUrl());
            StringEntity entity = new StringEntity(json, "utf-8");
            entity.setContentType("application/json");
            httpPost.setEntity(entity);
            httpPost.setHeader("Accept", "application/json");
            CloseableHttpResponse response = wxPayClient.execute(httpPost);
            //响应体
            String bodyAsString = EntityUtils.toString(response.getEntity());
            JSONObject jsonObject = JSON.parseObject(bodyAsString);
            System.out.println("响应体" + bodyAsString);
            //响应状态码
            int statusCode = response.getStatusLine().getStatusCode();
            System.out.println("状态码" + statusCode);
            if (statusCode != 200) {
                throw new BackendException(bodyAsString);
            }
            //封装返回参数
            return getResMap(dto.getPayType(), buyMap, token, jsonObject);
        } catch (Exception e) {
            log.warn("微信支付失败,错误信息:{}", e.getMessage());
            System.out.println(("微信支付失败,错误信息:{}" + e.getMessage()));
            throw new BackendException(CommonEnum.PARAMETER_ERROR);
        }
    }

    private boolean verifiedSign(String serialNo, String signStr, String signature) throws UnsupportedEncodingException {
        return verifier.verify(serialNo, signStr.getBytes("utf-8"), signature);
    }


    /**
     * 微信下单组装返回
     *
     * @param buyMap     支付map
     * @param token      签名
     * @param jsonObject 下单返回结果
     * @return {@link {@link HashMap< String, String>}}
     * @author xyl
     * @date 2022/7/12 16:05
     * @explain
     */
    private HashMap<String, String> getResMap(Integer payType, HashMap<String, Object> buyMap, HashMap<String, String> token, JSONObject jsonObject) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
        HashMap<String, String> resMap = new HashMap<>();
        StringBuilder stringBuilder = new StringBuilder();
        String appid = buyMap.get("appid").toString();
        resMap.put("appId", appid);
        stringBuilder.append(appid).append("\n")
                .append(token.get("timestamp")).append("\n")
                .append(token.get("nonce_str")).append("\n");

        if (payType.equals(PayTypeEnum.WX_APP_WECHAT_PAY.getCode())) {
            resMap.put("package", "prepay_id=" + jsonObject.get("prepay_id").toString());
            stringBuilder.append(resMap.get("package")).append("\n");
        } else {
            resMap.put("package", PACKAGE);
            resMap.put("prepayId", jsonObject.get("prepay_id").toString());
            resMap.put("partnerId", token.get("mchid"));
            stringBuilder.append(jsonObject.get("prepay_id").toString()).append("\n");
        }
        resMap.put("nonceStr", token.get("nonce_str"));
        resMap.put("timeStamp", token.get("timestamp"));
        log.warn("签名字符串:" + stringBuilder);
        String sign = sign(stringBuilder.toString().getBytes(StandardCharsets.UTF_8));
        log.warn("获取的签名:" + sign);
        resMap.put("signType", "RSA");
        resMap.put("paySign", sign);
        System.out.println(resMap);
        return resMap;
    }


    /**
     * 构建下单基础信息
     *
     * @param dto
     * @return {@link {@link HashMap< String, Object>}}
     * @author xyl
     * @date 2022/7/12 9:18
     * @explain
     */
    private BuyUrlMapBo getBuyUrlMapBo(BuyCreateDto dto) {
        BuyUrlMapBo bo = new BuyUrlMapBo();
        HashMap<String, Object> buyMap = new HashMap<>();
        //微信小程序
        if (dto.getPayType() == PayTypeEnum.WX_APP_WECHAT_PAY.getCode()) {
            //小程序appId
            buyMap.put(APP_ID, wxPayConfig.getAppId());
            //用户openId
            HashMap<String, Object> payerMap = new HashMap<>();
            payerMap.put("openid", dto.getOpenId());
            buyMap.put("payer", payerMap);
            //微信api接口
            String url = wxPayConfig.getDomain().concat(WeChatApiTypeEnum.JSAPI_PAY.getType());
            bo.setUrl(url);
            //微信app
        } else if (dto.getPayType() == PayTypeEnum.APP_WECHAT_PAY.getCode()) {
            buyMap.put(APP_ID, wxPayConfig.getAppAppId());
            //微信api接口
            String url = wxPayConfig.getDomain().concat(WeChatApiTypeEnum.APP_PAY.getType());
            bo.setUrl(url);
            //微信H5
        } else if (dto.getPayType() == PayTypeEnum.WX_H5_WECHAT_PAY.getCode()) {
            //todo 暂无配置
            buyMap.put(APP_ID, wxPayConfig.getAppAppId());
            //微信api接口
            String url = wxPayConfig.getDomain().concat(WeChatApiTypeEnum.H5_PAY.getType());
            bo.setUrl(url);
        } else {
            log.warn("发起支付失败,支付方式错误,请求参数:{}", dto);
            throw new BackendException(CommonEnum.PARAMETER_ERROR);
        }
        buyMap.put(MCH_ID, wxPayConfig.getMchId());
        buyMap.put("description", "商品购买");
        buyMap.put("out_trade_no", dto.getOutTradeNo());
        if (PROD.equals(active)) {
            //线上回调地址
            buyMap.put("notify_url", wxPayConfig.getPayNotifyUrl().concat(PayNotifyEnum.NATIVE_NOTIFY.getType()));
        } else {
            //测试线上回调地址
            buyMap.put("notify_url", wxPayConfig.getPayNotifyTestUrl().concat(PayNotifyEnum.NATIVE_NOTIFY.getType()));
        }
        bo.setBuyMap(buyMap);
        return bo;
    }


    /**
     * 获取微信支付平台证书
     *
     * @param method 提交方式
     * @param url    接口
     * @param body   主体
     * @return {@link {@link String}}
     * @author xyl
     * @date 2022/7/12 10:45
     * @explain
     */
    public HashMap<String, String> getToken(String method, HttpUrl url, String body) throws UnsupportedEncodingException, NoSuchAlgorithmException, SignatureException, InvalidKeyException {
        //随机字符串
        String nonceStr = PayUtil.createNonceStr();
        //当前时间戳
        long timestamp = System.currentTimeMillis() / 1000;
        String message = buildMessage(method, url, timestamp, nonceStr, body);
        String signature = sign(message.getBytes("utf-8"));
        HashMap<String, String> map = new HashMap<>();
        map.put("mchid", wxPayConfig.getMchId());
        map.put("nonce_str", nonceStr);
        map.put("timestamp", String.valueOf(timestamp));
        map.put("serial_no", wxPayConfig.getMchSerialNo());
        map.put("signature", signature);
        return map;
    }


    /**
     * 构建消息
     *
     * @param method    方式
     * @param url       请求连接
     * @param timestamp 时间戳
     * @param nonceStr  随机字符串
     * @param body      传递的主体
     * @return {@link {@link String}}
     * @author xyl
     * @date 2022/7/12 10:51
     * @explain
     */
    String buildMessage(String method, HttpUrl url, long timestamp, String nonceStr, String body) {
        String canonicalUrl = url.encodedPath();
        if (url.encodedQuery() != null) {
            canonicalUrl += "?" + url.encodedQuery();
        }
        return method + "\n"
                + canonicalUrl + "\n"
                + timestamp + "\n"
                + nonceStr + "\n"
                + body + "\n";
    }

    /**
     * 生成签名
     *
     * @param message
     * @return {@link {@link String}}
     * @author xyl
     * @date 2022/7/12 10:47
     * @explain
     */
    String sign(byte[] message) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
        Signature sign = null;
        try {
            PrivateKey merchantPrivateKey = wxPayConfig.getPrivateKey();
            sign = Signature.getInstance("SHA256withRSA");
            sign.initSign(merchantPrivateKey);
            try {
                sign.update(message);
            } catch (SignatureException e) {
                e.printStackTrace();
            }
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return Base64.getEncoder().encodeToString(sign.sign());
    }


    /**
     * 微信支付回调
     *
     * @param request  请求体
     * @param response 响应体
     * @return {@link {@link HashMap< String, String>}}
     * @author xyl
     * @date 2022/7/12 16:39
     * @explain
     */
    @Override
    public HashMap<String, String> weChatPay(HttpServletRequest request, HttpServletResponse response) {
        //获取报文
        String body = getRequestBody(request);
        log.info("微信支付回调,获得报文:{}", body);
        //随机串
        String nonceStr = request.getHeader("Wechatpay-Nonce");

        //微信传递过来的签名
        String signature = request.getHeader("Wechatpay-Signature");

        //证书序列号(微信平台)
        String serialNo = request.getHeader("Wechatpay-Serial");

        //时间戳
        String timestamp = request.getHeader("Wechatpay-Timestamp");

        //构造签名串

        //应答时间戳\n
        //应答随机串\n
        //应答报文主体\n
        String signStr = Stream.of(timestamp, nonceStr, body).collect(Collectors.joining("\n", "", "\n"));
        log.warn("微信支付回调,构建签名串:{}", signStr);
        HashMap<String, String> resultMap = new HashMap<>(2);
        try {
            //验证签名是否通过
            boolean result = verifiedSign(serialNo, signStr, signature);
            log.warn("微信支付回调,验证签名结果:{}", result);
            if (result) {
                //解密数据
                String plainBody = decryptBody(body);
                log.warn("微信支付回调,解密后的明文:{}", plainBody);
                Map<String, String> map = convertWeChatPayMsgToMap(plainBody);
                //处理业务逻辑 TODO
                if (map.get(TRADE_STATE).equals(SUCCESS)) {
                    log.warn("支付回调进入业务支付流程");
                    //获取订单号
                    String orderNo = map.get(OUT_TRADE_NO);

                    /购买会员支付回调
                    if (orderNo.startsWith("H")) {
                        MemberOrder memberOrder = memberOrderService.getByOrderNo(orderNo);
                        if (memberOrder == null || memberOrder.getOrderStatus() != 0) {
                            log.error("微信支付回调-----订单已处理;订单{}", memberOrder);
                            resultMap.put("code", "SUCCESS");
                            resultMap.put("message", "成功");
                            return resultMap;
                        }
                        memberOrderService.afterPaid(memberOrder,
                                System.currentTimeMillis(),
                                Long.parseLong(map.get(TOTAL)),
                                map.get(TRANSACTION_ID));
                    }

                    /积分商城支付回调
                    if (orderNo.startsWith("JF")) {
                        ScoreOrder scoreOrder = scoreOrderService.getByOrderNo(orderNo);
                        if (scoreOrder == null || scoreOrder.getStatus() != 0) {
                            log.error("微信支付回调-----订单已处理;订单{}", scoreOrder);
                            resultMap.put("code", "SUCCESS");
                            resultMap.put("message", "成功");
                            return resultMap;
                        }
                        scoreOrderService.afterPaid(null, scoreOrder,
                                System.currentTimeMillis(),
                                map.get(TRANSACTION_ID),
                                Long.parseLong(map.get(TOTAL)));
                    }

                    /充值订单支付回调
                    if (orderNo.startsWith("R")) {
                        RechargeOrder rechargeOrder = rechargeOrderService.getByOrderNo(orderNo);
                        if (rechargeOrder == null || rechargeOrder.getOrderStatus() != 0) {
                            log.error("微信支付回调-----订单已处理;订单{}", rechargeOrder);
                            map.put("code", "SUCCESS");
                            map.put("message", "成功");
                            return resultMap;
                        }
                        LambdaUpdateWrapper<RechargeOrder> updateWrapper = new UpdateWrapper<RechargeOrder>()
                                .lambda()
                                .eq(RechargeOrder::getId, rechargeOrder.getId())
                                .set(RechargeOrder::getOrderStatus, OrderStatusEnum.PENDING_DELIVERY.getCode())
                                .set(RechargeOrder::getActualAmount, map.get(TOTAL))
                                .set(RechargeOrder::getPayTime, System.currentTimeMillis())
                                .set(RechargeOrder::getPayId, map.get(TRANSACTION_ID));
                        if (rechargeOrderService.update(updateWrapper)) {
                            //记录用户账单
                            afterPayUtil.writeInUserBills(rechargeOrder, "充值");
                        }
                    }

                    /鉴定费支付回调
                    if (orderNo.startsWith("A")) {
                        TyGoodsAuthority goodsAuthority = goodsAuthorityService.findByOrderNo(orderNo);
                        if (goodsAuthority == null || !goodsAuthority.getAppraisalPayState().equals(TyGoodsAuthority.PAY_STATE_DZF)) {
                            log.error("微信支付回调-----订单已处理;订单{}", goodsAuthority);
                            map.put("code", "SUCCESS");
                            map.put("message", "成功");
                            return resultMap;
                        }
                        //付款完成修改状态
                        TyGoodsAuthority upGoodsAuthority = new TyGoodsAuthority();
                        upGoodsAuthority.setPayTime(new Date());
                        upGoodsAuthority.setPayId(map.get(TRANSACTION_ID));
                        upGoodsAuthority.setAppraisalPayState(TyGoodsAuthority.PAY_STATE_YZF);
                        AliyunSMSUtil.remand("(鉴定相关:用户已支付鉴定费,订单号:orderNo)");
                        goodsAuthorityService.updateById(upGoodsAuthority);
                    }

                    /二手交易支付回调
                    if (orderNo.startsWith(OrderConstant.M)) {
                        log.warn("微信支付回调,进入二手交易订单处理,订单号:{}", orderNo);
                        Order order = orderService.findOrderByOrderNo(orderNo);
                        if (order == null || order.getStatus() != OrderStatusEnum.PENDING_PAY.getCode()) {
                            log.warn("微信支付回调-----订单已处理;订单{}", order);
                            map.put("code", "SUCCESS");
                            map.put("message", "成功");
                            return resultMap;
                        }
                        Long payTime = System.currentTimeMillis();
                        Long actualPayAmount = Long.parseLong(map.get(TOTAL));
                        String payId = map.get(TRANSACTION_ID);
                        order.setPayTime(payTime);
                        order.setActualPayAmount(actualPayAmount);
                        order.setPayId(payId);
                        orderService.paymentSuccessfulOrderProcessing(order);
                    }

                    /商家入驻回调
                    if (orderNo.startsWith(OrderConstant.K)) {
                        log.warn("微信支付回调,进入商家入驻订单处理,订单号:{}", orderNo);
                        StoreSettledOrder order = storeSettledOrderService.findByOrderNo(orderNo);
                        if (order == null || order.getStatus() != 0) {
                            log.warn("微信支付回调-----订单已处理;订单{}", order);
                            map.put("code", "SUCCESS");
                            map.put("message", "成功");
                            return resultMap;
                        }
                        Long payTime = System.currentTimeMillis();
                        String payId = map.get(TRANSACTION_ID);
                        order.setPayTime(payTime);
                        order.setPayId(payId);
                        //处理商家入驻
                        storeSettledOrderService.settled(order);
                    }

                    /寄卖鉴定费回调
                    if (orderNo.startsWith(OrderConstant.C)) {
                        log.warn("微信支付回调,进入商家入驻订单处理,订单号:{}", orderNo);
                        ConsignmentSales order = consignmentSalesService.findByOrderNo(orderNo);
                        if (order == null || order.getStatus() != 4) {
                            log.warn("微信支付回调-----订单已处理;订单{}", order);
                            map.put("code", "SUCCESS");
                            map.put("message", "成功");
                            return resultMap;
                        }
                        Long payTime = System.currentTimeMillis();
                        String payId = map.get(TRANSACTION_ID);
                        order.setPayTime(payTime);
                        order.setPayId(payId);
                        order.setStatus(5);
                        boolean b = consignmentSalesService.updateById(order);
                        if (!b) {
                            log.warn("寄卖失败,支付鉴定费写入失败,写入信息:{}", order);
                        }
                        AliyunSMSUtil.remand("(寄卖鉴定费:用户已经支付,商品名称:{" + order.getName() + "})");
                    }

                }
                //响应微信
                map.put("code", "SUCCESS");
                map.put("message", "成功");
            }

        } catch (Exception e) {
            log.warn("微信支付回调异常:{}", e.getMessage());
        }
        return resultMap;
    }


    /**
     * 解密body的密文
     * <p>
     * "resource": {
     * "original_type": "transaction",
     * "algorithm": "AEAD_AES_256_GCM",
     * "ciphertext": "",
     * "associated_data": "",
     * "nonce": ""
     * }
     *
     * @param body
     * @return {@link {@link String}}
     * @author xyl
     * @date 2022/7/12 16:36
     * @explain
     */
    private String decryptBody(String body) throws UnsupportedEncodingException, GeneralSecurityException {
        AesUtil aesUtil = new AesUtil(wxPayConfig.getApiV3Key().getBytes(StandardCharsets.UTF_8));
        JSONObject object = JSONObject.parseObject(body);
        JSONObject resource = object.getJSONObject("resource");
        String ciphertext = resource.getString("ciphertext");
        String associatedData = resource.getString("associated_data");
        String nonce = resource.getString("nonce");
        return aesUtil.decryptToString(associatedData.getBytes(StandardCharsets.UTF_8), nonce.getBytes(StandardCharsets.UTF_8), ciphertext);

    }


    /**
     * 转换body为map
     *
     * @param plainBody
     * @return {@link {@link Map< String, String>}}
     * @author xyl
     * @date 2022/7/12 16:35
     * @explain
     */
    private Map<String, String> convertWeChatPayMsgToMap(String plainBody) {

        Map<String, String> paramsMap = new HashMap<>(2);

        JSONObject jsonObject = JSONObject.parseObject(plainBody);

        //商户订单号
        paramsMap.put(OUT_TRADE_NO, jsonObject.getString(OUT_TRADE_NO));

        //交易状态
        paramsMap.put(TRADE_STATE, jsonObject.getString(TRADE_STATE));

        //微信交易单号
        paramsMap.put(TRANSACTION_ID, jsonObject.getString(TRANSACTION_ID));

        //交易金额
        String amount = jsonObject.get("amount").toString();
        JSONObject amountJson = JSONObject.parseObject(amount);
        paramsMap.put(TOTAL, amountJson.getString(TOTAL));
        log.warn("微信V3支付回调,封装返回参数:{}", paramsMap);
        return paramsMap;

    }


    /**
     * 读取数据流
     *
     * @param request
     * @return {@link {@link String}}
     * @author xyl
     * @date 2022/7/12 16:33
     * @explain
     */
    private String getRequestBody(HttpServletRequest request) {

        StringBuilder sb = new StringBuilder();

        try (ServletInputStream inputStream = request.getInputStream();
             BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
        ) {
            String line;

            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }

        } catch (IOException e) {
            log.warn("读取数据流异常:{}", e.getMessage());
        }
        return sb.toString();
    }


}

下单dto


/**
 * @author xyl
 * @date 2022/7/11 17:16
 * @explain
 */
@Data
@ApiModel("下单dto")
public class BuyCreateDto {
    @ApiModelProperty(value = "商户订单号(自己的订单号)")
    private String outTradeNo;
    @ApiModelProperty(value = "支付的类型字典11")
    private Integer payType;
    @ApiModelProperty(value = "支付金额")
    private Long total;
    @ApiModelProperty(value = "用户OpenId")
    private String openId;


    public BuyCreateDto() {
    }

    public BuyCreateDto(String outTradeNo, Integer payType, Long total, String openId) {
        this.outTradeNo = outTradeNo;
        this.payType = payType;
        this.total = total;
        this.openId = openId;
    }
}

构建下单的基础信息返回的bo

/**
 * @author xyl
 * @date 2022/7/12 13:13
 * @explain
 */
@Data
@ApiModel("获取URL和构建支付mapBo")
public class BuyUrlMapBo {
    @ApiModelProperty(value = "微信支付API接口")
    private String url;
    @ApiModelProperty(value = "构建的支付信息map")
    private HashMap<String,Object> buyMap;
}

PayUtil工具类

/**
 * @author xyl
 */
public class PayUtil {
    /**
     * 获取随机字符串
     *
     * @return 2016年8月4日 上午9:26:07
     * @author Xuehao
     */
    public static String createNonceStr() {
        String s = UUID.randomUUID().toString();
        // 去掉“-”符号
        return s.replaceAll("\\-", "").replaceAll("-", "").toUpperCase();
    }

    /**
     * 获取时间戳
     *
     * @return 2016年8月4日 上午9:26:20
     * @author Xuehao
     */
    public static String createTimestamp() {
        return Long.toString(System.currentTimeMillis() / 1000);
    }

    /**
     * 构造签名
     *
     * @param params
     * @param encode
     * @return
     * @throws UnsupportedEncodingException
     */
    public static String createSign(Map<String, String> params, boolean encode) throws UnsupportedEncodingException {
        Set<String> keysSet = params.keySet();
        Object[] keys = keysSet.toArray();
        Arrays.sort(keys);
        StringBuffer temp = new StringBuffer();
        boolean first = true;
        for (Object key : keys) {
            if (first) {
                first = false;
            } else {
                temp.append("&");
            }
            temp.append(key).append("=");
            Object value = params.get(key);
            String valueString = "";
            if (null != value) {
                valueString = value.toString();
            }
            if (encode) {
                temp.append(URLEncoder.encode(valueString, "UTF-8"));
            } else {
                temp.append(valueString);
            }
        }
        return temp.toString();
    }

    /**
     * 生成签名
     *
     * @param parameters
     * @param appSecret
     * @return
     */
    public static String createSign(SortedMap<String, Object> parameters, String appSecret) {
        StringBuilder sb = new StringBuilder();
        Set es = parameters.entrySet();
        for (Object e : es) {
            Map.Entry entry = (Map.Entry) e;
            String k = (String) entry.getKey();
            Object v = entry.getValue();
            if (null != v && !"".equals(v)
                    && !"sign".equals(k) && !"key".equals(k)) {
                sb.append(k).append("=").append(v).append("&");
            }
        }
        sb.append("key=").append(appSecret);
        System.out.println(sb.toString());
        return Md5Util.md5EncoderByCharset(sb.toString(), "UTF-8").toUpperCase();
    }

    //请求xml组装
    public static String getRequestXml(SortedMap<String, Object> parameters) {
        StringBuilder sb = new StringBuilder();
        sb.append("<xml>");
        Set es = parameters.entrySet();
        for (Object e : es) {
            Map.Entry entry = (Map.Entry) e;
            String key = (String) entry.getKey();
            String value = (String) entry.getValue();
            if ("attach".equalsIgnoreCase(key) || "body".equalsIgnoreCase(key) || "sign".equalsIgnoreCase(key)) {
                sb.append("<").append(key).append(">").append("<![CDATA[").append(value).append("]]></").append(key).append(">");
            } else {
                sb.append("<").append(key).append(">").append(value).append("</").append(key).append(">");
            }
        }
        sb.append("</xml>");
        return sb.toString();
    }

    /**
     * xml解析
     *
     * @param strxml
     * @return
     * @throws JDOMException
     * @throws IOException
     */
    public static Map doXMLParse(String strxml) throws JDOMException, IOException {
        strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");
        if (null == strxml || "".equals(strxml)) {
            return null;
        }

        Map m = new HashMap();
        InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));
        SAXBuilder builder = new SAXBuilder();
        Document doc = builder.build(in);
        Element root = doc.getRootElement();
        List list = root.getChildren();
        for (Object aList : list) {
            Element e = (Element) aList;
            String k = e.getName();
            String v = "";
            List children = e.getChildren();
            if (children.isEmpty()) {
                v = e.getTextNormalize();
            } else {
                v = getChildrenText(children);
            }
            m.put(k, v);
        }
        //关闭流
        in.close();
        return m;
    }


    private static String getChildrenText(List children) {
        StringBuilder sb = new StringBuilder();
        if (!children.isEmpty()) {
            for (Object aChildren : children) {
                Element e = (Element) aChildren;
                String name = e.getName();
                String value = e.getTextNormalize();
                List list = e.getChildren();
                sb.append("<").append(name).append(">");
                if (!list.isEmpty()) {
                    sb.append(getChildrenText(list));
                }
                sb.append(value);
                sb.append("</").append(name).append(">");
            }
        }

        return sb.toString();
    }

    /**
     * 验证回调签名
     *
     * @param map
     * @return
     */
    public static boolean isTenpaySign(Map<String, String> map, String appSecret) {
        String charset = "utf-8";
        String signFromAPIResponse = map.get("sign");
        if (signFromAPIResponse == null || signFromAPIResponse.equals("")) {
            System.out.println("API返回的数据签名数据不存在,有可能被第三方篡改!!!");
            return false;
        }
        System.out.println("服务器回包里面的签名是:" + signFromAPIResponse);
        //过滤空 设置 TreeMap
        SortedMap<String, String> packageParams = new TreeMap<>();
        for (String parameter : map.keySet()) {
            String parameterValue = map.get(parameter);
            String v = "";
            if (null != parameterValue) {
                v = parameterValue.trim();
            }
            packageParams.put(parameter, v);
        }
        StringBuilder sb = new StringBuilder();
        Set es = packageParams.entrySet();
        for (Object e : es) {
            Map.Entry entry = (Map.Entry) e;
            String k = (String) entry.getKey();
            String v = (String) entry.getValue();
            if (!"sign".equals(k) && null != v && !"".equals(v)) {
                sb.append(k).append("=").append(v).append("&");
            }
        }
        sb.append("key=").append(appSecret);
        //将API返回的数据根据用签名算法进行计算新的签名,用来跟API返回的签名进行比较
        //算出签名
        String tobesign = sb.toString();
        String resultSign = Md5Util.md5EncoderByCharset(tobesign, "UTF-8").toUpperCase();
        String tenpaySign = packageParams.get("sign").toUpperCase();
        return tenpaySign.equals(resultSign);
    }
}

位置支付配置

@Configuration
@PropertySource("classpath:wxpay.properties") //读取配置文件
@ConfigurationProperties(prefix = "wxpay") //读取wxpay节点
@Data //使用set方法将wxpay节点中的值填充到当前类的属性中
@Slf4j
/**
 *  微信支付
 * @author xyl
 * @date 2022/7/11 16:59
 * @explain
 */
public class WxPayConfig {

    /**
     * 微信小程序appId
     */
    private String appId;

    /**
     * 微信小程序秘钥
     */
    private String appSmallSecret;

    /**
     * 微信appAppId
     */
    private String appAppId;

    /**
     * 微信appApp秘钥
     */
    private String appAppSecret;

    /**
     * 测试回调
     */
    private String payNotifyTestUrl;

    /**
     * 正式回调
     */
    private String payNotifyUrl;

    /**
     * 商户私钥文件
     */
    private String mchSerialNo;


    /**
     * APIv3密钥
     */
    private String apiV3Key;

    /**
     * 微信服务器地址
     */
    private String domain;

    /**
     * 微信商户号
     */
    private String mchId;

    /**
     * 微信秘钥
     */
    private String mchKey;


    /**
     * 获取商户的私钥文件
     *
     * @return
     */
    public PrivateKey getPrivateKey() {
        InputStream resourceAsStream = this.getClass().getResourceAsStream("/cert/apiclient_key.pem");
        return PemUtil.loadPrivateKey(resourceAsStream);
    }

    /**
     * 获取签名验证器
     *
     * @return
     */
    @Bean
    public ScheduledUpdateCertificatesVerifier getVerifier() {

        log.info("获取签名验证器");

        //获取商户私钥
        PrivateKey privateKey = getPrivateKey();
        System.out.println(privateKey);
        //私钥签名对象
        PrivateKeySigner privateKeySigner = new PrivateKeySigner(mchSerialNo, privateKey);

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

        // 使用定时更新的签名验证器,不需要传入证书
        ScheduledUpdateCertificatesVerifier verifier = new ScheduledUpdateCertificatesVerifier(
                wechatPay2Credentials,
                apiV3Key.getBytes(StandardCharsets.UTF_8));

        return verifier;
    }


    /**
     * 获取http请求对象
     *
     * @param verifier
     * @return
     */
    @Bean(name = "wxPayClient")
    public CloseableHttpClient getWxPayClient(ScheduledUpdateCertificatesVerifier verifier) {

        log.info("获取httpClient");

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

        WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
                .withMerchant(mchId, mchSerialNo, privateKey)
                .withValidator(new WechatPay2Validator(verifier));
        // ... 接下来,你仍然可以通过builder设置各种参数,来配置你的HttpClient

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

        return httpClient;
    }

    /**
     * 获取HttpClient,无需进行应答签名验证,跳过验签的流程
     */
    @Bean(name = "wxPayNoSignClient")
    public CloseableHttpClient getWxPayNoSignClient() {

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

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

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

        log.info("== getWxPayNoSignClient END ==");

        return httpClient;
    }

}

文件存放的位置

 

 

 这个问坑爹的支付整整坑了我一天时间,希望各位不需要经历这种苦难,另外V3支付秘钥需要去微信商户里开通,此方法APP支付和微信小程序已经跑通但是H5还没有对接过,不过JSAPI支付的话与小程序应该差不多

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值