Search for a command to run...
Для интеграции платёжного провайдера Робокасса с storefront на Next.js начните с добавления необходимых UI-компонентов. Таким образом провайдер будет отображаться на странице оформления заказа наряду с другими доступными методами оплаты.
Когда пользователь выбирает Робокасса, витрина должна вызвать метод с нужными параметрами. Это создаст платёжную сессию через API Робокасса и подготовит покупателя к перенаправлению. После этого кнопка Place Order должна отправить пользователя на страницу оплаты Робокасса, где он сможет выбрать предпочтительный способ оплаты.
После завершения оплаты Робокасса одновременно отправит вебхук и перенаправит покупателя обратно в витрину. То событие, которое придёт первым, завершит корзину и создаст новый заказ в Medusa.
В storefront на Next.js необходимо внести следующие изменения:
Чтобы сделать Робокасса доступным в качестве способа оплаты на странице оформления заказа витрины магазина, необходимо добавить её конфигурацию в маппинг платёжных провайдеров в файле с константами вашего storefront. Этот маппинг определяет как каждый провайдер отображается в интерфейсе.
Откройте и добавьте следующий код:
export const paymentInfoMap: Record<string,{ title: string; icon: React.ReactNode }> = {// ... другие провайдерыpp_robokassa_robokassa: {title: "Robokassa",icon: <CreditCard />,}}// Вспомогательная функция для проверки, является ли провайдер Робокассаexport const isRobokassa = (providerId?: string) => {return providerId?.startsWith("pp_robokassa")}
Вы расширяете объект , добавляя запись . Эта запись определяет заголовок и иконку, которые будут отображаться для Робокасса на странице оформления заказа.
Вспомогательная функция проверяет, принадлежит ли переданный к Робокасса. Это используется при рендеринге UI-компонентов, специфичных для конкретного провайдера.
При подключении Робокасса настройте политику cookie так, чтобы поддерживались междоменные редиректы. Это нужно для сохранения платёжной сессии при возврате пользователя в магазин.
Откройте и обновите конфигурацию файлов cookie следующим образом:
export const setCartId = async (cartId: string) => {cookies.set("_medusa_cart_id", cartId, {// ... другие настройки cookiesameSite: "lax", // Переключено с режима «Strict» для междоменных редиректов})}
Эта вспомогательная функция сохраняет идентификатор корзины в cookie с именем .
Опция установлена в значение вместо . Это изменение гарантирует, что cookie будет отправляться при кросс-доменных запросах во время процесса редиректа через Робокасса, предотвращая потерю платёжной сессии.
Чтобы перенаправить покупателя в Робокасса, платёжная сессия должна быть корректно инициализирована с обязательными параметрами, включая return URL для обоих случаев: успешной и неуспешной оплаты, язык, email покупателя, а также корзину для формирования онлайн-чеков.
Откройте и обновите логику инициализации платежа, включив в нее данные корзины и URL возврата для Робокасса:
await initiatePaymentSession(cart, {provider_id: selectedPaymentMethod,data: {SuccessUrl2: `${getBaseURL()}/api/capture-payment/${cart?.id}?country_code=${countryCode}`,SuccessUrl2Method: "GET",FailUrl2: `${getBaseURL()}/api/capture-payment/${cart?.id}?country_code=${countryCode}`,FailUrl2Method: "GET",EMail: cart?.email,Culture: countryCode === "ru" ? "ru" : "en",cart: cart}})
При инициализации платёжной сессии для Робокасса параметры и определяют, куда будет перенаправлен покупатель после попытки оплаты. Оба URL динамически формируются на основе базового URL storefront, идентификатора корзины и выбранного кода страны.
Объект включается в данные инициализации для формирования чека в соответствии с Федеральным законом № 54.
В storefront для каждого платёжного провайдера необходим отдельный компонент кнопки оплаты. Он отвечает за обработку оформления заказа после подтверждения пользователем и, используя данные платёжного сеанса, перенаправляет его на страницу оплаты Робокасса.
Откройте и добавьте следующий код:
const PaymentButton: React.FC<PaymentButtonProps> = ({cart,"data-testid": dataTestId,}) => {// ...switch (true) {// ... другие проверкиcase isRobokassa(paymentSession?.provider_id):return (<RobokassaPaymentButtonnotReady={notReady}cart={cart}data-testid={dataTestId}/>)default:return <Button disabled>Select a payment method</Button>}}// ... другие компоненты кнопок оплатыtype RobokassaPaymentProps = {cart: HttpTypes.StoreCartnotReady: boolean"data-testid"?: string}const RobokassaPaymentButton: React.FC<RobokassaPaymentProps> = ({cart,notReady,"data-testid": dataTestId,}) => {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_robokassa_robokassa")const handlePayment = () => {setSubmitting(true)const paymentUrl = (paymentSession?.data as any).paymentUrlif (paymentUrl) {router.push(paymentUrl)} else {setErrorMessage("Payment URL отсутствует")setSubmitting(false)}}return (<><Buttondisabled={notReady}isLoading={submitting}onClick={handlePayment}data-testid={dataTestId}size="large">Place order</Button><ErrorMessageerror={errorMessage}data-testid="robokassa-payment-error-message"/></>)}
Этот компонент находит в активной корзине платёжную сессию Робокасса и читает . При клике по Place order пользователь перенаправляется на этот URL, чтобы завершить оплату на странице Робокасса.
Если отсутствует, компонент показывает сообщение об ошибке и не выполняет переход. Состояние даёт визуальную обратную связь, пока готовится редирект.
Родительский использует , чтобы отрисовать для текущей сессии; иначе показывается неактивная кнопка Select a payment method.
После того как покупатель завершает оплату на странице Робокасса, он перенаправляется обратно на витрину магазина. Необходимо создать 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 paramsconst { origin, searchParams } = req.nextUrlconst countryCode = searchParams.get("country_code") || ""const headers = { ...(await getAuthHeaders()) }// Получить актуальные значения корзины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_idif (!orderId) {await placeOrder(cartId)// Ошибка при неавторизованном платеже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-роут обрабатывает редирект от Робокасса после попытки оплаты. Он получает актуальное состояние корзины, чтобы убедиться, что все изменения, внесённые во время оплаты, были отражены.
Если в корзине нет связанного идентификатора заказа, обработчик роута пытается оформить заказ. В случае успеха покупатель перенаправляется на страницу подтверждения заказа. Если же при обработке корзины возникла ошибка, покупатель возвращается на страницу оформления заказа с указанием ошибки и может повторить процесс оплаты заказа.
Когда оплата проходит успешно, роут повторно валидирует кэшированные данные корзины и заказа, удаляет cookie корзины и перенаправляет покупателя на страницу подтверждения заказа. Это гарантирует корректное завершение платёжного процесса и сохранение актуальных данных в storefront.
Вы можете ознакомиться с изменениями, внесенными в стартовый шаблон Medusa Next.js Starter Template, в директории .
Полный код интеграции можно посмотреть в разделе сomparison page — откройте вкладку и изучите различия в каталоге . Или запустите diff в терминале:
git clone https://github.com/gorgojs/medusa-pluginscd medusa-pluginsgit diff @gorgo/medusa-payment-robokassa@0.0.1...main -- examples/payment-robokassa/medusa-storefront