nestjs 小程序支付实现

最近一直搞nestjs 成功的测试了小程序支付,在这里要强调下,小程序支付开发测试是完全可以在本地完成的。
并不需要想网上说的那样要配置生产环境等等。用微信开发工具点击支付,会出现二维码,当你的扫码一样是可以支付成功的。

基本原来在这简单介绍一下:

第一步: 前端发起请求

是客户端发起拿到后端给的统一下单的支付数据包,然后前端拿到代码像这样:

//使用uniapp的登陆模板创建的小程序
startPay() {
                const self = this
                let url = '/api/payment/pay'
                let data = {
                    id: 1
                }
                this.$http.post(url, data).then(resdata => {

                    if (resdata.nonceStr) {
                        uni.requestPayment({
                            // appId: resdata.appId,
                            nonceStr: resdata.nonceStr,
                            package: resdata.package,
                            signType: resdata.signType,
                            paySign: resdata.paySign,
                            timeStamp: resdata.timeStamp.toString(),
                            //五个字段参与签名(区分大小写):appId,nonceStr,package,signType,timeStamp(需要注意的是,这5个参数签名排序的顺序按照ASCII字典序排序)
                            success: function (res) {

                                uni.showToast({
                                    title: res,
                                    icon: 'right'
                                })

                            },
                            fail: function (e) {
                                console.log(e, '_____')
                            },
                            complete: function (e) {
                                console.log(e, '+++++')
                            }
                        })

                    } else {
                        uni.showModal({
                            title: '提示',
                            content: data.retMsg
                        })
                    }
                })

只要你返回的数据包签名这些关键的东西是正确的就ok。

第二步:后端返回正确的签名和package数据

首先准备好统一下单的数包,最终会是一个xml的格式

wxSendData(appId, productIntro, mchId, nonceStr, notifyUrl, openId, tradeId, ip, price, sign) {
    const sendData = '' +
      '' + appId + '' +
      '' + productIntro + '' +
      '' + mchId + '' +
      '' + nonceStr + '' +
      '' + notifyUrl + '' +
      '' + openId + '' +
      '' + tradeId + '' +
      '' + ip + '' +
      '' + price + '' +
      'JSAPI' +
      '' + sign + '' +
      ''
    return sendData
  }

把这个数据用 http客户端工具发给微信的后台url

https://api.mch.weixin.qq.com/pay/unifiedorder

然后会得到如下数据说明成功:

//这个数据是xml转换得来的
{ return_code: 'SUCCESS',
  return_msg: 'OK',
  appid: 'wxbdbfdessssssssa1feb25XXX',
  mch_id: '15312ssd57841',
  nonce_str: '3sKDU6oQ2nnvxP3t',
  sign: '7E437FF7ED365FDB1C16CF8A03892E38',
  result_code: 'SUCCESS',
  prepay_id: 'wx161ss75813173293688b7230361648931100',
  trade_type: 'JSAPI' }

把这个数据包的里面的两个关键数据拿出来包装下,返回给前端
(此时不用高兴地太早,你很可能会遇到各种支付签名的错误)

getPayParams(prepayId, tradeId) {
    const nonceStr = this.getNonceStr()
    const timeStamp = new Date().getTime().toString()
    const packages = 'prepay_id=' + prepayId
    const paySign = this.getPaySign(this.appid, timeStamp, nonceStr, packages)
    // 前端需要的所有数据, 都从这里返回过去
    const payParamsObj = {
      nonceStr: nonceStr,
      timeStamp: timeStamp,
      package: packages,
      paySign: paySign,
      signType: 'MD5',
      tradeId: tradeId,
    }
    return payParamsObj
  }

基本到这里就完事了,有几个关键的东西注意别填错,尤其是商户apikey。

最终上完整代码是完全可以拿过去使用的,但是希望你仔细看好代码搞懂逻辑:

import { Injectable, HttpService } from '@nestjs/common';
import * as crypto from 'crypto-js';
import * as xml2json from 'xml2json';
import { map } from 'rxjs/operators';

@Injectable()
export class PaymentService {
  constructor(private readonly httpService: HttpService) { }
  private appid = 'wxbdb18';
  private appsecret = '5cdbf0e435213b5ac';
  private mchid = '1531241';
  private mchkey = 'kajzhongy';
  private notifyUrl = 'https://xcgshi.com/api/payment/notify';
  private wxpayUrl = 'https://api.mch.weixin.qq.com/pay/unifiedorder';

  async peyPackge(openid: string) {
    let signData = {
      //签名
      appid: this.appid,
      body: 'test',
      mch_id: this.mchid,
      nonce_str: this.getNonceStr(),
      notify_url: this.notifyUrl,
      openid: openid,
      out_trade_no: this.getTradeId('kd'),
      spbill_create_ip: '127.0.0.1',
      total_fee: 1,

    };


    const sign = this.getPrePaySign(signData.appid, signData.body, signData.mch_id, signData.nonce_str, signData.notify_url, signData.openid, signData.out_trade_no, signData.spbill_create_ip, signData.total_fee)
    const sendXmlData = this.wxSendData(signData.appid, signData.body, this.mchid, signData.nonce_str, signData.notify_url, signData.openid, signData.out_trade_no, signData.spbill_create_ip, signData.total_fee, sign)
    let paydata = await this.httpService
      .post(this.wxpayUrl, sendXmlData)
      .pipe(map(response => response.data))
      .toPromise();
    paydata = JSON.parse(xml2json.toJson(paydata)).xml;
    console.log(paydata)
    const resData = this.getPayParams(paydata.prepay_id, signData.out_trade_no)
    return resData
  }


  getNonceStr() {
    var text = ""
    var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
    for (var i = 0; i < 16; i++) {
      text += possible.charAt(Math.floor(Math.random() * possible.length))
    }
    return text
  }
  getPaySign(appId, timeStamp, nonceStr, packages) {
    var stringA = 'appId=' + appId +
      '&nonceStr=' + nonceStr +
      '&package=' + packages +
      '&signType=MD5' +
      '&timeStamp=' + timeStamp

    var stringSignTemp = stringA + '&key=' + this.mchkey
    var sign = crypto.MD5(stringSignTemp).toString().toUpperCase()
    return sign
  }
  getTradeId(attach) {
    var date = new Date().getTime().toString()
    var text = ""
    var possible = "0123456789"
    for (var i = 0; i < 5; i++) {
      text += possible.charAt(Math.floor(Math.random() * possible.length))
    }
    var tradeId = 'ty_' + attach + '_' + date + text
    return tradeId
  }

  getPrePaySign(appId, productIntro, mchId, nonceStr, notifyUrl, openId, tradeId, ip, price) {
    var stringA = 'appid=' + appId +

      '&body=' + productIntro +
      '&mch_id=' + mchId +
      '&nonce_str=' + nonceStr +
      '¬ify_url=' + notifyUrl +
      '&openid=' + openId +
      '&out_trade_no=' + tradeId +
      '&spbill_create_ip=' + ip +
      '&total_fee=' + price +
      '&trade_type=JSAPI'
    var stringSignTemp = stringA + '&key=' + this.mchkey
    var sign = crypto.MD5(stringSignTemp).toString().toUpperCase()
    return sign
  }

  wxSendData(appId, productIntro, mchId, nonceStr, notifyUrl, openId, tradeId, ip, price, sign) {
    const sendData = '' +
      '' + appId + '' +
      '' + productIntro + '' +
      '' + mchId + '' +
      '' + nonceStr + '' +
      '' + notifyUrl + '' +
      '' + openId + '' +
      '' + tradeId + '' +
      '' + ip + '' +
      '' + price + '' +
      'JSAPI' +
      '' + sign + '' +
      ''
    return sendData
  }

  getPayParams(prepayId, tradeId) {
    const nonceStr = this.getNonceStr()
    const timeStamp = new Date().getTime().toString()
    const packages = 'prepay_id=' + prepayId
    const paySign = this.getPaySign(this.appid, timeStamp, nonceStr, packages)
    // 前端需要的所有数据, 都从这里返回过去
    const payParamsObj = {
      nonceStr: nonceStr,
      timeStamp: timeStamp,
      package: packages,
      paySign: paySign,
      signType: 'MD5',
      tradeId: tradeId,
    }
    return payParamsObj
  }

}


···

你可能感兴趣的:(nestjs 小程序支付实现)