expo-duooomi-app/app/pointList.native.tsx

221 lines
10 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useEffect, useState } from 'react'
import { ScrollView, Dimensions } from 'react-native'
import { Block, Text, Img } from '@/@share/components'
import { Ionicons } from '@expo/vector-icons'
import { useSafeAreaInsets } from 'react-native-safe-area-context'
import { cn } from '@/utils/cn'
import { LinearGradient } from 'expo-linear-gradient'
import { Stack, useRouter } from 'expo-router'
import Alipay from 'expo-native-alipay'
import ExpoWeChat from 'expo-wechat'
import { alipay } from '@/lib/auth'
import { ANDROID_ID, IOS_UNIVERSAL_LINK } from '@/app.constants'
import { useUserBalance } from '@/hooks/core'
const { width: SCREEN_WIDTH } = Dimensions.get('window')
const OPTION_WIDTH = (SCREEN_WIDTH - 32 - 15) / 2 // 32px padding, 15px gap
const RECHARGE_OPTIONS = [
{ id: 1, points: '500', price: '$10.00' },
{ id: 2, points: '1,000', price: '$20.00' },
{ id: 3, points: '2,500', price: '$50.00' },
{ id: 4, points: '5,000', price: '$100.00' },
]
const PAYMENT_METHODS = [
{ id: 'alipay', label: '支付宝' },
{ id: 'wechat', label: '微信' },
] as const
export default function ChargePage() {
const router = useRouter()
const insets = useSafeAreaInsets()
const [selectedOption, setSelectedOption] = useState(RECHARGE_OPTIONS[0].id)
const [paymentMethod, setPaymentMethod] = useState<'alipay' | 'wechat'>('alipay')
const balanceRes = useUserBalance()
console.log('balanceRes-------', balanceRes)
const initPay = async () => {
// 设置 支付宝 URL Schemes要表述他是宇宙唯一性可以使用 `bundle Identifier`
// scheme = `alipay` + `APPID``APPID` 为支付宝分配给开发者的应用ID
// Alipay.setAlipayScheme(IOS_UNIVERSAL_LINK)
console.log('initpay ---------', Alipay)
Alipay.setAlipayScheme(ANDROID_ID)
// ⚠️ 目前不可用,设置支付宝沙箱环境,仅 Android 支持
Alipay.setAlipaySandbox(true)
console.log('-------alipay', await Alipay.getVersion())
const wechatAppId = 'wx940e1ed91a5c303c'
const result = await ExpoWeChat.registerApp(wechatAppId, IOS_UNIVERSAL_LINK)
console.log('-------wechat', result)
}
const handlePay = async () => {
// 支付宝端支付
// payInfo 是后台拼接好的支付参数
// return_url=
// const payInfo =
// 'alipay_sdk=alipay-sdk-java-dynamicVersionNo&app_id=2021001172656340&biz_content=%7B%22out_trade_no%22%3A%221111112222222%22%2C%22total_amount%22%3A%220.01%22%2C%22subject%22%3A%221234%22%2C%22product_code%22%3A%22QUICK_MSECURITY_PAY%22%7D&charset=UTF-8&format=json&method=alipay.trade.app.pay&notify_url=http%3A%2F%2Fane.boshu.ltd%2Fowner%2Fpay%2Fapi%2FownerPay%2Fcallback&sign=oUQmGtkv8mrhJ0YwHl9%2FfxMcoLACWuSFKiMTC4Id8nc%2FZVvDQ6MLQq5hhtEN03Qn1%2BAtzTAaofE8nNixdroxOek2l5YtOAcYcXVYlJIyogN%2B22erN2NpDTWJ7tQTKgYFDJLRiG0DZJaxfADhUUF6UR9kdA8omoXKLDlP17ZPUs5Jr4aKv5HJtH5C53ui7PbmyWYg934L4UDC2F%2F9pPQlRwwDeE1SAaV3HW9Dt83kK52o8%2FlChXdotbFdAvH0d4qYGhpEYU5sepj9xiOMyL9aC4pMXW9INYLLGbvtqtlRchZTAfH5yji6nqqQm9KKMmcVrWdBDLyjFVNpejq1UjbJBw%3D%3D&sign_type=RSA2&timestamp=2020-07-09+12%3A16%3A16&version=1.0'
if (paymentMethod === 'alipay') {
const { data, error } = await alipay.appPay({
subject: '1',
totalAmount: '1',
body: '',
outTradeNo: '1111' + Date.now(),
credits: 1,
})
// const payInfo =
// 'alipay_sdk=alipay-sdk-java-dynamicVersionNo&app_id=9021000158673972&biz_content=%7B%22out_trade_no%22%3A%221111112222222%22%2C%22total_amount%22%3A%220.01%22%2C%22subject%22%3A%221234%22%2C%22product_code%22%3A%22QUICK_MSECURITY_PAY%22%7D&charset=UTF-8&format=json&method=alipay.trade.app.pay&notify_url=http%3A%2F%2Fane.boshu.ltd%2Fowner%2Fpay%2Fapi%2FownerPay%2Fcallback&sign=oUQmGtkv8mrhJ0YwHl9%2FfxMcoLACWuSFKiMTC4Id8nc%2FZVvDQ6MLQq5hhtEN03Qn1%2BAtzTAaofE8nNixdroxOek2l5YtOAcYcXVYlJIyogN%2B22erN2NpDTWJ7tQTKgYFDJLRiG0DZJaxfADhUUF6UR9kdA8omoXKLDlP17ZPUs5Jr4aKv5HJtH5C53ui7PbmyWYg934L4UDC2F%2F9pPQlRwwDeE1SAaV3HW9Dt83kK52o8%2FlChXdotbFdAvH0d4qYGhpEYU5sepj9xiOMyL9aC4pMXW9INYLLGbvtqtlRchZTAfH5yji6nqqQm9KKMmcVrWdBDLyjFVNpejq1UjbJBw%3D%3D&sign_type=RSA2&timestamp=2020-07-09+12%3A16%3A16&version=1.0'
const resule = await Alipay.pay(data.orderStr)
console.log('alipay:resule-->>>', resule)
} else if (paymentMethod === 'wechat') {
}
// console.log('data-----------', JSON.stringify(data))
// console.log('error-----------', error)
// const payInfo = 'timestamp=2025-12-24+09%3A03%3A08&app_id=9021000158673972&method=alipay.trade.app.pay&charset=utf-8&format=json&version=1.0&sign_type=RSA2&sign=UVmGNmnCzvnrIVUHkCcDh%2BJQxVW7W9rBQj7VL4OJzkXYQfj1wrb5byPsQKamQWqxMbb76YbMFo5iWjctlYxDny%2BY8RKg7eJXB7GbJWHw3eWXvZsFIUTXALQF%2BXBmuW1b%2BYy8NRy08UgeRJhp4%2FF6HCR4G33Js3TanoR%2FS1HWYns2X%2BiCY5WfnnFmq52KSm3PcYnI%2BfkgdnrbAQxWqjLWd717OnvOMyX9ydssTYUUk34p3RZchS3ltUg0EnHBy0wUs1juc4P3qAeayH2E0TwUBCpJnyTNZfQ0QFIguEFXPLW9kEgfG0dJu%2Ft0BQHdon8KnEkHF4M67wcaJl4itnV5tw%3D%3D&biz_content=%7B%22body%22%3A%22zzzzzzzz%22%2C%22out_trade_no%22%3A%221111112222222%22%2C%22passback_params%22%3A%22%257B%2522userId%2522%253A%2520%25222088721091035181%2522%252C%2520%2522credits%2522%253A%25201%257D%22%2C%22product_code%22%3A%22QUICK_MSECURITY_PAY%22%2C%22seller_id%22%3A%222088301194649043%22%2C%22subject%22%3A%221%22%2C%22timeout_express%22%3A%2290m%22%2C%22total_amount%22%3A%221%22%7D'
}
useEffect(() => {
balanceRes.load()
initPay()
}, [])
const renderHeader = () => (
<Block className="flex-row items-center justify-between px-[16px]" style={{ paddingTop: insets.top + 12, paddingBottom: 12 }}>
<Block onClick={() => router.back()} opacity={0.7} className="-ml-[8px] h-[40px] w-[40px] items-center justify-center">
<Ionicons name="chevron-back" size={24} color="white" />
</Block>
<Text className="text-[16px] font-[700] text-white"></Text>
<Block className="w-[32px]" />
</Block>
)
const renderMyPoints = () => (
<Block className="mt-[12px] items-center justify-center py-[24px]">
<Text className="text-[14px] font-[400] text-[#B0B0B0]"></Text>
<Text className="mt-[4px] text-[40px] font-[900] italic tracking-tighter text-[#F5F5F5]">{balanceRes?.balance}</Text>
</Block>
)
const renderOptions = () => (
<Block className="mt-[16px] px-[16px]">
<Text className="mb-[16px] text-[14px] font-[700] text-white"></Text>
<Block className="flex-row flex-wrap justify-between gap-y-[16px]">
{RECHARGE_OPTIONS.map((item) => {
const isSelected = selectedOption === item.id
return (
<Block
key={item.id}
onClick={() => setSelectedOption(item.id)}
opacity={0.9}
className={cn(
'relative items-center justify-center overflow-hidden rounded-[16px] border-[2px] bg-[#FFFFFF33] py-[12px]',
isSelected ? 'border-[#FFE500]' : 'border-transparent',
)}
style={{ width: OPTION_WIDTH }}
>
<Img source={require('@/assets/images/itemBg.png')} className="absolute inset-0" style={{ resizeMode: 'cover' }} />
{/* Flash Icon */}
<Block className="mb-[8px]">
<Ionicons name="flash" size={24} color="#FFD700" />
</Block>
{/* Points */}
<Text className="mb-[16px] text-[24px] font-[700] text-white">{item.points}</Text>
{/* Price Button */}
<Block className="border-[#000000]">
<LinearGradient
colors={['#393939', '#060606']}
start={{ x: 0, y: 0 }}
end={{ x: 0, y: 1 }}
style={{
borderRadius: 100,
width: 120,
height: 32,
alignItems: 'center',
justifyContent: 'center',
borderColor: '#000000',
overflow: 'hidden',
borderWidth: 1,
}}
>
<Text className="text-[12px] font-[600] text-white"> {item.price}</Text>
</LinearGradient>
</Block>
</Block>
)
})}
</Block>
</Block>
)
const renderPaymentMethods = () => (
<Block className="mt-[32px] px-[16px]">
<Text className="font-700 mb-[16px] text-[14px] text-white">:</Text>
<Block className="gap-[12px]">
{PAYMENT_METHODS.map((method) => {
const isSelected = paymentMethod === method.id
return (
<Block
key={method.id}
onClick={() => setPaymentMethod(method.id)}
opacity={0.8}
className={cn('flex-row items-center gap-[12px] rounded-full px-[20px] py-[16px]', isSelected ? 'bg-white' : 'bg-[#FFFFFF33]')}
>
<Block
className={cn('h-[20px] w-[20px] items-center justify-center rounded-full', isSelected ? 'bg-black' : 'border-[2px] border-white/50')}
>
{isSelected && <Ionicons name="checkmark" size={14} color="white" />}
</Block>
<Text className={cn('font-700 text-[16px]', isSelected ? 'text-black' : 'text-white')}>{method.label}</Text>
</Block>
)
})}
</Block>
</Block>
)
const renderBottomBar = () => (
<Block
className="absolute bottom-0 left-0 right-0 border-t-[1px] border-white/5 bg-[#1C1E22]/95 px-[16px]"
style={{ paddingBottom: insets.bottom + 12, paddingTop: 16 }}
>
<Block
onClick={handlePay}
opacity={0.8}
className="h-[52px] items-center justify-center rounded-full bg-[#FFE500] shadow-lg shadow-[#FFE500]/20"
>
<Text className="text-[16px] font-[900] italic text-black"></Text>
</Block>
<Block className="mt-[12px] flex-row justify-center">
<Text className="text-[11px] text-[#888888]"> </Text>
<Block onClick={() => console.log('Open agreement')} opacity={0.6}>
<Text className="text-[11px] text-[#FFE500] underline"></Text>
</Block>
</Block>
</Block>
)
return (
<Block className="flex-1 bg-[#21221D]">
<Stack.Screen options={{ headerShown: false }} />
{renderHeader()}
<ScrollView showsVerticalScrollIndicator={false} contentContainerStyle={{ flexGrow: 1, paddingBottom: 200 }}>
{renderMyPoints()}
{renderOptions()}
{renderPaymentMethods()}
</ScrollView>
{renderBottomBar()}
</Block>
)
}