一、接入前准备,按照这个文档准备

准备:接入前准备-小程序支付 | 微信支付商户平台文档中心

准备好了就可以获得(第二点里需要的参数):
参数1商户号 merchantId:xxxxxx(全是数字)

参数2商户APIV3密钥 apiV3key:xxxxxxx(32位字母数字大小写串,开发自己准备的)

参数3商户证书序列号 merchantSerialNumber:xxxxx

查看方式:微信支付证书序列号在哪里看(v3商户证书序列号在哪里查找)-李飞SEO

参数4商户API私钥路径 privateKeyPath:apiclient_key.pem的文件路径

———————Native (二维码)支付下单,前四个就够了,5是用于Jsapi支付—————-
参数5国密的微信支付平台证书路径(X509文件) wechatPayCertificatePath:wechatpay_xxxx.pem()

这个文件找了好久,最后试用下边这个工具下下来可以用GitHub – wechatpay-apiv3/CertificateDownloader: Java 微信支付 APIv3 平台证书的命令行下载工具

二、支付代码接入

支付-官网文档:https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_7

微信给apiv3做了个maven包,终于像个样子了,结果我发现里边native支付例子虽然完整,但是第一大点里的1-5个参数完全不知道怎么给,jsapi参数同理,我是的东拼西凑凑齐了。maven包文档

贴一个jsapi的调用例子,小程序端唤起支付用它就够了,这是在官方的例子基础上补全了:

