20220727



最近要做一个微信小程序,需要微信支付,所以研究了下怎么在 java 上集成微信支付功能,特此记录下。

本文完整代码:点击跳转

准备工作

小程序开通微信支付

  1. 首先需要在微信支付的官网点击跳转上注册一个服务商
  2. 在服务商的管理页面中申请关联小程序,通过小程序的 appid 进行关联
  3. 进入微信公众平台,功能-微信支付中确认关联(如果服务商和小程序的注册主体不一样,还要经过微信的审核)

获取各种证书、密钥文件

这里比较麻烦,需要认真点。

目前微信支付的 api 有 V2 和 V3 两个版本,V2 是 xml 的数据结构不建议用了,很麻烦(虽然 V3 也不简单).

以下内容全部基于微信支付 V3 的版本

你需要获取如下东西:

  • 商户 id:这个可以在小程序微信公众平台-功能-微信支付 页面中的已关联商户号中得到
  • 商户密钥:这个需要在微信支付的管理后台中申请获取
  • 证书编号: 同样在微信支付的管理后台中申请证书,申请证书后就会看到证书编号
  • 证书私钥:上一步申请证书的时候同时也会获取到证书的公钥、私钥文件。开发中只需要私钥文件

代码开发

由于支付属于比较敏感的操作,所以建议将参数配置放在后台,前端请求获取到参数后直接调起微信支付。

整个微信支付流程如下:

  1. 小程序端请求后台获取统一支付参数
  2. 后台调用微信 api(官方文档)生成预订单,并构造统一下单接口的参数返回小程序
  3. 小程序根据参数调用统一下单接口(官方文档)

实际开发中,小程序端的开发内容很少,98%的工作量都在后台。

准备请求 htpClient

虽然微信官方目前没有正式放出官方 sdk,但是 github 上已经有了 sdk 的仓库,目前正在开发中,地址.这个工具做了一定程度的封装,极大简化了加密解密证书配置等繁琐的操作。

和微信的请求需要做双向加密,因此要在系统启动时创建一个专用的 httpClient,用来调用微信支付 api.代码如下:

@PostConstruct
public void init() throws Exception {
log.info("私钥路径:{}", certKeyPath);
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(new FileInputStream(certKeyPath));
// 获取证书管理器实例
certificatesManager = CertificatesManager.getInstance();
sign = SecureUtil.sign(SignAlgorithm.SHA256withRSA, merchantPrivateKey.getEncoded(), null); // 向证书管理器增加需要自动更新平台证书的商户信息
certificatesManager.putMerchant(merchantId, new WechatPay2Credentials(merchantId,
new PrivateKeySigner(merchantSerialNumber, merchantPrivateKey)), apiV3Key.getBytes(StandardCharsets.UTF_8));
// 从证书管理器中获取verifier
Verifier verifier = certificatesManager.getVerifier(merchantId);
WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
.withMerchant(merchantId, merchantSerialNumber, merchantPrivateKey)
.withValidator(new WechatPay2Validator(verifier));
httpClient = builder.build();
}

预定单生成并返回小程序请求参数

代码如下:

JSONObject obj = new JSONObject();
obj.put("mchid", merchantId);
obj.put("appid", appId);
obj.put("description", body.getDescription());
obj.put("out_trade_no", body.getSn());
obj.put("notify_url", "https://backend/asdfasdf/notify");
Map<String, String> attach = new HashMap<>();
attach.put("sn", body.getSn());
obj.put("attach", JSON.toJSONString(attach));
JSONObject amount = new JSONObject();
amount.put("total", body.getPrice().multiply(BigDecimal.valueOf(100)).intValue());
obj.put("amount", amount);
JSONObject payer = new JSONObject();
//放入用户的openId
payer.put("openid", "");
obj.put("payer", payer);
log.info("请求参数为:" + JSON.toJSONString(obj));
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi");
httpPost.addHeader("Accept", "application/json");
httpPost.addHeader("Content-type", "application/json; charset=utf-8");
httpPost.setEntity(new StringEntity(obj.toJSONString(), "UTF-8"));
try {
CloseableHttpResponse response = httpClient.execute(httpPost);
String bodyAsString = EntityUtils.toString(response.getEntity());
String prePayId = JSONObject.parseObject(bodyAsString).getString("prepay_id"); //准备小程序端的请求参数
Map<String, String> map = new HashMap<>(6);
map.put("appId", appId);
String timeStamp = String.valueOf(now / 1000);
map.put("timeStamp", timeStamp);
String nonceStr = IdUtil.fastSimpleUUID();
map.put("nonceStr", nonceStr);
String packageStr = "prepay_id=" + prePayId;
map.put("package", packageStr);
map.put("signType", "RSA");
// 注意\n必须添加
map.put("paySign", Base64.encode(sign.sign(appId + "\n" + timeStamp + "\n" + nonceStr + "\n" + packageStr + "\n")));
return map;
} catch (IOException e) {
throw new RuntimeException(e);
}

