1.支付前准备

1.1首先两个平台接入账户。

  1. 商户平台:https://pay.weixin.qq.com/index.php/core/home/login?return_url=%2F
  2. 公众平台:https://mp.weixin.qq.com/

1.2两个平台的作用

     3 商户平台:支付收款方,通俗点将就是网站用户支付的钱给谁。里面有商户的一些信息以及秘钥支付时要用。4.公众号平台:在这里需要它提供网页授权的一些信息。

2.微信公众号支付流程

    关于支付流程,官方给了一张很详细的流程图如下:

    当然这是所有支付的流程图,在这里博主结合自己的实现方式也画了一张只有授权和公众号支付流程的图。大家可以参考有画的不对的地方还请路过的大牛多多指教。

3. 微信公众号支付的那些坑

    1:获取授权的时候,访问授权接口不要用ajax提交。如果用ajax提交方式提交请求,微信服务端会报跨域请求不允许的错误。2:获取授权openid的时候appid,secret一定要正确,这里的secret获取方式为下图,如果忘记,可以重置:

    3:如果用的springmvc的框架,在统一支付接口记得用注解@ResponseBody,因为统一支付接口返回的数据一般是一个map,这个map中的数据前台页面要解析,所以需要这个注解。4:调用统一支付接口时,请求报文是xml格式的,所以要把需要的参数转变为xml形式。5:异步回调方法是支付成功后微信通知商户后台时调用,所以测试时需要在外网测试7:授权时,只授一次就行,所以在授权之前判断是否已经授权。8:微信支付以分为单位,支付金额需要注意转换单位。9:如果签名失败,一定要仔细检查参数是否都拼接完毕,拼接正确,一般签名失败最可能的原因就是secret错误,应该去微信公众平台重新查找并且核对。10:退款时,注意双向证书的路径。一般退款回调失败最可能的原因就是证书出错。

4. 微信公众号开发需要的一些官网网站

商户平台开发者文档网址
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4
用户授权需要查看的网址,重点查看用户管理相关说明
http://mp.weixin.qq.com/wiki/17/c0f37d5704f0b64713d5d2c37b468d75.html
商户平台微信支付网址
https://pay.weixin.qq.com/wiki/doc/api/index.html
商户平台登录网址
https://pay.weixin.qq.com/index.php/core/home/login?return_url=%2Findex.php%2Fcore%2Faccount
微信公众平台登录入口
https://mp.weixin.qq.com/

5.公众号支付代码

