首页 > 其他分享 >SpringBoot集成微信支付0.4.7版本

SpringBoot集成微信支付0.4.7版本

时间:2022-11-01 18:56:58浏览次数:60  
标签:return String 微信 0.4 param private new public SpringBoot

1.引入maven依赖

     <!--wechat支付-->
        <dependency>
            <groupId>com.github.wechatpay-apiv3</groupId>
            <artifactId>wechatpay-apache-httpclient</artifactId>
            <version>0.4.7</version>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.5</version>
        </dependency>

2.微信appid,证书配置信息类(根据自身情况放于yaml或数据库中)

@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class CWechat implements Serializable {

    private static final long serialVersionUID = -4851521781L;

    /**
     * id
     */
    private Long id;

    /**
     * 状态 0:关闭 1:开启
     */
    private Integer status;

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

    /**
     * 微信appid
     */
    private String appId;

    /**
     * 微信密钥
     */
    private String appSecret;

    /**
     * 证书序列号
     */
    private String serialNo;

    /**
     * v3证书密钥
     */
    private String v3Secret;

    /**
     * 私钥证书路径
     */
    private String privateKeyPath;

    /**
     * 商户申请H5时提交的授权域名 例如 http://www.baidu.com
     */
    private String referer;

    /**
     * 支付回调地址
     */
    private String notifyUrl;

    /**
     * 退款回调地址
     */
    private String refundServerUrl;

    /**
     * 创建时间
     */
    private LocalDateTime createTime;

    /**
     * 创建人
     */
    private String createBy;

    /**
     * 最后修改时间
     */
    private LocalDateTime updateTime;

    /**
     * 最后修改人
     */
    private String updateBy;


}

3.发起支付代码(标注部分为关键,也是0.4.7版本简化了对微信平台证书的代码设置。若系统只有一个微信商户号,提出去做成配置类,把生成的httpClient对象注入容器,使用的地方直接注入即可)

    /**
     * 微信官方JSAPI下单接口
     *
     * @param orderNo
     * @param amount
     * @param ip
     * @param goodsName
     * @param wechatConfig
     * @return
     * @throws IOException
     */
    public Map<String, String> createJsApiOrder(String orderNo, Integer amount, String ip, String goodsName, String openid, CWechat wechatConfig) throws Exception {
        //订单金额 单位分
        JSONObject amountObject = new JSONObject();
        amountObject.put("total", amount);
        amountObject.put("currency", "CNY");

        //场景信息
        JSONObject sceneInfoObject = new JSONObject();
        sceneInfoObject.put("payer_client_ip", ip);

        //支付者
        JSONObject payerObject = new JSONObject();
        payerObject.put("openid", openid);

        //参数
        JSONObject paramObject = new JSONObject();
        paramObject.put("appid", wechatConfig.getAppId());
        paramObject.put("mchid", wechatConfig.getMchId());
        paramObject.put("description", goodsName);
        paramObject.put("out_trade_no", orderNo);
        paramObject.put("notify_url", wechatConfig.getNotifyUrl());
        paramObject.put("amount", amountObject);
        paramObject.put("payer", payerObject);
        paramObject.put("scene_info", sceneInfoObject);

        HttpPost httpPost = new HttpPost(JSAPI_REQ_URL);
        httpPost.setHeader("Accept", "application/json");
        StringEntity entity = new StringEntity(paramObject.toJSONString(), "utf-8");
        entity.setContentType("application/json");
        httpPost.setEntity(entity);

        //完成签名并执行请求
        PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(new FileInputStream(wechatConfig.getPrivateKeyPath()));

        CertificatesManager certificatesManager = CertificatesManager.getInstance();
        certificatesManager.putMerchant(wechatConfig.getMchId(), new WechatPay2Credentials(wechatConfig.getMchId(),
                        new PrivateKeySigner(wechatConfig.getSerialNo(), merchantPrivateKey)),
                wechatConfig.getV3Secret().getBytes(StandardCharsets.UTF_8));

        CloseableHttpClient httpClient = WechatPayHttpClientBuilder.create()
                .withMerchant(wechatConfig.getMchId(), wechatConfig.getSerialNo(), merchantPrivateKey)
                .withValidator(new WechatPay2Validator(certificatesManager.getVerifier(wechatConfig.getMchId())))
                .build();

        CloseableHttpResponse response = httpClient.execute(httpPost);
        Map<String, String> retMap = new HashMap<>(2);
        try {
            int statusCode = response.getStatusLine().getStatusCode();
            JSONObject jsonObject = JSON.parseObject(EntityUtils.toString(response.getEntity()));
            Assert.isTrue(statusCode == 200, "微信JSAPI下单请求失败 msg:" + JSON.toJSONString(jsonObject));

            String prepayId = jsonObject.getString("prepay_id");
            Assert.isTrue(StringUtils.isNotBlank(prepayId), "微信JSAPI下单失败");

            Map<String, Object> params = new LinkedHashMap<>();
            params.put("appId", wechatConfig.getAppId());
            params.put("timeStamp", (int) (System.currentTimeMillis() / 1000));
            params.put("nonceStr", RandomUtil.randomString(32));
            prepayId = "prepay_id=" + prepayId;
            params.put("package", prepayId);
            params.put("signType", "RSA");

            String signText = StringUtils.joining("\n", new String[]{
                    String.valueOf(params.get("appId")),
                    String.valueOf(params.get("timeStamp")),
                    String.valueOf(params.get("nonceStr")),
                    String.valueOf(params.get("package"))});


            String paySign = RSA2.sign(signText, merchantPrivateKey, "utf-8");
            params.put("paySign", paySign);

            retMap.put("payInfo", JSON.toJSONString(params));
            retMap.put("param", JSON.toJSONString(paramObject));
            log.info("[微信官方JSAPI] 参数:{}, 响应:{}", JSON.toJSONString(paramObject), JSON.toJSONString(jsonObject));
        } catch (Exception e) {
            throw new BusinessException(e.getMessage());
        } finally {
            response.close();
        }

        return retMap;
    }

