feng 发布的文章

uniapp 中引入腾讯实时音视频TRTC 步骤

最近用uniapp做了一个小程序,里面需要用到腾讯的实时音视频,腾讯有提供一个微信小程序的sdk包方便开发者引入实时音视频,但是原生微信小程序没法在uniapp中直接用,需要转,转的过程存在很多坑,腾讯的TRTC更新也非常频繁,在这里记录一下转的步骤,以便后期查阅.

文档查看以及文件下载

腾讯TRTC官方文档地址

TRTC实时音视频SDK地址

首先插件市场下载miniprogram-to-uniapp插件 点击右边的使用HBuilderX 导入插件
https://ext.dcloud.net.cn/plugin?id=2656

准备工作

将TRTC微信小程序SDK下载到本地解压后,找到文件夹TRTCSimpleDemo,根据官方的引导,在debug中填好SDKAPPID和EXPIRETIME,在微信开发者工具中打开发布,确保功能是没有问题的,然后在hbuilderX里面导入这个目录

使用插件将小程序转成uniapp项目

按照miniprogram-to-uniapp插件的介绍,在目录上右键选择miniprogram-to-uniapp,稍等几分钟后,会在hbuilderX生成一个新的目录文件TRTCSimpleDemo_uni

删除components

删除目录components,static/components

引入components

新建目录wxcomponents,在TRTCSimpleDemo的components目录下将整个trtc-room目录复制进来

对文件内容进行调整

打开pages.json 找到代码段

...
{
    "path": "pages/room/room",
    "style": {
        ...
    }
},
...

改成

{
    "path": "pages/room/room",
    "style": {
        ...
        "usingComponents": {
            "trtc-room": "/wxcomponents/trtc-room/trtc-room"
        }
    }
},

打开文件/pages/room/room.vue,找到代码段

...
import trtcRoom from "../../components/trtc-room/trtc-room";
...
  components: {
    trtcRoom
  },
...

将上面这几句代码删除

修改图片引用路径 pages目录下的几个vue里引用的图片路径错误,真实图片都在static/images下,代码里写的是../../images/ 根据需要一一修改(把images 改成 static/images 或者全部改成绝对路径 /static/images/..)

全局搜索uni.getSetting() 全部改成 wx.getSetting()

到这里差不多就可以跑起来了,注意详情本地设置里 ES6转ES5 需要打勾 在uniapp 点击菜单发行->小程序-微信 ,然后在微信开发者工具里上传,就可以真机测试了. 需要注意的是如果不上传,直接真机调试,live-pusher 会显示在最上面,但是打包出来就是好的,具体原因不明,有可能是真机调试的同层渲染有问题.

nginx解析中文域名

最近给客户做一个网站,客户提供的域名是中文的,再设置nginx后发现代理没有起作用.
查了不少资料后发现浏览器是将地方语言(中文)转成punycode编码,然后交给DNS解析的.
那么需要做的就是吧中文域名通过punycode转码,然后放到nginx的server_name后面.
个人找到的一个中文编码网站http://tools.jb51.net/punycode/index.php

记录一下,以便后期查看

在thinkjs框架下使用tenpay开发native微信扫码付款应用.

因为需要做一个付款在winform应用程序上扫码付款,所以采用native扫码的模式一来实现.

微信支付扫码的签名验证借用了github上的开源框架 node-tenpay

node后台笔者偏爱于插件式的开源轻量级框架thinkjs

再补充一个微信扫码技术文档 微信开发文档

第一步: 商户支付回调URL设置:

进入商户平台-->产品中心-->开发配置 扫码支付回调链接
我设置的是 https://域名/pay/notify

第二步: 生成二维码:

首先 npm 引入tenpay 框架

npm i tenpay --save

全局创建一个tenpay api对象

// wxpay.js
const Tenpay = require('tenpay')
const config = require('../config/config.js')
/**
 * tenpayConfig: {
    appid: '公众号ID',
    mchid: '微信商户号',
    partnerKey: '微信支付安全密钥',
    notify_url: 'https://域名/pay/complete', // 支付后回调的接口
    spbill_create_ip: '服务器ip地址'
  }
 */
let api = null
module.exports = {
  getApi () {
    if(!api) api = new Tenpay(config.tenpayConfig)
    return api
  }
}

根据商品ID生成url链接

    api.getNativeUrl({
       product_id: product.id
    })

将商品链接转成二维码
node二维码生成开源框架:node-qrcode

npm install --save qrcode // npm 安装.
const QRCode = require('qrcode')

...
const qr = await QRCode.toDataURL(order.url)    // 转成base64
this.header('Content-Type', 'image/png; charset=UTF-8')    // 设置头部
this.body = base64ToSteam(qr)    // 输出图片流
// 将base64图片转流方法.
function base64ToSteam (data) {
    const base64 = data.replace(/^data:image\/\w+;base64,/, '')
    const dataBuffer = Buffer.from(base64, 'base64')

    const bufferStream = new stream.PassThrough()
    bufferStream.end(dataBuffer)

    return bufferStream
}

生成商品二维码后,只要有用户用微信扫码,微信会自动调你之前在商户后台设置的回调链接

第三步 扫码回调链接开发

tenpay框架已经将几个回调的参数校验解析已中间件的形式开放出来,这里我们只需要将他集成到thinkjs的中间件管理里,因为有多个地方用到了中间件,集中封装一下:

const wxpay = require('./wxpay') // 代码参考最上面,就是拿到tenpay的实例对象.
// 默认options
const defaultOptions = {
  type: 'nativePay' // 扫码的回调中间件传入nativePay
}
module.exports = (options = {}) => {
  options = Object.assign({}, defaultOptions, options);

  let api = wxpay.getApi()    // 拿到实例对象

  let pay = api.middleware(options.type)  // 创建回调函数

  return pay
}

