请确保实际支付时的请求目录与后台配置的目录一致(现在已经支持配置根目录,配置后有一定的生效时间,一般5分钟内生效),否则将无法成功唤起微信支付。
在微信商户平台(pay.weixin.qq.com)设置您的JSAPI支付支付目录,设置路径:商户平台–>产品中心–>开发配置,如图7.7所示。JSAPI支付在请求支付的时候会校验请求来源是否有在商户平台做了配置,所以必须确保支付目录已经正确的被配置,否则将验证失败,请求支付不成功。
开发JSAPI支付时,在统一下单接口中要求必传用户openid,而获取openid则需要您在公众平台设置获取openid的域名,只有被设置过的域名才是一个有效的获取openid的域名,否则将获取失败。具体界面如图7.8所示:
参考链接(请在微信客户端中打开此链接体验)
Scope为snsapi_base ,此连接为静默获取用户授权
https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx520c15f417810387&redirect_uri=http%3A%2F%2Fchong.qq.com%2Fphp%2Findex.php%3Fd%3D%26c%3DwxAdapter%26m%3DmobileDeal%26showwxpaytitle%3D1%26vb2ctag%3D4_2030_5_1194_60&response_type=code&scope=snsapi_base&state=123#wechat_redirect
https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
/**
* 微信内浏览器支付
*
* @return
*/
@PostMapping("/createJSAPI")
@ApiOperation("微信内支付,orderId订单ID,openId用户唯一识别ID")
public DzResult createJSAPI(String orderId, String openId) throws Exception {
if (StringUtils.isBlank(orderId) || StringUtils.isBlank(openId)) {
System.out.println("微信内支付空订单号-------------------->");
return new DzResult().error(OrderCode.ERROR_PARAMS, "参数错误");
}
// 获取当前用户
Claims claims = (Claims) request.getAttribute("authInfo");
if (claims == null) {
return new DzResult().error(OrderCode.NOT_LEGAL, "非法操作");
}
// 调用业务层查询订单信息
CyOrder order = weixinPayService.findOrderByOrderId(orderId);
if (order == null) {
return new DzResult().error(OrderCode.ORDER_NOT_EXIST, "订单不存在");
}
if (!claims.getId().equals(order.getFrontUserId())) {
return new DzResult().error(OrderCode.NOT_BELONG, "订单不属于当前用户");
}
// 商户订单号(用下划线后面数字为标识1:支付定金 2:支付尾款)
BigDecimal totalAmount;
// 将数据库价格去分
BigDecimal mun = BigDecimal.valueOf(100);
// 名称
String subject;
//设置商品详情参数
HashMap attach = new HashMap<>(16);
// 订单号
String outTradeNo = orderId;
//订单号
attach.put("outTradeNo", outTradeNo);
//自定义参数
String attachStr;
//全款
//微信是按分为单位,此处*100去分:
totalAmount = order.getPayMoney().multiply(mun);
subject = "呈衣全款支付";
attach.put("subject", subject);
//去掉小数点后的零
attach.put("totalAmount", totalAmount.stripTrailingZeros().toPlainString());
attachStr = JSONUtils.toJSONString(attach);
Map map = weixinPayService.createJSAPI(outTradeNo, totalAmount.stripTrailingZeros().toPlainString(), attachStr, openId);
if (map == null) {
return new DzResult().error(OrderCode.ORDER_WECAT_ERROR, "微信支付异常");
}
if ("FAIL".equals(map.get("return_code"))) {
System.out.println("通讯异常");
Object errorMsg = map.get("return_msg");
return new DzResult().error(OrderCode.ORDER_WECAT_ERROR, errorMsg + "");
}
if ("FAIL".equals(map.get("result_code"))) {
System.out.println("支付错误");
Object errCode = map.get("err_code");
Object errCodeDes = map.get("err_code_des");
String errorMsg = errCode + " : " + errCodeDes;
return new DzResult().error(OrderCode.ORDER_WECAT_ERROR, errorMsg + "");
}
System.out.println("调统一下单接口返回的结果都OK...............");
boolean haveMwebUrl = map.containsKey("mweb_url");
System.out.println("是否有拉起微信APP的路径:" + haveMwebUrl);
boolean havePrepay_id = map.containsKey("prepay_id");
System.out.println("是否有,预支付交易会话标识:" + havePrepay_id);
//新建一个map集合
Map resMap = new HashMap<>(16);
//appID
resMap.put("appId", WeChatPayConfig.app_id);
System.out.println("appId:-->" + resMap.get("appId").toString());
//时间戳
resMap.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000));
System.out.println("timeStamp:-->" + resMap.get("timeStamp").toString());
//随机字符串
resMap.put("nonceStr", WXPayUtil.generateNonceStr());
System.out.println("nonceStr:-->" + resMap.get("nonceStr").toString());
//订单详情扩展字段
resMap.put("package", "prepay_id="+map.get("prepay_id").toString());
System.out.println("package:-->" + map.get("prepay_id").toString());
//签名方式
resMap.put("signType", "MD5");
String paySign = WXPayUtil.generateSignature(resMap, WeChatPayConfig.partner_key);
//签名
resMap.put("paySign", paySign);
System.out.println("paySign:-->" + resMap.get("paySign").toString());
//返回给前端
map.put("WCPayRequest", resMap);
return new DzResult().success(map);
/**
* 微信浏览器内调用JSAPI支付
*
* @param outTradeNo
* @param totalFee
* @param attachStr
* @param openID
* @return
*/
@Override
public Map createJSAPI(String outTradeNo, String totalFee, String attachStr, String openID) {
System.out.println("进入微信内浏览器支付方法");
System.out.println("传过来的订单号:"+outTradeNo);
System.out.println("传过来的金额:"+totalFee);
System.out.println("传过来的自定义参数:"+attachStr);
System.out.println("传过来的openid:"+openID);
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
//设置失效时间5分钟
String expireTime = getOrderExpireTime(300 * 1000L);
//1.创建参数
HashMap param = new HashMap<>(16);
//公账号
param.put("appid", WeChatPayConfig.app_id);
//自定义参数(attach)
param.put("attach", attachStr);
//商品描述:商品名字
param.put("body", "呈衣定制");
//商户号
param.put("mch_id", WeChatPayConfig.partner);
//随机字符串
param.put("nonce_str", WXPayUtil.generateNonceStr());
//回调地址
param.put("notify_url", WeChatPayConfig.notify_url);
//用户的openID,JSAPI支付必填
param.put("openid", openID);
//订单号
param.put("out_trade_no", outTradeNo);
//ip,
param.put("spbill_create_ip", getIpAddr(request));
//设置失效时间5分钟
param.put("time_expire", expireTime);
//总金额(分)
param.put("total_fee", totalFee);
//交易类型
param.put("trade_type", "JSAPI");
System.out.println("appid"+param.get("appid").toString());
System.out.println("attach"+param.get("attach").toString());
System.out.println("body"+param.get("body").toString());
System.out.println("mch_id"+param.get("mch_id").toString());
System.out.println("nonce_str"+param.get("nonce_str").toString());
System.out.println("notify_url"+param.get("notify_url").toString());
System.out.println("openid"+param.get("openid").toString());
System.out.println("out_trade_no"+param.get("out_trade_no").toString());
System.out.println("spbill_create_ip"+param.get("spbill_create_ip").toString());
System.out.println("time_expire"+param.get("time_expire").toString());
System.out.println("total_fee"+param.get("total_fee").toString());
System.out.println("trade_type"+param.get("trade_type").toString());
System.out.println("partner_key"+WeChatPayConfig.partner_key);
try {
//2.生成要发送的xml,方法中传入签名
String xmlParam = WXPayUtil.generateSignedXml(param, WeChatPayConfig.partner_key);
System.out.println("调用微信统一下单接口,请求的参数" + xmlParam);
//请求的url地址
HttpClient httpclient = new HttpClient(WeChatPayConfig.request_url);
//是否是https协议
httpclient.setHttps(true);
//发送的xml数据
httpclient.setXmlParam(xmlParam);
//执行的请求方法
httpclient.post();
Map returnMap = WXPayUtil.xmlToMap(httpclient.getContent());
//订单号
returnMap.put("out_trade_no", outTradeNo);
//自定义参数,交易信息
returnMap.put("attach", attachStr);
System.out.println("调用统一下单接口正常");
return returnMap;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
在微信浏览器里面打开H5网页中执行JS调起支付。接口输入输出数据格式为JSON。
注意:WeixinJSBridge内置对象在其他浏览器中无效。
getBrandWCPayRequest参数以及返回值定义:
1、网页端接口请求参数列表(参数需要重新进行签名计算,参与签名的参数为:appId、timeStamp、nonceStr、package、signType,参数区分大小写。)
名称 变量名 必填 类型 示例值 描述
公众号id appId 是 String(16) wx8888888888888888 商户注册具有支付权限的公众号成功后即可获得
时间戳 timeStamp 是 String(32) 1414561699 当前的时间,其他详见时间戳规则
随机字符串 nonceStr 是 String(32) 5K8264ILTKCH16CQ2502SI8ZNMTM67VS 随机字符串,不长于32位。推荐随机数生成算法
订单详情扩展字符串 package 是 String(128) prepay_id=123456789 统一下单接口返回的prepay_id参数值,提交格式如:prepay_id=***
签名方式 signType 是 String(32) MD5 签名类型,默认为MD5,支持HMAC-SHA256和MD5。注意此处需与统一下单的签名类型一致
签名 paySign 是 String(64) C380BEC2BFD727A4B6845133519F3AD6 签名,详见签名生成算法
2、返回结果值说明
返回值 描述
get_brand_wcpay_request:ok 支付成功
get_brand_wcpay_request:cancel 支付过程中用户取消
get_brand_wcpay_request:fail 支付失败
调用支付JSAPI缺少参数:total_fee
1、请检查预支付会话标识prepay_id是否已失效
2、请求的appid与下单接口的appid是否一致
注:JS API的返回结果get_brand_wcpay_request:ok仅在用户成功完成支付时返回。由于前端交互复杂,get_brand_wcpay_request:cancel或者get_brand_wcpay_request:fail可以统一处理为用户遇到错误或者主动放弃,不必细化区分。
示例代码如下:
function onBridgeReady(){
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {
"appId":"wx2421b1c4370ec43b", //公众号名称,由商户传入
"timeStamp":"1395712654", //时间戳,自1970年以来的秒数
"nonceStr":"e61463f8efa94090b1f366cccfbbb444", //随机串
"package":"prepay_id=u802345jgfjsdfgsdg888",
"signType":"MD5", //微信签名方式:
"paySign":"70EA570631E4BB79628FBCA90534C63FF7FADD89" //微信签名
},
function(res){
if(res.err_msg == "get_brand_wcpay_request:ok" ){
// 使用以上方式判断前端返回,微信团队郑重提示:
//res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
}
});
}
if (typeof WeixinJSBridge == "undefined"){
if( document.addEventListener ){
document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
}else if (document.attachEvent){
document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
}
}else{
onBridgeReady();
}