3.1 StringUtils工具类

import java.io.UnsupportedEncodingException;

public class StringUtils {
    public StringUtils() {
    }

    public static boolean equals(String str1, String str2) {
        if (str1 == null) {
            return str2 == null;
        } else {
            return str1.equals(str2);
        }
    }

    public static boolean isEmpty(CharSequence cs) {
        return cs == null || cs.length() == 0;
    }

    public static boolean isNotEmpty(CharSequence cs) {
        return !isEmpty(cs);
    }

    public static boolean isBlank(CharSequence cs) {
        int strLen;
        if (cs != null && (strLen = cs.length()) != 0) {
            for (int i = 0; i < strLen; ++i) {
                if (!Character.isWhitespace(cs.charAt(i))) {
                    return false;
                }
            }

            return true;
        } else {
            return true;
        }
    }

    public static boolean isNotBlank(CharSequence cs) {
        return !isBlank(cs);
    }

    public static byte[] getContentBytes(String content, String charset) {
        if (isEmpty(charset)) {
            return content.getBytes();
        } else {
            try {
                return content.getBytes(charset);
            } catch (UnsupportedEncodingException var3) {
                throw new RuntimeException("转码过程中出现错误,指定的编码集不对,您目前指定的编码集是:" + charset);
            }
        }
    }

    public static String tryTrim(String str) {
        return str == null ? null : str.trim();
    }

    public static String joining(String separator, String... str) {
        StringBuilder builder = new StringBuilder();
        String[] var3 = str;
        int var4 = str.length;

        for (int var5 = 0; var5 < var4; ++var5) {
            String s = var3[var5];
            if (null != s) {
                builder.append(s).append(separator);
            }
        }

        return builder.toString();
    }

}

3.3 RSA2工具类

import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;

import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;

/**
 * @author White
 * @date 2022/10/9 15:21
 */
public class RSA2 {

    private static final String SIGN_SHA256RSA_ALGORITHMS = "SHA256WithRSA";


    public static String sign(String content, String privateKey, String characterEncoding) {

        return RSA.sign(content, privateKey, SIGN_SHA256RSA_ALGORITHMS, characterEncoding);
    }


    /**
     * RSA签名
     *
     * @param content           待签名数据
     * @param privateKey        私钥
     * @param characterEncoding 编码格式
     * @return 签名值
     */
    public static String sign(String content, PrivateKey privateKey, String characterEncoding) {
        return RSA.sign(content, privateKey, SIGN_SHA256RSA_ALGORITHMS, characterEncoding);
    }

