小废吴 發表於 2021-10-27 17:38:00

Vue3+Typescript+Node.js实现微信端公众号H5支付(JSAPI v3)教程--各种填坑

<p>----微信支付文档,不得不说,挺乱!(吐槽截止)</p>
<h3>功能背景</h3>
<p>微信公众号中,点击菜单或者扫码,打开公众号中的H5页面,进行支付。</p>
<p>&nbsp;</p>
<h3>一、技术栈</h3>
<p>前端:Vue:3.0.0,typescript:3.9.3,axios,vant,weixin-jsapi(微信官方wxjsdk)</p>
<p>后端:Koa,wxpay-3(不错的apiv3封装 https://github.com/yangfuhe/node-wxpay),axios<span style="color: rgba(4, 81, 165, 1); font-family: Consolas, &quot;Courier New&quot;, monospace"><span style="white-space: pre"><br></span></span></p>
<p>&nbsp;</p>
<h3>二、微信公众平台配置</h3>
<p>1. 申请公众号。</p>
<p>2. 公众号设置:功能设置,JS接口安全域名(前端调用JSSDK,调用微信开放JS接口时使用),网页授权域名(用户授权,获取openID前,需要获取code,整个过程中需要一个回调页面,此页面所在域名)。</p>
<p>&nbsp; &nbsp; 注意:a.这两个域名可以一样,根据实际情况使用。。一般是一样;目前教程中这里是同一个域名,就是前端所在的域名,比如:wxpay.test.cn,不要有http前缀;</p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;b.需要把下载文件放置到域名所在文件下,保证wxpay.test.cn/MP_verify_***.txt可访问。</p>
<p>3. 设置与开发:基本配置--公众号开发信息,记住AppID,AppSecret(获取access_token和openID时使用),IP白名单(微信开发者工具中,获取access_token时使用)。</p>
<p>&nbsp;</p>
<h3>三、微信商户平台配置</h3>
<p>1. 申请商户号。</p>
<p>2. 我的产品:开通JSAPI支付。</p>
<p>3. 开发配置:JSAPI支付,添加支付授权目录。此配置是前端支付页面URL路径。目前教程中与上面的两个域名一样(wxpay.test.cn)。</p>
<p>4. AppID账号管理:与公众号关联,即上面的公众号AppID绑定。申请关联后,要前往公众号:广告与服务--微信支付,商户号管理,同意关联。这样,公众号与商户号才能绑定。</p>
<p>5. 我的账号:账户设置--API安全,申请API证书,API证书管理--证书序列号,设置API秘钥(其实没用,因为是用的后面的v3秘钥),设置APIv3秘钥。</p>
<p>&nbsp;</p>
<h3>四、前端开发</h3>
<p>1. 添加JSSDK模块,npm install weixin-jsapi -s</p>
<p>2. 创建PayTest.vue页面,就一个支付按钮。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">template</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">div </span><span style="color: rgba(255, 0, 0, 1)">id</span><span style="color: rgba(0, 0, 255, 1)">="paytest"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">van-button </span><span style="color: rgba(255, 0, 0, 1)">round block type</span><span style="color: rgba(0, 0, 255, 1)">="primary"</span><span style="color: rgba(255, 0, 0, 1)"> @click</span><span style="color: rgba(0, 0, 255, 1)">="doPay"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>支付<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">van-button</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">div </span><span style="color: rgba(255, 0, 0, 1)">v-html</span><span style="color: rgba(0, 0, 255, 1)">="msg"</span><span style="color: rgba(255, 0, 0, 1)"> style</span><span style="color: rgba(0, 0, 255, 1)">="white-space: pre-wrap;"</span><span style="color: rgba(0, 0, 255, 1)">&gt;&lt;/</span><span style="color: rgba(128, 0, 0, 1)">div</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">div</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">template</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span></pre>
</div>
<p>3. 添加逻辑<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">script </span><span style="color: rgba(255, 0, 0, 1)">lang</span><span style="color: rgba(0, 0, 255, 1)">="ts"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span><span>import { Vue } from </span><span>"</span><span>vue-class-component</span><span>"</span><span>;</span></p>
<div class="cnblogs_code">
<pre><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">import { Action } from </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">"</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">vuex-class</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">"</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">;
import wx from </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">"</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">weixin-jsapi</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">"</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">;

export </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">default</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> class PayTest extends Vue {
@Action(</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">"</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">setOpenID</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">"</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">) private setOpenID: any;
private appID </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=</span> <span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">"</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">wx46adeb36e3e622ad</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">"</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">; </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">微信公众号appID,可做成配置项</span>
<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">private msg </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=</span> <span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">''</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">;

public created() {
    </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">判断本地是否已存openID   </span>
    <span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">if</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> (</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">!</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">this</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">.$store.state.openID) {
      </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">如果未存,则要通过授权,回调页面,获取code,然后获取openID,保存本地</span>
      <span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">this</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">.getWxAuth();
    }

    </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">初始化wx的jssdk的config</span>
    <span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">this</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">.initWxConfig();
}

