【愚公系列】2022年10月 微信小程序-电商项目-微信支付小程序内部API功能实现

举报
愚公搬代码 发表于 2022/10/31 19:32:49 2022/10/31
【摘要】 前言微信支付是腾讯集团旗下的第三方支付平台,致力于为用户和企业提供安全、便捷、专业的在线支付服务。以“微信支付,不止支付”为核心理念,为个人用户创造了多种便民服务和应用场景。微信支付为各类企业以及小微商户提供专业的收款能力,运营能力,资金结算解决方案,以及安全保障。用户可以使用微信支付来购物、吃饭、旅游、就医、交水电费等。企业、商品、门店、用户已经通过微信连在了一起,让智慧生活,变成了现实...

前言

微信支付是腾讯集团旗下的第三方支付平台,致力于为用户和企业提供安全、便捷、专业的在线支付服务。以“微信支付,不止支付”为核心理念,为个人用户创造了多种便民服务和应用场景。微信支付为各类企业以及小微商户提供专业的收款能力,运营能力,资金结算解决方案,以及安全保障。用户可以使用微信支付来购物、吃饭、旅游、就医、交水电费等。企业、商品、门店、用户已经通过微信连在了一起,让智慧生活,变成了现实。

小程序实现微信支付主要有两种方式:

  • 小程序内部API,要求商户开通了小程序支付功能
  • 第三方网站

一、微信支付小程序内部API功能实现

在这里插入图片描述
统一下单接口地址:
URL地址:https://api.mch.weixin.qq.com/pay/unifiedorder
URL地址:https://api2.mch.weixin.qq.com/pay/unifiedorder

1.相关小程序代码

// miniprogram/pages/confirm-order/index.js
Page({

  /**
   * 页面的初始数据
   */
  data: {
    carts:[],
    userMessage:'',
    totalPrice:0,
    address:{
      userName:'选择'
    },
    submchPayParams: {}, 
    submchPayorderResult:{},
    prepareSubmchPay: false
  },
  /**
   * 确认订单
   */
  onSubmit(e){
    wx.showActionSheet({
      itemList: ['默认支付', '小微商户'],
      success:(res)=> {
        console.log(res.tapIndex)
        let index = res.tapIndex
        if (index == 0){// 默认支付
          this.startNormalPay(e)
        }
      },
      fail (res) {
        console.log(res.errMsg)
      }
    })
    
  },

  // 发起支付
  async startNormalPay(e) {
    if (!this.data.address.id) {
      wx.showModal({
        title: '没有选择收货地址',
        showCancel: false
      })
      return
    }
    let address = this.data.address
    let addressDesc = `${address.userName},${address.telNumber},${address.region.join('')}${address.detailInfo}`
    let carts = this.data.carts
    let goodsCartsIds = carts.map(item => item.id)
    let goodsNameDesc = carts.map(item => `${item.goods_name}${item.sku_desc})x${item.num}`).join(',')
    if (goodsNameDesc.length > 200) goodsNameDesc = goodsNameDesc.substr(0, 200) + ".."
    let data = {
      totalFee: this.data.totalPrice,
      addressId: address.id,
      addressDesc,
      goodsCartsIds,
      goodsNameDesc
    }
    let res = await wx.wxp.request4({
      url: 'http://localhost:3000/user/my/order2',
      method: 'post',
      data
    })
    console.log(res);
    let payArgs = res.data.data.params
    wx.requestPayment({
      timeStamp: payArgs.timeStamp,
      nonceStr: payArgs.nonceStr,
      package: payArgs.package,
      signType: 'MD5',
      paySign: payArgs.paySign,
      success:async res1=> {
        console.log('success', res1);
        // requestPayment:ok
        if (res1.errMsg == 'requestPayment:ok') {
          // 微信支付成功
          await wx.wxp.showModal({
            title: '支付成功',
            showCancel: false
          })
          this.removeCartsGoods(goodsCartsIds)
        } else {
          // {errMsg: "requestPayment:fail cancel"}
          wx.showModal({
            title: '支付取消或失败了,请稍后得试',
            showCancel: false,
          })
        }
      },
      fail:(err1)=> {
        console.log('fail', err1);
      }
    })

  },

  // 将已经下单的商品从购物车中移除
  async removeCartsGoods(goodsCartsIds) {
    let data = {
      ids: goodsCartsIds
    }
    let res2 = await wx.wxp.request4({
      url: 'http://localhost:3000/user/my/carts',
      method: 'delete',
      data
    })
    console.log('res2', res2);

    if (res2.data.msg == 'ok') {
      wx.switchTab({
        url: '/pages/cart/index',
      })
    } else {
      wx.showModal({
        title: '更新购物车数据失败',
        showCancel: false
      })
    }
  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
    const eventChannel = this.getOpenerEventChannel()
    eventChannel.on('cartData', (res)=> {
      // console.log(res)
      this.setData({
        carts:res.data
      })
      this.calcTotalPrice()
    })
  },
  // 准备跳转地址列表表,选取地址
  toSelectAddress(){
    wx.navigateTo({
      url: '/pages/address-list/index',
      success:res=>{
        res.eventChannel.on('selectAddress', address=>{
          address.addressInfo = address.region.join('')+address.detailInfo 
          this.setData({
            address
          })
        })
      }
    })
  },
  // 重新计算总价
  calcTotalPrice(){
    let totalPrice = 0
    let carts = this.data.carts
    carts.forEach(item=>{
      totalPrice += item.price * item.num 
    })
    this.setData({
      totalPrice
    })
  },
})
<van-cell-group>
  <van-cell bind:click="toSelectAddress" is-link icon="location-o" size="large" title="{{address.userName}}" value="{{address.telNumber}}" label="{{address.addressInfo}}" />
