经过近一周的敲代码,终于把公众号支付和H5支付实现完成并测试通过,特此分享一些流程,一方面自己记录另一方面给新入门的一点思路

【本文介绍普通商户的公众号支付】

一、基本信息和配置

公众号支付的前提是要有一个拥有支付功能的公众号和一个已经通过ICP备案的域名,这里不再赘述,如果你申请支付成功,将会收到以下样子的邮件:

接下来,你就可以通过登陆账号和密码进入到微信支付商户平台,配置基本信息。

1.设置API密钥。
初次登陆没有密钥的需要设置一个具体在 账户中心>API安全,下图示:

先装个证书然后可以设置密钥(生成一个32位的UUID设为密钥即可,注意:密钥不能查看只能设置),以后在接口签名时需要这个东西,设置完成如下图示:

2.设置开发配置。
在产品中心>开发配置中添加公众号支付授权目录,注意是“目录”,并非域名,
你需要将项目发起支付的接口最后一个斜杠之前的地址配置进去,
如:http://www.bbcd.com/projectName/phone/gotoPayJS.do ,
你就需要添加http://www.bbcd.com/projectName/phone/这个地址

3.登陆微信公众平台,在 公众号设置>功能设置下配置网页授权域名,注意这里是域名。

通过以上三步配置就结束了,接下来就是敲代码了。

二、开发支付

强烈建议参考微信公众号开发文档
(https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_1)
总结下来微信公众号支付开发主要两步骤:
1. 通过统一下单接口发起请求,并获得prepay_id(预支付交易会话标识),这个标示是你接下来想微信提交支付的关键数据;
2. 在微信浏览器内H5调起支付。
是不是感觉很简单呀,接下来两步骤解说。

一)发起统一下单接口

接口地址:https://api.mch.weixin.qq.com/pay/unifiedorder
需求参数:

字段名 变量名 类型 示例值 描述
公众账号ID appid String(32) wxd678efh567hg6787 微信支付分配的公众账号ID(企业号corpid即为此appId)
商户号 mch_id String(32) 1230000109 微信支付分配的商户号
随机字符串 nonce_str String(32) 5K8264ILTKCH16CQ2502SI8ZNMTM67VS 随机字符串,长度要求在32位以内。推荐随机数生成算法
签名 sign String(32) C380BEC2BFD727A4B6845133519F3AD6 通过签名算法计算得出的签名值,详见签名生成算法
商品描述 body String(128) 腾讯充值中心-QQ会员充值 商品简单描述,该字段请按照规范传递,具体请见参数规定
商户订单号 out_trade_no String(32) 20150806125346 商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-
标价金额 total_fee Int 88 订单总金额,单位为分,详见支付金额
终端IP spbill_create_ip String(16) 123.12.12.123 APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP。
通知地址 notify_url String(256) http://www.weixin.qq.com/wxpay/pay.php 异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。
交易类型 trade_type String(16) JSAPI 取值如下:JSAPI,NATIVE,APP等,说明详见参数规定
用户标识 openid String(128) oUpF8uMuAJO_M2pxb1Q9zNjWeS6o trade_type=JSAPI时(即公众号支付),此参数必传,此参数为微信用户在商户对应appid下的唯一标识。openid如何获取,可参考【获取openid】。企业号请使用【企业号OAuth2.0接口】获取企业号内成员userid,再调用【企业号userid转openid接口】进行转换
设备号 device_info String(32) 013467007045764 自定义参数,可以为终端设备号(门店号或收银设备ID),PC网页或公众号内支付可以传”WEB”

我定义了一个bean存储,API里有各种字段,在这里我们只保留自己需要的,主要构造方法里我加上了几个固定字段的初始化,公众号,商户号,随机数,设备类型(”WEB”),通知地址。
这里我着重说下通知地址这个字段,它是公众号支付完成后微信端向我们发起回调的地址,告诉我们交易的结果,这个地址不能加参数,注意此回调必须向微信端做出回应,否则微信会认为我们没有正确接收结果,会持续回调多次(下文我会细说回应)