</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">用户授权,回调,获取openID</span>
<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">private getWxAuth() {
    </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">官方参考文档:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html</span>
    <span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">if</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> (</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">!</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">this</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">.$route.query.code) {
      </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">微信授权,授权后重定向到本页面</span>
<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">      const localUrl </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> window.location.href;
      alert(localUrl);
      window.location.href </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> `https:</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">open.weixin.qq.com/connect/oauth2/authorize?appid=${this.appID}&amp;redirect_uri=${localUrl}&amp;response_type=code&amp;scope=snsapi_userinfo&amp;state=STATE&amp;connect_redirect=1#wechat_redirect`;</span>
<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">    } </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">else</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> {
      </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">如果已经授权,获取code参数,通过后端获取openID,返回前端,保存本地缓存</span>
<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">      const url </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=</span> <span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">"</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">/public/wxPort/getWxOpenId</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">"</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">;
      </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">this</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">.axios.get(url, { params: { code: </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">this</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">.$route.query.code } })
      .then((res: any) </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=&gt;</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> {
          </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">openID保存本地</span>
          <span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">if</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> (res.status.code </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">===</span> <span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">1</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">) {
            </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">this</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">.setOpenID(res.data.openID);
            </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">this</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">.msg </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">+=</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> `</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">---</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">授权成功,openID:${res.data.openID}\n`;
          } </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">else</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> {
            </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">抛出错误</span>
            <span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">this</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">.msg </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">+=</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> `</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">---</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">获取openID失败:${JSON.stringify(res)}\n`;
          }
      }).</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">catch</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">((err: any) </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=&gt;</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> {
          </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">this</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">.msg </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">+=</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> `</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">---</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">获取openID失败err:${JSON.stringify(err)}\n`;
      });
    }
}
</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">初始化wx JSSDK</span>
<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">private initWxConfig() {
    </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">后端获取access_token和ticket,返回签名信息,初始化wx.config</span>
<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">    const url </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=</span> <span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">"</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">/public/wxPort/getTicket</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">"</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">;
    </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">this</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">.axios.get(url).then((res: any) </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=&gt;</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> {
      </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">if</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> (res.status.code </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">===</span> <span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">1</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">) {
      </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">this</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">.msg </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">+=</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> `</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">---</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">获取 ticket成功,返回结果:${JSON.stringify(res.data)}\n`;

      </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">官方参考文档:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html#1</span>
      <span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">初始化验证jssdk</span>
<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">      wx.config({
          debug: </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">true</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">, </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)"> 这里一般在测试阶段先用ture,等打包给后台的时候就改回false,</span>
<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">          appId: </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">this</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">.appID, </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)"> 必填,公众号的唯一标识</span>
<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">          timestamp: res.data.timestamp, </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)"> 必填,生成签名的时间戳</span>
<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">          nonceStr: res.data.nonceStr, </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)"> 必填,生成签名的随机串</span>
<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">          signature: res.data.signature, </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)"> 必填,签名</span>
<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">          jsApiList: [</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">"</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">chooseWXPay</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">"</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">], </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)"> 必填,需要使用的JS接口列表</span>
<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">      });

      </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">通过ready接口处理成功验证</span>
<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">      wx.ready(() </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=&gt;</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> {
          </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">this</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">.msg </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">+=</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> `</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">---</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">初始化wx.config成功\n`;
          wx.checkJsApi({
            jsApiList: [</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">'</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">chooseWXPay</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">'</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">], </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)"> 需要检测的JS接口列表,所有JS接口列表见附录2,</span>
<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">            success: (res: any) </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=&gt;</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> {
            </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)"> 以键值对的形式返回,可用的api值true,不可用为false</span>
            <span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)"> 如:{"checkResult":{"chooseWXPay":true},"errMsg":"checkJsApi:ok"}</span>
            <span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">this</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">.msg </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">+=</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> `</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">---</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">检查wx.checkJsApi成功:${JSON.stringify(res)}}\n`;
            }
          });
      });

      </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">通过error接口处理失败验证</span>
