Skip to content

feat: add yookassa payment provider - #5711

Closed
artemk1337 wants to merge 5 commits into
QuantumNous:mainfrom
artemk1337:artemk1337/yookassa-provider
Closed

feat: add yookassa payment provider#5711
artemk1337 wants to merge 5 commits into
QuantumNous:mainfrom
artemk1337:artemk1337/yookassa-provider

Conversation

@artemk1337

@artemk1337 artemk1337 commented Jun 24, 2026

Copy link
Copy Markdown

⚠️ 提交说明 / PR Notice

Important

  • 请提供人工撰写的简洁摘要,避免直接粘贴未经整理的 AI 输出。

📝 变更描述 / Description

(简述:做了什么?为什么这样改能生效?请基于你对代码逻辑的理解来写,避免粘贴未经整理的内容)

🚀 变更类型 / Type of change

  • 🐛 Bug 修复 (Bug fix) - 请关联对应 Issue,避免将设计取舍、理解偏差或预期不一致直接归类为 bug
  • ✨ 新功能 (New feature) - 重大特性建议先通过 Issue 沟通
  • ⚡ 性能优化 / 重构 (Refactor)
  • 📝 文档更新 (Documentation)

🔗 关联任务 / Related Issue

  • Closes # (如有)

✅ 提交前检查项 / Checklist

  • 人工确认: 我已亲自整理并撰写此描述,没有直接粘贴未经处理的 AI 输出。
  • 非重复提交: 我已搜索现有的 IssuesPRs,确认不是重复提交。
  • Bug fix 说明: 若此 PR 标记为 Bug fix,我已提交或关联对应 Issue,且不会将设计取舍、预期不一致或理解偏差直接归类为 bug。
  • 变更理解: 我已理解这些更改的工作原理及可能影响。
  • 范围聚焦: 本 PR 未包含任何与当前任务无关的代码改动。
  • 本地验证: 已在本地运行并通过测试或手动验证,维护者可以据此复核结果。
  • 安全合规: 代码中无敏感凭据,且符合项目代码规范。

📸 运行证明 / Proof of Work

(请在此粘贴截图、关键日志或测试报告,以证明变更生效)

Summary by CodeRabbit

  • New Features
    • Added YooKassa SBP top-up flow with amount calculation, confirmation redirect, and webhook-based completion.
    • Added YooKassa gateway/settings (enable, Shop ID, masked secret key, return URL, allowed methods) and related wallet endpoints.
    • Introduced PaymentMetadata storage to map YooKassa trades and complete top-ups.
  • Bug Fixes
    • Masked YooKassa secret key in settings responses and preserved it when unchanged.
    • Improved YooKassa webhook validation and ensured idempotent quota updates.
    • Unified discount calculation using exact and threshold-based tiers across providers.
  • Tests
    • Expanded YooKassa webhook/client tests and added coverage for tiered discount parsing/selection.

@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds YooKassa SBP payment support, payment metadata persistence, secret-key masking in admin options, and threshold-aware discount handling across backend and frontend.

Changes

YooKassa payment gateway integration

