热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

app调用微信支付疯狂踩坑(前端uniapp后端java)

之前有做了小程序的微信支付以为app的小程序的流程应该差不多,就直接把代码c过来然后发现不行,踩了一堆坑,硬生生耗了俩周(中

之前有做了小程序的微信支付以为app的小程序的流程应该差不多,就直接把代码c过来然后发现不行,踩了一堆坑,硬生生耗了俩周(中间还有其他事情耽搁了)
小程序调起微信支付

 


目录标题


    • 准备工作
    • 后台代码
    • 前端代码
    • 踩的坑

 


准备工作

微信商户平台注册
微信开放平台注册
注册会需要营业执照,申请支付接口的话会需要300元的审核费用,具体就不多说了,这个不难而且网上教程一堆。

先上代码吧,最后再说踩的坑


后台代码

package com.ruoyi.web.controller.defalt;import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.domain.COrder;
import com.ruoyi.system.domain.CUser;
import com.ruoyi.system.service.ICOrderService;
import com.ruoyi.system.service.ICUserService;
import com.ruoyi.util.RedisUtil;
import com.ruoyi.util.wx.PayForUtil;
import com.ruoyi.util.wx.WXPayUtils;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;import java.io.*;
import java.util.*;/*** 登录注册Controller* * @author 浮生* @date 2019-09-16*/
@Controller
@RequestMapping("/front")
public class FrontBuyController extends BaseController {@AutowiredRedisUtil redis;@Autowiredprivate ICOrderService orderService;@Autowiredprivate ICUserService cUserService;String appId = "微信开放平台申请的移动端的appid";// 商户号String partnerId = "微信商户平台的商户号";//商户号String mch_id = partnerId;//扩展字段,暂填写固定值Sign=WXPay,java无法使用package关键字作为变量名所以用packageValue替代String packageValue = "Sign=WXPay";// 第一次发起支付请求的接口地址String pay_url = "https://api.mch.weixin.qq.com/pay/unifiedorder";// key为商户平台设置的密钥keyString WXKeHukey = "商户平台设置的密钥";// 本机的ip地址String spbill_create_ip = "本机的ip地址";// 支付回调地址String notify_url = "支付回调地址";// 调起支付方式String trade_type = "APP";// 签名类型String SIGNTYPE = "MD5";/*** a调起微信预支付接口* * @param money:支付金额* @return* @throws UnsupportedEncodingException */@GetMapping("/buyStepOne")@ResponseBody@CrossOrigin(allowCredentials = "true")public AjaxResult buyStepOne(Long money,String redisKey) throws UnsupportedEncodingException {if(redis.hasKey(redisKey)) {//获取当前用户idLong userId = Long.parseLong(redis.get(redisKey).toString());// 通过course_id查询购买的课程信息System.out.println(money);// 生成的随机字符串String nonce_str = StringUtils.getRandomString(32);// 商品名称String body = new String("shuangxi-huiyuan".getBytes("ISO-8859-1"), "UTF-8");// 商户订单号(先用时间加上3位随机数代替)Date date = new Date();String orderNo = date.getTime() + StringUtils.getRandomString(3) + "";Map packageParams = new HashMap();// 小程序ID,微信分配的小程序IDpackageParams.put("appid", appId);// 小程序ID,微信分配的小程序IDpackageParams.put("attach", userId.toString());// 商户号,微信支付分配的商户号packageParams.put("mch_id", mch_id);// 随机字符串,长度要求在32位以内。packageParams.put("nonce_str", nonce_str);// 商品简单描述packageParams.put("body", body);// 商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|*且在同一个商户号下唯一packageParams.put("out_trade_no", orderNo);// 商户订单号// 订单总金额,单位为分packageParams.put("total_fee", money.toString());// 终端IP,支持IPV4和IPV6两种格式的IP地址。调用微信支付API的机器IPpackageParams.put("spbill_create_ip", spbill_create_ip);// 异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。packageParams.put("notify_url", notify_url);// 交易类型(JSAPI--JSAPI支付(或小程序支付)、NATIVE--Native支付、APP--app支付,MWEB--H5支付,不同trade_type决定了调起支付的方式,请根据支付产品正确上传)packageParams.put("trade_type", trade_type);// 除去数组中的空值和签名参数packageParams = WXPayUtils.paraFilter(packageParams);String prestr = WXPayUtils.createLinkString(packageParams); // 把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串System.out.println("签名数据"+prestr);// MD5运算生成签名,这里是第一次签名,用于调用统一下单接口,key为商户平台设置的密钥keyString mysign = WXPayUtils.sign(prestr, WXKeHukey, "utf-8").toUpperCase();System.out.println("=======================第一次签名:" + mysign + "=====================");// 拼接统一下单接口使用的xml数据,要将上一步生成的签名一起拼接进去String xml = "" + "" + appId + "" + "" + userId + "" + "" + "" + mch_id + "" + "" + nonce_str + ""+ "" + notify_url + "" + "" + orderNo + ""+ "" + spbill_create_ip + "" + "" + money + "" + "" + trade_type + "" + "" + mysign + ""+ "";System.out.println("调试模式_统一下单接口 请求XML数据:" + xml);// 调用统一下单接口,并接受返回的结果String result = WXPayUtils.httpRequest(pay_url, "POST", xml);System.out.println("调试模式_统一下单接口 返回XML数据:" + result);// 将解析结果存储在HashMap中
// Map map = doXMLParse(result);// String return_code = (String) map.get("return_code");//返回状态码// 解析结果,将结果存在map中Map map = WXPayUtils.doXMLToMap(result);System.out.println(map);String return_code = (String) map.get("return_code");// 返回给移动端需要的参数Map response = new HashMap();if (return_code == "SUCCESS" || return_code.equals(return_code)) {// 业务结果String prepay_id = (String) map.get("prepay_id");// 返回的预付单信息response.put("nonceStr", (String) map.get("nonceStr"));response.put("package", "prepay_id=" + prepay_id);Long timeStamp = System.currentTimeMillis() / 1000;response.put("timeStamp", timeStamp + "");// 这边要将返回的时间戳转化成字符串,不然小程序端调用wx.requestPayment方法会报签名错误String stringSignTemp = "appId=" + appId + "&nonceStr=" + (String) map.get("nonceStr") + "&package=prepay_id=" + prepay_id+ "&signType=" + SIGNTYPE + "&timeStamp=" + timeStamp;// 再次签名,这个签名用于小程序端调用wx.requesetPayment方法
// String paySign = WXPayUtils.sign(stringSignTemp, WXKeHukey, "utf-8").toUpperCase();
// System.out.println("=======================第二次签名:" + paySign + "=====================");// response.put("paySign", paySign);response.put("appid", appId);response.put("sign", (String) map.get("sign"));response.put("mysign",mysign);// 将订单和报名信息添加到数据库COrder c_order = new COrder();c_order.setCreateTime(new Date());c_order.setOrderMoney(money);c_order.setOrderFlag(0);c_order.setOrderNumber(orderNo);c_order.setUserId(userId);orderService.insertCOrder(c_order);}System.out.println(response);AjaxResult resultall = new AjaxResult();return resultall.success(response);}else {return error("未登陆");}}/*** 微信支付回调方法*/@GetMapping("/buySteptwo")@ResponseBody@CrossOrigin(allowCredentials = "true")public AjaxResult buySteptwo() {try {//读取参数 InputStream inputStream ; StringBuffer sb = new StringBuffer(); inputStream = getRequest().getInputStream(); String s ; BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8")); while ((s = in.readLine()) != null){ sb.append(s); } in.close(); inputStream.close(); //解析xml成map Map m = new HashMap(); m = doXMLParse(sb.toString());//过滤空 设置 TreeMap SortedMap packageParams = new TreeMap(); Iterator it = m.keySet().iterator(); while (it.hasNext()) { String parameter = it.next(); String parameterValue = m.get(parameter); String v = ""; if(null != parameterValue) { v = parameterValue.trim(); } packageParams.put(parameter, v); } // 微信支付的API密钥 String key = WXKeHukey; // key //判断签名是否正确 if(PayForUtil.isTenpaySign("UTF-8", packageParams,key)) { //------------------------------ //处理业务开始 //------------------------------ String resXml = ""; if("SUCCESS".equals((String)packageParams.get("result_code"))){ System.out.println("+++++++++++++++++++回调参数+++++++++++++++++++");System.out.println(packageParams);// 这里是支付成功 //执行自己的业务逻辑开始String app_id = (String)packageParams.get("appid");String mch_id = (String)packageParams.get("mch_id"); String openid = (String)packageParams.get("openid"); String is_subscribe = (String)packageParams.get("is_subscribe");//是否关注公众号//公用回传参数 我设置的是 "优惠券ID;课程Type:用户ID"String attach = (String)packageParams.get("attach");//商户订单号String out_trade_no = ((String)packageParams.get("out_trade_no")); out_trade_no = out_trade_no.replace(" ", "");//付款金额【以分为单位】String total_fee = (String)packageParams.get("total_fee"); //微信生成的交易订单号String transaction_id = (String)packageParams.get("transaction_id");//微信支付订单号//支付完成时间String time_end=(String)packageParams.get("time_end");
// //回调一次后进行判断,查询是否以及添加记录到数据库
// Record huidiao = Db.findById("c_order" , "order_number", transaction_id);
// if(huidiao != null) {
// redirect("/");
// return;
// }//通知微信.异步确认成功.必写.不然会一直通知后台.八次之后就认为交易失败了. resXml &#61; "" &#43; "" &#43; "" &#43; " "; /*** 支付成功后的处理*/// 更新订单数据Long userId &#61; Long.parseLong(attach);COrder c_order &#61; new COrder();c_order.setOrderNumber(out_trade_no);List orderList &#61; orderService.selectCOrderList(c_order);if(orderList.size()>&#61;1) {c_order &#61; orderList.get(0);c_order.setCreateTime(new Date());c_order.setOrderMoney(Long.parseLong(total_fee));c_order.setOrderFlag(1);c_order.setUserId(userId);orderService.updateCOrder(c_order);//查询会员月费Long monthMoney &#61; orderService.selectMonthMoney();//计算充值多少个月的会员long month &#61; Long.parseLong(total_fee)/monthMoney;//查询用户信息CUser cUser &#61; new CUser();cUser.setUserId(userId);List userList &#61; cUserService.selectUserInfoList(cUser);//根据用户vip剩余时间处理业务if(userList.size()<&#61;0) {cUser &#61; userList.get(0);Calendar userVip &#61; Calendar.getInstance();userVip.setTime(cUser.getUserVip());Calendar now &#61; Calendar.getInstance();userVip.setTime(new Date());if(userVip.after(now)) {//会员未过期}else {//会员已过期&#xff0c;或者未充值过会员userVip &#61; now;}userVip.add(Calendar.MONTH, (int)month);cUser.setUserVip(userVip.getTime());cUserService.updateCUser(cUser);return success();}else {System.out.println("查询不到用户");return error("查询不到用户");}}else {System.out.println("获取订单失败请与管理员联系");return error("获取订单失败请与管理员联系");}} else { System.out.println("支付失败,错误信息&#xff1a;" &#43; packageParams.get("err_code")); resXml &#61; "" &#43; "" &#43; "" &#43; " "; //TODO 支付失败要做什么 我这里先不写//------------------------------ //处理业务完毕 //------------------------------ BufferedOutputStream out &#61; new BufferedOutputStream( getResponse().getOutputStream()); out.write(resXml.getBytes()); out.flush(); out.close();} } else{ System.out.println("通知签名验证失败");return error("通知签名验证失败");} }catch(IOException e) {e.printStackTrace();return error("IO处理失败&#xff0c;请联系管理员&#xff01;");} catch (JDOMException e) {e.printStackTrace();return error("JDOM处理失败&#xff0c;请联系管理员&#xff01;");}return error(); }/** * 解析xml,返回第一级元素键值对。如果第一级元素有子节点&#xff0c;则此节点的值是子节点的xml数据。 * &#64;param strxml * &#64;return * &#64;throws JDOMException * &#64;throws IOException */ &#64;SuppressWarnings({ "rawtypes", "unchecked" })public static Map doXMLParse(String strxml) throws JDOMException, IOException { strxml &#61; strxml.replaceFirst("encoding&#61;\".*\"", "encoding&#61;\"UTF-8\""); if(null &#61;&#61; strxml || "".equals(strxml)) { return null; } Map m &#61; new HashMap(); InputStream in &#61; new ByteArrayInputStream(strxml.getBytes("UTF-8")); SAXBuilder builder &#61; new SAXBuilder(); Document doc &#61; builder.build(in); Element root &#61; doc.getRootElement(); List list &#61; root.getChildren(); Iterator it &#61; list.iterator(); while(it.hasNext()) { Element e &#61; (Element) it.next(); String k &#61; e.getName(); String v &#61; ""; List children &#61; e.getChildren(); if(children.isEmpty()) { v &#61; e.getTextNormalize(); } else { v &#61; getChildrenText(children); } m.put(k, v); } //关闭流 in.close(); return m; } /** * 获取子结点的xml * &#64;param children * &#64;return String */ &#64;SuppressWarnings("rawtypes")public static String getChildrenText(List children) { StringBuffer sb &#61; new StringBuffer(); if(!children.isEmpty()) { Iterator it &#61; children.iterator(); while(it.hasNext()) { Element e &#61; (Element) it.next(); String name &#61; e.getName(); String value &#61; e.getTextNormalize(); List list &#61; e.getChildren(); sb.append("<" &#43; name &#43; ">"); if(!list.isEmpty()) { sb.append(getChildrenText(list)); } sb.append(value); sb.append(""); } } return sb.toString(); } }

pom文件
可以参考下我之前的小程序的代码的pom文件&#xff0c;小程序调起微信支付&#xff0c;基本没做更改&#xff0c;只是用的spring-boot&#xff0c;本地打包加入外部jar包太麻烦就&#xff0c;直接走pom引入了外部的包&#xff0c;就多了下面一个包

org.jdomjdom1.1

用到的外部jar包和工具类&#xff0c;jar包好像都没用到就导了pom文件就可以了&#xff0c;添加pom文件后还是有问题&#xff0c;可以把jar包导下&#xff0c;maven项目导外部jar包&#xff0c;打jar包会有问题&#xff0c;需要自己去解决下&#xff0c;我就是解决不了&#xff0c;所以选择了直接找网上的pom文件直接导
https://download.csdn.net/download/qq_40488121/11704169


前端代码

&#xff08;前端不是我写的&#xff0c;我找前端要了份代码&#xff0c;全不全就不晓得了&#xff09;

uni.request({url:baseUrl&#43;&#39;/front/NOyStepOne&#39;,// method: &#39;GET&#39;,data: { redisKey:this.rediskey,money:30},success: res &#61;> {console.log(res.data.data)var str&#61;res.data.data.packagevar arr&#61;str.split("&#61;")[1]//胜利ar as1 &#61;arr.toUpperCase()//console.log(as1)var obj&#61;{appid:res.data.data.appid, //id 应用idpartnerid:&#39;1557620991&#39;, //商户号 prepayid:arr, //预支付package:&#39;Sign&#61;WXPay&#39;,noncestr:res.data.data.nonceStr,timestamp:res.data.data.timeStamp, //时间戳sign:res.data.data.sign}var orderInfo&#61;JSON.stringify(obj)console.log("222")console.log(obj)var test&#61;true;uni.requestPayment({provider: &#39;wxpay&#39;,orderInfo:orderInfo, //微信、支付宝订单数据success: function (res) {console.log(&#39;success:&#39; &#43; JSON.stringify(res));},fail: function (err) {console.log(&#39;fail:&#39; &#43; JSON.stringify(err));}});},fail: () &#61;> {},complete: () &#61;> {}});

踩的坑

说下我踩的坑吧


  1. 生成第一次签名的sign和xml里的参数要一模一样&#xff0c;我最开始就是不一致导致统一下单接口一直没调通&#xff0c;具体代码位置如下俩处

Map packageParams &#61; new HashMap();// 小程序ID,微信分配的小程序IDpackageParams.put("appid", appId);// 小程序ID,微信分配的小程序IDpackageParams.put("attach", userId.toString());// 商户号,微信支付分配的商户号packageParams.put("mch_id", mch_id);// 随机字符串&#xff0c;长度要求在32位以内。packageParams.put("nonce_str", nonce_str);// 商品简单描述packageParams.put("body", body);// 商户系统内部订单号&#xff0c;要求32个字符内&#xff0c;只能是数字、大小写字母_-|*且在同一个商户号下唯一packageParams.put("out_trade_no", orderNo);// 商户订单号// 订单总金额&#xff0c;单位为分packageParams.put("total_fee", money.toString());// 终端IP,支持IPV4和IPV6两种格式的IP地址。调用微信支付API的机器IPpackageParams.put("spbill_create_ip", spbill_create_ip);// 异步接收微信支付结果通知的回调地址&#xff0c;通知url必须为外网可访问的url&#xff0c;不能携带参数。packageParams.put("notify_url", notify_url);// 交易类型(JSAPI--JSAPI支付&#xff08;或小程序支付&#xff09;、NATIVE--Native支付、APP--app支付&#xff0c;MWEB--H5支付&#xff0c;不同trade_type决定了调起支付的方式&#xff0c;请根据支付产品正确上传)packageParams.put("trade_type", trade_type);// 除去数组中的空值和签名参数packageParams &#61; WXPayUtils.paraFilter(packageParams);String prestr &#61; WXPayUtils.createLinkString(packageParams); // 把数组所有元素&#xff0c;按照“参数&#61;参数值”的模式用“&”字符拼接成字符串

// 拼接统一下单接口使用的xml数据&#xff0c;要将上一步生成的签名一起拼接进去String xml &#61; "" &#43; "" &#43; appId &#43; "" &#43; "" &#43; userId &#43; "" &#43; "" &#43; "" &#43; mch_id &#43; "" &#43; "" &#43; nonce_str &#43; ""&#43; "" &#43; notify_url &#43; "" &#43; "" &#43; orderNo &#43; ""&#43; "" &#43; spbill_create_ip &#43; "" &#43; "" &#43; money &#43; "" &#43; "" &#43; trade_type &#43; "" &#43; "" &#43; mysign &#43; ""&#43; "";System.out.println("调试模式_统一下单接口 请求XML数据&#xff1a;" &#43; xml);// 调用统一下单接口&#xff0c;并接受返回的结果String result &#61; WXPayUtils.httpRequest(pay_url, "POST", xml);

这俩处的参数要一模一样&#xff0c;我就是因为多了个参数加在xml里&#xff0c;签名那里漏了&#xff0c;导致一直不行
2. app是不需要进行第二次签名的&#xff0c;前台需要的sign参数直接是在调用统一下单接口后放回的
3. 随机字符串也是在调用统一下单接口后放回的&#xff0c;不是用的我传给微信的&#xff0c;而是要用微信那传回来的


推荐阅读
  • 问题场景用Java进行web开发过程当中,当遇到很多很多个字段的实体时,最苦恼的莫过于编辑字段的查看和修改界面,发现2个页面存在很多重复信息,能不能写一遍?有没有轮子用都不如自己造。解决方式笔者根据自 ... [详细]
  • 实践指南:使用Express、Create React App与MongoDB搭建React开发环境
    本文详细介绍了如何利用Express、Create React App和MongoDB构建一个高效的React应用开发环境,旨在为开发者提供一套完整的解决方案,包括环境搭建、数据模拟及前后端交互。 ... [详细]
  • 本文探讨了如何通过优化 DOM 操作来提升 JavaScript 的性能,包括使用 `createElement` 函数、动画元素、理解重绘事件及处理鼠标滚动事件等关键主题。 ... [详细]
  • 本文探讨了如何通过Service Locator模式来简化和优化在B/S架构中的服务命名访问,特别是对于需要频繁访问的服务,如JNDI和XMLNS。该模式通过缓存机制减少了重复查找的成本,并提供了对多种服务的统一访问接口。 ... [详细]
  • 在现代前端开发中,组件化已成为不可或缺的技术,尤其在 React 和 Vue 生态中。然而,组件的管理和测试一直是开发者面临的挑战。本文将介绍如何使用 Storybook 来简化这一过程,提高开发效率。 ... [详细]
  • 在将 Android Studio 从 3.0 升级到 3.1 版本后,遇到项目无法正常编译的问题,具体错误信息为:org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':app:processDemoProductDebugResources'。 ... [详细]
  • 团队作业第二次—项目选题报告(追光的人)
    所属课程软件工程1916作业要求团队作业第二次—项目选题报告团队名称追光的人作业目标组员提出选题,大家挑选出可行性最高的进行分析,制作选题报告和选题PPT目录队员贡献分比例选题报告 ... [详细]
  • hbuilderX 编辑保存后 微信开发者工具无法自动刷新
    微信开发者工具不要勾选自动保存首先打开微信开发者工具,点击顶部的设置然后点击下拉列表中的编辑器设置选项 最后找到修改文件时自动保存选项 ... [详细]
  • Vue CLI 基础入门指南
    本文详细介绍了 Vue CLI 的基础使用方法,包括环境搭建、项目创建、常见配置及路由管理等内容,适合初学者快速掌握 Vue 开发环境。 ... [详细]
  • spring boot使用jetty无法启动 ... [详细]
  • 通过uni.getSystemInfoSync()可以同步获取系统信息,其中platform是获取客户端平台的switch(uni.getSystemInfoSyn ... [详细]
  • 阿里“云开发“小程序(uniCould)
    博主ps:网上资料少的可怜,哎,腾讯云涨价了,论服务器,我肯定选的阿里,再着你们对比下unico ... [详细]
  • 安卓ndk开发!高级Android晋升之View渲染机制,附答案
    缘起深圳市腾讯计算机系统有限公司成立于1998年11月,是中国最大的互联网综合服务提供商之一,也是中国服务用户最多的互联网企业之一。腾讯业务多元化& ... [详细]
  • php如何封装app(2023年最新分享)
    导读:今天编程笔记来给各位分享关于php如何封装app的相关内容,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在开始吧!本文目录一览: ... [详细]
  • uniapp中的样式和数据绑定(五)
    uni-app的样式1.uni-app种的样式1.1使用图标2.uni-app中的数据绑定3.v-bind动态绑定属性4.uni中的事件1.uni-app种的样式rpx即响应式p ... [详细]
author-avatar
odile微笑头
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有