微信小程序授权登录、绑定手机号含接口和小程序

  • 1、小程序授权登录
    • 1.1、 微信公众平台小程序登录服务端api地址:
    • 1.2、 登录流程:
    • 1.3、 请求地址(GET 请求):
    • 1.4、 请求参数:
    • 1.5、 返回的 JSON 数据包
    • 1.6、 表结构
    • 1.7、 后台接口编写
    • 1.8、 小程序扫码
  • 2、 获取手机号
    • 2.1、 微信公众平台小程序获取手机号说明地址:
    • 2.2、 获取手机号
    • 2.3、 后台接口编写
    • 2.4、 小程序端
  • 3、 充值(小程序支付)
    • 3.1、微信支付官方地址:
    • 3.2、业务流程时序图
    • 3.3、 后台接口
      • 3.4、支付我采用sdk进行实现,地址:
      • 3.5、不用SDK,手动编写,需要自己对参数值进行XML转义,提供工具类如下:
      • 3.6、小程序端调用
  • 4、GitHub地址:

本篇博客主要为了记录小程序授权登录和绑定手机号功能的实现,包含小程序端和API(java语言开发)。

1、小程序授权登录

1.1、 微信公众平台小程序登录服务端api地址:

https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/login/auth.code2Session.html

1.2、 登录流程:

登录凭证校验。通过 wx.login 接口(小程序操作)获得临时登录凭证 code 后传到开发者服务器调用此接口完成登录流程。

1.3、 请求地址(GET 请求):

https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code

1.4、 请求参数:

属性 类型 默认值 必填 说明
appid string 小程序 appId
secret string 小程序 appSecret
js_code string 登录时获取的 code
grant_type string 授权类型,此处只需填写 authorization_code

1.5、 返回的 JSON 数据包

属性 类型 说明
openid string 用户唯一标识
session_key string 会话密钥
unionid string 用户在开放平台的唯一标识符,在满足 UnionID 下发条件的情况下会返回。
errcode number 错误码
errmsg string 错误信息

1.6、 表结构

1.7、 后台接口编写