<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">      wx.error((err: any) </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=&gt;</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> {
          </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">this</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">.msg </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">+=</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> `</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">---</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">wx接口失败:${JSON.stringify(err)}}\n`;
      });
      } </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">else</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> {
      </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">抛出错误</span>
      <span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">this</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">.msg </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">+=</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> `</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">---</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">获取ticket失败,返回结果:${JSON.stringify(res)}\n`;
      }
    }).</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">catch</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">((err: any) </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=&gt;</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> {
      </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">this</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">.msg </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">+=</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> `</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">---</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">获取ticket失败err,返回结果:${JSON.stringify(err)}\n`;
    });
}

</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">支付按钮</span>
<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">private doPay() {
    </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">先是后端用户下单,下完单之后,前端再调取微信支付</span>
<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">    const url </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=</span> <span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">"</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">/public/wxPort/prepay</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">"</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">;
    </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">this</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">.axios.get(url, { params: { openID: </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">this</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">.$store.state.openID } })
      .then((res: any) </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=&gt;</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> {
      </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">if</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> (res.status.code </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">===</span> <span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">1</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">) {
          </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">this</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">.msg </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">+=</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> `</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">---</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">统一下单成功,返回结果:${JSON.stringify(res.data)}\n`;
          const _that </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=</span> <span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">this</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">;
          wx.chooseWXPay({
            timestamp: res.data.timestamp, </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)"> 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符</span>
<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">            nonceStr: res.data.nonceStr, </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)"> 支付签名随机串,不长于 32 位</span>
<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">            package: </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">"</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">prepay_id=</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">"</span> <span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">+</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> res.data.prepayID, </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)"> 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=\*\*\*)</span>
<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">            signType: </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">"</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">RSA</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">"</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">, </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)"> 微信支付V3的传入RSA,微信支付V2的传入格式与V2统一下单的签名格式保持一致</span>
<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">            paySign: res.data.paySign, </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)"> 支付签名</span>
<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">            success: </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">function</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> (res: any) {
            _that.msg </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">+=</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> `</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">---</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">chooseWXPay成功,返回结果:${JSON.stringify(res)}\n`;
            },
            </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)"> 支付取消回调函数</span>
<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">            cancel: </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">function</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> (res: any) {
            _that.msg </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">+=</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> `</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">---</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">chooseWXPay取消,返回结果:${JSON.stringify(res)}\n`;
            },
            </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)"> 支付失败回调函数</span>
