返回顶部
首页 > 资讯 > 后端开发 > JAVA >微信支付总结(JSAPI)(V3)(JAVA)
  • 110
分享到

微信支付总结(JSAPI)(V3)(JAVA)

微信微信开放平台微信公众平台 2023-08-31 21:08:57 110人浏览 薄情痞子
摘要

        前一阵子做了一个微信支付相关的功能,期间走了不少的弯路。在这里给大家趟趟雷,希望大家能因此受益。在这里,我从头到尾一步步给大家捋顺。 目录 一 相关准备  二 开始写代码 三 运行代码 一 相关准备         实现

        前一阵子做了一个微信支付相关的功能,期间走了不少的弯路。在这里给大家趟趟雷,希望大家能因此受益。在这里,我从头到尾一步步给大家捋顺。

目录

一 相关准备

 二 开始写代码

三 运行代码


一 相关准备

        实现微信支付首先得有对应的商户号。商户号是甲方的,但是现实中甲方对这些往往是两眼一抹黑,所以关于申请的工作往往是开发者弄。但是没关系,跟着腾讯提供的引导一步步走就行了。如果材料齐全的话,大约一到两天的时间就会下来。超管的角色默认和甲方提供的法人电话微信进行绑定,如果超管所绑定的微信用户电话号不是法人,则需要上传授权函,授权函的格式在帮助说明中有。

        等到商户号下来之后,有几个东西需要设置,这就和开发有关了。官方对此有比较明确的说明,地址为JSAPI支付-接入前准备 | 微信支付商户平台文档中心。我做的项目jsapi支付类型,他的形式在腾讯支付文档中有详细的说明。主要是需要下面这几个:1 商户号:这个在后台里就能看到。2 appid:这个需要找甲方要。商户号和appid之间是多对多的关系。绑定商户号和appid需要在商户平台进行申请,然后在appid对应的公众平台后台进行同意。3 商户证书序列号。这个商户证书序列号在申请完证书之后就可以看到。在微信支付:API商户证书序列号serialNo获取-YES开发框架网这篇文章中可以看到如何获取,而且你需要有超级管理员权限。4 商户APIV3密钥。这个在文档中如何配置也有明确的说明。

        当你按照文档下载商户证书时,你会得到一个zip文件,解压,你会得到4个文件,一定要保存好。

 二 开始写代码

接下来开始写代码了,整个支付过程中最重要的是4个参数:1商户号,2 商户证书序列号,3 商户APIV3密钥 4 公众号appid,相关代码如下:

package shitang.huidiao;public class WxConfig {  public static final String merchantId = "你的商户号";    public static final String privateKeyPath = "D:/apiclient_key.pem";    public static final String merchantSerialNumber = "xxxxxxx";    public static final String apiV3key = "xxxxxxx";    public static final String appid="xxxxxxx";}

至于这几个参数怎么获取,在百度上一搜就可以搜到。

整个代码根据微信提供的demo写的,比较简单,在这个页面可以看到相关url

开发指引-JSAPI支付 | 微信支付商户平台文档中心

我的代码是由Maven构建的,pom.xml中相关的依赖为

              com.GitHub.wechatpay-apiv3      wechatpay-apache-Httpclient      0.2.2              com.github.wechatpay-apiv3  wechatpay-java  0.2.5

首先是支付界面,是个简单的jsp界面,就是向后台提供支付参数,但是因为是在微信的架构中,所以我仔细说说,代码如下

请选择要充值的金额:
50
100
150
250
500

这个页面的中心逻辑是向后台提供支付参数,后台在接受到支付参数之后,首先先向微信平台发送请求,进行预支付,相关代码如下:

