From ce4d0a6e3a7f51ac1467231047c9d2793a9c7443 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yusuf=20=C4=B0pek?= Date: Mon, 17 Nov 2025 17:32:58 +0300 Subject: [PATCH] fix: correct Cryptomus signature generation to use base64 encoding - Updated signature algorithm to match Cryptomus API requirements (MD5(base64(JSON) + API_KEY)) - Removed unnecessary key sorting that was incompatible with Cryptomus specification - Simplified payment flow to redirect immediately to payment URL instead of showing intermediate success state --- src/app/api/support/create-payment/route.ts | 19 +- src/app/api/support/webhook/route.ts | 14 +- src/components/SupportModal.tsx | 183 ++++++++------------ 3 files changed, 76 insertions(+), 140 deletions(-) diff --git a/src/app/api/support/create-payment/route.ts b/src/app/api/support/create-payment/route.ts index e7e257b..7499024 100644 --- a/src/app/api/support/create-payment/route.ts +++ b/src/app/api/support/create-payment/route.ts @@ -25,23 +25,12 @@ interface CryptomusResponse { } function generateSign(data: any, apiKey: string): string { - // Convert data to JSON and sort keys for consistent signature - const sortedData = Object.keys(data) - .sort() - .reduce((result: any, key: string) => { - result[key] = data[key] - return result - }, {}) + // Cryptomus signature: MD5(base64(JSON) + API_KEY) + const jsonString = JSON.stringify(data) + const base64Data = Buffer.from(jsonString).toString('base64') - const jsonString = JSON.stringify(sortedData) - - // Create MD5 hash using Web Crypto API - const encoder = new TextEncoder() - const dataBuffer = encoder.encode(jsonString + apiKey) - - // For server-side, we'll use Node.js crypto const crypto = require('crypto') - return crypto.createHash('md5').update(jsonString + apiKey).digest('hex') + return crypto.createHash('md5').update(base64Data + apiKey).digest('hex') } export async function POST(request: NextRequest) { diff --git a/src/app/api/support/webhook/route.ts b/src/app/api/support/webhook/route.ts index 6733a10..bedbb09 100644 --- a/src/app/api/support/webhook/route.ts +++ b/src/app/api/support/webhook/route.ts @@ -15,19 +15,13 @@ function verifySign(data: any, apiKey: string): boolean { // Extract sign from data and create a copy without it const { sign: receivedSign, ...dataToVerify } = data - // Sort keys and create JSON string - const sortedData = Object.keys(dataToVerify) - .sort() - .reduce((result: any, key: string) => { - result[key] = dataToVerify[key] - return result - }, {}) - - const jsonString = JSON.stringify(sortedData) + // Cryptomus signature: MD5(base64(JSON) + API_KEY) + const jsonString = JSON.stringify(dataToVerify) + const base64Data = Buffer.from(jsonString).toString('base64') // Generate expected signature const crypto = require('crypto') - const expectedSign = crypto.createHash('md5').update(jsonString + apiKey).digest('hex') + const expectedSign = crypto.createHash('md5').update(base64Data + apiKey).digest('hex') return receivedSign === expectedSign } diff --git a/src/components/SupportModal.tsx b/src/components/SupportModal.tsx index e708999..960f926 100644 --- a/src/components/SupportModal.tsx +++ b/src/components/SupportModal.tsx @@ -7,24 +7,18 @@ import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' import { useLocale } from '@/contexts/LocaleContext' -import { Loader2, Heart, AlertCircle, CheckCircle } from 'lucide-react' +import { Loader2, Heart, AlertCircle } from 'lucide-react' interface SupportModalProps { isOpen: boolean onClose: () => void } -interface PaymentData { - payment_url: string - payment_id: string -} - export function SupportModal({ isOpen, onClose }: SupportModalProps) { const [amount, setAmount] = useState('') const [currency, setCurrency] = useState('USDT') const [email, setEmail] = useState('') const [loading, setLoading] = useState(false) - const [payment, setPayment] = useState(null) const [error, setError] = useState('') const { t } = useLocale() @@ -41,7 +35,6 @@ export function SupportModal({ isOpen, onClose }: SupportModalProps) { e.preventDefault() setLoading(true) setError('') - setPayment(null) try { const response = await fetch('/api/support/create-payment', { @@ -64,7 +57,10 @@ export function SupportModal({ isOpen, onClose }: SupportModalProps) { throw new Error(data.error || 'Payment creation failed') } - setPayment(data) + // Redirect immediately to payment URL + window.open(data.payment_url, '_blank') + resetForm() + onClose() } catch (err) { setError(err instanceof Error ? err.message : 'Unknown error') } finally { @@ -76,7 +72,6 @@ export function SupportModal({ isOpen, onClose }: SupportModalProps) { setAmount('') setCurrency('USDT') setEmail('') - setPayment(null) setError('') } @@ -108,112 +103,70 @@ export function SupportModal({ isOpen, onClose }: SupportModalProps) { - {!payment ? ( -
-
- - ) => setAmount(e.target.value)} - required - /> -
- -
- - -
- -
- - ) => setEmail(e.target.value)} - /> -
- - {error && ( -
- - {error} -
- )} - - -
- ) : ( -
-
- - {t('support.payment_created')} -
- -
- -
- {payment.payment_id} -
-
- -
- -
- {amount} {currency} -
-
- - - -
- {t('support.payment_note')} -
- - +
+
+ + ) => setAmount(e.target.value)} + required + />
- )} + +
+ + +
+ +
+ + ) => setEmail(e.target.value)} + /> +
+ + {error && ( +
+ + {error} +
+ )} + + +

{t('support.secure_payment')}