微信Native支付V3版本

微信Native支付V3版本

微信支付在开发之前也是需要进行商户接入的

接入文档链接: https://pay.weixin.qq.com/index.php/core/home/login

Native支付介绍
商户系统按微信支付协议生成支付二维码,用户再用微信“扫一扫”完成支付的模式

对应的链接: https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_7_3.shtml
微信提供的文档听详细的,需要准备的东西也有介绍,简单分为以下几种:

  • AppId
  • 商户号(MchId)
  • 商户私钥
  • 证书序列号
  • APIV3秘钥
  • 请求地址

当企业进行商户接入完毕后,就可以得到以上的参数,具体的步骤可以查看上面的文档

第一步

导入依赖

		<!-- 微信支付 -->
        <dependency>
            <groupId>com.github.wxpay</groupId>
            <artifactId>wxpay-sdk</artifactId>
            <version>0.0.3</version>
        </dependency>
        <dependency>
            <groupId>com.github.wechatpay-apiv3</groupId>
            <artifactId>wechatpay-apache-httpclient</artifactId>
            <version>0.2.2</version>
        </dependency>
第二步

将下面的参数写在配置文件中,或者数据库中,看自己需求

  • AppId
  • 商户号(MchId)
  • 商户私钥
  • 证书序列号
  • APIV3秘钥
  • 请求地址
第三步

controller层写自己的需要接受前端给传过来的的参数,根据自己业务整

第四步

就是具体与微信进行交互了,建议抽取成工具类,以便复用
上代码

JSONObject resultObject = new JSONObject();
        
        String zhxsu = EncryptUtil.Base64Decode(boat.getZhxsu());
        try {
            PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(
                    new ByteArrayInputStream("微信私钥".getBytes("utf-8")));

            //使用自动更新的签名验证器,不需要传入证书
            verifier = new AutoUpdateCertificatesVerifier(
                    new WechatPay2Credentials("微信商户id", new PrivateKeySigner("微信证书序列号", merchantPrivateKey)),
                    "微信APIV3密钥".getBytes("utf-8"));

            httpClient = WechatPayHttpClientBuilder.create()
                    .withMerchant("微信商户id", "微信证书序列号", merchantPrivateKey)
                    .withValidator(new WechatPay2Validator(verifier))
                    .build();

            HttpPost httpPost = new HttpPost("微信请求地址");
            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", "微信商户id(mchId)") 
                    .put("appid", "微信appId")  
                    .put("description", "商品描述") 
                    .put("notify_url", "微信回调地址")
                    .put("out_trade_no", "自己生成的订单号") 
                    .put("attach","其他,可以加可不加")
                    .put("time_expire", "过期时间");  
            rootNode.putObject("amount")
                    .put("total","金额");
            objectMapper.writeValue(bos, rootNode);

            httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
            CloseableHttpResponse response = httpClient.execute(httpPost);
            assertTrue(response.getStatusLine().getStatusCode() != 401);
            assertTrue(response.getStatusLine().getStatusCode() != 400);
            HttpEntity entity2 = response.getEntity();
            EntityUtils.consume(entity2);
            JSONObject jsonObject = JSONObject.parseObject(EntityUtils.toString(response.getEntity()));
            resultObject.put("code", 0);
            resultObject.put("data", jsonObject.getString("code_url")); // code_url就是返回的二维码链接
            resultObject.put("message", "成功");
            return resultObject;
        } catch (Exception e) {
            e.printStackTrace();
            log.error("调起微信支付出现问题=>{}", e.getMessage());
            resultObject.put("code", 500);
            resultObject.put("message", "微信支付请求失败");
            return resultObject;
        } finally {
            try {
                httpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
                log.error("微信支付关闭资源报错==>{}", e.getMessage());
            }
        }

做到这一步的时候就可以的到一个二维码链接了
可以把二维码链接给前端,前端生成二维码展示给用户,用户扫描二维码支付成功后,微信会向规定好的回调地址发请求
通知频率为15s/15s/30s/3m/10m/20m/30m/30m/30m/60m/3h/3h/3h/6h/6h - 总计 24h4m

第五步

回调地址开发
controller

    @CrossOrigin
    @ResponseBody
    @RequestMapping("你的微信回调地址")
    public Map weCall(HttpServletRequest request) {
        // 调用支付回调
        return 你的微信回调业务类.你的微信回调业务方法(request);
    }

service
由于代码不少,为了看起来不是那么复杂,我分了三个方法

