Command Palette

Search for a command to run...

Интеграция Т-Касса в витрину магазина

Для интеграции платёжного провайдера Т-Касса от Т-Банка в storefront (витрину магазина) на Next.js начните с добавления необходимых UI-компонентов. Таким образом провайдер будет отображаться на странице оформления заказа наряду с другими доступными методами оплаты.

Когда пользователь выбирает Т-Касса, витрина должна вызвать метод с нужными параметрами. Это создаст платёжную сессию через API Т-Касса и подготовит покупателя к перенаправлению. После этого кнопка Place Order должна отправить пользователя на страницу оплаты Т-Касса, где он сможет выбрать предпочтительный способ оплаты.

После завершения оплаты Т-Касса одновременно отправит вебхук и перенаправит покупателя обратно в витрину. То событие, которое придёт первым, завершит корзину и создаст новый заказ в Medusa.

Для витрины на Next.js необходимо внести следующие изменения:

Шаг 1: Настройте платёжный провайдер

Чтобы сделать Т-Касса доступным в качестве способа оплаты на странице оформления заказа витрины магазина, необходимо добавить её конфигурацию в маппинг платёжных провайдеров в файле с константами вашей витрины. Этот маппинг определяет как каждый провайдер отображается в интерфейсе.

Откройте и добавьте следующий код:

Структура проекта Medusa Storefront после обновления файла с константами

src/lib/constants.tsx
1export const paymentInfoMap: Record<
2 string,
3 { title: string; icon: React.ReactNode }
4> = {
5 // ... другие провайдеры
6 pp_tkassa_tkassa: {
7 title: "T-Kassa",
8 icon: <CreditCard />,
9 },
10}
11
12// Вспомогательная функция для проверки, является ли провайдер Т-Касса
13export const isTkassa = (providerId?: string) => {
14 return providerId?.startsWith("pp_tkassa")
15}

Вы расширяете объект , добавляя в него запись . Эта запись определяет заголовок и иконку, которые будут отображаться для Т-Касса на странице оформления заказа.

Вспомогательная функция проверяет, принадлежит ли переданный к Т-Касса. Это используется при рендеринге UI-компонентов, специфичных для конкретного провайдера.


Шаг 2: Обновите настройки cookies

При подключении Т-Касса настройте политику cookie так, чтобы поддерживались междоменные редиректы. Это нужно для сохранения платёжной сессии при возврате пользователя в магазин.

Откройте и обновите конфигурацию файлов cookie следующим образом:

Структура проекта Medusa Storefront после обновления файлов cookie

src/lib/data/cookies.ts
1export const setCartId = async (cartId: string) => {
2 cookies.set("_medusa_cart_id", cartId, {
3 // ... другие настройки cookie
4 sameSite: "lax", // Переключено с режима «Strict» для междоменных редиректов
5 })
6}

Эта вспомогательная функция сохраняет идентификатор корзины в cookie с именем .

Опция установлена в значение вместо . Это изменение гарантирует, что cookie будет отправляться при кросс-доменных запросах во время процесса редиректа через Т-Касса, предотвращая потерю платёжной сессии.


Шаг 3: Инициализируйте платёжную сессию

Чтобы перенаправить покупателя в Т-Касса, платёжная сессия должна быть корректно инициализирована с обязательными параметрами, включая return URL для обоих случаев: успешной и неуспешной оплаты.

Откройте и обновите логику инициализации платежа, включив в нее данные корзины и URL возврата для Т-Касса:

Структура проекта Medusa Storefront после обновления файла для компонента оплаты

src/modules/checkout/components/payment/index.tsx
1import { getBaseURL } from "@lib/util/env"
2
3const Payment = ({
4 // ...
5}) => {
6 // ...
7 const handleSubmit = async () => {
8 setIsLoading(true)
9 try {
10 // ...
11 const countryCode = cart?.shipping_address?.country_code
12 if (!checkActiveSession) {
13 await initiatePaymentSession(cart, {
14 provider_id: selectedPaymentMethod,
15 data: {
16 SuccessURL: `${getBaseURL()}/api/capture-payment/${cart?.id}?country_code=${countryCode}`,
17 FailURL: `${getBaseURL()}/api/capture-payment/${cart?.id}?country_code=${countryCode}`,
18 cart: cart,
19 }
20 })
21 }
22 }
23 }
24}

При инициализации платёжной сессии для Т-Касса параметры и определяют, куда будет перенаправлен покупатель после попытки оплаты. Оба URL динамически формируются на основе базового URL витрины, идентификатора корзины и выбранного кода страны.

Объект включается в данные инициализации для формирования чека в соответствии с Федеральным законом № 54.


Шаг 4: Создайте кнопку оплаты

В витрине для каждого платёжного провайдера необходим отдельный компонент кнопки оплаты. Он отвечает за обработку оформления заказа после подтверждения пользователем и, используя данные платёжного сеанса, перенаправляет его на страницу оплаты Т-Касса.

Откройте и добавьте следующий код:

Структура проекта Medusa Storefront после обновления файла для компонента кнопки оплаты