package com.wtp.wechat.bean;import com.wtp.wechat.util.CommonUtil;
import com.wtp.wechat.util.WechatPayUtil;
import com.wtp.wechat.util.WechatUtil;/*** * @ClassName: UnifiedOrder * @Description: 统一下单* @author tianpengw * @date 2017年10月12日 上午9:57:39 **/
public class UnifiedOrder {/*** 公众账号ID*/private String appid;/*** 商户号*/private String mch_id;/*** 随机串*/private String nonce_str;/*** 签名*/private String sign;/*** 商品描述*/private String body;/*** 商户订单号*/private String out_trade_no;/*** 总金额*/private Integer total_fee;/*** 终端IP(用户)*/private String spbill_create_ip;/*** 通知地址*/private String notify_url;/*** 交易类型*/private String trade_type;/*** 用户标识*/private String openid;/*** WEB*/private String device_info;public UnifiedOrder(){this.appid = WechatUtil.appid;this.mch_id = WechatPayUtil.mchId;this.nonce_str = CommonUtil.getUUID();this.device_info = WechatPayUtil.deviceInfo;this.notify_url = WechatPayUtil.notifyUrl;}public String getAppid() {return appid;}public void setAppid(String appid) {this.appid = appid;}public String getBody() {return body;}public void setBody(String body) {this.body = body;}public String getMch_id() {return mch_id;}public void setMch_id(String mch_id) {this.mch_id = mch_id;}public String getNonce_str() {return nonce_str;}public void setNonce_str(String nonce_str) {this.nonce_str = nonce_str;}public String getNotify_url() {return notify_url;}public void setNotify_url(String notify_url) {this.notify_url = notify_url;}public String getOpenid() {return openid;}public void setOpenid(String openid) {this.openid = openid;}public String getOut_trade_no() {return out_trade_no;}public void setOut_trade_no(String out_trade_no) {this.out_trade_no = out_trade_no;}public String getSpbill_create_ip() {return spbill_create_ip;}public void setSpbill_create_ip(String spbill_create_ip) {this.spbill_create_ip = spbill_create_ip;}public Integer getTotal_fee() {return total_fee;}public void setTotal_fee(Integer total_fee) {this.total_fee = total_fee;}public String getTrade_type() {return trade_type;}public void setTrade_type(String trade_type) {this.trade_type = trade_type;}public String getSign() {return sign;}public void setSign(String sign) {this.sign = sign;}public String getDevice_info() {return device_info;}public void setDevice_info(String device_info) {this.device_info = device_info;}
}

下面是微信支付相关的几个方法

