A solution to the cross-domain problem of running vUE projects locally

New a wechat H5 recharge page, static page style code through Google browser debugging written after the public is used in the need to call the relevant interface of wechat; This needs to run in the development tool of wechat, but the development tool debugging url does not support IP address, so we must find a way to solve;Copy the code

There are three plans:

  1. Disable the same Origin policy of the browser by using the browser features. (Not applicable here)
  2. Using the Vue CLI configuration
    • (1) Configure the running script:"serve": "vue-cli-service serve --host h5.xxxx.com --port 443 --https",
    • (2) At this point, the host used to access the local environment 127.0.0.1 and then changed to 127.0.0.1h5.xxxx.comExternal network environment, in this case will not run, so you need to modify the host file, modify the point; The host file is in the C drive system32.127.0.0.1 test.com😉
    • (3) Attention,The default HTTPS port is 443. The default HTTP port is 80, the port is HTTPS, so 443 needs to be configured. Second, when changing the host file, change the preceding#Comments removed, can be effective; If you executenpm run serveCan not run, please restart the computer;
    • (4) The last running address link ishttps://h5.xxxx.com/lvy-pay.https://h5.xxxx.com:443/lvy-pay443 is the default port and is omitted
  3. Use the VUE proxy service to solve cross-domain problems (this is the most common, please refer to the Document)

Development tool debugging

Copy https://h5.xxxx.com/lvy-pay to wechat web development tool, redirect_URI parameter error? This is due to the authorization callback domain name

  1. Here we need to understand the authorization principle of wechat public number wechat public number is the login authorization method of OAUTH2; In a nutshell, it is

    • 1.1 After a user confirms login through wechat, wechat will return an authorization code to a third party (access party). This authorization code is one-time and has a short validity period.
    • 1.2 A third party invokes the wechat interface to obtain the token through this code, and the validity period of the token is relatively short;
    • 1.3 Use token to call wechat platform interface and obtain wechat personal information (nickname, profile picture address, OpenID, UnionID, region…)
  2. The login authorization for the first oAuth2 is as follows:

    • 2.1 By reassigninglocation.hrefAnd pass in the service numberappIdAs well asredirect_uriJump to wechat authorization link
    • 2.2 Obtain the code with the consent of the user; Redirect_uri specifies the address of the authorized page, and the location.host must be authorized, otherwise the official address will not be recognized and code will not be returned.

    The following is an example (see the official documentation for details) :

    export const requestWxCode = () = > {
        const REDIRECT_URI = [location.protocol, "/ /", location.host, location.pathname].join(' ')
        location.href = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${APP_ID}&redirect_uri=${REDIRECT_URI}&response_type=code&scope=snsapi_userinfo&state=ok#wechat_redirect`
    }
    Copy the code
  3. Procedure for authorization to call back a domain name:

    • Take the account and password of the service number to log in wechat management background, find the webpage authorization
    • ② Fill in the authorization callback field
    • ③ Download mp_verify_nwXYqcey0l6bdram. TXT and upload it to the web server (or virtual host) directory specified by the domain name or path. Verify to ensure access;
    • (4) Note the third step, for the first time to use the domain name, the server root directory is not, so even if you put it locally, it is still not accessible; So you can either deploy it by yourself or find operation and maintenance to manually place it. When you can click Save, the domain name callback is successfully authorized

Encapsulate the request

XMLHttpRequest is used to encapsulate the valid token. The code is omitted here.

import { API_BASE_URL } from '.. /config'
import { $loginToken, $requestWxCode } from '.. /utils/utils'
import Toast from '@/components/Toast'
import { IS_DEV } from '.. /config'
import axios from 'axios'

import registerDialog from '@/components/Register'

const SUCCESS_CODE = 0
const NEED_REGISTER = 2
const TOKEN_FAIL = 100
const VERSION = '1.1.6'

axios.defaults.timeout = 5000
axios.defaults.baseURL = API_BASE_URL

