简介:

微信支付的文档就不吐槽了,记录下微信支付,小程序支付的实现

开发前准备

账号申请,公钥私钥啥的去官网开发指引-小程序支付 | 微信支付商户平台文档中心 (qq.com)

核心代码

下单及拉起支付

微信支付的所有接口调用都是差不多的,关键在于如何处理网络请求和签名,直接上代码

网络请求我用的是okhttp,以小程序支付为例

/*** 微信小程序支付下单* 商户系统先调用该接口在微信支付服务后台生成预支付交易单,返回正确的预支付交易会话标识后再按Native、JSAPI、APP等不同场景生成交易串调起支付。* 请求URL:https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi* 请求方式:POST** @param dto          微信小程序订单参数* @param merchantInfo 商户号信息* @return*/public static ResultData createWeChatAppletPayOrder(WeChatAppletPlaceOrderDto dto, MerchantInfo merchantInfo) {WeChatAppletPlaceOrder param = new WeChatAppletPlaceOrder();BeanUtils.copyBeanProp(param, dto);String reqData = JSON.toJSONString(param);log.info("创建微信小程序订单参数:{}", reqData);try {String token = WxApiV3SignUtils.getToken("POST", HttpUrl.parse(dto.getUrl()),reqData, merchantInfo.getMchId(), merchantInfo.getPrivateKey(), merchantInfo.getSerialNo());OkHttpClient okHttpClient = new OkHttpClient();RequestBody requestBodyJson = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), reqData);Request request = new Request.Builder().url(dto.getUrl()).addHeader("Accept", "*/*").addHeader("Authorization", token).post(requestBodyJson).build();Call call = okHttpClient.newCall(request);Response response = call.execute();String resStr = response.body().string();log.info("创建微信小程序订单返回结果:状态码:{}--{}", response.code() ,resStr);//响应成功if (response.isSuccessful()) {JSONObject json = JSON.parseObject(resStr);WeChatAppletPlaceOrderVO vo = new WeChatAppletPlaceOrderVO();vo.setPrepayId("prepay_id=" + json.getString("prepay_id"));log.info("prepay_id=" + json.getString("prepay_id"));/*小程序调起支付签名信息*/WeChatAppletRequestPaymentVO paymentVO = WxApiV3SignUtils.getSign(dto.getAppId(),"prepay_id=" + json.getString("prepay_id"), merchantInfo.getPrivateKey());vo.setTimeStamp(String.valueOf(paymentVO.getTimestamp()));log.info(String.valueOf(paymentVO.getTimestamp()));vo.setNonceStr(paymentVO.getNonceStr());log.info(paymentVO.getNonceStr());vo.setPaySign(paymentVO.getSignature());log.info(paymentVO.getSignature());vo.setPrepayId("prepay_id=" + json.getString("prepay_id"));vo.setSignType("RSA");return ResultData.success("success", vo);} else {return ResultData.error(response.code(), response.body().string());}} catch (Exception e) {log.error("创建微信小程序订单出错:" + e.getMessage());return ResultData.error(e.getMessage());}}

请求参数和返回参数按照接口文档封装就好了,讲一下token和返给前端拉起支付的签名,其实官方也有相关的代码示例,下面的getToken是所有微信支付相关请求获取token通用的方法,sign方法也是,下单和拉起支付的签名方式是一样的,只是签名的参数不同,

微信小程序支付的签名参数是:请求参数是请求方式method,小程序下单接口的url,时间戳timestamp,随机字符串nonceStr,请求参数body。

微信小程序拉起支付的参数是:小程序appId,时间戳timestamp,随机字符串nonceStr,下单成功返回的prepayId(小程序签名要在prepayId前拼接上"prepay_id=")