package com.wtp.wechat.util;import java.util.Map;import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;import com.wtp.wechat.bean.UnifiedOrder;
/*** * @ClassName: WechatPayUtil * @Description: 微信支付工具类* @author tianpengw * @date 2017年10月12日 下午3:38:25 **/
public class WechatPayUtil {private static Logger log = LogManager.getLogger(WechatPayUtil.class);/*** 微信支付分配的商户号*/public static final String mchId = PropertiesUtil.getProperties("pay.mchid");/*** 商户平台密钥*/public static final String apiKey = PropertiesUtil.getProperties("pay.apikey");/*** 微信支付分配的商户号*/public static final String notifyUrl = PropertiesUtil.getProperties("pay.notifyUrl");/*** 终端设备号(门店号或收银设备ID),注意:PC网页或公众号内支付请传"WEB"*/public static String deviceInfo = "WEB";/*** JSAPI -- 公众号支付*/public static String tradeTypeJs = "JSAPI";/*** MWEB -- H5支付*/public static String tradeTypeH5 = "MWEB";/*** 统一订单地址*/private static String unifiedUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder";/*** * @Description: 公众号支付-统一订单类型获得prepay_id* @author tianpengw * @param uo* @return*/public static String getUnifiedOrderPId(UnifiedOrder uo){String sign = createUnifiedOrderSign(uo);uo.setSign(sign);String xml = XMLBeanUtils.objectToXMLStr(uo);log.info("公账号支付统一订单请求参数:"+xml);String res = HttpHelper.httpsRequest(unifiedUrl,"POST",xml);log.info("公账号支付统一订单返回结果:"+res);Map<String, String> responseMap = XMLBeanUtils.readStringXmlOut(res);return responseMap.get("prepay_id");}/*** * @Description: 微信支付 -统一订单类型获得mweb_url* @author tianpengw * @param uo* @return*/public static String getUnifiedOrderMWebUrl(UnifiedOrder uo){String sign = createUnifiedOrderSign(uo);uo.setSign(sign);String xml = XMLBeanUtils.objectToXMLStr(uo);log.info("H5支付统一订单请求参数:"+xml);String res = HttpHelper.httpsRequest(unifiedUrl,"POST",xml);log.info("H5支付统一订单返回结果:"+res);Map<String, String> responseMap = XMLBeanUtils.readStringXmlOut(res);return responseMap.get("mweb_url");}/*** 获取统一下单签名* @param unifiedOrder* @return*/private static String createUnifiedOrderSign(UnifiedOrder unifiedOrder){StringBuffer sign = new StringBuffer();sign.append("appid=").append(unifiedOrder.getAppid());sign.append("&body=").append(unifiedOrder.getBody());sign.append("&device_info=").append(unifiedOrder.getDevice_info());sign.append("&mch_id=").append(unifiedOrder.getMch_id());sign.append("&nonce_str=").append(unifiedOrder.getNonce_str());sign.append("&notify_url=").append(unifiedOrder.getNotify_url());/*** H5支付签名时没有用户的openId*/if(!CommonUtil.isEmpty(unifiedOrder.getOpenid())){sign.append("&openid=").append(unifiedOrder.getOpenid());}sign.append("&out_trade_no=").append(unifiedOrder.getOut_trade_no());sign.append("&spbill_create_ip=").append(unifiedOrder.getSpbill_create_ip());sign.append("&total_fee=").append(unifiedOrder.getTotal_fee());sign.append("&trade_type=").append(unifiedOrder.getTrade_type());sign.append("&key=").append(apiKey);return SignatureUtil.MD5(sign.toString()).toUpperCase();}
}

getUnifiedOrderPId是获得prepareId的方法
getUnifiedOrderMWebUrl是获得微信H5支付的地址,这里先不用管,下章再说
createUnifiedOrderSign这个方法是获得sign签名的方法,apiKey就是上面商户平台设置的API密钥。

用到几个工具类的方法我也列举出来:
1)xstream 生成xml格式的字符串和解析字符串类xml为map的方法