export function $post(url, params) {
  axios.defaults.headers.common['AppVersion'] = VERSION
  if ($loginToken()) {
    axios.defaults.headers.common['Authorization'] = $loginToken()
  }
  return new Promise(resolve= > {
    axios({ url, params, method: 'post' }).then(({ data: { data, code, message }}) = > {
      if (IS_DEV) {
        console.warn('req => ', url, 'code = ',code, 'data = ',data)
      }
      if (code === SUCCESS_CODE) {
        resolve(data)
      } else if (code === NEED_REGISTER) {
        localStorage.removeItem('login_info')
        registerDialog()
      } else if (code === TOKEN_FAIL) {
        Toast(message)
        localStorage.removeItem('login_info')
        $requestWxCode()
      } else {
        console.error('Request fail: code = ' + code + ', message = ' + message)
        Toast(message || 'Server is abnormal. Please try again later.')}})})}Copy the code

Iv. Specific login logic:

* 4.1 First determine whether to open it in the wechat app. Only open it in the app can the authorized link of wechat be called; * 4.2 Check whether there is a valid token in the cache (token exists and has not expired), if so, you can directly request the interface; * 4.3 If the token does not exist or does exist but has expired, the code needs to be re-evaluated to see if the code exists on the link or if the code is duplicate (duplicate means it must be unavailable because the code is one-time) * 4.4 If the code does not exist, manually redirect the route, To call wechat's authorization link is as follows:Copy the code
location.href = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${APP_ID}&redirect_uri=${REDIRECT_URI}&response_type=code&scope=snsapi_userinfo&state=ok#wechat_redirect`
Copy the code

After the user agrees to authorize, you will find the routing address changed into https://h5.xxxx.com/lvy-pay?code=0014sGyh2lXvIC05NYvh2B3qyh24sGyY&state=ok, containing the code above, take to the code, Exchange the token with the server and store the token and expiration time in the cache for direct invocation next time. At this point, you are free to send interface requests

5. Call wechat Pay