然后在thinkjs的middleware.js 中间件管理中的后面插入:

// middleware.js
   const pay = require('../utils/paycallback')

   ....
   {
    handle: 'payload', // thinkjs 自己的参数转换中间件,类似于bodyParser的功能.
    options: {
      ...
    }
  },
   // 扫码回调
  {
    handle: pay,
    options: {
      type: 'nativePay'
    },
    match: '/pay/notify' // 这里根据自己的接口匹配设置,代表中间件只在这个接口起作用.
  }

然后开发 /pay/notify 接口:
中间件会帮你在ctx对象下绑定两个方法:
replyNative : 扫描成功,传入微信的预订单编号
reply : 失败,传入失败原因.

    try {
      let res = this.ctx.request
      let info = res.weixin // 中间件会将微信的参数解析到request下的weixin对象中.

      let product_id = info.product_id // 商品id,就是你生成url链接时候,传入的商品id
      let openid = info.openid        // 用户的openid  在你商户下对应微信唯一ID

      // 下面是业务代码
      // 获取商品信息.
      let product = await this.model('product_list').where({ id: product_id }).find()

      if (think.isEmpty(product)) {
        return this.ctx.reply('未找到商品')
      }
      
      // 自己数据库生成订单号.
      ....

      // 调用tenpay的unifiedOrder方法统一下单,生成预订单编号.
      let result = await api.unifiedOrder({
        out_trade_no: orderId, // 订单编号
        body: product.name, // 商品名称
        total_fee: product.amount * 100, // 我们数据库是元为单位,微信要求分为单位提交
        openid: openid,
        trade_type: 'NATIVE'
      })

      ....

      return this.ctx.replyNative(result.prepay_id) // 成功后将统一下单生成的prepay_id 通过replyNative方法返回给微信服务器
    } catch (error) {
      this.ctx.reply(error.message || '服务器错误')
    }

然后将代码发布到服务器上后,你测试用微信扫码后,会发现服务器会返回报错,原因是tenpay要求开发人员将微信的xml内容解析后直接已字符串的形式放在body下,而thinkjs的payload会把xml内容解析后,将xml变成对象的形式,放在 body.xml 下.所以我们还要设置一步让payload不要解析xml,直接把流转成文字放在body下就可以了(这是我看了tenpay的源码后才发现的...)

  {
    handle: 'payload',
    options: {
      ....
      extendTypes: {
        text: ['text/xml', 'application/xml'] // xml以text形式解析.
      }
    }
  },

第四步 用户支付后微信回调

tenpay框架的config里如果设置了notify_url地址,微信在用户支付后会回调这个接口
首先一样,让tenpay框架的中间件去处理微信解析和校验的功能

// middleware.js
....
一样要放在payload后面
// 用户支付完成通知.
  {
    handle: pay,
    options: {
      type: 'pay'
    },
    match: '/pay/complete' // 你的回调链接 同上.
  },
....

然后开发支付回调/pay/complete接口:


    try {
      let info = this.ctx.request.weixin // 拿到中间件解析内容.
      const orderModel = this.model('order')
      if (info.return_code === 'SUCCESS') {
        let orderId = info.out_trade_no // 订单号

        let total_fee = parseInt(info.total_fee) / 100  // 金额
        if (info.result_code === 'SUCCESS') { // 支付成功.
          // 交易成功.
          let order = await orderModel.where({ id: orderId }).find()
          if (think.isEmpty(order)) throw new Error('未找到订单')
          
          // 自己数据库订单处理
          ....

        } else {  // 支付失败
          ....
        }
      }
      this.ctx.reply()  // 回复消息(参数为空回复成功, 传值则为错误消息)
    } catch (error) {
      this.ctx.reply(error.message || '服务器错误')
    }

第五步 主动向微信服务器查询订单信息

到第4步已经走完了,但是总有些订单有可能因为网络/数据库原因,complete接口没有接收到或者保存失败之类的.这时候要有一个主动向服务器请求订单状态的接口,这里简单写一下提供参考:

    let code = this.query('code')
    let result = await api.orderQuery({
      out_trade_no: code
    })
    // return this.success(result)
    if (result.return_code === 'SUCCESS') {
      if (result.result_code === 'SUCCESS') {
        if (result.trade_state === 'SUCCESS') {
          // 付款成功
          let amount = parseInt(result.total_fee) / 100
          let complete_time = result.time_end.replace(/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/, '$1-$2-$3 $4:$5:$6')
          ....
          // 成功回调.
        } else {
          // 付款失败
          return this.success({
            status: 1,
            message: result.trade_state_desc,
            info: result
          })
        }
      } else {
        return this.fail(result.err_code_des)
      }
    } else {
      return this.fail(result.return_msg)
    }

postman接口测试

之前使用postman都只会设置静态的环境变量 如设置请求的域名. 这样切换就可以很方便的切换正式库和测试库.
最近由于需要做接口测试,必须先调用login接口后记录token值,然后再依次调用后面的所有的接口需要把token动态的写入.去查了postman的相关资料,终于发现在postman的测试(Tests)标签栏里是可以做接口返回数据校验已经设置环境变量当前值的.

以下是我写在login接口中的Tests脚本

pm.test("Status code is 200", function () {
    pm.response.to.have.status(200);
});

pm.test('success', function () {
    var jsonData = pm.response.json();
    pm.environment.set("token", jsonData.result.token);
    pm.expect(jsonData.code).to.eql(200);   // 返回的json code值为200
})

其他的接口里可以不设置token,单纯检测状态值返回以及json code返回

最后提一下: postman里面的runner是没法做并发测试的.

如果需要并发测试,可以参考 github开源框架
newman通过导出postman配置文件,用node进行并发测试.