Update remote soul-content with local content
This commit is contained in:
384
components/payment-modal.tsx
Normal file
384
components/payment-modal.tsx
Normal file
@@ -0,0 +1,384 @@
|
||||
"use client"
|
||||
|
||||
import type React from "react"
|
||||
import { useState, useEffect } from "react"
|
||||
import { X, CheckCircle, Bitcoin, Globe, Copy, Check, QrCode } from "lucide-react"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { useStore } from "@/lib/store"
|
||||
|
||||
const WechatIcon = () => (
|
||||
<svg viewBox="0 0 24 24" className="w-5 h-5" fill="currentColor">
|
||||
<path d="M8.691 2.188C3.891 2.188 0 5.476 0 9.53c0 2.212 1.17 4.203 3.002 5.55a.59.59 0 0 1 .213.665l-.39 1.48c-.019.07-.048.141-.048.213 0 .163.13.295.29.295a.326.326 0 0 0 .167-.054l1.903-1.114a.864.864 0 0 1 .717-.098 10.16 10.16 0 0 0 2.837.403c.276 0 .543-.027.811-.05-.857-2.578.157-4.972 1.932-6.446 1.703-1.415 3.882-1.98 5.853-1.838-.576-3.583-4.196-6.348-8.596-6.348zM5.785 5.991c.642 0 1.162.529 1.162 1.18a1.17 1.17 0 0 1-1.162 1.178A1.17 1.17 0 0 1 4.623 7.17c0-.651.52-1.18 1.162-1.18zm5.813 0c.642 0 1.162.529 1.162 1.18a1.17 1.17 0 0 1-1.162 1.178 1.17 1.17 0 0 1-1.162-1.178c0-.651.52-1.18 1.162-1.18zm5.34 2.867c-1.797-.052-3.746.512-5.28 1.786-1.72 1.428-2.687 3.72-1.78 6.22.942 2.453 3.666 4.229 6.884 4.229.826 0 1.622-.12 2.361-.336a.722.722 0 0 1 .598.082l1.584.926a.272.272 0 0 0 .14.047c.134 0 .24-.111.24-.247 0-.06-.023-.12-.038-.177l-.327-1.233a.582.582 0 0 1-.023-.156.49.49 0 0 1 .201-.398C23.024 18.48 24 16.82 24 14.98c0-3.21-2.931-5.837-6.656-6.088V8.89c-.135-.01-.269-.03-.406-.03zm-2.53 3.274c.535 0 .969.44.969.982a.976.976 0 0 1-.969.983.976.976 0 0 1-.969-.983c0-.542.434-.982.97-.982zm4.844 0c.535 0 .969.44.969.982a.976.976 0 0 1-.969.983.976.976 0 0 1-.969-.983c0-.542.434-.982.969-.982z" />
|
||||
</svg>
|
||||
)
|
||||
|
||||
const AlipayIcon = () => (
|
||||
<svg viewBox="0 0 24 24" className="w-5 h-5" fill="currentColor">
|
||||
<path d="M20.422 13.066c-.198-.07-.405-.137-.62-.202.107-.263.204-.534.29-.814h-3.32v-.93h3.927v-.627h-3.927v-1.18h-1.637c.07-.138.131-.28.184-.425l-1.483-.326a4.091 4.091 0 0 1-.405.75h-2.78v1.181H7.72v.627h2.932v.93H7.205v.652h5.784a9.296 9.296 0 0 1-.608.814 13.847 13.847 0 0 0-2.76-.93l-.483.652c1.038.273 1.96.608 2.766 1.008a8.483 8.483 0 0 1-3.603 1.484l.43.652c1.71-.378 3.103-1.03 4.18-1.957.665.395 1.223.835 1.67 1.32l.608-.652c-.44-.43-.984-.836-1.637-1.215a9.6 9.6 0 0 0 .72-.93c.182-.264.345-.53.488-.798.587.168 1.12.35 1.598.544 1.956.8 2.82 1.614 2.82 2.665 0 .727-.587 1.277-2.21 1.277-1.193 0-2.524-.203-3.996-.609l-.103.75c1.445.378 2.843.567 4.196.567 2.158 0 3.204-.748 3.204-2.013 0-1.382-1.183-2.437-3.58-3.413z" />
|
||||
<path d="M21.714 4H2.286A2.286 2.286 0 0 0 0 6.286v11.428A2.286 2.286 0 0 0 2.286 20h19.428A2.286 2.286 0 0 0 24 17.714V6.286A2.286 2.286 0 0 0 21.714 4zM2.286 5.143h19.428c.631 0 1.143.512 1.143 1.143v8.08c-.957-.454-2.222-.903-3.75-1.346a9.8 9.8 0 0 0 .607-2.02h-4.571V9.286h5.143V8.143h-5.143V6.286H13.43v1.857H8.286v1.143h5.143V11H8.286v1.143h6.356a11.54 11.54 0 0 1-.916 1.512 16.648 16.648 0 0 0-3.3-1.12l-.576.78c1.242.328 2.348.73 3.31 1.21a10.175 10.175 0 0 1-4.317 1.78l.514.78c2.048-.454 3.718-1.237 5.008-2.344.796.472 1.464 1 2.004 1.583l.726-.78c-.527-.516-1.179-.996-1.96-1.455.327-.407.627-.839.9-1.295.264-.447.495-.907.694-1.38.7.2 1.341.412 1.916.637 2.343.96 3.378 1.935 3.378 3.195 0 .872-.703 1.532-2.647 1.532-1.43 0-3.023-.244-4.786-.732l-.123.9c1.73.454 3.407.68 5.03.68 2.585 0 3.84-.899 3.84-2.416 0-1.166-.69-2.152-2.066-2.96v-.001c-.24-.14-.495-.276-.77-.408V6.286a1.143 1.143 0 0 0-1.143-1.143z" />
|
||||
</svg>
|
||||
)
|
||||
|
||||
type PaymentMethod = "wechat" | "alipay" | "usdt" | "paypal" | "stripe" | "bank"
|
||||
|
||||
interface PaymentModalProps {
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
type: "section" | "fullbook"
|
||||
sectionId?: string
|
||||
sectionTitle?: string
|
||||
amount: number
|
||||
onSuccess: () => void
|
||||
}
|
||||
|
||||
export function PaymentModal({ isOpen, onClose, type, sectionId, sectionTitle, amount, onSuccess }: PaymentModalProps) {
|
||||
const [paymentMethod, setPaymentMethod] = useState<PaymentMethod>("alipay")
|
||||
const [isProcessing, setIsProcessing] = useState(false)
|
||||
const [isSuccess, setIsSuccess] = useState(false)
|
||||
const [showQRCode, setShowQRCode] = useState(false)
|
||||
const [copied, setCopied] = useState(false)
|
||||
|
||||
const { purchaseSection, purchaseFullBook, user, settings } = useStore()
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
setShowQRCode(false)
|
||||
setIsSuccess(false)
|
||||
setIsProcessing(false)
|
||||
}
|
||||
}, [isOpen])
|
||||
|
||||
const paymentConfig = settings?.paymentMethods || {
|
||||
wechat: { enabled: true, qrCode: "", account: "", groupQrCode: "" },
|
||||
alipay: { enabled: true, qrCode: "", account: "" },
|
||||
usdt: { enabled: true, network: "TRC20", address: "", exchangeRate: 7.2 },
|
||||
paypal: { enabled: false, email: "", exchangeRate: 7.2 },
|
||||
}
|
||||
|
||||
const usdtAmount = (amount / (paymentConfig.usdt?.exchangeRate || 7.2)).toFixed(2)
|
||||
const paypalAmount = (amount / (paymentConfig.paypal?.exchangeRate || 7.2)).toFixed(2)
|
||||
|
||||
const handleCopyAddress = (address: string) => {
|
||||
navigator.clipboard.writeText(address)
|
||||
setCopied(true)
|
||||
setTimeout(() => setCopied(false), 2000)
|
||||
}
|
||||
|
||||
const handlePayment = async () => {
|
||||
// 直接显示支付二维码/详情页面
|
||||
setShowQRCode(true)
|
||||
|
||||
// 如果有跳转链接,尝试打开
|
||||
if (paymentMethod === "wechat" && paymentConfig.wechat?.qrCode) {
|
||||
const link = paymentConfig.wechat.qrCode
|
||||
if (link.startsWith("http") || link.startsWith("weixin://")) {
|
||||
window.open(link, "_blank")
|
||||
}
|
||||
} else if (paymentMethod === "alipay" && paymentConfig.alipay?.qrCode) {
|
||||
const link = paymentConfig.alipay.qrCode
|
||||
if (link.startsWith("http") || link.startsWith("alipays://")) {
|
||||
window.open(link, "_blank")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const confirmPayment = async () => {
|
||||
setIsProcessing(true)
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000))
|
||||
|
||||
let success = false
|
||||
if (type === "section" && sectionId) {
|
||||
success = await purchaseSection(sectionId, sectionTitle, paymentMethod)
|
||||
} else if (type === "fullbook") {
|
||||
success = await purchaseFullBook(paymentMethod)
|
||||
}
|
||||
|
||||
setIsProcessing(false)
|
||||
|
||||
if (success) {
|
||||
setIsSuccess(true)
|
||||
|
||||
// 支付成功后跳转微信群
|
||||
const groupUrl = paymentConfig.wechat?.groupQrCode
|
||||
if (groupUrl) {
|
||||
setTimeout(() => {
|
||||
window.open(groupUrl, "_blank")
|
||||
}, 800)
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
onSuccess()
|
||||
onClose()
|
||||
setIsSuccess(false)
|
||||
setShowQRCode(false)
|
||||
}, 2500)
|
||||
}
|
||||
}
|
||||
|
||||
if (!isOpen) return null
|
||||
|
||||
const paymentMethods: {
|
||||
id: PaymentMethod
|
||||
name: string
|
||||
icon: React.ReactNode
|
||||
color: string
|
||||
enabled: boolean
|
||||
extra?: string
|
||||
}[] = [
|
||||
{
|
||||
id: "wechat",
|
||||
name: "微信支付",
|
||||
icon: <WechatIcon />,
|
||||
color: "bg-[#07C160]",
|
||||
enabled: paymentConfig.wechat?.enabled ?? true,
|
||||
},
|
||||
{
|
||||
id: "alipay",
|
||||
name: "支付宝",
|
||||
icon: <AlipayIcon />,
|
||||
color: "bg-[#1677FF]",
|
||||
enabled: paymentConfig.alipay?.enabled ?? true,
|
||||
},
|
||||
{
|
||||
id: "usdt",
|
||||
name: `USDT (${paymentConfig.usdt?.network || "TRC20"})`,
|
||||
icon: <Bitcoin className="w-5 h-5" />,
|
||||
color: "bg-[#26A17B]",
|
||||
enabled: paymentConfig.usdt?.enabled ?? true,
|
||||
extra: `≈ $${usdtAmount}`,
|
||||
},
|
||||
{
|
||||
id: "paypal",
|
||||
name: "PayPal",
|
||||
icon: <Globe className="w-5 h-5" />,
|
||||
color: "bg-[#003087]",
|
||||
enabled: paymentConfig.paypal?.enabled ?? false,
|
||||
extra: `≈ $${paypalAmount}`,
|
||||
},
|
||||
]
|
||||
|
||||
const availableMethods = paymentMethods.filter((m) => m.enabled)
|
||||
|
||||
// 二维码/详情页面
|
||||
if (showQRCode) {
|
||||
const isCrypto = paymentMethod === "usdt"
|
||||
const isPaypal = paymentMethod === "paypal"
|
||||
const isWechat = paymentMethod === "wechat"
|
||||
const isAlipay = paymentMethod === "alipay"
|
||||
|
||||
let address = ""
|
||||
let displayAmount = `¥${amount.toFixed(2)}`
|
||||
let title = "扫码支付"
|
||||
let hint = "支付完成后,请点击下方已完成支付按钮"
|
||||
let qrCodeUrl = ""
|
||||
|
||||
if (isCrypto) {
|
||||
address = paymentConfig.usdt?.address || ""
|
||||
displayAmount = `$${usdtAmount} USDT`
|
||||
title = "USDT支付"
|
||||
hint = "请转账到以下地址,完成后点击确认"
|
||||
} else if (isPaypal) {
|
||||
address = paymentConfig.paypal?.email || ""
|
||||
displayAmount = `$${paypalAmount} USD`
|
||||
title = "PayPal支付"
|
||||
hint = "请转账到以下PayPal账户"
|
||||
} else if (isWechat) {
|
||||
title = "微信支付"
|
||||
qrCodeUrl = paymentConfig.wechat?.qrCode || ""
|
||||
hint = "请使用微信扫码支付"
|
||||
} else if (isAlipay) {
|
||||
title = "支付宝支付"
|
||||
qrCodeUrl = paymentConfig.alipay?.qrCode || ""
|
||||
hint = "请使用支付宝扫码支付"
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center p-4">
|
||||
<div className="absolute inset-0 bg-black/70 backdrop-blur-sm" onClick={onClose} />
|
||||
<div className="relative w-full max-w-md bg-[#0f2137] rounded-2xl border border-gray-700/50 shadow-2xl overflow-hidden">
|
||||
<button onClick={onClose} className="absolute top-4 right-4 p-2 text-gray-400 hover:text-white z-10">
|
||||
<X className="w-5 h-5" />
|
||||
</button>
|
||||
|
||||
<div className="p-6 pt-12">
|
||||
{/* 标题 */}
|
||||
<h3 className="text-xl font-semibold text-white text-center mb-4">{title}</h3>
|
||||
|
||||
{/* 金额显示 */}
|
||||
<div className="text-center mb-6">
|
||||
<p className="text-3xl font-bold text-[#38bdac]">{displayAmount}</p>
|
||||
</div>
|
||||
|
||||
{/* QR Code Display */}
|
||||
{(isWechat || isAlipay) && (
|
||||
<div className="flex flex-col items-center mb-6">
|
||||
<div className="w-48 h-48 bg-white rounded-xl p-3 mb-4 flex items-center justify-center">
|
||||
{qrCodeUrl ? (
|
||||
<img
|
||||
src={qrCodeUrl || "/placeholder.svg"}
|
||||
alt="支付二维码"
|
||||
className="w-full h-full object-contain"
|
||||
/>
|
||||
) : (
|
||||
<div className="flex flex-col items-center text-gray-400">
|
||||
<QrCode className="w-16 h-16 mb-2" />
|
||||
<span className="text-sm">请在后台配置收款码</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-gray-400 text-sm">{hint}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Crypto/PayPal Address */}
|
||||
{(isCrypto || isPaypal) && (
|
||||
<div className="mb-6">
|
||||
<div className="bg-[#0a1628] rounded-xl p-4 border border-gray-700/30">
|
||||
<p className="text-gray-400 text-sm mb-2">
|
||||
{isCrypto ? `收款地址 (${paymentConfig.usdt?.network})` : "PayPal账户"}
|
||||
</p>
|
||||
<div className="flex items-center gap-2">
|
||||
<p className="text-white text-sm break-all flex-1 font-mono">{address || "请在后台配置收款地址"}</p>
|
||||
{address && (
|
||||
<button
|
||||
onClick={() => handleCopyAddress(address)}
|
||||
className="text-[#38bdac] hover:text-[#4fd4c4]"
|
||||
>
|
||||
{copied ? <Check className="w-5 h-5" /> : <Copy className="w-5 h-5" />}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-gray-500 text-sm mt-2 text-center">{hint}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 提示信息 */}
|
||||
<div className="bg-[#38bdac]/10 border border-[#38bdac]/30 rounded-xl p-3 mb-4">
|
||||
<p className="text-[#38bdac] text-sm text-center">支付完成后,系统将自动开通阅读权限</p>
|
||||
</div>
|
||||
|
||||
{/* Action Buttons */}
|
||||
<div className="flex gap-3">
|
||||
<Button
|
||||
onClick={() => setShowQRCode(false)}
|
||||
variant="outline"
|
||||
className="flex-1 border-gray-600 text-white hover:bg-gray-700/50 bg-transparent"
|
||||
>
|
||||
返回
|
||||
</Button>
|
||||
<Button
|
||||
onClick={confirmPayment}
|
||||
disabled={isProcessing}
|
||||
className="flex-1 bg-[#38bdac] hover:bg-[#2da396] text-white"
|
||||
>
|
||||
{isProcessing ? "处理中..." : "已完成支付"}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// 支付成功页面
|
||||
if (isSuccess) {
|
||||
return (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center p-4">
|
||||
<div className="absolute inset-0 bg-black/70 backdrop-blur-sm" />
|
||||
<div className="relative w-full max-w-md bg-[#0f2137] rounded-2xl border border-gray-700/50 overflow-hidden">
|
||||
<div className="p-8 text-center">
|
||||
<div className="w-20 h-20 mx-auto mb-4 rounded-full bg-[#38bdac]/20 flex items-center justify-center">
|
||||
<CheckCircle className="w-10 h-10 text-[#38bdac]" />
|
||||
</div>
|
||||
<h3 className="text-xl font-semibold text-white mb-2">支付成功</h3>
|
||||
<p className="text-gray-400">{type === "fullbook" ? "您已解锁全部内容" : "您已解锁本节内容"}</p>
|
||||
{paymentConfig.wechat?.groupQrCode && <p className="text-[#07C160] text-sm mt-4">正在跳转到微信群...</p>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// 主支付选择页面
|
||||
return (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center p-4">
|
||||
<div className="absolute inset-0 bg-black/70 backdrop-blur-sm" onClick={onClose} />
|
||||
<div className="relative w-full max-w-md bg-[#0f2137] rounded-2xl border border-gray-700/50 shadow-2xl overflow-hidden max-h-[90vh] overflow-y-auto">
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="absolute top-4 right-4 p-2 text-gray-400 hover:text-white transition-colors z-10"
|
||||
>
|
||||
<X className="w-5 h-5" />
|
||||
</button>
|
||||
|
||||
{/* Header */}
|
||||
<div className="p-6 pt-12 border-b border-gray-700/50">
|
||||
<h3 className="text-lg font-semibold text-white mb-1">确认支付</h3>
|
||||
<p className="text-gray-400 text-sm">
|
||||
{type === "fullbook" ? "购买整本书,解锁全部内容" : `购买: ${sectionTitle}`}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Amount */}
|
||||
<div className="p-6 border-b border-gray-700/50 text-center">
|
||||
<p className="text-gray-400 text-sm mb-1">支付金额</p>
|
||||
<p className="text-4xl font-bold text-white">¥{amount.toFixed(2)}</p>
|
||||
{(paymentMethod === "usdt" || paymentMethod === "paypal") && (
|
||||
<p className="text-[#38bdac] text-sm mt-1">≈ ${paymentMethod === "usdt" ? usdtAmount : paypalAmount} USD</p>
|
||||
)}
|
||||
{user?.referredBy && (
|
||||
<p className="text-[#38bdac] text-sm mt-2">
|
||||
通过邀请注册,{settings?.distributorShare || 90}%将返还给推荐人
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Payment Methods */}
|
||||
<div className="p-6 space-y-3">
|
||||
<p className="text-gray-400 text-sm mb-3">选择支付方式</p>
|
||||
{availableMethods.map((method) => (
|
||||
<button
|
||||
key={method.id}
|
||||
onClick={() => setPaymentMethod(method.id)}
|
||||
className={`w-full p-4 rounded-xl border flex items-center gap-4 transition-all ${
|
||||
paymentMethod === method.id
|
||||
? "border-[#38bdac] bg-[#38bdac]/10"
|
||||
: "border-gray-700 hover:border-gray-600 hover:bg-[#162840]"
|
||||
}`}
|
||||
>
|
||||
<div className={`w-10 h-10 rounded-lg ${method.color} flex items-center justify-center text-white`}>
|
||||
{method.icon}
|
||||
</div>
|
||||
<div className="flex-1 text-left">
|
||||
<span className="text-white">{method.name}</span>
|
||||
{method.extra && <span className="text-gray-400 text-sm ml-2">{method.extra}</span>}
|
||||
</div>
|
||||
{paymentMethod === method.id && <CheckCircle className="w-5 h-5 text-[#38bdac]" />}
|
||||
</button>
|
||||
))}
|
||||
{availableMethods.length === 0 && <p className="text-gray-500 text-center py-4">暂无可用支付方式</p>}
|
||||
</div>
|
||||
|
||||
{/* Submit Button */}
|
||||
<div className="p-6 pt-0">
|
||||
<Button
|
||||
onClick={handlePayment}
|
||||
disabled={isProcessing || availableMethods.length === 0}
|
||||
className="w-full bg-[#38bdac] hover:bg-[#2da396] text-white py-6 text-lg"
|
||||
>
|
||||
{isProcessing ? (
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-5 h-5 border-2 border-white border-t-transparent rounded-full animate-spin" />
|
||||
处理中...
|
||||
</div>
|
||||
) : (
|
||||
`确认支付 ¥${amount.toFixed(2)}`
|
||||
)}
|
||||
</Button>
|
||||
<p className="text-gray-500 text-xs text-center mt-3">支付即表示同意《用户协议》和《隐私政策》</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user