当前位置:   article > 正文

java对接微信支付api3心得(小白易懂)_wechatpay-java

wechatpay-java

        公司做的项目中,需要支付功能,因为做的微信小程序项目,所以直接就用微信支付了,以前我也对接过微信支付,但以前没有java的sdk,并且还是用的xml报文,我们还得解析xml才行,所以麻烦的很,这次对接突然发现,微信已经提供了好多中语言的sdk,其中也包括了Java的sdk,下载地址在微信支付文档里面也有。

微信支付JSAPI文档

微信支付SDK下载指引地址​​​​​​​

一、阅读文档方法指引

        因为我自己也是从一开始什么都不懂到处百度过来的,所以懂得那种感受,这篇文章很基础很基础,先从微信支付文档开始介绍。

        对接第三方的功能,首先会看文档很重要,但就拿微信支付来说,它提供的功能太多了,各种各样,什么jsapi,什么扫码支付,什么app支付等等,太多了,不知道从哪里说起,所以我这里简单介绍一下如何阅读。

       

 如上图,先关注导航条,你直接看文档肯定有点懵,所以我们先找到导航条的指引文档,指引文档如下:

       

如图所见,又是巴拉巴拉一大堆,如果看着麻烦,可以点进去具体看下是什么:

        这个其实解释已经比较清晰了,应用场景和图片都已经描述出来了,只要耐心理解,迟早会明白什么意思的,还不明白,那就说明经验可能还不够,很多东西没有见过或者做过,这就没办法了,只能一步步的摸索,支付场景做的多了,就知道什么意思了。

       

当然,我这里提供一个思路,实在不明白,可以先不管文档,先看自己的需求是什么,例如,我现在的需求就是在微信小程序上实现微信支付,然后我看哪个最像我需要的,根据侧边栏的菜单,一看就找到了小程序支付几个字。

        然后专门看自己需要的文档,仔细理解,从产品介绍,到接入前准备,跟着文档一步步做就是了,这样就不会因为内容太多而崩溃了,实在无法理解的地方,可以先放下,不要进死胡同了。 

        这里需要提醒的是,如果啥都不了解的情况下,一定要耐心看完这些基础文档指引,对于不影响自己对接的地方如果实在不理解可以先不管它,然后不用急着实际对接,把一些准备工作做好就可以了。

        另外,切记,对于新手来说,如果觉得阅读文档费劲的话,先不急着尝试到处翻,因为翻多了你看不懂,反而因为一开始的理解的东西因为翻到其他地方的东西而混淆,等对接差不多了,可以多看看。

        然后,下面是一个比较重要的地方,如果不太懂的,我这里稍作解释,图如下:

        这个图没有截全,如果想看全部的,去开发指引目录下看原图。

        这是一个微信支付业务流程图,也就是描述从用户发起支付请求到获取支付结果的整个过程细节图,这个图很重要,新手看不太懂的话这里必须解释一下,不然没法对接。

        一般支付流程都大差不差,简单来说步骤如下:

  1. 用户触发支付请求
  2. 自己服务端接收请求后,调用第三方支付平台(这里是微信)提供的下单接口
  3. 第三方支付平台会返回一些东西给你(具体是什么跟平台和支付方式有关,扫码支付可能是一个字符串还是什么,忘记了,可以给你生成二维码,用户扫二维码就可以支付,还有一些第三方支付平台会返回html给你,之前对接过一个台湾的第三方支付,让你展示一个支付页面,然后跳转到他们的指定页面)
  4. 你拿到第三方支付平台返回的东西给你后,需要根据实际情况处理一下,例如上面举例,扫码支付要生成二维码(没记错大概就是这样,这个也不重要,大概流程就是这样子)再例如上面的流程图,它要你把一些参数进行签名,然后返回前端。
  5. 前端拿着你签名的数据和明文数据调用微信提供的函数(具体情况有可能是app或者h5页面,他们也有微信的sdk的),然后就能触发微信的支付页面。
  6. 用户支付完毕后,第三方支付平台会给你指定的回调接口发送请求,这就需要你提供一个接口,让他们调用就可以了,先理解这个概念就行。(至于上图里面有一些鉴权、提交授权等等,跟我们没关系,前端sdk跟微信平台调用,不需要我们做什么)
  7. 如果微信调用你提供的接口,调不通(网络、接口报错等),它会根据固定规则重复调用的,具体后面会讲。这个时候可以调用第三方支付提供的查询订单支付结果接口,主动查询支付结果。

        差不多就是上面步骤吧,如果上面解释的太细看不清晰的话,再次简单描述一下:

  1. 用户触发支付请求
  2. 调三方下单接口
  3. 处理第三方下单接口返回数据,然后给前端
  4. 前端触发支付页面,用户确认支付及输入密码
  5. 服务端回调接口接收支付结果,根据支付结果处理订单后续逻辑
  6. 如果长时间未收到支付结果,主动调用第三方支付接口查询结果。