import com.alibaba.fastjson.JSONObject;import com.wechat.pay.java.core.Config;import com.wechat.pay.java.core.RSAConfig;import com.wechat.pay.java.core.exception.HttpException;import com.wechat.pay.java.core.exception.MalformedMessageException;import com.wechat.pay.java.core.exception.ServiceException;import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;import com.wechat.pay.java.service.payments.jsapi.model.CloseOrderRequest;import com.wechat.pay.java.service.payments.jsapi.model.Payer;import com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest;import com.wechat.pay.java.service.payments.jsapi.model.PrepayWithRequestPaymentResponse;import com.wechat.pay.java.service.payments.jsapi.model.QueryOrderByIdRequest;import com.wechat.pay.java.service.payments.jsapi.model.QueryOrderByOutTradeNoRequest;import com.wechat.pay.java.service.payments.model.Transaction;import com.wechat.pay.java.service.payments.jsapi.model.Amount;import lombok.extern.slf4j.Slf4j;@Slf4jpublic class JsapiDemoService {public static String merchantId = "1xxxx";//public static String privateKeyPath = "";public static String merchantSerialNumber = "xxxx";public static String wechatPayCertificatePath = "";public static com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension service;public static PrepayWithRequestPaymentResponse createWxJsapOrder(String certificatePath, String keyPath, JsapiReq cliReq) {String method = Thread.currentThread().getStackTrace()[1].getMethodName();// 使用自动更新平台证书的RSA配置// 一个商户号只能初始化一个配置,否则会因为重复的下载任务报错Config config =new RSAConfig.Builder().merchantId(merchantId)// 使用 com.wechat.pay.java.core.util 中的函数从本地文件中加载商户私钥,商户私钥会用来生成请求的签名.privateKeyFromPath(keyPath).merchantSerialNumber(merchantSerialNumber).wechatPayCertificatesFromPath(certificatePath).build();// 构建serviceJsapiServiceExtension service = new JsapiServiceExtension.Builder().config(config).build();// request.setXxx(val)设置所需参数,具体参数可见Request定义PrepayRequest request = new PrepayRequest();Payer payer = new Payer();payer.setOpenid(cliReq.getOpenid());request.setPayer(payer);Amount amount = new Amount();amount.setTotal(10);//订单总金额,单位为分request.setAmount(amount);request.setAppid("wxddddxxxxxx");request.setMchid("1xxxx");request.setDescription("ms");request.setNotifyUrl("https://xxx.net/callBackr");//request.setOutTradeNo("out_trade_no_1");request.setOutTradeNo(cliReq.getOutTradeNo());// 调用下单方法,得到应答try {// ... 调用接口PrepayWithRequestPaymentResponse response = service.prepayWithRequestPayment(request);// 使用微信扫描 code_url 对应的二维码,即可体验Native支付System.out.println(JSONObject.toJSON(response));return response;} catch (HttpException e) { // 发送HTTP请求失败// 调用e.getHttpRequest()获取请求打印日志或上报监控,更多方法见HttpException定义log.error(method, e);} catch (ServiceException e) { // 服务返回状态小于200或大于等于300,例如500// 调用e.getResponseBody()获取返回体打印日志或上报监控,更多方法见ServiceException定义log.error(method, e);} catch (MalformedMessageException e) { // 服务返回成功,返回体类型不合法,或者解析返回体失败// 调用e.getMessage()获取信息打印日志或上报监控,更多方法见MalformedMessageException定义log.error(method, e);} catch (Exception e) { // 服务返回成功,返回体类型不合法,或者解析返回体失败// 调用e.getMessage()获取信息打印日志或上报监控,更多方法见MalformedMessageException定义log.error(method, e);}return new PrepayWithRequestPaymentResponse();}/** 关闭订单 */public static void closeOrder() {CloseOrderRequest request = new CloseOrderRequest();// 调用request.setXxx(val)设置所需参数,具体参数可见Request定义// 调用接口service.closeOrder(request);}/** JSAPI支付下单,并返回JSAPI调起支付数据 */public static PrepayWithRequestPaymentResponse prepayWithRequestPayment() {PrepayRequest request = new PrepayRequest();// 调用request.setXxx(val)设置所需参数,具体参数可见Request定义// 调用接口return service.prepayWithRequestPayment(request);}/** 微信支付订单号查询订单 */public static Transaction queryOrderById() {QueryOrderByIdRequest request = new QueryOrderByIdRequest();// 调用request.setXxx(val)设置所需参数,具体参数可见Request定义// 调用接口return service.queryOrderById(request);}/** 商户订单号查询订单 */public static Transaction queryOrderByOutTradeNo() {QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();// 调用request.setXxx(val)设置所需参数,具体参数可见Request定义// 调用接口return service.queryOrderByOutTradeNo(request);}}

补下JsapiReq的定义

public class JsapiReq implements Serializable {private static final long serialVersionUID = 1L;private String openid;private String outTradeNo;}

另外记一下java/spring/srpingboot里resource文件读取路径的方法

String userDir = System.getProperty("user.dir");String certificatePath = userDir + "/src/main/resources/cert/wechatpay_xxx.pem";String keyPath = userDir + "/src/main/resources/cert/apiclient_xxxxkey.pem";

三、支付回调

支付回调-官网文档:微信支付-开发者文档

代码

@PostMapping(value = "/callBack")public Map callBack(@RequestBody JSONObject jsonObject) {String method = Thread.currentThread().getStackTrace()[1].getMethodName();try {String key = WxNativePayProxy.getWxV3Key();String json = jsonObject.toString();String associated_data = (String) JSONUtil.getByPath(JSONUtil.parse(json), "resource.associated_data");String ciphertext = (String) JSONUtil.getByPath(JSONUtil.parse(json), "resource.ciphertext");String nonce = (String) JSONUtil.getByPath(JSONUtil.parse(json), "resource.nonce");String decryptData = new AesUtil(key.getBytes(StandardCharsets.UTF_8)).decryptToString(associated_data.getBytes(StandardCharsets.UTF_8), nonce.getBytes(StandardCharsets.UTF_8), ciphertext);//验签成功JSONObject decryptDataObj = JSONObject.parseObject(decryptData, JSONObject.class);//decryptDataObj 为解码后的obj,其内容如下。之后便是验签成功后的业务处理//{//"sp_appid": "wx8888888888888888",//"sp_mchid": "1230000109",//"sub_appid": "wxd678efh567hg6999",//"sub_mchid": "1900000109",//"out_trade_no": "1217752501201407033233368018",//"trade_state_desc": "支付成功",//"trade_type": "MICROPAY",//"attach": "自定义数据",//"transaction_id": "1217752501201407033233368018",//"trade_state": "SUCCESS",//"bank_type": "CMC",//"success_time": "2018-06-08T10:34:56+08:00",//...//"payer": {//"openid": "oUpF8uMuAJO_M2pxb1Q9zNjWeS6o"//},//"scene_info": {//"device_id": "013467007045764"//}//}}catch (Exception e){log.info("{} ,parms{}, 异常:", method, jsonObject.toJSONString(), e);}Map res = new HashMap();res.put("code", "SUCCESS");res.put("message", "成功");return res;}

其中有用到微信解码工具类:AesUtil

import java.io.IOException;import java.security.GeneralSecurityException;import java.security.InvalidAlgorithmParameterException;import java.security.InvalidKeyException;import java.security.NoSuchAlgorithmException;import java.util.Base64;import javax.crypto.Cipher;import javax.crypto.NoSuchPaddingException;import javax.crypto.spec.GCMParameterSpec;import javax.crypto.spec.SecretKeySpec;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;}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);return new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)), "utf-8");} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {throw new IllegalStateException(e);} catch (InvalidKeyException | InvalidAlgorithmParameterException e) {throw new IllegalArgumentException(e);}}}