实现APP微信支付之Java服务端开发[通俗易懂]

实现APP微信支付之Java服务端开发[通俗易懂]首先要申请微信的应用,在微信开放平台https://open.weixin.qq.com/中建立移动应用,并在“接口信息”里面开通“微信支付”。

欢迎大家来到IT世界,在知识的湖畔探索吧!

首先要申请微信的应用,在微信开放平台https://open.weixin.qq.com/中建立移动应用,并在“接口信息”里面开通“微信支付”。

实现APP微信支付之Java服务端开发[通俗易懂]

随后,把api证书、apiv3密钥申请好并把回调地址设置好

实现APP微信支付之Java服务端开发[通俗易懂]

实现APP微信支付之Java服务端开发[通俗易懂]

接下来直接上代码!

这里是用maven引用微信支付的依赖

        <!-- 微信支付 -->
        <dependency>
            <groupId>com.github.wechatpay-apiv3</groupId>
            <artifactId>wechatpay-apache-httpclient</artifactId>
            <version>0.4.4</version>
        </dependency>

欢迎大家来到IT世界,在知识的湖畔探索吧!

这里是获取预支付交易会话标识的代码部分

欢迎大家来到IT世界,在知识的湖畔探索吧!    private  CloseableHttpClient httpClient;
    private  AutoUpdateCertificatesVerifier verifier;

    @Autowired
    private WxPayConfigServiceImpl wxPayConfigService;

    public void setup(String mchId, String appV3Key, String serialNo, String keyPath, String keyAlias) {
        try {
            KeyPair keyPair = WXKeyPairFactory.createPKCS12(keyPath, keyAlias, mchId);
            PrivateKey merchantPrivateKey = keyPair.getPrivate();
            //使用自动更新的签名验证器,不需要传入证书
            verifier = new AutoUpdateCertificatesVerifier(
                    new WechatPay2Credentials(mchId, new PrivateKeySigner(serialNo, merchantPrivateKey)),
                    appV3Key.getBytes(StandardCharsets.UTF_8));

            httpClient = WechatPayHttpClientBuilder.create()
                    .withMerchant(mchId, serialNo, merchantPrivateKey)
                    .withValidator(new WechatPay2Validator(verifier))
                    .build();
        } catch (Exception e) {
            System.out.println(e.getMessage());
            e.printStackTrace();
        }
    }

    /**
     * 微信APP支付
     *
     * @param desc 订单描述
     * @param outTradeNo 订单号
     * @param total 金额(单位为分)
     * @return Client
     * @throws Exception
     */
    @Override
    public String createOrder(String desc, String outTradeNo, int total) throws Exception {
        // 用于判断商户平台或商家服务
        String appId = "";// 应用ID
        String mchId = "";// 商户号
        String serialNo = "";// 商户证书序列号
        String appV3Key = "";// API V3密钥
        String keyPath = "";// 商家平台证书路径
        String keyAlias = "Tenpay Certificate";// 证书的别名,这里是固定值
        String notifyUrl = "";// 回调地址

        setup(mchId, appV3Key, serialNo, keyPath, keyAlias);
        try {
            HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/app");
            httpPost.addHeader("Accept", "application/json");
            httpPost.addHeader("Content-type", "application/json; charset=utf-8");

            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectMapper objectMapper = new ObjectMapper();

            ObjectNode rootNode = objectMapper.createObjectNode();
            rootNode.put("mchid", mchId)
                    .put("appid", appId)
                    .put("notify_url", notifyUrl)
                    .put("description", desc)
                    .put("out_trade_no", outTradeNo);
            rootNode.putObject("amount")
                    .put("total", total);

            objectMapper.writeValue(bos, rootNode);

            httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
            CloseableHttpResponse response = httpClient.execute(httpPost);
            String bodyAsString = EntityUtils.toString(response.getEntity());

            JSONObject jsonObject = JSON.parseObject(bodyAsString);

            return (String) jsonObject.get("prepay_id");
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

生成签名部分

    /**
     * APP支付 V3  SHA256withRSA 签名.
     *
     * @param AppId        应用id
     * @param timestamp    当前时间戳   因为要配置到TOKEN 中所以 签名中的要跟TOKEN 保持一致
     * @param nonceStr     随机字符串  要和TOKEN中的保持一致
     * @param prepayId         预支付交易会话ID
     * @param mchId         商户号
     * @param keyPath       支付证书
     * @param keyAlias      证书的别名
     * @return the string
     */
    @SneakyThrows
    public static String generateAPPSignRSA(String AppId, long timestamp, String nonceStr, String prepayId,String mchId, String keyPath, String keyAlias)  {
        KeyPair keyPair = WXKeyPairFactory.createPKCS12(keyPath, keyAlias, mchId);
        String signatureStr = Stream.of(AppId, String.valueOf(timestamp), nonceStr, prepayId)
                .collect(Collectors.joining("\n", "", "\n"));
        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(keyPair.getPrivate());
        sign.update(signatureStr.getBytes(StandardCharsets.UTF_8));
        return Base64Utils.encodeToString(sign.sign());
    }

回调验证部分代码

欢迎大家来到IT世界,在知识的湖畔探索吧!    // 微信支付回调
    @PostMapping("/notify")
    public Map<String, String> wechatCallback(HttpServletRequest request, HttpServletResponse response) {
        Map<String, String> map = new HashMap<>(2);
        try {
            //微信返回的请求体
            String body = WXPayUtil.getRequestBody(request);
            //如果验证签名序列号通过
            if (wxAppPayService.verifiedSign(request, body)) {
                //微信支付通知实体类
                WxPayNotify payNotify = JSONObject.parseObject(body, WxPayNotify.class);
                //如果支付成功
                if ("TRANSACTION.SUCCESS".equals(payNotify.getEventType())) {
                    //通知资源数据
                    WxResourceVo resource = payNotify.getResource();
                    //解密后资源数据
                    String apiV3Key = wxPayConfigService.selectWechatMerchantApiV3Key();
                    String notifyResourceStr = WXPayUtil.decryptResponseBody(resource.getAssociatedData(), resource.getNonce(), resource.getCiphertext(), apiV3Key);
                    //通知资源数据对象
                    WxNotifyResourceVo notifyResourceVo = JSONObject.parseObject(notifyResourceStr, WxNotifyResourceVo.class);
                    String outTradeNo = notifyResourceVo.getOutTradeNo();
                    String transactionId = notifyResourceVo.getTransactionId();
                    //逻辑实现
                    //do something
                    int res = 1;
                    if (res == 1){
                        //通知微信正常接收到消息,否则微信会轮询该接口
                        map.put("code", "SUCCESS");
                        map.put("message", "成功");
                    }else{
                        map.put("code", "FAIL");
                        map.put("message", "失败");
                        response.setStatus(416);
                    }
                } else {
                    map.put("code", "FAIL");
                    map.put("message", "失败");
                    response.setStatus(416);
                }
                //通知微信正常接收到消息,否则微信会轮询该接口
                return map;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        map.put("code", "FAIL");
        map.put("message", "失败");
        response.setStatus(416);
        return map;
    }

验证部分代码

    /**
     * 验签
     * @param request request请求
     * @param body 微信返回请求体
     * @return true,false
     */
    @Override
    public boolean verifiedSign(HttpServletRequest request,String body){
        boolean result = false;
        try{
            String appId = "";// 应用ID
            String mchId = "";// 商户号
            String serialNo = "";// 商户证书序列号
            String appV3Key = "";// API V3密钥
            String keyPath = "";// 商家平台证书路径
            String keyAlias = "Tenpay Certificate";// 证书的别名,这里是固定值

            //微信返回的证书序列号
            String resSerialNo = request.getHeader("Wechatpay-Serial");
            //微信返回的随机字符串
            String nonceStr = request.getHeader("Wechatpay-Nonce");
            //微信返回的时间戳
            String timestamp = request.getHeader("Wechatpay-Timestamp");
            //微信返回的签名
            String wechatSign = request.getHeader("Wechatpay-Signature");

            KeyPair keyPair = WXKeyPairFactory.createPKCS12(keyPath, keyAlias, mchId);
            PrivateKey merchantPrivateKey = keyPair.getPrivate();

            //组装签名字符串
            String signStr = Stream.of(timestamp, nonceStr, body)
                    .collect(Collectors.joining("\n", "", "\n"));
            //使用自动更新的签名验证器,不需要传入证书
            verifier = new AutoUpdateCertificatesVerifier(
                    new WechatPay2Credentials(mchId, new PrivateKeySigner(serialNo, merchantPrivateKey)),
                    appV3Key.getBytes(StandardCharsets.UTF_8));
            result = verifier.verify(resSerialNo, signStr.getBytes(StandardCharsets.UTF_8), wechatSign);
        }catch (Exception e){
            e.printStackTrace();
            System.out.println("e:" + e);
        }
        return result;
    }

封装的函数

    /**
     * 获取请求报文
     * @param request
     * @return
     * @throws IOException
     */
    public static String getRequestBody(HttpServletRequest request) throws IOException {
        ServletInputStream stream = null;
        BufferedReader reader = null;
        StringBuffer sb = new StringBuffer();
        try {
            stream = request.getInputStream();
            // 获取响应
            reader = new BufferedReader(new InputStreamReader(stream));
            String line;
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
        } catch (IOException e) {
            throw new IOException("读取返回支付接口数据流出现异常!");
        } finally {
            reader.close();
        }
        return sb.toString();
    }

解码部分代码



    /**
     * 解密
     * @param associatedData 附加数据包
     * @param nonce 加密使用的随机串
     * @param ciphertext Base64编码后的密文
     * @param apiV3Key       支付apiV3秘钥
     * @return
     */
    public static String decryptResponseBody(String associatedData, String nonce, String ciphertext, String apiV3Key) {
        try {
            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
            SecretKeySpec key = new SecretKeySpec(apiV3Key.getBytes("UTF-8"), "AES");
            GCMParameterSpec spec = new GCMParameterSpec(128, nonce.getBytes("UTF-8"));
            cipher.init(Cipher.DECRYPT_MODE, key, spec);
            cipher.updateAAD(associatedData.getBytes("UTF-8"));
            byte[] bytes;
            try {
                bytes = cipher.doFinal(Base64Utils.decodeFromString(ciphertext));
            } catch (GeneralSecurityException e) {
                throw new IllegalArgumentException(e);
            }
            return new String(bytes, StandardCharsets.UTF_8);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://itzsg.com/17551.html

(0)

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

联系我们YX

mu99908888

在线咨询: 微信交谈

邮件:itzsgw@126.com

工作时间:时刻准备着!

关注微信