前端网页js代码

    var pay_orderno;    //订单编号//判断是否是授权回来的outhvar outh = $.getUrlParam("outh"); if (!empty(outh)){ //说明outh不为空,即为授权回来的,则直接微信支付 wxzhifu(); } function getRequest() { var url = location.search; //获取url中"?"符后的字串 var theRequest = new Object(); if (url.indexOf("?") != -1) { var str = url.substr(1); strs = str.split("&"); for (var i = 0; i < strs.length; i++) { theRequest[strs[i].split("=")[0]] = unescape(strs[i].split("=")[1]); } } return theRequest; } //保存订单信息 function saveOrder(){ var mode = ""; if($("#AliPay")[0].checked) { mode = "支付宝"; }else if($("#bankpay")[0].checked) { mode = "银联支付"; }else if($("#wxpay")[0].checked) { mode = "微信支付"; } var dishjson = data.dishJson; //订单表dishJson内容 var dishJson = (JSON.parse(dishjson))[0]; var oldcanpin = dishJson.canpin; var newcanpin = oldcanpin + ",支付方式:"+ mode; //拼接支付方式以后的菜品信息 //alert(newcanpin); dishJson["canpin"] = newcanpin; data["dishJson"] = JSON.stringify(dishJson); var url="/cyDishorder/saveCyDcOrders"; var successfull= function (datas) { var bizData=datas.bizData; pay_orderno = bizData.orderNum; //alert(pay_orderno); //var paymoney = $("#ydmoney").text().substring(1); if(datas.rtnCode == "0000000"){ pay(); //跳转支付方法 //alert("==============") } } ajaxPostFun(url, data, successfull,null, "保存餐厅预定订单"); } //跳转到支付页面 function pay() { if($("#AliPay")[0].checked) { location.href = server+"/AliPayDingCan/" + pay_orderno; }else if($("#bankpay")[0].checked) { location.href = server+"/CYChinapay/" + pay_orderno; }else if($("#wxpay")[0].checked){ //查询该用户是否微信授权 var url = server+"/Member/FindById/"+userid; var successFun = function (data) { if(empty(data.bizData.openid)){ //alert("微信未授权"); //如果该用户没有授权,则进行授权操作 wxshouquan(); }else{ //alert("微信已授权"); //用户已经授权,直接微信支付 wxzhifu(); } } ajaxPostFun(url, {}, successFun, null, "查询用户是否授权"); }else { layer.msg("请选择支付方式"); return false; } } //微信授权 function wxshouquan() { location.href = server + "/CyWechatPay/outh?userid=" + userid; } //微信支付 function wxzhifu(){ var url = server+"/CyWechatPay/unifiedorder"; ajaxPostFun(url, {userid:userid,orderno:pay_orderno}, function(res) { var _data = res.bizData; function onBridgeReady(){ WeixinJSBridge.invoke( 'getBrandWCPayRequest', { "appId":_data.appId, //公众号名称,由商户传入 "timeStamp":_data.timeStamp, //时间戳,自1970年以来的秒数 "nonceStr":_data.nonceStr, //随机串 "package" :_data.package, "signType":_data.signType, //微信签名方式: "paySign": _data.paySign //微信签名 }, function(ress){ alert( JSON.stringify(ress)); if(ress.err_msg == "get_brand_wcpay_request:ok" ) { layer.msg("支付成功!",{time:2000}); //window.location.href = "buyGoodsChenggong.html?orderid="+orderid; }else if(ress.err_msg == "get_brand_wcpay_request:cancel"){ layer.msg("支付取消!",{time:2000}); }else { layer.msg("未知错误!",{time:2000}); } // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回 ok,但并不保证它绝对可靠。 } ); } 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(); } }, null, "微信支付获取package包"); }

支付,退款,controller代码