Layer / File(s) Summary
Discount config and reuse
setting/operation_setting/payment_setting.go, setting/operation_setting/payment_setting_test.go, controller/topup.go, controller/topup_stripe.go, controller/topup_waffo.go, controller/topup_waffo_pancake.go, controller/topup_waffo_pancake_test.go, web/default/src/features/wallet/..., web/default/src/features/system-settings/integrations/amount-discount-visual-editor.tsx
Replaces exact-amount discount maps with AmountDiscountConfig, adds threshold support and JSON handling, and updates discount selection in controllers and wallet helpers.
YooKassa settings and option masking
setting/payment_yookassa.go, model/option.go, controller/option.go, controller/payment_webhook_availability.go, web/default/src/features/system-settings/types.ts, web/default/src/features/system-settings/billing/*, web/default/src/features/system-settings/integrations/payment-settings-section.tsx
Adds YooKassa setting variables, synchronizes them through option state, masks the secret key in option responses, skips masked secret updates, updates YooKassa availability checks, and adds billing form fields.
Payment metadata and recharge persistence
model/payment_metadata.go, model/main.go, model/task_cas_test.go, model/topup.go, model/payment_method_guard_test.go
Defines PaymentMetadata, adds persistence and lookup helpers, registers the model in migrations and test cleanup, and adds the YooKassa recharge transaction and quota resolution logic.
YooKassa service client and payment handlers
service/yookassa.go, service/yookassa_test.go, controller/topup_yookassa.go, controller/topup_yookassa_test.go, controller/topup.go, router/api-router.go
Implements the YooKassa HTTP client, request/response DTOs, payment creation and retrieval, amount/pay/webhook handlers, webhook tests, top-up info exposure, and route registration.
Wallet payment UI and hooks
web/default/src/features/wallet/types.ts, web/default/src/features/wallet/constants.ts, web/default/src/features/wallet/api.ts, web/default/src/features/wallet/lib/payment.ts, web/default/src/features/wallet/lib/ui.tsx, web/default/src/features/wallet/hooks/*, web/default/src/features/wallet/index.tsx, web/default/src/features/wallet/components/recharge-form-card.tsx
Extends wallet types and API calls for YooKassa, adds the YooKassa payment type and icon, updates top-up info parsing and payment processing, and switches discount display to the new helper logic.
Locale strings for YooKassa
web/default/src/i18n/locales/*.json
Adds YooKassa-related translation entries across the locale files for payment labels, gateway settings, discount text, and masked secret hints.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant WalletHook
  participant Controller
  participant YooKassaClient
  participant DB
  participant YooKassaAPI

  User->>WalletHook: submit YooKassa top-up
  WalletHook->>Controller: POST /api/user/yookassa/pay
  Controller->>DB: insert pending TopUp + PaymentMetadata
  Controller->>YooKassaClient: CreatePayment(...)
  YooKassaClient->>YooKassaAPI: POST /v3/payments
  YooKassaAPI-->>YooKassaClient: confirmation_url + payment_id
  Controller-->>WalletHook: confirmation_url, trade_no
  YooKassaAPI->>Controller: payment.succeeded webhook
  Controller->>YooKassaClient: GetPayment(payment_id)
  Controller->>DB: RechargeYooKassa(tradeNo)
  Controller-->>User: top-up success
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • QuantumNous/new-api#1352: Modifies the Stripe top-up payment flow in controller/topup_stripe.go, which is part of the same payment-calculation area updated here.
  • QuantumNous/new-api#4089: Touches controller/topup.go payment amount logic and provider-specific top-up state, which this PR also changes for YooKassa.
  • QuantumNous/new-api#4935: Changes controller/option.go update handling in the same function that now special-cases the YooKassa secret key.

Suggested labels

enhancement

Suggested reviewers

  • Calcium-Ion
  • creamlike1024

Poem

🐰 Hop hop, the pay paths bloom,
YooKassa lights the checkout room.
Thresholds dance and secrets hide,
Webhooks bounce with quota pride.
A carrot for the code to munch—
SBP now joins the payment bunch!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 13.92% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding the YooKassa payment provider.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
web/default/src/features/system-settings/integrations/payment-settings-section.tsx (1)

114-120: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Validate discount amount keys as backend-compatible integers.

This top-level shape check allows values like [{"min_amount":10.5,"discount":0.95}] or {"10.5":0.95}. The backend threshold model uses MinAmount int, and exact keys are parsed as integers, so these accepted configs can fail to load or be silently ignored.

Suggested validation tightening
   AmountDiscount: z.string().superRefine((value, ctx) => {
     const error = getJsonError(
       value,
-      (parsed) =>
-        !!parsed &&
-        (Array.isArray(parsed) ||
-          (typeof parsed === 'object' && !Array.isArray(parsed)))
+      (parsed) => {
+        if (!parsed) return false
+        if (Array.isArray(parsed)) {
+          return parsed.every((item) => {
+            if (!item || typeof item !== 'object') return false
+            const record = item as Record<string, unknown>
+            return (
+              Number.isInteger(Number(record.min_amount)) &&
+              Number.isFinite(Number(record.discount))
+            )
+          })
+        }
+        if (typeof parsed !== 'object') return false
+        return Object.entries(parsed).every(
+          ([amount, discount]) =>
+            Number.isInteger(Number(amount)) &&
+            Number.isFinite(Number(discount))
+        )
+      }
     )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@web/default/src/features/system-settings/integrations/payment-settings-section.tsx`
around lines 114 - 120, The AmountDiscount validation in
payment-settings-section.tsx is too permissive and currently accepts non-integer
threshold values and numeric object keys that the backend cannot use. Tighten
the existing z.string().superRefine logic by updating the getJsonError check so
it only accepts backend-compatible structures with integer min_amount values and
integer-like keys, rejecting decimals such as 10.5 in both array entries and
object keys. Keep the fix local to the AmountDiscount validator and use the
existing helper symbols getJsonError and superRefine to locate the change.

Source: Coding guidelines

🧹 Nitpick comments (3)
web/default/src/features/wallet/hooks/use-payment.ts (1)

73-80: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Replace this nested ternary with explicit branches.

This dispatch is already past the repo's TS guideline limit and will get harder to extend as more providers are added. An if/else ladder or small helper keeps the branching readable.

As per coding guidelines, "web/default/**/*.{ts,tsx}: Do not use 2+ levels of nested ternary expressions; use if-else, early returns, or extracted functions instead."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/default/src/features/wallet/hooks/use-payment.ts` around lines 73 - 80,
The payment amount dispatch in usePayment is using a multi-level nested ternary
that violates the TS style guideline and is hard to extend. Refactor the branch
in usePayment to use explicit if/else logic or an extracted helper for the
provider selection across isStripe, isPancake, and isYooKassa, while keeping the
same calls to calculateStripeAmount, calculateWaffoPancakeAmount,
calculateYooKassaAmount, and calculateAmount.