<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">            fail: </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">function</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> (res: any) {
            _that.msg </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">+=</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> `</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">---</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">chooseWXPay失败,返回结果:${JSON.stringify(res)}\n`;
            },
          });
      } </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">else</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> {
          </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">抛出错误</span>
          <span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">this</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">.msg </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">+=</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> `</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">---</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">统一下单失败,返回结果:${JSON.stringify(res)}\n`;
      }
      })
      .</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">catch</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">((err: any) </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=&gt;</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> {
      </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">this</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">.msg </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">+=</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> `</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">---</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">统一下单失败err,返回结果:${JSON.stringify(err)}\n`;
      });
}
}
</span><span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">script</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span></pre>
</div>
<p>&nbsp;</p>
<h3>四、后端开发</h3>
<p>需要安装插件:npm install wxpay-v3 -s</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">以下变量可以在config中统一配置
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">公众号appID</span>
<span style="color: rgba(0, 0, 255, 1)">const</span> appID = <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">wx46adeb36e3e622ad</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">公众号AppSecret</span>
<span style="color: rgba(0, 0, 255, 1)">const</span> appSecret = <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">5229c2220748d7a3c3cfe1028fda01c7</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">商户号mchID</span>
<span style="color: rgba(0, 0, 255, 1)">const</span> mchID = <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">1615227157</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">商户号API证书管理--证书序列号</span>
<span style="color: rgba(0, 0, 255, 1)">const</span> serialNo = <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">37CED47F994ED5F8B44766290CE7B979CE2CEFD3</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">商户号API安全--APIv3密钥</span>
<span style="color: rgba(0, 0, 255, 1)">const</span> apiv3PrivateKey = <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">01234567890123456789012345678901</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">商户号API证书,秘钥,也可以将秘钥中的文本复制过来</span>
<span style="color: rgba(0, 0, 255, 1)">const</span> privateKey = require(<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">fs</span><span style="color: rgba(128, 0, 0, 1)">'</span>).readFileSync(Path.join(__dirname, <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">apiclient_key.pem</span><span style="color: rgba(128, 0, 0, 1)">'</span>)).toString();</pre>
</div>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">微信公众号登录授权,获取用户的openID,access_token等信息</span>
    <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">async</span><span style="color: rgba(0, 0, 0, 1)"> getWxOpenId(ctx: any) {
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">官方参考文档:</span><span style="color: rgba(0, 128, 0, 1); text-decoration: underline">https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html</span>
      let code = ctx.query.code; <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">获取code值</span>
      let url = <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">https://api.weixin.qq.com/sns/oauth2/access_token?appid=</span><span style="color: rgba(128, 0, 0, 1)">'</span> + appID + <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">&amp;secret=</span><span style="color: rgba(128, 0, 0, 1)">'</span> + appSecret + <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">&amp;code=</span><span style="color: rgba(128, 0, 0, 1)">'</span> + code + <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">&amp;grant_type=authorization_code</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
      let res: any </span>= <span style="color: rgba(0, 0, 255, 1)">await</span> axios.<span style="color: rgba(0, 0, 255, 1)">get</span><span style="color: rgba(0, 0, 0, 1)">(url);
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">错误时{"errcode":40029,"errmsg":"invalid code"}
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">正确时
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> {
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">   "access_token":"ACCESS_TOKEN",
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">   "expires_in":7200,
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">   "refresh_token":"REFRESH_TOKEN",
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">   "openid":"OPENID",
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">   "scope":"SCOPE"
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">   }</span>
      <span style="color: rgba(0, 0, 255, 1)">if</span> (res.status === <span style="color: rgba(128, 0, 128, 1)">200</span><span style="color: rgba(0, 0, 0, 1)">) {
            </span><span style="color: rgba(0, 0, 255, 1)">if</span> ((res.data.errcode &amp;&amp; res.data.errcode.length &gt; <span style="color: rgba(128, 0, 128, 1)">0</span>) || !<span style="color: rgba(0, 0, 0, 1)">res.data.openid) {
                ctx.body </span>=<span style="color: rgba(0, 0, 0, 1)"> {
                  status: StatusCode.ErrorCustome(</span><span style="color: rgba(128, 0, 128, 1)">800</span>, <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">获取微信用户授权openID失败:</span><span style="color: rgba(128, 0, 0, 1)">'</span> +<span style="color: rgba(0, 0, 0, 1)"> JSON.stringify(res.data)),
                }
            } </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
                ctx.body </span>=<span style="color: rgba(0, 0, 0, 1)"> {
                  status: StatusCode.Success(</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">获取数据成功</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">),
                  data: {
                        openID: res.data.openid
                  }
                }
            }
      } </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
            ctx.body </span>=<span style="color: rgba(0, 0, 0, 1)"> {
                status: StatusCode.ErrorCustome(</span><span style="color: rgba(128, 0, 128, 1)">800</span>, <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">获取微信用户授权openID失败:</span><span style="color: rgba(128, 0, 0, 1)">'</span> +<span style="color: rgba(0, 0, 0, 1)"> res.data)
            }
      }
    }</span></pre>
</div>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">获取JSSDK的access_token和ticket</span>
    <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">async</span><span style="color: rgba(0, 0, 0, 1)"> getTicket(ctx: any) {
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">官方参考文档:</span><span style="color: rgba(0, 128, 0, 1); text-decoration: underline">https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html</span><span style="color: rgba(0, 128, 0, 1)">#62
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">获取access_token,强烈建议保存数据库或缓存,根据过期时间判断是否要重新获取   
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">获取jsapi_ticketn,强烈建议保存数据库或缓存,根据过期时间判断是否要重新获取</span>
      <span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
            let tempTicket </span>= <span style="color: rgba(128, 0, 0, 1)">''</span>;<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">此处从数据库或缓存获取,如果未保存或过期,要重新获取。此处代码省略</span>
            <span style="color: rgba(0, 0, 255, 1)">if</span> (!<span style="color: rgba(0, 0, 0, 1)">tempTicket) {
                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">获取access_token</span>
                let tokenUrl = `https:<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&amp;appid=${appID}&amp;secret=${appSecret}`;</span>
                let tokenRes: any = <span style="color: rgba(0, 0, 255, 1)">await</span> axios.<span style="color: rgba(0, 0, 255, 1)">get</span><span style="color: rgba(0, 0, 0, 1)">(tokenUrl);
                </span><span style="color: rgba(0, 0, 255, 1)">if</span> (!tokenRes || tokenRes.status != <span style="color: rgba(128, 0, 128, 1)">200</span> || tokenRes.data.errcode || !<span style="color: rgba(0, 0, 0, 1)">tokenRes.data.access_token) {
                  ctx.body </span>=<span style="color: rgba(0, 0, 0, 1)"> {
                        status: StatusCode.ErrorCustome(</span><span style="color: rgba(128, 0, 128, 1)">800</span>, <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">获取微信access_token失败:</span><span style="color: rgba(128, 0, 0, 1)">'</span> +<span style="color: rgba(0, 0, 0, 1)"> JSON.stringify(tokenRes.data))
                  }
                  </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">;
                }

                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">获取票据</span>
                let ticketUrl = `https:<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=${tokenRes.data.access_token}&amp;type=jsapi`;</span>
                let ticketRes: any = <span style="color: rgba(0, 0, 255, 1)">await</span> axios.<span style="color: rgba(0, 0, 255, 1)">get</span><span style="color: rgba(0, 0, 0, 1)">(ticketUrl);
                </span><span style="color: rgba(0, 0, 255, 1)">if</span> (!ticketRes || ticketRes.status != <span style="color: rgba(128, 0, 128, 1)">200</span> || ticketRes.data.errcode != <span style="color: rgba(128, 0, 128, 1)">0</span> || !<span style="color: rgba(0, 0, 0, 1)">ticketRes.data.ticket) {
                  ctx.body </span>=<span style="color: rgba(0, 0, 0, 1)"> {
                        status: StatusCode.ErrorCustome(</span><span style="color: rgba(128, 0, 128, 1)">800</span>, <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">获取微信ticket失败:</span><span style="color: rgba(128, 0, 0, 1)">'</span> +<span style="color: rgba(0, 0, 0, 1)"> JSON.stringify(ticketRes))
                  }
                  </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">;
                }
                tempTicket </span>=<span style="color: rgba(0, 0, 0, 1)"> ticketRes.data.ticket;
            }

            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">生成签名</span>
            let timestamp = Math.floor(Date.now() / <span style="color: rgba(128, 0, 128, 1)">1000</span><span style="color: rgba(0, 0, 0, 1)">);
            let nonceStr </span>=<span style="color: rgba(0, 0, 0, 1)"> WXPortController.generateNonceStr();
            let obj </span>=<span style="color: rgba(0, 0, 0, 1)"> {
                jsapi_ticket: tempTicket,
                noncestr: nonceStr,
                timestamp: timestamp,
                url: ctx.header.referer</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">url必须是调用JS接口页面的完整URL</span>
<span style="color: rgba(0, 0, 0, 1)">            }
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">按照ASCII码从小到大排序</span>
            let signStr =<span style="color: rgba(0, 0, 0, 1)"> WXPortController.raw(obj);

            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> hash加密</span>
            <span style="color: rgba(0, 0, 255, 1)">const</span> crypto = require(<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">crypto</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">);
            let shasum </span>= crypto.createHash(<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">sha1</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">);
            shasum.update(signStr);
            let signature </span>= shasum.digest(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">hex</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);

            ctx.body </span>=<span style="color: rgba(0, 0, 0, 1)"> {
                status: StatusCode.Success(</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">获取数据成功</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">),
                data: {
                  timestamp,
                  nonceStr,
                  signature
                }
            }
      }
      </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (err) {
            ctx.</span><span style="color: rgba(0, 0, 255, 1)">throw</span><span style="color: rgba(0, 0, 0, 1)">(err.message);
      }
    }</span></pre>
</div>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">微信统一下单</span>
    <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">async</span><span style="color: rgba(0, 0, 0, 1)"> prepay(ctx: any) {
      </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">调用wxpay-v3的插件</span>
            <span style="color: rgba(0, 0, 255, 1)">const</span> Payment = require(<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">wxpay-v3</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">);
            </span><span style="color: rgba(0, 0, 255, 1)">const</span> paymnetTemp: any = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Payment({
                appid: appID,
                mchid: mchID,
                private_key: privateKey,
                serial_no: serialNo,
                apiv3_private_key: apiv3PrivateKey,
            });
            let res </span>= <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> paymnetTemp.jsapi({
                description: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">测试支付</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,
                out_trade_no: Date.now().toString(),
                notify_url: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">http://gzh.zhongguoysd.top</span><span style="color: rgba(128, 0, 0, 1)">'</span>,<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">异步接收微信支付结果通知的回调地址</span>
<span style="color: rgba(0, 0, 0, 1)">                amount: {
                  total: </span><span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">
                },
                payer: {
                  openid: ctx.query.openID
                },

            });

            </span><span style="color: rgba(0, 0, 255, 1)">const</span> timestamp = Math.floor(Date.now() / <span style="color: rgba(128, 0, 128, 1)">1000</span><span style="color: rgba(0, 0, 0, 1)">);
            </span><span style="color: rgba(0, 0, 255, 1)">const</span> nonceStr =<span style="color: rgba(0, 0, 0, 1)"> WXPortController.generateNonceStr();
            </span><span style="color: rgba(0, 0, 255, 1)">if</span> (res.status === <span style="color: rgba(128, 0, 128, 1)">200</span> &amp;&amp;<span style="color: rgba(0, 0, 0, 1)"> res.data) {
                let prepayIDRes </span>=<span style="color: rgba(0, 0, 0, 1)"> JSON.parse(res.data);
                </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (prepayIDRes) {
                  </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">生成支付签名</span>
                  let str = `${appID}\n${timestamp}\n${nonceStr}\nprepay_id=<span style="color: rgba(0, 0, 0, 1)">${prepayIDRes.prepay_id}\n`;
                  let paySign </span>= paymnetTemp.rsaSign(str, privateKey, <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">SHA256withRSA</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">);
                  ctx.body </span>=<span style="color: rgba(0, 0, 0, 1)"> {
                        status: StatusCode.Success(</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">获取数据成功</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">),
                        data: {
                            prepayID: prepayIDRes.prepay_id,
                            paySign,
                            timestamp,
                            nonceStr
                        }
                  }
                  </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">;
                }
            } </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
                ctx.body </span>=<span style="color: rgba(0, 0, 0, 1)"> {
                  status: StatusCode.ErrorCustome(</span><span style="color: rgba(128, 0, 128, 1)">800</span>, <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">获取prepayID失败:</span><span style="color: rgba(128, 0, 0, 1)">'</span> +<span style="color: rgba(0, 0, 0, 1)"> JSON.stringify(res.data)),
                }
            }
      }
      </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (err) {
            ctx.</span><span style="color: rgba(0, 0, 255, 1)">throw</span><span style="color: rgba(0, 0, 0, 1)">(err.message);
      }
    }</span></pre>
