shinnosuke
5 天以前 e52934f6527027bd6008c2ade61c3083159efd4a
rencaika/pages/payment-confirm/payment-confirm.uvue
@@ -40,8 +40,8 @@
               <text class="actual-label">实际支付</text>
            </view>
            <view class="confirm-button" hover-class="confirm-button-active" @tap="confirmPayment">
               <text>确认支付</text>
            <view class="confirm-button" :class="isSubmitting ? 'confirm-button-disabled' : ''" hover-class="confirm-button-active" @tap="confirmPayment">
               <text>{{isSubmitting ? '支付中...' : '确认支付'}}</text>
            </view>
            <view class="safe-bottom"></view>
         </view>
@@ -51,7 +51,7 @@
<script setup lang="uts">
   import { safeBack } from '../../utils/navigation.uts'
   import { createAppOrder, getOrderCouponQuote } from '../../services/portal.uts'
   import { abandonPendingOrderPayment, createAppOrder, createPendingOrderPayment, getOrderCouponQuote } from '../../services/portal.uts'
   import { resolveApiUrl } from '../../services/http.uts'
   import { getSession } from '../../services/session.uts'
@@ -67,7 +67,7 @@
   const couponText = ref('无')
   const couponAmount = ref(0.0)
   const productName = ref('【川味盛宴】川菜12人餐')
   const productImage = ref('/static/food-assets/food-1.jpg')
   const productImage = ref('/static/common/image-placeholder.png')
   const salePrice = ref(138.0)
   const originalAmount = ref(266.0)
   const productDiscount = ref('6折')
@@ -80,13 +80,13 @@
   let quoteSequence = 0
   const applyProductById = (id: string) => {
      if (id == 'food-set-002') { productName.value = '【川香四溢】川菜4人餐'; productImage.value = '/static/food-assets/food-2.jpg'; salePrice.value = 118.0; originalAmount.value = 198.0; productDiscount.value = '6折'; return }
      if (id == 'food-set-002') { productName.value = '【川香四溢】川菜4人餐'; productImage.value = '/static/common/image-placeholder.png'; salePrice.value = 118.0; originalAmount.value = 198.0; productDiscount.value = '6折'; return }
      if (id == 'food-set-003') { productName.value = '【欢聚时光】招牌菜6人餐'; salePrice.value = 168.0; originalAmount.value = 248.0; productDiscount.value = '6.8折'; return }
      if (id == 'food-set-004') { productName.value = '【暖心小聚】特色菜双人餐'; productImage.value = '/static/food-assets/food-2.jpg'; salePrice.value = 88.0; originalAmount.value = 126.0; productDiscount.value = '7折'; return }
      if (id == 'food-set-004') { productName.value = '【暖心小聚】特色菜双人餐'; productImage.value = '/static/common/image-placeholder.png'; salePrice.value = 88.0; originalAmount.value = 126.0; productDiscount.value = '7折'; return }
      if (id == 'food-set-005') { productName.value = '【家庭聚餐】经典川菜8人餐'; salePrice.value = 238.0; originalAmount.value = 366.0; productDiscount.value = '6.5折'; return }
      if (id == 'food-set-006') { productName.value = '【人气必点】招牌菜4人餐'; productImage.value = '/static/food-assets/food-2.jpg'; salePrice.value = 128.0; originalAmount.value = 188.0; productDiscount.value = '6.8折'; return }
      if (id == 'food-set-006') { productName.value = '【人气必点】招牌菜4人餐'; productImage.value = '/static/common/image-placeholder.png'; salePrice.value = 128.0; originalAmount.value = 188.0; productDiscount.value = '6.8折'; return }
      if (id == 'food-set-007') { productName.value = '【周末欢聚】特色菜10人餐'; salePrice.value = 298.0; originalAmount.value = 498.0; productDiscount.value = '6折'; return }
      if (id == 'food-set-008') { productName.value = '【一人食】暖心工作餐'; productImage.value = '/static/food-assets/food-2.jpg'; salePrice.value = 39.0; originalAmount.value = 49.0; productDiscount.value = '8折'; return }
      if (id == 'food-set-008') { productName.value = '【一人食】暖心工作餐'; productImage.value = '/static/common/image-placeholder.png'; salePrice.value = 39.0; originalAmount.value = 49.0; productDiscount.value = '8折'; return }
   }
   onLoad((options: any) => {
@@ -180,25 +180,87 @@
      const selectedCouponId: any = uni.getStorageSync(couponStorageKey())
      const couponId = selectedCouponId != null ? '' + selectedCouponId : ''
      createAppOrder(merchantId.value, productId.value, couponId, createOrderIdempotencyKey.value, (data: any) => {
         isSubmitting.value = false
         uni.removeStorageSync(couponStorageKey())
         const realOrderNo = data != null && data.orderNo != null ? '' + data.orderNo : ''
         if (realOrderNo == '') {
            isSubmitting.value = false
            uni.showToast({ title: '订单创建失败,请稍后重试', icon: 'none' })
            return
         }
         if (data != null && data.paid === true) {
            isSubmitting.value = false
            uni.redirectTo({ url: '/pages/payment-success/payment-success?orderId=' + encodeURIComponent(realOrderNo) })
            return
         }
         uni.redirectTo({
            url: '/pages/pending-payment/pending-payment?orderId=' + encodeURIComponent(realOrderNo)
               + '&talentId=' + encodeURIComponent(talentId)
         })
         // 一次点击即支付:用户既然点了「确认支付」,就不要再让他到第二页点同一个按钮——
         // 建单之后立刻发起预支付并拉起收银台,只有放弃或失败才落到待支付页。
         startPayment(realOrderNo, talentId, data != null && data.priceVersion != null ? Number(data.priceVersion) : 1)
      }, (error) => {
         isSubmitting.value = false
         uni.showToast({ title: error.message, icon: 'none' })
      })
   }
   const startPayment = (orderNo: string, talentId: string, priceVersion: number) => {
      createPendingOrderPayment(orderNo, talentId, priceVersion, (data: any) => {
         if (data != null && data.paymentStatus == 'PAID') {
            openPaymentSuccess(orderNo)
            return
         }
         if (data != null && data.paymentStatus == 'PAYMENT_PROCESSING') {
            // 关单中或结果未定:落到待支付页由它等结论(那里会显示「支付处理中」并退避轮询)。
            openPendingPayment(orderNo, talentId, true)
            return
         }
         const params: any = data != null ? data.paymentParams : null
         if (params == null || params.timeStamp == null || params.nonceStr == null || params.package == null || params.paySign == null) {
            uni.showToast({ title: '支付参数获取失败,请在待支付页重试', icon: 'none' })
            openPendingPayment(orderNo, talentId, false)
            return
         }
         presentCashier(orderNo, talentId, params)
      }, (error: any) => {
         uni.showToast({ title: error.message, icon: 'none' })
         openPendingPayment(orderNo, talentId, false)
      })
   }
   const presentCashier = (orderNo: string, talentId: string, params: any) => {
      // #ifdef MP-WEIXIN
      uni.requestPayment({
         provider: 'wxpay',
         timeStamp: '' + params.timeStamp,
         nonceStr: '' + params.nonceStr,
         package: '' + params.package,
         signType: params.signType != null ? '' + params.signType : 'RSA',
         paySign: '' + params.paySign,
         success: () => openPendingPayment(orderNo, talentId, true),
         fail: (error: any) => {
            const message = error != null && error.errMsg != null ? '' + error.errMsg : ''
            if (message.indexOf('cancel') >= 0) {
               // 用户关掉了收银台:告诉服务端一声,让它立刻尝试关单(服务端不据此判定收款)。
               abandonPendingOrderPayment(orderNo, talentId, '', () => openPendingPayment(orderNo, talentId, false), () => openPendingPayment(orderNo, talentId, false))
               return
            }
            uni.showToast({ title: '支付未完成,请在待支付页继续', icon: 'none' })
            openPendingPayment(orderNo, talentId, false)
         }
      })
      // #endif
      // #ifndef MP-WEIXIN
      uni.showToast({ title: '请在微信小程序中完成支付', icon: 'none' })
      openPendingPayment(orderNo, talentId, false)
      // #endif
   }
   const openPendingPayment = (orderNo: string, talentId: string, waitingPayment: boolean) => {
      isSubmitting.value = false
      uni.redirectTo({
         url: '/pages/pending-payment/pending-payment?orderId=' + encodeURIComponent(orderNo)
            + '&talentId=' + encodeURIComponent(talentId)
            + (waitingPayment ? '&waiting=1' : '')
      })
   }
   const openPaymentSuccess = (orderNo: string) => {
      isSubmitting.value = false
      uni.redirectTo({ url: '/pages/payment-success/payment-success?orderId=' + encodeURIComponent(orderNo) })
   }
</script>
@@ -238,5 +300,6 @@
   .confirm-button { display: flex; width: 514rpx; height: 86rpx; margin: 146rpx auto 0; border-radius: 45rpx; background-image: linear-gradient(90deg, #FF4C32 0%, #FF841C 100%); justify-content: center; align-items: center; }
   .confirm-button text { color: #FFFFFF; font-size: 30rpx; font-weight: 600; }
   .confirm-button-active { opacity: .82; transform: scale(.99); }
   .confirm-button-disabled { opacity: .62; transform: none; }
   .safe-bottom { height: 44rpx; height: calc(44rpx + env(safe-area-inset-bottom)); }
</style>