Medusa plugins
Y

Yookassa

YooKassa payment provider for Medusa

Need customizations for this plugin?

Get in touch
npm install medusa-payment-yookassa
Category
payment
Built by
sergei-kudinov
Type
unknown
Last updated
3 weeks ago
Monthly downloads
373
Github stars
14

Возможности

  • 🔗  Бесшовная интеграция с платёжной системой YooKassa
  • 🧾  Формирование онлайн-чеков в соответствии с 54-ФЗ
  • 1️⃣  Одностадийные (автосписание) и  2️⃣  двухстадийные (авторизация/холдирование) сценарии оплаты
  • 🔄  Возвраты и отмена заказов
  • 🔔  Вебхук-уведомления о статусах платежей в реальном времени
  • 🛡  Проверка вебхуков для обеспечения безопасности
  • 🔍  Подробное логирование для отладки

💬  Чат поддержки плагина YooKassa

Есть вопросы или идеи по новым функциям плагина?
Присоединяйтесь к чату в Telegram – @medusajs_yookassa

👥  Чат сообщества Medusa.js

Общайтесь в Telegram с другими разработчиками Medusa – @medusajs_chat

Требования

Установка

yarn add medusa-payment-yookassa
# или
npm install medusa-payment-yookassa

Настройка

Добавьте конфигурацию провайдера в файл в приложении Medusa Admin:

// ...
module.exports = defineConfig({
// ...
modules: [
{
resolve: "@medusajs/medusa/payment",
options: {
providers: [
{
resolve: "medusa-payment-yookassa/providers/payment-yookassa",
id: "yookassa",
options: {
shopId: process.env.YOOKASSA_SHOP_ID,
secretKey: process.env.YOOKASSA_SECRET_KEY,
capture: true,
paymentDescription: "Test payment",
useReceipt: true,
useAtolOnlineFFD120: true,
taxSystemCode: 1,
taxItemDefault: 1,
taxShippingDefault: 1
},
}
]
}
}
]
})

Добавьте следующие переменные окружения: идентификатор магазина и секретный ключ :

Затем настройте URL вебхука для уведомлений от YooKassa здесь. URL должен иметь следующий формат:

Параметры провайдера

ПараметрОписаниеОбязательныйПо умолчанию
Идентификатор вашего магазина в ЮKassaДа-
Секретный ключ, который используется для проведения операций через ЮKassaДа-
Описание платежа по умолчанию, если контекст не указан в YooKassaНет-
Определяет тип проведения платежа:- — одностадийная оплата- — двухстадийная оплатаНет
Включает формирование онлайн-чеков по 54-ФЗНет
Включается, если вы используете онлайн-кассу Атол Онлайн, обновленную до ФФД 1.2Применимо только при Нет
Система налогообложения:- : — общая СН- : — упрощенная СН (доходы)- : — упрощенная СН (доходы минус расходы)- : — единый налог на вмененный доход- : — единый сельскохозяйственный налог- : — патентная СН Обязательный, если вы используете онлайн-кассу Атол Онлайн, обновленную до ФФД 1.2Применимо только при Нет-
Ставка НДС по товарам::- : — без НДС- : — 0%- : — 10%- : — 20%- : — 10/110- : — 20/110- : — 5%- : — 7%- : — 5/105- : — 7/107Для самозанятый - фиксированное значеие Применимо только при Нет-
Ставка НДС для доставки (аналогично ) Применимо только при Нет-

Интеграция с Storefront (витриной магазина)

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

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

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

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

1. Конфигурация платежного провайдера

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

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

export const paymentInfoMap: Record<
string,
{ title: string; icon: React.ReactNode }
> = {
// ... другие провайдеры
pp_yookassa_yookassa: {
title: "YooKassa",
icon: <CreditCard />,
}
}
// Вспомогательная функция для проверки, является ли провайдер YooKassa
export const isYookassa = (providerId?: string) => {
return providerId?.startsWith("pp_yookassa")
}

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

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

2. Настройки Cookie

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

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

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

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

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

3. Инициализация платёжной сессии

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

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

