微信支付 php详解,微信支付之公众号支付详解

本文主要和大家分享微信支付之公众号支付详解,随着微信支付的流行,大多产品都开发了自己的公众号、小程序等,产品的营销需要支付的支撑,最近做了个微信公号号支付,采坑无数,今天给大家分享一下,希望能帮助到大家。

可以先看一下微信支付API文档

https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6

https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_4

https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419317853&token=&lang=zh_CN

建议先看一下微信的开发者文档,虽然有点坑 。。。。

https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_1

应用场景

所需jar包

xmlpull

xmlpull

1.1.3.1

xpp3

xpp3

1.1.4c

com.thoughtworks.xstream

xstream

1.4.7

org.apache.httpcomponents

fluent-hc

4.3.5

org.apache.httpcomponents

httpclient

4.3.5

org.apache.httpcomponents

httpclient-cache

4.3.5

org.apache.httpcomponents

httpcore

4.3.2

org.apache.httpcomponents

httpmime

4.3.5

公众号支付所需参数public class WeixinMatchPayConfigure {

/**

* 域名 项目域名,根据需求自行配置

*/

public static final String ROOTURL = WeixinPayConfigure.ROOTURL;

/**

* 订单域名 项目域名,根据需求自行配置

*/

public static final String ORDER_ROOTURL = WeixinPayConfigure.ORDER_ROOTURL;

/**

* 赛事 域名 项目域名,根据需求自行配置

*/

public static final String MATCHURL = "http://www.baidu.com";

/**

* 公共账号id 必填 (18位数↓)

*/

public static final String APPID = WeixinPayConfigure.APPID;

/**

* 商户id 商户账号 必填

*/

public static final String MCH_ID = "11111111";

/**

* 应用秘钥 必填(可在微信商户平台上查找)

*/

public static final String APP_SECRET = "fd87878fsf87fsf8cvsd8";

/**API秘钥*/ 必填(可在微信商户平台上查找)

public static final String API_KEY = "fsdfn23482njdvjw23455555";

/**

* 统一下单URL 微信官方提供

*/

public static final String PAY_UNIFIED_ORDER_API = "https://api.mch.weixin.qq.com/pay/unifiedorder";

/**

* 微信公众号交易类型 (扫码支付类型:NATIVE,公众号支付类型:JSAPI)

*/

public static final String TRADE_TYPE = "JSAPI";

/**

* 获取code的回调地址 你项目要展示的首页路径

*/

public static final String REDIRECT_URI = "http://order.uxuexi.com/pay/apply.html";

/**微信H5支付结果通知页*/

public static final String NOTIFY_URL = ROOTURL + "/api/pay/weixin/notify.html";

/**

* 不弹出授权页面,直接跳转,只能获取用户openid

*/

public static final String SCOPE = "snsapi_base";

/**

* 弹出授权页面,需要用户确认,可以获取用户的较多信息

*/

public static final String USERINFOSCOPE = "snsapi_userinfo";

/**

* 获取微信code的url(登录授权)

*/

public static final String GET_CODE_URL = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect";

/**

* 获取用户的OpenId的url(必须先获得code之后调用)

*/

public static final String GET_OPENID_URL = "https://api.weixin.qq.com/sns/oauth2/access_token";

/**

* 微信支付成功之后的回调

*/

public static final String NOTIFY_ACTIVITY_URL = WeixinPayConfigure.ORDER_ROOTURL + "/pay/wxnotify.json";

}

获取微信用户的openId

大致步骤:获取用户授权(获取code)------------->根据code获取openID(用户的基本信息)

1、配置授权域

这个则是在公众号登陆平台上面配置的↓

48175ffbcb8921a3fc1a312e9ccda5fa.png

2、发起API请求获取用户授权

URL:https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect

需要拼接三个参数:APPID、REDIRECT_URI、SCOPE,代码如下://项目入口,如果这个项目是在公众号中,那么公众号菜单下面配置的就是这个接口的路径↓

@At

@Ok("jsp:match.entrance")

@NotSso

public Object entrance() {

String codeUrl = WeiXinApiUrlUtil.getMatchUrl();

return codeUrl;

}

public static String getMatchUrl() {

String url = WeixinMatchPayConfigure.GET_CODE_URL;

url = url.replace("APPID", urlEnodeUTF8(WeixinMatchPayConfigure.APPID));

url = url.replace("REDIRECT_URI", WeixinMatchPayConfigure.REDIRECT_URI);

url = url.replace("SCOPE", WeixinMatchPayConfigure.USERINFOSCOPE);

return url;

}返回前端的是个Url路径,同个这个路径来获取微信用户授权,然后跳转我们自己的首页(REDIRECT_URI)