@Controller
@RequestMapping(value = "/CyWechatPay")
public class CyWechatController { @Autowired private IMemberService memberService; @Autowired private IMobileCyDishorderService mobileCyDishorderService; @Autowired private IMobileMemberService mobileMemberService; @Autowired private IMobileServiceStyleService mobileServiceStyleService; private static Logger logger = Logger.getLogger(CyWechatController.class); private static String url_snsapi_base = "https://open.weixin.qq.com/connect/oauth2/authorize"; private static String url_access_token = "https://api.weixin.qq.com/sns/oauth2/access_token"; private static String url_unifiedorder = "https://api.mch.weixin.qq.com/pay/unifiedorder"; /** * 统一下单 * @return prepare_id 预付款id * @throws IOException * @throws JDOMException */ @ResponseBody @RequestMapping("/unifiedorder") public Map<String, Object> unifiedorder(@RequestParam(value = "orderno", required = true)String orderno, String userid,HttpServletRequest request) throws Exception { //根据订单号查询订单时间 CyDishorderEntity orderEntity = (CyDishorderEntity) mobileCyDishorderService.findOne("orderNum", orderno); //获得随机数 String nonce_str = WxTool.getRandomCode(32); //根据用户id查询用户信息 Map<String,Object> memberEntity =memberService.findByid("id",userid); String openid = memberEntity.get("openid").toString(); //生成xml,参数说明(说明,标识码,订单号,IP,价格,openid用户在微信端的唯一标示) String xml = this.getXmlData(orderEntity.getRemarks(), nonce_str, orderno, WxTool.getRemoteHost(request), new BigDecimal(orderEntity.getPayMoney()),openid); logger.info("生成的订单信息======>" + xml); //订单提交的微信地址(预支付地址) String result = HttpRequest.httpsRequest("https://api.mch.weixin.qq.com/pay/unifiedorder", "POST", xml); //告诉编译器忽略 unchecked 警告信息,如使用List,ArrayList等未进行参数化产生的警告信息。 @SuppressWarnings("unchecked") Map<String, Object> map2 = XMLUtil.doXMLParse(result); logger.info("统一下单接口返回的结果集======>" + map2); //return_code为微信返回的状态码,SUCCESS表示支付成功 //return_msg 如非空,为错误原因 签名失败 参数格式校验错误 if (map2.get("return_code").toString().equalsIgnoreCase("SUCCESS") && map2.get("result_code").toString().equalsIgnoreCase("SUCCESS")) { //预支付成功 logger.info("微信预支付成功!"); //map2.put("timestamp",Long.toString(System.currentTimeMillis() / 1000)); //传递map2给前台页面处理 Map<String, Object> map = new HashMap<String, Object>(); map.put("appId", map2.get("appid")); map.put("timeStamp", Long.toString(System.currentTimeMillis() / 1000)); map.put("nonceStr", map2.get("nonce_str")); map.put("package", "prepay_id=" + map2.get("prepay_id")); map.put("signType", "MD5"); map.put("paySign", WxTool.getSignUtil(map, ConfigUtil.APP_SECRECT)); logger.info("统一下------->" + map); return map; } else { //支付失败,进行相应的失败业务处理 logger.info("微信预支付失败!"); //传递null给页面处理 return null; } } /** * 申请退款 */ @RequestMapping("refund/{orderno}") @ResponseBody public String refund(@PathVariable String orderno) throws Exception { //根据订单号查询订单 CyDishorderEntity orderEntity = (CyDishorderEntity) mobileCyDishorderService.findOne("orderNum", orderno); //获得随机数 String nonce_str = WxTool.getRandomCode(32); StringBuilder sb2 = new StringBuilder(); //微信签名需要的参数 Map<String, Object> signMap = new HashMap<String, Object>(); signMap.put("appid",ConfigUtil.APPID);//应用APPID signMap.put("mch_id",ConfigUtil.MCH_ID);//微信支付商户号 signMap.put("nonce_str",nonce_str);//随机数 signMap.put("op_user_id",ConfigUtil.MCH_ID); signMap.put("out_trade_no",orderEntity.getOrderNum());//订单号out_refund_no signMap.put("out_refund_no",orderEntity.getRefund_queryid());//退款流水号 signMap.put("refund_fee",new BigDecimal(orderEntity.getPayMoney()).multiply(new BigDecimal(100)).intValue());//退款金额 signMap.put("total_fee",new BigDecimal(orderEntity.getPayMoney()).multiply(new BigDecimal(100)).intValue());//总金额 signMap.put("transaction_id","");//微信生成的订单号,在支付通知中有返回 //生成xml,微信要求的xml形式 StringBuffer xml =new StringBuffer(); xml.append("<xml>"); xml.append("<appid>"+ConfigUtil.APPID+"</appid>");//应用ID xml.append("<mch_id>"+ConfigUtil.MCH_ID+"</mch_id>");//微信支付分配的商户号 xml.append("<nonce_str>"+nonce_str+"</nonce_str>");//随机字符串,不长于32位。 xml.append("<op_user_id>"+ConfigUtil.MCH_ID+"</op_user_id>");//操作员,默认为商户号 xml.append("<out_refund_no>"+orderEntity.getRefund_queryid()+"</out_refund_no>");//商户退款单号,商户系统内部的退款单号,商户系统内部唯一 xml.append("<out_trade_no>"+orderEntity.getOrderNum()+"</out_trade_no>");//商户订单号 xml.append("<refund_fee>"+new BigDecimal(orderEntity.getPayMoney()).multiply(new BigDecimal(100)).intValue()+"</refund_fee>");//退款金额 xml.append("<total_fee>"+new BigDecimal(orderEntity.getPayMoney()).multiply(new BigDecimal(100)).intValue()+"</total_fee>");//订单金额 xml.append("<transaction_id>"+"</transaction_id>");//微信订单号,微信生成的订单号,在支付通知中有返回 xml.append("<sign>"+WxTool.getSignUtil(signMap, ConfigUtil.APP_SECRECT)+"</sign>");//签名 xml.append("</xml>"); logger.info("生成的申请退款信息=============================>" + xml.toString()); /** * JAVA使用证书文件 */ logger.info("加载证书开始=========================================》》》》》"); //指定读取证书格式为PKCS12 KeyStore keyStore = KeyStore.getInstance("PKCS12"); //读取本机存放的PKCS12证书文件 FileInputStream instream = new FileInputStream(new File("/home/smit/down/apiclient_cert.p12")); try { //指定PKCS12的密码(商户ID) keyStore.load(instream, ConfigUtil.MCH_ID.toCharArray()); } finally { instream.close(); } //ssl双向验证发送http请求报文 SSLContext sslcontext = null; sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, ConfigUtil.MCH_ID.toCharArray()).build(); SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1"}, null, SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build(); HttpPost httppost = new HttpPost("https://api.mch.weixin.qq.com/secapi/pay/refund"); StringEntity se = new StringEntity(xml.toString(), "UTF-8"); httppost.setEntity(se); //定义响应实例对象 CloseableHttpResponse responseEntry = null; String xmlStr2 = null;//读入响应流中字符串的引用 responseEntry = httpclient.execute(httppost);//发送请求 HttpEntity entity = responseEntry.getEntity();//获得响应实例对象 if (entity != null) {//读取响应流的内容 BufferedReader bufferedReader = null; bufferedReader = new BufferedReader(new InputStreamReader(entity.getContent(), "UTF-8")); while ((xmlStr2 = bufferedReader.readLine()) != null) { sb2.append(xmlStr2); } } Map<String, Object> map = XMLUtil.doXMLParse(sb2.toString()); logger.info("申请退款接口返回的结果集======>" + map); //return_code为微信返回的状态码,SUCCESS表示申请退款成功,return_msg 如非空,为错误原因 签名失败 参数格式校验错误 if (map.get("return_code").toString().equalsIgnoreCase("SUCCESS") && map.get("result_code").toString().equalsIgnoreCase("SUCCESS")) { logger.info("****************退款申请成功!**********************"); //修改订单状态为申请退款 orderEntity.setOrderStatus(CyOrderStatusEnum.REFUND_SUCCESS.getCode()); mobileCyDishorderService.update(orderEntity); return "SUCCESS"; } else { logger.info("*****************退款申请失败!*********************"); return "FAIL"; } } //生成xml方法 private static String getXmlData(String body, String nonce_str, String tradeNo, String ip, BigDecimal totla, String openid) throws Exception { Map<String, Object> signMap = new HashMap<String, Object>(); signMap.put("appid", ConfigUtil.APPID);//应用APPID signMap.put("nonce_str", nonce_str);//随机数 signMap.put("body", body);//商品描述 signMap.put("mch_id", ConfigUtil.MCH_ID);//微信支付商户号 signMap.put("notify_url", "http://域名/CyWechatPay/paysuccess.do");//异步通知回调地址 signMap.put("out_trade_no", tradeNo);//订单号 signMap.put("total_fee", totla.multiply(new BigDecimal(100)).intValue());//总金额 signMap.put("trade_type", "JSAPI");

转载于:https://www.cnblogs.com/guzhengtao/p/20180821_1118.html

微信公众号授权,支付,退款总结【shoucang】相关推荐