注意在生成小程序请求参数 paySign 时,\n 必须添加

小程序端请求

小程序端获取请求参数后,直接调用wx.requestPayment(后台返回的参数),即可调起支付

支付成功回调

微信支付成功后,会通知服务端支付成功,通过之前配置的回调接口。注意回调接口必须为 https。

微信回调参数也是加密的,必须要经过解密后才能获取,代码如下:

注意:部分参数是通过请求头提供的,nginx 等代理在转发请求时可能会将请求头过滤掉,导致无法获取对应参数

@Override
public void notify(HttpServletRequest servletRequest) { String timeStamp = servletRequest.getHeader("Wechatpay-Timestamp");
String nonce = servletRequest.getHeader("Wechatpay-Nonce");
String signature = servletRequest.getHeader("Wechatpay-Signature");
String certSn = servletRequest.getHeader("Wechatpay-Serial"); try (BufferedReader reader = new BufferedReader(new InputStreamReader(servletRequest.getInputStream()))) {
StringBuilder stringBuilder = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
stringBuilder.append(line);
}
String obj = stringBuilder.toString();
log.info("回调数据:{},{},{},{},{}", obj, timeStamp, nonce, signature, certSn);
Verifier verifier = certificatesManager.getVerifier(merchantId);
String sn = verifier.getValidCertificate().getSerialNumber().toString(16).toUpperCase(Locale.ROOT);
NotificationRequest request = new NotificationRequest.Builder().withSerialNumber(sn)
.withNonce(nonce)
.withTimestamp(timeStamp)
.withSignature(signature)
.withBody(obj)
.build();
NotificationHandler handler = new NotificationHandler(verifier, apiV3Key.getBytes(StandardCharsets.UTF_8));
// 验签和解析请求体
Notification notification = handler.parse(request);
JSONObject res = JSON.parseObject(notification.getDecryptData());
//做一些操作
JSONObject attach = res.getJSONObject("attach");
} catch (Exception e) {
log.error("微信支付回调失败", e);
}
}

拓展

以上只有关键的支付和回调接口示例,其他的接口请求也是类似的,就不一一列出。


