文章目录

  • 前言
  • 一、微信支付流程理解
    • 1、流程图
  • 二、调用接口准备
    • 1、初始化微信连接
    • 2、调用微信统一下单接口
    • 3、后端生成签名
  • 三、微信回调通知
    • 1.微信验签
    • 2、数据解密
  • 总结

前言

最近在开发一款小程序用到微信支付,中间还是遇到了一些小坑,在这里记录一下,希望可以给到第一次做支付的童鞋一些帮助,此教程以小程序支付为例,其他支付与此类似


一、微信支付流程理解

1、流程图

我们在做到微信支付时,首先需要理解微信支付的整个流程,官方文档这里已经介绍的很详细了,直接上图:

开发步骤逻辑说明 :(以下每个步骤会在后续中详细讲解)

1、用户在小程序端调用后台自己写的下单接口,在这个接口中生成商户自己的订单。

2、后端在自己写的下单接口中 调用微信的统一下单接口,微信会返回一个预支付订单id。

3、后端在收到微信返回的预支付订单id后,需要将数据进行加签处理,并将数据返回给前端小程序。

4、小程序调起微信支付接口,其中参数见微信官方文档(注意:小程序再在起微信支付接口所需要的参数是后端签名后返回的数据)。

5、用户授权且支付成功后,微信会根据后端在预下单时填写的支付回调地址,异步通知商户后端支付信息。

6、后端在收到微信支付通知时,需要进行验签处理。验签通过后,后端执行自己的后续支付成功逻辑。

  • 后端再收到微信通知后,将收到结果反馈,否则微信服务器会再次发送通知。后端也需要校验当前支付信息是否已经接收并处理。如果已处理则不需要再次处理

二、调用接口准备

1、初始化微信连接

微信官方提供了相关的SDK,我们用微信提供的SDK就可以了,不要在看其他的各种自己写的连接教程、加签验签、调用证书等等。直接使用官方提供的SDK省时省力还安全。/font>
官方地址:微信支付API v3的Apache HttpClient扩展,实现了请求签名的生成和应答签名的验证

1、初始化HttpClient,我们直接使用官方推荐的方法:自动更新证书功能(不用考虑证书的问题)
使用 AutoUpdateCertificatesVerifier 类替代默认的验签器。它会在构造时自动下载商户对应的微信支付平台证书,并每隔一段时间(默认为1个小时)更新证书。

代码如下(示例):
具体参数参考官方文档:微信支付统一下单

 /***  初始化微信支付链接*  privateKey  商户API私钥*  apiv3  API v3密钥*  mchid  商户id*  serialNumber 商户API证书的证书序列号* @return*/public HttpClient initWChant(){PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(new ByteArrayInputStream(privateKey.getBytes(StandardCharsets.UTF_8)));String apiv3 = WChantPay.getApiv3();String mchid = WChantPay.getMchid();String serialNumber = WChantPay.getSerialNumber();AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(new WechatPay2Credentials(mchid, new PrivateKeySigner(serialNumber, merchantPrivateKey)),apiv3.getBytes(StandardCharsets.UTF_8));WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create().withMerchant(WChantPay.getMchid(), serialNumber, merchantPrivateKey).withValidator(new WechatPay2Validator(verifier));return builder.build();}

2、调用微信统一下单接口

代码如下(示例):

