Browse Source

微信支付

lifanagju_citu 4 months ago
parent
commit
8e87c32219
2 changed files with 244 additions and 15 deletions
  1. 67 1
      api/common.js
  2. 177 14
      pagesA/vipPackage/index.vue

+ 67 - 1
api/common.js

@@ -345,4 +345,70 @@ export const getSubscribeTemplateList = async () => {
       auth: false
     }
   })
-}
+}
+
+// 求职端交易订单  创建
+export const orderCreated = async (data) => {
+  return request({
+    url: '/app-api/menduner/system/trade/order/create',
+    method: 'POST',
+    data,
+    custom: {
+      openEncryption: true,
+      showLoading: false,
+      auth: true
+    }
+  })
+}
+
+// 求职端交易订单  创建
+export const getOrder = async (params) => {
+  return request({
+    url: '/app-api/menduner/system/trade/order/get/unpaid',
+    method: 'GET',
+    params,
+    custom: {
+      showLoading: false,
+      auth: true
+    }
+  })
+}
+
+// 获得社交用户
+export const getSocialUser = async (type) => {
+  return request({
+    url: `/app-api/menduner/system/social-user/get?type=${type}`,
+    method: 'GET',
+    // params,
+    custom: {
+      showLoading: false,
+      auth: true
+    }
+  })
+}
+
+// 提交支付订单
+export const payOrderSubmit = async (data) => {
+  return request({
+    url: '/app-api/pay/order/submit',
+    method: 'POST',
+    data,
+    custom: {
+      showLoading: false,
+      auth: true
+    }
+  })
+}
+
+// 社交绑定,使用 code 授权码
+export const socialUserBind = async (data) => {
+  return request({
+    url: '/app-api/menduner/system/social-user/bind',
+    method: 'POST',
+    data,
+    custom: {
+      showLoading: false,
+      auth: true
+    }
+  })
+}

+ 177 - 14
pagesA/vipPackage/index.vue

@@ -2,8 +2,8 @@
   <view>
     <view class="vipBox">
       <view class="avatar">
-        <img :src="getUserAvatar(baseInfo?.avatar, baseInfo?.sex)" alt="" class="img-box">
-        <image src="/static/svg/vip.svg" class="vipIcon"></image>
+        <img :src="getUserAvatar(baseInfo?.avatar, baseInfo?.sex)" alt="" class="img-box" :class="{'img-box-atc': userInfo?.vipExpireDate}">
+        <image v-if="userInfo.value?.vipExpireDate" src="/static/svg/vip.svg" class="vipIcon"></image>
       </view>
       <view class="nameBox">
         <view class="name font-weight-bold font-size-16">{{ baseInfo?.name || userInfo?.phone }}</view>
@@ -20,7 +20,7 @@
             <view
               class="card"
               :class="{ recommend: val.recommend, vipFlag: val.my, active: val.id === chooseId}"
-              @tap="handleChoose(val.id)"
+              @tap="handleChoose(val, index)"
             >
               <text>{{ val.name }}</text>
               <view>
@@ -107,11 +107,12 @@
 </template>
 
 <script setup>
-import { ref, computed, watch } from 'vue'
-
+import { ref, computed } from 'vue'
 import { getUserAvatar } from '@/utils/avatar'
 import { userStore } from '@/store/user'
 import { getMembershipPackageList } from '@/api/vip'
+// import { orderCreated, getOrder, payOrderSubmit } from '@/api/common'
+import { orderCreated, getOrder, getSocialUser, socialUserBind, payOrderSubmit } from '@/api/common'
 const useUserStore = userStore()
 
 const baseInfo = computed(() => useUserStore?.baseInfo)
@@ -153,11 +154,10 @@ const list = computed(() => {
 })
 
 const current = ref(0)