前端页面

$(document).ready(function(){

//其实这个时候跳转的URL它会跟着一连串属性,入下图 ↓

window.location.href='${obj}';

});

24bd6dd6084622730c7497f19b592130.png

3、微信用户openId是什么?

在微信用户关注公众号时,会对应的产生一个openId(openId:加密后的用户微信号),一个微信用户对应一个公众号产生的openId是唯一,当然,如果该微信号去关注另一个公众号所产生openID肯定和当前这个不一样的,OpenID 是最对『微信应用』的用户唯一值,同一个『微信开发者账号』下的不同应用中,使用同一个『微信用户』登录,此值会不一样,

废话不说,上代码↓

①、上文表明此接口请求带有参数code,那么需在这里接受code参数@At

@Ok("jsp:match.apply")

@NotSso

public Object apply(@Param("code") String code) {

return matchPayViewService.apply(code);

}

②、获取openID放到session中去public Object apply(String code) {

//创建空map

Map map = MapUtil.map();

//获取session

HttpSession session = Mvcs.getReq().getSession();

//获取session中oppenID

String oppendId = ConvertUtil.obj2str(session.getAttribute("oppendId"));

//非空校验oppenID

if (!Util.isEmpty(oppendId)) {

map.put("isWeChat", "yes");

return map;

}

//校验code是否为空,为空说明不是微信公众号支付

if (Util.isEmpty(code)) {

map.put("isWeChat", "no");

return map;

}

//获取访问用户的token,(工具类1)

UserInfoAccessTokenDt accessTokenDt = userInfoAccessTokenDtBaseService.getUserAccessToken(code);

//获取微信用户信息,(工具类2)

UserInfoDt userInfo = userInfoDtBaseService.getUserInfo(accessTokenDt);

ExceptionUtil.checkEmpty(userInfo, "获取微信用户信息失败");

map.put("userInfo", userInfo);

int sessionInactive = 30 * 60;

//把相关数据放到session中去

session.setMaxInactiveInterval(sessionInactive);

session.setAttribute("oppendId", userInfo.getOpenid());

session.setAttribute("userInfo", userInfo);

map.put("isWeChat", "yes");

//获取当前用户

map.put("userId", fetchUser.getCurrentUserId());

//-----------------------------------------------------权限校验,可根据自己的项目进行业务操作

map.put("isgxltUser", gXUnicomBusinessService.isPermission(fetchUser.getCurrentUserId()));

return map;

}

注:上面这些是获取微信用户授权、获取用户的基本信息,公众号支付需要用户的openID,所以。。。。。。

创建预支付Url

1、创建订单

①、此处不解释@At

@Ok("jsp:match.createorder")

@NotSso

public Object createOrder(@Param("..") final OrderAddForm orderAddForm, final String gradeName) {

return matchPayViewService.createOrder(orderAddForm, gradeName);

}

②、创建订单public Map createOrder(OrderAddForm orderAddForm, String gradeName) {

ExceptionUtil.checkId(orderAddForm.getMatchId(), "赛事ID不能为空");

Map map = MapUtil.map();

orderAddForm.setCustomPrice(matchBaseService.getPrice(orderAddForm.getMatchId()).getPrice());

OrderMatchEntity order = dbDao.insert(orderAddForm.toEntity());

map.put("order", order);

map.put("gradeName", gradeName);

//拼接预支付订单

String result = sendReqGetPreOrder(order);

//转化为JsApiParam对象,(工具类3)

JsApiParam jap = dowithWxReturn(result);

map.put("jap", jap);

return map;

}

③、拼接预支付订单参数private String sendReqGetPreOrder(OrderMatchEntity order) {

ExceptionUtil.checkEmpty(order.getId(), "订单id不能为空");

Map params = MapUtil.map();

params.put("appid", WeixinMatchPayConfigure.APPID);

params.put("mch_id", WeixinMatchPayConfigure.MCH_ID);

params.put("notify_url", WeixinMatchPayConfigure.NOTIFY_ACTIVITY_URL);

params.put("trade_type", WeixinMatchPayConfigure.TRADE_TYPE);//单次订单为jsapi方式

int randomNumLength = 32;

params.put("nonce_str", RandomUtil.randomString(randomNumLength));

//获取openID

params.put("openid", getWxUserInfoWithEx().getOpenid());

String body = "赛事报名";

params.put("body", body);

params.put("out_trade_no", order.getId());

long total_fee = AmountUtils.changeY2F(order.getCustomPrice());

params.put("total_fee", total_fee);

params.put("device_info", "WEB");

//加密签名,工具类(微信支付PC端文档中有)

String sign = Signature.getSign(params, WeixinMatchPayConfigure.API_KEY);

params.put("sign", sign);

//HttpRequest 工具类(微信支付PC端文档中有)

return HttpRequest.sendPost(WeixinMatchPayConfigure.PAY_UNIFIED_ORDER_API, params);

}