*** 微信统一下单* @param User 用户信息* @param wxpayDetail 后端自己生成的订单信息* @param httpClient 上一步生成的微信链接对象* @return* @throws Exception*/
public Map<String,String> pay(User user,WxpayDetail wxpayDetail,HttpClient httpClient){HttpPost httpPost = new HttpPost(WChantPay.getPayUrl());httpPost.addHeader("Accept", "application/json");httpPost.addHeader("Content-type","application/json; charset=utf-8");org.apache.commons.io.output.ByteArrayOutputStream bos = new ByteArrayOutputStream();ObjectMapper objectMapper = new ObjectMapper();ObjectNode rootNode = objectMapper.createObjectNode();//商户idrootNode.put("mchid",WChantPay.getMchid())//小程序id.put("appid", WChantPay.getAppId())//描述.put("description", "商品描述")//微信通知回调地址.put("notify_url", WChantPay.getNotifyUrl())//商户订单id.put("out_trade_no", wxpayDetail.getHomeOrderId());//如果前端直接传的是分此处不需要再转int round = Math.round(wxpayDetail.getTotal() * 100);rootNode.putObject("amount")//支付金额,单位是(分).put("total", round);rootNode.putObject("payer")//支付用户的openid.put("openid", merchantUsers.getOpenId());objectMapper.writeValue(bos, rootNode);httpPost.setEntity(new StringEntity(bos.toString("UTF-8")));HttpResponse response = httpClient.execute(httpPost);String bodyAsString = EntityUtils.toString(response.getEntity());//微信返回的预支付idJSONObject jsonObject = JSONObject.parseObject(bodyAsString);}

3、后端生成签名

/***  构建签名* @param prepay_id  微信预订单返回的预支付订单id* @param orderId  商户自己的订单号* @return  将这个数据返回给小程序前端*/public Map<String,String> responseSign(String prepay_id,String orderId) {Map<String,String> map=new HashMap();map.put("appId",WChantPay.getAppId());long timeStamp = System.currentTimeMillis()/1000;map.put("timeStamp",timeStamp+"");String nonceStr = WXPayUtil.generateNonceStr();map.put("nonceStr", nonceStr);map.put("package", "prepay_id="+ prepay_id);String //一定要注意,最后也要加个\n     s=WChantPay.getAppId()+"\n"+timeStamp+"\n"+nonceStr+"\n"+"prepay_id="+prepay_id.toString()+"\n";String paySign = WXPayUtil.sign(s.getBytes(StandardCharsets.UTF_8));map.put("paySign",paySign);map.put("signType","RSA");map.put("orderId",orderId);return map;}

到此第一步功能就完成了,接下来就是小程序使用后端返回的数据调用微信的支付接口 具体文档参见:小程序调起支付

三、微信回调通知

微信再收到小程序支付后,会将支付结果异步通知给商户的后端,通知地址为预下单时 notify_url 字段,参见上面的统一下单代码。

接收通知这个接口我们分为两个步骤,第一个是进行验签 第二个是进行数据解密

官方文档:微信支付通知

1.微信验签

//微信验签
public boolean notify(HttpServletRequest request){//获取微信返回的body中的数据String body= ReadAsChars(request);//证书序列号String serial = request.getHeader("Wechatpay-Serial");//微信应答签名String signature= request.getHeader("Wechatpay-Signature");//时间戳String timesTamp = request.getHeader("Wechatpay-Timestamp");//随机字符串String nonce = request.getHeader("Wechatpay-Nonce");//初始化微信连接 参数与上面初始化微信连接 一致PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(new ByteArrayInputStream(WChantPayService.privateKey.getBytes(StandardCharsets.UTF_8)));AutoUpdateCertificatesVerifier autoUpdateCertificatesVerifier = new AutoUpdateCertificatesVerifier(new WechatPay2Credentials(WChantPay.getMchid(), new PrivateKeySigner(WChantPay.getSerialNumber(), merchantPrivateKey)),WChantPay.getApiv3().getBytes(StandardCharsets.UTF_8));]//构建验签串String sign = wChantPayService.responseSign(timesTamp, nonce, body);System.out.println("签名串:" + sign);//验签 boolean verify = autoUpdateCertificatesVerifier.verify(serial, sign.getBytes(StandardCharsets.UTF_8), signature);System.out.println("验签:" + verify);
}
/*** 获取request中的请求body*/
public static String ReadAsChars(HttpServletRequest request) {BufferedReader br = null;StringBuilder sb = new StringBuilder("");try {br = request.getReader();String str;while ((str = br.readLine()) != null) {sb.append(str);}br.close();} catch (IOException e) {e.printStackTrace();} finally {if (null != br) {try {br.close();} catch (IOException e) {e.printStackTrace();}}}return sb.toString();}/*** 构造验签名串.** @param wechatpayTimestamp HTTP头 Wechatpay-Timestamp 中的应答时间戳。* @param wechatpayNonce     HTTP头 Wechatpay-Nonce 中的应答随机串* @param body               响应体* @return the string*/public String responseSign(String wechatpayTimestamp, String wechatpayNonce, String body) {return Stream.of(wechatpayTimestamp, wechatpayNonce, body).collect(Collectors.joining("\n", "", "\n"));}

2、数据解密

收到微信的数据后,将数据记得转换成json 或者自己定义的对象,取出你需要的数据,比较简单这里就不再贴代码了

 public void decodeData(String associatedData,String nonce,String ciphertext) {//微信官方提供的解密工具AesUtil aesUtil=new AesUtil(WChantPay.getApiv3().getBytes(StandardCharsets.UTF_8));String rsData = aesUtil.decryptToString(associatedData.getBytes(StandardCharsets.UTF_8), nonce.getBytes(StandardCharsets.UTF_8), ciphertext);}
package com.home.machine.pay.utils;import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;/*** @author wasin* @description: date 2021-05-08*/
public class AesUtil {static final int KEY_LENGTH_BYTE = 32;static final int TAG_LENGTH_BIT = 128;private final byte[] aesKey;public AesUtil(byte[] key) {if (key.length != KEY_LENGTH_BYTE) {throw new IllegalArgumentException("无效的ApiV3Key,长度必须为32个字节");}this.aesKey = key;}/**** @param associatedData 微信通知返回的附加数据* @param nonce 微信通知返回的随机字符串* @param ciphertext  待解密的数据* @return* @throws GeneralSecurityException* @throws IOException*/public String decryptToString(byte[] associatedData, byte[] nonce, String ciphertext)throws GeneralSecurityException, IOException {try {Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");SecretKeySpec key = new SecretKeySpec(aesKey, "AES");GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH_BIT, nonce);cipher.init(Cipher.DECRYPT_MODE, key, spec);cipher.updateAAD(associatedData);byte[] decode = Base64.getDecoder().decode(ciphertext);return new String(cipher.doFinal(decode), "utf-8");} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {throw new IllegalStateException(e);} catch (InvalidKeyException | InvalidAlgorithmParameterException e) {throw new IllegalArgumentException(e);}}
}

WXPayUtil相关代码

 public static String sign(byte[] message){Signature sign = null;try {sign = Signature.getInstance("SHA256withRSA");PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(new FileInputStream(WChantPayConfig.getPrivateKeyUrl()));//商户私钥地址 F:/xxx/xxx/apiclient_key.pemsign.initSign(merchantPrivateKey);sign.update(message);return Base64.getEncoder().encodeToString(sign.sign());} catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException | FileNotFoundException e) {e.printStackTrace();return null;}}

总结

至此微信的支付就完成了,欢迎大家一起交流。

微信支付apiv3全教程相关推荐

  1. Java接入微信支付ApiV3详细教程

    1.申请商户API证书 APIv2 中,调用微信支付安全级别较高的接口(如:退款.企业红包.企业付款) APIv3 中,调用微信支付所有接口 2.商户申请商户API证书时,会生成商户私钥,并保存在本地 ...

  2. html如何自动打开手机微信支付,微信支付分正式上线!微信支付分开通教程

    原标题:微信支付分正式上线!微信支付分开通教程 [PConline资讯]腾讯最新功能悄悄上线,开通微信支付分! 相信很多朋友都不知道,微信支付分是怎么回事,更不知道如何去开通.微信支付分怎么开通?今天 ...

  3. java 庖丁解牛api_Java 微信支付 APIv3 平台证书的命令行下载工具

    Certificate Downloader Certificate Downloader 是 Java 微信支付 APIv3 平台证书的命令行下载工具.该工具可从 https://api.mch.w ...

  4. 一文搞懂「微信支付 Api-v3」接口规则所有知识点

    文章目录 简介 v2 与 v3 的区别 API 密钥设置 获取 API 证书 请求签名 示例代码 构造签名串 构造 HTTP 头中的 Authorization 获取证书序列号 通过工具获取 通过代码 ...

  5. 开通微信支付分最新教程来了!

    据微信支付官方消息,微信支付分最新覆盖了打车场景,先乘后付模式正式接入打车平台,用户开启微信支付分服务后,分数达标即有机会享受"先打车后付款",上车免预付,到达目的地后自动扣款.首 ...

  6. 微信支付接口怎么申请 微信支付接口申请教程

     微信支付接口:点击进去 一直让大家翘首以盼的微信最核心的杀手锏--"微信支付"终于开放申请了!今天微信团队在官网发布消息,微信公众平台支付功能已正式开放申请,已开通公众号的 ...

  7. 微信支付apiV3异常:The corresponding provider for the merchant already exists

    异常信息 java.lang.IllegalStateException: The corresponding provider for the merchant already exists. 原因 ...

  8. 微信支付接口配置教程(下)

    微信第三方平台微信支付接口配置教程(下) 上传微信支付证书 登录后台系统,在后台微信商城里,点击微信支付证书.这一步我们需要上传对应的微信支付证书,这个证书就是文章<微信第三方平台微信支付接口配 ...

  9. C# 微信支付APIv3 SDK RSAUtility

    暂时没有看到关于微信支付APIv3 of C# 的SDK,对于新进来的开发人员在实现底层(签名,加密,验签)多多少少会碰一点点壁: 下面提供相关方法供参考. 文档中心:微信支付开发文档/开放平台 (建 ...

最新文章

  1. java 限制文本框长度_java中限制文本框输入长度的显示(转载)
  2. python实现合并链表_python:16.合并两个排序的链表
  3. php 把一个数组分成有n个元素的二维数组的算法
  4. react滑动切换tab动画效果_使用React实现的水平标签(Tab)栏
  5. 20135310陈巧然家庭作业汇总[3.56 3.67 6.23 6.39.6.40 6.41]
  6. (2)ZYNQ FPGA加载比特流(FPGA不积跬步101)
  7. 超实用Mac软件分享
  8. 入行Web前端的学习方法有哪些?
  9. vue 项目常见功能(搜索 时间戳转换 过滤器)
  10. 实习日志(1)2011-12-30
  11. 【No.4 Ionic】修改 cordova 插件
  12. lan speed test怎么用_别浪费,你家的200M光纤真的用起来了吗?
  13. python实现DDA算法
  14. 【Python爬虫】之西瓜视频地址解密20210822
  15. 软考高级网络规划设计师历年论文真题汇总2009-2021
  16. 软件工程之软件设计③(概要设计说明书,详细设计说明书)
  17. 如何开启Windows共享文件夹服务
  18. Andriod Studio下载安装教程
  19. Win10查看屏保的存储位置
  20. linux swp文件是什么,SWP 文件扩展名: 它是什么以及如何打开它?

热门文章

  1. python:从0开始自动发微博
  2. 【论文速递】BLIP:Bootstrapping Language-Image Pre-training for Unified Vision-Language Understanding and G
  3. python3.9下错误,pip安装matplotlib卡在Building wheel for matplotlib (setup.py)..不动的原因与解决
  4. 微电子半导体集成电路专业术语英语整理
  5. 二代测序linux软件,二代测序数据分析软件包大全
  6. android北京工资待遇,【北京京东工资】android开发工程师待遇-看准网
  7. sql查询汉字首字母
  8. python selenium 大众点评餐厅信息+用户评论 爬虫
  9. Python模块之二:Python3 常用模块总结
  10. harbor企业级镜像仓库搭建