
最近要做一个微信小程序,需要微信支付,所以研究了下怎么在 java 上集成微信支付功能,特此记录下。
本文完整代码:点击跳转
准备工作
小程序开通微信支付
- 首先需要在微信支付的官网点击跳转上注册一个服务商
- 在服务商的管理页面中申请关联小程序,通过小程序的 APPid 进行关联
- 进入微信公众平台,功能-微信支付中确认关联(如果服务商和小程序的注册主体不一样,还要经过微信的审核)
获取各种证书、密钥文件
这里比较麻烦,需要认真点。
目前微信支付的 api 有 V2 和 V3 两个版本,V2 是 xml 的数据结构不建议用了,很麻烦(虽然 V3 也不简单).
以下内容全部基于微信支付 V3 的版本
你需要获取如下东西:
- 商户 id:这个可以在小程序微信公众平台-功能-微信支付 页面中的已关联商户号中得到
- 商户密钥:这个需要在微信支付的管理后台中申请获取
- 证书编号: 同样在微信支付的管理后台中申请证书,申请证书后就会看到证书编号
- 证书私钥:上一步申请证书的时候同时也会获取到证书的公钥、私钥文件。开发中只需要私钥文件
代码开发
由于支付属于比较敏感的操作,所以建议将参数配置放在后台,前端请求获取到参数后直接调起微信支付。
整个微信支付流程如下:
- 小程序端请求后台获取统一支付参数
- 后台调用微信 api(官方文档)生成预订单,并构造统一下单接口的参数返回小程序
- 小程序根据参数调用统一下单接口(官方文档)
实际开发中,小程序端的开发内容很少,98%的工作量都在后台。
相关影片资源迅雷下载推荐
【游戏开发面经汇总】- 计算机基础篇
这是一篇包含一万多字的游戏开发面试经验贴,主要针对实习和校招,并面向游戏程序开发中游戏客户端开发和游戏引擎开发岗位的同学,涵盖了绝大多数游戏公司面试中考察的关于计算机基础方面的高频经典问题。准备秋招, ...
游戏开发,【游戏开发面经汇总】 - 计算机基础篇
准备请求 htpClient
虽然微信官方目前没有正式放出官方 sdk,但是 github 上已经有了 sdk 的仓库,目前正在开发中,地址.这个工具做了一定程度的封装,极大简化了加密解密证书配置等繁琐的操作。
和微信的请求需要做双向加密,因此要在系统启动时创建一个专用的 httpClient,用来调用微信支付 api.代码如下:
@PostConstruct |
public void init() throws Exception { |
http://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); |
http://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(); |
http://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); |
} |
} |
拓展
以上只有关键的支付和回调接口示例,其他的接口请求也是类似的,就不一一列出。
原文链接:
企业网站建设与开发最低只要299元起,包含域名服务器,需要的联系QQ345424724,电话15516990022,18530226930相关影片资源迅雷下载推荐
学软件开发有哪些基本要求?
想了挺久,决定还是来知乎对寻找一下答案。90后屌丝一个,中专毕业自考大专,大二上完果断跑路做销售去了。大三没有回学校拿毕业证。现在刚到北京一个多月,在亲戚的安排下现在一家影视制片公司实习中。觉得自己的路 ...
软件开发,学软件开发有哪些基本要求?
标签: 小程序开发 手把手教你springboot集成微信支付