package com.wtp.wechat.util;import java.io.InputStream;import java.io.Writer;
import java.util.HashMap;
import java.util.List;
import java.util.Map;import javax.servlet.http.HttpServletRequest;import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.naming.NoNameCoder;
import com.thoughtworks.xstream.io.xml.XppDriver;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import com.thoughtworks.xstream.core.util.QuickWriter;public class XMLBeanUtils {private static XStream xStream = new XStream(new XppDriver(new NoNameCoder()){@Overridepublic HierarchicalStreamWriter createWriter(Writer out) {return new PrettyPrintWriter(out) {// 对所有xml节点的转换都增加CDATA标记boolean cdata = true;@Override@SuppressWarnings("rawtypes")public void startNode(String name, Class clazz) {super.startNode(name, clazz);}@Overridepublic String encodeNode(String name) {return name;}@Overrideprotected void writeText(QuickWriter writer, String text) {if (cdata) {writer.write("<![CDATA[");writer.write(text);writer.write("]]>");} else {writer.write(text);}}};}});/*** * @Description: bean转xml字符串* @author tianpengw * @param obj* @return*/public static String objectToXMLStr(Object obj){xStream.alias("xml", obj.getClass());return xStream.toXML(obj);}/** * 解析微信发来的请求(XML) *  * @param request * @return * @throws Exception */  @SuppressWarnings("unchecked")  public static Map<String, String> parseXml(HttpServletRequest request) throws Exception {  // 将解析结果存储在HashMap中  Map<String, String> map = new HashMap<String, String>();  // 从request中取得输入流  InputStream inputStream = request.getInputStream();  // 读取输入流  SAXReader reader = new SAXReader();  Document document = reader.read(inputStream);  // 得到xml根元素  Element root = document.getRootElement();  // 得到根元素的所有子节点  List<Element> elementList = root.elements();  // 遍历所有子节点  for (Element e : elementList){map.put(e.getName(), e.getText());  }// 释放资源  inputStream.close();  inputStream = null;  return map;  }/*** @description 将xml字符串转换成map* @param xml* @return Map*/public static Map<String, String> readStringXmlOut(String xml) {Map<String, String> map = new HashMap<String, String>();Document doc = null;try {doc = DocumentHelper.parseText(xml); // 将字符串转为XMLElement rootElt = doc.getRootElement(); // 获取根节点@SuppressWarnings("unchecked")List<Element> list = rootElt.elements();// 获取根节点下所有节点for (Element element : list) { // 遍历节点map.put(element.getName(), element.getText()); // 节点的name为map的key,text为map的value}} catch (DocumentException e) {e.printStackTrace();} catch (Exception e) {e.printStackTrace();}return map;}}

2)发起Http请求方法

    /*** * @Description: 发起Http请求* @author tianpengw * @param requestUrl 请求地址* @param requestMethod 请求方式 GET/POST* @param outputStr 请求参数,如果没有置 null* @return*/public static String httpsRequest(String requestUrl, String requestMethod, String outputStr){    try {    URL url = new URL(requestUrl);    HttpURLConnection conn = (HttpURLConnection) url.openConnection();    conn.setDoOutput(true);    conn.setDoInput(true);    conn.setUseCaches(false);    // 设置请求方式(GET/POST)    conn.setRequestMethod(requestMethod);    conn.setRequestProperty("content-type", "application/x-www-form-urlencoded");    // 当outputStr不为null时向输出流写数据    if (null != outputStr) {    OutputStream outputStream = conn.getOutputStream();    // 注意编码格式    outputStream.write(outputStr.getBytes("UTF-8"));    outputStream.close();    }    // 从输入流读取返回内容    InputStream inputStream = conn.getInputStream();    InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");    BufferedReader bufferedReader = new BufferedReader(inputStreamReader);    String str = null;  StringBuffer buffer = new StringBuffer();    while ((str = bufferedReader.readLine()) != null) {    buffer.append(str);    }    // 释放资源    bufferedReader.close();    inputStreamReader.close();    inputStream.close();    inputStream = null;    conn.disconnect();    return buffer.toString();    } catch (Exception e) {e.printStackTrace();}    return null;    } 

3)MD5加密方法

   /*** * @Description: 生成 MD5* @author tianpengw * @param data* @return*/public static String MD5(String data){StringBuilder sb = new StringBuilder();try {java.security.MessageDigest md = MessageDigest.getInstance("MD5");byte[] array = md.digest(data.getBytes("UTF-8"));sb = new StringBuilder();for (byte item : array) {sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));}} catch (Exception e) {e.printStackTrace();}return sb.toString();}

4)获得客户端IP方法

   /*** * @Description: 获取客户端地址* @author tianpengw * @param request* @return*/public static String getClientIP(HttpServletRequest request) {String ipAddress = request.getHeader("x-forwarded-for");  if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {  ipAddress = request.getHeader("Proxy-Client-IP");  }  if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {  ipAddress = request.getHeader("WL-Proxy-Client-IP");  }  if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {  ipAddress = request.getRemoteAddr();  if(ipAddress.equals("127.0.0.1") || ipAddress.equals("0:0:0:0:0:0:0:1")){  //根据网卡取本机配置的IP  InetAddress inet=null;  try {  inet = InetAddress.getLocalHost();  } catch (UnknownHostException e) {  e.printStackTrace();  }  ipAddress= inet.getHostAddress();  }  }  //对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割  if(ipAddress!=null && ipAddress.length()>15){ //"***.***.***.***".length() = 15  if(ipAddress.indexOf(",")>0){  ipAddress = ipAddress.substring(0,ipAddress.indexOf(","));  }  }  return ipAddress;}

5)获得UUID

   /***   * @Description: 获得一个UUID* @author tianpengw * @return String*/public static String getUUID(){  return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);  }

6)timstamp生成

String timestamp = Long.toString(System.currentTimeMillis() / 1000);

7)是否是微信浏览器判断

   /*** * @Description: 判断是否是微信浏览器发起的请求,如果是返回true,反正返回false* @author tianpengw * @param req* @return*/public static boolean isWechatBrowser(HttpServletRequest req){String ua = req.getHeader("user-agent").toLowerCase();  if (ua.indexOf("micromessenger") >= 0) {// 是微信浏览器  return true;  } return false;}

方法我都列出来,至于业务相关的订单id,商品body(注意内容不要太长限制为128,本人在这里吃过亏)等字段都需要自己加入自此获得了宝贵的prepared_id。

二)微信浏览器内调起支付

带着上文的prepared_id我们开始向页面出发,再出发之前要把通关文件都一一准备好,让我们看下一个通关类:

package com.wtp.wechat.bean;
/*** * @ClassName: JSAPIConfig * @Description: JSAPI使用的配置信息* @author tianpengw * @date 2017年10月12日 下午3:44:48 **/
public class JSAPIConfig {/*** 公众号id:商户注册具有支付权限的公众号成功后即可获得*/private String appId;/*** 时间戳*/private String timeStamp;/*** 随机字符串,不长于32位*/private String nonceStr;/*** 签名*/private String paySign;/*** 签名算法,暂支持MD5*/private String signType;/*** 订单详情扩展字符串-package* 统一下单接口返回的prepay_id参数值,提交格式如:prepay_id=****/private String packageName;public JSAPIConfig(){this.signType = "MD5";}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 getPaySign() {return paySign;}public void setPaySign(String paySign) {this.paySign = paySign;}public String getSignType() {return signType;}public void setSignType(String signType) {this.signType = signType;}public String getPackageName() {return packageName;}public void setPackageName(String packageName) {this.packageName = packageName;}
}

此bean因微信的内置对象WeixinJSBridge而出现,接口示例如下:

在微信浏览器里面打开H5网页中执行JS调起支付。接口输入输出数据格式为JSON。
注意:WeixinJSBridge内置对象在其他浏览器中无效。
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,但并不保证它绝对可靠。 });