//获取微信标识

private UserInfoDt getWxUserInfoWithEx() {

Object userInfo = Mvcs.getReq().getSession().getAttribute("userInfo");

if (Util.isEmpty(userInfo)) {

throw ExceptionUtil.bEx("获取你的微信身份的标识失败请重新退出再次进入");

}

//类型转换

return ConvertUtil.cast(userInfo, UserInfoDt.class);

}

④、处理调用微信预支付订单的返回值private JsApiParam dowithWxReturn(String result) {

//该类见(工具类3)

JsApiParam jsApiParam = new JsApiParam();

Map weixinPrepayInfo = MapUtil.map();

try {

//------------------解析XML(工具类:微信支付PC端文档中有)

weixinPrepayInfo = XMLParser.getMapFromXML(result);

String return_code = (String) weixinPrepayInfo.get("return_code");

if ("SUCCESS".equals(return_code)) {

String prepay_id = (String) weixinPrepayInfo.get("prepay_id");

//给jsApiParam对象赋值

jsApiParam.setPrepay_id(prepay_id);

jsApiParam.setPackageInfo("prepay_id=" + prepay_id);

jsApiParam.setPaySign(getJsApiPaySign(jsApiParam));

return jsApiParam;

} else {

throw ExceptionUtil.bEx("预支付失败");

}

} catch (Exception e) {

ExceptionUtil.bEx("调用微信预支付接口出错");

}

return jsApiParam;

}

⑤、调用jsApiParam对象打回前台后,则需要调用微信内部的提供的js方法getBrandWCPayRequestfunction onBridgeReady(){

//微信内部提供的js方法

WeixinJSBridge.invoke(

'getBrandWCPayRequest', {

"appId":"${obj.jap.appId}", //公众号名称,由商户传入

"timeStamp":"${obj.jap.timeStamp}", //时间戳,自1970年以来的秒数

"nonceStr":"${obj.jap.nonceStr}", //随机串

"package":"${obj.jap.packageInfo}",

"signType":"MD5", //微信签名方式:

"paySign":"${obj.jap.paySign}" //微信签名

},

function(res){

// 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回 ok,但并不保证它绝对可靠。

if (res.err_msg == "get_brand_wcpay_request:ok") {

//跳转到成功页面

window.location.href = "http://lannong.uxuexi.com/register/success.html";

} else if (res.err_msg == "get_brand_wcpay_request:cancel") {

WeixinJSBridge.call('closeWindow');

} else {

}

}

);

}

⑥、调用⑤后页面则会弹出微信支付框,如图↓

716653a19778f0571105a174c118f5be.png

支付成功

1、支付成功后则调用成功后的回调函数@At

@Filters

public void wxnotify() throws Exception {

matchPayViewService.weChatPayViewService();

}

2、签名校验public void weChatPayViewService() throws Exception {

HttpServletRequest request = Mvcs.getReq();

HttpServletResponse response = Mvcs.getResp();

//获取微信响应的内容

String responseString = getWeiXinResponseContent(request);

PrintWriter out = response.getWriter();

String resp = "";

String signKey = WeixinPayConfigure.API_KEY;

//------------------------------签名校验↓,(工具类:微信支付PC端文档中有)

boolean verify = Signature.checkIsSignValidFromResponseString(responseString, signKey);

if (!verify) {

logger.error("签名验证失败");

resp = "签名验证失败";

out.write(resp);

out.close();

//签名失败直接返回

return;

}

//解析xml

Map map = XMLParser.getMapFromXML(responseString);

String result_code = ConvertUtil.obj2str(map.get("result_code"));

if (!"SUCCESS".equalsIgnoreCase(result_code)) {

resp = PayCommonUtil.getResponseXML("ERROR", "ERROR");

out.write(resp);

out.close();

//支付失败直接返回

return;

}

//处理订单

resp = handleOrder(map);

out.write(resp);

out.close();

}

//获取微信响应内容

private String getWeiXinResponseContent(HttpServletRequest request) throws IOException,

UnsupportedEncodingException {

InputStream inStream = request.getInputStream();

ByteArrayOutputStream outStream = new ByteArrayOutputStream();

byte[] buffer = new byte[1024];

int len = 0;

while ((len = inStream.read(buffer)) != -1) {

outStream.write(buffer, 0, len);

}

//获取微信调用我们notify_url的返回信息

String responseString = new String(outStream.toByteArray(), "utf-8");

outStream.close();

inStream.close();

return responseString;

}

