小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

微信公眾號支付功能開發(fā)

 Levy_X 2017-10-12
導語:
  通過這幾天的對微信公眾號支付的學習,我知道了想要完成在微信內(nèi)置瀏覽器訪問第三方網(wǎng)站進行支付或者其他操作都首先要進行獲取網(wǎng)頁授權(quán)的操作,也就是要獲取用戶的openid,只有有了openid我們才有權(quán)限進行接下來的操作,我現(xiàn)在就來安步奏詳細說明一下其中的流程和坑。
  一、獲取openid
  根據(jù)官方文檔,我們可以得到以下步奏:
  1 第一步:用戶同意授權(quán),獲取code
  首先我們需要得到APPID和用戶需要跳轉(zhuǎn)的地址returnUrl
  然后根據(jù)這兩個信息組成一個新的url進行跳轉(zhuǎn)
  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String backUrl = 'http://ynfywtq.hk1./Weixin/callBack'; //第一步:用戶同意授權(quán),獲取code,會重定向到backUrl String 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); }1
  2
  3
  4
  5
  6
  7
  8
  9
  10
  當跳轉(zhuǎn)到這個url后,微信內(nèi)置的瀏覽器就會解析這段代碼,他會判斷你是不是用微信瀏覽器進行的訪問,如果不是就會顯示必須用微信客戶端登錄,如果是客戶端并且你的scope=snsapi_userinfo就會彈出一個授權(quán)頁面讓用戶授權(quán),授權(quán)之后頁面將跳轉(zhuǎn)至redirect_uri/?code=CODE&state=STATE
  如果出現(xiàn)redirect_url參數(shù)錯誤,是因為在微信公眾平臺沒有配置好,一定要把地址寫上后,測試能不能訪問到那個txt文件
  2 第二步:通過code換取網(wǎng)頁授權(quán)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換取網(wǎng)頁授權(quán)access_token
  3 第三步:刷新access_token(如果需要)
  4 第四步:拉取用戶信息(需scope為 snsapi_userinfo)
  通過access_token和openid拉取用戶信息,如果為base請求的url會不同
  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //接收code String code = request.getParameter('code'); //第二步:通過code換取網(wǎng)頁授權(quán)access_token String url = 'https://api.weixin.qq.com/sns/oauth2/access_token?appid=' WeixinUtil.APPID '&secret=' WeixinUtil.APPSECRET '&code=' code '&grant_type=authorization_code'; //它會返回一個json數(shù)據(jù)包 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、有自己的賬號體系就要再數(shù)據(jù)庫事先創(chuàng)建好表單,然后與微信信息進行綁定 }1
  2
  3
  4
  5
  6
  7
  8
  9
  10
  11
  12
  13
  14
  15
  16
  17
  18
  19
  20
  21
  22
  23
  24
  5 附:檢驗授權(quán)憑證(access_token)是否有效
  (對比)使用第三方SDK來簡化完成獲取openid解析:
  這里我們使用的是:
   com.github.binarywang weixin-java-mp 2.7.0 1
  2
  3
  4
  5
  6
  首先我們需要配置基本信息
  @Data@Component//是一個泛化的概念,僅僅表示一個組件 (Bean) ,可以作用在任何層次。/*(把普通pojo實例化到spring容器中,相當于配置文件中的)* 用這個注解注冊之后就可以用@Autowired來進行調(diào)用了* */@ConfigurationProperties(prefix = 'wechat')//這是調(diào)用配置文件public class WechatAccountConfig { private String mpAppId; private String mpAppSecret; //商戶號 private String mchId; //商戶秘鑰 private String mchKey; //商戶證書路徑 private String keyPath; //微信支付異步通知地址 private String notifyUrl;}1
  2
  3
  4
  5
  6
  7
  8
  9
  10
  11
  12
  13
  14
  15
  16
  17
  18
  19
  20
  21
  然后把這些信息注冊到service里面
  @Componentpublic class WechatMpConfig { @Autowired private WechatAccountConfig accountConfig; @Bean public WxMpService wxMpService(){ WxMpService wxMpService = new WxMpServiceImpl(); wxMpService.setWxMpConfigStorage(wxMpConfigStorage());//這是根據(jù)官方文檔知道的設(shè)置方法,設(shè)置了之后wxMpService就有了配置文件 return wxMpService; } @Bean public WxMpConfigStorage wxMpConfigStorage(){ WxMpInMemoryConfigStorage wxMpInMemoryConfigStorage = new WxMpInMemoryConfigStorage(); wxMpInMemoryConfigStorage.setAppId(accountConfig.getMpAppId()); wxMpInMemoryConfigStorage.setSecret(accountConfig.getMpAppSecret()); return wxMpInMemoryConfigStorage; }}1
  2
  3
  4
  5
  6
  7
  8
  9
  10
  11
  12
  13
  14
  15
  16
  17
  18
  19
  20
  21
  然后就是在controller里面寫具體的url跳轉(zhuǎn)代碼,通過下面兩個方法
  authorize可以實現(xiàn)普通方法中的第一二步
  userInfo可以完成通過code獲取token和openid了
  @Controller@RequestMapping('/wechat')@Slf4jpublic class WechatController { @Autowired private WxMpService wxMpService; //這也是點擊項目首頁后第一個調(diào)用的方法,獲取openid @GetMapping('/authorize') public String authorize(@RequestParam('returnUrl') String returnUrl){ //1.配置 //2.調(diào)用方法 String url = 'http://ynfywtq.hk1./sell/wechat/userInfo'; //這里是根據(jù)配置的方法去重定向到下面一個方法得到返回值,這里主要是要為了獲取code String redirectUrl = wxMpService.oauth2buildAuthorizationUrl(url, WxConsts.OAUTH2_SCOPE_BASE, URLEncoder.encode(returnUrl)); //log.info('【微信網(wǎng)頁授權(quán)】獲取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('【微信網(wǎng)頁授權(quán)】{}', e); throw new SellException(ResultEnum.WECHAT_MP_ERROR.getCode(), e.getError().getErrorMsg()); } //我們是為了網(wǎng)頁支付,而網(wǎng)頁支付主要是要openid String openId = wxMpOAuth2AccessToken.getOpenId(); return 'redirect:' returnUrl '?openid=' openId; }}1
  2
  3
  4
  5
  6
  7
  8
  9
  10
  11
  12
  13
  14
  15
  16
  17
  18
  19
  20
  21
  22
  23
  24
  25
  26
  27
  28
  29
  30
  31
  32
  33
  34
  35
  36
  37
  二、創(chuàng)建訂單發(fā)起支付
  由于發(fā)起支付首先是需要一個訂單的,所以我們要根據(jù)需求首先完成創(chuàng)建訂單的操作,訂單信息里面就會包含用戶的openid的信息,之后我們在發(fā)起支付就可以通過訂單里面openid來進行支付的操作
  而支付這里我只用了第三方SDK完成:
   cn.springboot best-pay-sdk 1.1.0 1
  2
  3
  4
  5
  6
  第一步:發(fā)起支付(是在訂單已經(jīng)被創(chuàng)建好的前提下)
  第二部:異步判斷支付狀態(tài)
  傳入?yún)?shù)為orderid和returnUrl(支付成功后的回調(diào)地址)
  //只需要接受orderid和回調(diào)地址 @GetMapping('/create') public ModelAndView create(@RequestParam('orderId') String orderId, @RequestParam('returnUrl') String returnUrl, Map map) { //1. 根據(jù)傳過來的orderid查詢訂單 OrderDTO orderDTO = orderService.findOne(orderId); if(orderDTO == null){ throw new SellException(ResultEnum.ORDER_NOT_EXIST); } //2. 發(fā)起支付 PayResponse payResponse = payService.create(orderDTO); map.put('payResponse', payResponse); map.put('returnUrl', returnUrl); //返回到WeixinJSBridge內(nèi)置對象在其他瀏覽器中無效 return new ModelAndView('pay/create', map); }1
  2
  3
  4
  5
  6
  7
  8
  9
  10
  11
  12
  13
  14
  15
  16
  17
  18
  19
  具體發(fā)起操作
  @Override public PayResponse create(OrderDTO orderDTO) { PayRequest payRequest = new PayRequest(); //發(fā)起支付需要傳一些參數(shù) payRequest.setOpenid(orderDTO.getBuyerOpenid());//用戶openid payRequest.setOrderAmount(orderDTO.getOrderAmount().doubleValue());//訂單總金額 payRequest.setOrderId(orderDTO.getOrderId());//訂單orderid payRequest.setOrderName(ORDER_NAME);//訂單名字,自己隨便起 payRequest.setPayTypeEnum(BestPayTypeEnum.WXPAY_H5);//支付方式 log.info('【微信支付】發(fā)起支付,request={}', JsonUtil.toJson(payRequest)); PayResponse payResponse = bestPayService.pay(payRequest);//根據(jù)傳參得到預付支付的參數(shù) log.info('【微信支付】發(fā)起支付生成預付信息,response={}', JsonUtil.toJson(payResponse)); return payResponse; }1
  2
  3
  4
  5
  6
  7
  8
  9
  10
  11
  12
  13
  14
  2017-08-26 19:35:27.679 INFO 12268 --- [nio-8080-exec-1] com.akk.service.impl.PayServiceImpl : 【微信支付】發(fā)起支付,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 : 【微信支付】發(fā)起支付生成預付信息,response={ 'appId': 'wx8b20c44179a091b4', 'timeStamp': '1503747328', 'nonceStr': '5utjFpjTlD5Tjxbn', 'packAge': 'prepay_id\u003dwx20170826193526270d20a17b0967136475', 'signType': 'MD5', 'paySign': '6C770BCB3057365BF76A56652C8BDBA8'}1
  2
  3
  4
  5
  6
  7
  8
  9
  10
  11
  12
  13
  14
  15
  16
  有了預付信息后按照微信官方文檔的說法就是
  生成JSAPI頁面調(diào)用的支付參數(shù)并簽名
  而我發(fā)現(xiàn)我們這里的步奏似乎和官方的步奏不太一樣
  官方是
  5.先同一調(diào)用下單API,生成預付單 6.生成JSAPI頁面調(diào)用的支付參數(shù)并簽名
  7.用戶點擊支付8.微信支付系統(tǒng)驗證參數(shù)的合法性和授權(quán)域權(quán)限
  而這里的第三方SDK再用戶點擊支付后直接產(chǎn)生預付信息在生成JSAPI頁面調(diào)用的支付參數(shù)并簽名,就沒有了統(tǒng)一下單的調(diào)用減少了一層邏輯
  否則按照官方的標準,用戶操作應該是
  1.下單(產(chǎn)生預付單)
  2.用戶確認后點擊支付
  然后回到
  9.用戶輸入密碼的界面,之后系統(tǒng)會像微信支付系統(tǒng)驗證授權(quán) 10.再異步通知商戶后臺支付結(jié)果(是否成功)(通過配置的notifyUrl來通知商戶系統(tǒng)支付結(jié)果,然后商戶在做進一步判斷,防止中間被黑)
  @Override public PayResponse notify(String notifyData) { //1. 驗證簽名 //2. 支付狀態(tài) //3. 支付金額 //4. 支付人(下單人 == 支付人)比如有代付,有的必須本人支付 //前兩步SDK已經(jīng)做了 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); } //判斷金額是否一致,由于這里兩個變量的類型不一致,數(shù)據(jù)庫用的BigDecimal而SDK用的double,所以要做轉(zhuǎn)換 //但是轉(zhuǎn)換類型比較還是不行,因為轉(zhuǎn)換后小數(shù)點后面會出現(xiàn)奇怪的數(shù)字,所以要用相減判斷 if(!MathUtil.equals(payResponse.getOrderAmount(), orderDTO.getOrderAmount().doubleValue())) { log.info('【微信支付】異步通知,訂單金額不一致,orderId={},微信通知金額={},系統(tǒng)金額={}', payResponse.getOrderId(), payResponse.getOrderAmount(), orderDTO.getOrderAmount()); throw new SellException(ResultEnum.WXPAY_NOTIFY_MONEY_VERIFY_ERROR); } //修改訂單支付狀態(tài) orderService.paid(orderDTO); return payResponse; }1
  2
  3
  4
  5
  6
  7
  8
  9
  10
  11
  12
  13
  14
  15
  16
  17
  18
  19
  20
  21
  22
  23
  24
  25
  26
  27
  28
  29
  30
  31
  32
  33
  11.返回成功結(jié)果給微信支付系統(tǒng)
  12.返回支付結(jié)果并發(fā)消息給用戶。完成支付
  基本上按照第三方SDK的操作進行編寫就不會出錯
  三、訂單的取消
  當交易發(fā)生之后一段時間內(nèi),由于買家或者賣家的原因需要退款時,賣家可以通過退款接口將支付款退還給買家,微信支付將在收到退款請求并且驗證成功之后,按照退款規(guī)則將支付款按原路退到買家賬號上。
  我這里只進行了退款操作,沒有寫判斷的邏輯
  @Override public 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; }1
  2
  3
  4
  5
  6
  7
  8
  9
  10
  11
  12
  13
  總結(jié):
  用第三方SDK完成操作的確很簡單,但是我們還要能明白其中的詳細原理最好,最好的方式是自己先完成一遍不用SDK的普通調(diào),在去用別人的包的時候就不會一頭霧水了。
  這樣在寫屬于自己特定的邏輯的時候就很清楚該怎樣操作了。
  比如統(tǒng)一下單和確定支付是否分開寫。

    本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多