package com.bocomsoft.controller.wxpay;import java.text.SimpleDateFORMat;import java.util.Date;import java.util.stream.Collectors;import java.util.stream.Stream;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.WEB.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.ResponseBody;import org.springframework.web.client.RestTemplate;import org.springframework.web.servlet.ModelAndView;import com.alibaba.fastjson.JSONObject;import com.wechat.pay.java.core.RSAAutoCertificateConfig;import com.wechat.pay.java.service.payments.jsapi.JsapiService;import com.wechat.pay.java.service.payments.jsapi.model.Amount;import com.wechat.pay.java.service.payments.jsapi.model.Payer;import com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest;import com.wechat.pay.java.service.payments.jsapi.model.PrepayResponse;@RequestMapping("/wxpay")@Controllerpublic class WxpayController {private RSAAutoCertificateConfig config;@AutowiredWxService service;//这个是内部业务逻辑,不用管@RequestMapping("/test")@ResponseBodypublic JSONObject test() throws Exception {JSONObject json1 = new JSONObject();json1.put("code", 1);return json1;}@Autowiredpublic void setConfig(){config = new RSAAutoCertificateConfig.Builder().merchantId(WxConfig.merchantId).privateKeyFromPath(WxConfig.privateKeyPath).merchantSerialNumber(WxConfig.merchantSerialNumber).apiV3Key(WxConfig.apiV3key).build();}@RequestMapping("/yuzhifu")@ResponseBodypublic JSONObject yuzhifu(@RequestParam(value = "openid") String openid,@RequestParam(value = "number") String number, @RequestParam(value = "staff_no") String staff_no,@RequestParam(value = "jine") String jine,@RequestParam(value = "NAME") String NAME) throws Exception {Date now=new Date();SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");SimpleDateFormat sdf2=new SimpleDateFormat("yyyy-MM-dd");String shijian=sdf.format(now);String order_no=shijian+number;System.out.println("order_no="+order_no);        JsapiService service = new JsapiService.Builder().config(config).build();// request.setXxx(val)设置所需参数,具体参数可见Request定义PrepayRequest request = new PrepayRequest();Amount amount = new Amount();amount.setTotal((int)(Double.parseDouble(jine)*100));//amount.setTotal(1);request.setAmount(amount);request.setAppid(WxConfig.appid);request.setMchid(WxConfig.merchantId);request.setDescription("食堂账户充值");request.setNotifyUrl("回调url");//这个回调url必须是https开头的request.setOutTradeNo(order_no);Payer payer = new Payer();payer.setOpenid(openid);request.setPayer(payer);PrepayResponse response = service.prepay(request);System.out.println(response.getPrepayId());String prepayid=response.getPrepayId(); String nonceStr = WXPayUtil.generateNonceStr();         long timeStamp = WXPayUtil.getCurrentTimestamp();         String prepayidstr= "prepay_id=" + prepayid;         String signType = "RSA";         String signatureStr = Stream.of(WxConfig.appid, String.valueOf(timeStamp), nonceStr, prepayidstr)                 .collect(Collectors.joining("\n", "", "\n"));        String paySign = WXPayUtil.getSign(signatureStr);                resultjson.put("code", 0);        resultjson.put("appId",WxConfig.appid);        resultjson.put("timeStamp", String.valueOf(timeStamp));        resultjson.put("nonceStr", nonceStr);        resultjson.put("package", prepayidstr);        resultjson.put("signType",signType);        resultjson.put("paySign", paySign);        resultjson.put("openid", openid);return resultjson;}}

这个是签名加密类,代码如下:

package com.bocomsoft.controller.wxpay;import java.io.File;import java.io.FileInputStream;import java.io.IOException;import java.io.InputStream;import java.net.URISyntaxException;import java.NIO.charset.StandardCharsets;import java.security.InvalidKeyException;import java.security.NoSuchAlGorithmException;import java.security.PrivateKey;import java.security.SecureRandom;import java.security.Signature;import java.security.SignatureException;import java.util.Random;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.util.Base64Utils;import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;public class WXPayUtil {private static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";     private static final Random RANDOM = new SecureRandom();      public static String getSign(String signatureStr) throws InvalidKeyException, NoSuchAlgorithmException, SignatureException, IOException, URISyntaxException {        //replace 根据实际情况,不一定都需要        //String replace = privateKey.replace("\\n", "\n");      File file = new File(WxConfig.privateKeyPath);                InputStream in = new FileInputStream(file);        PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(in);           Signature sign = Signature.getInstance("SHA256withRSA");        sign.initSign(merchantPrivateKey);        sign.update(signatureStr.getBytes(StandardCharsets.UTF_8));        return Base64Utils.encodeToString(sign.sign());    }         public static String generateNonceStr() {        char[] nonceChars = new char[32];        for (int index = 0; index < nonceChars.length; ++index) {            nonceChars[index] = SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length()));        }        return new String(nonceChars);    }          public static Logger getLogger() {        Logger logger = LoggerFactory.getLogger("wxpay java sdk");        return logger;    }         public static long getCurrentTimestamp() {        return System.currentTimeMillis()/1000;    }         public static long getCurrentTimestampMs() {        return System.currentTimeMillis();    }    public static void main(String[] args) {    String signatureStr = "ssssss";        try {           String paySign = WXPayUtil.getSign(signatureStr);           System.out.println("paySign="+paySign);        } catch (Exception e) {            e.printStackTrace();        }}}

大家注意啊,坑人的地方主要两个地方,首先是签名方法,我才疏学浅,在官网上看得脑瓜都大了,没看明白,感谢伟大的互联网,让我找到了加密方法,先向大神表示感谢,再贴出原文的链接,供大家参考:微信支付V3 JSAPI签名_jsapi v3 签名_Coco_淳的博客-CSDN博客。第二个就是这个,大家注意到这了吗

 @Autowired
    public void setConfig(){
        config = new RSAAutoCertificateConfig.Builder().merchantId(WxConfig.merchantId).privateKeyFromPath(WxConfig.privateKeyPath)
                .merchantSerialNumber(WxConfig.merchantSerialNumber).apiV3Key(WxConfig.apiV3key).build();
    }

官网提供的demo是一个main函数,直接就执行下去了,但是在java后端的情况下,他会报错,The corresponding provider for the merchant already exists 大致的意思是apiV3的RSAConfig重复build,所以要在Controller中要以单例的方式实现,原文的参考链接如下:微信支付apiV3异常:The corresponding provider for the merchant already exists_迪八戈的博客-CSDN博客。当代码写到这一步了,用户在客户端也进行完支付了。然后进入下个阶段,这个阶段是微信平台向先前预支付设置的地址发送加了密的post请求,当我们接受到请求之后,首先是解密,然后是进行相关的业务内部运行逻辑。代码如下:

package shitang.huidiao;import java.text.SimpleDateFormat;import java.util.Date;import java.util.HashMap;import java.util.List;import java.util.Map;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestHeader;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import org.springframework.web.client.RestTemplate;import com.alibaba.fastjson.JSONObject;import com.wechat.pay.java.core.RSAAutoCertificateConfig;import com.wechat.pay.java.core.notification.NotificationConfig;import com.wechat.pay.java.core.notification.NotificationParser;import com.wechat.pay.java.core.notification.RequestParam;@RestController@RequestMapping("/huidiao")public class HuidiaoController {NotificationConfig config;@Autowiredpublic void setConfig(){config = new RSAAutoCertificateConfig.Builder()        .merchantId(WxConfig.merchantId)        .privateKeyFromPath(WxConfig.privateKeyPath)        .merchantSerialNumber(WxConfig.merchantSerialNumber)        .apiV3Key(WxConfig.apiV3key)        .build();}@AutowiredHuidiaoService service;@PostMapping(value = "/wxAppPayNotify")public String wxAppPayNotify( @RequestHeader("Wechatpay-Serial") String wechatPayCertificateSerialNumber,  @RequestHeader("Wechatpay-Signature") String signature,  @RequestHeader("Wechatpay-Timestamp") String timstamp,  @RequestHeader("Wechatpay-Nonce") String nonce,  @RequestBody String requestBody)  {RequestParam requestParam = new RequestParam.Builder()        .serialNumber(wechatPayCertificateSerialNumber)        .nonce(nonce)        .signature(signature)        .timestamp(timstamp)// 若未设置signType,默认值为 WECHATPAY2-SHA256-RSA2048                .body(requestBody)        .build();// 初始化 NotificationParserNotificationParser parser = new NotificationParser(config);// 验签并解密报文JSONObject decryptObject = parser.parse(requestParam,JSONObject.class);System.out.println("decryptObject="+decryptObject.toJSONString());String trade_state=decryptObject.getString("trade_state");JSONObject jsonResponse = new JSONObject();if(trade_state.equals("SUCCESS")) {//各种业务逻辑}else{            //还是各种业务逻辑        }jsonResponse.put("code", "SUCCESS");jsonResponse.put("message", "成功");return jsonResponse.toJSONString();}}

三 运行代码

该打包打包,该运行就运行,没啥说的,但是如果你的jdk版本是8,就会出现问题,(我的本机的jdk版本是11,是否会出现问题待验证,但是服务器的jdk版本是8,不能轻易去动)。

错误出现场景:用户在微信公众号里面发送一条消息后,接口收到的加密数据,在解密的时候报错。

错误内容:java.security.InvalidKeyException: Illegal key size

错误原因描述:JRE本身中自带的“local_policy.jar ”和“US_export_policy.jar”只支持128位密钥的加密算法

需要替换文件,原文链接如下:https://www.cnblogs.com/fswhq/p/16046179.html

最后希望大家共同进步。

来源地址:https://blog.csdn.net/ZX5191/article/details/130495972

--结束END--

本文标题: 微信支付总结(JSAPI)(V3)(JAVA)

本文链接: https://lsjlt.com/news/387142.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作