package com.carlos.pay.common.util;import com.alibaba.fastjson.JSONObject;
import com.carlos.pay.doamin.vo.WeChatAppletRequestPaymentVO;
import lombok.extern.slf4j.Slf4j;
import okhttp3.HttpUrl;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.FileCopyUtils;
import sun.misc.BASE64Decoder;import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.Random;/*** @author carlos*/
@Slf4j
public class WxApiV3SignUtils {private static final String schema = "WECHATPAY2-SHA256-RSA2048";private static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";private static final Random RANDOM = new SecureRandom();/*** 签名,生成token** @param method* @param url* @param body* @param mchId      商户号* @param privateKey 商户证书私钥* @param serialNo   商户API证书序列号* @return* @throws Exception*/public static String getToken(String method, HttpUrl url, String body, String mchId, String privateKey, String serialNo) throws Exception {String nonceStr = generateNonceStr();log.info("nonceStr: " + nonceStr);long timestamp = System.currentTimeMillis() / 1000;log.info("timestamp: " + timestamp);String message = buildMessage(method, url, timestamp, nonceStr, body);String signature = sign(message.getBytes("utf-8"), privateKey);log.info("signature: " + signature);return schema + " " + "mchid=\"" + mchId + "\","+ "nonce_str=\"" + nonceStr + "\","+ "timestamp=\"" + timestamp + "\","+ "serial_no=\"" + serialNo + "\","+ "signature=\"" + signature + "\"";}/*** 拉起支付签名返给前端拉起支付** @param appId* @param prepayId* @param privateKey* @return* @throws Exception*/public static WeChatAppletRequestPaymentVO getSign(String appId, String prepayId, String privateKey) throws Exception {String nonceStr = generateNonceStr();log.info("nonceStr: " + nonceStr);long timestamp = System.currentTimeMillis() / 1000;log.info("timestamp: " + timestamp);String message = appId + "\n" + timestamp + "\n" + nonceStr + "\n" + prepayId + "\n";String signature = sign(message.getBytes("utf-8"), privateKey);log.info("signature: " + signature);return new WeChatAppletRequestPaymentVO(nonceStr, timestamp, signature);}/*** @param message* @param privateKey 商户私钥* @return*/private static String sign(byte[] message, String privateKey) throws Exception {Signature sign = Signature.getInstance("SHA256withRSA");sign.initSign(getPrivateKey(privateKey));sign.update(message);return Base64.getEncoder().encodeToString(sign.sign());}private static String buildMessage(String method, HttpUrl url, long timestamp, String nonceStr, String body) {String canonicalUrl = url.encodedPath();if (url.encodedQuery() != null) {canonicalUrl += "?" + url.encodedQuery();}return method + "\n"+ canonicalUrl + "\n"+ timestamp + "\n"+ nonceStr + "\n"+ body + "\n";}/*** String转私钥PrivateKey** @param key* @return* @throws Exception*/public static PrivateKey getPrivateKey(String key) throws Exception {byte[] keyBytes;keyBytes = (new BASE64Decoder()).decodeBuffer(key);PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);KeyFactory keyFactory = KeyFactory.getInstance("RSA");return keyFactory.generatePrivate(keySpec);}/*** String转私钥PrivateKey 从pem文件中获取私钥** @param fileName* @return* @throws Exception*/public static String getPrivateKeyByFile(String fileName) throws Exception {ClassPathResource cpr = new ClassPathResource(fileName);String content = new String(FileCopyUtils.copyToByteArray(cpr.getInputStream()), "utf-8");String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "").replace("-----END PRIVATE KEY-----", "").replaceAll("\\s+", "");return privateKey;}/*** 获取随机字符串 Nonce Str** @return String 随机字符串*/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);}/*** V3回调解密** @param body* @param keyV3* @return* @throws IOException* @throws GeneralSecurityException*/public static String decryptBody(String body, String keyV3) throws IOException, GeneralSecurityException {AesUtil aesUtil = new AesUtil(keyV3.getBytes("utf-8"));JSONObject object = JSONObject.parseObject(body);JSONObject resource = object.getJSONObject("resource");String ciphertext = resource.getString("ciphertext");String associatedData = resource.getString("associated_data");String nonce = resource.getString("nonce");log.info("回调解密参数--ciphertext:" + ciphertext);log.info("回调解密参数--associatedData:" + associatedData);log.info("回调解密参数--nonce:" + nonce);return aesUtil.decryptToString(associatedData.getBytes("utf-8"), nonce.getBytes("utf-8"), ciphertext);}
}

对了上面的getPrivateKey和getPrivateKeyByFile都是用来获取PrivateKey的,一个是从String转的,一个是读pem文件的,我的pem文件是放在resources下的。

好了,下单拉起支付就到这了。

回调

回调没什么好说的,返回的是加密的报文,用上文的WxApiV3SignUtils.decryptBody方法解密据OK啦

上代码咯