src/modules/checkout/components/payment-button/index.tsx
1import {
2 // ...
3 isTkassa
4} from "@lib/constants"
5import { useRouter } from "next/navigation"
6
7const PaymentButton: React.FC<PaymentButtonProps> = ({
8 cart,
9 "data-testid": dataTestId,
10}) => {
11 // ...
12 switch (true) {
13 // ... другие проверки
14 case isTkassa(paymentSession?.provider_id):
15 return (
16 <TkassaPaymentButton
17 notReady={notReady}
18 cart={cart}
19 data-testid={dataTestId}
20 />
21 )
22 // ... другие проверки
23 }
24}
25
26// ... другие компоненты кнопок оплаты
27
28type TkassaPaymentProps = {
29 cart: HttpTypes.StoreCart
30 notReady: boolean
31 "data-testid"?: string
32}
33
34const TkassaPaymentButton: React.FC<TkassaPaymentProps> = ({
35 cart,
36 notReady,
37 "data-testid": dataTestId,
38}) => {
39 const [submitting, setSubmitting] = useState(false)
40 const [errorMessage, setErrorMessage] = useState<string | null>(null)
41 const router = useRouter()
42
43 const paymentSession = cart.payment_collection?.payment_sessions?.find(
44 session =>
45 session.provider_id === "pp_tkassa_tkassa"
46 )
47
48 const handlePayment = () => {
49 setSubmitting(true)
50 const paymentUrl = (paymentSession?.data as any).PaymentURL
51 if (paymentUrl) {
52 router.push(paymentUrl)
53 } else {
54 setErrorMessage("Payment URL отсутствует")
55 setSubmitting(false)
56 }
57 }
58
59 return (
60 <>
61 <Button
62 disabled={notReady}
63 isLoading={submitting}
64 onClick={handlePayment}
65 data-testid={dataTestId}
66 size="large"
67 >
68 Place order
69 </Button>
70 <ErrorMessage
71 error={errorMessage}
72 data-testid="tkassa-payment-error-message"
73 />
74 </>
75 )
76}

Этот компонент находит для Т-Касса в текущей корзине и извлекает , предоставленный бэкендом. При нажатии кнопки Place order покупатель перенаправляется на этот URL для завершения транзакции на странице оплаты Т-Касса.

Если отсутствует, компонент выводит сообщение об ошибке, не позволяя продолжить. Состояние обеспечивает визуальную обратную связь во время подготовки перенаправления.

Компонент определяет, принадлежит ли текущая платёжная сессия к Т-Касса, с помощью вспомогательной функции . Если да, он рендерит для обработки процесса оплаты.

Интеграция этого компонента гарантирует, что процесс оплаты через Т-Касса будет запускаться при оформлении заказа.


Шаг 5: Создайте API-роут для подтверждения платежа

После того как покупатель завершает оплату на странице Т-Касса, он перенаправляется обратно на витрину магазина. Необходимо создать API-роут, который обработает этот callback, проверит статус платежа и завершит корзину.

Создайте файл со следующим содержимым:

Структура проекта Medusa Storefront после создания файла для API route

src/app/api/capture-payment/[cartId]/route.ts
1import { NextRequest, NextResponse } from "next/server"
2import { revalidateTag } from "next/cache"
3import {
4 getCacheTag,
5 getAuthHeaders,
6 removeCartId
7} from "@lib/data/cookies"
8import { sdk } from "@lib/config"
9import { placeOrder } from "@lib/data/cart"
10
11type Params = Promise<{ cartId: string }>
12
13export async function GET(req: NextRequest, { params }: { params: Params }) {
14 const { cartId } = await params
15 const { origin, searchParams } = req.nextUrl
16
17 const countryCode = searchParams.get("country_code") || ""
18 const headers = { ...(await getAuthHeaders()) }
19
20 // Получить актуальные значения корзины
21 const cartCacheTag = await getCacheTag("carts")
22 revalidateTag(cartCacheTag)
23 const { cart } = await sdk.store.cart.retrieve(cartId, {
24 fields: "id, order_link.order_id, items.total"
25 },
26 headers
27 )
28 if (!cart) {
29 return NextResponse.redirect(`${origin}/${countryCode}`)
30 }
31
32 const orderId = (cart as unknown as Record<string, any>).order_link?.order_id
33 if (!orderId) {
34 await placeOrder(cartId)
35 // Ошибка при неавторизованном платеже
36 return NextResponse.redirect(
37 `${origin}/${countryCode}/checkout?step=review&error=payment_failed`
38 )
39 }
40
41 const orderCacheTag = await getCacheTag("orders")
42 revalidateTag(orderCacheTag)
43 removeCartId()
44 return NextResponse.redirect(
45 `${origin}/${countryCode}/order/${orderId}/confirmed`
46 )
47}

Этот API-роут обрабатывает редирект от Т-Касса после попытки оплаты. Он получает актуальное состояние корзины, чтобы убедиться, что все изменения, внесённые во время оплаты, были отражены.

Если в корзине нет связанного идентификатора заказа, обработчик роута пытается оформить заказ. В случае успеха покупатель перенаправляется на страницу подтверждения заказа. Если же при обработке корзины возникла ошибка, покупатель возвращается на страницу оформления заказа с указанием ошибки и может повторить процесс оплаты заказа.

Когда оплата проходит успешно, роут повторно валидирует кэшированные данные корзины и заказа, удаляет cookie корзины и перенаправляет покупателя на страницу подтверждения заказа. Это гарантирует корректное завершение платёжного процесса и сохранение актуальных данных в витрине.


Полный пример кода интеграции в витрину

Вы можете ознакомиться с изменениями, внесенными в стартовый шаблон Medusa Next.js Starter Template, в директории .

Полный код интеграции можно посмотреть в разделе сomparison page, откройте вкладку и изучите различия в каталоге . Или запустите в терминале:

Terminal
1git clone https://github.com/gorgojs/medusa-plugins
2cd medusa-plugins
3git diff @gorgo/medusa-payment-tkassa@0.0.1...main -- examples/payment-tkassa/medusa-storefront
Изменено 5 июня 2026 г.·Редактировать страницу