medusa-payment-yookassa 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +43 -40
- package/README.ru.md +393 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,15 +14,19 @@ YooKassa Payments for Medusa
|
|
|
14
14
|
</h1>
|
|
15
15
|
|
|
16
16
|
<p align="center">
|
|
17
|
-
A Medusa plugin that provides YooKassa payments.
|
|
17
|
+
A Medusa plugin that provides YooKassa payments.
|
|
18
|
+
<br/>
|
|
19
|
+
<a href="https://github.com/sergkoudi/medusa-payment-yookassa/blob/HEAD/packages/medusa-payment-yookassa/README.ru.md">Читать README на русском →</a>
|
|
18
20
|
</p>
|
|
19
21
|
|
|
22
|
+
<br/>
|
|
23
|
+
|
|
20
24
|
<p align="center">
|
|
21
25
|
<a href="https://medusajs.com">
|
|
22
26
|
<img src="https://img.shields.io/badge/Medusa-^2.7.0-blue?logo=medusa" alt="Medusa" />
|
|
23
27
|
</a>
|
|
24
28
|
<a href="https://medusajs.com">
|
|
25
|
-
<img src="https://img.shields.io/badge/Tested_with_Medusa-v2.
|
|
29
|
+
<img src="https://img.shields.io/badge/Tested_with_Medusa-v2.10.2-green?logo=checkmarx" alt="Medusa" />
|
|
26
30
|
</a>
|
|
27
31
|
</p>
|
|
28
32
|
|
|
@@ -40,27 +44,28 @@ A Medusa plugin that provides YooKassa payments.
|
|
|
40
44
|
|
|
41
45
|
## Features
|
|
42
46
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
🔄
|
|
47
|
-
|
|
48
|
-
🛡
|
|
49
|
-
|
|
47
|
+
- 🔗 **Seamless integration** with the YooKassa payment system
|
|
48
|
+
- 🧾 **Receipt generation** compliant with Federal Law No. 54, supporting FFD 1.05 and 1.2 formats
|
|
49
|
+
- 1️⃣ **One-step** (autocapture) and **2️⃣ two-step** (authorization/hold) payment flows
|
|
50
|
+
- 🔄 **Full refund** and **order cancellation** support
|
|
51
|
+
- 🔔 **Webhook support** for real-time payment status updates
|
|
52
|
+
- 🛡 **Webhook verification** for enhanced security
|
|
53
|
+
- 🔍 **Detailed logging** for debugging
|
|
50
54
|
|
|
51
|
-
## 💬 Plugin Support Chat
|
|
55
|
+
## 💬 YooKassa Plugin Support Chat
|
|
52
56
|
|
|
53
|
-
|
|
57
|
+
Got questions or ideas for new plugin features?
|
|
58
|
+
Join the Telegram chat – [@medusajs_yookassa](https://t.me/medusajs_yookassa)
|
|
54
59
|
|
|
55
|
-
## 👥
|
|
60
|
+
## 👥 Medusa.js Community Chat
|
|
56
61
|
|
|
57
|
-
|
|
62
|
+
Connect with other Medusa developers on Telegram – [@medusajs_chat](https://t.me/medusajs_chat)
|
|
58
63
|
|
|
59
|
-
##
|
|
64
|
+
## Requirements
|
|
60
65
|
|
|
61
|
-
- Medusa
|
|
66
|
+
- Medusa v2.7.0 or later
|
|
62
67
|
- Node.js v20 or later
|
|
63
|
-
- A YooKassa account
|
|
68
|
+
- A YooKassa account – [sign in or create one](https://yookassa.ru/joinups/?source=ks)
|
|
64
69
|
|
|
65
70
|
## Installation
|
|
66
71
|
|
|
@@ -72,9 +77,9 @@ npm install medusa-payment-yookassa
|
|
|
72
77
|
|
|
73
78
|
## Configuration
|
|
74
79
|
|
|
75
|
-
Add the provider configuration in your `medusa-config.js` file of the Medusa
|
|
80
|
+
Add the provider configuration in your `medusa-config.js` file of the Medusa Admin application:
|
|
76
81
|
|
|
77
|
-
```
|
|
82
|
+
```ts
|
|
78
83
|
// ...
|
|
79
84
|
module.exports = defineConfig({
|
|
80
85
|
// ...
|
|
@@ -92,7 +97,7 @@ module.exports = defineConfig({
|
|
|
92
97
|
capture: true,
|
|
93
98
|
paymentDescription: "Test payment",
|
|
94
99
|
useReceipt: true,
|
|
95
|
-
|
|
100
|
+
useAtolOnlineFFD120: true,
|
|
96
101
|
taxSystemCode: 1,
|
|
97
102
|
taxItemDefault: 1,
|
|
98
103
|
taxShippingDefault: 1
|
|
@@ -120,17 +125,17 @@ https://{YOUR_MEDUSA_DOMAIN}/hooks/payment/yookassa_yookassa
|
|
|
120
125
|
|
|
121
126
|
## Provider Options
|
|
122
127
|
|
|
123
|
-
| Option | Description
|
|
124
|
-
| --------------------- |
|
|
125
|
-
| `shopId` | The SHOP_ID of YooKassa
|
|
126
|
-
| `secretKey` | The secret key of YooKassa
|
|
127
|
-
| `paymentDescription` | Default description on the payment if the context does not provide
|
|
128
|
-
| `capture` | Automatic payment capture (`true` for one-step payment, `false` for two-step payment)
|
|
129
|
-
| `useReceipt` | Enable receipt generation according to Russian fiscal data format (FFD)
|
|
130
|
-
| `useAtolOnlineFFD120` | Enable when Atol Online FFD 1.2 sales register is used<br>Applicable only if `useReceipt` = `true`
|
|
131
|
-
| `taxSystemCode` | Store tax system:<br>- `1`: General taxation system<br>- `2`: Simplified (income)<br>- `3`: Simplified (income minus expenses)<br>- `4`: Single tax on imputed income<br>- `5`: Single agricultural tax<br>- `6`: Patent taxation system<br><br>Required if you use the Atol Online FFD 1.2 sales register<br>Applicable only if `useReceipt` = `true`
|
|
132
|
-
| `taxItemDefault` | Default VAT rate for products:<br>- `1`: No VAT<br>- `2`: 0%<br>- `3`: 10%<br>- `4`: 20%<br>- `5`: 10/110<br>- `6`: 20/110<br>- `7`: 5%<br>- `8`: 7%<br>- `9`: 5/105<br>- `10`: 7/107<br><br>For receips for self-employed - fixed value `1`<br>Applicable only if `useReceipt` = `true`
|
|
133
|
-
| `taxShippingDefault` | Default VAT rate for shipping, same options as `taxItemDefault` <br>Applicable only if `useReceipt` = `true`
|
|
128
|
+
| Option | Description | Required | Default |
|
|
129
|
+
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------- | ------- |
|
|
130
|
+
| `shopId` | The SHOP_ID of YooKassa | Yes | - |
|
|
131
|
+
| `secretKey` | The secret key of YooKassa | Yes | - |
|
|
132
|
+
| `paymentDescription` | Default description on the payment if the context does not provide on YooKassa | No | - |
|
|
133
|
+
| `capture` | Automatic payment capture (`true` for one-step payment, `false` for two-step payment) | No | `true` |
|
|
134
|
+
| `useReceipt` | Enable receipt generation according to Russian fiscal data format (FFD) | No | `false` |
|
|
135
|
+
| `useAtolOnlineFFD120` | Enable when Atol Online FFD 1.2 sales register is used<br>Applicable only if `useReceipt` = `true` | No | `false` |
|
|
136
|
+
| `taxSystemCode` | Store tax system:<br>- `1`: General taxation system<br>- `2`: Simplified (income)<br>- `3`: Simplified (income minus expenses)<br>- `4`: Single tax on imputed income<br>- `5`: Single agricultural tax<br>- `6`: Patent taxation system<br><br>Required if you use the Atol Online FFD 1.2 sales register<br>Applicable only if `useReceipt` = `true` | No (Yes if you use the Atol Online FFD 1.2) | - |
|
|
137
|
+
| `taxItemDefault` | Default VAT rate for products:<br>- `1`: No VAT<br>- `2`: 0%<br>- `3`: 10%<br>- `4`: 20%<br>- `5`: 10/110<br>- `6`: 20/110<br>- `7`: 5%<br>- `8`: 7%<br>- `9`: 5/105<br>- `10`: 7/107<br><br>For receips for self-employed - fixed value `1`<br>Applicable only if `useReceipt` = `true` | No | - |
|
|
138
|
+
| `taxShippingDefault` | Default VAT rate for shipping, same options as `taxItemDefault` <br><br>Applicable only if `useReceipt` = `true` | No | - |
|
|
134
139
|
|
|
135
140
|
## Storefront Integration
|
|
136
141
|
|
|
@@ -140,8 +145,6 @@ When YooKassa is selected, the storefront should call `initiatePayment` with the
|
|
|
140
145
|
|
|
141
146
|
Once the payment is completed, YooKassa will concurrently send a webhook and redirect the customer back to the storefront. Whichever arrives first will complete the cart and create a new order in Medusa.
|
|
142
147
|
|
|
143
|
-
For the Next.js start you need to make the following changes:
|
|
144
|
-
|
|
145
148
|
### 1. Payment Provider Configuration
|
|
146
149
|
|
|
147
150
|
To make YooKassa available as a payment method on the storefront checkout page, you must add its configuration to the payment provider mapping in your storefront’s constants file. This mapping determines how each payment provider is displayed in the UI.
|
|
@@ -168,9 +171,9 @@ export const isYookassa = (providerId?: string) => {
|
|
|
168
171
|
}
|
|
169
172
|
```
|
|
170
173
|
|
|
171
|
-
You extend the `paymentInfoMap` object to include a `pp_yookassa_yookassa` entry. This entry defines the title and icon shown for YooKassa on the checkout page.
|
|
174
|
+
You extend the `paymentInfoMap` object to include a `pp_yookassa_yookassa` entry. This entry defines the title and the icon that will be shown for YooKassa on the checkout page.
|
|
172
175
|
|
|
173
|
-
The `isYookassa`
|
|
176
|
+
The helper function `isYookassa` checks whether a given `providerId` belongs to YooKassa. This is useful when rendering provider-specific UI-components.
|
|
174
177
|
|
|
175
178
|
### 2. Cookie Settings Update
|
|
176
179
|
|
|
@@ -195,9 +198,9 @@ The `sameSite` option is set to `lax` instead of `strict`. This change ensures t
|
|
|
195
198
|
|
|
196
199
|
### 3. Payment Session Initialization
|
|
197
200
|
|
|
198
|
-
To redirect a customer to YooKassa, the payment session must be properly initialized with the required parameters, including the return URL for the post-payment callback.
|
|
201
|
+
To redirect a customer to YooKassa, the payment session must be properly initialized with the required parameters, including the return URL for the post-payment callback and the shopping cart for generating receipts.
|
|
199
202
|
|
|
200
|
-
Open [`src/modules/checkout/components/payment/index.tsx`](https://github.com/sergkoudi/medusa-payment-yookassa/blob/main/examples/medusa-storefront/src/modules/checkout/components/payment/index.tsx#L87-L96) and update the payment initialization logic to include YooKassa’s redirect URL:
|
|
203
|
+
Open [`src/modules/checkout/components/payment/index.tsx`](https://github.com/sergkoudi/medusa-payment-yookassa/blob/main/examples/medusa-storefront/src/modules/checkout/components/payment/index.tsx#L87-L96) and update the payment initialization logic to include YooKassa’s redirect URL and cart:
|
|
201
204
|
|
|
202
205
|

|
|
203
206
|
|
|
@@ -222,7 +225,7 @@ The `cart` object is included to build a compliant receipt in accordance with Fe
|
|
|
222
225
|
|
|
223
226
|
### 4. Payment Button Component
|
|
224
227
|
|
|
225
|
-
Medusa storefront requires a dedicated payment button for each provider to handle the
|
|
228
|
+
Medusa storefront requires a dedicated payment button component for each payment provider to handle the checkout flow after the customer confirms his order. This component leverages the payment session data and navigates the customer to the YooKassa payment page.
|
|
226
229
|
|
|
227
230
|
Open [`src/modules/checkout/components/payment-button/index.tsx`](https://github.com/sergkoudi/medusa-payment-yookassa/blob/bce2e01d8932b3c09b66a3a1b06aa1d5c4cfc445/examples/medusa-storefront/src/modules/checkout/components/payment-button/index.tsx#L205-L247) and add the following code:
|
|
228
231
|
|
|
@@ -302,7 +305,7 @@ If the `confirmation_url` is missing, the component displays an error message in
|
|
|
302
305
|
|
|
303
306
|
The parent `PaymentButton` uses `isYookassa` to determine whether to render the `YookassaPaymentButton` for the current session; otherwise, it shows a disabled Select a payment method button.
|
|
304
307
|
|
|
305
|
-
### 5. Payment Capture
|
|
308
|
+
### 5. Payment Capture API route
|
|
306
309
|
|
|
307
310
|
After the customer completes payment on the YooKassa page, he is redirected back to the storefront. You need an API route to handle this callback, verify the payment status, and complete the cart.
|
|
308
311
|
|
|
@@ -360,7 +363,7 @@ export async function GET(req: NextRequest, { params }: { params: Params }) {
|
|
|
360
363
|
}
|
|
361
364
|
```
|
|
362
365
|
|
|
363
|
-
This route handles the redirect from YooKassa after a payment attempt. It retrieves the latest state of the cart to ensure any updates made during payment are reflected.
|
|
366
|
+
This API route handles the redirect from YooKassa after a payment attempt. It retrieves the latest state of the cart to ensure any updates made during payment are reflected.
|
|
364
367
|
|
|
365
368
|
If the cart does not contain an associated order ID, the route tries to place an order. If successful, the customer is redirected to the order confirmation page. If any error happens during cart completion, the customer is redirected back to the checkout page indicating an error, and he can proceed the checkout once again.
|
|
366
369
|
|
|
@@ -370,7 +373,7 @@ When the payment is successful, the route revalidates the cached cart and order
|
|
|
370
373
|
|
|
371
374
|
You can refer to the modifications made in the [Medusa Next.js Starter Template](https://github.com/medusajs/nextjs-starter-medusa), which are located in the [`examples/medusa-storefront`](https://github.com/sergkoudi/medusa-payment-yookassa/tree/main/examples/medusa-storefront) directory.
|
|
372
375
|
|
|
373
|
-
The complete integration diff can be viewed in the [comparison page](https://github.com/sergkoudi/medusa-payment-yookassa/compare/v0.0.3...main), open the
|
|
376
|
+
The complete integration diff can be viewed in the [comparison page](https://github.com/sergkoudi/medusa-payment-yookassa/compare/v0.0.3...main), open the `Files changed` tab, and explore the differences under the `examples/medusa-storefront` directory. Or run diff in the terminal:
|
|
374
377
|
|
|
375
378
|
```bash
|
|
376
379
|
git clone https://github.com/sergkoudi/medusa-payment-yookassa
|
package/README.ru.md
ADDED
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://www.medusajs.com">
|
|
3
|
+
<picture>
|
|
4
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/user-attachments/assets/9a99f9e8-f80e-4411-9bed-6e2032b1ab1c">
|
|
5
|
+
<source media="(prefers-color-scheme: light)" srcset="https://github.com/user-attachments/assets/9a99f9e8-f80e-4411-9bed-6e2032b1ab1c">
|
|
6
|
+
<img alt="Medusa logo" src="https://github.com/user-attachments/assets/9a99f9e8-f80e-4411-9bed-6e2032b1ab1c" height="120">
|
|
7
|
+
</picture>
|
|
8
|
+
</a>
|
|
9
|
+
|
|
10
|
+
</p>
|
|
11
|
+
|
|
12
|
+
<h1 align="center">
|
|
13
|
+
Платежи YooKassa для Medusa
|
|
14
|
+
</h1>
|
|
15
|
+
|
|
16
|
+
<p align="center">
|
|
17
|
+
Плагин Medusa для приёма платежей через YooKassa.
|
|
18
|
+
<br/>
|
|
19
|
+
<a href="https://github.com/sergkoudi/medusa-payment-yookassa/blob/HEAD/packages/medusa-payment-yookassa/README.md">Read README in English →</a>
|
|
20
|
+
</p>
|
|
21
|
+
|
|
22
|
+
<br/>
|
|
23
|
+
|
|
24
|
+
<p align="center">
|
|
25
|
+
<a href="https://medusajs.com">
|
|
26
|
+
<img src="https://img.shields.io/badge/Medusa-^2.7.0-blue?logo=medusa" alt="Medusa" />
|
|
27
|
+
</a>
|
|
28
|
+
<a href="https://medusajs.com">
|
|
29
|
+
<img src="https://img.shields.io/badge/Протестировано_с_Medusa-v2.10.2-green?logo=checkmarx" alt="Medusa" />
|
|
30
|
+
</a>
|
|
31
|
+
</p>
|
|
32
|
+
|
|
33
|
+
<p align="center">
|
|
34
|
+
<a href="https://t.me/medusajs_yookassa">
|
|
35
|
+
<img src="https://img.shields.io/badge/Telegram-Чат_поддержки_Medusa.js⊷YooKassa-0088cc?logo=telegram&style=social" alt="Чат Medusa.js⊷YooKassa в Telegram" />
|
|
36
|
+
</a>
|
|
37
|
+
</p>
|
|
38
|
+
|
|
39
|
+
<p align="center">
|
|
40
|
+
<a href="https://t.me/medusajs_chat">
|
|
41
|
+
<img src="https://img.shields.io/badge/Telegram-Чат_dev--сообщества_Medusa.js-0088cc?logo=telegram&style=social" alt="Чат сообщества разработчиков Medusa.js в Telegram" />
|
|
42
|
+
</a>
|
|
43
|
+
</p>
|
|
44
|
+
|
|
45
|
+
## Возможности
|
|
46
|
+
|
|
47
|
+
- 🔗 **Бесшовная интеграция** с платёжной системой YooKassa
|
|
48
|
+
- 🧾 **Формирование онлайн-чеков** в соответствии с 54-ФЗ
|
|
49
|
+
- 1️⃣ **Одностадийные** (автосписание) и **2️⃣ двухстадийные** (авторизация/холдирование) сценарии оплаты
|
|
50
|
+
- 🔄 **Возвраты и отмена заказов**
|
|
51
|
+
- 🔔 **Вебхук-уведомления** о статусах платежей в реальном времени
|
|
52
|
+
- 🛡 **Проверка вебхуков** для обеспечения безопасности
|
|
53
|
+
- 🔍 **Подробное логирование** для отладки
|
|
54
|
+
|
|
55
|
+
## 💬 Чат поддержки плагина YooKassa
|
|
56
|
+
|
|
57
|
+
Есть вопросы или идеи по новым функциям плагина?
|
|
58
|
+
Присоединяйтесь к чату в Telegram – [@medusajs_yookassa](https://t.me/medusajs_yookassa)
|
|
59
|
+
|
|
60
|
+
## 👥 Чат сообщества Medusa.js
|
|
61
|
+
|
|
62
|
+
Общайтесь в Telegram с другими разработчиками Medusa – [@medusajs_chat](https://t.me/medusajs_chat)
|
|
63
|
+
|
|
64
|
+
## Требования
|
|
65
|
+
|
|
66
|
+
- Medusa v2.7.0 или выше
|
|
67
|
+
- Node.js v20 или выше
|
|
68
|
+
- Аккаунт YooKassa – [зарегистрируйтесь или войдите](https://yookassa.ru/joinups/?source=ks)
|
|
69
|
+
|
|
70
|
+
## Установка
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
yarn add medusa-payment-yookassa
|
|
74
|
+
# или
|
|
75
|
+
npm install medusa-payment-yookassa
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Настройка
|
|
79
|
+
|
|
80
|
+
Добавьте конфигурацию провайдера в файл `medusa-config.js` в приложении Medusa Admin:
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
// ...
|
|
84
|
+
module.exports = defineConfig({
|
|
85
|
+
// ...
|
|
86
|
+
modules: [
|
|
87
|
+
{
|
|
88
|
+
resolve: "@medusajs/medusa/payment",
|
|
89
|
+
options: {
|
|
90
|
+
providers: [
|
|
91
|
+
{
|
|
92
|
+
resolve: "medusa-payment-yookassa/providers/payment-yookassa",
|
|
93
|
+
id: "yookassa",
|
|
94
|
+
options: {
|
|
95
|
+
shopId: process.env.YOOKASSA_SHOP_ID,
|
|
96
|
+
secretKey: process.env.YOOKASSA_SECRET_KEY,
|
|
97
|
+
capture: true,
|
|
98
|
+
paymentDescription: "Test payment",
|
|
99
|
+
useReceipt: true,
|
|
100
|
+
useAtolOnlineFFD120: true,
|
|
101
|
+
taxSystemCode: 1,
|
|
102
|
+
taxItemDefault: 1,
|
|
103
|
+
taxShippingDefault: 1
|
|
104
|
+
},
|
|
105
|
+
}
|
|
106
|
+
]
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
]
|
|
110
|
+
})
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Добавьте следующие переменные окружения: идентификатор магазина `shopId` и секретный ключ `secretKey`:
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
YOOKASSA_SHOP_ID=1234567
|
|
117
|
+
YOOKASSA_SECRET_KEY=live_secret_api_key
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Затем настройте URL вебхука для уведомлений от YooKassa [здесь](https://yookassa.ru/my/merchant/integration/http-notifications). URL должен иметь следующий формат:
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
https://{YOUR_MEDUSA_DOMAIN}/hooks/payment/yookassa_yookassa
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Параметры провайдера
|
|
127
|
+
|
|
128
|
+
| Параметр | Описание | Обязательный | По умолчанию |
|
|
129
|
+
| --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | ------------ |
|
|
130
|
+
| `shopId` | Идентификатор вашего магазина в ЮKassa | Да | - |
|
|
131
|
+
| `secretKey` | Секретный ключ, который используется для проведения операций через ЮKassa | Да | - |
|
|
132
|
+
| `paymentDescription` | Описание платежа по умолчанию, если контекст не указан в YooKassa | Нет | - |
|
|
133
|
+
| `capture` | Определяет тип проведения платежа:<br>- `true` — одностадийная оплата<br>- `false` — двухстадийная оплата | Нет | `true` |
|
|
134
|
+
| `useReceipt` | Включает формирование онлайн-чеков по 54-ФЗ | Нет | `false` |
|
|
135
|
+
| `useAtolOnlineFFD120` | Включается, если вы используете онлайн-кассу Атол Онлайн, обновленную до ФФД 1.2<br><br>Применимо только при `useReceipt=true` | Нет | `false` |
|
|
136
|
+
| `taxSystemCode` | Система налогообложения:<br>- `1`: — общая СН<br>- `2`: — упрощенная СН (доходы)<br>- `3`: — упрощенная СН (доходы минус расходы)<br>- `4`: — единый налог на вмененный доход<br>- `5`: — единый сельскохозяйственный налог<br>- `6`: — патентная СН<br><br> Обязательный, если вы используете онлайн-кассу Атол Онлайн, обновленную до ФФД 1.2<br>Применимо только при `useReceipt=true` | Нет | - |
|
|
137
|
+
| `taxItemDefault` | Ставка НДС по товарам::<br>- `1`: — без НДС<br>- `2`: — 0%<br>- `3`: — 10%<br>- `4`: — 20%<br>- `5`: — 10/110<br>- `6`: — 20/110<br>- `7`: — 5%<br>- `8`: — 7%<br>- `9`: — 5/105<br>- `10`: — 7/107<br><br>Для самозанятый - фиксированное значеие `1`<br>Применимо только при `useReceipt=true` | Нет | - |
|
|
138
|
+
| `taxShippingDefault` | Ставка НДС для доставки (аналогично `taxItemDefault`) <br><br>Применимо только при `useReceipt=true` | Нет | - |
|
|
139
|
+
|
|
140
|
+
## Интеграция с Storefront (витриной магазина)
|
|
141
|
+
|
|
142
|
+
Для интеграции платёжного провайдера YooKassa с storefront на Next.js начните с добавления необходимых UI-компонентов. Таким образом провайдер будет отображаться на странице оформления заказа наряду с другими доступными методами оплаты.
|
|
143
|
+
|
|
144
|
+
Когда пользователь выбирает YooKassa, витрина должна вызвать метод `initiatePayment` с нужными параметрами. Это создаст платёжную сессию через API YooKassa и подготовит покупателя к перенаправлению. После этого кнопка *Place Order* должна отправить пользователя на страницу оплаты YooKassa, где он сможет выбрать предпочтительный способ оплаты.
|
|
145
|
+
|
|
146
|
+
После завершения оплаты YooKassa одновременно отправит вебхук и перенаправит покупателя обратно в витрину. То событие, которое придёт первым, завершит корзину и создаст новый заказ в Medusa.
|
|
147
|
+
|
|
148
|
+
Для запуска на Next.js необходимо внести следующие изменения:
|
|
149
|
+
|
|
150
|
+
### 1. Конфигурация платежного провайдера
|
|
151
|
+
|
|
152
|
+
Чтобы сделать YooKassa доступным в качестве способа оплаты на странице оформления заказа витрины магазина, необходимо добавить её конфигурацию в маппинг платёжных провайдеров в файле с константами вашего storefront. Этот маппинг определяет как каждый провайдер отображается в интерфейсе.
|
|
153
|
+
|
|
154
|
+
Откройте [`src/lib/constants.tsx`](https://github.com/sergkoudi/medusa-payment-yookassa/blob/bce2e01d8932b3c09b66a3a1b06aa1d5c4cfc445/examples/medusa-storefront/src/lib/constants.tsx#L33-L36) и добавьте следующий код:
|
|
155
|
+
|
|
156
|
+

|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
export const paymentInfoMap: Record<
|
|
160
|
+
string,
|
|
161
|
+
{ title: string; icon: React.ReactNode }
|
|
162
|
+
> = {
|
|
163
|
+
// ... другие провайдеры
|
|
164
|
+
pp_yookassa_yookassa: {
|
|
165
|
+
title: "YooKassa",
|
|
166
|
+
icon: <CreditCard />,
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Вспомогательная функция для проверки, является ли провайдер YooKassa
|
|
171
|
+
export const isYookassa = (providerId?: string) => {
|
|
172
|
+
return providerId?.startsWith("pp_yookassa")
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Вы расширяете объект `paymentInfoMap`, добавляя в него запись `pp_yookassa_yookassa`. Эта запись определяет заголовок и иконку, которые будут отображаться для YooKassa на странице оформления заказа.
|
|
177
|
+
|
|
178
|
+
Вспомогательная функция `isYookassa` проверяет, принадлежит ли переданный `providerId` к YooKassa.
|
|
179
|
+
Это используется при рендеринге UI-компонентов, специфичных для конкретного провайдера.
|
|
180
|
+
|
|
181
|
+
### 2. Настройки Cookie
|
|
182
|
+
|
|
183
|
+
При подключении YooKassa настройте политику cookie так, чтобы поддерживались междоменные редиректы. Это нужно для сохранения платёжной сессии при возврате пользователя в магазин.
|
|
184
|
+
|
|
185
|
+
Откройте [`src/lib/data/cookies.ts`](https://github.com/sergkoudi/medusa-payment-yookassa/blob/bce2e01d8932b3c09b66a3a1b06aa1d5c4cfc445/examples/medusa-storefront/src/lib/data/cookies.ts#L75) и обновите конфигурацию файлов cookie следующим образом:
|
|
186
|
+
|
|
187
|
+

|
|
188
|
+
|
|
189
|
+
```ts
|
|
190
|
+
export const setCartId = async (cartId: string) => {
|
|
191
|
+
cookies.set("_medusa_cart_id", cartId, {
|
|
192
|
+
// ... другие настройки cookie
|
|
193
|
+
sameSite: "lax", // Переключено с режима «Strict» для междоменных редиректов
|
|
194
|
+
})
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Эта вспомогательная функция сохраняет идентификатор корзины в cookie с именем `_medusa_cart_id`.
|
|
199
|
+
|
|
200
|
+
Опция `sameSite` установлена в значение `lax` вместо `strict`. Это изменение гарантирует, что cookie будет отправляться при кросс-доменных запросах во время процесса редиректа через YooKassa, предотвращая потерю платёжной сессии.
|
|
201
|
+
|
|
202
|
+
### 3. Инициализация платёжной сессии
|
|
203
|
+
|
|
204
|
+
Чтобы перенаправить покупателя в YooKassa, платёжная сессия должна быть корректно инициализирована с необходимыми параметрами, включая return URL после оплаты и корзину для формирования онлайн-чеков.
|
|
205
|
+
|
|
206
|
+
Откройте [`src/modules/checkout/components/payment/index.tsx`](https://github.com/sergkoudi/medusa-payment-yookassa/blob/main/examples/medusa-storefront/src/modules/checkout/components/payment/index.tsx#L87-L96) и обновите логику инициализации платежа, включив в нее данные корзины и URL возврата для YooKassa:
|
|
207
|
+
|
|
208
|
+

|
|
209
|
+
|
|
210
|
+
```ts
|
|
211
|
+
await initiatePaymentSession(cart, {
|
|
212
|
+
provider_id: selectedPaymentMethod,
|
|
213
|
+
data: {
|
|
214
|
+
confirmation: {
|
|
215
|
+
type: "redirect",
|
|
216
|
+
return_url: `${getBaseURL()}/api/capture-payment/${cart?.id}?country_code=${countryCode}`
|
|
217
|
+
},
|
|
218
|
+
cart: cart
|
|
219
|
+
}
|
|
220
|
+
})
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
При инициировании платёжной сессии для YooKassa передайте объект `confirmation` с `type: "redirect"` и `return_url`. Позже YooKassa предоставит `confirmation_url`, на которое клиент должен быть перенаправлен.
|
|
224
|
+
|
|
225
|
+
Параметр `return_url` указывает на конечную точку захвата вашего магазина и используется как для успешных, так и для неудачных попыток оплаты.
|
|
226
|
+
|
|
227
|
+
Объект `cart` включается в данные инициализации для формирования чека в соответствии с Федеральным законом № 54.
|
|
228
|
+
|
|
229
|
+
### 4. Компонент кнопки оплаты
|
|
230
|
+
|
|
231
|
+
В storefront для каждого платёжного провайдера необходим отдельный компонент кнопки оплаты. Он отвечает за обработку оформления заказа после подтверждения пользователем и, используя данные платёжного сеанса, перенаправляет его на страницу оплаты YooKassa.
|
|
232
|
+
|
|
233
|
+
Откройте [`src/modules/checkout/components/payment-button/index.tsx`](https://github.com/sergkoudi/medusa-payment-yookassa/blob/bce2e01d8932b3c09b66a3a1b06aa1d5c4cfc445/examples/medusa-storefront/src/modules/checkout/components/payment-button/index.tsx#L205-L247) и добавьте следующий код:
|
|
234
|
+
|
|
235
|
+

|
|
236
|
+
|
|
237
|
+
```ts
|
|
238
|
+
const PaymentButton: React.FC<PaymentButtonProps> = ({
|
|
239
|
+
cart,
|
|
240
|
+
"data-testid": dataTestId,
|
|
241
|
+
}) => {
|
|
242
|
+
// ...
|
|
243
|
+
switch (true) {
|
|
244
|
+
// ... другие проверки
|
|
245
|
+
case isYookassa(paymentSession?.provider_id):
|
|
246
|
+
return (
|
|
247
|
+
<YookassaPaymentButton
|
|
248
|
+
notReady={notReady}
|
|
249
|
+
cart={cart}
|
|
250
|
+
data-testid={dataTestId}
|
|
251
|
+
/>
|
|
252
|
+
)
|
|
253
|
+
default:
|
|
254
|
+
return <Button disabled>Select a payment method</Button>
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// ... другие компоненты кнопок оплаты
|
|
259
|
+
|
|
260
|
+
const YookassaPaymentButton = ({
|
|
261
|
+
cart,
|
|
262
|
+
notReady
|
|
263
|
+
}: {
|
|
264
|
+
cart: HttpTypes.StoreCart
|
|
265
|
+
notReady: boolean
|
|
266
|
+
"data-testid"?: string
|
|
267
|
+
}) => {
|
|
268
|
+
const [submitting, setSubmitting] = useState(false)
|
|
269
|
+
const [errorMessage, setErrorMessage] = useState<string | null>(null)
|
|
270
|
+
const router = useRouter()
|
|
271
|
+
|
|
272
|
+
const paymentSession = cart.payment_collection?.payment_sessions?.find(
|
|
273
|
+
session => session.provider_id === "pp_yookassa_yookassa"
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
const handlePayment = () => {
|
|
277
|
+
setSubmitting(true)
|
|
278
|
+
|
|
279
|
+
const confirmation = paymentSession?.data?.confirmation as IConfirmation
|
|
280
|
+
if (confirmation?.confirmation_url) {
|
|
281
|
+
router.push(confirmation.confirmation_url)
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return (
|
|
286
|
+
<>
|
|
287
|
+
<Button
|
|
288
|
+
disabled={notReady}
|
|
289
|
+
isLoading={submitting}
|
|
290
|
+
onClick={handlePayment}
|
|
291
|
+
size="large"
|
|
292
|
+
data-testid="submit-order-button"
|
|
293
|
+
>
|
|
294
|
+
Place an order and pay
|
|
295
|
+
</Button>
|
|
296
|
+
<ErrorMessage
|
|
297
|
+
error={errorMessage}
|
|
298
|
+
data-testid="manual-payment-error-message"
|
|
299
|
+
/>
|
|
300
|
+
</>
|
|
301
|
+
)
|
|
302
|
+
}
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
Этот компонент находит `payment_session` YooKassa в активной корзине и извлекает значение `data.confirmation.confirmation_url`. Когда пользователь нажимает кнопку *Place an order*, он перенаправляется на этот URL для завершения оплаты на странице YooKassa.
|
|
306
|
+
|
|
307
|
+
Если `confirmation_url` отсутствует, компонент отображает сообщение об ошибке вместо продолжения процесса. Состояние `isLoading` обеспечивает визуальную обратную связь во время подготовки перенаправления.
|
|
308
|
+
|
|
309
|
+
Родительский компонент `PaymentButton` использует функцию `isYookassa`, чтобы определить, нужно ли отобразить `YookassaPaymentButton` для текущей сессии; в противном случае показывается неактивная кнопка *Select a payment method*.
|
|
310
|
+
|
|
311
|
+
### 5. API-роут подтверждения платежа
|
|
312
|
+
|
|
313
|
+
После того как покупатель завершает оплату на странице YooKassa, он перенаправляется обратно на витрину магазина. Необходимо создать API-роут, который обработает этот callback, проверит статус платежа и завершит корзину.
|
|
314
|
+
|
|
315
|
+
Создайте файл [`src/app/api/capture-payment/[cartId]/route.ts`](https://github.com/sergkoudi/medusa-payment-yookassa/blob/main/examples/medusa-storefront/src/app/api/capture-payment/%5BcartId%5D/route.ts) со следующим содержимым:
|
|
316
|
+
|
|
317
|
+

|
|
318
|
+
|
|
319
|
+
```ts
|
|
320
|
+
import { NextRequest, NextResponse } from "next/server"
|
|
321
|
+
import { revalidateTag } from "next/cache"
|
|
322
|
+
import {
|
|
323
|
+
getCacheTag,
|
|
324
|
+
getAuthHeaders,
|
|
325
|
+
removeCartId
|
|
326
|
+
} from "@lib/data/cookies"
|
|
327
|
+
import { sdk } from "@lib/config"
|
|
328
|
+
import { placeOrder } from "@lib/data/cart"
|
|
329
|
+
|
|
330
|
+
type Params = Promise<{ cartId: string }>
|
|
331
|
+
|
|
332
|
+
export async function GET(req: NextRequest, { params }: { params: Params }) {
|
|
333
|
+
const { cartId } = await params
|
|
334
|
+
const { origin, searchParams } = req.nextUrl
|
|
335
|
+
|
|
336
|
+
const countryCode = searchParams.get("country_code") || ""
|
|
337
|
+
const headers = { ...(await getAuthHeaders()) }
|
|
338
|
+
|
|
339
|
+
// Retrieve fresh cart values
|
|
340
|
+
const cartCacheTag = await getCacheTag("carts")
|
|
341
|
+
revalidateTag(cartCacheTag)
|
|
342
|
+
const { cart } = await sdk.store.cart.retrieve(cartId, {
|
|
343
|
+
fields: "id, order_link.order_id"
|
|
344
|
+
},
|
|
345
|
+
headers
|
|
346
|
+
)
|
|
347
|
+
if (!cart) {
|
|
348
|
+
return NextResponse.redirect(`${origin}/${countryCode}`)
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const orderId = (cart as unknown as Record<string, any>).order_link?.order_id
|
|
352
|
+
if (!orderId) {
|
|
353
|
+
await placeOrder(cartId)
|
|
354
|
+
// Fail when payment not authorized
|
|
355
|
+
return NextResponse.redirect(
|
|
356
|
+
`${origin}/${countryCode}/checkout?step=review&error=payment_failed`
|
|
357
|
+
)
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
const orderCacheTag = await getCacheTag("orders")
|
|
361
|
+
revalidateTag(orderCacheTag)
|
|
362
|
+
removeCartId()
|
|
363
|
+
return NextResponse.redirect(
|
|
364
|
+
`${origin}/${countryCode}/order/${orderId}/confirmed`
|
|
365
|
+
)
|
|
366
|
+
}
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
Этот API-роут обрабатывает редирект от YooKassa после попытки оплаты. Он получает актуальное состояние корзины, чтобы убедиться, что все изменения, внесённые во время оплаты, были отражены.
|
|
370
|
+
|
|
371
|
+
Если в корзине нет связанного идентификатора заказа, обработчик роута пытается оформить заказ. В случае успеха покупатель перенаправляется на страницу подтверждения заказа. Если же при обработке корзины возникла ошибка, покупатель возвращается на страницу оформления заказа с указанием ошибки и может повторить процесс оплаты заказа.
|
|
372
|
+
|
|
373
|
+
Когда оплата проходит успешно, роут повторно валидирует кэшированные данные корзины и заказа, удаляет cookie корзины и перенаправляет покупателя на страницу подтверждения заказа. Это гарантирует корректное завершение платёжного процесса и сохранение актуальных данных в storefront.
|
|
374
|
+
|
|
375
|
+
### Пример
|
|
376
|
+
|
|
377
|
+
Вы можете ознакомиться с изменениями, внесенными в стартовый шаблон [Medusa Next.js Starter Template](https://github.com/medusajs/nextjs-starter-medusa) в директории [`examples/medusa-storefront`](https://github.com/sergkoudi/medusa-payment-yookassa/tree/main/examples/medusa-storefront).
|
|
378
|
+
|
|
379
|
+
Полный код интеграции можно посмотреть в разделе [comparison page](https://github.com/sergkoudi/medusa-payment-yookassa/compare/v0.0.3...main), откройте вкладку `Files changed` и изучите различия в каталоге `examples/medusa-storefront`. Или запустите `diff` в терминале:
|
|
380
|
+
|
|
381
|
+
```bash
|
|
382
|
+
git clone https://github.com/sergkoudi/medusa-payment-yookassa
|
|
383
|
+
cd medusa-payment-yookassa
|
|
384
|
+
git diff v0.0.3...main -- examples/medusa-storefront
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
## Разработка
|
|
388
|
+
|
|
389
|
+
Документацию по развертыванию окружения для разработки можно найти [здесь](https://github.com/sergkoudi/medusa-payment-yookassa/tree/main/examples).
|
|
390
|
+
|
|
391
|
+
## Лицензия
|
|
392
|
+
|
|
393
|
+
Распространяется на условиях [лицензии MIT](LICENSE).
|