</van-cell-group>
<view style="padding:10px;">
  <block wx:for="{{carts}}" wx:key="id">
  <van-card custom-class="goods-card" price="{{item.price*item.num/100}}元" desc="{{item.sku_desc}}"
    title="{{item.goods_name}}" thumb="{{item.goods_image}}">
    <view slot="footer">
      <text>x{{item.num}}</text>
    </view>
  </van-card>
  </block>
</view>
<van-cell-group title=" ">
  <van-cell title="优惠" value="暂无可用" is-link />
</van-cell-group>

<van-cell-group title=" ">
  <van-cell title="配置方式" value="快递免运费" />
  <van-field model:value="{{ userMessage }}" label="买家留言" border="{{ false }}" placeholder="留言建议提前协商" />
</van-cell-group>

<van-cell-group title=" ">
  <van-cell title="商品金额" value="¥900" />
  <van-cell title="运费" value="+¥200" />
  <van-cell custom-class="total-price" title="" value="合计:¥900" />
</van-cell-group>

<van-submit-bar
  price="{{ totalPrice }}"
  button-text="提交订单"
  bind:submit="onSubmit"
/>

<!-- 小微商户支付 -->
<pay wx:if="{{ prepareSubmchPay }}" params="{{ submchPayParams }}" bindsuccess="bindPaySuccess" bindfail="bindPayFail" bindcomplete="bindPayComplete" />
{
  "navigationBarTitleText": "确认订单",
  "usingComponents": {
    "van-cell": "@vant/weapp/cell/index",
    "van-cell-group": "@vant/weapp/cell-group/index",
    "van-checkbox": "@vant/weapp/checkbox/index",
    "van-checkbox-group": "@vant/weapp/checkbox-group/index",
    "van-card": "@vant/weapp/card/index",
    "van-stepper": "@vant/weapp/stepper/index",
    "van-field": "@vant/weapp/field/index",
    "van-submit-bar": "@vant/weapp/submit-bar/index",
    "pay": "../../components/pay/index"
  }
}
/* miniprogram/pages/confirm-order/index.wxss */
.goods-card{
  background-color: #fefefe !important;
}
.goods-card .van-card__title{
  margin-top: 10px;
}
.goods-card .van-card__img {
  border-radius: 10px;
}
.goods-card-container {
  display:flex;margin:10px;background:#fefefe;
}
.goods-card-container + .goods-card-container{
  padding-top: 10px;
}
.total-price .van-cell__value{
  color: rgb(236, 176, 98);
}
page{
  padding-bottom: 100px;
}

2.pay支付组件

