Native支付是指商户系统按微信支付协议生成支付二维码,用户再用微信“扫一扫”完成支付的模式。
微信商户平台
微信支付 - 中国领先的第三方支付平台 | 微信支付提供安全快捷的支付方式微信支付是腾讯公司的支付业务品牌,微信支付商户平台支持线下场所、公众号、小程序、PC网站、APP、企业微信等经营场景快速接入微信支付。微信支付全面打通O2O生活消费领域,提供专业的互联网+行业解决方案,微信支付支持微信红包和微信理财通,是移动支付的首选。https://pay.weixin.qq.com/index.php/core/
账户中心->商户信息->微信支付商户号
参照文档:
什么是商户API证书?如何获取商户API证书?腾讯客服成立于2002年,承接了腾讯集团的所有业务的海量用户服务,一直以来致力于依托先进的互联网技术,致力于通过建设多元化特色渠道,智能化解决用户问题,同时传递用户心声,助力产品优化体验,成为业界领先的互联网客服团队。https://kf.qq.com/faq/161222NneAJf161222U7fARv.html得到三个文件apiclient_cert.p12,apiclient_cert.pem,apiclient_key.pem
账户中心->API安全->API证书管理
参照文档:
什么是APIv3密钥?如何设置?腾讯客服成立于2002年,承接了腾讯集团的所有业务的海量用户服务,一直以来致力于依托先进的互联网技术,致力于通过建设多元化特色渠道,智能化解决用户问题,同时传递用户心声,助力产品优化体验,成为业界领先的互联网客服团队。https://kf.qq.com/faq/180830E36vyQ180830AZFZvu.html
产品中心->AppID账号管理
https://github.com/wechatpay-apiv3/CertificateDownloaderhttps://github.com/wechatpay-apiv3/CertificateDownloader
Certificate Downloader 是 Java 微信支付 APIv3 平台证书的命令行下载工具。该工具可从 https://api.mch.weixin.qq.com/v3/certificates 接口获取商户可用证书,并使用 APIv3 密钥和AES_256_GCM 算法进行解密,并把解密后证书下载到指定位置。
完整命令:
java -jar CertificateDownloader.jar -k ${apiV3key} -m ${mchId} -f ${mchPrivateKeyFilePath} -s ${mchSerialNo} -o ${outputFilePath} -c ${wechatpayCertificateFilePath}
必需参数有:
非必需参数有:
对于微信支付平台的应答,需要使用平台证书来进行验签;但平台证书只能通过获取平台证书接口下载,所以当第一次去获取证书时,会出现个“死循环”。
为解决这个“死循环”,可以临时跳过验签,来获得证书。也就是说可以不提供微信支付证书参数(-c 参数)来下载,
错误:如果命令出现“java.security.InvalidKeyException: Illegal key size”的错误,应该是jdk版本导致微信支付256位密钥加解密失败。
解决办法:
下载最新版本的jdk1.8
下载JCE无限制权限策略文件local_policy.jar
https://wwi.lanzoup.com/iXGs404zm1dg
解压后
替换jdk1.8.0_xxx/jre/lib/security/目录下的local_policy.jar
产品中心->我的产品->Native支付
com.github.wechatpay-apiv3
wechatpay-java
0.2.6
接口文档:
微信支付-开发者文档https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_4_1.shtmlJava实现代码:
/** 商户号 */
private static String merchantId = "";
/** 商户API私钥路径 */
private static String privateKeyPath = "xxxx/apiclient_key.pem";
/** 商户证书序列号 */
private static String merchantSerialNumber = "";
/** 商户证书路径 */
private static String wechatPayCertificatePath = "xxxx/output.pem";
/** APPID */
private static String appID = "";
/** 支付成功回调地址 */
private static String notifyUrl = "";
private static NativePayService service;
/**
* 微信预支付
*/
public static String prepareWXPay(String title, float money, String outTradeNo) {
// 初始化商户配置
if (service == null) {
RSAConfig config =
new RSAConfig.Builder()
.merchantId(merchantId)
.privateKeyFromPath(privateKeyPath)
.merchantSerialNumber(merchantSerialNumber)
.wechatPayCertificatesFromPath(wechatPayCertificatePath)
.build();
// 初始化服务
service = new NativePayService.Builder().config(config).build();
}
PrepayRequest request = new PrepayRequest();
request.setAppid(appID);
request.setMchid(merchantId);
request.setOutTradeNo(outTradeNo);
request.setDescription(title);
request.setNotifyUrl(notifyUrl);
Amount amount = new Amount();
amount.setTotal(Convert.toInt(money * 100));
amount.setCurrency("CNY");
request.setAmount(amount);
// 调用接口
PrepayResponse resp = service.prepay(request);
if (resp != null) {
return resp.getCodeUrl();
}
return null;
}
接口调用成功后会获得二维码的串,转成二维码后把二维码图片链接返给前端,或者直接把串丢给前端让前端自己生成二维码。
/**
* 微信支付通知
*/
@RequestMapping(value = "/wxpayNotifyUrl", method = RequestMethod.POST)
@ResponseBody
public void wxPayNotify(@RequestBody JSONObject jsonObject) {
log.info("wxpayNotify:{}", jsonObject);
log.info("event_type:{}, summary:{}", jsonObject.getString("event_type"), jsonObject.getString("summary"));
if (jsonObject.getString("event_type").equals("TRANSACTION.SUCCESS")) {
JSONObject resource = jsonObject.getJSONObject("resource");
String ret = WXPayUtil.verifyPayNotify(resource);
log.info("ret:{}", ret);
if (ret != null) {
JSONObject retObj = JSON.parseObject(ret);
if (retObj.getString("trade_state").equals("SUCCESS")) {
log.info(retObj.getString("trade_state_desc"));
onWXPaySuccess(retObj.getString("out_trade_no"));
}
else {
log.info(retObj.getString("trade_state_desc"));
onWXPayFailure(retObj.getString("out_trade_no"));
}
}
}
}
支付回调验签
/**
* 支付回调验签
* @param resource 回调信息
*/
public static String verifyPayNotify(JSONObject resource) {
String associated_data = resource.getString("associated_data");
String cipherText = resource.getString("ciphertext");
String nonce = resource.getString("nonce");
String ret = null;
try {
String decryptData = new AesUtil(apiV3Key.getBytes(StandardCharsets.UTF_8)).decryptToString(associated_data.getBytes(StandardCharsets.UTF_8), nonce.getBytes(StandardCharsets.UTF_8), cipherText);
System.out.println("decryptData = " + decryptData);
ret = decryptData;
//TODO 业务校验
} catch (Exception e) {
System.out.println("e = " + e);
}
return ret;
}