Source: Coding guidelines

controller/topup_waffo_pancake_test.go (1)

44-49: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add one threshold-based regression case.

This table only exercises AmountDiscountConfig.Exact, so it will not catch a wiring regression where getWaffoPancakePayMoney stops honoring Thresholds. A single case that sets Thresholds and verifies the highest matching min_amount wins would cover the new behavior this PR introduced.

As per coding guidelines, "**/*_test.go: Backend tests must protect real behavior, API contracts, billing/accounting invariants, data compatibility, or regression paths."

Also applies to: 53-89

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@controller/topup_waffo_pancake_test.go` around lines 44 - 49, Add a
regression test case in the `getWaffoPancakePayMoney`/`Test...` table that sets
`operation_setting.GetPaymentSetting().AmountDiscount.Thresholds` and asserts
the correct discount is applied from the highest matching `min_amount`; this
table currently only covers `AmountDiscountConfig.Exact`, so include a new row
that verifies threshold selection behavior and protects against wiring
regressions in `getWaffoPancakePayMoney`.

Source: Coding guidelines

controller/topup_yookassa_test.go (1)

114-160: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use assert for the non-fatal value checks in these new tests.

These cases are good regression coverage, but the final state/value assertions should use assert rather than require per the backend test guideline so one mismatch does not abort the rest of the checks in the case.

As per coding guidelines, "New or substantially rewritten Go backend tests MUST use github.com/stretchr/testify/require for setup and fatal assertions, and github.com/stretchr/testify/assert for non-fatal value checks."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@controller/topup_yookassa_test.go` around lines 114 - 160, The new YooKassa
webhook tests use require for final value/state checks that should be non-fatal,
which conflicts with the backend test guideline. In the test functions
TestYooKassaWebhookPaymentSucceeded, TestYooKassaWebhookIsIdempotent,
TestYooKassaWebhookRejectsInvalidAmount, and
TestYooKassaWebhookRejectsInvalidStatus, keep require for setup and fatal
failures, but switch the post-conditions on topUp, user, status, quota, and
response code checks to assert so one mismatch does not stop the remaining
validations.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@controller/payment_webhook_availability.go`:
- Around line 109-110: The webhook availability check is incorrectly tied to
top-up enablement, so existing payment callbacks can be blocked when the gateway
is disabled. Update isYooKassaWebhookEnabled to use webhook configuration
readiness instead of isYooKassaTopUpEnabled, and wire it to the webhook-specific
helper such as isYooKassaWebhookConfigured so webhook handling stays independent
from new top-up availability.

In `@controller/topup_yookassa.go`:
- Around line 121-123: The token-mode amount conversion in TopUp should not
truncate the requested quota before it is stored and later used by
RechargeYooKassa. Update the logic around amount assignment so TopUp.Amount
preserves the original request value (or otherwise stores the exact token
quantity needed for later crediting), and keep payMoney calculation separate
from the persisted recharge amount. Use the TopUp flow and RechargeYooKassa as
the key points to ensure quota is credited from the full requested amount rather
than the floored value.
- Around line 225-280: The fallback in YooKassaNotify is currently ineffective
because validateYooKassaPayment still requires payment.Metadata["trade_no"] to
match topUp.TradeNo even after tradeNo was recovered from
GetPaymentMetadataByExternalPaymentID. Update validateYooKassaPayment to stop
depending on webhook metadata for trade_no, and instead validate against the
resolved order context (topUp.TradeNo) while keeping the other status, paid,
currency, and amount checks intact. Use the existing YooKassaNotify and
validateYooKassaPayment flow to locate the change.

In `@model/payment_metadata.go`:
- Around line 5-7: The payment metadata schema does not enforce the lookup
invariant used by GetPaymentMetadataByExternalPaymentID, where payment_provider
and external_payment_id are treated as a unique pair. Update the PaymentMetadata
model to add a composite unique constraint or equivalent composite index on that
pair, and keep the existing individual fields consistent with that uniqueness
guarantee. If records may be created before the provider is known, add a
write-time guard in the creation path to prevent duplicates once
payment_provider is set.

In `@service/yookassa.go`:
- Around line 20-21: The YooKassa payment client currently defaults to
http.DefaultClient, which can hang indefinitely if a request path misses
YooKassaRequestTimeoutContext. Update the default in YooKassaHTTPClient or
enforce a timeout inside do so all YooKassa requests always have a bounded
timeout. Use the YooKassaHTTPClient and do symbols to locate the client setup
and request execution paths.

In `@setting/operation_setting/payment_setting_test.go`:
- Around line 10-32: Add a regression test for
AmountDiscountConfig.DiscountForAmount that exercises both Exact and Thresholds
together, since the current TestAmountDiscountConfigThresholds and
TestAmountDiscountConfigExactAmountCompatibility only cover them separately.
Update payment_setting_test.go with a mixed-input case that unmarshals a config
containing both forms and asserts the intended precedence/selection behavior for
representative amounts, using AmountDiscountConfig.DiscountForAmount as the
target behavior to lock down.

In `@setting/operation_setting/payment_setting.go`:
- Around line 56-68: Preserve exact-match precedence in
AmountDiscountConfig.DiscountForAmount: the current threshold loop can overwrite
a discount already selected from discounts.Exact, which breaks the exact-first
contract used by top-up pricing. Update the logic in DiscountForAmount so that
an Exact[amount] match wins and the thresholds in discounts.Thresholds are only
considered when no valid exact discount was found, keeping the multiplier stable
for controller/topup.go and controller/topup_waffo_pancake.go.

In `@web/default/src/features/wallet/hooks/use-payment.ts`:
- Around line 109-124: The payment amount is being truncated too early in
use-payment.ts, which affects Stripe and the generic request path as well as
YooKassa. Update the request flow in usePayment so that Math.floor(topupAmount)
is applied only in the isYooKassa branch, while requestStripePayment and
requestPayment continue sending the original topupAmount value; use the existing
requestStripePayment, requestYooKassaPayment, and requestPayment symbols to keep
the provider-specific normalization isolated.

In `@web/default/src/i18n/locales/en.json`:
- Line 3655: The i18n entry in the locale JSON uses a mixed-script key, with
Cyrillic characters in the source string while the displayed text uses Latin
SBP, which can cause translation lookup mismatches. Update the relevant key in
the en.json locale entry to use one canonical Latin SBP form consistently, and
make sure the same normalized key is used anywhere this string is referenced
through t(...).

---

Outside diff comments:
In
`@web/default/src/features/system-settings/integrations/payment-settings-section.tsx`:
- Around line 114-120: The AmountDiscount validation in
payment-settings-section.tsx is too permissive and currently accepts non-integer
threshold values and numeric object keys that the backend cannot use. Tighten
the existing z.string().superRefine logic by updating the getJsonError check so
it only accepts backend-compatible structures with integer min_amount values and
integer-like keys, rejecting decimals such as 10.5 in both array entries and
object keys. Keep the fix local to the AmountDiscount validator and use the
existing helper symbols getJsonError and superRefine to locate the change.

---

Nitpick comments:
In `@controller/topup_waffo_pancake_test.go`:
- Around line 44-49: Add a regression test case in the
`getWaffoPancakePayMoney`/`Test...` table that sets
`operation_setting.GetPaymentSetting().AmountDiscount.Thresholds` and asserts
the correct discount is applied from the highest matching `min_amount`; this
table currently only covers `AmountDiscountConfig.Exact`, so include a new row
that verifies threshold selection behavior and protects against wiring
regressions in `getWaffoPancakePayMoney`.

In `@controller/topup_yookassa_test.go`:
- Around line 114-160: The new YooKassa webhook tests use require for final
value/state checks that should be non-fatal, which conflicts with the backend
test guideline. In the test functions TestYooKassaWebhookPaymentSucceeded,
TestYooKassaWebhookIsIdempotent, TestYooKassaWebhookRejectsInvalidAmount, and
TestYooKassaWebhookRejectsInvalidStatus, keep require for setup and fatal
failures, but switch the post-conditions on topUp, user, status, quota, and
response code checks to assert so one mismatch does not stop the remaining
validations.

In `@web/default/src/features/wallet/hooks/use-payment.ts`:
- Around line 73-80: The payment amount dispatch in usePayment is using a
multi-level nested ternary that violates the TS style guideline and is hard to
extend. Refactor the branch in usePayment to use explicit if/else logic or an
extracted helper for the provider selection across isStripe, isPancake, and
isYooKassa, while keeping the same calls to calculateStripeAmount,
calculateWaffoPancakeAmount, calculateYooKassaAmount, and calculateAmount.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: fb2a94ac-f5fe-4c25-ba18-4418c9272ce2

📥 Commits

Reviewing files that changed from the base of the PR and between 64eafc9 and 1f52fba.

📒 Files selected for processing (40)
  • controller/option.go
  • controller/payment_webhook_availability.go
  • controller/topup.go
  • controller/topup_stripe.go
  • controller/topup_waffo.go
  • controller/topup_waffo_pancake.go
  • controller/topup_waffo_pancake_test.go
  • controller/topup_yookassa.go
  • controller/topup_yookassa_test.go
  • model/main.go
  • model/option.go
  • model/payment_metadata.go
  • model/task_cas_test.go
  • model/topup.go
  • router/api-router.go
  • service/yookassa.go
  • service/yookassa_test.go
  • setting/operation_setting/payment_setting.go
  • setting/operation_setting/payment_setting_test.go
  • setting/payment_yookassa.go
  • web/default/src/features/system-settings/billing/index.tsx
  • web/default/src/features/system-settings/billing/section-registry.tsx
  • web/default/src/features/system-settings/integrations/amount-discount-visual-editor.tsx
  • web/default/src/features/system-settings/integrations/payment-settings-section.tsx
  • web/default/src/features/system-settings/types.ts
  • web/default/src/features/wallet/api.ts
  • web/default/src/features/wallet/components/recharge-form-card.tsx
  • web/default/src/features/wallet/constants.ts
  • web/default/src/features/wallet/hooks/use-payment.ts
  • web/default/src/features/wallet/hooks/use-topup-info.ts
  • web/default/src/features/wallet/index.tsx
  • web/default/src/features/wallet/lib/payment.ts
  • web/default/src/features/wallet/lib/ui.tsx
  • web/default/src/features/wallet/types.ts
  • web/default/src/i18n/locales/en.json
  • web/default/src/i18n/locales/fr.json
  • web/default/src/i18n/locales/ja.json
  • web/default/src/i18n/locales/ru.json
  • web/default/src/i18n/locales/vi.json
  • web/default/src/i18n/locales/zh.json

Comment thread controller/payment_webhook_availability.go Outdated
Comment thread controller/topup_yookassa.go Outdated
Comment thread controller/topup_yookassa.go
Comment thread model/payment_metadata.go Outdated
Comment thread service/yookassa.go Outdated
Comment thread setting/operation_setting/payment_setting_test.go
Comment thread setting/operation_setting/payment_setting.go
Comment thread web/default/src/features/wallet/hooks/use-payment.ts Outdated
Comment thread web/default/src/i18n/locales/en.json Outdated
@artemk1337
artemk1337 force-pushed the artemk1337/yookassa-provider branch from 1f52fba to ddd894e Compare June 24, 2026 08:44

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (1)
controller/topup_waffo_pancake_test.go (1)

91-103: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use assert.InDelta for non-fatal value assertions.

Line 102 currently uses require.InDelta; this should be assert.InDelta for a non-fatal value check per the test guideline.

♻️ Suggested change
-		require.InDelta(t, 31.875, actual, 0.000001)
+		assert.InDelta(t, 31.875, actual, 0.000001)

(If not already present, add github.com/stretchr/testify/assert to imports.)

As per coding guidelines, backend tests should use require for setup/fatal assertions and assert for non-fatal value checks.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@controller/topup_waffo_pancake_test.go` around lines 91 - 103, The test in
getWaffoPancakePayMoney is using a fatal value assertion where a non-fatal check
is preferred. Update the threshold discount test in
controller/topup_waffo_pancake_test.go to use assert.InDelta instead of
require.InDelta, and add the testify/assert import if it is not already present,
while keeping require for setup or fatal preconditions only.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@controller/topup_yookassa_test.go`:
- Around line 25-49: The test setup in topup_yookassa_test.go mutates
process-wide globals but the cleanup in the same helper restores only hard-coded
defaults, which can leak state across tests. Capture the original values for the
YooKassa and payment-related globals before changing them, then restore those
saved values in the t.Cleanup block using the existing symbols like
common.SetDatabaseTypes, setting.YooKassaEnabled, setting.YooKassaShopID,
setting.YooKassaSecretKey, operation_setting.GetPaymentSetting(),
service.YooKassaAPIBaseURL, and service.YooKassaHTTPClient. Avoid resetting the
HTTP client to http.DefaultClient unless that was the original value.
- Around line 103-106: Update the webhook fixture in the YooKassa notification
test to match the real YooKassa contract: the payload should use type as
notification and event as payment.succeeded instead of relying on
payment.succeeded in type. Adjust the request body in the test around the
httptest.NewRequest call so it exercises the actual webhook shape and verifies
the handler logic against YooKassa’s expected fields.

In `@controller/topup_yookassa.go`:
- Around line 147-173: The issue is that `paymentMetadata.Insert()` can fail
after `CreatePayment` has already succeeded, and `topup_yookassa.go` currently
marks the order failed, which breaks later webhook processing. In
`CreatePayment` flow, avoid transitioning the top-up to `TopUpStatusFailed`
after a remote YooKassa payment exists; instead keep it pending and
retry/persist the missing local metadata, or ensure the exact quota is stored
before calling `service.NewYooKassaClient(nil).CreatePayment` and is recoverable
in the webhook path. Use the existing `paymentMetadata.Insert`,
`model.UpdatePendingTopUpStatus`, and webhook-handling logic to locate and
adjust this flow.
- Around line 25-30: The YooKassa webhook payload struct is reading the wrong
field for event routing, so the handler misses successful payment notifications.
Update yooKassaWebhookPayload and the webhook handling logic in
topup_yookassa.go to use the event field instead of Type for detecting success,
while keeping Object.ID for the payment identifier. Make sure the branch that
marks top-ups as completed checks for payment.succeeded from the event value so
real success webhooks are processed.

In `@model/topup.go`:
- Around line 655-667: getYooKassaTopUpQuota currently falls back to deriving
quota from TopUp.Amount, which can over-credit token-mode YooKassa orders when
quota_to_add is missing or invalid. Update this function to fail closed for
YooKassa token-mode flows: in the getYooKassaTopUpQuota path, only return a
positive quota when PaymentMetadata.Metadata unmarshal succeeds and quota_to_add
parses to a valid value, otherwise return an error or a safe zero/explicit
failure instead of multiplying Amount by QuotaPerUnit. If you need a fallback,
move it to a provider-independent exact quota field on TopUp rather than using
Amount here.

---

Nitpick comments:
In `@controller/topup_waffo_pancake_test.go`:
- Around line 91-103: The test in getWaffoPancakePayMoney is using a fatal value
assertion where a non-fatal check is preferred. Update the threshold discount
test in controller/topup_waffo_pancake_test.go to use assert.InDelta instead of
require.InDelta, and add the testify/assert import if it is not already present,
while keeping require for setup or fatal preconditions only.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c51ef965-a42a-4d02-95d2-0508ce4284f1

📥 Commits

Reviewing files that changed from the base of the PR and between 1f52fba and ddd894e.

📒 Files selected for processing (20)
  • controller/option.go
  • controller/payment_webhook_availability.go
  • controller/topup.go
  • controller/topup_stripe.go
  • controller/topup_waffo.go
  • controller/topup_waffo_pancake.go
  • controller/topup_waffo_pancake_test.go
  • controller/topup_yookassa.go
  • controller/topup_yookassa_test.go
  • model/main.go
  • model/option.go
  • model/payment_metadata.go
  • model/task_cas_test.go
  • model/topup.go
  • router/api-router.go
  • service/yookassa.go
  • service/yookassa_test.go
  • setting/operation_setting/payment_setting.go
  • setting/operation_setting/payment_setting_test.go
  • setting/payment_yookassa.go
✅ Files skipped from review due to trivial changes (1)
  • setting/payment_yookassa.go
🚧 Files skipped from review as they are similar to previous changes (12)
  • model/task_cas_test.go
  • controller/topup_waffo.go
  • router/api-router.go
  • setting/operation_setting/payment_setting_test.go
  • controller/topup_stripe.go
  • controller/topup.go
  • controller/topup_waffo_pancake.go
  • controller/option.go
  • model/option.go
  • service/yookassa_test.go
  • setting/operation_setting/payment_setting.go
  • model/payment_metadata.go

Comment thread controller/topup_yookassa_test.go
Comment thread controller/topup_yookassa_test.go
Comment thread controller/topup_yookassa.go
Comment thread controller/topup_yookassa.go Outdated
Comment thread model/topup.go Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
web/default/src/features/system-settings/integrations/payment-settings-section.tsx (2)

490-490: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Don’t persist unsupported YooKassa payment methods.

The UI says only SBP is supported, but this free-text field can still save arbitrary values. Normalize this to sbp or validate the field so unsupported methods can’t be stored.

Also applies to: 695-702, 1440-1456

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@web/default/src/features/system-settings/integrations/payment-settings-section.tsx`
at line 490, The YooKassa payment method field is still being persisted as free
text in the payment settings flow, which allows unsupported values to be saved.
Update the handling in payment-settings-section.tsx around the
YooKassaPaymentMethods mapping and the related validation/submit paths
(including the other referenced sections) so the value is normalized to sbp or
rejected unless it matches the supported option. Use the existing
YooKassaPaymentMethods field and the form submit/validation logic to ensure only
the supported method can be stored.

