1. 程式人生 > 實用技巧 >SpringBoot微信點餐專案(二)——微信內調起支付寶支付

SpringBoot微信點餐專案(二)——微信內調起支付寶支付

iwehdio的部落格園:https://www.cnblogs.com/iwehdio/

1、微信部分

  • 微信支付文件:【微信支付】JSAPI支付開發者文件

  • 微信公眾賬戶測試號獲取:公眾號測試號

  • 使用natapp內網穿透:基於springboot接入微信公眾號(內網穿透技術)。以下捱打url中,帶有http://xxxx.natappfree.cc格式的均為請求到的不同的穿透域名。

  • 此外,還需要設定體驗介面許可權表下的網頁賬號的授權回撥頁面域名(不能填帶/的路徑)。

  • 獲取openID:手工方式和SDK方式。

    • 請求:

      重定向到 /sell/wechat/authorize
      
    • 引數:

      returnUrl: http://xxx.com/abc  //【必填】
      
    • 返回:

      http://xxx.com/abc?openid=oZxSYw5ldcxv6H0EU67GgSXOUrVg
      
  • 網頁回撥手動獲取openID:微信網頁授權

    • 第一步:使用者同意授權,獲取code

      • 請求使用者授權,訪問:

        https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
        
        ----
        
        https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=http://hpqv53.natappfree.cc/sell/auth&response_type=code&scope=snsapi_base&state=123#wechat_redirect
        
      • 具體引數含義:

      引數 是否必須 說明
      appid 公眾號的唯一標識
      redirect_uri 授權後重定向的回撥連結地址, 請使用 urlEncode 對連結進行處理
      response_type 返回型別,請填寫code
      scope 應用授權作用域,snsapi_base (不彈出授權頁面,直接跳轉,只能獲取使用者openid),snsapi_userinfo (彈出授權頁面,可通過openid拿到暱稱、性別、所在地。並且, 即使在未關注的情況下,只要使用者授權,也能獲取其資訊 )
      state 重定向後會帶上state引數,開發者可以填寫a-zA-Z0-9的引數值,最多128位元組
      #wechat_redirect 無論直接開啟還是做頁面302重定向時候,必須帶此引數
      • 如果使用者同意授權,頁面將跳轉至 redirect_uri/?code=CODE&state=STATE。

        • code作為換取access_token的票據,每次使用者授權帶上的code將不一樣,code只能使用一次,5分鐘未被使用自動過期。
    • 第二步:通過code換取網頁授權access_token

      • 獲取code後,請求以下連結獲取access_token:

        https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
        
        --- 改成具體的APPID和SECRETID
        
        https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRETID&code=CODE&grant_type=authorization_code
        
      • 引數說明:

        引數 是否必須 說明
        appid 公眾號的唯一標識
        secret 公眾號的appsecret
        code 填寫第一步獲取的code引數
        grant_type 填寫為authorization_code
      • 返回格式:

        //成功時
        {
          "access_token":"ACCESS_TOKEN",
          "expires_in":7200,
          "refresh_token":"REFRESH_TOKEN",
          "openid":"OPENID",
          "scope":"SCOPE" 
        }
        
        //錯誤時
        {"errcode":40029,"errmsg":"invalid code"}
        
      • 引數說明:

        引數 描述
        access_token 網頁授權介面呼叫憑證,注意:此access_token與基礎支援的access_token不同
        expires_in access_token介面呼叫憑證超時時間,單位(秒)
        refresh_token 使用者重新整理access_token
        openid 使用者唯一標識,請注意,在未關注公眾號時,使用者訪問公眾號的網頁,也會產生一個使用者和公眾號唯一的OpenID
        scope 使用者授權的作用域,使用逗號(,)分隔
    • 程式碼:

      @RestController
      public class WeiXinController {
          private final Logger logger = LoggerFactory.getLogger(WeChatController.class);
          @GetMapping("/auth")
          public void auth(@RequestParam("code") String code) {
              logger.info("code={}",code);
              String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRETID&code="+code+"&grant_type=authorization_code";
              RestTemplate restTemplate = new RestTemplate();
              String access = restTemplate.getForObject(url, String.class);
              logger.info(access);
          }
      }
      
  • 使用SDK方式:MP_OAuth2網頁授權

    • maven依賴:

      <dependency>
          <groupId>com.github.binarywang</groupId>
          <artifactId>weixin-java-mp</artifactId>
          <version>2.7.0</version>
      </dependency>
      
      <!-- Springboot的配置類 -->
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-configuration-processor</artifactId>
          <optional>true</optional>
      </dependency>
      
    • 配置類:

      @Component
      @ConfigurationProperties(prefix = "wechat")
      public class WechatAccountConfig {
          private String mpAppId;
          private String mpAppSecret;
      }
      
    • 配置檔案:

      wechat:
        mpAppId: wx4451dcc6f165f0f4
        mpAppSecret: cf93b89968d2fdf90af56d733ccd1a16
      
    • 自動配置:

      @Component
      @Configuration
      public class WechatMConfig {
          @Autowired
          private WechatAccountConfig accountConfig;
          @Bean
          public WxMpService wxMPService(){
              WxMpService wxMpService = new WxMpServiceImpl();
              wxMpService.setWxMpConfigStorage(wxMpConfigStorage());
              return wxMpService;
          }
          @Bean
          public WxMpConfigStorage wxMpConfigStorage(){
              WxMpInMemoryConfigStorage wxMpConfigStorage = new WxMpInMemoryConfigStorage();
              wxMpConfigStorage.setAppId(accountConfig.getMpAppId());
              wxMpConfigStorage.setSecret(accountConfig.getMpAppSecret());
              return wxMpConfigStorage;
          }
      }
      
    • SDK網頁授權:

      1. authorize方法相當於獲取code。
      2. userInfo方法相當於根據code獲取openid。
      @Controller
      @RequestMapping("/wechat")
      public class WechatController {
          @Autowired
          private WxMpService wxMpService;
          @GetMapping("/authorize")
          public String authorize(@RequestParam("returnUrl") String returnUrl){
              String url = "http://hpqv53.natappfree.cc/sell/wechat/userInfo";
              String redirectUrl = wxMpService.oauth2buildAuthorizationUrl(url, WxConsts.OAUTH2_SCOPE_BASE, returnUrl);
              System.out.println(redirectUrl);
              return "redirect:"+redirectUrl;
      
          }
          @GetMapping("/userInfo")
          public String userInfo(@RequestParam("code") String code,
                               @RequestParam("state") String returnUrl){
              WxMpOAuth2AccessToken accessToken;
              try {
                  accessToken = wxMpService.oauth2getAccessToken(code);
              } catch (WxErrorException e) {
                  throw new SellException(ResultEnum.WECHAT_MP_ERROR,e.getError().getErrorMsg());
              }
              String openId = accessToken.getOpenId();
              return "redirect:"+returnUrl+"?openid="+openId;
          }
      }
      
    • 測試請求以下連結:

      http://hpqv53.natappfree.cc/sell/wechat/authorize?returnUrl=http://www.imooc.com
      
  • 接入前端除錯:

    • 首先更改前端專案中的/config/index.js中:

      sellUrl: 'sell.com',
      openidUrl: 'http://43bwcy.natappfree.cc/sell/wechat/authorize',
      
    • 然後npm run build編譯,編譯後的檔案在dist目錄下,再複製到Nginx部署靜態資源的位置。

    • 獲取電腦和手機的IP地址。測試能不能ping通。

    • 在手機上設定手動HTTP代理,地址為電腦的IP地址,埠為8888。

    • 下載fiddler,設定監聽埠為8888,並且允許遠端連線。

