Java微信公众平台开发之公众号支付(微信内H5调起支付)
官方文档
准备工作:已通过微信认证的公众号,必须通过ICP备案域名(否则会报支付失败)
借鉴了很多大神的文章,在此先谢过了
整个支付流程,看懂就很好写了
一、设置支付目录
在微信公众平台设置您的公众号支付支付目录,设置路径见下图。公众号支付在请求支付的时候会校验请求来源是否有在公众平台做了配置,所以必须确保支付目录已经正确的被配置,否则将验证失败,请求支付不成功。
支付授权目录就是指支付方法的请求全路径
二、设置授权域名
开发公众号支付时,在统一下单接口中要求必传用户openid,而获取openid则需要您在公众平台设置获取openid的域名,只有被设置过的域名才是一个有效的获取openid的域名,否则将获取失败。具体界面如下图所示:
三、授权进入授权目录
授权暂时参考我的另一篇的文章,点我参考 ,根据实际需求,我是采用静默授权
/**
* 授权进入支付页面
*
* @param request
* @param response
* @param url
* @return
* @throws Exception
*/
@RequestMapping(value = "prePayPage", method = { RequestMethod.GET })
public String jsPay(HttpServletRequest request, HttpServletResponse response) throws Exception {
AuthAccessToken authAccessToken = null;
String code = request.getParameter("code");//可用redis保存
if(code==null){
return null;
}
String state = request.getParameter("state");
if(state.equals(MD5Util.MD5Encode("ceshi", ""))){
AuthTokenParams authTokenParams = new AuthTokenParams();
authTokenParams.setAppid("your appid");
authTokenParams.setSecret(""your secret");
authTokenParams.setCode(code);
authAccessToken = oAuthService.getAuthAccessToken(authTokenParams, ACCESS_TOKEN_URL);
}
if(authAccessToken!=null){
logger.info("正在支付的openid=" + authAccessToken.getOpenid());
}
return "system/wxpay/testpay";
}
AuthTokenParams.java
/**
* 获取授权请求token的请求参数
* @author phil
* @date 2017年7月2日
*
*/
public class AuthTokenParams extends AbstractParams {
private String appid; //公众号的唯一标识
private String secret; //公众号的appsecret
private String code; //填写第一步获取的code参数
private String grant_type = "authorization_code"; public AuthTokenParams() {
super();
} public AuthTokenParams(String appid, String secret, String code, String grant_type) {
super();
this.appid = appid;
this.secret = secret;
this.code = code;
this.grant_type = grant_type;
} /**
* 参数组装
* @return
*/
public Map<String, String> getParams() {
Map<String, String> params = new TreeMap<String, String>();
params.put("appid", this.appid);
params.put("secret", this.secret);
params.put("code", this.code);
params.put("grant_type", this.grant_type);
return params;
} /get、set方法
}
支付页面的body
<script type="text/javascript">
var prepay_id ;
var paySign ;
var appId ;
var timeStamp ;
var nonceStr ;
var packageStr ;
var signType ;
function pay(){
var url = '${ctx}/wxpay/jsPay';
$.ajax({
type:"post",
url:url,
dataType:"json",
data:{openId:'${openId}'},
success:function(data) {
if(data.resultCode == 'SUCCESS'){
appId = data.appId;
paySign = data.paySign;
timeStamp = data.timeStamp;
nonceStr = data.nonceStr;
packageStr = data.packageStr;
signType = data.signType;
callpay();
}else{
alert("统一下单失败");
}
}
});
} function onBridgeReady(){
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {
"appId":appId, //公众号名称,由商户传入
"paySign":paySign, //微信签名
"timeStamp":timeStamp, //时间戳,自1970年以来的秒数
"nonceStr":nonceStr , //随机串
"package":packageStr, //预支付交易会话标识
"signType":signType //微信签名方式
},
function(res){
if(res.err_msg == "get_brand_wcpay_request:ok" ) {
//window.location.replace("index.html");
alert('支付成功');
}else if(res.err_msg == "get_brand_wcpay_request:cancel"){
alert('支付取消');
}else if(res.err_msg == "get_brand_wcpay_request:fail" ){
alert('支付失败');
} //使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回 ok,但并不保证它绝对可靠。
}
);
}
function callpay(){
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();
}
}
</script>
四、统一下单获取prepay_id 并返回页面支付参数
统一下单的官方文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1 备注:如果获取prepay_id基本上支付成功了
/**
* 微信内H5调起支付
*
* @param request
* @param response
* @param openId
* @return
* @throws Exception
*/
@ResponseBody
@RequestMapping("jspay")
public JsPayResult jsPay(HttpServletRequest request, HttpServletResponse response, String openId) throws Exception {
JsPayResult result = null;
logger.info("****正在支付的openId****" + openId);
// 统一下单
String out_trade_no = PayUtil.createOutTradeNo();
int total_fee = 1; // 产品价格1分钱,用于测试
String spbill_create_ip = HttpReqUtil.getRemortIP(request);
logger.info("支付IP" + spbill_create_ip);
String nonce_str = PayUtil.createNonceStr(); // 随机数据
//参数组装
UnifiedOrderParams unifiedOrderParams = new UnifiedOrderParams();
unifiedOrderParams.setAppid(WeChatConfig.APP_ID);// 必须
unifiedOrderParams.setMch_id(WeChatConfig.MCH_ID);// 必须
unifiedOrderParams.setOut_trade_no(out_trade_no);// 必须
unifiedOrderParams.setBody("支付测试");// 必须
unifiedOrderParams.setTotal_fee(total_fee); // 必须
unifiedOrderParams.setNonce_str(nonce_str); // 必须
unifiedOrderParams.setSpbill_create_ip(spbill_create_ip); // 必须
unifiedOrderParams.setTrade_type("JSAPI"); // 必须
unifiedOrderParams.setOpenid(openId);
unifiedOrderParams.setNotify_url(WeChatConfig.NOTIFY_URL);// 异步通知url
// 统一下单 请求的Xml(正常的xml格式)
String unifiedXmL = wechatPayService.abstractPayToXml(unifiedOrderParams);////签名并入service
// 返回<![CDATA[SUCCESS]]>格式的XML
String unifiedOrderResultXmL = HttpReqUtil.HttpsDefaultExecute(HttpReqUtil.POST_METHOD,WeChatConfig.UNIFIED_ORDER_URL, null, unifiedXmL);
// 进行签名校验
if (SignatureUtil.checkIsSignValidFromWeiXin(unifiedOrderResultXmL)) {
String timeStamp = PayUtil.createTimeStamp();
//统一下单响应
UnifiedOrderResult unifiedOrderResult = XmlUtil.getObjectFromXML(unifiedOrderResultXmL, UnifiedOrderResult.class);
/**** 用map方式进行签名 ****/
// SortedMap<Object, Object> signMap = new TreeMap<Object,
// Object>();
// signMap.put("appId", WeiXinConfig.APP_ID); // 必须
// signMap.put("timeStamp", timeStamp);
// signMap.put("nonceStr", nonceStr);
// signMap.put("signType", "MD5");
// signMap.put("package", "prepay_id=" + prepay_id);
// String paySign = SignatureUtil.createSign(signMap, key, SystemConfig.CHARACTER_ENCODING);
result = new JsPayResult();
result.setAppId(WeChatConfig.APP_ID);
result.setTimeStamp(timeStamp);
result.setNonceStr(unifiedOrderResult.getNonce_str());//直接用返回的
/**** prepay_id 2小时内都有效,再次支付方法自己重写 ****/
result.setPackageStr("prepay_id=" + unifiedOrderResult.getPrepay_id());
/**** 用对象进行签名 ****/
String paySign = SignatureUtil.createSign(result, WeChatConfig.API_KEY, SystemConfig.CHARACTER_ENCODING);
result.setPaySign(paySign);
result.setResultCode("SUCCESS");
} else {
logger.info("签名验证错误");
}
/**** 返回对象给页面 ****/
return result;
}
JsPayResult.java
package com.phil.wechatpay.model.resp; import com.phil.wechatpay.model.rep.JsPayParams; /**
* 微信内H5返回结果
* @author phil
* @date 2017年6月27日
*
*/
public class JsPayResult extends JsPayParams { /**
*
*/
private static final long serialVersionUID = 392188712101246402L; private String errMsg; private String resultCode; public String getErrMsg() {
return errMsg;
} public void setErrMsg(String errMsg) {
this.errMsg = errMsg;
} public String getResultCode() {
return resultCode;
} public void setResultCode(String resultCode) {
this.resultCode = resultCode;
} }
JsPayParams.java
package com.phil.wechatpay.model.rep; import java.io.Serializable; /**
* 微信内H5调起支付参数
* @author phil
* @date 2017年6月27日
*
*/
public class JsPayParams implements Serializable{ /**
*
*/
private static final long serialVersionUID = 8255883197124904824L; private String appId; // 公众号id private String timeStamp; // 时间戳 格式1414561699 private String nonceStr; // 随机字符串 private String packageStr; // package参数 订单详情扩展字符串 prepay_id=*** private String signType = "MD5"; // 签名方式 private String paySign; // 签名 public String getAppId() {
return appId;
} public void setAppId(String appId) {
this.appId = appId;
} public String getTimeStamp() {
return timeStamp;
} public void setTimeStamp(String timeStamp) {
this.timeStamp = timeStamp;
} public String getNonceStr() {
return nonceStr;
} public void setNonceStr(String nonceStr) {
this.nonceStr = nonceStr;
} public String getPackageStr() {
return packageStr;
} public void setPackageStr(String packageStr) {
this.packageStr = packageStr;
} public String getSignType() {
return signType;
} public void setSignType(String signType) {
this.signType = signType;
} public String getPaySign() {
return paySign;
} public void setPaySign(String paySign) {
this.paySign = paySign;
} }
五、完成支付并通知支付结果
支付通知结果官方文档 https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7
package com.phil.wechatpay.controller; import java.io.BufferedOutputStream; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody; import com.phil.common.result.ResultState;
import com.phil.common.util.HttpReqUtil;
import com.phil.common.util.SignatureUtil;
import com.phil.common.util.XmlUtil;
import com.phil.wechatpay.model.resp.PayNotifyResult; /**
* 微信支付结果通知(统一下单参数的notify_url)
* @author phil
* @date 2017年6月27日
*
*/
@Controller
@RequestMapping("/wxpay/")
public class WechatPayNotifyController { static final Logger logger = Logger.getLogger(WechatPayNotifyController.class); @ResponseBody
@RequestMapping("notify")
public ResultState notify(HttpServletRequest request, HttpServletResponse response) throws Exception {
ResultState resultState = new ResultState();
logger.info("开始处理支付返回的请求");
String resXml = ""; // 反馈给微信服务器
String notifyXml = HttpReqUtil.inputStreamToStrFromByte(request.getInputStream());// 微信支付系统发送的数据(<![CDATA[product_001]]>格式)
logger.debug("微信支付系统发送的数据"+notifyXml);
// 验证签名
if (SignatureUtil.checkIsSignValidFromWeiXin(notifyXml)) {
PayNotifyResult notify = XmlUtil.getObjectFromXML(notifyXml, PayNotifyResult.class);
logger.debug("支付结果" + notify.toString());
if ("SUCCESS".equals(notify.getResult_code())) {
resultState.setErrcode(0);// 表示成功
resultState.setErrmsg(notify.getResult_code());
/**** 业务逻辑 保存openid之类的****/
// 通知微信.异步确认成功.必写.不然会一直通知后台.八次之后就认为交易失败了
resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>" + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
} else {
resultState.setErrcode(-1);// 支付失败
resultState.setErrmsg(notify.getErr_code_des());
logger.info("支付失败,错误信息:" + notify.getErr_code_des());
resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[" + notify.getErr_code_des() + "]]></return_msg>" + "</xml> ";
}
} else {
resultState.setErrcode(-1);// 支付失败
resultState.setErrmsg("签名验证错误");
logger.info("签名验证错误");
resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[签名验证错误]]></return_msg>" + "</xml> ";
} BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
out.write(resXml.getBytes());
out.flush();
out.close();
return resultState;
}
}
ResultState.java
package com.phil.common.result; import java.io.Serializable; /**
* 微信API返回状态
*
* @author phil
* @date 2017年7月2日
*
*/
public class ResultState implements Serializable { /**
*
*/
private static final long serialVersionUID = 1692432930341768342L; //@SerializedName("errcode")
private int errcode; // 状态 //@SerializedName("errmsg")
private String errmsg; //信息 public int getErrcode() {
return errcode;
} public void setErrcode(int errcode) {
this.errcode = errcode;
} public String getErrmsg() {
return errmsg;
} public void setErrmsg(String errmsg) {
this.errmsg = errmsg;
}
}
Java微信公众平台开发之公众号支付(微信内H5调起支付)的更多相关文章
- vue 微信内H5调起支付
在微信内H5调起微信支付,主要依赖于一个微信的内置对象WeixinJSBridge,这个对象在其他浏览器中无效. 主要代码: import axios from 'axios'; export def ...
- 微信公众平台开发—利用OAuth2.0获取微信用户基本信息
在借鉴前两篇获取微信用户基本信息的基础下,本人也总结整理了一些个人笔记:如何通过OAuth2.0获取微信用户信息 1.首先在某微信平台下配置OAuth2.0授权回调页面: 2.通过appid构造url ...
- 微信公众平台开发教程(十一)微信"企业号“上线
什么是企业号? 企业号是微信为企业客户提供的移动应用入口 关注更安全 只有企业通讯录的成员才能关注企业号,分级管理员.保密消息等各种特性确保企业内部信息的安全. 应用可配置 企业可自行在企业号中可配置 ...
- 第六篇 :微信公众平台开发实战Java版之如何自定义微信公众号菜单
我们来了解一下 自定义菜单创建接口: http请求方式:POST(请使用https协议) https://api.weixin.qq.com/cgi-bin/menu/create?access_to ...
- 第五篇 :微信公众平台开发实战Java版之如何获取公众号的access_token以及缓存access_token
一.access_token简介 为了使第三方开发者能够为用户提供更多更有价值的个性化服务,微信公众平台 开放了许多接口,包括自定义菜单接口.客服接口.获取用户信息接口.用户分组接口.群发接口等, 开 ...
- 第九篇 :微信公众平台开发实战Java版之如何实现自定义分享内容
第一部分:微信JS-SDK介绍 微信JS-SDK是微信公众平台面向网页开发者提供的基于微信内的网页开发工具包. 通过使用微信JS-SDK,网页开发者可借助微信高效地使用拍照.选图.语音.位置等手机系统 ...
- 第八篇 :微信公众平台开发实战Java版之如何网页授权获取用户基本信息
第一部分:微信授权获取基本信息的介绍 我们首先来看看官方的文档怎么说: 如果用户在微信客户端中访问第三方网页,公众号可以通过微信网页授权机制,来获取用户基本信息,进而实现业务逻辑. 关于网页授权回调域 ...
- 第七篇 :微信公众平台开发实战Java版之如何获取微信用户基本信息
在关注者与公众号产生消息交互后,公众号可获得关注者的OpenID(加密后的微信号,每个用户对每个公众号的OpenID是唯一的.对于不同公众号,同一用户的openid不同). 公众号可通过本接口来根据O ...
- 第四篇 :微信公众平台开发实战Java版之完成消息接受与相应以及消息的处理
温馨提示: 这篇文章是依赖前几篇的文章的. 第一篇:微信公众平台开发实战之了解微信公众平台基础知识以及资料准备 第二篇 :微信公众平台开发实战之开启开发者模式,接入微信公众平台开发 第三篇 :微信公众 ...
随机推荐
- as 快捷键
as 快捷键 Ctrl+G / Ctrl+Alt+Shift+G:查询变量或者函数或者类在哪里被使用或被调用,后者是前者的复杂表现,可以选择查询范围等. Alt+H:查找功能,全局查找 F4:查看类 ...
- create groups 和 create folder reference
当将文件拖入工程中的时候会出现这个对话框,这个对话框中在Added folders中有两种选择:Create groups 和 Create folder references 这两种的区别是 ...
- java excel导出
下面是jsp代码: <li class="btns"><input id="btnExport" class="btn btn-pr ...
- Android Studio 运行java程序
当我们装了Android Studio 学习安卓开发的时候,难免会要学习java,这时候,难道在重新装一个编译器吗?不需要,我们直接用 Android Studio 就可以. 1.新建一个空项目,选择 ...
- linux 配置Apache 、PHP
1. 安装 Apache 安装apache,首先要使用管理员权限,如果如法获取请联系您的管理员. centos: yum install httpd httpd-devel 2. 安装PHP 同样也需 ...
- Luogu 1402 酒店之王(二分图最大匹配)
Luogu 1402 酒店之王(二分图最大匹配) Description XX酒店的老板想成为酒店之王,本着这种希望,第一步要将酒店变得人性化.由于很多来住店的旅客有自己喜好的房间色调.阳光等,也有自 ...
- Echarts报错[MODULE_MISS]"echarts/config" is not exists!
项目用到Echarts插件,时下比较流行的是模块化包引入,但是很悲催的是楼主用的是标签式引入,所以从官网copy来的代码总是报一个 [MODULE_MISS]"echarts/config的 ...
- 理解Linux文件系统之inode
很少转发别人的文章,但是这篇写的太好了. 理解inode 作者: 阮一峰 inode是一个重要概念,是理解Unix/Linux文件系统和硬盘储存的基础. 我觉得,理解inode,不仅有助于提高系统 ...
- volume 生命周期管理 - 每天5分钟玩转 Docker 容器技术(44)
Data Volume 中存放的是重要的应用数据,如何管理 volume 对应用至关重要.前面我们主要关注的是 volume 的创建.共享和使用,本节将讨论如何备份.恢复.迁移和销毁 volume. ...
- localStorage和sessionStorage的使用方法和一些特性介绍
本文主要介绍的是localStorage和sessionStorage的使用方法和一些特性,以及一些其他的存储方式的比较. 客服端存储方案包括以下几种: 1.Cookie 2.Us ...