</div>
<div class="cnblogs_code">
<pre>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">生成随机字符串</span>
    <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> generateNonceStr(length = <span style="color: rgba(128, 0, 128, 1)">32</span><span style="color: rgba(0, 0, 0, 1)">) {
      </span><span style="color: rgba(0, 0, 255, 1)">const</span> chars = <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
      let noceStr </span>= <span style="color: rgba(128, 0, 0, 1)">''</span>, maxPos =<span style="color: rgba(0, 0, 0, 1)"> chars.length;
      </span><span style="color: rgba(0, 0, 255, 1)">while</span> (length--) noceStr += chars;
      </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> noceStr;
    }

    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">将对象按照asscii序列化为字符串</span>
    <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span><span style="color: rgba(0, 0, 0, 1)"> raw(args: any) {
      </span><span style="color: rgba(0, 0, 255, 1)">var</span> keys =<span style="color: rgba(0, 0, 0, 1)"> Object.keys(args);
      keys </span>=<span style="color: rgba(0, 0, 0, 1)"> keys.sort()
      </span><span style="color: rgba(0, 0, 255, 1)">var</span> newArgs: any =<span style="color: rgba(0, 0, 0, 1)"> {};
      keys.forEach(function (key) {
            newArgs </span>=<span style="color: rgba(0, 0, 0, 1)"> args;
      });
      </span><span style="color: rgba(0, 0, 255, 1)">var</span> <span style="color: rgba(0, 0, 255, 1)">string</span> = <span style="color: rgba(128, 0, 0, 1)">''</span><span style="color: rgba(0, 0, 0, 1)">;
      </span><span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(0, 0, 255, 1)">var</span> k <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> newArgs) {
            </span><span style="color: rgba(0, 0, 255, 1)">string</span> += <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">&amp;</span><span style="color: rgba(128, 0, 0, 1)">'</span> + k + <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">=</span><span style="color: rgba(128, 0, 0, 1)">'</span> +<span style="color: rgba(0, 0, 0, 1)"> newArgs;
      }
      </span><span style="color: rgba(0, 0, 255, 1)">string</span> = <span style="color: rgba(0, 0, 255, 1)">string</span>.substr(<span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">);
      </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">;
    };</span></pre>