await initiatePaymentSession(cart, {
provider_id: selectedPaymentMethod,
data: {
confirmation: {
type: "redirect",
return_url: `${getBaseURL()}/api/capture-payment/${cart?.id}?country_code=${countryCode}`
},
cart: cart
}
})

При инициировании платёжной сессии для YooKassa передайте объект с и . Позже YooKassa предоставит , на которое клиент должен быть перенаправлен.

Параметр указывает на конечную точку захвата вашего магазина и используется как для успешных, так и для неудачных попыток оплаты.

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

4. Компонент кнопки оплаты

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

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

const PaymentButton: React.FC<PaymentButtonProps> = ({
cart,
"data-testid": dataTestId,
}) => {
// ...
switch (true) {
// ... другие проверки
case isYookassa(paymentSession?.provider_id):
return (
<YookassaPaymentButton
notReady={notReady}
cart={cart}
data-testid={dataTestId}
/>
)
default:
return <Button disabled>Select a payment method</Button>
}
}
// ... другие компоненты кнопок оплаты
const YookassaPaymentButton = ({
cart,
notReady
}: {
cart: HttpTypes.StoreCart
notReady: boolean
"data-testid"?: string
}) => {
const [submitting, setSubmitting] = useState(false)
const [errorMessage, setErrorMessage] = useState<string | null>(null)
const router = useRouter()
const paymentSession = cart.payment_collection?.payment_sessions?.find(
session => session.provider_id === "pp_yookassa_yookassa"
)
const handlePayment = () => {
setSubmitting(true)
const confirmation = paymentSession?.data?.confirmation as IConfirmation
if (confirmation?.confirmation_url) {
router.push(confirmation.confirmation_url)
}
}
return (
<>
<Button
disabled={notReady}
isLoading={submitting}
onClick={handlePayment}
size="large"
data-testid="submit-order-button"
>
Place an order and pay
</Button>
<ErrorMessage
error={errorMessage}
data-testid="manual-payment-error-message"
/>
</>
)
}

Этот компонент находит YooKassa в активной корзине и извлекает значение . Когда пользователь нажимает кнопку Place an order, он перенаправляется на этот URL для завершения оплаты на странице YooKassa.

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

Родительский компонент использует функцию , чтобы определить, нужно ли отобразить для текущей сессии; в противном случае показывается неактивная кнопка Select a payment method.

5. API-роут подтверждения платежа

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

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

import { NextRequest, NextResponse } from "next/server"
import { revalidateTag } from "next/cache"
import {
getCacheTag,
getAuthHeaders,
removeCartId
} from "@lib/data/cookies"
import { sdk } from "@lib/config"
import { placeOrder } from "@lib/data/cart"
type Params = Promise<{ cartId: string }>
export async function GET(req: NextRequest, { params }: { params: Params }) {
const { cartId } = await params
const { origin, searchParams } = req.nextUrl
const countryCode = searchParams.get("country_code") || ""
const headers = { ...(await getAuthHeaders()) }
// Retrieve fresh cart values
const cartCacheTag = await getCacheTag("carts")
revalidateTag(cartCacheTag)
const { cart } = await sdk.store.cart.retrieve(cartId, {
fields: "id, order_link.order_id"
},
headers
)
if (!cart) {
return NextResponse.redirect(`${origin}/${countryCode}`)
}
const orderId = (cart as unknown as Record<string, any>).order_link?.order_id
if (!orderId) {
await placeOrder(cartId)
// Fail when payment not authorized
return NextResponse.redirect(
`${origin}/${countryCode}/checkout?step=review&error=payment_failed`
)
}
const orderCacheTag = await getCacheTag("orders")
revalidateTag(orderCacheTag)
removeCartId()
return NextResponse.redirect(
`${origin}/${countryCode}/order/${orderId}/confirmed`
)
}

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

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

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

Пример

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

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

git clone https://github.com/sergkoudi/medusa-payment-yookassa
cd medusa-payment-yookassa
git diff v0.0.3...main -- examples/medusa-storefront

Разработка

Документацию по развертыванию окружения для разработки можно найти здесь.

Лицензия

Распространяется на условиях лицензии MIT.