    /**
     * RSA验签名检查
     *
     * @param content           待签名数据
     * @param sign              签名值
     * @param publicKey         公钥
     * @param characterEncoding 编码格式
     * @return 布尔值
     */
    public static boolean verify(String content, String sign, String publicKey, String characterEncoding) {

        return RSA.verify(content, sign, publicKey, SIGN_SHA256RSA_ALGORITHMS, characterEncoding);
    }


    /**
     * RSA验签名检查
     *
     * @param content           待签名数据
     * @param sign              签名值
     * @param publicKey         公钥
     * @param characterEncoding 编码格式
     * @return 布尔值
     */
    public static boolean verify(String content, String sign, PublicKey publicKey, String characterEncoding) {
        return RSA.verify(content, sign, publicKey, SIGN_SHA256RSA_ALGORITHMS, characterEncoding);
    }

    /**
     * RSA验签名检查
     *
     * @param content           待签名数据
     * @param sign              签名值
     * @param publicKey         公钥
     * @param characterEncoding 编码格式
     * @return 布尔值
     */
    public static boolean verify(String content, String sign, Certificate publicKey, String characterEncoding) {
        PublicKey pubKey = publicKey.getPublicKey();
        return verify(content, sign, pubKey, characterEncoding);
    }

    /**
     * 解密
     *
     * @param content           密文
     * @param privateKey        商户私钥
     * @param characterEncoding 编码格式
     * @return 解密后的字符串
     * @throws GeneralSecurityException 解密异常
     * @throws IOException              解密异常
     */
    public static String decrypt(String content, String privateKey, String characterEncoding) throws GeneralSecurityException, IOException {
        return RSA.decrypt(content, privateKey, characterEncoding);
    }


    /**
     * 得到私钥
     *
     * @param key 密钥字符串(经过base64编码)
     * @return 私钥
     * @throws GeneralSecurityException 加密异常
     */
    public static PrivateKey getPrivateKey(String key) throws GeneralSecurityException {
        return RSA.getPrivateKey(key);
    }

    /**
     * @param content           加密文本
     * @param publicKey         公钥
     * @param cipherAlgorithm   算法
     * @param characterEncoding 编码类型
     * @return 加密后文本
     * @throws GeneralSecurityException 加密异常
     * @throws IOException              IOException
     */
    public static String encrypt(String content, String publicKey, String cipherAlgorithm, String characterEncoding) throws GeneralSecurityException, IOException {
        return Base64.encode(RSA.encrypt(content.getBytes(characterEncoding), RSA.getPublicKey(publicKey), 2048, 11, cipherAlgorithm));
    }

4.1 回调数据封装为类

@Data
public class WechatPayOrderNoticeDTO implements Serializable {

    private String id;

    private String create_time;

    private String resource_type;

    private String event_type;

    private String summary;

    private WechatPayOrderNoticeResourceDTO resource;

}

@Data
public class WechatPayOrderNoticeResourceDTO implements Serializable {

    private String original_type;

    private String algorithm;

    private String ciphertext;

    private String associated_data;

    private String nonce;
}

4.2 回调验签