</div>
<p>&nbsp;</p>
<h3>五、填坑问题</h3>
<p>1. {"errMsg":"config:fail,Error:系统错误,错误码:40048,invalid url domain"}</p>
<p>&nbsp; &nbsp; 解决:公众号设置:功能设置,JS接口安全域名(前端调用JSSDK,调用微信开放JS接口时使用)</p>
<p>2. 获取用户授权时,报错页面:redirect_uri参数错误</p>
<p>&nbsp; &nbsp; 解决:公众号设置:功能设置,网页授权域名(用户授权,获取openID前,需要获取code,整个过程中需要一个回调页面,此页面所在域名)。</p>
<p>3.&nbsp;{"errMsg":"config:fail,Error:系统错误,错误码:63002,invalid signature"}</p>
<p>&nbsp; &nbsp; 解决:商户号,开发配置:JSAPI支付,添加支付授权目录。此配置是前端支付页面URL路径。</p>
<p>4. {errorCode=72002, errorMsg=mchid is not bind appid}或者 "商户号与appid不匹配"</p>
<p>&nbsp; &nbsp; 解决:商户号, AppID账号管理:与公众号关联,即上面的公众号AppID绑定。申请关联后,要前往公众号:广告与服务--微信支付,商户号管理,同意关联。这样,公众号与商户号才能绑定。</p>
<p>5. 支付验证签名失败</p>
<p>&nbsp; &nbsp; 这里造成的原因很多:【官方详细参考:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_1.shtml】</p>
<p>&nbsp; &nbsp; 5.1 统一下单采用jsapi v3,所以wx.chooseWXPay中的signType要用RSA。官方参考链接:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html#58</p>
<p>&nbsp; &nbsp; 5.2&nbsp;wx.chooseWXPay中paySign签名生成要用SHA256withRSA。官方参考链接:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_4.shtml</p>
<p>&nbsp; &nbsp; 5.3 paySign中的签名串要完整,尤其是【prepay_id=】不能丢:`${appID}\n${timestamp}\n${nonceStr}\nprepay_id=${prepayIDRes.prepay_id}\n`;</p>
<p>&nbsp; &nbsp; 5.4 验证签名是否正确的工具(很有用!很有用!):链接:https://pan.baidu.com/s/1ixOAnYyZVW13dFr0jWVpvw&nbsp; &nbsp; 提取码:wujv</p>
<p>&nbsp; &nbsp; 5.5 如果用旧版v2的JSAPI,那签名方式signType就与v3版不同,主要是MD5的方式,注意要与统一下单的签名方式保持一致:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">wx.chooseWXPay({
timestamp: </span><span style="color: rgba(128, 0, 128, 1)">0</span>, <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符</span>
nonceStr: <span style="color: rgba(128, 0, 0, 1)">''</span>, <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 支付签名随机串,不长于 32 位</span>
package: <span style="color: rgba(128, 0, 0, 1)">''</span>, <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=\*\*\*)</span>
signType: <span style="color: rgba(128, 0, 0, 1)">''</span>, <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 微信支付V3的传入RSA,微信支付V2的传入格式与V2统一下单的签名格式保持一致</span>
paySign: <span style="color: rgba(128, 0, 0, 1)">''</span>, <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 支付签名</span>
<span style="color: rgba(0, 0, 0, 1)">success: function (res) {
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 支付成功后的回调函数</span>
<span style="color: rgba(0, 0, 0, 1)">}
});</span></pre>
</div>
<p>&nbsp;6.&nbsp;{“errcode”:10164,“errmsg”:“invalid ip ***.***.***.***,not in whitelist rid:***....”},白名单问题。</p>
<p>&nbsp; &nbsp;解决:公众号,设置与开发,基本配置:IP白名单,把提示的invalid ip加进去。</p>
<p>&nbsp;</p>
<h3>六、结束语</h3>
<p>简单的微信JSAPI v3版本的教程基本完工,至于后期的退款等功能,wxpay-v3插件都已封装,直接调用即可。</p>
<p>整体来说,初次研究微信支付时,下手很乱,因为文档写的就很乱,新版和旧版在官网上并未很好的分割。</p>
<p>最终还是看这个:(按照文档的顺序,一层一层的扒皮)</p>
<p>1. 先看统一下单:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_1.shtml,里面有未知的参数openid</p>
<p>2. 然后看如何获取openid:https://pay.weixin.qq.com/wiki/doc/apiv3/terms_definition/chapter1_1_3.shtml#part-3</p>
<p>3. 统一下单获取perpay_id后,再看JSAPI调起支付:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_4.shtml</p>
<p>4. 调起支付使用JSSDK的方式,首先要初始配置JSSDK:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html#1&nbsp; 步骤1-5,保证wx.config是配置成功的</p>
<p>5. wx.config中的签名,要看附录1,JS-SDK使用权限签名算法:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html#62</p>
<p>6. JSSDK所有接口,要看附录2,https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html#63,目前支付用<span style="color: rgba(34, 34, 34, 1); font-family: -apple-system, BlinkMacSystemFont, &quot;SF UI Text&quot;, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif">chooseWXPay</span></p>
<p><span style="color: rgba(34, 34, 34, 1); font-family: -apple-system, BlinkMacSystemFont, &quot;SF UI Text&quot;, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif">7. 配置JSSDK完成后,要调用支付</span>wx.chooseWXPay要看:<span style="color: rgba(34, 34, 34, 1); font-family: -apple-system, BlinkMacSystemFont, &quot;SF UI Text&quot;, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif">&nbsp;</span><span style="color: rgba(34, 34, 34, 1); font-family: -apple-system, BlinkMacSystemFont, &quot;SF UI Text&quot;, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif">https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html#58</span></p>
<p><span style="color: rgba(34, 34, 34, 1); font-family: -apple-system, BlinkMacSystemFont, &quot;SF UI Text&quot;, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif">8.&nbsp;</span>wx.chooseWXPay里面有个paySign签名,如何生成签名,就回到JSAPI调起支付的页面下方,有【JSAPI调起支付的参数需要按照签名规则进行签名计算】,即:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_4.shtml</p>
<p>&nbsp;</p>
<p>基本上要看的JSAPI v3的文档就上面这些,v2的旧版我也看了一部分,没有试,就不发了。大家有任何问题,请留言。</p>
<p><em>(文章为老吕原创,转载请注明出处)</em></p><br><br>
来源:https://www.cnblogs.com/laolv4519/p/15471866.html
頁: [1]
查看完整版本: Vue3+Typescript+Node.js实现微信端公众号H5支付(JSAPI v3)教程--各种填坑