  1. vue开发项目微信公众号授权支付开发

    一.注册微信公众号服务号并填写企业信息(个人订阅号没有开发微信支付的权限) 链接: https://mp.weixin.qq.com/ 二.在微信公众号内进行微信认证(3-5个工作日) 三.在微信公众 ...

  2. 微信登录 sdk 服务器,微信登录(微信公众号授权)的开发(详解)——两三行代码的事,何必呢...

    新版重构的SDK已经开始在写了,具体的使用方法参考 新版重构的SDK已经开始在写了,具体的使用方法参考 新版重构的SDK已经开始在写了,具体的使用方法参考 新版重构的SDK已经开始在写了,具体的使用方 ...

  3. 微信公众号授权步骤详细步骤介绍和整合springboot开发(java版)

    文章有不当之处,欢迎指正,如果喜欢微信阅读,你也可以关注我的微信公众号:好好学java,获取优质学习资源. 一.微信公众号授权步骤 首先到微信公众平台注册账号,可以看到有四种类型(服务号,订阅号,小程 ...

  4. 微信登录(微信公众号授权)的开发(详解)

    1.UnionId和OpenId 微信登录最重要的两个返回信息,一个是UnionId,一个是OpenId.两者之间有着必然的联系. 首先,先来理一下微信开放平台的架构.开发微信登录,必须有一个开放平台 ...

  5. java微信公众号JSAPI支付以及所遇到的坑

    java微信公众号JSAPI支付以及所遇到的坑 上周做了个支付宝微信扫码支付,今天总结一下.微信相比支付宝要麻烦许多 由于涉及到代理商,没办法,让我写个详细的申请流程,懵逼啊. 笔记地址 http:/ ...

  6. vue微信公众号授权开发流程

    vue微信公众号授权开发流程 项目采用的是vue2.0开发的,还未更新到vue3.0.项目描述:只有通过微信授权登录,不需要绑定手机号. 1.注册公众号,这个不多说了 2.配置公众号 在[公众号设置] ...

  7. 微信公众号授权登录(asp.net + angular)

    微信是时下最火的,上面有数以亿计的用户,如果能接入微信将大大减低注册门槛,当然,接入微信登录是有门槛的.微信登录一般有两个,一个是微信开放平台授权登录,一个是微信公众号授权登录,两者都需要认证才可以继 ...

  8. vue移动端项目微信公众号授权登录

    前言 在我们做移动端项目时, 很多功能是以登录后才能进行后续的操作, 并且许多pc端的网页都有微信扫码登录功能, 为了做到pc与移动端统一, 往往移动端项目需要添加微信登录功能, 那么为什么手机端不能 ...

  9. Java在Web端微信公众号授权登录

    Java在Web端微信公众号授权登录 1.需要在微信开发平台配置 url:是自己服务中的微信需要推给你的地址(需要使用二级域名,可以去添加链接描述)购买9块钱1个月使用权或者白嫖都可 token 这个 ...

  10. uniapp中h5网页微信公众号授权

    uniapp微信网页授权 uniapp中h5网页微信公众号授权 主要代码 获取code返回的code截取代码 uniapp中h5网页微信公众号授权 微信官方文档–>网页授权 uniapp中h5网 ...

最新文章

  1. 冇内容管理系统分析-[JS]详尽解析window.event对象
  2. Leetcode周赛5827. 检查操作是否合法
  3. mysql+添加乱码_rsyslog+loganalyzer+mysql的IP添加与中文乱码解决
  4. matlab2c使用c++实现matlab函数系列教程-sort函数
  5. 开启MyBatis(一)
  6. toma线攻略_AMNESIA WORLD 手打攻略【SCHOOL WORLD part】アムネシア
  7. CAS单点登录及处理流程介绍(一)
  8. 在线翻译转换器对接百度翻译、有道词典和谷歌翻译api
  9. 下三角99乘法表 C语言
  10. android 的hook技术,Android Native Hook技术(一)
  11. Xiaojie雷达之路---脉冲压缩
  12. win 7旗舰版开机提示 :explorer.exe-无法找到入口
  13. itune音乐排行榜:法国周榜TOP100(2022年7月16日)
  14. 小程序向webview传参_h5与小程序互相跳转,传参和获取参数
  15. scikit-learn:4.3. Preprocessing data(standardi/normali/binari..zation、encoding、missing value)
  16. 强大的实用的mac软件卸载应用软件,彻底清除App残留
  17. Day 41多表查询以及pymysql相关操作 完善
  18. plc中int数据类型范围_PLC数据类型INT 和WORD的区别点-工业支持中心-西门子中国...
  19. [绍棠_Swift] SwiftyJSON的使用详解(附样例,用于JSON数据处理)
  20. cyk的小学数学题 小学数学

热门文章

  1. lilo是什么意思_lilo是什么意思_lilo的用法_lilo造句_趣词词典
  2. Vue组件中关于@click.native.prevent事件
  3. Launcher folder、foldericon
  4. MySQL中文存到数据库是,springMVC保存数据到mysql数据库中文乱码问题解决方法
  5. Bytes和bits的区别(字节和位的区别)
  6. 计算机组成原理与体系结构——随机存储器和只读存储器
  7. Alphabetic Removals详解(特殊算法巧解)
  8. 使用QT5+Opencv完成简单的图像处理及视频处理软件
  9. 仿真BPSK调制在AWGN信道下分别使用卷积码和未使用卷积码的性能对比,其中,卷积码的约束长度为7,生成多项式为[171,133],码率为1/2,译码分别采用硬判决译码和软判决译码
  10. C++字母大小写转换