1417-1433: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Align the return URL example with the origin-only validation.

Line 1425 shows https://example.com/console/topup, but the schema is described as origin-only. Use an origin-only placeholder/description, or relax the schema to allow paths.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@web/default/src/features/system-settings/integrations/payment-settings-section.tsx`
around lines 1417 - 1433, The Payment return URL field in the YooKassa settings
is using a path-based example that conflicts with the origin-only validation.
Update the placeholder/description in payment-settings-section.tsx around the
FormField for YooKassaReturnURL to show an origin-only value, or, if paths are
intended, adjust the corresponding schema validation to accept full URLs with
paths so the example and validation match.
model/topup.go (1)

646-648: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Check that the quota update actually touched a user row.

Update(...).Error is nil when no rows match, so a deleted/missing user would leave the top-up marked successful without crediting anyone. Check RowsAffected and return an error to roll back the transaction.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@model/topup.go` around lines 646 - 648, The quota increment in the top-up
transaction only checks Update(...).Error, so a missing or deleted user can
still let the transaction succeed without crediting anyone. Update the logic in
the top-up flow around tx.Model(&User{}).Where(...).Update(...) to also inspect
RowsAffected, and if it is zero, return an error so the transaction rolls back
and the top-up is not marked successful.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@model/topup.go`:
- Around line 635-642: The top-up success log is being recorded even for the
losing transaction path that only observes an already-successful status. In the
top-up flow around the conditional update and RecordTopupLog usage in topup.go,
add a quotaCredited flag that is set only after the user quota update succeeds,
and gate RecordTopupLog on that flag instead of the current status check. Update
the duplicate-success handling in the same top-up transaction path so only the
transaction that actually credits quota emits the log.

