feat: 苹果支付
This commit is contained in:
parent
108b96d0bf
commit
9e41d2e6a3
@ -2,17 +2,16 @@ import ReturnArrowSvg from '@/assets/icons/svg/returnArrow.svg';
|
|||||||
import StarSvg from '@/assets/icons/svg/whiteStart.svg';
|
import StarSvg from '@/assets/icons/svg/whiteStart.svg';
|
||||||
import PrivacyModal from '@/components/owner/qualification/privacy';
|
import PrivacyModal from '@/components/owner/qualification/privacy';
|
||||||
import Normal from '@/components/owner/rights/normal';
|
import Normal from '@/components/owner/rights/normal';
|
||||||
import PayTypeModal from '@/components/owner/rights/payType';
|
|
||||||
import Premium, { PayItem } from '@/components/owner/rights/premium';
|
import Premium, { PayItem } from '@/components/owner/rights/premium';
|
||||||
import ProRights from '@/components/owner/rights/proRights';
|
import ProRights from '@/components/owner/rights/proRights';
|
||||||
import { createOrder, createPayment, getPAy, payFailure, paySuccess } from '@/components/owner/rights/utils';
|
import { createOrder, createPayment, getPAy, isOrderExpired, payFailure, paySuccess } from '@/components/owner/rights/utils';
|
||||||
import { ThemedText } from '@/components/ThemedText';
|
import { ThemedText } from '@/components/ThemedText';
|
||||||
import { CreateOrder } from '@/types/personal-info';
|
import { CreateOrder } from '@/types/personal-info';
|
||||||
import { ErrorCode, getAvailablePurchases, getPurchaseHistories, ProductPurchase, useIAP } from 'expo-iap';
|
import { ErrorCode, getAvailablePurchases, getPurchaseHistories, ProductPurchase, useIAP } from 'expo-iap';
|
||||||
import { useLocalSearchParams, useRouter } from "expo-router";
|
import { useLocalSearchParams, useRouter } from "expo-router";
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Image, ScrollView, StyleSheet, TouchableOpacity, View } from 'react-native';
|
import { ActivityIndicator, Image, ScrollView, StyleSheet, TouchableOpacity, View } from 'react-native';
|
||||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||||
|
|
||||||
export default function Rights() {
|
export default function Rights() {
|
||||||
@ -30,8 +29,8 @@ export default function Rights() {
|
|||||||
finishTransaction,
|
finishTransaction,
|
||||||
validateReceipt,
|
validateReceipt,
|
||||||
} = useIAP();
|
} = useIAP();
|
||||||
// 购买方式弹窗
|
// 用户选择购买的loading
|
||||||
const [showPayType, setShowPayType] = useState<boolean>(false);
|
const [confirmLoading, setConfirmLoading] = useState<boolean>(false);
|
||||||
// 选择购买方式
|
// 选择购买方式
|
||||||
const [payChoice, setPayChoice] = useState<'ApplePay'>('ApplePay');
|
const [payChoice, setPayChoice] = useState<'ApplePay'>('ApplePay');
|
||||||
// 获取路由参数
|
// 获取路由参数
|
||||||
@ -51,14 +50,12 @@ export default function Rights() {
|
|||||||
const [premiumPay, setPremiumPay] = useState<PayItem[]>();
|
const [premiumPay, setPremiumPay] = useState<PayItem[]>();
|
||||||
const [loading, setLoading] = useState<boolean>(false);
|
const [loading, setLoading] = useState<boolean>(false);
|
||||||
|
|
||||||
// 确认支付
|
|
||||||
const [confirmPay, setConfirmPay] = useState<boolean>(false);
|
|
||||||
|
|
||||||
// 查看历史订单
|
// 查看历史订单
|
||||||
const fetchPurchaseHistory = async () => {
|
const fetchPurchaseHistory = async () => {
|
||||||
try {
|
try {
|
||||||
const purchaseHistories = await getPurchaseHistories();
|
const purchaseHistories = await getPurchaseHistories();
|
||||||
console.log('Purchase history fetched:', purchaseHistories);
|
console.log('Purchase history fetched:', purchaseHistories);
|
||||||
|
return purchaseHistories
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch purchase history:', error);
|
console.error('Failed to fetch purchase history:', error);
|
||||||
}
|
}
|
||||||
@ -85,13 +82,14 @@ export default function Rights() {
|
|||||||
const res = await requestPurchase({
|
const res = await requestPurchase({
|
||||||
request: {
|
request: {
|
||||||
ios: {
|
ios: {
|
||||||
sku: "MEMBERSHIP_PRO_QUARTERLY",
|
sku: payType,
|
||||||
andDangerouslyFinishTransactionAutomaticallyIOS: false,
|
andDangerouslyFinishTransactionAutomaticallyIOS: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
console.log('Purchase success:', res);
|
||||||
// 支付成功
|
// 支付成功
|
||||||
paySuccess(transaction_id, res?.transaction_id || "")
|
paySuccess(transaction_id, res?.transactionId || "")
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.log('Purchase failed:', error);
|
console.log('Purchase failed:', error);
|
||||||
// 支付失败
|
// 支付失败
|
||||||
@ -107,8 +105,7 @@ export default function Rights() {
|
|||||||
|
|
||||||
const initializeStore = async () => {
|
const initializeStore = async () => {
|
||||||
try {
|
try {
|
||||||
await requestProducts({ skus: ["MEMBERSHIP_PRO_QUARTERLY"], type: 'subs' });
|
await requestProducts({ skus: ["MEMBERSHIP_PRO_QUARTERLY", "MEMBERSHIP_PRO_YEARLY", "MEMBERSHIP_PRO_MONTH"], type: 'subs' });
|
||||||
console.log("subscriptions", subscriptions);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to initialize store:', error);
|
console.error('Failed to initialize store:', error);
|
||||||
}
|
}
|
||||||
@ -130,15 +127,23 @@ export default function Rights() {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// 用户确认购买时,进行 创建订单,创建支付 接口调用
|
// 用户确认购买时,进行 创建订单,创建支付 接口调用
|
||||||
useEffect(() => {
|
const confirmPurchase = async () => {
|
||||||
console.log('confirmPay', confirmPay);
|
setConfirmLoading(true);
|
||||||
if (confirmPay) {
|
const history = await fetchPurchaseHistory()
|
||||||
|
const historyIds = history?.filter((item: any) => isOrderExpired(item?.expirationDateIos))?.map((i) => { return i?.id })
|
||||||
|
if (historyIds?.includes(payType)) {
|
||||||
|
setConfirmLoading(false);
|
||||||
|
setTimeout(() => {
|
||||||
|
alert("您已购买过该权益,无需重复购买");
|
||||||
|
}, 0);
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
// 创建订单
|
// 创建订单
|
||||||
createOrder(premiumPay?.filter((item) => item.product_code === payType)?.[0]?.id || 1, 1).then((res: CreateOrder) => {
|
createOrder(premiumPay?.filter((item) => item.product_code === payType)?.[0]?.id || 1, 1).then((res: CreateOrder) => {
|
||||||
// 创建支付
|
// 创建支付
|
||||||
createPayment(res?.id || "", payChoice).then((res) => {
|
createPayment(res?.id || "", payChoice).then((res) => {
|
||||||
console.log("createPayment", res);
|
|
||||||
console.log("payType", payType);
|
|
||||||
// 苹果支付
|
// 苹果支付
|
||||||
handlePurchase(payType, res?.transaction_id || "")
|
handlePurchase(payType, res?.transaction_id || "")
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
@ -147,8 +152,12 @@ export default function Rights() {
|
|||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
console.log("createOrder", err);
|
console.log("createOrder", err);
|
||||||
})
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.log("confirmPurchase", error);
|
||||||
|
} finally {
|
||||||
|
setConfirmLoading(false);
|
||||||
}
|
}
|
||||||
}, [confirmPay]);
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (currentPurchase) {
|
if (currentPurchase) {
|
||||||
@ -157,18 +166,28 @@ export default function Rights() {
|
|||||||
}, [currentPurchase]);
|
}, [currentPurchase]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
fetchPurchaseHistory()
|
||||||
|
}, [])
|
||||||
|
|
||||||
console.log('currentPurchaseError', currentPurchaseError);
|
|
||||||
|
|
||||||
}, [currentPurchaseError]);
|
|
||||||
return (
|
return (
|
||||||
<View style={{ flex: 1 }}>
|
<View style={{ flex: 1 }}>
|
||||||
|
{/* 整个页面的中间添加一个loading */}
|
||||||
|
{confirmLoading && (
|
||||||
|
<View style={[styles.loadingContainer, { top: insets.top + 60 }]}>
|
||||||
|
<View style={styles.loadingContent}>
|
||||||
|
<ActivityIndicator size="large" color="#AC7E35" />
|
||||||
|
<ThemedText style={{ color: '#AC7E35', fontSize: 14, fontWeight: '700' }}>
|
||||||
|
{t('personal:rights.confirmLoading')}
|
||||||
|
</ThemedText>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
<ScrollView style={[styles.container, { paddingTop: insets.top, paddingBottom: insets.bottom + 80 }]}>
|
<ScrollView style={[styles.container, { paddingTop: insets.top, paddingBottom: insets.bottom + 80 }]}>
|
||||||
{/* 导航栏 */}
|
{/* 导航栏 */}
|
||||||
<View
|
<View
|
||||||
style={styles.header}
|
style={styles.header}
|
||||||
>
|
>
|
||||||
<TouchableOpacity onPress={() => { router.push('/owner') }} style={{ padding: 16 }}>
|
<TouchableOpacity onPress={() => { router.push('/owner'); setConfirmLoading(false) }} style={{ padding: 16 }}>
|
||||||
<ReturnArrowSvg />
|
<ReturnArrowSvg />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<ThemedText style={styles.headerTitle}>
|
<ThemedText style={styles.headerTitle}>
|
||||||
@ -219,12 +238,11 @@ export default function Rights() {
|
|||||||
<Premium setPayType={setPayType} setShowTerms={setShowTerms} payType={payType} premiumPay={premiumPay} loading={loading} style={{ display: userType === 'normal' ? "none" : "flex" }} />
|
<Premium setPayType={setPayType} setShowTerms={setShowTerms} payType={payType} premiumPay={premiumPay} loading={loading} style={{ display: userType === 'normal' ? "none" : "flex" }} />
|
||||||
</View>
|
</View>
|
||||||
{/* 支付方式 */}
|
{/* 支付方式 */}
|
||||||
<PayTypeModal setConfirmPay={setConfirmPay} modalVisible={showPayType} setModalVisible={setShowPayType} payChoice={payChoice} setPayChoice={setPayChoice} />
|
{/* <PayTypeModal setConfirmPay={setConfirmPay} modalVisible={showPayType} setModalVisible={setShowPayType} payChoice={payChoice} setPayChoice={setPayChoice} /> */}
|
||||||
{/* 会员权益信息 */}
|
{/* 会员权益信息 */}
|
||||||
<View style={{ flex: 1, marginBottom: 80 }}>
|
<View style={{ flex: 1, marginBottom: 80 }}>
|
||||||
<ProRights style={{ display: userType === 'normal' ? "none" : "flex" }} />
|
<ProRights style={{ display: userType === 'normal' ? "none" : "flex" }} />
|
||||||
</View>
|
</View>
|
||||||
{/* <ApplePay /> */}
|
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
{/* 付费按钮 */}
|
{/* 付费按钮 */}
|
||||||
<View style={{
|
<View style={{
|
||||||
@ -241,9 +259,7 @@ export default function Rights() {
|
|||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={styles.goPay}
|
style={styles.goPay}
|
||||||
onPress={async () => {
|
onPress={async () => {
|
||||||
setUserType('premium');
|
confirmPurchase()
|
||||||
// handlePurchase(payType);
|
|
||||||
setShowPayType(true)
|
|
||||||
}}
|
}}
|
||||||
activeOpacity={0.8}
|
activeOpacity={0.8}
|
||||||
>
|
>
|
||||||
@ -269,6 +285,23 @@ export default function Rights() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
|
loadingContent: {
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
backgroundColor: '#fff',
|
||||||
|
borderRadius: 12,
|
||||||
|
padding: 16,
|
||||||
|
},
|
||||||
|
loadingContainer: {
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
position: 'absolute',
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
zIndex: 9,
|
||||||
|
backgroundColor: 'rgba(255, 255, 255, 0.5)',
|
||||||
|
},
|
||||||
payChoice: {
|
payChoice: {
|
||||||
width: 20,
|
width: 20,
|
||||||
height: 20,
|
height: 20,
|
||||||
@ -389,4 +422,3 @@ const styles = StyleSheet.create({
|
|||||||
function validateAndGrantPurchase(purchase: ProductPurchase) {
|
function validateAndGrantPurchase(purchase: ProductPurchase) {
|
||||||
throw new Error('Function not implemented.');
|
throw new Error('Function not implemented.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,252 +0,0 @@
|
|||||||
import { ThemedText } from '@/components/ThemedText';
|
|
||||||
import { useIAP } from 'expo-iap';
|
|
||||||
import React, { useCallback, useEffect, useState } from 'react';
|
|
||||||
import { Alert, InteractionManager, Platform, View } from 'react-native';
|
|
||||||
|
|
||||||
// Define your product SKUs
|
|
||||||
const bulbPackSkus = ['dev.hyo.martie.10bulbs', 'dev.hyo.martie.30bulbs'];
|
|
||||||
const subscriptionSkus = ['dev.hyo.martie.premium'];
|
|
||||||
|
|
||||||
export default function PurchaseScreen() {
|
|
||||||
const {
|
|
||||||
connected,
|
|
||||||
products,
|
|
||||||
subscriptions,
|
|
||||||
currentPurchase,
|
|
||||||
currentPurchaseError,
|
|
||||||
requestProducts,
|
|
||||||
requestPurchase,
|
|
||||||
finishTransaction,
|
|
||||||
validateReceipt,
|
|
||||||
} = useIAP();
|
|
||||||
|
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
|
||||||
const [isReady, setIsReady] = useState(false);
|
|
||||||
|
|
||||||
// Initialize products when IAP connection is established
|
|
||||||
useEffect(() => {
|
|
||||||
if (!connected) return;
|
|
||||||
|
|
||||||
const initializeIAP = async () => {
|
|
||||||
try {
|
|
||||||
// Get both products and subscriptions
|
|
||||||
await requestProducts({ skus: bulbPackSkus, type: 'inapp' });
|
|
||||||
await requestProducts({ skus: subscriptionSkus, type: 'subs' });
|
|
||||||
setIsReady(true);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error initializing IAP:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
initializeIAP();
|
|
||||||
}, [connected, requestProducts]);
|
|
||||||
|
|
||||||
// Validate receipt helper
|
|
||||||
const handleValidateReceipt = useCallback(
|
|
||||||
async (sku: string, purchase: any) => {
|
|
||||||
try {
|
|
||||||
if (Platform.OS === 'ios') {
|
|
||||||
return await validateReceipt(sku);
|
|
||||||
} else if (Platform.OS === 'android') {
|
|
||||||
const purchaseToken = purchase.purchaseTokenAndroid;
|
|
||||||
const packageName =
|
|
||||||
purchase.packageNameAndroid || 'your.package.name';
|
|
||||||
const isSub = subscriptionSkus.includes(sku);
|
|
||||||
|
|
||||||
return await validateReceipt(sku, {
|
|
||||||
packageName,
|
|
||||||
productToken: purchaseToken,
|
|
||||||
isSub,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return { isValid: true }; // Default for unsupported platforms
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Receipt validation failed:', error);
|
|
||||||
return { isValid: false };
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[validateReceipt],
|
|
||||||
);
|
|
||||||
|
|
||||||
// Handle successful purchases
|
|
||||||
useEffect(() => {
|
|
||||||
if (currentPurchase) {
|
|
||||||
handlePurchaseUpdate(currentPurchase);
|
|
||||||
}
|
|
||||||
}, [currentPurchase]);
|
|
||||||
|
|
||||||
// Handle purchase errors
|
|
||||||
useEffect(() => {
|
|
||||||
if (currentPurchaseError) {
|
|
||||||
setIsLoading(false);
|
|
||||||
|
|
||||||
// Don't show error for user cancellation
|
|
||||||
if (currentPurchaseError.code === 'E_USER_CANCELLED') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Alert.alert(
|
|
||||||
'Purchase Error',
|
|
||||||
'Failed to complete purchase. Please try again.',
|
|
||||||
);
|
|
||||||
console.error('Purchase error:', currentPurchaseError);
|
|
||||||
}
|
|
||||||
}, [currentPurchaseError]);
|
|
||||||
|
|
||||||
const handlePurchaseUpdate = async (purchase: any) => {
|
|
||||||
try {
|
|
||||||
setIsLoading(true);
|
|
||||||
console.log('Processing purchase:', purchase);
|
|
||||||
|
|
||||||
const productId = purchase.id;
|
|
||||||
|
|
||||||
// Validate receipt on your server
|
|
||||||
const validationResult = await handleValidateReceipt(productId, purchase);
|
|
||||||
|
|
||||||
if (validationResult.isValid) {
|
|
||||||
// Determine if this is a consumable product
|
|
||||||
const isConsumable = bulbPackSkus.includes(productId);
|
|
||||||
|
|
||||||
// Finish the transaction
|
|
||||||
await finishTransaction({
|
|
||||||
purchase,
|
|
||||||
isConsumable, // Set to true for consumable products
|
|
||||||
});
|
|
||||||
|
|
||||||
// Record purchase in your database
|
|
||||||
await recordPurchaseInDatabase(purchase, productId);
|
|
||||||
|
|
||||||
// Update local state (e.g., add bulbs, enable premium features)
|
|
||||||
await updateLocalState(productId);
|
|
||||||
|
|
||||||
// Show success message
|
|
||||||
showSuccessMessage(productId);
|
|
||||||
} else {
|
|
||||||
Alert.alert(
|
|
||||||
'Validation Error',
|
|
||||||
'Purchase could not be validated. Please contact support.',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error handling purchase:', error);
|
|
||||||
Alert.alert('Error', 'Failed to process purchase.');
|
|
||||||
} finally {
|
|
||||||
setIsLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Request purchase for products
|
|
||||||
const handlePurchaseBulbs = async (productId: string) => {
|
|
||||||
if (!connected) {
|
|
||||||
Alert.alert(
|
|
||||||
'Not Connected',
|
|
||||||
'Store connection unavailable. Please try again later.',
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
setIsLoading(true);
|
|
||||||
|
|
||||||
// Platform-specific purchase request (v2.7.0+)
|
|
||||||
await requestPurchase({
|
|
||||||
request: {
|
|
||||||
ios: {
|
|
||||||
sku: productId,
|
|
||||||
andDangerouslyFinishTransactionAutomaticallyIOS: false,
|
|
||||||
},
|
|
||||||
android: {
|
|
||||||
skus: [productId],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
setIsLoading(false);
|
|
||||||
console.error('Purchase request failed:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Request purchase for subscriptions
|
|
||||||
const handlePurchaseSubscription = async (subscriptionId: string) => {
|
|
||||||
if (!connected) {
|
|
||||||
Alert.alert(
|
|
||||||
'Not Connected',
|
|
||||||
'Store connection unavailable. Please try again later.',
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
setIsLoading(true);
|
|
||||||
|
|
||||||
// Find subscription to get offer details
|
|
||||||
const subscription = subscriptions.find((s) => s.id === subscriptionId);
|
|
||||||
const subscriptionOffers = subscription?.subscriptionOfferDetails?.map(
|
|
||||||
(offer) => ({
|
|
||||||
sku: subscriptionId,
|
|
||||||
offerToken: offer.offerToken,
|
|
||||||
}),
|
|
||||||
) || [{ sku: subscriptionId, offerToken: '' }];
|
|
||||||
|
|
||||||
// Platform-specific subscription request (v2.7.0+)
|
|
||||||
await requestPurchase({
|
|
||||||
request: {
|
|
||||||
ios: {
|
|
||||||
sku: subscriptionId,
|
|
||||||
},
|
|
||||||
android: {
|
|
||||||
skus: [subscriptionId],
|
|
||||||
subscriptionOffers,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
type: 'subs',
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
setIsLoading(false);
|
|
||||||
console.error('Subscription request failed:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const recordPurchaseInDatabase = async (purchase: any, productId: string) => {
|
|
||||||
// Implement your database recording logic here
|
|
||||||
console.log('Recording purchase in database:', { purchase, productId });
|
|
||||||
};
|
|
||||||
|
|
||||||
const updateLocalState = async (productId: string) => {
|
|
||||||
// Update your local app state based on the purchase
|
|
||||||
if (bulbPackSkus.includes(productId)) {
|
|
||||||
// Add bulbs to user's account
|
|
||||||
const bulbCount = productId.includes('10bulbs') ? 10 : 30;
|
|
||||||
console.log(`Adding ${bulbCount} bulbs to user account`);
|
|
||||||
} else if (subscriptionSkus.includes(productId)) {
|
|
||||||
// Enable premium features
|
|
||||||
console.log('Enabling premium features');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const showSuccessMessage = (productId: string) => {
|
|
||||||
InteractionManager.runAfterInteractions(() => {
|
|
||||||
if (bulbPackSkus.includes(productId)) {
|
|
||||||
const bulbCount = productId.includes('10bulbs') ? 10 : 30;
|
|
||||||
Alert.alert(
|
|
||||||
'Thank You!',
|
|
||||||
`${bulbCount} bulbs have been added to your account.`,
|
|
||||||
);
|
|
||||||
} else if (subscriptionSkus.includes(productId)) {
|
|
||||||
Alert.alert(
|
|
||||||
'Thank You!',
|
|
||||||
'Premium subscription activated successfully.',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<View>
|
|
||||||
{/* Your purchase UI components */}
|
|
||||||
<ThemedText>Connection Status: {connected ? 'Connected' : 'Disconnected'}</ThemedText>
|
|
||||||
<ThemedText>Products Ready: {isReady ? 'Yes' : 'No'}</ThemedText>
|
|
||||||
{/* Add your purchase buttons and UI here */}
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -84,3 +84,18 @@ export const paySuccess = async (transaction_id: string, third_party_transaction
|
|||||||
})
|
})
|
||||||
return payment
|
return payment
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 判断订单是否过期
|
||||||
|
/**
|
||||||
|
* 判断指定时间戳是否已过期(即当前时间是否已超过该时间戳)
|
||||||
|
* @param expirationTimestamp - 过期时间戳(单位:毫秒)
|
||||||
|
* @returns boolean - true: 未过期,false: 已过期
|
||||||
|
*/
|
||||||
|
export const isOrderExpired = async (transactionDate: number) => {
|
||||||
|
// 如果没有提供过期时间,视为无效或未设置,认为“已过期”或状态未知
|
||||||
|
if (!transactionDate || isNaN(transactionDate)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const now = Date.now(); // 当前时间戳(毫秒)
|
||||||
|
return now < transactionDate;
|
||||||
|
}
|
||||||
|
|||||||
@ -113,6 +113,7 @@
|
|||||||
"weChatPay": "WeChat",
|
"weChatPay": "WeChat",
|
||||||
"apple": "Apple Pay",
|
"apple": "Apple Pay",
|
||||||
"confirm": "Confirm",
|
"confirm": "Confirm",
|
||||||
"cancel": "Cancel"
|
"cancel": "Cancel",
|
||||||
|
"confirmLoading": "Confirming..."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -113,6 +113,7 @@
|
|||||||
"weChatPay": "微信支付",
|
"weChatPay": "微信支付",
|
||||||
"apple": "苹果支付",
|
"apple": "苹果支付",
|
||||||
"confirm": "确认",
|
"confirm": "确认",
|
||||||
"cancel": "取消"
|
"cancel": "取消",
|
||||||
|
"confirmLoading": "正在购买..."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user