2、支付部分

  • 主要用到的是統一下單和支付結果通知兩個API。

  • 使用SDK:支付SDK

    • 請求:

      重定向 /sell/pay/create
      
    • 引數:

      orderId:16465165415452
      returnUrl:http://xxx.com/abc/order/16465165415452
      
    • 返回:

      http://xxx.com/abc/order/16465165415452
      
    • maven依賴:

      <dependency>
          <groupId>cn.springboot</groupId>
          <artifactId>best-pay-sdk</artifactId>
          <version>1.1.0</version>
      </dependency>
      
  • 但是微信測試號不提供支付功能,只能用在微信內拉起支付寶的方式測試,因為支付寶提供了沙箱測試環境。開放平臺-沙箱環境 (alipay.com)

  • 使用官方的老SDK

    • 引入老SDK的maven依賴:

      <dependency>
          <groupId>com.alipay.sdk</groupId>
          <artifactId>alipay-sdk-java</artifactId>
          <version>4.10.167.ALL</version>
      </dependency>
      
    • 根據官方的接入文件,主要是對AlipayClient和AlipayTradeWapPayRequest進行一些配置,然後用form = alipayClient.pageExecute(alipayRequest).getBody()獲取提交到前端的表單,用response.getWriter().write(form)輸出。

  • 支付寶支付相關的配置類:

    • 配置類:

      @Component
      @ConfigurationProperties(prefix = "alipay")
      public class AlipayConfig {
          String charset = "UTF-8";
          String appId;
          String appPrivateKey;
          String alipayPublicKey;
          String serverUrl = "https://openapi.alipaydev.com/gateway.do";
          String baseUrl;
      }
      
    • 配置檔案:

      alipay:
        app-id: 
        alipay-public-key: 
        app-private-key: 
        base-url: 
      
    • 可以先用這種方式測試一下。

  • 支付寶針對官方的老SDK提供了實現的Demo

    • demo中,ap.js提供了跳轉邏輯,pay.htm是提供了跳轉頁面,另外兩個是示例。使用需要注意的是要在htm檔案中正確配置ap.js的路徑。
    • 以get方式為例,最終訪問的是demo_get.htm下的確認支付的a標籤中的href路徑。如果使用get方式,需要將後端的請求資料動態傳入,可以使用freemaker模板引擎。
  • freemaker模板引擎:

    • maven依賴:

      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-freemarker</artifactId>
      </dependency>
      
    • 前端使用:

      • 在template目錄下建立字尾為.ftl的檔案。
      • 使用${鍵名}的方式獲取後端傳入的資料,對於物件使用${鍵名.屬性名}
    • 後端資料:

      • 返回一個ModelAndView物件,設定viewName為.ftl檔名。
      • 設定model為一個map形式的資料。
  • 實現微信呼叫支付寶支付:

    • 主要就是要傳給前端一個拼接好的url。

    • alipayClient.pageExecute(alipayRequest).getBody()返回的String物件是一個form表單,寫到前端是用post方式。

      • 而沒有找到單獨的獲取url等的方法,只好用擷取form字串的方法。(其實用post的demo可能好一點?)
      • 在form表單中,只有biz_content屬性是在表單屬性,其他都被拼接在了action屬性中。
    • 必須呼叫alipayClient.pageExecute(alipayRequest)方法的原因是,需要根據支付寶公鑰和應用私鑰獲得簽名sign屬性,當然也可以自行實現。而且經過這個方法後,其中的屬性都被UrlEncode了(除了biz_content)。

    • 所以解決辦法是對form字串進行擷取(其實不是一個很好的方法),然後拼接UrlEncode後的biz_content屬性。

    • 測試控制器:

      @Controller
      @RequestMapping("/pay")
      public class PayController {
          @Autowired
          private AlipayConfig alipayConfig;
          @GetMapping("/create")
          public ModelAndView create(@RequestParam("orderId") String orderId,
                             @RequestParam("returnUrl") String returnUrl,
                             HttpServletResponse response){
      
              String CHARSET = alipayConfig.getCharset();
              String APP_ID = alipayConfig.getAppId();
              String APP_PRIVATE_KEY = alipayConfig.getAppPrivateKey();
              String ALIPAY_PUBLIC_KEY = alipayConfig.getAlipayPublicKey();
              AlipayClient alipayClient = new DefaultAlipayClient(alipayConfig.getServerUrl(), APP_ID, APP_PRIVATE_KEY, "json", CHARSET, ALIPAY_PUBLIC_KEY, "RSA2"); //獲得初始化的AlipayClient
              AlipayTradeWapPayRequest alipayRequest = new AlipayTradeWapPayRequest();//建立API對應的request
              String baseUrl = alipayConfig.getBaseUrl();
              alipayRequest.setReturnUrl(baseUrl+"/pay/returnUrl");
              alipayRequest.setNotifyUrl(baseUrl+"/pay/notifyUrl");//在公共引數中設定回跳和通知地址
              alipayRequest.setBizContent("{" +
                      " \"out_trade_no\":\"201603200756150101002\"," +
                      " \"total_amount\":\"88.88\"," +
                      " \"subject\":\"Iphone6 84G\"," +
                      " \"product_code\":\"QUICK_WAP_PAY\"" +
                      " }");
      
              String form = "";
              try {
                  form = alipayClient.pageExecute(alipayRequest).getBody();
              } catch (AlipayApiException e) {
                  e.printStackTrace();
              }
              String oriUrl = StringUtils.substringBetween(form,alipayConfig.getServerUrl(),"\">");
              String biz_content = "";
              try {
                  biz_content = URLEncoder.encode(alipayRequest.getBizContent(),"utf-8");
              } catch (UnsupportedEncodingException e) {
                  e.printStackTrace();
              }
              String toUrl = oriUrl + "&biz_content=" + biz_content;
      
              toUrl = alipayConfig.getServerUrl() + toUrl;
              Map<String,String> map = new HashMap<>();
              map.put("toUrl",toUrl);
              return new ModelAndView("confirm_order",map);
          }
      }
      
    • 前端模板頁面confirm_order.ftl,其實就是把demo_get.htm中a標籤中具體的url變成了${toUrl}

  • 實現效果:

  • 實現具體業務:

    • 根據傳入的orderId查詢訂單詳情,獲取相關資訊並且傳到前端。

    • 控制器:

      @Controller
      @RequestMapping("/pay")
      public class PayController {
          private Logger logger = LoggerFactory.getLogger(PayController.class);
          @Autowired
          private OrderMasterService masterService;
          @Autowired
          private PayService payService;
          @GetMapping("/create")
          public ModelAndView create(@RequestParam("orderId") String orderId,
                                     @RequestParam("returnUrl") String returnUrl) {
              OrderDTO orderDTO = masterService.findOne(orderId);
              if (orderDTO == null) {
                  logger.error("【支付出錯】訂單不存在,orderId={}",orderId);
                  throw new SellException(ResultEnum.ORDER_NOT_EXIST);
              }
              //拼接訂單中的商品名
              StringBuilder subject = new StringBuilder();
              List<OrderDetail> orderDetailList = orderDTO.getOrderDetailList();
              for (OrderDetail orderDetail : orderDetailList) {
                  subject.append(orderDetail.getProductName()).append(" ");
              }
              Map<String, String> returnMap = payService.create(orderDTO);
              Map<String, String> map = new HashMap<>();
              map.put("toUrl", returnMap.get("toUrl"));
              try {
                  map.put("checkUrl", returnMap.get("checkUrl") + "&returnUrl=" + URLEncoder.encode(returnUrl,"utf-8"));
              } catch (UnsupportedEncodingException e) {
                  e.printStackTrace();
              }
              map.put("productName",subject.toString());
              map.put("amount", orderDTO.getOrderAmount().toString());
              map.put("buyerName",orderDTO.getBuyerName());
              map.put("orderTime", orderDTO.getCreateTime().toString());
              map.put("orderId", orderId);
              return new ModelAndView("confirm_order", map);
          }
      }
      
    • 業務層:

      @Service
      public class PayServiceImpl implements PayService {
          @Autowired
          private AlipayConfig alipayConfig;
          @Autowired
          private OrderMasterService masterService;
          private Logger logger = LoggerFactory.getLogger(PayServiceImpl.class);
          @Override
          public Map<String, String> create(OrderDTO orderDTO) {
              String subject = getProductNames(orderDTO);
              String CHARSET = alipayConfig.getCharset();
              String APP_ID = alipayConfig.getAppId();
              String APP_PRIVATE_KEY = alipayConfig.getAppPrivateKey();
              String ALIPAY_PUBLIC_KEY = alipayConfig.getAlipayPublicKey();
              AlipayClient alipayClient = new DefaultAlipayClient(alipayConfig.getServerUrl(), APP_ID, APP_PRIVATE_KEY, "json", CHARSET, ALIPAY_PUBLIC_KEY, "RSA2"); //獲得初始化的AlipayClient
              AlipayTradeWapPayRequest alipayRequest = new AlipayTradeWapPayRequest();//建立API對應的request
              String baseUrl = alipayConfig.getBaseUrl();
              alipayRequest.setReturnUrl(baseUrl + "/pay/backwechat");
              alipayRequest.setNotifyUrl(baseUrl + "/pay/notifyUrl");//在公共引數中設定回跳和通知地址
              alipayRequest.setBizContent("{" +
                      " \"out_trade_no\":\"" + orderDTO.getOrderId() + "\"," +
                      " \"total_amount\":\"" + orderDTO.getOrderAmount() + "\"," +
                      " \"subject\":\"" + subject + "\"," +
                      " \"product_code\":\"QUICK_WAP_PAY\"" +
                      " }");
              String form = "";
              try {
                  form = alipayClient.pageExecute(alipayRequest).getBody();
              } catch (AlipayApiException e) {
                  e.printStackTrace();
              }
              String oriUrl = StringUtils.substringBetween(form, alipayConfig.getServerUrl(), "\">");
              String biz_content = "";
              try {
                  biz_content = URLEncoder.encode(alipayRequest.getBizContent(), "utf-8");
              } catch (UnsupportedEncodingException e) {
                  e.printStackTrace();
              }
              String toUrl = oriUrl + "&biz_content=" + biz_content;
      
              toUrl = alipayConfig.getServerUrl() + toUrl;
              String checkUrl = baseUrl + "/pay/checkpay?orderId=" + orderDTO.getOrderId();
              Map<String, String> map = new HashMap<>();
              map.put("toUrl",toUrl);
              map.put("checkUrl",checkUrl);
      
              return map;
          }
      
          private String getProductNames(OrderDTO orderDTO) {
              StringBuilder subject = new StringBuilder();
              List<OrderDetail> orderDetailList = orderDTO.getOrderDetailList();
              for (OrderDetail orderDetail : orderDetailList) {
                  subject.append(orderDetail.getProductName()).append(" ");
            }
              return subject.toString();
          }
      }
      
  • 非同步通知:

    • 驗證簽名。

    • 獲取返回的支付狀態。

    • 校驗支付金額。

    • 全部校驗通過後,通知支付平臺停止非同步通知。

    • 具體見支付寶非同步回撥文件

    • 控制器:

      @PostMapping("/notifyUrl")
      public void notifyUrl(HttpServletRequest request, HttpServletResponse response) {
          boolean signVerified = payService.notifyUrl(request, response);
          if (signVerified) {
              masterService.paid(masterService.findOne(request.getParameter("out_trade_no")));
          }
          logger.info("【非同步回撥】回撥結果{},orderId={}",signVerified,request.getParameter("out_trade_no"));
      }
      
    • 業務層:

      @Override
      public boolean notifyUrl(HttpServletRequest request, HttpServletResponse response) {
          response.setContentType("text/html;charset=utf-8");
      
          Map<String, String> paramsMap = new HashMap<>(); //將非同步通知中收到的所有引數都存放到map中
          Map<String, String[]> requestParams = request.getParameterMap();
          for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext();) {
              String name = iter.next();
              String[] values = requestParams.get(name);
              String valueStr = "";
              for (int i = 0; i < values.length; i++) {
                  valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
              }
              paramsMap.put(name, valueStr);
          }
      
          boolean signVerified = false; //呼叫SDK驗證簽名
          try {
              signVerified = AlipaySignature.rsaCheckV1(paramsMap, alipayConfig.getAlipayPublicKey(), alipayConfig.getCharset(), alipayConfig.getSign_type());
          } catch (AlipayApiException e) {
              e.printStackTrace();
          }
      
      
          boolean check = checkBotifyRequest(paramsMap);
          try {
              PrintWriter writer = response.getWriter();
              check = checkBotifyRequest(paramsMap);
              if(signVerified && check){
                  writer.write("success");
                  logger.info("【回撥支付成功】,orderId={}",request.getParameter("out_trade_no"));
              }else{
                  writer.write("failure");
                  logger.info("【回撥支付失敗】,orderId={}",request.getParameter("out_trade_no"));
              }
              response.getWriter().close();
          } catch (IOException e) {
              e.printStackTrace();
          }
      
          return signVerified && check;
      }
      
  • 還要考慮的一點是從外部瀏覽器支付完成後如何跳轉回微信:

    • 首先,直接用同步回撥returnUrl(固定為/backwechat)回到微信:

      • 控制器:

        @GetMapping("/backwechat")
        public ModelAndView backwechat(){
            return new ModelAndView("jumpback");
        }
        
      • 前端:

        <script>
            window.onload =function () {
                window.location = "weixin://"
            }
        </script>
        
      • 這樣回到的就是跳轉之前的微信介面,但是現在的問題是如何進入下一步。

    • 改造原有的跳轉頁面,增加一個支付完成的按鈕。

      • 新增在pay.htm中:

        <div class="wrapper buy-wrapper">
            <a id="checkpay" class="J-btn-submit btn mj-submit btn-strong btn-larger btn-block">支付完成</a>
        </div>
        
        var checkUrl = getQueryString(location.href, "checkUrl");
        var returnUrl = getQueryString(location.href, "returnUrl");
        document.querySelector("#checkpay").href = checkUrl + "&returnUrl=" + returnUrl;
        
      • 為了能獲取checkUrl,需要對之前的前端檔案進行一些改造:

        • confir_order.ftl:

          <input id="orderId" type="hidden" value=${checkUrl} />
          
          var checkUrl = document.getElementById("orderId").value;
          
          _AP.pay(e.target.href, checkUrl);
          
        • ap.js:

          b.pay = function(d, checkUrl) {
              var c = encodeURIComponent(a.encode(d));
              location.href = "pay.htm?goto=" + c + "&checkUrl=" + checkUrl;
          };
          
      • 後端的處理:

        • PayServiceImpl中拼接checkUrl,同時使用一個map返回toUrl和checkUrl:

          String checkUrl = baseUrl + "/pay/checkpay?orderId=" + orderDTO.getOrderId();
          
        • PayController中放入map中:

          map.put("checkUrl", returnMap.get("checkUrl"));
          
    • 最後的結果:

      • 如果支付前直接點支付完成,會查詢訂單是否完成。
      • 支付完成後在瀏覽器中點選完成付款,會跳轉到微信中,然後點選支付完成會跳轉到最終頁面。

  • 接入前端除錯:

    • 首先更改前端專案中的/config/index.js中:

      sellUrl: 'http://sell.com',
      openidUrl: 'http://5w3ab3.natappfree.cc/sell/wechat/authorize',
      wechatPayUrl: 'http://5w3ab3.natappfree.cc/sell/pay/create'
      
    • 重新部署前端。

  • 退款:

    • 使用請求AlipayTradeRefundRequest,傳入orderId和退款金額。

    • 業務層:

      @Override
      public void refund(String orderId) {
          String CHARSET = alipayConfig.getCharset();
          String APP_ID = alipayConfig.getAppId();
          String APP_PRIVATE_KEY = alipayConfig.getAppPrivateKey();
          String ALIPAY_PUBLIC_KEY = alipayConfig.getAlipayPublicKey();
          AlipayClient alipayClient = new DefaultAlipayClient(alipayConfig.getServerUrl(), APP_ID, APP_PRIVATE_KEY, "json", CHARSET, ALIPAY_PUBLIC_KEY, "RSA2"); //獲得初始化的AlipayClient
          AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();//建立API對應的request類
          OrderDTO orderDTO = masterService.findOne(orderId);
          request.setBizContent("{" +
                                "\"out_trade_no\":\"" + orderId + "\"," +
                                "\"out_request_no\":\"1000001\"," +
                                "\"refund_amount\":\"0" + orderDTO.getOrderAmount() + "\"}"); //設定業務引數
          AlipayTradeRefundResponse response = null;//通過alipayClient呼叫API,獲得對應的response類
          try {
              response = alipayClient.execute(request);
          } catch (AlipayApiException e) {
              e.printStackTrace();
          }
          System.out.print(response.getBody());
      }
      
    • 在orderMasterService的cancel取消訂單方法中最後被呼叫。


iwehdio的部落格園:https://www.cnblogs.com/iwehdio/