微信公众号开发-公众号支付

导语:

通过这几天的对微信公众号支付的学习,我知道了想要完成在微信内置浏览器访问第三方网站进行支付或者其他操作都首先要进行获取网页授权的操作,也就是要获取用户的openid,只有有了openid我们才有权限进行接下来的操作,我现在就来安步奏详细说明一下其中的流程和坑。

一、获取openid

根据官方文档,我们可以得到以下步奏:

1 第一步:用户同意授权,获取code

首先我们需要得到APPID和用户需要跳转的地址returnUrl
然后根据这两个信息组成一个新的url进行跳转

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {String backUrl = "http://ynfywtq.hk1.mofasuidao.cn/Weixin/callBack";//第一步:用户同意授权,获取code,会重定向到backUrlString url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid="+WeixinUtil.APPID+ "&redirect_uri="+URLEncoder.encode(backUrl)+ "&response_type=code"+ "&scope=snsapi_userinfo"+ "&state=STATE#wechat_redirect";response.sendRedirect(url);}

当跳转到这个url后,微信内置的浏览器就会解析这段代码,他会判断你是不是用微信浏览器进行的访问,如果不是就会显示必须用微信客户端登录,如果是客户端并且你的scope=snsapi_userinfo就会弹出一个授权页面让用户授权,授权之后页面将跳转至redirect_uri/?code=CODE&state=STATE

如果出现redirect_url参数错误,是因为在微信公众平台没有配置好,一定要把地址写上后,测试能不能访问到那个txt文件

2 第二步:通过code换取网页授权access_token和openid

获取code后,请求以下链接获取access_token: https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
通过code换取网页授权access_token

3 第三步:刷新access_token(如果需要)

4 第四步:拉取用户信息(需scope为 snsapi_userinfo)

通过access_token和openid拉取用户信息,如果为base请求的url会不同

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//接收codeString code = request.getParameter("code");//第二步:通过code换取网页授权access_tokenString url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid="+WeixinUtil.APPID+ "&secret="+WeixinUtil.APPSECRET+ "&code="+ code+ "&grant_type=authorization_code";//它会返回一个json数据包JSONObject jsonObject = WeixinUtil.doGetStr(url);String openid = jsonObject.getString("openid");String token = jsonObject.getString("access_token");//第三步:刷新access_token(如果需要)//第四步:拉取用户信息(需scope为 snsapi_userinfo)String infoUrl = "https://api.weixin.qq.com/sns/userinfo?access_token="+token+ "&openid="+openid+ "&lang=zh_CN";JSONObject userInfo = WeixinUtil.doGetStr(infoUrl);//1、使用微信用户信息直接登录,无需注册和绑定//System.out.println(userInfo);request.setAttribute("info", userInfo);request.getRequestDispatcher("/index1.jsp").forward(request, response);//2、有自己的账号体系就要再数据库事先创建好表单,然后与微信信息进行绑定}

5 附:检验授权凭证(access_token)是否有效

(对比)使用第三方SDK来简化完成获取openid解析:

这里我们使用的是:

<!--微信公众号的第三方SDK--><dependency><groupId>com.github.binarywang</groupId><artifactId>weixin-java-mp</artifactId><version>2.7.0</version></dependency>

首先我们需要配置基本信息

@Data
@Component//是一个泛化的概念,仅仅表示一个组件 (Bean) ,可以作用在任何层次。
/*(把普通pojo实例化到spring容器中,相当于配置文件中的<bean id="" class=""/>)
* 用这个注解注册之后就可以用@Autowired来进行调用了
* */@ConfigurationProperties(prefix = "wechat")//这是调用配置文件
public class WechatAccountConfig {private String mpAppId;private String mpAppSecret;//商户号private String mchId;//商户秘钥private String mchKey;//商户证书路径private String keyPath;//微信支付异步通知地址private String notifyUrl;
}

然后把这些信息注册到service里面

@Component
public class WechatMpConfig {@Autowiredprivate WechatAccountConfig accountConfig;@Beanpublic WxMpService wxMpService(){WxMpService wxMpService = new WxMpServiceImpl();wxMpService.setWxMpConfigStorage(wxMpConfigStorage());//这是根据官方文档知道的设置方法,设置了之后wxMpService就有了配置文件return wxMpService;}@Beanpublic WxMpConfigStorage wxMpConfigStorage(){WxMpInMemoryConfigStorage wxMpInMemoryConfigStorage = new WxMpInMemoryConfigStorage();wxMpInMemoryConfigStorage.setAppId(accountConfig.getMpAppId());wxMpInMemoryConfigStorage.setSecret(accountConfig.getMpAppSecret());return wxMpInMemoryConfigStorage;}
}

然后就是在controller里面写具体的url跳转代码,通过下面两个方法
authorize可以实现普通方法中的第一二步
userInfo可以完成通过code获取token和openid了

@Controller
@RequestMapping("/wechat")
@Slf4j
public class WechatController {@Autowiredprivate WxMpService wxMpService;//这也是点击项目首页后第一个调用的方法,获取openid@GetMapping("/authorize")public String authorize(@RequestParam("returnUrl") String returnUrl){//1.配置//2.调用方法String url = "http://ynfywtq.hk1.mofasuidao.cn/sell/wechat/userInfo";//这里是根据配置的方法去重定向到下面一个方法得到返回值,这里主要是要为了获取codeString redirectUrl = wxMpService.oauth2buildAuthorizationUrl(url, WxConsts.OAUTH2_SCOPE_BASE, URLEncoder.encode(returnUrl));//log.info("【微信网页授权】获取code,result={}", redirectUrl);//重定向必须要加"redirect:"拼接,否则要像ssm一样配置好return "redirect:" + redirectUrl;}//这里得到code和目标地址,如果不是用微信客户顿打开的话就会重定向到另一个地址,叫你用客户端打开@GetMapping("/userInfo")public String userInfo(@RequestParam("code") String code,@RequestParam("state") String returnUrl){WxMpOAuth2AccessToken wxMpOAuth2AccessToken = new WxMpOAuth2AccessToken();try {wxMpOAuth2AccessToken = wxMpService.oauth2getAccessToken(code);} catch (WxErrorException e) {log.error("【微信网页授权】{}", e);throw new SellException(ResultEnum.WECHAT_MP_ERROR.getCode(), e.getError().getErrorMsg());}//我们是为了网页支付,而网页支付主要是要openidString openId = wxMpOAuth2AccessToken.getOpenId();return "redirect:" + returnUrl +"?openid=" + openId;}
}

二、创建订单发起支付

由于发起支付首先是需要一个订单的,所以我们要根据需求首先完成创建订单的操作,订单信息里面就会包含用户的openid的信息,之后我们在发起支付就可以通过订单里面openid来进行支付的操作
而支付这里我只用了第三方SDK完成:

<!--廖老师自己写的支付SDK--><dependency><groupId>cn.springboot</groupId><artifactId>best-pay-sdk</artifactId><version>1.1.0</version></dependency>

第一步:发起支付(是在订单已经被创建好的前提下)

第二部:异步判断支付状态

传入参数为orderid和returnUrl(支付成功后的回调地址)

//只需要接受orderid和回调地址@GetMapping("/create")public ModelAndView create(@RequestParam("orderId") String orderId,@RequestParam("returnUrl") String returnUrl,Map<String, Object> map) {//1. 根据传过来的orderid查询订单OrderDTO orderDTO = orderService.findOne(orderId);if(orderDTO == null){throw new SellException(ResultEnum.ORDER_NOT_EXIST);}//2. 发起支付PayResponse payResponse = payService.create(orderDTO);map.put("payResponse", payResponse);map.put("returnUrl", returnUrl);//返回到WeixinJSBridge内置对象在其他浏览器中无效return new ModelAndView("pay/create", map);}

具体发起操作

@Overridepublic PayResponse create(OrderDTO orderDTO) {PayRequest payRequest = new PayRequest();//发起支付需要传一些参数payRequest.setOpenid(orderDTO.getBuyerOpenid());//用户openidpayRequest.setOrderAmount(orderDTO.getOrderAmount().doubleValue());//订单总金额payRequest.setOrderId(orderDTO.getOrderId());//订单orderidpayRequest.setOrderName(ORDER_NAME);//订单名字,自己随便起payRequest.setPayTypeEnum(BestPayTypeEnum.WXPAY_H5);//支付方式log.info("【微信支付】发起支付,request={}", JsonUtil.toJson(payRequest));PayResponse payResponse = bestPayService.pay(payRequest);//根据传参得到预付支付的参数log.info("【微信支付】发起支付生成预付信息,response={}", JsonUtil.toJson(payResponse));return payResponse;}
2017-08-26 19:35:27.679  INFO 12268 --- [nio-8080-exec-1] com.akk.service.impl.PayServiceImpl      : 【微信支付】发起支付,request={"payTypeEnum": "WXPAY_H5","orderId": "1503747327509255028","orderAmount": 0.01,"orderName": "微信点餐订单","openid": "oopqG1kXTv-S_NsiOlwFGjyofJZg"
}
2017-08-26 19:35:28.298  INFO 12268 --- [nio-8080-exec-1] com.akk.service.impl.PayServiceImpl      : 【微信支付】发起支付生成预付信息,response={"appId": "wx8b20c44179a091b4","timeStamp": "1503747328","nonceStr": "5utjFpjTlD5Tjxbn","packAge": "prepay_id\u003dwx20170826193526270d20a17b0967136475","signType": "MD5","paySign": "6C770BCB3057365BF76A56652C8BDBA8"
}

有了预付信息后按照微信官方文档的说法就是
生成JSAPI页面调用的支付参数并签名
而我发现我们这里的步奏似乎和官方的步奏不太一样
官方是
5.先同一调用下单API,生成预付单 6.生成JSAPI页面调用的支付参数并签名
7.用户点击支付8.微信支付系统验证参数的合法性和授权域权限

而这里的第三方SDK再用户点击支付后直接产生预付信息在生成JSAPI页面调用的支付参数并签名,就没有了统一下单的调用减少了一层逻辑
否则按照官方的标准,用户操作应该是
1.下单(产生预付单)
2.用户确认后点击支付
然后回到
9.用户输入密码的界面,之后系统会像微信支付系统验证授权 10.再异步通知商户后台支付结果(是否成功)(通过配置的notifyUrl来通知商户系统支付结果,然后商户在做进一步判断,防止中间被黑)

@Overridepublic PayResponse notify(String notifyData) {//1. 验证签名//2. 支付状态//3. 支付金额//4. 支付人(下单人 == 支付人)比如有代付,有的必须本人支付//前两步SDK已经做了PayResponse payResponse = bestPayService.asyncNotify(notifyData);log.info("【微信支付】异步通知,payResponse={}", JsonUtil.toJson(payResponse));//查询订单OrderDTO orderDTO = orderService.findOne(payResponse.getOrderId());//判断订单是否存在if(orderDTO == null) {log.info("【微信支付】异步通知,订单不存在,orderId={}", payResponse.getOrderId());throw new SellException(ResultEnum.ORDER_NOT_EXIST);}//判断金额是否一致,由于这里两个变量的类型不一致,数据库用的BigDecimal而SDK用的double,所以要做转换//但是转换类型比较还是不行,因为转换后小数点后面会出现奇怪的数字,所以要用相减判断if(!MathUtil.equals(payResponse.getOrderAmount(), orderDTO.getOrderAmount().doubleValue())) {log.info("【微信支付】异步通知,订单金额不一致,orderId={},微信通知金额={},系统金额={}",payResponse.getOrderId(),payResponse.getOrderAmount(),orderDTO.getOrderAmount());throw new SellException(ResultEnum.WXPAY_NOTIFY_MONEY_VERIFY_ERROR);}//修改订单支付状态orderService.paid(orderDTO);return payResponse;}

11.返回成功结果给微信支付系统
12.返回支付结果并发消息给用户。完成支付

基本上按照第三方SDK的操作进行编写就不会出错

三、订单的取消

当交易发生之后一段时间内,由于买家或者卖家的原因需要退款时,卖家可以通过退款接口将支付款退还给买家,微信支付将在收到退款请求并且验证成功之后,按照退款规则将支付款按原路退到买家帐号上。
我这里只进行了退款操作,没有写判断的逻辑

@Overridepublic RefundResponse refund(OrderDTO orderDTO) {RefundRequest refundRequest = new RefundRequest();refundRequest.setOrderId(orderDTO.getOrderId());refundRequest.setOrderAmount(orderDTO.getOrderAmount().doubleValue());refundRequest.setPayTypeEnum(BestPayTypeEnum.WXPAY_H5);log.info("【微信退款】request={}", JsonUtil.toJson(refundRequest));RefundResponse refundResponse = bestPayService.refund(refundRequest);log.info("【微信退款】response={}", JsonUtil.toJson(refundResponse));return refundResponse;}

总结:

用第三方SDK完成操作的确很简单,但是我们还要能明白其中的详细原理最好,最好的方式是自己先完成一遍不用SDK的普通调,在去用别人的包的时候就不会一头雾水了。
这样在写属于自己特定的逻辑的时候就很清楚该怎样操作了。
比如统一下单和确定支付是否分开写。

微信公众号开发-公众号支付相关推荐

  1. 微信三方代开发公众号消息管理-群发消息

    三方代开发公众号消息管理-群发消息 微信公众平台为订阅号提供了每天1条的群发权限,为服务号提供每月(自然月)4条的群发权限.而对于某些具备开发能力的公众号运营者,可以通过高级群发接口,实现更灵活的群发 ...

  2. 微信公众号开发--服务号

    前言 因公司需要开发一款手机打卡程序,本人没有安卓APP开发经验,所以决定将写个服务号的公众号,集外出打卡,打卡查询等功能; 一,开发前测试帐号申请 以下是官方给出的建议,大家可以多参考参考 1)如果 ...

  3. 微信公众号开发--公众号关注推送配置与菜单配置

    用户关注公众号回调 AppID:开发者ID,微信公众号的唯一标识 AppSecret:开发者密码,操作微信公众号的验证 IP白名单:获取access_token时,需要IP白名单才可以获取 OpenI ...

  4. 微信公众号开发 公众号接口开发 封装统一的GET/POST请求接口

    10万+IT人都在关注,史上最全面的微信开发实战教程:包含公众号,小程序,微信支付等开发案例 欢迎关注笔者个人博客:http://blogs.chenyunkeji.com/ 在微信公众号/小程序开发 ...

  5. c#实现微信公众号开发--服务号通过oauth2获取用户信息

    2018年春节策划了一个"带现金红包的贺年卡"微信号推广活动,先说下效果:2个小时实现新增关注用户4万多户,活动页面PV达到16.8万,后因红包预算费用原因结束活动. 实现原理:每 ...

  6. 微信公众平台开发订阅号

    忙活着搭建了ecplise和tomcat,申请了sae服务器,最后回过头来才发现微信订阅号的开发,如果仅仅只是每天发布一些文章,根本不需要去开通开发者帐号,只需要在注册成功后,就可以去群发里面编辑文章 ...

  7. 微信公众号开发-公众号被动回复用户消息

    专栏简介

  8. 笔记-微信订阅号开发

    目录 笔记-微信订阅号开发流程 创建菜单 网页授权: js-sdk 笔记-微信订阅号开发流程 创建菜单 先进入:https://mp.weixin.qq.com/ 创建一个小程序订阅号: 订阅号和服务 ...

  9. 用Sunny_ngrok免费地址映射工具解决微信公众平台开发本地测试问题

    问题: 1.微信公众平台开发如何进行本地测试? 2.微信公众平台目前只支持80和433端口如何解决? 3.如何解决外网访问内网? 案例场景: 在微信公众平台开发服务号,开发新功能,想本地起服务进行测试 ...

  10. java微信公众号支付开发平台_Java微信公众平台开发之公众号支付(微信内H5调起支付)...

    官方文档 准备工作:已通过微信认证的公众号,必须通过ICP备案域名(否则会报支付失败) 借鉴了很多大神的文章,在此先谢过了 整个支付流程,看懂就很好写了 一.设置支付目录 在微信公众平台设置您的公众号 ...

最新文章

  1. aida64副屏监控_“遥信”在电力监控系统中的重要作用
  2. 哪种营销方法效果最差_网络营销推广中如何监控评测网络效果?
  3. Weblogic部署项目过程中的一些问题
  4. mysql web备份软件_Windows下实现MySQL自动备份的批处理(复制目录或mysqldump备份)
  5. opencv 图像分割 阈值分割 图像二值化 灰度图
  6. md文件编辑器_File Cabinet Pro for Mac(菜单栏文件管理器)
  7. mysql使用 CONCAT(字段,字段) 函数拼接
  8. jquery验证表单很简单的方法
  9. Intel超线程技术 Hyper-Threading Technology (3) - 处理器资源与超线程(复制的资源)
  10. Spring Cloud Eureka(一)搭建一个注册中心
  11. 20分钟充满!华为P50系列或最高支持100W超级快充
  12. Redis的哨兵(sentinel)模式
  13. error c2678解决方法
  14. matlab导数曲线怎样画,matlab三次样条曲线的绘制(spline和csape函数详解)
  15. GIMP 快速入门(2)
  16. 1. Emacs使用本地elpa镜像
  17. 【Linux】Alibaba Cloud Linux 3 中第二硬盘、MySQL8.*、MySQL7.*、Redis、Nginx、Java 系统安装
  18. cisco路由器忘记密码恢复
  19. maven指定本地仓库
  20. 程序员笑话集:bug跟蚊子的相似之处

热门文章

  1. Win7下64位扫雷逆向以及辅助制作
  2. AMOS分析技术:路径分析的非递归模型
  3. 维修电工技师、高级技师技能实训考核装置
  4. 大学计算机绘图实训报告,CAD制图实训心得体会范文4篇
  5. jsp 默认select option 默认选中方法
  6. ubuntu18.04 端口转发工具 Rinetd
  7. matlab实现三角追赶法,矩阵三角分解的追赶法在MATLAB的实现
  8. 《OpenCV3编程入门》毛星云编著
  9. WinCE 5.0下的鼠标键盘驱动分析
  10. 华为的薪酬体系整体框架,值得收藏