---

Outside diff comments:
In `@model/topup.go`:
- Around line 646-648: The quota increment in the top-up transaction only checks
Update(...).Error, so a missing or deleted user can still let the transaction
succeed without crediting anyone. Update the logic in the top-up flow around
tx.Model(&User{}).Where(...).Update(...) to also inspect RowsAffected, and if it
is zero, return an error so the transaction rolls back and the top-up is not
marked successful.

In
`@web/default/src/features/system-settings/integrations/payment-settings-section.tsx`:
- Line 490: The YooKassa payment method field is still being persisted as free
text in the payment settings flow, which allows unsupported values to be saved.
Update the handling in payment-settings-section.tsx around the
YooKassaPaymentMethods mapping and the related validation/submit paths
(including the other referenced sections) so the value is normalized to sbp or
rejected unless it matches the supported option. Use the existing
YooKassaPaymentMethods field and the form submit/validation logic to ensure only
the supported method can be stored.
- Around line 1417-1433: The Payment return URL field in the YooKassa settings
is using a path-based example that conflicts with the origin-only validation.
Update the placeholder/description in payment-settings-section.tsx around the
FormField for YooKassaReturnURL to show an origin-only value, or, if paths are
intended, adjust the corresponding schema validation to accept full URLs with
paths so the example and validation match.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2cda48a8-fbe1-45a8-875f-ba4ed3a4f188

