diff --git a/package-lock.json b/package-lock.json index 4e803fe..ab65428 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "no-copy-frontend", - "version": "0.44.0", + "version": "0.49.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "no-copy-frontend", - "version": "0.44.0", + "version": "0.49.0", "dependencies": { "@a2seven/yoo-checkout": "^1.1.4", "@react-input/mask": "^2.0.4", @@ -39,6 +39,7 @@ "eslint": "^9", "eslint-config-next": "16.0.3", "sass": "^1.94.2", + "types-yoomoneycheckoutwidget": "^0.0.2", "typescript": "^5" } }, @@ -7732,6 +7733,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/types-yoomoneycheckoutwidget": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/types-yoomoneycheckoutwidget/-/types-yoomoneycheckoutwidget-0.0.2.tgz", + "integrity": "sha512-Vab30GrWEE5qq65CbBcJW5deFjJ9jRVlmh+ZneaUsqnPKPlWHGwhA1ukx+o70LjqEdgK+HgMkxNlFsqchLX7ww==", + "dev": true, + "license": "MIT" + }, "node_modules/typescript": { "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", diff --git a/package.json b/package.json index ee396ab..7b1edf1 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "eslint": "^9", "eslint-config-next": "16.0.3", "sass": "^1.94.2", + "types-yoomoneycheckoutwidget": "^0.0.2", "typescript": "^5" } } diff --git a/src/app/actions/yooKassa.ts b/src/app/actions/yooKassa.ts index 0e95728..7ef69b5 100644 --- a/src/app/actions/yooKassa.ts +++ b/src/app/actions/yooKassa.ts @@ -3,6 +3,7 @@ import { YooCheckout, ICreatePayment } from '@a2seven/yoo-checkout'; import { getSessionData, deleteSession } from '@/app/actions/session'; import { API_BASE_URL } from '@/app/actions/definitions'; +import { id } from 'zod/v4/locales'; const checkout = new YooCheckout({ shopId: '1276731', secretKey: 'test_0Yns_0NHV5GJf6ypJ5HC4NSfnLO8SJkw-1PwrVWsDl4' }); @@ -36,6 +37,7 @@ export async function makePaymentOperation(email: string, tariffId: number, oper } export async function yooKasaCreatePayment(amount: number, tariff: number, operationType: 'TARIFF' | 'TOKEN') { + console.log('yooKasaCreatePayment'); const userEmail = await getSessionData('email'); const timestamp = Date.now(); @@ -51,21 +53,37 @@ export async function yooKasaCreatePayment(amount: number, tariff: number, opera value: amount.toLocaleString().replace(/\s/g, ''), currency: 'RUB' }, - payment_method_data: { + /* payment_method_data: { type: 'bank_card' - }, + }, */ + /* payment_method_id: "3138d708-000f-5001-9000-19865cfcca68", */ confirmation: { - type: 'redirect', - return_url: 'test' + type: 'embedded', + /* type: 'redirect', + return_url: 'test' */ }, metadata: { userMail: userEmail, tariff: tariff }, + merchant_customer_id: userEmail, capture: true }; try { + const payment = await checkout.createPayment(createPayload, idempotenceKey); + const response = await makePaymentOperation(userEmail, tariff, payment.id, operationType); + console.log(payment); + if (payment && response) { + return payment?.confirmation.confirmation_token; + } else { + return null + } + } catch (error) { + return null + } + + /* try { const payment = await checkout.createPayment(createPayload, idempotenceKey); const response = await makePaymentOperation(userEmail, tariff, payment.id, operationType); if (response && payment) { @@ -75,7 +93,7 @@ export async function yooKasaCreatePayment(amount: number, tariff: number, opera } } catch (error) { return null - } + } */ } export async function fetchPaymentsHistory() { diff --git a/src/app/ui/payment/YooMoneyWidget.tsx b/src/app/ui/payment/YooMoneyWidget.tsx new file mode 100644 index 0000000..025d6ac --- /dev/null +++ b/src/app/ui/payment/YooMoneyWidget.tsx @@ -0,0 +1,107 @@ +import { useCallback, useEffect } from 'react'; +import { + YooMoneyCheckoutWidget, + YooMoneyCheckoutWidgetConfig, + YooMoneyErrorCallbackResult +} from "types-yoomoneycheckoutwidget"; + +let isLoading = false; +let isInited = false; +export default function YooWidget({ config, onComplete, onSuccess, onFail, onModalClose }: { + /** + * Описание https://yookassa.ru/developers/payment-acceptance/integration-scenarios/widget/reference#initialization-parameters + */ + config: YooMoneyCheckoutWidgetConfig, + /** после успешной оплаты*/ + onComplete?: () => void, + /** после успешной оплаты, только если не подписались на onComplete*/ + onSuccess?: () => void, + /** только если вы отключили обработку неуспешных попыток в виджете: пользователь выбрал любой способ оплаты, но платеж не прошел; закончился срок действия токена.*/ + onFail?: () => void, + /** после закрытия модального окна платежной формы если customization.modal==true */ + onModalClose?: () => void, +}) { + const isModal = config?.customization?.modal; + let checkout: YooMoneyCheckoutWidget; + const destroy = () => { + console.log('checkout.destroy'); + checkout.destroy(); + isInited = false; + } + + const initializeWidget = useCallback( + () => { + isInited = true; + isLoading = false; + if (!isModal) { + const userErrorCallBack = config.error_callback; + config.error_callback = (error: YooMoneyErrorCallbackResult) => { + userErrorCallBack(error); + isInited = false; + }; + } + checkout = new window.YooMoneyCheckoutWidget(config); + console.log('checkout.create'); + checkout.on('complete', (result) => { + console.log('on complete', result); + onComplete && onComplete(); + destroy(); + }); + onSuccess && checkout.on('success', (result) => { + console.log('on success', result); + onSuccess(); + destroy(); + }); + onFail && checkout.on('fail', (result) => { + console.warn('on fail', result); + onFail(); + destroy(); + }); + + if (config.customization?.modal) { + checkout.on('modal_close', (result) => { + console.log('on modal_close', result); + onModalClose && onModalClose(); + destroy(); + }); + } + checkout.render(config.customization?.modal ? undefined : 'payment-form'); + + }, [config, onSuccess]); + + useEffect(() => { + console.log('isInited'); + console.log(isInited); + + if (!isLoading && !isInited) { + if (!window.YooMoneyCheckoutWidget) { + isLoading = true; + console.log('load script'); + const script = document.createElement('script'); + script.src = 'https://yookassa.ru/checkout-widget/v1/checkout-widget.js'; + script.async = true; + script.onload = initializeWidget; + document.head.appendChild(script); + return () => { + console.log('unload script'); + document.head.removeChild(script); + }; + } else { + console.log('init without loading'); + initializeWidget(); + } + } + + return () => { + if (!isInited && !isModal) { + destroy(); + } + } + }); + + return ( + <> + {isModal ? '' :
} + > + ); +} \ No newline at end of file diff --git a/src/app/ui/payment/payment-tab-tariffs.tsx b/src/app/ui/payment/payment-tab-tariffs.tsx index b914972..df3324e 100644 --- a/src/app/ui/payment/payment-tab-tariffs.tsx +++ b/src/app/ui/payment/payment-tab-tariffs.tsx @@ -7,6 +7,8 @@ import { useTranslations } from 'next-intl'; import { yooKasaCreatePayment } from '@/app/actions/yooKassa'; import { useState, ReactNode, useEffect } from 'react'; import ModalWindow from '@/app/components/ModalWindow'; +import YooMoneyWidget from './YooMoneyWidget'; +import { YooMoneyCheckoutWidgetConfig, YooMoneyErrorCallbackResult } from "types-yoomoneycheckoutwidget"; interface Tariff { id: number; @@ -50,7 +52,7 @@ export default function PaymentTabTariffs() { } | null>(null); const [isProcessing, setIsProcessing] = useState