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版之完成消息接受与相应以及消息的处理
温馨提示: 这篇文章是依赖前几篇的文章的. 第一篇:微信公众平台开发实战之了解微信公众平台基础知识以及资料准备 第二篇 :微信公众平台开发实战之开启开发者模式,接入微信公众平台开发 第三篇 :微信公众 ...
随机推荐
- workday3~4
这两天算是把一个模块的功能做完了,功能是数据统计,即按输入的时间统计X小时各个服务器status的数量以及各个服务器的信息,学到了不少东西,为了避免忘记,先记录,有空再整理一下. 1.时间戳的问题 选 ...
- 7.modifier插件的自定义和使用
1.在plugins下面创建一个文件 modifier.changeDate.php 编写: <?php function smarty_modifier_changeDate($utime,$ ...
- meta小结
mate 标签定义及使用说明 元数据(Metadata)是数据的数据信息. 标签提供了 HTML 文档的元数据.元数据不会显示在客户端,当时会被浏览器解析. META元素通常用于指定网页的描述,关键词 ...
- Android远程桌面助手
很早之前,做过一个<WinCE远程桌面助手>,在没有屏幕或者在调试LCD驱动时,发挥了很大作用,平日开发也是必备.后来还被网友用于处理一些疑难问题,如无法输入开机密码时可通过该工具远程输入 ...
- js-ES6学习笔记-Set结构和Map结构
http://www.cnblogs.com/lonhon/ 1.ES6 提供了新的数据结构 Set.它类似于数组,但是成员的值都是唯一的,没有重复的值. Set 本身是一个构造函数,用来生成 Set ...
- 从栈不平衡问题 理解 calling convention
最近在开发的过程中遇到了几个很诡异的问题,造成了栈不平衡从而导致程序崩溃. 经过几经排查发现是和调用规约(calling convention)相关的问题,特此分享出来. 首先,讲一下什么是调用规约. ...
- ORM的概念, ORM到底是什么
一.ORM简介 对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术.简单的说,ORM是通过使 ...
- 00002、div的文字垂直居中与背景的渐变
1.div可以放多行的文字,当显示文字较少时,文字可垂直居中 text-align: center; display: table-cell; vertical-align: middle; text ...
- 怎么在linux ubuntu 上的nginx 绑定域名
前一篇介绍了,如果在ubuntu上运行nodejs,毕竟访问的时候都是用ip地址+端口号,但是上production 环境都需要域名的,IP地址当然不行 读过上一篇的朋友知道了,如果在upstart ...
- AugularJS从入门到实践(三)
前 言 前端 AngularJS是为了克服HTML在构建应用上的不足而设计的.(引用百度百科) AngularJS使用了不同的方法,它尝试去补足HTML本身在构建应用方面的缺陷.Angu ...