-
 const payType = ref([
   {
     name: '微信支付',
-    value: 'wxpay',
+    value: 'wx_lite',
     icon: 'icon-weixinzhifu',
     color: '#1AAD19'
   }
@@ -170,14 +170,18 @@ const payType = ref([
 ])
 
 const payTypeCurrent = ref(0)
-
+const channel = ref('')
 const radioChange = (index) => {
+  channel.value = payType[index].value
   payTypeCurrent.value = index
 }
 
-
-const handleChoose = (id) => {
-  chooseId.value = id
+const chooseIndex = ref(0)
+const chooseItem = ref(null)
+const handleChoose = (val, index) => {
+  chooseId.value = val.id
+  chooseIndex.value = index
+  chooseItem.value = val
 }
 
 const handleOpen = () => {
@@ -188,9 +192,153 @@ const handleClose = () => {
   popup.value.close()
 }
 
+// 设置 openid 到本地存储,目前只有 pay 支付时会使用
+const setOpenid = (openid) => {
+  uni.setStorageSync('openid', openid)
+}
+
+const bind = () => {
+  return new Promise(async (resolve, reject) => {
+    // 1. 获得微信 code
+    const codeResult = await uni.login()
+    if (codeResult.errMsg !== 'login:ok') {
+      return resolve(false)
+    }
+    // 2. 绑定账号 // // 社交快捷登录
+    const obj = {
+      type: socialType,
+      code: codeResult.code,
+      state: 'default',
+    }
+    const bindResult = await socialUserBind(obj);
+    if (bindResult.code === 0) {
+      setOpenid(bindResult.data)
+      return resolve(true)
+    } else {
+      return resolve(false)
+    }
+  })
+}
+
+const bindWeiXin = () => {
+  uni.showModal({
+    title: '微信支付',
+    content: '请先绑定微信再使用微信支付',
+    success: function (res) {
+      if (res.confirm) {
+        // 微信小程序绑定
+        bind()
+      }
+    },
+  });
+}
+
+const socialType = 34; // 社交类型 - 微信小程序
+
+// 预支付
+const prepay = async (channel, orderData) => { 
+  return new Promise(async (resolve, reject) => {
+    let data = {
+      id: orderData.payOrder.id,
+      channelCode: channel,
+      channelExtras: {},
+    };
+    // 特殊逻辑:微信公众号、小程序支付时,必须传入 openid
+    if (['wx_pub', 'wx_lite'].includes(channel)) {
+      const userRes = await getSocialUser(socialType)
+      const openid = userRes?.data?.openid ? userRes.data.openid : null
+      // 如果获取不到 openid,微信无法发起支付,此时需要引导
+      if (!openid) {
+        bindWeiXin()
+        return
+      }
+      data.channelExtras.openid = openid
+    }
+    // 发起预支付 API 调用
+    payOrderSubmit(data).then((res) => {
+      // 成功时
+      res.code === 0 && resolve(res)
+      // 失败时
+      if (res.code !== 0 && res.msg.indexOf('无效的openid') >= 0) {
+        // 特殊逻辑:微信公众号、小程序支付时,必须传入 openid 不正确的情况
+        if (
+          res.msg.indexOf('无效的openid') >= 0 || // 获取的 openid 不正确时,或者随便输入了个 openid
+          res.msg.indexOf('下单账号与支付账号不一致') >= 0
+        ) {
+          // https://developers.weixin.qq.com/community/develop/doc/00008c53c347804beec82aed051c00
+          bindWeiXin()
+        }
+      }
+    })
+  })
+}
+
+const weChatMiniProgramPay = async (orderData) => {
+    let res = await prepay('wx_lite', orderData); // 预支付
+    if (res?.code !== 0) {
+      return;
+    }
+    // 调用微信小程序支付
+    const payConfig = res?.data?.displayContent ? JSON.parse(res.data.displayContent) : null
+    if (!payConfig) return uni.showToast({ title: '购买失败', icon: 'none'})
+    uni.requestPayment({
+      provider: 'wxpay',
+      timeStamp: payConfig.timeStamp,
+      nonceStr: payConfig.nonceStr,
+      package: payConfig.packageValue,
+      signType: 'RSA',
+      paySign: payConfig.paySign,
+      success: (res) => {
+        popup.value.close()
+        uni.showToast({ title: '支付成功', icon: 'none'})
+        useUserStore.getUserInfo()
+        // this.payResult('success');
+      },
+      fail: (err) => {
+        if (err.errMsg === 'requestPayment:fail cancel') {
+          uni.showToast({ title: '支付已取消', icon: 'none'})
+        } else {
+          // this.payResult('fail');
+          uni.showToast({ title: '支付失败', icon: 'none'})
+        }
+      },
+    });
+  }
+
+// uni.showToast({ title: '支付升级中', icon: 'none'})
 // 支付
 const handlePay = async () => {
-  uni.showToast({ title: '支付升级中', icon: 'none'})
+  const val = chooseItem.value
+  try {
+    const res = await getOrder({
+      spuId: val.id, // 商品编号
+      type: val.type
+    })
+    if (res.data) {
+      // 获取支付码
+      weChatMiniProgramPay(res.data)
+      return
+    }
+
+    await orderCreated({
+      spuId: val.id, // 商品编号
+      spuName: val.name, // 商品名称
+      price: val.price*100, // 价格
+      type: val.type // 订单类型 0平台订单|1求职端订单|2招聘端订单|3会员套餐
+    })
+
+    const _res = await getOrder({
+      spuId: val.id, // 商品编号
+      type: val.type
+    })
+
+    // 获取支付码
+    weChatMiniProgramPay(_res.data)
+  } catch (error) {
+    console.log(error)
+  } finally {
+    val.loading = false
+  }
 }
 
 const getMemberList = async () => {
@@ -207,6 +355,8 @@ const getMemberList = async () => {
       }
       if (item.recommend) {
         recommend.value = index // 推荐套餐
+        chooseIndex.value = index
+        chooseItem.value = item
       }
       return {
         ...item,
@@ -219,17 +369,27 @@ const getMemberList = async () => {
     })
     // 低于当前套餐的(套餐)不展示
     memberList.value = vipFlagIndex ? list.slice(vipFlagIndex) : list
-    handleChoose(memberList.value[0]?.id)
+    handleChoose(memberList.value[0], recommend.value)
     if ((!userInfo.value?.vipFlag || userInfo.value?.vipExpireDate - new Date().getTime() > 0 ) && typeof recommend.value === 'number') {
       current.value = parseInt(recommend.value / 2)
       chooseId.value = memberList.value[recommend.value]?.id
+      chooseIndex.value = recommend.value
+      chooseItem.value = memberList.value[recommend.value]
     }
   } catch (error) {
     uni.showToast({ title: '查询数据失败,请重试', icon: 'none' })
   }
 }
-
 getMemberList()
+
+// const getPayMethodsList = async () => {
+//   try {
+//   } catch (error) {
+//     // uni.showToast({ title: '查询数据失败,请重试', icon: 'none' })
+//   }
+// }
+// getPayMethodsList()
+
 </script>
 
 <style lang="scss" scoped>
@@ -250,6 +410,9 @@ getMemberList()
       border-radius: 50%;
       border: 1px solid gold;
     }
+    .img-box-atc {
+      border: 1px solid gold;
+    }
     .vipIcon {
       position: absolute;
       width: 50%;