二、部分前置说明

        对接支付里面最麻烦的就是各种加密解密,签名验签,因为如果是明文传输的话,安全性堪忧,甚至网络封包被拦截或者篡改的话,你拿到的数据都不一定是正确的,而这,在支付领域是不允许的,比如用户支付的时候取消了,然后伪造一个请求,发给你的回调接口,你收到后二话不说就将订单处理成已经支付,这就玩大了。

        所以一般都会对数据进行签名或者加密,现在一般都是私钥签名,公钥验签,而且是成对出现的,这个成对出现的很重要,一定要明白,也就是说,一个公钥和一个私钥加起来是一对,我们和第三方支付平台分别拥有其中一对,我用自己一对里面的私钥对数据进行签名,平台用我们一对里面的公钥验签,同样,我们想要验签平台给我们的签名数据,则需要用平台的公钥验签。

        简单说,就是我们需要用到两个东西,一个是平台的公钥,用来验签平台返回给我们的数据是否正确(没有被篡改),另外一个是自己的私钥,用来对数据进行签名。(而这两个东西,具体来说就是微信文档里面说的证书和私钥)

        平台也一样,它有自己的私钥,也有我们的公钥。

上面是微信文档里面的描述,实在不理解证书的,可以把证书当成我上面说的公钥。

签名和验签使用场景:

        你请求第三方支付接口,需要对数据进行签名(用你自己的私钥签名),好让第三方支付平台确认数据真实性以及你的身份,当第三方支付接口给你返回数据或者接收第三方回调请求时,你需要对数据进行验签(用平台的证书验签)。

三、java对接微信小程序支付对接流程以及碰到的问题

        该说不说,之前就有大佬提醒过我,虽然微信支付流程以及很完善和流行了,但实际对接还是会有很多坑,一开始我来楞了下,但实际对接的确花了我很多心思,我采用的方式是用的javaSDK对接,毕竟微信都提供了,肯定方便快捷。

        于是根据指引找到了javaSdk的maven的地址。

        微信官方推荐的sdk地址(gitHub)

sdk的maven坐标:

  1. <dependency>
  2. <groupId>com.github.wechatpay-apiv3</groupId>
  3. <artifactId>wechatpay-java</artifactId>
  4. <version>0.2.10</version>
  5. </dependency>

       

这里需要再次提醒一下,养成看文档的好习惯,sdk有说明文件的。

         

        这里具体就一个个解释了,大家可以看看,也花不了几个时间,另外附带一下微信官方文档相关的地址:

微信小程序支付对接官方文档api接口

        在上文第一大点的最后我也简单描述了对接支付的大体流程,所以这里就按照流程对接就行了,先找到预支付(预下单)接口文档,当然,既然我们使用了sdk,就不需要关注太多细节了,直接就按照sdk的说明文件操作就行了。

 如果sdk的描述文件不知道从哪里下手的话,老规矩我描述一下大概流程:

  1. 构建Config配置类,这个类里面会加载你自己的私钥和平台的公钥还有加解密等等对象,官方推荐我们用构建者获取这个对象,明显意思就是不需要重复创建的对象,所以直接扔给spring作为单例对象管理就好了。
  2. 获取具体请求的执行器(或者说调用者,执行器是我自己取的名字),官方sdk对每个接口的请求做了封装,让我们不需要关注如何调用http,如何加密解密签名验签,这些东西全都由这个执行器里面的方法处理,并且每个方法对应相应第三方接口,我要调用某个接口,直接用执行器对应方法即可。(这里要注意的是,微信执行器是根据功能划分的,每个功能对应一个执行器,比如小程序支付是用的jsApi的执行器,扫码支付用的Native的执行器等)
  3. 用对应的执行器执行对应的方法,即可调用对应的三方接口,只需要自己根据方法需要的形参组装好参数就可以了,其他的都不用管,具体看示例一目了然。
  4. 先调用预下单接口,然后拿到返回值,根据文档组装参数并进行签名(签名文档
  5. 然后用微信开发者工具,根据文档使用对应的函数,函数传递的参数就是第4步组装的参数,文档里面有示例的,实在不行看我下面的截图。
  6. 将服务端签名好的数据一个个拷贝在前端的函数里面,然后点击真机调试,它会自动生成一个二维码,你扫码支付就能测试(不要管二维码怎么来的,因为这是测试有的东西,你扫码支付测试就完了)

        

这里附上一开始我对接的代码,因为可能有些细节对小白来说还是需要再去百度的,比如构建的时候需要私钥文件的路径,如何获取到路径:

  1. static {
  2. ClassPathResource keyClassPath = new ClassPathResource("/wechatpay/apiclient_key.pem");//获取到resources路径下面的文件信息,包含了文件的path
  3. try {
  4. URL keyUrl = keyClassPath.getURL();
  5. privateKeyPath = keyUrl.getPath();
  6. } catch (IOException e) {
  7. e.printStackTrace();
  8. }
  9. config = new RSAAutoCertificateConfig.Builder()
  10. .merchantId(merchantId)
  11. .privateKeyFromPath(privateKeyPath)
  12. .merchantSerialNumber(merchantSerialNumber)
  13. .apiV3Key(apiV3key)
  14. .build();
  15. service = new JsapiServiceExtension.Builder().config(config).build();
  16. signer = config.createSigner();
  17. }

私钥文件我放在这里:

 

        到这里,一开始我也是按照描述文件的示例使用,测试通过没啥问题(对接文档千万不要着急想着优化,先用最简单粗暴的方式调通后,再考虑优化比如把config交给spring等),但后面写完回调接口,想要测试回调是否正常时,直接傻眼了,因为构建cinfig报错了,它找不到路径。

微信官方文档描述回调文档

微信滚放文档描述回调2

        这里原因我百度了很久,得到的结果应该是我把项目打成jar包了,私钥文件也就在jar包里面,然后部署到linux上之后,这个jar包里面的文件路径就是虚拟路径了,并不真实存在磁盘某个路径中,所以会找不到这个路径,而sdk原本提供的构建类RSAAutoCertificateConfig.Builder它只能接受用绝对路径加载私钥文件,这下就尴尬了。

        当然,这个其实也好解决,你直接在linux的某个目录下新建一个文件夹,然后把私钥文件上传上去,然后代码里面写死,先调通再说也行。

        但这终究是一个问题,因为写死是硬编码,不利于维护和拓展,所以我就一直在看sdk的源码,想要看看有没有办法在sdk的基础上拓展一下,但sdk关键地方有限制,比如构建类用final修饰了,我没法继承,然后也没有看到能支持扩展的适配器模式。

        当然,说拓展前,我得先说一下怎么拓展,我看了源码,因为它是通过FileInputStream通过指定路径去加载文件的,所以它会找不到这个路径,但我测试发现,找不到路径,但获取ClassPathResource对象的时候并不会报错,也就是说,我还是能找到这个文件的,只是找不到目录而已,并且,ClassPathResource还能直接获取到这个文件的输入流,所以这下就好办了,sdk的构建类只能通过传入路径加载私钥,我拓展一下,直接传入输入流,然后你读取不就好了,干嘛费劲的再去找目录。

       于是,我就自己创建了一个类,然后把sdk里面的构建config类里面的源码全部拷贝一份(不让我继承,我直接全部拷贝不就行了),当然,拷贝谁是有讲究的,sdk提供了两个构建类,一个是自动下载平台证书(平台公钥,可以验签)的,上面的代码我使用的就是它,还有一个从本地加载平台证书(这个就需要自己调用平台接口获取平台证书了,注意,这里是平台证书,不是你自己的证书),这里肯定优先选自动下载的啊,不用老是自己去维护,sdk对这里的相关描述如下图:

         

        找到了可以拷贝的对象,我就拷贝代码放入自己定义的构建类里面,然后在里面写一个方法,参数就是InputStream(输入流),至于拿到输入流怎么处理,这就好办了,原本它怎么处理我就怎么处理。

四、直接上代码(不想费那么多事直接看这里)

        其实写到这里,突然发现,我很想把所有事情表达清楚细节,但好像没办法,反而可能因为描述太多而难以理解,所以好像很多东西还真得一步步慢慢积累,没办法一步到位解释清楚,就像上面说的东西,没有对linux了解,没有对spring了解,没有对io流了解,好像还是有很多东西不懂,但如果对于懂这些的人来说,又没必要说的这么详细,但写都写了,只好强撑着写下去了。

我自己定义的构建类,可以用输入流加载私钥文件:

  1. import com.wechat.pay.java.core.AbstractRSAConfig;
  2. import com.wechat.pay.java.core.AbstractRSAConfigBuilder;
  3. import com.wechat.pay.java.core.certificate.CertificateProvider;
  4. import com.wechat.pay.java.core.certificate.RSAAutoCertificateProvider;
  5. import com.wechat.pay.java.core.cipher.AeadAesCipher;
  6. import com.wechat.pay.java.core.cipher.AeadCipher;
  7. import com.wechat.pay.java.core.cipher.RSAVerifier;
  8. import com.wechat.pay.java.core.cipher.Verifier;
  9. import com.wechat.pay.java.core.http.AbstractHttpClientBuilder;
  10. import com.wechat.pay.java.core.http.HttpClient;
  11. import com.wechat.pay.java.core.notification.NotificationConfig;
  12. import com.wechat.pay.java.core.util.IOUtil;
  13. import com.wechat.pay.java.core.util.PemUtil;
  14. import java.io.IOException;
  15. import java.io.InputStream;
  16. import java.nio.charset.StandardCharsets;
  17. import java.security.PrivateKey;
  18. import static com.wechat.pay.java.core.notification.Constant.AES_CIPHER_ALGORITHM;
  19. import static com.wechat.pay.java.core.notification.Constant.RSA_SIGN_TYPE;
  20. import static java.util.Objects.requireNonNull;
  21. public class MyRSAAutoCertificateConfig extends AbstractRSAConfig
  22. implements NotificationConfig {
  23. private final CertificateProvider certificateProvider;
  24. private final AeadCipher aeadCipher;
  25. private MyRSAAutoCertificateConfig(Builder builder) {
  26. super(
  27. builder.merchantId,
  28. builder.privateKey,
  29. builder.merchantSerialNumber,
  30. builder.certificateProvider);
  31. this.certificateProvider = builder.certificateProvider;
  32. this.aeadCipher = new AeadAesCipher(builder.apiV3Key);
  33. }
  34. /**
  35. * 获取签名类型
  36. *
  37. * @return 签名类型
  38. */
  39. @Override
  40. public String getSignType() {
  41. return RSA_SIGN_TYPE;
  42. }
  43. /**
  44. * 获取认证加解密器类型
  45. *
  46. * @return 认证加解密器类型
  47. */
  48. @Override
  49. public String getCipherType() {
  50. return AES_CIPHER_ALGORITHM;
  51. }
  52. /**
  53. * 创建验签器
  54. *
  55. * @return 验签器
  56. */
  57. @Override
  58. public Verifier createVerifier() {
  59. return new RSAVerifier(certificateProvider);
  60. }
  61. /**
  62. * 创建认证加解密器
  63. *
  64. * @return 认证加解密器
  65. */
  66. @Override
  67. public AeadCipher createAeadCipher() {
  68. return aeadCipher;
  69. }
  70. public static class Builder extends AbstractRSAConfigBuilder<Builder> {
  71. protected HttpClient httpClient;
  72. protected byte[] apiV3Key;
  73. protected CertificateProvider certificateProvider;
  74. protected AbstractHttpClientBuilder<?> httpClientBuilder;
  75. protected String merchantId;
  76. protected PrivateKey privateKey;
  77. protected String merchantSerialNumber;
  78. public Builder apiV3Key(String apiV3key) {
  79. this.apiV3Key = apiV3key.getBytes(StandardCharsets.UTF_8);
  80. return self();
  81. }
  82. public Builder httpClient(HttpClient httpClient) {
  83. this.httpClient = httpClient;
  84. return this;
  85. }
  86. public Builder httpClientBuilder(AbstractHttpClientBuilder<?> builder) {
  87. httpClientBuilder = builder;
  88. return this;
  89. }
  90. //使用input流加载key
  91. public Builder privateKeyFromInputStream(InputStream inputStream) throws IOException {
  92. try {
  93. String s = IOUtil.toString(inputStream);
  94. super.privateKey = PemUtil.loadPrivateKeyFromString(s);
  95. } finally {
  96. if (inputStream != null) {
  97. try {
  98. inputStream.close(); // 关闭
  99. } catch (IOException e) {
  100. e.printStackTrace();
  101. }
  102. }
  103. }
  104. return self();
  105. }
  106. @Override
  107. protected Builder self() {
  108. return this;
  109. }
  110. public MyRSAAutoCertificateConfig build() {
  111. this.merchantId = super.merchantId;
  112. this.merchantSerialNumber = super.merchantSerialNumber;
  113. this.privateKey = super.privateKey;
  114. RSAAutoCertificateProvider.Builder providerBuilder =
  115. new RSAAutoCertificateProvider.Builder()
  116. .merchantId(requireNonNull(merchantId))
  117. .apiV3Key(requireNonNull(apiV3Key))
  118. .privateKey(requireNonNull(privateKey))
  119. .merchantSerialNumber(requireNonNull(merchantSerialNumber));
  120. if (httpClient != null) {
  121. providerBuilder.httpClient(httpClient);
  122. }
  123. if (httpClientBuilder != null) {
  124. providerBuilder.httpClientBuilder(httpClientBuilder);
  125. }
  126. certificateProvider = providerBuilder.build();
  127. return new MyRSAAutoCertificateConfig(this);
  128. }
  129. }
  130. }

​​​​​​​

        然后使用就简单了,原本只能用私钥path构建config,我加了这个方法后,就可以用输入流直接加载(原先的方法就不要用了,它会报错),使用方式:

  1. MyRSAAutoCertificateConfig config = new MyRSAAutoCertificateConfig.Builder()
  2. .privateKeyFromInputStream(keyClassPath.getInputStream())
  3. .merchantId(merchantId)
  4. .merchantSerialNumber(merchantSerialNumber)
  5. .apiV3Key(apiV3key)
  6. .build();

         既然都能构建了,那就直接交给spring吧,毕竟这个对象不需要重复创建:

  1. @Configuration
  2. public class LadeWechatPayConfig {
  3. /**
  4. * 商户号
  5. */
  6. private String merchantId = "";
  7. /**
  8. * 商户API私钥路径
  9. */
  10. private String privateKeyPath = "";
  11. /**
  12. * 商户API证书路径
  13. */
  14. private String wechatPayCertificatePath = "";
  15. /**
  16. * 商户证书序列号(这是你自己的证书序列号哈,不是平台的)
  17. */
  18. private String merchantSerialNumber = "";
  19. /**
  20. * 商户APIV3密钥
  21. */
  22. private String apiV3key = "";
  23. /*appid*/
  24. private String appid = "";
  25. private ClassPathResource keyClassPath = new ClassPathResource("/wechatpay/apiclient_key.pem");//获取到resources包下指定文件
  26. /*
  27. * 初始化config对象,这个对象在构建各种支付执行器对象的时候需要传入,并且可复用
  28. * */
  29. @Bean
  30. public MyRSAAutoCertificateConfig getMyRSAConfig(){
  31. MyRSAAutoCertificateConfig config = null;
  32. try {
  33. config = new MyRSAAutoCertificateConfig.Builder()
  34. .privateKeyFromInputStream(keyClassPath.getInputStream())
  35. .merchantId(merchantId)
  36. .merchantSerialNumber(merchantSerialNumber)
  37. .apiV3Key(apiV3key)
  38. .build();//这个是我自己定义的那个对象,可以用stream加载证书文件
  39. } catch (IOException e) {
  40. log.error("--------------初始化微信支付config异常----------------");
  41. }
  42. return config;
  43. }
  44. /*
  45. * 构建微信支付jsapi执行器对象
  46. * */
  47. @Bean
  48. public JsapiServiceExtension getJsapiServiceExtension(@Autowired Config config){
  49. JsapiServiceExtension service = new JsapiServiceExtension.Builder().config(config).build();
  50. return service;
  51. }

        再贴一下我实际测试下单的代码:

  1. @Slf4j
  2. @SpringBootTest
  3. public class WechatPayTest {
  4. /** 商户号 */
  5. public static String merchantId = "";
  6. /** 商户证书序列号 */
  7. public static String merchantSerialNumber = "";
  8. /** 商户APIV3密钥 */
  9. public static String apiV3key = "";
  10. /*appid*/
  11. public static String appid= "";
  12. @Autowired
  13. private MyRSAAutoCertificateConfig config;//这里没有用Cinfig接口接收,直接用MyRSAAutoCertificateConfig接收了,后文会说为啥这样
  14. @Autowired
  15. private JsapiServiceExtension jsapiServiceExtension;
  16. /*
  17. * 创建预支付工单
  18. * */
  19. @Test
  20. public void createOrder() throws Exception {
  21. // request.setXxx(val)设置所需参数,具体参数可见Request定义
  22. PrepayRequest request = new PrepayRequest();
  23. Amount amount = new Amount();
  24. amount.setTotal(1);
  25. Payer payer = new Payer();
  26. payer.setOpenid("");
  27. request.setAmount(amount);
  28. request.setAppid(appid);
  29. request.setMchid(merchantId);
  30. request.setDescription("测试商品标题");
  31. request.setNotifyUrl("");//回调地址
  32. request.setOutTradeNo("11111111115");
  33. request.setPayer(payer);
  34. // response包含了调起支付所需的所有参数,可直接用于前端调起支付
  35. PrepayWithRequestPaymentResponse response = jsapiServiceExtension.prepayWithRequestPayment(request);
  36. System.out.println(response);
  37. }
  38. /*
  39. * 查询微信支付订单,根据商户工单
  40. * */
  41. @Test
  42. public void queryOrderByOutTradeNo(){
  43. //todo 微信支付订单号查询订单
  44. QueryOrderByOutTradeNoRequest orderByOutTradeNoRequest = new QueryOrderByOutTradeNoRequest();
  45. orderByOutTradeNoRequest.setMchid(merchantId);
  46. orderByOutTradeNoRequest.setOutTradeNo("11111111115");
  47. Transaction transaction = jsapiServiceExtension.queryOrderByOutTradeNo(orderByOutTradeNoRequest);
  48. System.out.println(transaction);
  49. }
  50. /*
  51. * 唤醒小程序支付请求参数的签名
  52. * */
  53. @Test
  54. public void awakenAppletSign(){
  55. AwakenAppletSignReq req = new AwakenAppletSignReq();
  56. req.setAppId(appid);
  57. req.setTimeStamp(System.currentTimeMillis() + "");
  58. req.setNonceStr(IdWorker.getIdStr());
  59. req.setOrderPackage("prepay_id=wx26171828262790b07fc8eexxxcedb0000");
  60. String s = WechatSignUtil.awakenAppletSign(req, config.createSigner());
  61. System.out.println(s);
  62. }
  63. }

回调接口测试方法:

  1. @ApiModel("微信支付回调通知")
  2. @RestController
  3. @RequestMapping("/noJwt/wechatnotify")
  4. @Slf4j
  5. public class WechatpayNotifyController {
  6. //这里不能用config注入依赖,只能用MyRSAAutoCertificateConfig
  7. //因为NotificationParser需要的不是Config,用Config传不进去
  8. //尽管他们的具体实现类是同一个,但接口不同就放不去,所以直接扔子类
  9. @Autowired
  10. private MyRSAAutoCertificateConfig config;
  11. /*@Autowired
  12. private NotificationConfig notificationConfig;*/
  13. @ApiOperation("微信小程序支付回调接口被调用")
  14. @PostMapping("/appletPayNotify")
  15. public Map appletPayNotify(HttpServletRequest request, HttpServletResponse response) {
  16. log.info("------------------微信小程序支付回调接口被调用-------------------");
  17. HashMap<String, Object> resultMap = new HashMap<>();
  18. try {
  19. //获取微信返回的body中的数据
  20. String body = MyHttpUtil.ReadAsChars(request);
  21. log.info("body{}",body);
  22. //证书序列号
  23. String serial = request.getHeader("Wechatpay-Serial");
  24. log.info("Wechatpay-Serial{}",serial);
  25. //微信应答签名
  26. String signature = request.getHeader("Wechatpay-Signature");
  27. log.info("Wechatpay-Signature{}",signature);
  28. //时间戳
  29. String timesTamp = request.getHeader("Wechatpay-Timestamp");
  30. log.info("Wechatpay-Timestamp{}",timesTamp);
  31. //随机字符串
  32. String nonce = request.getHeader("Wechatpay-Nonce");
  33. log.info("Wechatpay-Nonce{}",nonce);
  34. // 构造 RequestParam
  35. RequestParam requestParam = new RequestParam.Builder()
  36. .serialNumber(serial)
  37. .nonce(nonce)
  38. .signature(signature)
  39. .timestamp(timesTamp)
  40. .body(body)
  41. .build();
  42. log.info("RequestParam{}",requestParam);
  43. // 初始化 NotificationParser
  44. NotificationParser parser = new NotificationParser(config);
  45. // 以支付通知回调为例,验签、解密并转换成 Transaction
  46. Transaction transaction = parser.parse(requestParam, Transaction.class);
  47. log.info("Transaction{}",transaction);
  48. } catch (Exception e) {
  49. e.printStackTrace();
  50. log.error("支付回调出现异常:"+e.getMessage());
  51. resultMap.put("code","FAIL");
  52. resultMap.put("message","错误");
  53. response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
  54. return resultMap;
  55. }
  56. log.info("--------------------------微信小程序支付回调接口调用正常结束-----------------------------------");
  57. resultMap.put("code","SUCCESS");
  58. resultMap.put("message","成功");
  59. return resultMap;
  60. }
  61. }

用到的一些工具类(我简单封装了一些,但其实上面的代码还有很大优化空间,该封装封装,该优化优化):

  1. import javax.servlet.http.HttpServletRequest;
  2. import java.io.BufferedReader;
  3. import java.io.IOException;
  4. public class MyHttpUtil {
  5. /**
  6. * 获取request中的请求body
  7. */
  8. public static String ReadAsChars(HttpServletRequest request) {
  9. BufferedReader br = null;
  10. StringBuilder sb = new StringBuilder("");
  11. try {
  12. br = request.getReader();
  13. String str;
  14. while ((str = br.readLine()) != null) {
  15. sb.append(str);
  16. }
  17. br.close();
  18. } catch (IOException e) {
  19. e.printStackTrace();
  20. } finally {
  21. if (null != br) {
  22. try {
  23. br.close();
  24. } catch (IOException e) {
  25. e.printStackTrace();
  26. }
  27. }
  28. }
  29. return sb.toString();
  30. }
  31. }
  1. /*
  2. * 微信签名工具类
  3. * 这里的规则是微信文档里面的,具体可以看微信文档
  4. * */
  5. @Slf4j
  6. public class WechatSignUtil {
  7. public static String awakenAppletSign(AwakenAppletSignReq req, Signer signer) {
  8. String jsonString = req.getAppId() + "\n"
  9. + req.getTimeStamp() + "\n"
  10. + req.getNonceStr() + "\n"
  11. + req.getOrderPackage() + "\n";
  12. log.info(req.toString());
  13. SignatureResult sign = signer.sign(jsonString);
  14. String signSign = sign.getSign();
  15. return signSign;
  16. }
  17. }

        好了,以上,就是我本次对接微信支付所有的思考过程以及收获了,希望能帮助到你,当然,如果有哪里不对的,或者不太优雅有更好的方式处理,可以指点一下,学海无涯,我也只是一个小菜鸡,感谢。

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号