createOrder(goodsId).then(({ prepay_id }) = > {
  const timeStamp = String(Date.now())
  const nonceStr = `qingteng_${timeStamp}_The ${Math.floor(Math.random() * 10000)}`
  const $$package = `prepay_id=${prepay_id}`
  const paramStr = `appId=${APP_ID}&nonceStr=${nonceStr}&package=${$$package}&signType=MD5&timeStamp=${timeStamp}&key=${API_KEY}`

  window.WeixinJSBridge.invoke('getBrandWCPayRequest', {appId: APP_ID,
    timeStamp,
    nonceStr,
    package: $$package,
    signType: 'MD5'.paySign: md5(paramStr).toUpperCase(),
  },({ err_msg }) = > {
    if (err_msg === "get_brand_wcpay_request:ok") {
      Toast('Purchase successful')
      this.getUserProperty()
      this.currentType = ' '
    } else if (err_msg === "get_brand_wcpay_request:fail") {
      Toast('Payment failed, please try again later')}else if (err_msg === "get_brand_wcpay_request:cancel") {
      Toast('Payment cancelled')}})}Copy the code

Process:

  • ① : Create the order and get the corresponding prepay_id from the server, which is the required parameter of the payment package; Core is to call WeChat browser environment provided by the window. The WeixinJSBridge. Invoke (‘ getBrandWCPayRequest, options, () = > {}};
  • ② This also involves the issue of authority, not any domain name page can call the payment interface, that is very dangerous; Must want to have company qualification to assure, can be invoked below authorizing this domain name to go;
  • ③ The specific is to log in the business number, increase the H5 payment domain name authorization;

This is the documentation and summary of configuration and authorization issues during the development process

6. Knowledge expansion

  1. Default HTTP and HTTPS ports
  2. XMLHttpRequest learning: AxiOS learning
  3. OAuth(Open Authorization) is an open standard that allows a user to give third-party applications access to the user’s private resources (such as photos, videos, and contact lists) stored on a site without having to provide a user name and password to third-party applications.

Allows users to provide a token instead of a username and password to access the data they store with a particular service provider. Each token grants a specific site access to a specific resource for a specific period of time. In this way, OAuth allows users to authorize third party websites to access their information stored on another service provider. Both parties need to share all content of their access to Xu Hou or their data. Code examples:

export default {
  created() {
    // Resolve the problem that the first authorization needs to be rolled back twice
    window.addEventListener("popstate".() = > {
      window.WeixinJSBridge.invoke("closeWindow");
    });
    this.init();
  },

  methods: {
    $requestWxCode2 = (code) = > {
      if(! IS_WECHAT) { Toast('Please use it in wechat')}else {
        const { protocol, host, pathname, search } = location
        const REDIRECT_URI = `${protocol}//${host}${pathname}${search}`
        const wxAuthorizeAddress = 'https://open.weixin.qq.com/connect/oauth2/authorize'
        window.location.href = `${wxAuthorizeAddress}? appid=${APP_ID}&redirect_uri=The ${encodeURIComponent(REDIRECT_URI)}&response_type=code&scope=snsapi_userinfo&state=ok&connect_redirect=1#wechat_redirect`}}init() {
      // Forcibly enable login expiration in test environment or formal environment Settings
      // this.setLoginExpirationTimeInDev(false);
      if(! $loginToken()) {this.userLogin();
        return;
      }
      
      this.initPageInfo();
    },

    userLogin() {
      let code = location.search.match(/code=(\w+)[&#]? /)? location.search.match(/code=(\w+)#? /) [1]
        : "";
      // 1. Authorize to get the code
      if(! code) {// Auth2 authorizes a new page and re-creates the created declaration cycle function
        $requestWxCode2()
        return
      }
      
      // 2. Exchange code for token and cache the token and its expiration time
      h5UserLogin(code).then(({ token, expires_in }) = > {
        Storage.setItem("wechat_code", code);
        Storage.setItem("login_info", {
          token,
          expiredTime: Date.now() + expires_in * 1000});Each interface needs to handle tokens
        this.initPageInfo();
      });

      history.pushState({ page: 1 }, null.window.location.href)
    },

    setLoginExpirationTimeInDev(forcedOpen = false) {
      if(! IS_DEV || ! forcedOpen) {return;
      }
      const { expiredTime = "" } = Storage.getItem("login_valid") | | {};if(! expiredTime || expiredTime <Date.now()) {
        Storage.setItem("login_valid", { expiredTime: Date.now() + 5000 });
        $requestWxCode2("");
      } else {
        history.pushState({ page: 1 }, null.window.location.href); }},initPageInfo(){},Pay(goodsId) {
      if(! $checkIsWeChat()) {return;
      }

      createOrder(goodsId).then(({ prepay_id }) = > {
        const timeStamp = String(Date.now());
        const nonceStr = `qingteng_${timeStamp}_The ${Math.floor(
          Math.random() * 10000
        )}`;
        const $$package = `prepay_id=${prepay_id}`;
        const paramStr = `appId=${APP_ID}&nonceStr=${nonceStr}&package=${$$package}&signType=MD5&timeStamp=${timeStamp}&key=${API_KEY}`;

        window.WeixinJSBridge.invoke(
          "getBrandWCPayRequest",
          {
            appId: APP_ID,
            timeStamp,
            nonceStr,
            package: $$package,
            signType: "MD5".paySign: md5(paramStr).toUpperCase(),
          },
          ({ err_msg }) = > {
            if (err_msg === "get_brand_wcpay_request:ok") {
              Toast("Purchase successful");
              this.getUserProperty();
              this.currentType = "";
            } else if (err_msg === "get_brand_wcpay_request:fail") {
              Toast("Payment failed, please try again later");
            } else if (err_msg === "get_brand_wcpay_request:cancel") {
              Toast("Payment has been cancelled"); }}); }); ,}}};Copy the code