@ApiOperation("微信授权登陆")
@RequestMapping(value = "/wxUserLogin", method = RequestMethod.POST)
@ResponseBody
@ApiImplicitParams({@ApiImplicitParam(name = "code", value = "CODE", required = true, dataType = "String", paramType = "query"),@ApiImplicitParam(name = "avatar", value = "avatarUrl", required = true, dataType = "String", paramType = "query"),@ApiImplicitParam(name="nickName",value="昵称",required = true,dataType = "String",paramType = "query")
})
public CommonResult wxUserLogin(String code, String avatar, String nickName) throws Exception {Map<String, Object> map = new HashMap<>();StringBuffer buffer= new StringBuffer("https://api.weixin.qq.com/sns/jscode2session?appid=").append(appId).append("&secret=").append(appIdSecret).append("&js_code=").append(code).append("&grant_type=authorization_code");String jsonString = HttpRequest.sendGet(buffer.toString());JSONObject jsStrs = JSONObject.parseObject(jsonString);String openId = jsStrs.getString("openid");String sessionKey = jsStrs.getString("session_key");map.put("openId", openId);map.put("sessionKey", sessionKey);map.put("mobileFlag",false);if (openId != null) {//查询用户信息User user =userService.getUserByOpenId(openId);if(user==null){user=new User();user.setNickName(nickName);user.setOpenId(openId);user.setHeadPortrait(avatar);userService.insertUser(user);}else{if(user.getMobile()!=null){map.put("mobileFlag",true);}}map.put("user",user);}return new CommonResult(200,"操作成功",map);
}
@EnableAutoConfiguration
public class HttpRequest {public static String sendGet(String url) throws Exception {String result = ""; BufferedReader in = null;try {URL realUrl = new URL(url);// 打开和URL之间的连接URLConnection connection = realUrl.openConnection();// 设置通用的请求属性connection.setRequestProperty("accept", "*/*");connection.setRequestProperty("Accept-Charset", "UTF-8");connection.setRequestProperty("contentType", "UTF-8");connection.setRequestProperty("connection", "Keep-Alive");connection.setRequestProperty("Content-type", "application/x-www-form-urlencoded;charset=UTF-8");connection.setRequestProperty("user-agent","Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");// 建立实际的连接connection.connect();// 获取所有响应头字段Map<String, List<String>> map = connection.getHeaderFields();// 遍历所有的响应头字段for (String key : map.keySet()) {System.out.println(key + "--->" + map.get(key));}// 定义 BufferedReader输入流来读取URL的响应in = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"));String line;while ((line = in.readLine()) != null) {result += line;}} catch (Exception e) {System.out.println("发送GET请求出现异常!" + e);e.printStackTrace();throw new Exception();}// 使用finally块来关闭输入流finally {try {if (in != null) {in.close();}} catch (Exception e2) {e2.printStackTrace();}}return result;
}
/*** 向指定 URL 发送POST方法的请求* * @param url*            发送请求的 URL* @param param*            请求参数,请求参数应该是 name1=value1&name2=value2 的形式。* @return 所代表远程资源的响应结果*/
public static String sendPost(String url, String param) {OutputStreamWriter out = null;BufferedReader in = null;String result = "";try {URL realUrl = new URL(url);// 打开和URL之间的连接URLConnection conn = realUrl.openConnection();// 设置通用的请求属性conn.setRequestProperty("accept", "*/*");conn.setRequestProperty("connection", "Keep-Alive");conn.setRequestProperty("user-agent","Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");//设置请求编码格式conn.setRequestProperty("Accept-Charset", "UTF-8");conn.setRequestProperty("contentType", "UTF-8");// 发送POST请求必须设置如下两行conn.setDoOutput(true);conn.setDoInput(true);// 获取URLConnection对象对应的输出流out = new OutputStreamWriter(conn.getOutputStream(), "UTF-8"); // 发送请求参数out.write(param);// flush输出流的缓冲out.flush();// 定义BufferedReader输入流来读取URL的响应in = new BufferedReader(new InputStreamReader(conn.getInputStream(),"UTF-8"));String line;while ((line = in.readLine()) != null) {result += line;}} catch (Exception e) {System.out.println("发送 POST 请求出现异常!"+e);e.printStackTrace();}//使用finally块来关闭输出流、输入流finally{try{if(out!=null){out.close();}if(in!=null){in.close();}}catch(IOException ex){ex.printStackTrace();}}return result;
}
public static String sendPost1(String url, String dataparam) {String result = "";BufferedReader in = null;try {/*"/GPServer/"+ method +*/String urlNameString = url;URL realUrl = new URL(urlNameString);URLConnection connection = realUrl.openConnection();//设置网络请求时间最多为5秒;connection.setConnectTimeout(5000);//读取网页请求结果时间为15秒connection.setReadTimeout(25000);//设置网络请求时间最多为5秒;// 设置通用的请求属性connection.setRequestProperty("accept", "*/*");connection.setRequestProperty("connection", "Keep-Alive");connection.setRequestProperty("user-agent","Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");connection.setDoOutput(true);  connection.setDoInput(true);  //设置请求编码格式connection.setRequestProperty("Accept-Charset", "UTF-8");connection.setRequestProperty("contentType", "UTF-8");// 获取URLConnection对象对应的输出流
//            String dataparam="f=pjson&name=张三";//文件流编码设置OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream(), "UTF-8"); out.write(dataparam);//错误方式,这种方式容易出现乱码// PrintWriter out = new PrintWriter(connection.getOutputStream());  // flush输出流的缓冲  out.flush();  in = new BufferedReader(new InputStreamReader(connection.getInputStream(),"UTF-8"));String line;while ((line = in.readLine()) != null) {result += line;}} catch (Exception e) {e.printStackTrace();}// 使用finally块来关闭输入流finally {try {if (in != null) {in.close();}} catch (Exception e2) {e2.printStackTrace();}}return result;
}
}

1.8、 小程序扫码

wx.getSetting({success: res => {if (res.authSetting['scope.userInfo']) {var that = this// 登录wx.login({success: res => {// 发送 res.code 到后台换取 openId, sessionKeywx.getUserInfo({withCredentials: true,success: function (res_user) {that.globalData.userInfo = res_user.userInfo;debuggerwx.request({url: getApp().globalData.servsers + '/custom/api/wxUserLogin',data: { code: res.code, avatar: res_user.userInfo.avatarUrl,nickName: res_user.userInfo.nickName },method: 'POST',header: { "Content-Type": "application/x-www-form-urlencoded" },success: function (res) {console.log(JSON.stringify(res.data));that.globalData.openId = res.data.openId;that.globalData.sessionKey = res.data.sessionKey;that.globalData.userInfo = res.data.user;wx.hideLoading();}, fail: function (e) {wx.hideLoading();}})// });}, fail: function (e) {debugger;}})// }}, fail: function (e) {debugger;wx.hideLoading();}})// }// })} else {// wx.redirectTo({//   url: '/pages/getuser/index',// })}
}
})

2、 获取手机号

2.1、 微信公众平台小程序获取手机号说明地址:

https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/getPhoneNumber.html

2.2、 获取手机号

获取微信用户绑定的手机号,需先调用wx.login接口。
因为需要用户主动触发才能发起获取手机号接口,所以该功能不由 API 来调用,需用 button 组件的点击来触发。
注意 目前该接口针对非个人开发者,且完成了认证的小程序开放(不包含海外主体)。需谨慎使用,若用户举报较多或被发现在不必要场景下使用,微信有权永久回收该小程序的该接口权限。
使用方法
需要将 button 组件 open-type 的值设置为 getPhoneNumber,当用户点击并同意之后,可以通过 bindgetphonenumber 事件回调获取到微信服务器返回的加密数据, 然后在第三方服务端结合 session_key 以及 app_id 进行解密获取手机号。
注意
在回调中调用 wx.login 登录,可能会刷新登录态。此时服务器使用 code 换取的 sessionKey 不是加密时使用的 sessionKey,导致解密失败。建议开发者提前进行 login;或者在回调中先使用 checkSession 进行登录态检查,避免 login 刷新登录态。

2.3、 后台接口编写

//Controller层
@ApiOperation("绑定手机号码")
@RequestMapping(value = "/bindMobile", method = RequestMethod.POST)
@ResponseBody
@ApiImplicitParams({@ApiImplicitParam(name = "openId", value = "OPENID", required = true, dataType = "String", paramType = "query"),@ApiImplicitParam(name = "id", value = "用户id", required = true, dataType = "Long", paramType = "query"),@ApiImplicitParam(name = "encrypData", value = "encrypData", required = true, dataType = "String", paramType = "query"),@ApiImplicitParam(name = "iv", value = "iv", required = true, dataType = "String", paramType = "query"),@ApiImplicitParam(name = "sessionKey", value = "sessionKey", required = true, dataType = "String", paramType = "query")
})
public CommonResult bindMobile(String openId, Long id, String encrypData, String iv, String sessionKey ) {try {User user = userService.getUserByIdAndOpenId(id,openId);if(user==null){return new CommonResult(203,"用户校验失败");}logger.info("Bind_RequestJson==================>>>> openId="+openId+",encrypData="+encrypData+",iv="+iv+",sessionKey:"+sessionKey);return userService.bind(user,encrypData, iv, sessionKey);} catch (Exception e) {e.printStackTrace();logger.info("BindError===>"+e.getMessage());return new CommonResult(203,"操作失败",e.getMessage());}
}
//Service层
@Overridepublic CommonResult bind(User user, String encrypData, String iv, String sessionKey) {String str = "";Map map = new HashMap();try {str = AESDecodeUtils.decrypt(encrypData, iv, sessionKey);} catch (Exception e) {e.printStackTrace();return new CommonResult(203,"获取手机号码失败");}JSONObject jsStrs = JSONObject.parseObject(str);String phoneNumber = jsStrs.getString("phoneNumber");if (!StringUtils.isNotBlank(phoneNumber)) {return new CommonResult(203,"获取手机号码失败");}user.setMobile(phoneNumber);this.updateUser(user);map.put("mobileFlag", true);map.put("user",user);return new CommonResult(200,"绑定手机号成功", map);}public int updateUser(User user) {return userMapper.updateUser(user);}

2.4、 小程序端

<button open-type="getPhoneNumber" style="background-color: #4c5870;color: #ffffff;margin: 20px;border-radius:0px" bindgetphonenumber="getPhoneNumber">手机号授权</button>
getPhoneNumber: function (e) {var encryptedData = e.detail.encryptedData;var iv = e.detail.iv;var thatss = this;if (e.detail.errMsg == 'getPhoneNumber:fail user deny') { //用户点击拒绝
wx.showModal({title: '手机号未授权',content: '如需正常使用小程序功能,请按确定后并重新点击【手机号授权】按钮,然后选择【允许】',showCancel: false,success: function (res) {if (res.confirm) {}}
})
return false;
} else {wx.showLoading({title: 'Loading...',
})
wx.login({success: function (e) {wx.getUserInfo({success: function (res) {let promise = new Promise(function (resolve, reject) {getApp().globalData.userInfo = res.userInfo;wx.request({url: getApp().globalData.servsers + '/api/wechat/custom/wxUserLogin',data: { code: res.code, avatar: res_user.userInfo.avatarUrl, nickName: res_user.userInfo.nickName },method: 'POST',header: { "Content-Type": "application/x-www-form-urlencoded" },success: function (res) {console.log(JSON.stringify(res.data));that.globalData.openId = res.data.openId;that.globalData.sessionKey = res.data.sessionKey;that.globalData.userInfo = res.data.user;wx.hideLoading();}, fail: function (e) {wx.hideLoading();}})})},fail: function (e) {wx.hideLoading();debugger;}})}, fail: function () {wx.hideLoading();debugger;}
})
}
}

3、 充值(小程序支付)

3.1、微信支付官方地址:

https://developers.weixin.qq.com/miniprogram/dev/api/open-api/payment/wx.requestPayment.html
参数

属性 类型 默认值 必填 说明
timeStamp string 时间戳,从 1970 年 1 月 1 日 00:00:00 至今的秒数,即当前的时间
nonceStr string 随机字符串,长度为32个字符以下
package string 统一下单接口返回的 prepay_id 参数值,提交格式如:prepay_id=***
signType string MD5 签名算法
paySign string 签名,具体签名方案参见 小程序支付接口文档
success function 接口调用成功的回调函数
fail function 接口调用失败的回调函数
complete function 接口调用结束的回调函数(调用成功、失败都会执行)

3.2、业务流程时序图


1、用户进入小程序
2、用户使用小程序进行支付(商户系统,就是我们所开发的程序)
3、调用小程序登录API(目的是为了获取用户的openId,这一步,我们可以在授权登录的时候做)
4、验证身份成功后异步返回openId
5、生成商户订单(在我们的数据库添加一条待支付状态的订单记录)
6、调用支付统一下单API(目的是为了获取预支付单号)
7、返回预付单信息(prepay_id即预支付单号),实际需要先执行6、7再执行5把预支付单号存到订单记录中,是为了解决当下未付款订单从未支付订单列表再次支付的问题。
8、组合appId、nonceStr、prepay_id、signType、timeStamp、key得到支付签名paySign
9、返回支付所需要的5个参数和sign
10、用户确认支付
11、鉴权调用微信后台支付
12、异步返回给微信小程序支付结果
13、小程序展示支付结果
14、同步推送支付结果给我们所开发的程序(我们设置的notify_url通知地址的路径)。
15、根据微信后台返回给我们的微信状态更新订单状态成功返回成功,失败返回失败并添加支付失败记录。

3.3、 后台接口

3.4、支付我采用sdk进行实现,地址:

https://github.com/Wechat-Group/WxJava
由于支付需要商户号和商户秘钥,这里就不在把此部分代码放入GitHub,下面是我工作中实现的支付接口代码:

@Log(title = "统一下单", businessType = BusinessType.INSERT)
@ApiOperation("微信支付统一下单")
@ApiImplicitParams({@ApiImplicitParam(name = "openId", value = "openId", required = true, dataType = "String", paramType = "query"),@ApiImplicitParam(name = "employeeId", value = "员工id", required = true, dataType = "Long", paramType = "query"),@ApiImplicitParam(name = "amount", value = "金额", required = true, dataType = "Double", paramType = "query")
})
@PostMapping("/placeOrder")
public RequestResult add(String openId, Long employeeId, Double amount,HttpServletRequest request) throws WxPayException {ZjEmployee zjEmployee = zjEmployeeService.selectZjEmployeeDetailById(employeeId);
if (zjEmployee == null) {return RequestResult.employee_error("无效用户!");
}
if(amount>0){ZjRechargeRecord zjRechargeRecord = new ZjRechargeRecord();
int cent = (new Double(amount * 100).intValue());
String formatStr = new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date());
zjRechargeRecord.setTransactionNum("ZJ" + formatStr + new DecimalFormat("0").format(amount).toString());
zjRechargeRecord.setRechargeTime(new Date());
zjRechargeRecord.setEmployeeId(employeeId);
zjRechargeRecord.setAmount(amount);
zjRechargeRecord.setPayStatus("1");
//设置充值类型为个人充值
zjRechargeRecord.setRechargeType("2");
WxPayUnifiedOrderRequest wxPayRequest= WxPayUnifiedOrderRequest.newBuilder().build();
//设置随机字符串
wxPayRequest.setNonceStr(RandomUtil.getRandomString(32));
//设置body
wxPayRequest.setBody("充值餐费金额");
//设置商户订单号
wxPayRequest.setOutTradeNo(zjRechargeRecord.getTransactionNum());
//设置币种
wxPayRequest.setFeeType("CNY");
//设置标价金额
wxPayRequest.setTotalFee(cent);
//设置终端ip
wxPayRequest.setSpbillCreateIp(getIp(request));
//设置通知地址
wxPayRequest.setNotifyUrl(AppletConfig.IMGURLPREFIX+"/api/wechat/notify/order");
//设置交易类型
wxPayRequest.setTradeType("JSAPI");
//设置用户标识
wxPayRequest.setOpenid(openId);
//设置签名方式
wxPayRequest.setSignType("MD5");
WxPayUnifiedOrderResult wxPayUnifiedOrderResult= this.wxService.unifiedOrder(wxPayRequest);
boolean addOrderFlag=true;
if("OK".equals(wxPayUnifiedOrderResult.getReturnMsg())){zjRechargeRecord.setWxOrderNum(wxPayUnifiedOrderResult.getPrepayId());addOrderFlag=zjRechargeRecordService.insertZjRechargeRecord(zjRechargeRecord)>0?true:false;
}
if(addOrderFlag){StringBuffer buffer = new StringBuffer();String timeStamp =String.valueOf(System.currentTimeMillis() / 1000);buffer.append("appId=").append(wxPayUnifiedOrderResult.getAppid()).append("&nonceStr=").append(wxPayUnifiedOrderResult.getNonceStr()).append("&package=prepay_id=").append(wxPayUnifiedOrderResult.getPrepayId()).append("&signType=MD5&timeStamp=").append(timeStamp).append("&key=").append(AppletConfig.APIKEY);String paySign= Md5Utils.MD5Encode(buffer.toString(), "UTF-8").toUpperCase();Map map = new HashMap();map.put("paySign",paySign);map.put("timeStamp",timeStamp);map.put("nonceStr",wxPayUnifiedOrderResult.getNonceStr());map.put("signType","MD5");map.put("package","prepay_id="+wxPayUnifiedOrderResult.getPrepayId());return RequestResult.success(map);
}else{return RequestResult.employee_error("下单失败请稍后再试!");
}}else{return RequestResult.employee_error("充值金额必须大于0!");
}
}
/***  支付回调通知处理* @param xmlData* @return* @throws WxPayException*/
@Log(title = "充值支付", businessType = BusinessType.INSERT)
@PostMapping("/notify/order")
public String parseOrderNotifyResult(@RequestBody String xmlData) throws WxPayException {final WxPayOrderNotifyResult notifyResult = this.wxService.parseOrderNotifyResult(xmlData);if("SUCCESS".equals(notifyResult.getResultCode())){boolean addMountFlag = true;ZjRechargeRecord zjRechargeRecord = zjRechargeRecordService.getZjRechargeRecordByTransactionNum(notifyResult.getOutTradeNo());if(zjRechargeRecord==null){WxPayNotifyResponse.fail("充值失败请联系管理员");}else{zjRechargeRecord.setPayStatus("2");zjRechargeRecordService.updateZjRechargeRecord(zjRechargeRecord);addMountFlag = zjEmployeeService.rechargeByPriceAndId(zjRechargeRecord.getAmount(), zjRechargeRecord.getEmployeeId()) > 0 ? true : false;if(!addMountFlag){ZjRechargeFailLog zjRechargeFailLog=new ZjRechargeFailLog();zjRechargeFailLog.setTransactionNum(zjRechargeRecord.getTransactionNum());zjRechargeFailLog.setWxOrderNum(zjRechargeRecord.getWxOrderNum());zjRechargeFailLog.setEmployeeId(zjRechargeRecord.getEmployeeId());zjRechargeFailLog.setAmount(zjRechargeRecord.getAmount());zjRechargeFailLog.setRechargeTime(zjRechargeRecord.getRechargeTime());zjRechargeFailLogService.insertZjRechargeFailLog(zjRechargeFailLog);WxPayNotifyResponse.fail("充值失败请联系管理员");}return WxPayNotifyResponse.success("成功");}else{return WxPayNotifyResponse.fail(notifyResult.getReturnMsg());}
}
public static String getIp(HttpServletRequest request) {String ip = request.getHeader("X-Forwarded-For");if (StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) {//多次反向代理后会有多个ip值,第一个ip才是真实ipint index = ip.indexOf(",");if (index != -1) {return ip.substring(0, index);} else {return ip;}}ip = request.getHeader("X-Real-IP");if (StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) {return ip;}try {return InetAddress.getLocalHost().getHostAddress();} catch (Exception e) {return "127.0.0.1";}
}

3.5、不用SDK,手动编写,需要自己对参数值进行XML转义,提供工具类如下:

参数值转义后的结果示例:
代码:

public class PayCommonUtil {private final static Logger logger = LoggerFactory.getLogger(PayCommonUtil.class);
public static Map<String, String> weixinPrePay(String openid, String sn,int totalAmount,  String body, String request) {  SortedMap<String, Object> parameterMap = new TreeMap<String, Object>();  parameterMap.put("appid", AppletConfig.APPID);parameterMap.put("mch_id", AppletConfig.MCHID);//设置32位随机字符串parameterMap.put("nonce_str", RandomUtil.getRandomString(32));parameterMap.put("body", body); parameterMap.put("out_trade_no", sn);  parameterMap.put("fee_type", "CNY");  parameterMap.put("total_fee", totalAmount+"");  parameterMap.put("spbill_create_ip", request);  parameterMap.put("notify_url", "自己设置的接收通知结果的url");  parameterMap.put("trade_type", "JSAPI");  parameterMap.put("openid", openid);  String sign = PayCommonUtil.createSign("UTF-8", parameterMap,AppletConfig.APIKEY);parameterMap.put("sign", sign);  String requestXML = PayCommonUtil.getRequestXml(parameterMap);  System.out.println(requestXML);  String result = PayCommonUtil.httpsRequest(  "https://api.mch.weixin.qq.com/pay/unifiedorder", "POST",  requestXML);  Map<String, String> map = null;  try {  map = PayCommonUtil.doXMLParse(result);  } catch (JDOMException e) {
logger.info("生成微信支付失败=============================>>>>weixinPrePay-JDOMException:"+e.getMessage()); // TODO Auto-generated catch block  e.printStackTrace();   } catch (IOException e) {
logger.info("生成微信支付失败=============================>>>>weixinPrePay-IOException:"+e.getMessage());// TODO Auto-generated catch block  e.printStackTrace();  }  return map;
}
//拼接xml
public static String getRequestXml(SortedMap<String, Object> parameters) {StringBuffer sb = new StringBuffer();sb.append("<xml>");Set es = parameters.entrySet();Iterator it = es.iterator();while (it.hasNext()) {Map.Entry entry = (Map.Entry) it.next();String key = (String) entry.getKey();String value = (String) entry.getValue();if ("attach".equalsIgnoreCase(key) || "body".equalsIgnoreCase(key) || "sign".equalsIgnoreCase(key)) {sb.append("<" + key + ">" + "<![CDATA[" + value + "]]></" + key + ">");} else {sb.append("<" + key + ">" + value + "</" + key + ">");}}sb.append("</xml>");return sb.toString();
}
//通过签名算法算出签名值
public static String createSign(String characterEncoding, SortedMap<String, Object> parameters, String apiKey) {StringBuffer sb = new StringBuffer();Set es = parameters.entrySet();Iterator it = es.iterator();while (it.hasNext()) {Map.Entry entry = (Map.Entry) it.next();String k = (String) entry.getKey();Object v = entry.getValue();if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {sb.append(k + "=" + v + "&");}}sb.append("key=" + apiKey);System.out.println(sb.toString());String sign = Md5Utils.MD5Encode(sb.toString(), characterEncoding).toUpperCase();return sign;
}
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);conn.setRequestMethod(requestMethod);conn.setRequestProperty("content-type", "application/x-www-form-urlencoded");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();conn.disconnect();return buffer.toString();} catch (ConnectException ce) {System.out.println("连接异常" + ce);} catch (Exception e) {System.out.println("请求" + e);}return null;
}//xml解析
public static Map doXMLParse(String strxml) throws  IOException, JDOMException {strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");if (null == strxml || "".equals(strxml)) {return null;}Map m = new HashMap();InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));SAXBuilder builder = new SAXBuilder();Document doc = builder.build(in);Element root = doc.getRootElement();List list = root.getChildren();Iterator it = list.iterator();while (it.hasNext()) {Element e = (Element) it.next();String k = e.getName();String v = "";List children = e.getChildren();if (children.isEmpty()) {v = e.getTextNormalize();} else {v = getChildrenText(children);}System.out.println(k+"-"+v);m.put(k, v);}in.close();return m;
}
public static String getChildrenText(List children) {StringBuffer sb = new StringBuffer();if (!children.isEmpty()) {Iterator it = children.iterator();while (it.hasNext()) {Element e = (Element) it.next();String name = e.getName();String value = e.getTextNormalize();List list = e.getChildren();sb.append("<" + name + ">");if (!list.isEmpty()) {sb.append(getChildrenText(list));}sb.append(value);sb.append("</" + name + ">");}}return sb.toString();
}
}

需要在pom中加入jdom依赖,用于解析xml

<dependency><groupId>org.jdom</groupId><artifactId>jdom</artifactId><version>1.1.3</version>
</dependency>

3.6、小程序端调用

需要先调用我们的统一下单接口得到参数,再调用wx.wx.requestPayment进行扣款操作

let openId = wx.getStorageSync('openId');
let { amount, userData } =this.data;
Req({url:'/api/wechat/placeOrder',
method:"POST",
data:{ employeeId:userData.id, openId, amount }
}).then((res)=> {let { data } =res.data;
wx.requestPayment({timeStamp: data.timeStamp,//时间戳nonceStr: data.nonceStr,//随机字符串package: data.package,//prepay_idsignType: 'MD5',//签名算法paySign: data.paySign,//签名success (res) { let { errMsg } =res;if(errMsg == "requestPayment:ok") {Req({url:'/api/wechat/getPersonalInfo',method:"GET",data:{ employeeId:userData.id }}).then((res)=> {let { data } =res.data;wx.setStorageSync('userData', JSON.stringify(data));wx.switchTab({url: '/pages/index/index',})},(err)=>{})}},fail (res) { }
})
},(err)=>{})

4、GitHub地址:

GitHub上SpringBoot工程中仅提供授权登录、绑定手机号的完整代码,微信支付青参照博客充值(小程序支付)这一章节,另会附上user表sql、小程序部分代码,由于小程序是使用我个人申请注册的,故只可以正常演示登录授权功能、获取手机号和充值功能小程序秘钥等需要在开发时替换成项目中申请的小程序的秘钥。amy_demo文件夹下为小程序代码,wechat-applet文件夹下为后台接口代码。
附上GitHub地址:
https://github.com/Amywang1996/wechat
如有不足之处,欢迎交流,提醒更正O(∩_∩)O~!

微信小程序授权登录、绑定手机号(接口+小程序)相关推荐

  1. 微信小程序授权登录+获取手机号

    微信小程序授权登录+获取手机号 一.官方文档背景: 小程序登录的链接: 微信小程序获取手机号的链接: 二.微信小程序授权登录+获取手机号 1.简单说明官方文档的操作:先授权登录后拿手机号 2.前端代码 ...

  2. 基于Thinkphp5+EasyWeChat+fastadmin微信小程序授权登录获取手机号微信公众号网页---联合授权登录

    战前准备 1.使用 composer 安装 EasyWeChat $ composer require overtrue/wechat:~4.0 -vvv 或者在composer.json文件renq ...

  3. 微信小程序授权登录获取手机号,偶尔出现41003问题

    最近做微信授权登录获取手机号有时会出现41003问题,研究半天网上查了不少资料,梳理下过程: 从文档可知主要是wx.login()获取code顺序问题 <button class="b ...

  4. 苹果授权登录绑定手机号被拒绝

    问题1悲剧原因: 描述: 5. 1.1 Legal: Privacy - Data Collection and Storage Guideline 5.1.1 - Legal - Privacy - ...

  5. 【uniapp小程序授权登录】

    uniapp微信小程序授权登录具体流程 打开小程序公众平台,申请小程序appid hbuilderx创建项目并打开项目,点击manifest.json->点击小程序配置->配置小程序app ...

  6. 【小程序】- 微信授权、绑定手机号

    微信小程序授权登录并登记手机号码,以下是根据小程序官网梳理的流程图,方便自己理解: 官方指引:小程序登录 // 微信授权登录bindGetUserInfo: function(e) {// 将用户信息 ...

  7. 微信小程序 授权登录+手机发送验证码+jwt验证接口(laravel8+php)

    参考博客 小程序授权登录并 laravel7(laravel8) token 应用 - 王越666 - 博客园 微信小程序sku商品规格选择器 - 简书 微信小程序 和 laravel8 实现搜索后分 ...

  8. 微信小程序---微信信息授权登录以及手机号授权登录

    微信小程序-微信信息授权登录以及手机号授权登录 (小白 为了以后方便查看)点击微信信息授权登录后 再次点击按钮进行手机号授权登录 图片演示 wxml(login.wxml)代码 <view cl ...

  9. uniapp微信小程序授权登录和获取微信绑定的手机号码

    uniapp微信小程序授权登录和获取微信绑定的手机号码 弹出授权的弹框 <view class="weixinOk" @tap="getUserProfile&qu ...

  10. 微信小程序授权登录和获取手机号功能(详细)

    总结,新增获取用户手机号功能,里面用到了关于获取用户信息和用户手机号的功能,一开始写的时候发现我把两个按钮写在一个登录页面上,获取手机号逻辑是当用户点击授权之后跳转到首页,当点击拒绝弹窗提示,最后发现 ...

最新文章

  1. fastp: 极速全能的FASTQ文件自动质控+过滤+校正+预处理软件
  2. 【数据库】将Excel导入达梦数据库,并执行表合并
  3. 【QA】pytorch中的worker如何工作的
  4. effective c++ 条款20:宁以pass-by-reference-to-const替换pass-by-value
  5. 【Boost】boost库中thread多线程详解4——谈谈recursive_mutex
  6. Java加密与解密的艺术~RSA实现
  7. linux 下安装maven
  8. jquery.imageScroller实现图片滚动
  9. linux温故知新十
  10. 可重复使用的外科缝合器行业调研报告 - 市场现状分析与发展前景预测
  11. Python 字典实现原理
  12. JavaScript案例精解(一)
  13. Javascript (history,location)对象
  14. corosycnpacemaker的高可用web集群
  15. VMware Workstation 14 Pro永久激活密钥
  16. 一阶系统开环传递函数表达式_自动控制总结:第二章、控制系统的数学模型
  17. 【C语言】1-100求和;1-N求和
  18. SSM框架 基于Bootstrap fileinput 实现文件上传功能
  19. 移动硬盘部分分区不能识别解决方法
  20. JavaScript 引用类型 读书笔记

热门文章

  1. 华为开源镜像站体验评测报告
  2. 解决winrm4j中文乱码问题和执行脚本文件
  3. pt100专用芯片_T100/PT1000温度传感器芯片-PT100温度传感器芯片
  4. 新浪微博登陆页面html代码,新浪微博登陆页面 找到我的位置,选择编辑;
  5. Scrapy爬虫框架,入门案例(非常详细)
  6. sumif单列求和_sumif函数使用方法:单条件求和
  7. 蓝底换白底边缘不干净_PS∶红底证件照换成蓝色背景,边缘怎样处理,让照片更自然呢?...
  8. multivariate_normal TypeError: ufunc ‘add‘ output (typecode ‘O‘) could not be coerced to provided……
  9. Web3到底是什么?
  10. 泪目!java面试八股文是哪些