手把手教你springboot集成微信支付的更多相关文章

  1. 小D课堂-SpringBoot 2.x微信支付在线教育网站项目实战_1-1.SpringBoot整合微信支付开发在线教育视频站点介绍

    笔记 第一章项目介绍和前期准备 1.SpringBoot整合微信支付开发在线教育视频站点介绍     简介: 课程介绍,和小D课堂在线教育项目搭建开发 1.课程大纲介绍         2.微信支付项 ...

  2. 手把手教你springboot中导出数据到excel中

    手把手教你springboot中导出数据到excel中 问题来源: 前一段时间公司的项目有个导出数据的需求,要求能够实现全部导出也可以多选批量导出(虽然不是我负责的,我自己研究了研究),我们的项目是x ...

  3. ***CodeIgniter集成微信支付(转)

    微信支付Native扫码支付模式二之CodeIgniter集成篇  http://www.cnblogs.com/24la/p/wxpay-native-qrcode-codeigniter.html ...

  4. iOS开发集成微信支付

    首先需要理清楚流程: 1.用户使用APP客户端,选择商品下单. 2.商户客户端(就是你做的APP)将用户的商品数据传给商户服务器,请求生成支付订单. 3.商户后台调用统一下单API向微信的服务器发送请 ...

  5. 关于ApiCloud的Superwebview在androidstudio中集成微信支付模块,提示模块未绑定的问题

    前两天ApiCloud项目集成了微信支付模块,android端今天也将ApiCloud官方的uzWxPay.jar集成了.在编译玩测试的时候提示wxPay模块为绑定!我的项目是使用ApiCloud推出 ...

  6. iOS 集成微信支付【转载】

    目前项目里有微信支付的需求,调研过一段时间后,发现其实并没有想象中的那么困难.如果你只是想实现该功能,一个方法足以,但是若你想深入了解实现原理.就需要花费更多的功夫了.目前我只清楚微信支付需要做签名, ...

  7. 如何在Spring Boot项目中集成微信支付V3

    Payment Spring Boot 是微信支付V3的Java实现,仅仅依赖Spring内置的一些类库.配置简单方便,可以让开发者快速为Spring Boot应用接入微信支付. 演示例子: paym ...

  8. iOS集成微信支付

    微信支付的开发 前言:之前听说过微信支付有很多坑,其实没有想象的那么坑,整体感觉很容易上手,按照它的流程来不会有错!PS:官方的流程看的TMD烦,好啦,废话有点多,进入开发.(ps:每个微信的版本一直 ...

  9. app集成微信支付服务端代码-php版本

    1.微信支付分为两种,一种是微信公众品台的微信支付,另一种是微信开放平台的微信支付 2.上周做的是开放品台的微信支付,把遇到的问题总结一下 第一,下载官方提供的代码,解压后放到根目录下,然后认真读文档 ...

随机推荐

  1. 探究MySQL中SQL查询的成本

    成本 什么是成本,即SQL进行查询的花费的时间成本,包含IO成本和CPU成本. IO成本:即将数据页从硬盘中读取到内存中的读取时间成本.通常1页就是1.0的成本. CPU成本:即是读取和检测是否满足条 ...

  2. 【Java面试】如何理解Spring Boot中的Starter?

    一个工作了3年的Java程序员,遇到一个Spring Boot的问题. 他对这个问题有一些了解,但是回答得不是很好,希望参考我的高手回答. 这个问题是:"如何理解Spring Boot中的S ...

  3. 《C Primer Plus》第六版笔记--1~3章

    目录 第一章 初识C语言 1 使用C语言的7个步骤 1.1 定义程序目标 1.2 设计程序(功能实现) 1.3 编写代码 1.4 编译 1.5 运行程序 1.6 测试和调试程序 1.7 维护和修改代码 ...

  4. Halodoc使用 Apache Hudi 构建 Lakehouse的关键经验

    Halodoc 数据工程已经从传统的数据平台 1.0 发展到使用 LakeHouse 架构的现代数据平台 2.0 的改造.在我们之前的博客中,我们提到了我们如何在 Halodoc 实施 Lakehou ...

  5. 第6章 字符串(上)——C风格字符串

    6.1 C-strings(C 风格字符串) C风格字符串: 字符数组是元素为字符型的数组,字符串是以空字符'\0' 作为数组最后一个元素的字符数组. 如果指定了数组的大小,而字符串的长度又小于数组大 ...

  6. js 表面学习 - 认识结构2

    单行注释以 // 开头. 多行注释以 /* 开头,以 */ 结尾. 任何位于 /* 和 */ 之间的文本都会被 JavaScript 忽略. JavaScript 数据类型 JavaScript 变量 ...

  7. CODING DevOps 助力中化信息打造新一代研效平台,驱动“线上中化”新未来

    中化信息技术有限公司,简称"中化信息",是世界 500 强企业中国中化控股有限责任公司(简称"中国中化")的全资直属公司,依托于中国中化的信息化建设实践,建立起 ...

  8. 女朋友说:你要搞懂了MySQL三大日志,我就让你嘿嘿嘿!

    1. 背景 MySQL实现事务.崩溃恢复.集群的主从复制,底层都离不开日志,所以日志是MySQL的精华所在.只有了解MySQL日志,才算是彻底搞懂MySQL. 今天一灯就带你深入浅出的学习MySQL的 ...

  9. docker安装dvwa

    docker run -d --name dvwa -p 8011:80 -p 33066:3306 -e MYSQL_PASS="password" citizenstig/dv ...

  10. Homebrew安装(macos)

    参照大佬的博客文章:https://zhuanlan.zhihu.com/p/111014448 OSX 将下面命令复制到终端执行 /bin/zsh -c "$(curl -fsSL htt ...