3、处理订单,此处业务不再讲解,不懂得可以去看PC端微信支付文档@Aop("txDb")

private String handleOrder(Map map) throws Exception {

String resp = PayCommonUtil.getResponseXML("SUCCESS", "OK");

String transaction_id = ConvertUtil.obj2str(map.get("transaction_id"));

String time_end = ConvertUtil.obj2str(map.get("time_end"));

String out_trade_no = (String) map.get("out_trade_no");

if (Util.isEmpty(transaction_id) || Util.isEmpty(time_end) || Util.isEmpty(out_trade_no)) {

resp = PayCommonUtil.getResponseXML("ERROR", "参数错误,微信支付订单号、支付完成时间、订单号均不能为空");

return resp;

}

OrderMatchEntity order = dbDao.fetch(OrderMatchEntity.class, ConvertUtil.obj2long(out_trade_no));

if (Util.isEmpty(order)) {

resp = PayCommonUtil.getResponseXML("ERROR", "订单不存在");

return resp;

}

int orderStatus = order.getStatus();

if (OrderStatusEnum.FINISHED.intKey() == orderStatus) {

return resp;

}

if (OrderStatusEnum.WAITING_PAY.intKey() == orderStatus) {

//此处写你所需的业务即可

//更新订单为完成状态

//实际支付金额(分)

//添加支付记录

}

return resp;

}

4、公众号微信支付,支付后微信会返回三种状态,如图↓

c1c723efd6ed47a444b1f39b4d45d5ed.png

那么,关于三种状态我们所需跳转的页面则可以在前台用js来实现function(res){

// 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回 ok,但并不保证它绝对可靠。

if (res.err_msg == "get_brand_wcpay_request:ok") {

//跳转到成功页面

window.location.href = "http://lannong.uxuexi.com/register/success.html";

} else if (res.err_msg == "get_brand_wcpay_request:cancel") {

WeixinJSBridge.call('closeWindow');

} else {

}

}

工具类

1、获取访问用户的token@IocBean

public class UserInfoAccessTokenDtBaseService {

/**

* 通过code获取用户的openId

*

* @param code 编号

*

* @return 用户的openId

*/

public UserInfoAccessTokenDt getUserAccessToken(String code) {

ExceptionUtil.checkEmpty(code, "用户同意授权,获取的code不能为空");

//获取用户openid的连接

String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + WeixinH5PayConfigure.APPID

+ "&secret=" + WeixinH5PayConfigure.APP_SECRET + "&code=" + code + "&grant_type=authorization_code";

Response res = Http.get(url);

String content = res.getContent();

//----------------------------------从 JSON 字符串中,根据获取某种指定类型的 JSON 对象

UserInfoAccessTokenDt accessTokenDt = JsonUtil.fromJson(content, UserInfoAccessTokenDt.class);

return accessTokenDt;

}

}

2、获取微信用户基本信息@IocBean

public class UserInfoDtBaseService {

/**

* 获取用户信息

*

* @param accessTokenDt 获取用户信息的token

*

* @return 用户信息对象

*/

public UserInfoDt getUserInfo(UserInfoAccessTokenDt accessTokenDt) {

ExceptionUtil.checkEmpty(accessTokenDt, "访问用户的accessToken不能为空");

String url = "https://api.weixin.qq.com/sns/userinfo?access_token=" + accessTokenDt.getAccess_token()

+ "&openid=" + accessTokenDt.getOpenid() + "&lang=zh_CN";

Response res = Http.get(url);

String content = res.getContent();

//------------------------------------- 从 JSON 字符串中,根据获取某种指定类型的 JSON 对象。↓

UserInfoDt UserInfo = JsonUtil.fromJson(content, UserInfoDt.class);

return UserInfo;

}

}

@Data

public class UserInfoAccessTokenDt {

//TODO(注释去这个链接下找:http://mp.weixin.qq.com/wiki/17/c0f37d5704f0b64713d5d2c37b468d75.html)

private String access_token;

private String expires_in;

private String refresh_token;

private String openid;

private String scope;

private String unionid;

}

3、微信公众号支付参数@Data

public class JsApiParam {

/**

* 公众号appid

*/

private String appId = WeixinH5PayConfigure.APPID;

/**

* 时间戳

*/

private String timeStamp = System.currentTimeMillis() + "";

/**

* 随机字符串

*/

private String nonceStr = RandomUtil.randIntString(32);

/**

* 签名方式

*/

private String signType = "MD5";

/**

* 预支付id

*/

private String packageInfo;

/**

* 支付签名

*/

private String paySign;

/**

* 订单号

*/

private String orderNo;

/**

* 微信预付单号

*/

private String prepay_id;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值