/**
     * 微信回调处理
     *
     * @return
     */
    @Override
    public Map weCallback(HttpServletRequest request) {
        Map<String, String> resultMap = new HashMap<String, String>();
        try {
            StringBuilder sigStr = new StringBuilder();
            sigStr.append(request.getHeader("Wechatpay-Timestamp")).append("\n");
            sigStr.append(request.getHeader("Wechatpay-Nonce")).append("\n");

            BufferedReader br = request.getReader();
            String str = null;
            StringBuilder builder = new StringBuilder();
            while ((str = br.readLine()) != null) {
                builder.append(str);
            }
            sigStr.append(builder.toString()).append("\n");
            // 验证签名
            if (!signVerify(request.getHeader("Wechatpay-Serial"), sigStr.toString(), request.getHeader("Wechatpay-Signature"))) {
                log.error("微信回调签名错误");
                resultMap.put("code", "FAIL");
                resultMap.put("message", "微信回调签名错误");
                return resultMap;
            }
            // 解密密文
            String resultWeChat = decryptOrder(builder.toString());
            if (StringUtils.isBlank(resultWeChat)) {
                log.error("微信回调解密失败");
                resultMap.put("code", "FAIL");
                resultMap.put("message", "微信回调解密失败");
                return resultMap;
            }
            JSONObject jsonObject = JSONObject.parseObject(resultWeChat);
            // 验证订单
            if ("SUCCESS".equals(jsonObject.getString("trade_state"))) {
            // 拿到自己的订单详情了,可以根据自己的实际业务进操作了
                String outTradeNo = jsonObject.getString("out_trade_no");  // 订单号
                String attach = jsonObject.getString("attach"); // 附加数据
                int intValue = JSONObject.parseObject(jsonObject.getString("amount")).getIntValue("payer_total");
                // 充值金额 微信是以 分 作为计量单位
                String eupj = transition(intValue);

                // 支付完成时间 例如:2015-05-20T13:29:35+08:00表示,北京时间2015年5月20日 13点29分35秒。
                String tepme = DateUtils.weChatDateDispose(jsonObject.getString("success_time"));  // 支付完成时间
                String transactionId = jsonObject.getString("transaction_id");  // 微信流水号

                resultMap.put("code", "SUCCESS");
                resultMap.put("message", "成功");
                return resultMap; // 请不要修改或删除
                // todo 同步成功短信通知
            } else {
                String outTradeNo = jsonObject.getString("out_trade_no");  // 订单号
                log.error("微信回调支付状态不是成功,订单号==>{},订单状态是={}", outTradeNo, jsonObject.getString("trade_state"));
                resultMap.put("code", "FAIL");
                resultMap.put("message", "同步失败");
                return resultMap;
            }
        } catch (IOException e) {
            e.printStackTrace();
            log.error("微信支付回调出现错误==>{}", e.getMessage());
            resultMap.put("code", "FAIL");
            resultMap.put("message", e.getMessage());
            return resultMap;
        }
    }

    /**
     * 微信支付回调签名验证
     */
    private boolean signVerify(String serial, String message, String signature) {
        AutoUpdateCertificatesVerifier verifier = null;
        try {
            PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(
                    new ByteArrayInputStream("微信支付私钥".getBytes("utf-8")));
            // 使用自动更新的签名验证器,不需要传入证书
            verifier = new AutoUpdateCertificatesVerifier(
                    new WechatPay2Credentials("商户id(mch_id)", new PrivateKeySigner("证书序列号", merchantPrivateKey)),
                    "API V3秘钥".getBytes("utf-8"));
            return verifier.verify(serial, message.getBytes("utf-8"), signature);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            log.error("微信支付回调签名验证出现异常=>{}", e.getMessage());
        }
        return false;
    }

    /**
     * 微信支付回调解密
     */
    private String decryptOrder(String body) {
        try {
            AesUtil util = new AesUtil("API V3秘钥".getBytes("utf-8"));
            ObjectMapper objectMapper = new ObjectMapper();
            JsonNode node = objectMapper.readTree(body);
            JsonNode resource = node.get("resource");
            String ciphertext = resource.get("ciphertext").textValue();
            String associatedData = resource.get("associated_data").textValue();
            String nonce = resource.get("nonce").textValue();
            return util.decryptToString(associatedData.getBytes("utf-8"), nonce.getBytes("utf-8"), ciphertext);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("微信支付回调解密出现问题==>{}", e.getMessage());
        }
        return null;
    }

	/**
     * 分转元 保留小数点后两位
     */
    private String transition(int num) {
        BigDecimal bigDecimal1 = new BigDecimal(num + "");
        BigDecimal bigDecima12 = new BigDecimal("100.00");
        return bigDecimal1.divide(bigDecima12).setScale(2).toString();
    }

以上就是发起请求和完成支付的步骤

注意点

  1. 里面返回的格式和"code"中的内容千万不要改变,因为微信就是根据这个进行查看回调结果的,如果微信在24小时内没有收到回调信息的话,会自动退款的
  2. 给微信发送请求生成订单二维码链接时,金额一定要是int类型,而且还是以分为单位,我在下面附上 分转元, 元转分的方法
  3. 使用微信V3支付的话可能会报java.security.InvalidKeyException: Illegal key size这个错误,
    原因:JDK 默认的 Key 长度不支持 256,使用的 AESUtil 是需要256的
    方案:
    去 oracle 官网下载对应jdk版本的 jce_policy ,JDK8 点此下载 http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html,不想注册Oracle,嫌麻烦的,来Q群183579482 的群文件里面下载吧。

下载之后找到你电脑安装的路径 方法也适用Centos),将下载出来的两个jar包解压到里面把之前的jar包覆盖了,就OK;

该解决方案来自于: https://blog.csdn.net/qq_41647999/article/details/109721250

完结,撒花

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lemon20120331

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值