分类 默认分类 下的文章

UNI-APP 签名画板封装

前段时间公司有需要用户签名的功能开发,我针对签字封装了一个组件,方便使用,在安卓,ios,h5,微信小程序中测试通过,其他环境未进行测试.
github地址:https://github.com/aoobao/signature

核心组件所在文件 components/SignaturePad/SignaturePad.vue

组件中有使用到uview-ui的popup组件,使用前需要引入uview组件库

https://www.uviewui.com/

基本使用方法

可参考页面 pages/index/index

将组件引入到template中

<template>
  <view>
    ....
        <signature-pad ref="signature"></signature-pad>
  </view> 
</template>

js代码调用

this.$refs.signature
  .sign({
    width: '700rpx',  // 画板宽度
    height: '500rpx', // 画板高度
    color: '#000'     // 画笔宽高
  })
  .then(path => {
    console.log(path); // 图片输出路径(h5页面为base64)
  })
  .catch(e => {
    console.log('取消签名', e);
  });

效果展示

https://aoobao.github.io/signature/signature.mp4

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)
    }