这几个字段里唯一注意的是paySign签名,我为了省事,通过拼字符串来进行MD5加密(其他用到的方法我上面列举的都有):

StringBuffer sign = new StringBuffer();
sign.append("appId=").append(WechatUtil.appid);
sign.append("&nonceStr=").append(nonce);
sign.append("&package=").append(packageName);
sign.append("&signType=").append(config.getSignType());
sign.append("&timeStamp=").append(timestamp);
sign.append("&key=").append(WechatPayUtil.apiKey);String signature = SignatureUtil.MD5(sign.toString()).toUpperCase();

我部分代码如下:

后台部分代码:

前台部分代码:

//对浏览器的UserAgent进行正则匹配,不含有微信独有标识的则为其他浏览器
var useragent = navigator.userAgent;
if (useragent.match(/MicroMessenger/i) == 'MicroMessenger') {$.ajax({type : "POST",url : "gotoJSPayJS.json",dataType : "json",contentType : 'application/json;charset=UTF-8',data : JSON.stringify({orderId:orderId}),success : function(res){if("success" == res.errCode){var obj = res.data;if (typeof WeixinJSBridge == "undefined"){jQAlert("请在微信浏览器发起支付!");}else{WeixinJSBridge.invoke('getBrandWCPayRequest',{  "appId" : obj.appId, //公众号名称,由商户传入  "timeStamp":obj.timeStamp,//时间戳,自1970年以来的秒数  "nonceStr" : obj.nonceStr,//随机串  "package" : obj.packageName,//商品包信息  "signType" : obj.signType,//微信签名方式"paySign" : obj.paySign//微信签名  },function(res){      if(res.err_msg == "get_brand_wcpay_request:ok"){window.location.href="payComplete.do?orderId="+orderId;}else if(res.err_msg == "get_brand_wcpay_request:cancel"){jQAlert("支付取消!");}else if(res.err_msg == "get_brand_wcpay_request:fail"){jQAlert("支付失败:"+res.err_desc);}else{jQAlert("支付异常");}}); }}else{jQAlert("支付操作异常,请稍后再试!");}},error : function(XMLHttpRequest, textStatus,errorThrown) {jQAlert('支付异常,请联系客服!');},complete : function(XMLHttpRequest, textStatus) {}});

支付返回成功后,几乎可以保证微信会立刻调用你前面设置的notifyUrl配置的回调地址,接口里你就需要实现业务逻辑的方法比如更新订单状态,修改支付记录的状态,变更商品的数量等等业务逻辑,为了细说这个PayCallback我也列出我的部分代码:

   /*** 支付完成回调函数* @param request* @return*/@RequestMapping(value="payNotify.do")public void wechatPayNotify(HttpServletRequest request,HttpServletResponse resp){log.info("++++++进入支付回调界面+++++");try {log.info("++++++订单:" + orderId + ",完成回调 :" + result +(("SUCCESS").equals(result)?"":",结果描述:"+resultDes));} catch (Exception e) {e.printStackTrace();}//处理完成需要返回微信已经支付完成回复try {resp.getWriter().write(XMLBeanUtils.objectToXMLStr(new PayCallback()));} catch (IOException e) {e.printStackTrace();}}

再看下PayCallback对象:

package com.wtp.wechat.bean;
/*** * @ClassName: PayCallback * @Description: 回调结束返回微信内容* @author tianpengw * @date 2017年10月12日 下午4:06:27 **/
public class PayCallback {private String return_code;private String return_msg;public PayCallback() {this.return_code = "SUCCESS";this.return_msg = "OK";}public String getReturn_code() {return return_code;}public void setReturn_code(String return_code) {this.return_code = return_code;}public String getReturn_msg() {return return_msg;}public void setReturn_msg(String return_msg) {this.return_msg = return_msg;}
}

三、特殊情况-微信浏览器访问网页之公众号支付(补充)

有这样一种场景,某个商品在朋友圈或通过其他朋友分享,用户在进入商品详情里购买时将使用到的支付方式就是公众号支付(从API里也能体现出这里不支持H5支付),此时作为服务端的我们,需要的是获得用户的openid,由于我们并不能确定该用户是否是关注用户,所以这里我们需要通过网页授权获得当前用户的微信信息(至少需得到openid)

本人采用的是静默授权的方式
部分代码片段:

if(HttpHelper.isWechatBrowser(req)){/** 静默授权获得openid,这里不查询的原因是防止已经绑定过公众号的手机号在其他手机号的微信上进行支付操作,这里只完成支付操作不进行信息的变更*/String code = req.getParameter("code");if(!MyStringUtils.isEmpty(code)){//认证后回调Oauth2AccessToken oat = WechatUtil.getUserOpenId(code);if(null != oat && !MyStringUtils.isEmpty(oat.getOpenid())){String openId = (String) HttpUtils.getObjectFromSession(req, HttpUtils.openid_name);if(!oat.getOpenid().equals(openId)){mv.addObject("openId", oat.getOpenid());}}}else{//此处开始进行微信认证String url = WechatUtil.getOauth2Url(WechatUtil.scope_base, URLEncoder.encode(MyConstants.product_address + "/phone/checkStand.do?orderId="+orderId,"utf-8"));mv.setViewName("redirect:"+url);}}

这样通过静默授权获得当前微信在服务号里的唯一的openId,然后完成此后的支付操作,

注:可能网友会想这种情况下手机号虽已经绑定了自己的微信openId,但在其他微信里登陆自己的手机号也是能完成支付的。是的,这里我的控制逻辑如此,账号唯一绑定的是用户的手机号,微信主要作为辅助支付和自动登陆,当然你可以有自己的的业务逻辑控制,按照自己实际需求来实现。

至此整个普通商户的公众号支付已经写完。

如果感兴趣请关注另一个博客【微信支付之H5支付】,最后会把我总结出的微信相关的一些接口调用封装起来,做成jar包发出来,以后在微信大版本不变的情况下 只需引用jar里的方法即可,省去不少开发时间。

微信支付之公众号支付相关推荐

  1. php微信公众号支付实例教程,php微信支付之公众号支付功能

    这篇文章主要为大家详细介绍了php微信支付之公众号支付功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 网上的很多PHP微信扫码支付接入教程都颇为复杂,且需要配置和引入较多的文件,本人通过整理后 ...

  2. 微信支付、公众号支付、微信APP支付教程

    这两天有朋友问我微信支付的一些事情,我就抽了点时间整理了一下微信支付相关的一些东西,在这里分享给大家,希望能帮助大家少走弯路. 微信支付分为APP支付和公众号支付两大类,其中公众号支付又分为(公众号支 ...

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

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

  4. APP支付和公众号支付区别在哪?

    PP支付与公众号支付容易搞混淆,尤其对于一些半专业的外行人来说,傻傻分不清. 随着移动支付的盛行,或者说随着微信社交产品的全民普及,再加上消费者被一轮又一轮的支付大战烧脑,再不懂支付的也都了解那么丁点 ...

  5. 什么是APP支付和公众号支付

    APP支付与公众号支付容易搞混淆,尤其对于一些半专业的外行人来说,傻傻分不清. 随着移动支付的盛行,或者说随着微信社交产品的全民普及,再加上消费者被一轮又一轮的支付大战烧脑,再不懂支付的也都了解那么丁 ...

  6. 微信支付(公众号支付)微信公众平台开发教程(5)

    简介 Senparc.Weixin SDK 是由盛派网络(Senparc)团队自主研发的针对微信各模块的 开发套件(C#SDK), 已全面支持微信公众号.微信支付.企业号.开放平台.JSSDK.摇一摇 ...

  7. 【微信开发】---- 公众号支付

    公众号支付就是在微信里面的H5页面唤起微信支付,不用扫码即可付款的功能.做这个功能首先要明确的就是,只有和商户号mch_id匹配的appid才能成功支付.商户号在注册成功的时候就会将相关信息发送到邮箱 ...

  8. 微信h5支付和公众号支付、支付宝h5支付

    1.微信h5支付: 首先是支付参数的构建,这个需要通过后台进行组装,后台会跟微信进行数据的交互,而你所需要做的是拿到返回的数据,然后传递给微信的JSSDK,由微信的JSSDK进行后续操作,比如说先跳转 ...

  9. 微信H5支付、非微信H5支付、公众号支付、小程序支付

    文章目录 前言 一.微信H5支付和非微信H5支付 二.公众号支付 三.小程序支付 总结 前言 最近公司又要搞微信支付,大体上就是把app上VIP那一套内容但做成网页版,更方便用户去购买vip,老板就让 ...

最新文章

  1. 读书笔记(2) OpenLayers中的图层
  2. 职场小窍门:看穿同事性格的16个小动作
  3. shell中join链接多个域_shell 如何实现两个表的join操作
  4. Java线程(二):线程同步synchronized和volatile
  5. AttributeError系列之:AttributeError: module 'scipy.misc' has no attribute 'imread'报错问题
  6. php向下滑动,js如何判断鼠标滚轮是向下还是向上滚动
  7. ECC密钥结构和密码学基础
  8. fetch git pull 切换_git fetch git pull
  9. 动态规划 —— 背包问题 P01 —— 0-1背包
  10. Keras深度学习框架配置
  11. Excel数据分析实用小技巧【过坑】
  12. Ubuntu12.04下apache服务器的安装也配置
  13. xtile 下载_Stata: gen 命令中的 group() 函数的潜在风险
  14. 如何把小米手机上的便签导出为文本?
  15. 工商银行java script error windows7_Win8.1装工行网银提示"called runscript when not marked in progress"的解决方法...
  16. 供水供气管道泄漏监测系统原理
  17. 今日“春分”,我們來場春天的“飛花令”吧
  18. centos添加硬盘
  19. 一文帮你深度了解Axie Infinity-啊蟹(内含细致教程)
  20. system 权限读取注册表HKEY_CURRENT_USER

热门文章

  1. QuickTime Player解码器
  2. 程序员简历造假的后果!
  3. PAT甲级 1110 完全二叉树
  4. 高考水平科测试软件,免费 高中选课选科综合测评 - 在线工具网 - 工作生活好帮手...
  5. 一次未成功的渗透测试实战
  6. 荣光医院医道会比赛策略(续)
  7. 免费好用的天气服务 - Tuya(结尾附视频)
  8. Mac小技巧之信息和剪贴板不同步
  9. mandriva 2010 安装手记
  10. ROBOTSTUDIO中基础术语、在线功能