/*** 微信小程序支付回调v3** @param request* @param response* @return* @throws IOException* @throws GeneralSecurityException*/@Overridepublic synchronized Map<String, String> wxMiniPayCallBackV3(HttpServletRequest request, HttpServletResponse response) throws IOException, GeneralSecurityException {log.info("微信小程序支付回调开始");String line = request.getReader().readLine();request.getReader().close();log.info("接收到的报文:" + line);String result = WxApiV3SignUtils.decryptBody(line);log.info("解密后的报文:" + result);//将字符串转换成jsonJSONObject json = JSONObject.parseObject(result);Map<String, String> map = new HashMap<>();try {//TODO 你的业务 boolean flag = 业务处理的成功失败} catch (Exception e) {//响应微信map.put("code", "FAIL");map.put("message", "失败");log.info("回复微信消息:FAIL");return map;}if (flag) {//响应微信map.put("code", "SUCCESS");map.put("message", "成功");log.info("回复微信消息:SUCCESS");return map;}//响应微信map.put("code", "FAIL");map.put("message", "失败");log.info("回复微信消息:FAIL");return map;}

总结

微信支付相关的东西其实并不难,其实核心就是WxApiV3SignUtils里面的东西,签名,加解密。

微信支付,小程序支付V3相关推荐

  1. jsapi支付签名_微信支付小程序支付全流程

    点击蓝色字关注我们! 一个努力中的公众号 长的好看的人都关注了 本文给大家讲解微信小程序支付全流程,以及相关功能源代码,项目不开放,带来不便尽请谅解.小程序支付主要包含如下几步骤,1.预下单-调用微信 ...

  2. 微信支付-小程序支付全流程

    点击蓝色字关注我们! 一个努力中的公众号 长的好看的人都关注了 本文给大家讲解微信小程序支付全流程,以及相关功能源代码,项目不开放,带来不便尽请谅解.小程序支付主要包含如下几步骤,1.预下单-调用微信 ...

  3. Java--微信支付--小程序支付--v3版--完整的代码例子

    微信官方文档–小程序支付:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_1.shtml 微信官方文档–支付接入前准备https:// ...

  4. 微信支付小程序支付和APP支付

    这里介绍的是微信小程序支付和APP支付 引入的依赖是微信支付第三方jar weixin-java-pay github:WxJava <dependency><groupId> ...

  5. 微信支付:小程序支付/扫码支付

    0.yml文件配置 1.微信支付controller import io.swagger.annotations.Api; import io.swagger.annotations.ApiOpera ...

  6. 微信服务商-小程序支付-商户传入的appid参数不正确,请联系商户处理

    报错:商户传入的appid参数不正确,请联系商户处理. 处理方式: 1. 2.需要在商户号,配置小程序的APPID https://pay.weixin.qq.com/static/pay_setti ...

  7. 微信退款小程序支付/退款

    最近在写微信退款的时候发现了很多的Demo,但是方法都不同,而且很难确定Demo是不是适用自己的项目. 在发起退款的时候第一步自然还是先去下载证书,这个没话说,下载完成后在退款Controller可以 ...

  8. 微信小程序支付接口报错:appid和openid不匹配

    一.接口地址 二.接口几个字段讲解 三.报错的原因 四.解决方法 一.接口地址 地址在 -> 微信官方文档 -> 微信支付 -> 小程序支付 -> 统一下单 对比 V2-旧版, ...

  9. 微信支付,JSAPI支付,APP支付,H5支付,Native支付,小程序支付功能详情以及回调处理

    一.支付相关文档地址 支付wiki:https://pay.weixin.qq.com/wiki/doc/apiv3/index.shtml 支付api: https://pay.weixin.qq. ...

  10. 微信小程序支付(1)Uni-app平台API接口

    uni-app 是一个使用 Vue.js 开发所有前端应用的框架,开发者编写一套代码,可发布到iOS.Android.Web(响应式).以及各种小程序(微信/支付宝/百度/头条/QQ/钉钉/淘宝).快 ...

最新文章

  1. (三)硕博生常用的英文文献下载的网站
  2. Bellman-Ford算法——为什么要循环n-1次?图有n个点,又不能有回路,所以最短路径最多n-1边。又因为每次循环,至少relax一边所以最多n-1次就行了!...
  3. 3、事件响应函数(一)
  4. 响应式编程 函数式编程_函数式编程的基本原理简介
  5. MYSQL DELETE 别名
  6. 2018-06-18
  7. java平台无关性_为什么Java能够实现平台无关性?
  8. python怎么向列表中添加内容_Python中向List添加元素方法
  9. 小学三年级计算机基础知识课件,小学三年级信息技术基础知识ppt课件.ppt
  10. 在线VLOOKUP数据查找工具
  11. AIX添加ASM的裸盘
  12. iTextSharp 添加文字内容
  13. sublime text3格式化代码快捷键
  14. 初学者如何快速搭建一个属于自己的网站
  15. 测试人员的KPI考核指标
  16. 3DS动物之森(animal acrossing)游戏记录
  17. 【spring aop】连接点(Jointpoint)、切入点(Pointcut)、增强(Advice)、方面/切面(Aspect、Advisor)
  18. Android CMake 编译so库
  19. JVM<一>内存管理[三]垃圾回收器
  20. 领导艺术-八项管理定律

热门文章

  1. 在腾讯这一年,坚守初心持续单纯!
  2. 云服务器可以虚拟内存吗,云服务器可以设置虚拟内存吗
  3. 字体图标之Symbol用法
  4. 前端开发:JS中截取字符串的用法总结
  5. java初级程序员考试_Java初级程序员必须要知道的10个基础面试题
  6. Excel排序,数据透视图,公式预测
  7. python leetcode 387. First Unique Character in a String
  8. 启动数据库MySQL
  9. 线程,进程,线程安全的理解
  10. 【nn.LSTM详解】