fix
13404089107
2026-04-27 ae13033b094e5dc99d21cdae2b40be80de739c5c
traditionalApp/pages/vip/vip.vue
@@ -115,25 +115,37 @@
            <view class="payment-methods">
               <text class="method-title">选择支付方式</text>
               <view class="method-item" @tap="selectPayment('wechat')">
               <view v-if="isIosApp" class="method-item" @tap="selectPayment('appleiap')">
                  <view class="method-left">
                     <image class="pay-icon" src="/static/image/vip/weixinPay.png" mode="aspectFit"></image>
                     <text class="method-name">微信支付</text>
                     <view class="pay-icon pay-icon--apple">A</view>
                     <text class="method-name">App Store 支付</text>
                  </view>
                  <view class="method-radio">
                     <image class="radio-img" :src="selectedPaymentMethod === 'wechat' ? '/static/image/vip/check.png' : '/static/image/vip/uncheck.png'" mode="aspectFit"></image>
                     <image class="radio-img" :src="selectedPaymentMethod === 'appleiap' ? '/static/image/vip/check.png' : '/static/image/vip/uncheck.png'" mode="aspectFit"></image>
                  </view>
               </view>
               <view class="method-item" @tap="selectPayment('alipay')">
                  <view class="method-left">
                     <image class="pay-icon" src="/static/image/vip/aliPay.png" mode="aspectFit"></image>
                     <text class="method-name">支付宝支付</text>
               <template v-else>
                  <view class="method-item" @tap="selectPayment('wechat')">
                     <view class="method-left">
                        <image class="pay-icon" src="/static/image/vip/weixinPay.png" mode="aspectFit"></image>
                        <text class="method-name">微信支付</text>
                     </view>
                     <view class="method-radio">
                        <image class="radio-img" :src="selectedPaymentMethod === 'wechat' ? '/static/image/vip/check.png' : '/static/image/vip/uncheck.png'" mode="aspectFit"></image>
                     </view>
                  </view>
                  <view class="method-radio">
                     <image class="radio-img" :src="selectedPaymentMethod === 'alipay' ? '/static/image/vip/check.png' : '/static/image/vip/uncheck.png'" mode="aspectFit"></image>
                  <view class="method-item" @tap="selectPayment('alipay')">
                     <view class="method-left">
                        <image class="pay-icon" src="/static/image/vip/aliPay.png" mode="aspectFit"></image>
                        <text class="method-name">支付宝支付</text>
                     </view>
                     <view class="method-radio">
                        <image class="radio-img" :src="selectedPaymentMethod === 'alipay' ? '/static/image/vip/check.png' : '/static/image/vip/uncheck.png'" mode="aspectFit"></image>
                     </view>
                  </view>
               </view>
               </template>
            </view>
            <view class="popup-footer">
@@ -150,8 +162,10 @@
import { computed, ref } from 'vue'
import { onLoad, onShow } from '@dcloudio/uni-app'
import { getMemberOpenPage, purchaseMember } from './service'
import { requestAppleIapPayment, isIosAppRuntime } from '@/utils/appleIap.js'
import { resolveCurrentAppUserId } from '@/utils/app-user.js'
type PaymentMethod = 'wechat' | 'alipay'
type PaymentMethod = 'wechat' | 'alipay' | 'appleiap'
type PurchaseMemberType = 'MONTH_CARD' | 'QUARTER_CARD'
@@ -160,6 +174,9 @@
   title: string
   price: number | string | null
   days: number | null
   appleProductId?: string
   productId?: string
   iosProductId?: string
}
interface MemberBenefitItem {
@@ -177,6 +194,7 @@
}
interface AppUserInfo {
   id?: number | null
   nickname?: string
   phone?: string
   avatar?: string
@@ -198,7 +216,8 @@
const statusBarHeight = ref(20)
const currentPlan = ref('')
const showPaymentPopup = ref(false)
const selectedPaymentMethod = ref<PaymentMethod>('alipay')
const isIosApp = isIosAppRuntime()
const selectedPaymentMethod = ref<PaymentMethod>(isIosApp ? 'appleiap' : 'alipay')
const purchaseSubmitting = ref(false)
const memberOpenPage = ref<MemberOpenPageData>(createDefaultMemberOpenPage())
const appUserInfo = ref<AppUserInfo>(createDefaultAppUserInfo())
@@ -409,6 +428,9 @@
      })
      return
   }
   if (isIosApp) {
      selectedPaymentMethod.value = 'appleiap'
   }
   showPaymentPopup.value = true
}
@@ -463,6 +485,19 @@
   }
   purchaseSubmitting.value = true
   try {
      if (selectedPaymentMethod.value === 'appleiap') {
         const response = await submitAppleIapMemberPurchase(memberType)
         if (response?.code !== 0 || !response.data) {
            return
         }
         applyPurchaseResult(response.data)
         showPaymentPopup.value = false
         uni.showToast({
            title: '开通成功',
            icon: 'none',
         })
         return
      }
      const response = await purchaseMember({
         memberType,
         paymentMethod: selectedPaymentMethod.value === 'wechat' ? 'WECHAT' : 'ALIPAY',
@@ -479,6 +514,38 @@
   } finally {
      purchaseSubmitting.value = false
   }
}
/**
 * 拉起苹果内购后,将交易凭证回传会员购买接口完成验单。
 */
async function submitAppleIapMemberPurchase(memberType: PurchaseMemberType) {
   const appleProductId = resolveMemberAppleProductId(selectedPlan.value)
   if (!appleProductId) {
      uni.showToast({
         title: '当前套餐未配置苹果内购商品',
         icon: 'none',
      })
      return null
   }
   const iapResult = await requestAppleIapPayment({
      productId: appleProductId,
      username: resolveAppleIapUsername(),
   })
   if (!normalizeOptionalText(iapResult?.transactionReceipt)) {
      uni.showToast({
         title: '苹果内购凭证缺失',
         icon: 'none',
      })
      return null
   }
   return purchaseMember({
      memberType,
      paymentMethod: 'APPLE_IAP',
      appleProductId,
      transactionReceipt: iapResult.transactionReceipt,
      transactionIdentifier: iapResult.transactionIdentifier,
   })
}
/**
@@ -506,6 +573,21 @@
   }
   uni.setStorageSync('appUserInfo', nextUserInfo)
   syncCachedUserInfo()
}
function resolveMemberAppleProductId(plan?: MemberOpenPackage | null) {
   if (!plan) {
      return ''
   }
   return normalizeOptionalText(plan.appleProductId || plan.productId || plan.iosProductId)
}
function resolveAppleIapUsername() {
   const userId = resolveCurrentAppUserId()
   if (userId > 0) {
      return String(userId)
   }
   return normalizeOptionalText(appUserInfo.value.phone) || 'guest'
}
/**
@@ -1068,6 +1150,19 @@
      width: 100rpx;
      height: 68.57rpx;
   }
   .pay-icon--apple {
      width: 68rpx;
      height: 68rpx;
      border-radius: 16rpx;
      background: #111111;
      color: #ffffff;
      display: flex;
      align-items: center;
      justify-content: center;
      font-size: 34rpx;
      font-weight: 600;
   }
}
.method-name {