开发环境
java1.8
springboot 2.7.5
本文是以JSAPI支付为例子,其他支付完全可以以此参考
JSAPI支付适用于线下场所、公众号场景和PC网站场景。
商户已有H5商城网站,用户通过消息或扫描二维码在微信内打开网页时,可以调用微信支付完成下单购买的流程。
1、登陆已认证企业服务号,开通微信支付
注册微信商户号请参考官方文档,参考地址如下:
https://pay.weixin.qq.com/index.php/apply/applyment_home/guide_normal#none
商户开通JSAPI支付
商户到公众平台开通JSAPI支付
同时配置JSAPI回调域,授权目录为公网IP,本地测试要使用内网穿透
JSAPI接口
JSAPI支付时必要传入openid
前提配置
openid获取是在微信登录授权时获取的,他和access_token一同获取到,分为两步。
相关文档参考:
https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html#0
-
第一步:根据appid获取code()访问地址为:https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
-
注意在公众号中配置回调接受code的地址:redirect_uri
-
注意scope的类型
-
第二步:根据code,appid,appsecret获取openid,访问地址为:https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
正确访问后返回结果
微信支付的统一流程
1,后端向微信第三方发送预支付(其中包括支付详细信息),
2,将预支付的返回结果返回给前端,前端拉起支付界面
3,支付状态进入后端回调接口中,后端记录数据持久到数据库
V2接口实现支付
导入maven
<!--微信支付依赖-->
<dependency>
<groupId>com.github.tedzhdz</groupId>
<artifactId>wxpay-sdk</artifactId>
<version>3.0.10</version>
</dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-pay</artifactId>
<version>3.4.0</version>
</dependency>
接口参考文档:
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1
使用微信统一下单接口
- 红色的是自己填写的
- 蓝色是SDK生成的
接口实现
@Controller
public class WxPayController {
String appID = "xxxxxxx"; //公众号id
String mchID = "xxxxxxx"; //商户号
String appSecret = "xxxxxxx";//公众号idSecret
String key = "xxxxxxx"; //商户key,用于sign验证
//申请授权码地址
String notify_url = "xxxxxxx/jsapiPayback";
//授权回调地址
String state="";
//统一下单,接收openid
@GetMapping("/wxjspay")
public ModelAndView wxjspay(HttpServletRequest request,HttpServletResponse response) throws Exception {
//创建sdk客户端
WXPay wxPay = new WXPay(new WXPayConfigCustom());
//构造请求的参数
Map<String,String> requestParam = new HashMap<>();
requestParam.put("out_trade_no","10029293889");//订单号
requestParam.put("body", "iphone8");//订单描述
requestParam.put("fee_type", "CNY");//人民币
requestParam.put("total_fee", String.valueOf(1)); //金额
requestParam.put("spbill_create_ip", "");//客户端ip
requestParam.put("notify_url", "xxxxx/payback");//微信异步通知支付结果接口,
requestParam.put("trade_type", "JSAPI");
//从之前的步骤中拿到openid
String openid = ;
requestParam.put("openid",openid);
//调用统一下单接口
Map<String, String> resp = wxPay.unifiedOrder(requestParam);
//准备h5网页需要的数据
Map<String,String> jsapiPayParam = new HashMap<>();
jsapiPayParam.put("appId",appID);
jsapiPayParam.put("timeStamp",System.currentTimeMillis()/1000+"");
jsapiPayParam.put("nonceStr", UUID.randomUUID().toString());//随机字符串
jsapiPayParam.put("package","prepay_id="+resp.get("prepay_id"));
jsapiPayParam.put("signType","HMAC-SHA256");
jsapiPayParam.put("paySign", WXPayUtil.generateSignature(jsapiPayParam,key,WXPayConstants.SignType.HMACSHA256));
//将h5网页响应给前端
return new ModelAndView("wxpay",jsapiPayParam);
}
// 支付功能
@GetMapping("xxxxxxx/jsapiPayback")
public void h5payBack(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 读取回调数据
InputStream inputStream = request.getInputStream();
StringBuffer sb = new StringBuffer();
String s;
BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
while ((s = in.readLine()) != null) {
sb.append(s);
}
in.close();
inputStream.close();
// 解析xml成map
Map<String, Object> m = XmlUtil.xmlToMap(sb.toString());
// 过滤空 设置 TreeMap
SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();
Iterator<String> it = m.keySet().iterator();
while (it.hasNext()) {
String parameter = it.next();
Object parameterValue = m.get(parameter);
String v = "";
if (null != parameterValue) {
v = parameterValue.toString().trim();
}
packageParams.put(parameter, v);
}
System.out.println("packageParams=" + packageParams);
// 微信支付的API密钥
String key = environment.getProperty("wechat.pay.apiKey");
// 判断签名是否正确
if (WechatUtils.isTenpaySign(packageParams, key)) {
String resXml = "";
if ("SUCCESS".equals((String) packageParams.get("result_code"))) {
log.info("已支付, packageParams:{}", packageParams.toString());
//开始自己的逻辑
}
// 通知微信.异步确认成功.必写.不然会一直通知后台.八次之后就认为交易失败了.
resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
+ "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
} else {
log.info("支付失败,错误信息:" + packageParams.get("err_code"));
log.info("支付失败");
resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
+ "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";
}
response.getWriter().write(resXml);
} else {
log.info("通知签名验证失败");
}
}
V3接口实现支付
前置条件
- Java 1.8+。
- 成为微信支付商户。
- 商户 API 证书:指由商户申请的,包含商户的商户号、公司名称、公钥信息的证书。
- 商户 API 私钥:商户申请商户API证书时,会生成商户私钥,并保存在本地证书文件夹的文件 apiclient_key.pem 中。
- APIv3密钥:为了保证安全性,微信支付在回调通知和平台证书下载接口中,对关键信息进行了 AES-256-GCM 加密。APIv3密钥是加密时使用的对称密钥。
导入maven,这个maven是官方推荐maven,里面很多需要填写的已经封装好,非常推荐
<!--微信支付依赖-->
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-java</artifactId>
<version>0.2.7</version>
</dependency>
v3接口文档参考
https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_1.shtml
接口实现
@GetMapping("/wxjspay")
public ModelAndView wxjspay(HttpServletRequest request,HttpServletResponse response) throws Exception {
/** 商户号 */
public static String merchantId = "";
/** 商户API私钥路径 */
public static String privateKeyPath = "";
/** 商户证书序列号 */
public static String merchantSerialNumber = "";
/** 商户APIV3密钥 */
public static String apiV3key = "";
RSAConfig config =
new RSAConfig.Builder()
.merchantId(merchantId)
// 使用 com.wechat.pay.java.core.util 中的函数从本地文件中加载商户私钥,商户私钥会用来生成请求的签名
.privateKeyFromPath(privateKeyPath)
.merchantSerialNumber(merchantSerialNumber)
.wechatPayCertificatesFromPath(wechatPayCertificatePath)
.build();
// 初始化服务
service =
new JsapiServiceExtension.Builder()
.config(config)
.signType("RSA") // 不填默认为RSA
.build();
try {
// ... 调用接口
PrepayWithRequestPaymentResponse response = prepayWithRequestPayment();
System.out.println(response);
Map<String,String> jsapiPayParam = new HashMap<>();
jsapiPayParam.put("appId",appID);
jsapiPayParam.put("timeStamp",System.currentTimeMillis()/1000+"");
jsapiPayParam.put("nonceStr", UUID.randomUUID().toString());//随机字符串
jsapiPayParam.put("package","prepay_id="+resp.get("prepay_id"));
jsapiPayParam.put("signType","HMAC-SHA256");
jsapiPayParam.put("paySign", WXPayUtil.generateSignature(jsapiPayParam,key,WXPayConstants.SignType.HMACSHA256));
//将h5网页响应给前端
return new ModelAndView("wxpay",jsapiPayParam);
} catch (HttpException e) { // 发送HTTP请求失败
// 调用e.getHttpRequest()获取请求打印日志或上报监控,更多方法见HttpException定义
} catch (ServiceException e) { // 服务返回状态小于200或大于等于300,例如500
// 调用e.getResponseBody()获取返回体打印日志或上报监控,更多方法见ServiceException定义
} catch (MalformedMessageException e) { // 服务返回成功,返回体类型不合法,或者解析返回体失败
// 调用e.getMessage()获取信息打印日志或上报监控,更多方法见MalformedMessageException定义
}
}
/** 关闭订单 */
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);
}
}
回调处理见v2后面的**“xxxxxxx/jsapiPayback”**接口,v3与其一致
此文章包含部分参考
原文链接:https://blog.csdn.net/weixin_45865428/article/details/117807648