Compare commits

...

2 Commits

Author SHA1 Message Date
3ce5e07573 Merge branch 'main' of https://git.nuttyreading.com/zm/taimed-international-app 2025-12-11 17:30:53 +08:00
d98e1ef024 更新:ios支付 2025-12-11 17:30:45 +08:00
8 changed files with 176 additions and 32 deletions

View File

@@ -11,7 +11,7 @@
// 保存原生 switchTab 方法 // 保存原生 switchTab 方法
const originalSwitchTab = uni.switchTab; const originalSwitchTab = uni.switchTab;
uni.switchTab = (options) => { uni.switchTab = (options) => {
if (options.url.includes('/pages/book/index') && !userStore.token) { if (options.url.includes('/pages/book/index') && !uni.getStorageSync('token')) {
uni.showModal({ uni.showModal({
title: '提示', title: '提示',
content: '请先登录后访问该页面', content: '请先登录后访问该页面',

View File

@@ -7,8 +7,8 @@ export const ENV = process.env.NODE_ENV || 'development';
*/ */
const BASE_URL_MAP = { const BASE_URL_MAP = {
development: { development: {
MAIN: 'http://192.168.110.100:9300/pb/', // 张川川 //MAIN: 'http://192.168.110.100:9300/pb/', // 张川川
//MAIN: 'https://global.nuttyreading.com/', // 线上 MAIN: 'https://global.nuttyreading.com/', // 线上
// PAYMENT: 'https://dev-pay.example.com', // 暂时用不到 // PAYMENT: 'https://dev-pay.example.com', // 暂时用不到
// CDN: 'https://cdn-dev.example.com', // 暂时用不到 // CDN: 'https://cdn-dev.example.com', // 暂时用不到
}, },

View File

@@ -358,3 +358,21 @@ export async function getUserContributionByTypeList(current : number, limit : nu
}) })
return res return res
} }
/**
* ios支付
* @param transactionId 支付交易id
* @param productId 商品id
* @param orderId 订单id
* @param receiptData 苹果返回收据
* @param customerOid 用户id
* @return
*/
export async function getIosPayment(transactionId : string, productId : string, orderId : string, receiptData : string, customerOid : string) {
const res = await mainClient.request<IApiResponse>({
url: 'Ipa/veri/',
method: 'POST',
data: { transactionId, productId, orderId, receiptData, customerOid}
})
return res
}

View File

@@ -242,7 +242,7 @@ const selectedFirstLevel = ref<number>('医学') // 当前选中的一级分类
*/ */
const handleFirstLevelClick = (item: string) => { const handleFirstLevelClick = (item: string) => {
getPrompt() getPrompt()
if(!userStore.token) return if(!uni.getStorageSync('token')) return
selectedFirstLevel.value = item selectedFirstLevel.value = item
switch (item) { switch (item) {
case '医学': case '医学':
@@ -332,7 +332,7 @@ const getSociologyCateList = async () => {
*/ */
const curseClickJump = (item: IMedicalTag) => { const curseClickJump = (item: IMedicalTag) => {
getPrompt() getPrompt()
if(!userStore.token) return if(!uni.getStorageSync('token')) return
uni.navigateTo({ uni.navigateTo({
url: `/pages/course/list/category?id=${item.id}&title=${item.title}&pid=${item.pid}&subject=${selectedFirstLevel.value}` url: `/pages/course/list/category?id=${item.id}&title=${item.title}&pid=${item.pid}&subject=${selectedFirstLevel.value}`
}) })
@@ -343,7 +343,7 @@ const curseClickJump = (item: IMedicalTag) => {
*/ */
const onPageJump = (url: string, id?: number, title?: string) => { const onPageJump = (url: string, id?: number, title?: string) => {
getPrompt() getPrompt()
if(!userStore.token) return if(!uni.getStorageSync('token')) return
let targetUrl = url let targetUrl = url
if (id !== undefined) { if (id !== undefined) {
targetUrl += `?id=${id}` targetUrl += `?id=${id}`
@@ -391,7 +391,7 @@ const getNewsList = async () => {
*/ */
const newsClick = (item: INews) => { const newsClick = (item: INews) => {
getPrompt() getPrompt()
if(!userStore.token) return if(!uni.getStorageSync('token')) return
uni.navigateTo({ uni.navigateTo({
url: `/pages/news/details?newsId=${item.id}&url=${item.url}&type=${item.type}` url: `/pages/news/details?newsId=${item.id}&url=${item.url}&type=${item.type}`
}) })
@@ -422,7 +422,8 @@ const getTryListenList = async () => {
* 登录提示语 * 登录提示语
*/ */
const getPrompt = () => { const getPrompt = () => {
if(!userStore.token) { console.log(userStore.token);
if(!uni.getStorageSync('token')) {
uni.showModal({ uni.showModal({
title: '提示', title: '提示',
content: '请先登录后访问该页面', content: '请先登录后访问该页面',
@@ -441,7 +442,7 @@ const getTryListenList = async () => {
* 统一请求所有数据 * 统一请求所有数据
*/ */
const requestAll = async () => { const requestAll = async () => {
if(userStore.token){ if(uni.getStorageSync('token')){
getLearnCourse() getLearnCourse()
} }
getMedicalTags() getMedicalTags()
@@ -470,7 +471,6 @@ onMounted(() => {
* 页面显示 * 页面显示
*/ */
onShow(() => { onShow(() => {
console.log('进来了1');
// 检查是否有固定的分类选择状态 // 检查是否有固定的分类选择状态
const fixed = uni.getStorageSync('fixed') const fixed = uni.getStorageSync('fixed')
if (fixed && currentItem.value) { if (fixed && currentItem.value) {

View File

@@ -115,7 +115,7 @@
</view> </view>
<!-- 游客体验 --> <!-- 游客体验 -->
<view class="youke-l"> <view class="youke-l" v-if="!isAndorid">
<view @click="onPageJump('/pages/course/index')"> <view @click="onPageJump('/pages/course/index')">
{{ $t('login.noLogin') }} {{ $t('login.noLogin') }}
</view> </view>
@@ -184,6 +184,7 @@ let codeTimer: any = null
// 提交点击次数 // 提交点击次数
const submitClickNum = ref(0) const submitClickNum = ref(0)
const isAndorid = ref(false)
/** /**
* 切换登录方式 * 切换登录方式
@@ -437,7 +438,18 @@ const agreeAgreements = () => {
uni.setStorageSync('Agreements_agreed', agree.value); uni.setStorageSync('Agreements_agreed', agree.value);
} }
/**
* 判断当前系统
*/
const getOS = () =>{
const oprateOs = uni.getSystemInfoSync().platform
console.log(oprateOs, 'oprateOs');
isAndorid.value = oprateOs === "android" ? true : false
console.log(isAndorid.value);
}
onMounted(() => { onMounted(() => {
getOS()
loadAgreements() loadAgreements()
}) })
</script> </script>

View File

@@ -1,5 +1,5 @@
<template> <template>
<view class="user-page" :style="{ paddingTop: getNotchHeight() + 30 + 'px' }" v-if="userStore.token"> <view class="user-page" :style="{ paddingTop: getNotchHeight() + 30 + 'px' }" v-if="userStore.token && uni.getStorageSync('token')">
<!-- 设置图标 --> <!-- 设置图标 -->
<view class="settings-icon" :style="{ top: getNotchHeight() + 30 + 'px' }" @click="goSettings"> <view class="settings-icon" :style="{ top: getNotchHeight() + 30 + 'px' }" @click="goSettings">
<wd-icon name="setting1" size="24px" color="#666" /> <wd-icon name="setting1" size="24px" color="#666" />
@@ -97,7 +97,6 @@
// 默认头像 // 默认头像
const defaultAvatar = '/static/logo.png' const defaultAvatar = '/static/logo.png'
// 用户信息 // 用户信息
const userInfo = computed(() => userStore.userInfo) const userInfo = computed(() => userStore.userInfo)
@@ -276,20 +275,17 @@
} }
onShow(() => { onShow(() => {
console.log(userInfo, 'userInfo'); if (uni.getStorageSync('token')) {
if (userStore.token) {
getData() getData()
} }
}) })
onMounted(() => { onMounted(() => {
console.log(userInfo, 'userInfo'); if (uni.getStorageSync('token')) {
if (userStore.token) {
getPlatform() getPlatform()
getHufen() getHufen()
} }
}) })
</script> </script>

View File

@@ -8,8 +8,8 @@
<view class="text">活动充值金额</view> <view class="text">活动充值金额</view>
<view class="recharge"> <view class="recharge">
<view class="recharge_block" @click="chosPric(item)" <view class="recharge_block" @click="chosPric(item)"
:class="aloneItem.priceTypeId === item.priceTypeId ? 'selected' : ''" :class="aloneItem.priceTypeId === item.priceTypeId ? 'selected' : ''" v-for="item in eventAmountList"
v-for="item in eventAmountList" :key="item.priceTypeId"> :key="item.priceTypeId">
<view class="recharge_money">NZ${{item.realMoney}}</view> <view class="recharge_money">NZ${{item.realMoney}}</view>
<view style="font-size: 26rpx;">{{item.money}}{{ $t('global.coin') }}</view> <view style="font-size: 26rpx;">{{item.money}}{{ $t('global.coin') }}</view>
<span class="activity-label" v-if="item.givejf >0">{{item.description}}</span> <span class="activity-label" v-if="item.givejf >0">{{item.description}}</span>
@@ -23,8 +23,8 @@
<view class="text">{{$t('order.rechargeAmount')}}</view> <view class="text">{{$t('order.rechargeAmount')}}</view>
<view class="recharge"> <view class="recharge">
<view class="recharge_block" @click="chosPric(item)" <view class="recharge_block" @click="chosPric(item)"
:class="aloneItem.priceTypeId === item.priceTypeId ? 'selected' : ''" :class="aloneItem.priceTypeId === item.priceTypeId ? 'selected' : ''" v-for="item in standardAmountList"
v-for="item in standardAmountList" :key="item.priceTypeId"> :key="item.priceTypeId">
<view class="recharge_money">NZ${{item.realMoney}}</view> <view class="recharge_money">NZ${{item.realMoney}}</view>
<view style="font-size: 26rpx;">{{item.money}}{{ $t('global.coin') }}</view> <view style="font-size: 26rpx;">{{item.money}}{{ $t('global.coin') }}</view>
<span class="activity-label" v-if="item.givejf >0">{{item.description}}</span> <span class="activity-label" v-if="item.givejf >0">{{item.description}}</span>
@@ -80,7 +80,7 @@
import { ref, computed, onMounted, toRefs, reactive } from 'vue' import { ref, computed, onMounted, toRefs, reactive } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { useMessage } from '@/uni_modules/wot-design-uni' import { useMessage } from '@/uni_modules/wot-design-uni'
import { getBookBuyConfigList, getAgreement, getActivityDescription, verifyGooglePay, getPlaceOrder } from '@/api/modules/user' import { getBookBuyConfigList, getAgreement, getActivityDescription, verifyGooglePay, getPlaceOrder, getIosPayment } from '@/api/modules/user'
import { useUserStore } from '@/stores/user' import { useUserStore } from '@/stores/user'
import { useThrottle } from '@/hooks/useThrottle'; import { useThrottle } from '@/hooks/useThrottle';
@@ -136,6 +136,8 @@
//正常金额数据 //正常金额数据
const standardAmountList = ref([]) const standardAmountList = ref([])
const iapChannel = ref(null)
/** /**
* 获取使用环境 * 获取使用环境
*/ */
@@ -145,6 +147,7 @@
isAndroid.value = true; isAndroid.value = true;
console.log('运行Android上') console.log('运行Android上')
} else { } else {
isAndroid.value = false;
qudao.value = 'Google' qudao.value = 'Google'
console.log('运行iOS上') console.log('运行iOS上')
} }
@@ -189,10 +192,11 @@
* 获取订单编号 * 获取订单编号
*/ */
const getPlaceOrderObj = async () => { const getPlaceOrderObj = async () => {
console.log(isAndroid.value);
const { priceTypeId, realMoney, money } = toRefs(aloneItem.value) const { priceTypeId, realMoney, money } = toRefs(aloneItem.value)
const data = { const data = {
userId: userStore.userInfo.id, // 用户di userId: userStore.userInfo.id, // 用户di
paymentMethod: '5', //支付方式4point 5google paymentMethod: isAndroid.value ? '5' : '3', //支付方式3ios 5google
orderMoney: money.value, //订单金额 orderMoney: money.value, //订单金额
realMoney: realMoney.value, //实际金额 realMoney: realMoney.value, //实际金额
come: '10', //订单来源 2医学吴门医述 10海外读书 come: '10', //订单来源 2医学吴门医述 10海外读书
@@ -200,12 +204,12 @@
productId: priceTypeId.value // 商品id productId: priceTypeId.value // 商品id
} }
try { try {
// uni.hideLoading()
const res = await getPlaceOrder(data) const res = await getPlaceOrder(data)
orderSn.value = res.orderSn orderSn.value = res.orderSn
console.log(orderSn.value, '获取订单号'); console.log(orderSn.value, '获取订单号');
uni.showLoading({ title: t('order.orderCreating') }) uni.showLoading({ title: t('order.orderCreating') })
getGooglePay() isAndroid.value ? getGooglePay() : checkProvider()
// getGooglePay()
} catch (error) { } catch (error) {
console.error('获取订单号失败', error) console.error('获取订单号失败', error)
} }
@@ -233,7 +237,121 @@
} }
/** /**
* 初始化 * 检测支付提供商判断是否支持applepay
*/
const checkProvider = () => {
uni.getProvider({
service: 'payment',
success: (res) => {
console.log('getProvider返回结果', res); // 关键日志看是否包含applepay
iapChannel.value = res.providers.find((channel) => {
return (channel.id === 'appleiap')
})
getProductInfo()
}
});
}
/**
* 查询后台配置的商品信息
*/
const getProductInfo = () => {
const id = String(aloneItem.value.priceTypeId)
iapChannel.value.requestProduct([id], (res: any) => {
console.log(res, '查询苹果后台配置的商品id');
topay(id)
}, (err: any) => {
uni.showToast({ title: '未获取到产品信息,请联系管理员', icon: 'none' });
console.error('失败', err);
});
}
/**
* 准备支付-调出支付窗口
*/
const topay = (id: string) => {
return new Promise((resolve, reject) => {
uni.hideLoading()
uni.requestPayment({
provider: 'appleiap',
orderInfo: {
productid: id,
username: orderSn.value, // 订单id
quantity: 1,
manualFinishTransaction: true
},
success: (res) => {
console.log(res, 'res-topay');
iapCheck(res);
resolve(res);
},
fail: (err) => {
console.log('支付错误', err);
restoreComplateRequest()
uni.showToast({
title: '关闭支付弹窗',
icon: 'none'
})
reject(err);
}
});
})
}
/**
* 调用后台支付-ios
*/
const iapCheck = async (res : { transactionIdentifier : string; transactionReceipt : string }) => {
// console.log(res.transactionIdentifier,res.payment.productid,res.payment.username,res.transactionReceipt,userStore.userInfo.id);
try {
const obj = await getIosPayment(res.transactionIdentifier, res.payment.productid, res.payment.username, res.transactionReceipt, userStore.userInfo.id)
console.log(obj, '校验订单')
finishTransaction(res)
} catch (error) {
console.error('校验订单失败:', error)
// 也需要释放订单,防止再次提交支付窗口拉不起来
finishTransaction(res)
}
}
/**
* 检查是否存在未关闭的订单
*/
const restoreComplateRequest = () => {
return new Promise((resolve, reject) => {
iapChannel.value.restoreCompletedTransactions({
manualFinishTransaction: true,
}, (res: unknown) => {
console.log(res, '成功-restoreCompletedTransactions');
res.map((item: any) => {
finishTransaction(item)
})
resolve(res);
}, (err : any) => {
console.log(err, '失败-restoreCompletedTransactions');
reject(err);
})
});
}
/**
* 关闭订单
*/
const finishTransaction = (trans : any) => {
iapChannel.value.finishTransaction(
trans,
(success : any) => {
console.log("关闭订单成功", success);
},
(fail : any) => {
console.log("关闭订单失败", fail);
}
);
}
/**
* 谷歌初始化
*/ */
const getGooglePay = () => { const getGooglePay = () => {
googlePay.init({ googlePay.init({

View File

@@ -55,7 +55,7 @@
provider: "weixin", provider: "weixin",
scene: "WXSceneSession", scene: "WXSceneSession",
type: 0, type: 0,
href: '', href: 'https://a.app.qq.com/o/simple.jsp?pkgname=com.cn.medicine',
title: "吴门医述", title: "吴门医述",
summary: "我正在使用吴门医述提升自己,赶紧跟我一起来体验吧!", summary: "我正在使用吴门医述提升自己,赶紧跟我一起来体验吧!",
imageUrl: "static/icon/home_icon_logo.png", imageUrl: "static/icon/home_icon_logo.png",
@@ -72,7 +72,7 @@
provider: "weixin", provider: "weixin",
scene: "WXSceneTimeline", scene: "WXSceneTimeline",
type: 0, type: 0,
href: '', href: 'https://a.app.qq.com/o/simple.jsp?pkgname=com.cn.medicine',
title: "吴门医述", title: "吴门医述",
summary: "我正在使用吴门医述提升自己,赶紧跟我一起来体验吧!", summary: "我正在使用吴门医述提升自己,赶紧跟我一起来体验吧!",
imageUrl: "static/icon/home_icon_logo.png", imageUrl: "static/icon/home_icon_logo.png",
@@ -110,7 +110,7 @@
} }
.visitor-block { .visitor-block {
padding: 40rpx 20rpx; padding: 100rpx 20rpx 40rpx 20rpx;
.visitor_img { .visitor_img {
width: 150rpx; width: 150rpx;