📥 Commits

Reviewing files that changed from the base of the PR and between 28c64e0 and 6e0819d.

📒 Files selected for processing (11)
  • controller/topup_yookassa.go
  • controller/topup_yookassa_test.go
  • model/payment_method_guard_test.go
  • model/topup.go
  • web/default/src/features/system-settings/integrations/payment-settings-section.tsx
  • web/default/src/i18n/locales/en.json
  • web/default/src/i18n/locales/fr.json
  • web/default/src/i18n/locales/ja.json
  • web/default/src/i18n/locales/ru.json
  • web/default/src/i18n/locales/vi.json
  • web/default/src/i18n/locales/zh.json
✅ Files skipped from review due to trivial changes (5)
  • web/default/src/i18n/locales/fr.json
  • web/default/src/i18n/locales/ja.json
  • web/default/src/i18n/locales/ru.json
  • web/default/src/i18n/locales/zh.json
  • web/default/src/i18n/locales/en.json
🚧 Files skipped from review as they are similar to previous changes (3)
  • controller/topup_yookassa_test.go
  • web/default/src/i18n/locales/vi.json
  • controller/topup_yookassa.go

Comment thread model/topup.go
@artemk1337
artemk1337 force-pushed the artemk1337/yookassa-provider branch 2 times, most recently from 61640eb to b25e068 Compare June 24, 2026 14:35
@artemk1337
artemk1337 force-pushed the artemk1337/yookassa-provider branch from b25e068 to 960239d Compare June 24, 2026 17:01
@Calcium-Ion

Copy link
Copy Markdown
Member

感谢您的贡献,很抱歉我们暂不接受支付相关的直接pr,如果你是该支付的相关维护者,可以发送邮件给我们进行探讨 newapi@quantumnous.com

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants