feat: похуйу

This commit is contained in:
2026-05-12 23:31:10 +03:00
parent d6345ee0d2
commit b7900fcdf3
7 changed files with 107 additions and 4 deletions

View File

@@ -25,7 +25,7 @@ export interface PaymentQuote {
export async function getPaymentConfig(): Promise<PaymentConfig> {
const csrf = await getCsrfToken()
const res = await fetch(`${PAYMENT_API_URL}/config`, {
const res = await fetch(`${PAYMENT_API_URL}/payment/config`, {
credentials: 'include',
headers: { 'X-CSRF-Token': csrf },
})
@@ -44,3 +44,53 @@ export async function getPaymentQuote(usdtAmount: number): Promise<PaymentQuote>
if (!res.ok) throw data
return data
}
export interface CreateOrderPayload {
usdt_amount: number
usdt_exchange_rate: number
gas_fee: number
total_price: number
}
export interface OrderResult {
status_code: number
order: {
id: string
created_at: string
updated_at: string
user_id: string
usdt_amount: string
usdt_exchange_rate: string
gas_fee: string
total_price: string
service_fee: string
status: string
client_payment_id: string
itpay_payment_qr_url_desktop: string
itpay_payment_qr_url_android: string
itpay_payment_qr_url_ios: string
itpay_payment_qr_image_desktop: string
itpay_payment_qr_image_android: string
itpay_payment_qr_image_ios: string
itpay_id: string
itpay_qr_id: string
itpay_amount: string
itpay_created_at: string
}
}
export async function createOrder(payload: CreateOrderPayload): Promise<OrderResult> {
const csrf = await getCsrfToken()
const res = await fetch(`${PAYMENT_API_URL}/order/create`, {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': csrf,
},
body: JSON.stringify(payload),
})
const data = await res.json()
if (!res.ok) throw data
return data
}

View File

@@ -0,0 +1,12 @@
import { useMutation } from '@tanstack/react-query'
import { createOrder } from '../api/paymentApi'
export function useCreateOrder() {
return useMutation({
mutationFn: createOrder,
onSuccess: (data) => {
const url = data.order.itpay_payment_qr_url_desktop
if (url) window.location.href = url
},
})
}

View File

@@ -1,3 +1,4 @@
export { usePaymentConfig } from './hooks/usePaymentConfig'
export { usePaymentQuote } from './hooks/usePaymentQuote'
export type { PaymentConfig, PaymentQuote } from './api/paymentApi'
export { useCreateOrder } from './hooks/useCreateOrder'
export type { PaymentConfig, PaymentQuote, CreateOrderPayload, OrderResult } from './api/paymentApi'

View File

@@ -174,6 +174,24 @@
border-top: 1px solid var(--glass-border);
}
.payBtn {
width: 100%;
margin-top: 24px;
padding: 18px;
border-radius: 12px;
background: var(--grad-center);
color: var(--text-primary);
font-size: 16px;
font-weight: 600;
letter-spacing: 1px;
transition: opacity 0.2s;
}
.payBtn:disabled {
opacity: 0.4;
cursor: not-allowed;
}
@media (max-width: 1024px) {
.body {
grid-template-columns: 1fr;

View File

@@ -1,7 +1,7 @@
import { useConverter } from '@widgets/currency-converter'
import { USDT_RATE, GAS_PRICE } from '@shared/config/constants'
import { useDebounce } from '@shared/lib/hooks/useDebounce'
import { usePaymentConfig, usePaymentQuote } from '@features/payment'
import { usePaymentConfig, usePaymentQuote, useCreateOrder } from '@features/payment'
import { CommissionPanel } from './CommissionPanel'
import { AgreementCheck } from './AgreementCheck'
import styles from './ConverterSection.module.css'
@@ -19,6 +19,17 @@ export function ConverterSection() {
const rubTotal = quote?.total_price ?? ''
const { mutate: submitOrder, isPending } = useCreateOrder()
function handlePay() {
submitOrder({
usdt_amount: c.numRub,
usdt_exchange_rate: 1,
gas_fee: 1,
total_price: Number(rubTotal) || 0,
})
}
return (
<div className={styles.wrap}>
<div className={styles.header}>
@@ -49,6 +60,7 @@ export function ConverterSection() {
</button>
<button
type="button"
disabled
className={styles.tab}
data-active={c.mode === 'sell' || undefined}
onClick={() => c.setMode('sell')}
@@ -116,6 +128,15 @@ export function ConverterSection() {
<div className={styles.bottom}>
<AgreementCheck checked={c.agreed} onToggle={() => c.setAgreed(!c.agreed)} />
</div>
<button
type="button"
className={styles.payBtn}
onClick={handlePay}
disabled={!rubTotal || isPending}
>
{isPending ? 'Обработка...' : 'Оплатить'}
</button>
</div>
)
}

View File

@@ -42,6 +42,7 @@ export function Converter() {
className={styles.tab}
data-active={c.mode === 'sell' || undefined}
onClick={() => c.setMode('sell')}
disabled
>
ПРОДАТЬ
</button>