    @PostMapping("/wechatH5PayBack")
    public ResponseEntity<Map<String, Object>> wechatH5PayBack(HttpServletRequest request) throws Exception {
        BufferedReader br = request.getReader();
        String str;
        StringBuilder body = new StringBuilder();
        while ((str = br.readLine()) != null) {
            body.append(str);
        }

        WechatPayOrderNoticeDTO wechatPayOrderNoticeDTO = JSON.parseObject(body.toString(), WechatPayOrderNoticeDTO.class);

        Map<String, Object> map = new LinkedHashMap<>();
        map.put("code", "SUCCESS");
        map.put("message", "成功");
        ResponseEntity<Map<String, Object>> entity = new ResponseEntity<>(map, HttpStatus.OK);

        if (!"TRANSACTION.SUCCESS".equals(wechatPayOrderNoticeDTO.getEvent_type())) {
            log.info("[微信H5支付] 回调结果为交易失败");
            return entity;
        }

        // 解析支付数据
        String payText = WechatApiV3RSAUtils.decrypt(
                wxMchIdsProperties.getV3Key().getBytes("UTF-8")
                , wechatPayOrderNoticeDTO.getResource().getAssociated_data().getBytes("UTF-8")
                , wechatPayOrderNoticeDTO.getResource().getNonce().getBytes("UTF-8")
                , wechatPayOrderNoticeDTO.getResource().getCiphertext());

        JSONObject payNotice = JSON.parseObject(payText);
//        log.info("[微信H5支付] 支付回调解密数据:{}", JSON.toJSONString(payText));

        String privateSerialNo = "xxx";
        String privateKeyPath = "xxx";
        String v3Key = "xxx";

        PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(new FileInputStream(privateKeyPath));

        // 获取证书管理器实例
        CertificatesManager certificatesManager = CertificatesManager.getInstance();
        // 向证书管理器增加需要自动更新平台证书的商户信息
        certificatesManager.putMerchant(mchId, new WechatPay2Credentials(mchId,
                        new PrivateKeySigner(privateSerialNo, merchantPrivateKey)),
                v3Key.getBytes(StandardCharsets.UTF_8));
        // 从证书管理器中获取verifier
        Verifier verifier = certificatesManager.getVerifier(mchId);

        // 构建req,传入必要参数
        NotificationRequest req = new NotificationRequest.Builder()
                .withSerialNumber(request.getHeader("Wechatpay-Serial"))
                .withNonce(request.getHeader("Wechatpay-Nonce"))
                .withTimestamp(request.getHeader("Wechatpay-Timestamp"))
                .withSignature(request.getHeader("Wechatpay-Signature"))
                .withBody(body.toString())
                .build();

        NotificationHandler handler = new NotificationHandler(verifier, v3Key.getBytes(StandardCharsets.UTF_8));
        // 验签和解析请求体
        Notification notification = handler.parse(req);
        if (Objects.isNull(notification)) {
            log.info("[微信JSAPI支付] notification为空 验签失败");
            throw new BusinessException("微信JSAPI支付回调-验签失败");
        }

        log.info("[微信JSAPI支付] 验签成功 解密后param : " + JSON.toJSONString(payText));

        //业务代码
       

        return entity;
    }

 

标签:return,String,微信,0.4,param,private,new,public,SpringBoot
From: https://www.cnblogs.com/runwithraining/p/16848779.html

相关文章

  • Android开发 对接微信分享SDK总结
    原文:Android开发对接微信分享SDK总结-Stars-One的杂货小窝公司项目需要对接微信分享,本来之前准备对接友盟分享的,但友盟的分享实际参数太多,而我又只需要对接一个微信......
  • springboot + mybatis 框架的搭建
    分享一下搭建框架的心得,有什么不对的地方欢饮大家指正。 下面是pom.xml里面的配置!!!<?xmlversion="1.0"encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/......
  • 安装Ubuntu 18.0.4
     安装后1.设置静态ip与dns   参考链接:https://www.cnblogs.com/youname/p/15704027.html、https://www.cnblogs.com/mouseleo/p/14976527.html   sudovi/e......
  • springBoot+mysql实现用户权限控制--系统框架搭建(四)
    上篇文章说了AOP实现上下文的存储,有需要的可以看看,​​AOP实现上下文存储---系统框架搭建(三)​​环境需求:springboot+mysql5.7.16+Lombok1.18.121、需求背景为了实......
  • 如何在EasyCVR平台配置AI智能识别的微信端告警消息推送?
    我们在此前的文章中和大家分享过关于EasyCVR视频融合平台智能告警相关的开发及功能介绍,其中包括微信端的开发流程分享,感兴趣的用户可以翻阅往期的文章进行了解。智能告警功......
  • 开发微信小程序
    项目部署到线上,连接微信公众号:......
  • h5在其他浏览器打开微信
    目录URLscheme常用的一些URLscheme微信相关URLschemeURLscheme什么是URLscheme?常用的一些URLscheme常用URLscheme微信相关URLschemeweixin://打开微信其......
  • 三 docker安装rabbitMQ之springboot集成stomp,实现前端主动刷新
    一 场景分析对于一些需要数据同步的场景中,例如后台数据有变化,需要程序主动刷新前端界面的数据显示,这样能提供更好的用户数据交互,能第一时间了解到资源信息的变化,而不是......
  • Springboot 自定义注解
    @Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic@interfaceMyAnnotation{Stringkey();}Annotation型定义为 @in......
  • springboot 请求参数 在swagger中 时间戳(Timestamp)被不断下拉展示
     在项目中,请求参数是Timestamp类型的在swagger显示如下: 对应的参数:@ApiModelProperty(value="查询时间-开始yyyy-MM-dd")privateTimestampstartDate;修改成:@A......