// componenets/xunhupay/xunhupay.js
Component({
  /**
   * 组件的属性列表
   */
  properties: {
    params: { // 支付订单参数
      type: Object,
      value: null
    },
    envVersion: {
      type: String,
      value: "release"
    }
  },

  /**
   * 组件的初始数据
   */
  data: {
    showPayModal: false,
    paying: false
  },

  /**
   * 组件的方法列表
   */
  methods: {
    setPaying(newPayingData) {
      this.setData({
        paying: newPayingData
      })
      this.triggerEvent('dataChange', { paying: newPayingData })
    },
    onTapCancel () {
      // 用户点击了支付组件外的地方(灰色地方)
      console.log(' 跳转到 xunhupay 小程序失败 - 用户点击了提醒窗体以外的地方')
      this.triggerEvent('fail', { navigateSuccess: false })
      this.triggerEvent('complete')
    },
    navigateSuccess () {
      console.log(' 跳转到 xunhupay 小程序成功')
      // 成功跳转:标记支付中状态
      this.setPaying(true)
    },
    navigateFail (e) {
      // 跳转失败
      console.log(' 跳转到 xunhupay 小程序失败 - 失败回调', e)
      this.triggerEvent('fail', { navigateSuccess: false, info: e })
      this.triggerEvent('complete')
    }
  },

  /** 
   * 组件生命周期
   */
  lifetimes: {
    // 组件显示时,自动触发小程序跳转
    attached() {
      this.setPaying(false)
      if (!this.data.params) {
        console.error(' 跳转到 xunhupay 小程序失败 - 错误:没有传递跳转参数', r)
        this.triggerEvent('fail', { error: true, navigateSuccess: false })
        this.triggerEvent('complete')
      }

      // 监听 app.onShow 事件
      wx.onAppShow(appOptions => {
        if (!this.data.paying) return;

        // 恢复支付前状态
        this.setPaying(false)
        
        if (appOptions.scene === 1038 && appOptions.referrerInfo.appId === 'wx2574b5c5ee8da56b') {
          // 来源于 xunhupay 小程序返回
          console.log('确认来源于 xunhupay 回调返回', appOptions)
          let extraData = appOptions.referrerInfo.extraData

          // if (extraData.success) { 这个地方demo中有错误,字段已经更改了,但是代码没有更新
          if (extraData.paySuccess) {
            this.triggerEvent('success', { navigateSuccess: true, info: extraData })
            this.triggerEvent('complete')
          } else {
            this.triggerEvent('fail', { navigateSuccess: true, info: extraData })
            this.triggerEvent('complete')
          }
        }
      })

      // 尝试直接跳转到 xunhupay 发起小程序支付
      wx.navigateToMiniProgram({
        appId: 'wx2574b5c5ee8da56b',
        path: 'pages/cashier/cashier',
        extraData: this.data.params,
        envVersion: this.data.envVersion,
        success: r => {
          console.log('跳转到 xunhupay 小程序成功', r)
          // 成功跳转:标记支付中状态
          this.setPaying(true)
        },
        fail: e => {
          // 跳转失败:弹出提示组件引导用户跳转
          console.log('跳转到 xunhupay 小程序失败 - 准备弹窗提醒跳转', e)
          this.setData({
            showPayModal: true
          })
        }
      })
    }
  }
})

{
  "component": true,
  "usingComponents": {}
}
<view class="bg" catchtap="onTapCancel"></view>
<view wx:if="{{ showPayModal }}" class="modal">
  <view class="content">支付需要跳转到第三方平台进行支付</view>
  <navigator class="button" target="miniProgram" app-id="wx2574b5c5ee8da56b" path="pages/cashier/cashier" extra-data="{{ params }}" version="{{ envVersion }}" bindsuccess="navigateSuccess" bindfail="navigateFail">确认跳转</navigator>
</view>
.bg {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;

  background-color: black;
  opacity: 0.5;
}

.button {
  background: none;
}

.button::after {
  border: none;
}

.modal {
  position: fixed;
  left: 10vw;
  top: 30vh;
  width: 80vw;
  height: 20vh;

  background-color: white;
  border-radius: 5rpx;

  text-align: center;
  line-height: 10vh;
}

.modal .content {
  height: 10vh;
  color: #9d9d9d;
  font-size: 28rpx;
}

.modal .button {
  height: 10vh;
  color: #3cc51f;
  font-size: 36rpx;
}

3.效果

在这里插入图片描述

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。