Skip to content

[Feature Request] Waffo Pancake gateway — full integration with subscription support + admin catalog binding flow#4935

Merged
seefs001 merged 26 commits into
QuantumNous:mainfrom
Hill-waffo:refactor/pancake-sdk-migration
May 22, 2026
Merged

[Feature Request] Waffo Pancake gateway — full integration with subscription support + admin catalog binding flow#4935
seefs001 merged 26 commits into
QuantumNous:mainfrom
Hill-waffo:refactor/pancake-sdk-migration

Conversation

@Hill-waffo

@Hill-waffo Hill-waffo commented May 18, 2026

Copy link
Copy Markdown
Contributor

📝 变更描述 / Description

集成 Waffo Pancake 作为支付通道,覆盖钱包充值订阅套餐两个入口。Waffo Pancake 是 Merchant-of-Record 服务,让个人开发者和 OPC 一人公司免注册公司就能全球收款。

主要工作:

  • 用户侧:钱包页 / 订阅页都增加 Pancake 选项(🥞),跳 Pancake 托管 checkout 完成支付
  • 操作员侧:System Settings → Integrations → Waffo Pancake 一站式绑定(填凭证 → 下拉里选 Store + Product 或一键创建 → Save)
  • 单 webhook 端点 /api/waffo-pancake/webhook/:env 用 trade_no 前缀分流钱包 / 订阅完成
  • 跟 PR feat(payment): enable Waffo Pancake pay #4904 关系:本 PR 是 superset,包含 feat(payment): enable Waffo Pancake pay #4904 范围 + 订阅集成 + admin 绑定流程。

首页 footer 显示用户协议 / 隐私政策链接(commit 0af011ef):

复用 new-api 已有的 user_agreement_enabled / privacy_policy_enabled 状态(登录页 / 注册页 TermsFooter 用的是同一套),让未登录用户也能从首页直接访问 admin 配置的协议正文。

跟 Pancake 集成的关联:MoR 网关接入通常要求商户在站点显著位置暴露 T&C / 隐私政策入口(买家在 Pancake hosted checkout 之前需要能查看),所以放在同一 PR 一并补齐。可独立 cherry-pick。

依赖 github.com/waffo-com/waffo-pancake-sdk-go v0.2.0,无其他外部依赖。

🚀 变更类型 / Type of change

  • ✨ 新功能 (New feature)

✅ 提交前检查项 / Checklist

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

📸 运行证明 / Proof of Work

5.18._1.5x.mp4

Summary by CodeRabbit

  • New Features

    • Added Waffo Pancake payment support for subscription plans
    • Integrated Waffo Pancake SDK for improved webhook handling and payment processing
    • Enhanced Waffo Pancake configuration workflow with store and product binding
  • Improvements

    • Streamlined Waffo Pancake settings by removing unnecessary configuration options
    • Strengthened webhook signature verification and environment validation
    • Updated UI for subscription plan creation and payment method selection with Waffo Pancake integration

Hill-waffo and others added 16 commits May 15, 2026 19:22
…dpoints

Backend: new POST endpoints under /api/option/waffo-pancake/ for listing
the merchant's stores + products, minting a fresh store/product pair in
one round-trip, and atomically committing all five operator-controlled
fields. All three fall back to persisted credentials when the body is
blank, so a returning admin doesn't need to re-paste the private key
(which is stripped from GET /api/option/ by the sensitive-key filter).
Catalog query passes `stores(limit: 100)` to bypass the SDK's default
single-store cap. A GraphQL response envelope-fix transport works
around a single/double wrap mismatch in waffo-pancake-sdk-go v0.1.1.

Frontend: new no-auto-save admin section. Typed creds debounce-verify
against /catalog, "+ Create" mints a fresh pair via /pair and refreshes
the dropdown from the authoritative GraphQL response (rather than
echoing the response body), Save commits all five fields atomically.
Dropdowns seed from saved bindings on mount with a raw-ID fallback
item while the catalog query is in flight, so refreshing the page
shows the bound store + product immediately; both bound IDs are also
rendered next to the Save button.

Settings: drop the legacy Enabled / Sandbox / WebhookPublicKey fields.
The gateway is on iff MerchantID + PrivateKey + ProductID are
populated; environment is decided by the API key the operator pastes;
webhook public keys ship inside the SDK.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Routes:
- POST /api/user/waffo-pancake/amount (quote)
- POST /api/user/waffo-pancake/pay (create checkout session)
- POST /api/waffo-pancake/webhook/:env (test/prod-segregated webhook)

All three were already implemented in the controllers but kept commented
in the router. Uncommenting unblocks the end-user wallet top-up flow that
the binding-flow commit (4f7ea8b) made configurable in the admin UI.

User top-up page now lists Waffo Pancake above legacy Waffo
(controller/topup.go reordering); the Pancake gateway gates on
presence of MerchantID + PrivateKey + ProductID rather than the
removed Enabled/Sandbox/WebhookPublicKey flags
(controller/option.go drops the isVisiblePublicKeyOption helper that
exposed those keys).

Wallet redirects in-tab (window.location.href) rather than
window.open in both default and classic frontends — window.open fires
after `await pay(...)` returns, by which point the user-gesture
context is gone and pop-up blockers kick in. Same pattern as Stripe
Checkout / Lemonsqueezy / Creem hosted checkouts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Pancake SDK auto-generates X-Idempotency-Key on every signed POST
as SHA256(merchantID + path + body), including queries to /v1/graphql.
Pancake server-side dedupes on this key, so two identical query bodies
return the same cached snapshot — and a freshly-created Store or
OnetimeProduct would never appear in the catalog dropdown because the
catalog query body never changes.

Idempotency keys belong on state-changing operations (create / update /
delete) where they protect against duplicate side effects from retries
and double-clicks. Queries are reads; they should always be fresh.

Strip the header at the transport layer for /v1/graphql only — every
other endpoint (/v1/stores, /v1/onetime-products/{create,publish},
/v1/checkout-sessions, etc.) keeps its idempotency key intact so the
double-click safety on "+ Create" / Save / Pair flows is preserved.

The proper fix is upstream in waffo-pancake-sdk-go (the SDK shouldn't
set X-Idempotency-Key on GraphQL queries at all); this workaround
unblocks new-api in the meantime.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
User-facing purchase:
- POST /api/subscription/waffo-pancake/pay creates a SubscriptionOrder
  (status=pending) with a WAFFO_PANCAKE_SUB- prefixed trade_no, then
  hands Pancake an authenticated checkout session pinned to the plan's
  WaffoPancakeProductId with a PriceSnapshot override at PriceAmount.
- subscription-purchase-dialog now shows a Waffo Pancake button when
  enableWaffoPancake && plan.waffo_pancake_product_id; handler uses
  in-tab redirect (window.location.href) to avoid the popup-blocker
  pattern the existing Stripe/Creem buttons still suffer from.

Webhook dispatch:
- WaffoPancakeWebhook now dispatches by trade_no prefix:
  WAFFO_PANCAKE_SUB-… → ResolveWaffoPancakeSubscriptionTradeNo +
  model.CompleteSubscriptionOrder; plain WAFFO_PANCAKE-… → existing
  TopUp recharge flow. Same buyer-identity defence in depth on both.

Admin plan editing:
- SubscriptionPlan model gains WaffoPancakeProductId (varchar 128).
- Mutate drawer renders a dropdown of existing Pancake OnetimeProducts
  in the saved store (POST /api/option/waffo-pancake/subscription-
  product-options) with a "+ 新建" button that mints a new product
  server-side using persisted creds + StoreID + ReturnURL (POST
  /api/option/waffo-pancake/subscription-product). Refetches from the
  authoritative GraphQL catalog after mint rather than echoing the
  response body — matches the wallet binding's pattern.
- Plan-form schema + defaults + planToFormValues extended; columns
  badge added; pay-link API helper added.

Schema migration: GORM AutoMigrate adds the column on next startup; dev
SQLite needs a one-off `ALTER TABLE subscription_plans ADD COLUMN
waffo_pancake_product_id varchar(128) NOT NULL DEFAULT ''` if not
restarting.

Why OnetimeProduct (not SubscriptionProduct): new-api's
SubscriptionPlan is a time-limited prepayment — UserSubscription has a
fixed expiration and renewal is a manual re-purchase. There's no
renewal-event handling on the webhook side, mirroring the existing
Stripe integration. SubscriptionProduct would mean Pancake auto-renews
on its side but new-api never extends the user's access — bad UX.
Revisit if/when new-api adds renewal-event handling.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…c tab

Settings page (default frontend):
- Explainer chip under the binding section spells out why one Store +
  Product is enough at the gateway level and what each is used for:
  the bound Store is the parent of every Pancake product new-api
  creates (wallet top-up + subscription-plan products), the bound
  Product powers wallet top-ups specifically (per-session price
  override means no pre-made $1/$5/$10 SKUs), and subscription plans
  do NOT share the bound Product — each plan owns its own product set
  via the Subscriptions admin.
- Subtitle dedup: removed the duplicate "无需注册公司 / without
  registering a company" without losing any information.

Pancake icon:
- 🥞 emoji replaces the generic credit-card icon in both default
  (wallet/lib/ui.tsx) and classic (topup/RechargeCard.jsx) wallets —
  lucide doesn't ship a pancake glyph and react-icons/si is brand-
  only, so emoji is the cleanest path. Sized via the same className
  the caller passes, role=img + aria-label for accessibility.

Classic frontend admin:
- Waffo Pancake settings tab uncommented in PaymentSetting.jsx.
- SettingsPaymentGatewayWaffoPancake.jsx slimmed to the three
  operator-facing fields the new backend setting struct exposes
  (MerchantID + PrivateKey + ReturnURL) — Store/Product binding still
  happens through the default frontend's catalog flow.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ensureSubscriptionPlanTableSQLite is hand-maintained — the new column was
present in the GORM model (handled by AutoMigrate for MySQL/Postgres) but
missing from the SQLite CREATE TABLE DDL and the upgrade-path required[]
array, so SQLite-backed deploys errored on subscription writes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…dpoint

The classic admin's Save handler POSTed /api/option/waffo-pancake/initialize
after persisting credentials, intending to auto-provision a Store + Product.
That route was removed in 4f7ea8b (replaced by /save + /pair, with binding
now operator-driven), so every classic-frontend save 404'd and surfaced a
misleading "Waffo Pancake 自动创建商品失败" error.

Classic now just persists the three operator-typed fields. Store/Product
binding is exclusively the default frontend's catalog flow, matching the
documented design.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…+ TS errors

Audit-driven cleanup of the binding-flow + subscription commits:

- Remove WaffoPancakeProvisionedMerchantID + WaffoPancakeProvisionedReturnURL
  across setting / option / service / billing-defaults / system-settings-types.
  They were written on every Save but never read — relics of a removed
  EnsureWaffoPancakePrimaryProduct re-provision loop that the Pair + Save
  split made redundant. Two fewer OptionMap entries, two fewer UpdateOption
  calls per save.

- Rewrite the setting/payment_waffo_pancake.go module doc to describe the
  actual flow (SaveWaffoPancakeConfig + CreateWaffoPancakePrimaryPair) and
  reclassify StoreID/ProductID from "internally-managed" to "operator-bound"
  — operators pick them from the catalog or click "+ Create", they're
  never silently auto-provisioned now.

- Coerce Base UI Select's `string | null` onValueChange to `string` in
  waffo-pancake-settings-section.tsx — the Store and Product selects fed
  null straight into `Dispatch<SetStateAction<string>>` setters, which tsc
  has been flagging since 4f7ea8b.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…tions

Peer Go controllers (Stripe / Creem) sit at 5–7% inline-comment density;
peer TSX integration sections (creem-product-dialog, payment-settings-
section, waffo-settings-section) have 0–2 inline comments. Pancake code
had 18–31% in Go and 102 inline comments in waffo-pancake-settings-
section.tsx — a 3-30x outlier.

Trimmed long rationale prose, deleted peer-alignment notes ("matches
Stripe pattern"), section-divider banners ("// =====") and inline trivia
that restated the code. Kept the genuinely-non-obvious comments:

  - waffoPancakeGraphQLEnvelopeFixTransport: SDK envelope-decode bug +
    GraphQL idempotency-key strip (both block readers from inferring
    "why this transport exists" without the comment).
  - Brief godoc-style function headers (matches peer Go convention).
  - State-machine comments where behaviour is non-obvious (mount-only
    PrivateKey init, debounce signature dedupe, returning-admin
    blank-creds fallback).

Comment counts before → after:
  service/waffo_pancake.go                             211 → 71
  controller/topup_waffo_pancake.go                    116 → 32
  controller/subscription_payment_waffo_pancake.go      20 → 4
  waffo-pancake-settings-section.tsx                   102 → 33
  subscriptions-mutate-drawer.tsx + etc.                    compressed

`go build ./...` ✅. `tsc --noEmit` ✅.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds inline User Agreement / Privacy Policy links next to the copyright
on the home-page Footer when either is configured in System Settings →
Site → System Information. Same `user_agreement_enabled` /
`privacy_policy_enabled` status flags that drive the sign-in / sign-up
legal footers and the registration consent gate — admin clears the
document field → all three surfaces hide their link in lockstep.

UX: single inline row separated by `·` (Linear / Vercel / Notion
pattern). Two render branches:
  - default Footer: copyright + legal links on the left flex group,
    project attribution on the right.
  - custom-HTML Footer: legal links inline-composed with project
    attribution in the right-hand cell of the rounded box.

Implementation notes:
  - LegalLinks emits fragmented siblings (no wrapper div) so the
    parent flex container's gap-x uniformly controls separator and
    link spacing.
  - ProjectAttribution gains an `inline` prop so callers can compose
    it inline with other footer elements (e.g. legal links) instead
    of always wrapping in its own centered/right-aligned div.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…rs, and API layering

Style audit (commit 1b17d68 trimmed comment density; this lands the rest).

Backend (Go):
- Admin endpoints' logger format aligned with peer (Stripe / Creem / Waffo):
  Chinese verb + key=value field chain + %q-quoted strings, instead of the
  English "Waffo Pancake X failed: %v" style.
- Admin endpoints' error data switched from English to Chinese ("参数错误",
  "Waffo Pancake 凭证未配置", etc.) to match the rest of the codebase's
  user-facing error message convention.
- Webhook event types exported: waffoPancakeWebhookEvent →
  WaffoPancakeWebhookEvent and waffoPancakeWebhookData →
  WaffoPancakeWebhookData, so VerifyConfiguredWaffoPancakeWebhook's
  exported signature no longer leaks an unexported return type (Go
  unexported-return lint).

Frontend (TS):
- WaffoPancakePaymentResponse and SubscriptionPayResponse now declare
  the `token` / `token_expires_at` (and Pancake-only session_id /
  expires_at / order_id on the shared subscription response) that the
  backend has been returning all along. Removes a real contract drift —
  the fields were live but invisible to TS consumers.
- Lifted three direct api.post calls out of waffo-pancake-settings-
  section.tsx into a new waffo-pancake-api.ts helper module
  (listWaffoPancakeCatalog / createWaffoPancakePair /
  saveWaffoPancakeConfig). The section is now the only file in
  system-settings/integrations/ that no longer reaches into api.post
  directly — matching the convention used by every other integration
  section.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Updated the "Pancake 控制台" link in both the default and classic admin
settings pages from /dashboard to /merchant/dashboard — the former is a
public landing page, the operator actually needs the merchant console.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Pancake GraphQL Store.onetimeProducts nested field returns all
products regardless of status (the resolver is a DataLoader, takes no
filter args). Operators would see archived / draft products in the
binding dropdowns and could bind them, only to hit "product
unavailable" at checkout time.

Filter at the service layer rather than restructuring the catalog query
into two phases (server-side filter would need top-level
onetimeProducts(storeId, filter) + GraphQL aliases per store, adds a
round-trip without practical bandwidth gain for typical catalogs of
< 5 stores).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
SDK v0.2.0 fixed both issues that waffoPancakeGraphQLEnvelopeFixTransport
was working around — the SDK now returns the GraphQL envelope verbatim
(no more double-unwrap), and queries no longer carry X-Idempotency-Key
(GraphQLResource.Query sets postOptions.NoIdempotency internally). The
whole custom transport is gone; HTTP plumbing reverts to the SDK's
defaults.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
SDK v0.2.0 exposes VerifyWebhookTyped[T] which combines signature
verification + typed Data unmarshal in one call. Replacing the previous
VerifyWebhook + json.Unmarshal pair drops the encoding/json import and
the derefString helper (its sole caller is now a 3-line inline).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented May 18, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 079cbc51-555b-42e3-8d39-44d54be01ade

📥 Commits

Reviewing files that changed from the base of the PR and between e2bdb9b and 7bf9bd4.

⛔ Files ignored due to path filters (4)
  • web/classic/public/waffo-logo-dark.svg is excluded by !**/*.svg
  • web/classic/public/waffo-logo-light.svg is excluded by !**/*.svg
  • web/default/public/waffo-logo-dark.svg is excluded by !**/*.svg
  • web/default/public/waffo-logo-light.svg is excluded by !**/*.svg
📒 Files selected for processing (4)
  • web/classic/src/components/topup/RechargeCard.jsx
  • web/default/src/features/system-settings/integrations/waffo-pancake-settings-section.tsx
  • web/default/src/features/system-settings/integrations/waffo-settings-section.tsx
  • web/default/src/features/wallet/lib/ui.tsx

Walkthrough

Integrates the Waffo Pancake SDK, adds admin catalog/pairing flows, subscription checkout and order handling, tightens webhook/trade resolution via buyer identity, changes options/settings persistence and DB schema, and wires frontend settings, subscription UI, icons, footer, and i18n.

Changes

Waffo Pancake SDK Integration & Subscriptions

Layer / File(s) Summary
Service & SDK
service/waffo_pancake.go, service/waffo_pancake_test.go, go.mod
Replaced custom HTTP/signature logic with Pancake SDK client; added SDK-driven checkout/session/token fields, webhook verification mapping, buyer-identity checks, store/product creation, catalog listing, and related tests.
Controllers & Routing
controller/topup_waffo_pancake.go, controller/subscription_payment_waffo_pancake.go, controller/topup.go, controller/option.go, controller/payment_webhook_availability.go, router/api-router.go
New admin endpoints for pairing/catalog/save, subscription payment endpoint, repositioned top-up pay method, removed public webhook key exposure from options, credential-based enablement logic, and webhook env routing/dispatch for subscriptions vs top-ups.
Models & DB
model/subscription.go, model/main.go, model/option.go, setting/payment_waffo_pancake.go
Added waffo_pancake_product_id to SubscriptionPlan and SQLite migration; narrowed in-memory options and added atomic UpdateOptionsBulk; removed enabled/sandbox/webhook key/currency flags from settings.
Frontend system settings & API
web/default/src/features/system-settings/integrations/waffo-pancake-api.ts, web/default/src/features/system-settings/integrations/waffo-pancake-settings-section.tsx, web/classic/src/pages/Setting/Payment/SettingsPaymentGatewayWaffoPancake.jsx
New typed admin API wrappers for catalog/pair/save; rewritten Pancake settings UI with credential verification, catalog fetch, create-pair and save flows; simplified classic settings to Merchant ID / Private Key / Return URL.
Frontend subscriptions & wallet
web/default/src/features/subscriptions/*, web/default/src/features/wallet/*, web/classic/src/components/topup/*
Added subscription payment API and dialog support for Waffo Pancake (same-tab redirect), plan form support for waffo_pancake_product_id, mutate-drawer product creation, subscription plan badge, icon updates, and checkout URL safety checks.
Options & atomic updates
model/option.go, controller/option.go
Introduced UpdateOptionsBulk to persist multiple options atomically and narrowed updateOptionMap; removed public-key allowlist so sensitive-suffix keys are filtered consistently.
Tests, router & i18n/footer
service/waffo_pancake_test.go, controller/payment_webhook_availability_test.go, router/api-router.go, web/default/src/i18n/locales/*, web/classic/src/i18n/locales/*, web/default/src/components/layout/components/footer.tsx
Updated tests for identity-driven trade resolution and subscription order resolution; router adds Pancake endpoints; extensive EN/ZH translation additions and footer legal-links inline rendering.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Suggested reviewers

  • seefs001
  • Calcium-Ion
  • creamlike1024

Poem

🥕 I’m a rabbit with a pocket full of keys,
I stitched a Pancake flow with quiet ease.
Sessions spun, tokens warm and bright,
Orders queued and webhooks hand in flight.
Hop, deploy — now let the pancakes light!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.00% 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 and specifically describes the main feature: Waffo Pancake gateway integration with subscription support and admin catalog binding flow.
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 and usage tips.

@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

🧹 Nitpick comments (3)
web/default/src/i18n/locales/en.json (1)

2281-2281: ⚡ Quick win

Avoid duplicate validation messages that differ only by punctuation.

"Merchant ID is required." duplicates the existing "Merchant ID is required" and can split usage across two keys. Prefer one canonical key/message.

🤖 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/i18n/locales/en.json` at line 2281, Remove the duplicate
validation message that differs only by punctuation: consolidate to a single
canonical key/value by deleting the entry with the key/value "Merchant ID is
required." and using the existing "Merchant ID is required" message everywhere;
update any references that point to the dotted key to the canonical key to avoid
split usage (search for occurrences of "Merchant ID is required." and replace
with "Merchant ID is required").
web/default/src/i18n/locales/zh.json (1)

525-525: 🏗️ Heavy lift

Use hierarchical i18n keys for newly added entries.

These newly added entries continue sentence-as-key style. For new work, please add semantic hierarchical keys (e.g., integrations.waffoPancake.store.bindTitle) and reference those in UI code.

As per coding guidelines, "Use hierarchical and semantically clear translation key names such as dashboard.overview.title and maintain naming consistency".

Also applies to: 553-553, 1700-1701, 2303-2304, 2597-2597, 2834-2834, 3196-3196, 3667-3669, 3723-3723, 3844-3847, 4272-4272, 4322-4329, 4360-4361, 4400-4400

🤖 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/i18n/locales/zh.json` at line 525, Replace the
sentence-as-key JSON entry ("Bind a Pancake store + product") with a
hierarchical, semantic i18n key (for example
integrations.waffoPancake.store.bindTitle) and move the Chinese value under that
new key; then update any UI code that references the old sentence key to use the
new hierarchical key (search for usages of the literal English sentence). Apply
the same pattern to the other problematic entries listed (lines referenced in
the review) so all new translations use consistent hierarchical keys like
integrations.<integrationName>.<entity>.<action> or dashboard.overview.title.
service/waffo_pancake_test.go (1)

68-122: ⚡ Quick win

Add parity tests for ResolveWaffoPancakeSubscriptionTradeNo.

This file now hardens top-up identity checks, but the new subscription resolver path has no mirrored tests. Please add the same core cases (happy path, unknown order, identity mismatch, missing identity) for subscription orders to prevent drift between the two webhook flows.

🤖 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 `@service/waffo_pancake_test.go` around lines 68 - 122, Add a new set of tests
mirroring the top-up cases for ResolveWaffoPancakeSubscriptionTradeNo: implement
tests for the happy path (valid order and matching
MerchantProvidedBuyerIdentity), unknown order (webhook order ID not in DB),
buyer identity mismatch (webhook buyer identity not matching the stored
Subscription.UserId), and missing buyer identity (empty
MerchantProvidedBuyerIdentity) similar to TestResolveWaffoPancakeTradeNo_*; use
the same setup helper (setupWaffoPancakeTestDB), create Subscription records
with PaymentMethod/Provider set to WaffoPancake and proper TradeNo values, call
ResolveWaffoPancakeSubscriptionTradeNo with
WaffoPancakeWebhookEvent/WaffoPancakeWebhookData, and assert returned tradeNo
and error expectations (require.NoError/require.Error,
require.Empty/require.Equal, and require.Contains for "buyer identity mismatch")
to ensure parity between top-up and subscription webhook flows.
🤖 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 `@service/waffo_pancake.go`:
- Around line 376-404: SaveWaffoPancakeConfig claims atomic persistence but
performs multiple independent model.UpdateOption calls (including keys
"WaffoPancakeMerchantID", "WaffoPancakePrivateKey", "WaffoPancakeReturnURL",
"WaffoPancakeStoreID", "WaffoPancakeProductID"), so a mid-sequence failure can
leave partial state; change the implementation to perform a single transactional
bulk update (e.g. wrap the updates in a DB transaction or add a
model.UpdateOptionsBulk that writes all pairs together and commits/rolls back)
and only refresh any in-memory options after the transaction commits, preserving
the existing "blank privateKey = keep current" behavior when building the bulk
payload.

In `@web/classic/src/components/topup/index.jsx`:
- Around line 456-458: The current code assigns the backend-provided checkoutUrl
directly to window.location.href (checkoutUrl), which may allow unsafe schemes;
before navigation parse and validate checkoutUrl (e.g., with the URL
constructor) and ensure it is an absolute URL with protocol 'http:' or 'https:'
and a valid host; only then perform window.location.href = checkoutUrl,
otherwise abort the redirect and surface/log an error; update the code path that
produces the in-tab redirect (the place using checkoutUrl and
window.location.href) to include this validation.

In
`@web/default/src/features/subscriptions/components/dialogs/subscription-purchase-dialog.tsx`:
- Around line 314-322: The Waffo Pancake button label is hardcoded; update the
SubscriptionPurchaseDialog component to use i18n by calling t() from
useTranslation instead of the literal "Waffo Pancake" (e.g., replace the Button
child with t('subscriptions.payWaffoPancake')), ensure useTranslation is
imported and initialized in the component (reference SubscriptionPurchaseDialog
/ handlePayWaffoPancake / hasWaffoPancake), and add the new translation key
('subscriptions.payWaffoPancake') to the locale resource files for each
supported language.

In `@web/default/src/features/subscriptions/components/subscriptions-columns.tsx`:
- Around line 165-171: Replace the hardcoded badge label 'Waffo Pancake' with an
i18n string using the t() function from useTranslation: import/use the
useTranslation hook in the component that renders StatusBadge
(subscriptions-columns.tsx), call const { t } = useTranslation(), and pass
label={t('subscriptions.badge.waffoPancake', 'Waffo Pancake')} (or your existing
translation key) to the StatusBadge where plan.waffo_pancake_product_id is
checked; ensure the translation key is added to the locale files.

In
`@web/default/src/features/subscriptions/components/subscriptions-mutate-drawer.tsx`:
- Line 667: The label "Waffo Pancake Product ID" in
subscriptions-mutate-drawer.tsx is user-facing and must be internationalized:
import and call useTranslation() in the component (or reuse the existing t
function if already present) and replace the plain FormLabel text with
t('subscriptions.waffoPancakeProductId') (or a similarly named key like
'subscriptions.waffoPancakeProductIdLabel'), then add that key and translation
strings to the i18n resource files; update the FormLabel usage
(FormLabel>{t('subscriptions.waffoPancakeProductId')}</FormLabel>) so all UI
text goes through t().

In
`@web/default/src/features/system-settings/integrations/waffo-pancake-settings-section.tsx`:
- Around line 553-565: The JSX contains a 3-level nested ternary using
credsReady, verifying, and hasCatalog which is hard to read; extract a small
derived variable (e.g., const statusMessage) above the JSX and compute it with
if/else (or early returns) using the existing t(...) strings: if !credsReady ->
t('Fill in the credentials above to begin.'), else if verifying -> t('Verifying
credentials and pulling stores from your Pancake account...'), else if
hasCatalog -> t('Mint a fresh pair below — or pick an existing one further down.
Click Save when ready.'), else -> t('No stores on this merchant yet. Set a
return URL and click Create to mint your first pair.'), then replace the nested
ternary in the JSX with {statusMessage}.

In `@web/default/src/features/wallet/lib/ui.tsx`:
- Around line 130-136: Replace the hardcoded aria-label on the pancake span with
an i18n string: import and call useTranslation() in the component, get t, and
change aria-label='pancake' to aria-label={t('emoji.pancake')} (or another
descriptive i18n key) on the <span> used in ui.tsx; ensure the translation key
is added to the locale files and keep the existing className and role attributes
intact so only the aria-label is localized.

In `@web/default/src/i18n/locales/en.json`:
- Line 525: The entry "Bind a Pancake store + product" (and other
sentence-as-key strings referenced in the comment) should be replaced with
hierarchical i18n keys under a stable namespace like integrations.waffoPancake
(for example integrations.waffoPancake.bindStoreProduct) and the human-visible
text moved into the value; update any code/UI that currently uses the
sentence-as-key to use the new key name (e.g., replace usages of "Bind a Pancake
store + product" with i18n.t('integrations.waffoPancake.bindStoreProduct'));
apply the same change for the other listed strings (lines cited in the comment)
so all Pancake-related entries follow integrations.waffoPancake.* naming and
maintain consistent, semantic keys.

In `@web/default/src/i18n/locales/zh.json`:
- Line 2597: The zh locale string for the key "No stores on this merchant yet.
Set a return URL and click Create to mint your first pair." contains the English
CTA "Create"; update the translated value to use the localized action label
(e.g., replace "Create" with "创建" or the project's standard Chinese CTA used
elsewhere) so the entire string is in Chinese; modify the value for that exact
source string in zh.json to use the localized term.

---

Nitpick comments:
In `@service/waffo_pancake_test.go`:
- Around line 68-122: Add a new set of tests mirroring the top-up cases for
ResolveWaffoPancakeSubscriptionTradeNo: implement tests for the happy path
(valid order and matching MerchantProvidedBuyerIdentity), unknown order (webhook
order ID not in DB), buyer identity mismatch (webhook buyer identity not
matching the stored Subscription.UserId), and missing buyer identity (empty
MerchantProvidedBuyerIdentity) similar to TestResolveWaffoPancakeTradeNo_*; use
the same setup helper (setupWaffoPancakeTestDB), create Subscription records
with PaymentMethod/Provider set to WaffoPancake and proper TradeNo values, call
ResolveWaffoPancakeSubscriptionTradeNo with
WaffoPancakeWebhookEvent/WaffoPancakeWebhookData, and assert returned tradeNo
and error expectations (require.NoError/require.Error,
require.Empty/require.Equal, and require.Contains for "buyer identity mismatch")
to ensure parity between top-up and subscription webhook flows.

In `@web/default/src/i18n/locales/en.json`:
- Line 2281: Remove the duplicate validation message that differs only by
punctuation: consolidate to a single canonical key/value by deleting the entry
with the key/value "Merchant ID is required." and using the existing "Merchant
ID is required" message everywhere; update any references that point to the
dotted key to the canonical key to avoid split usage (search for occurrences of
"Merchant ID is required." and replace with "Merchant ID is required").

In `@web/default/src/i18n/locales/zh.json`:
- Line 525: Replace the sentence-as-key JSON entry ("Bind a Pancake store +
product") with a hierarchical, semantic i18n key (for example
integrations.waffoPancake.store.bindTitle) and move the Chinese value under that
new key; then update any UI code that references the old sentence key to use the
new hierarchical key (search for usages of the literal English sentence). Apply
the same pattern to the other problematic entries listed (lines referenced in
the review) so all new translations use consistent hierarchical keys like
integrations.<integrationName>.<entity>.<action> or dashboard.overview.title.
🪄 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: f91ee358-8511-4961-9f1d-f06dee5f6a74

📥 Commits

Reviewing files that changed from the base of the PR and between 5dd0d3b and 05d807c.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (38)
  • controller/option.go
  • controller/payment_webhook_availability.go
  • controller/payment_webhook_availability_test.go
  • controller/subscription_payment_waffo_pancake.go
  • controller/topup.go
  • controller/topup_waffo_pancake.go
  • go.mod
  • model/main.go
  • model/option.go
  • model/subscription.go
  • router/api-router.go
  • service/waffo_pancake.go
  • service/waffo_pancake_test.go
  • setting/payment_waffo_pancake.go
  • web/classic/src/components/settings/PaymentSetting.jsx
  • web/classic/src/components/topup/RechargeCard.jsx
  • web/classic/src/components/topup/index.jsx
  • web/classic/src/pages/Setting/Payment/SettingsPaymentGatewayWaffoPancake.jsx
  • web/default/src/components/layout/components/footer.tsx
  • web/default/src/features/subscriptions/api.ts
  • web/default/src/features/subscriptions/components/dialogs/subscription-purchase-dialog.tsx
  • web/default/src/features/subscriptions/components/subscriptions-columns.tsx
  • web/default/src/features/subscriptions/components/subscriptions-mutate-drawer.tsx
  • web/default/src/features/subscriptions/lib/plan-form.ts
  • web/default/src/features/subscriptions/types.ts
  • 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/payment-settings-section.tsx
  • web/default/src/features/system-settings/integrations/waffo-pancake-api.ts
  • web/default/src/features/system-settings/integrations/waffo-pancake-settings-section.tsx
  • web/default/src/features/system-settings/integrations/waffo-settings-section.tsx
  • web/default/src/features/system-settings/types.ts
  • web/default/src/features/wallet/components/subscription-plans-card.tsx
  • web/default/src/features/wallet/hooks/use-waffo-pancake-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/zh.json

Comment thread service/waffo_pancake.go
Comment thread web/classic/src/components/topup/index.jsx
Comment on lines +314 to +322
{hasWaffoPancake && (
<Button
variant='outline'
className='flex-1'
onClick={handlePayWaffoPancake}
disabled={paying || limitReached}
>
Waffo Pancake
</Button>

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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Localize the new payment button label.

The new button text is hardcoded and won’t be translated with language switching.

💡 Suggested fix
-                      Waffo Pancake
+                      {t('Waffo Pancake')}

As per coding guidelines, "All user-facing text content must support i18n using the t() function from useTranslation() in React components".

🤖 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/subscriptions/components/dialogs/subscription-purchase-dialog.tsx`
around lines 314 - 322, The Waffo Pancake button label is hardcoded; update the
SubscriptionPurchaseDialog component to use i18n by calling t() from
useTranslation instead of the literal "Waffo Pancake" (e.g., replace the Button
child with t('subscriptions.payWaffoPancake')), ensure useTranslation is
imported and initialized in the component (reference SubscriptionPurchaseDialog
/ handlePayWaffoPancake / hasWaffoPancake), and add the new translation key
('subscriptions.payWaffoPancake') to the locale resource files for each
supported language.

Comment on lines +165 to +171
{plan.waffo_pancake_product_id && (
<StatusBadge
label='Waffo Pancake'
variant='neutral'
copyable={false}
/>
)}

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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Localize the new payment-channel badge label.

The added badge text is hardcoded and won’t be translated.

💡 Suggested fix
-                  label='Waffo Pancake'
+                  label={t('Waffo Pancake')}

As per coding guidelines, "All user-facing text content must support i18n using the t() function from useTranslation() in React components".

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{plan.waffo_pancake_product_id && (
<StatusBadge
label='Waffo Pancake'
variant='neutral'
copyable={false}
/>
)}
{plan.waffo_pancake_product_id && (
<StatusBadge
label={t('Waffo Pancake')}
variant='neutral'
copyable={false}
/>
)}
🤖 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/subscriptions/components/subscriptions-columns.tsx`
around lines 165 - 171, Replace the hardcoded badge label 'Waffo Pancake' with
an i18n string using the t() function from useTranslation: import/use the
useTranslation hook in the component that renders StatusBadge
(subscriptions-columns.tsx), call const { t } = useTranslation(), and pass
label={t('subscriptions.badge.waffoPancake', 'Waffo Pancake')} (or your existing
translation key) to the StatusBadge where plan.waffo_pancake_product_id is
checked; ensure the translation key is added to the locale files.

}
return (
<FormItem>
<FormLabel>Waffo Pancake Product ID</FormLabel>

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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Translate the new Pancake field label.

This label is user-facing and should go through i18n.

💡 Suggested fix
-                      <FormLabel>Waffo Pancake Product ID</FormLabel>
+                      <FormLabel>{t('Waffo Pancake Product ID')}</FormLabel>

As per coding guidelines, "All user-facing text content must support i18n using the t() function from useTranslation() in React components".

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<FormLabel>Waffo Pancake Product ID</FormLabel>
<FormLabel>{t('Waffo Pancake Product ID')}</FormLabel>
🤖 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/subscriptions/components/subscriptions-mutate-drawer.tsx`
at line 667, The label "Waffo Pancake Product ID" in
subscriptions-mutate-drawer.tsx is user-facing and must be internationalized:
import and call useTranslation() in the component (or reuse the existing t
function if already present) and replace the plain FormLabel text with
t('subscriptions.waffoPancakeProductId') (or a similarly named key like
'subscriptions.waffoPancakeProductIdLabel'), then add that key and translation
strings to the i18n resource files; update the FormLabel usage
(FormLabel>{t('subscriptions.waffoPancakeProductId')}</FormLabel>) so all UI
text goes through t().

Comment thread web/default/src/features/wallet/lib/ui.tsx
"Billing Process": "Billing Process",
"Billing Source": "Billing Source",
"Bind": "Bind",
"Bind a Pancake store + product": "Bind a Pancake store + product",

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.

🛠️ Refactor suggestion | 🟠 Major | 🏗️ Heavy lift

Use hierarchical i18n keys for newly added Pancake strings.

These new entries are sentence-as-key strings, which expands inconsistency and makes reuse/maintenance harder. Please add these new keys under a stable namespace (e.g., integrations.waffoPancake.*) and keep UI text in values.

As per coding guidelines, "Use hierarchical and semantically clear translation key names such as dashboard.overview.title and maintain naming consistency".

Also applies to: 552-553, 1008-1014, 1700-1701, 2303-2304, 2597-2597, 2737-2737, 2834-2834, 2848-2848, 2890-2890, 3196-3196, 3667-3667, 3688-3690, 3723-3723, 3844-3848, 4197-4197, 4272-4272, 4322-4329, 4360-4361, 4400-4400

🤖 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/i18n/locales/en.json` at line 525, The entry "Bind a Pancake
store + product" (and other sentence-as-key strings referenced in the comment)
should be replaced with hierarchical i18n keys under a stable namespace like
integrations.waffoPancake (for example
integrations.waffoPancake.bindStoreProduct) and the human-visible text moved
into the value; update any code/UI that currently uses the sentence-as-key to
use the new key name (e.g., replace usages of "Bind a Pancake store + product"
with i18n.t('integrations.waffoPancake.bindStoreProduct')); apply the same
change for the other listed strings (lines cited in the comment) so all
Pancake-related entries follow integrations.waffoPancake.* naming and maintain
consistent, semantic keys.

Comment thread web/default/src/i18n/locales/zh.json Outdated
Hill-waffo and others added 10 commits May 18, 2026 15:20
…ransaction

Previously SaveWaffoPancakeConfig claimed atomicity in its doc but ran
five independent model.UpdateOption calls in sequence — a mid-sequence
failure (DB error, constraint violation, etc.) left the gateway in a
partially-configured state, e.g. MerchantID updated but StoreID still
pointing at the old value.

Adds model.UpdateOptionsBulk which wraps all writes in a single
DB.Transaction and only dispatches updateOptionMap after the commit
succeeds. Other multi-field config saves can adopt the same helper.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The classic wallet was setting window.location.href = checkoutUrl with
no scheme check; the default frontend had isSafeHttpCheckoutUrl in
features/wallet/hooks/use-waffo-pancake-payment.ts but classic was the
parity gap. Mirrors the http/https-only validator inline.

Pancake itself never returns unsafe schemes, but the check is cheap
defence-in-depth against a compromised gateway or an injection
upstream.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two keys differed only by trailing period and translated separately
("Merchant ID is required" / "Merchant ID is required."). The dotted
variant had a single call site in waffo-pancake-settings-section.tsx —
switched it to the period-less canonical key and removed the duplicate
entries from en.json + zh.json.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Chinese translation for the empty-state hint on the admin binding
page kept the English "Create" verb verbatim, mixing scripts in an
otherwise all-Chinese sentence. Replaced with 「创建」 to match the
surrounding text.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The 🥞 span's aria-label was hardcoded "pancake" — a screen-reader-only
string that should respect the user's locale. Switched to
i18next.t('Pancake') (using the global i18next instance since
getPaymentIcon is a plain function, not a React component, so the
useTranslation hook is unavailable).

Brand strings on the wallet/subscription badge labels ("Waffo Pancake",
"Stripe", "Creem") stay hardcoded — peer convention.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The bind-status hint had a 4-way nested ternary (credsReady / verifying
/ hasCatalog) that was hard to read past the second level. Lifted it to
a derived bindStatusMessage variable computed via if/else above the
JSX; the render site is now a single {bindStatusMessage}.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…tionTradeNo

Four-case parity with the existing TopUp resolver tests: happy path,
unknown order, buyer identity mismatch, missing buyer identity. Catches
behaviour drift between top-up and subscription webhook paths — the
two functions share the same defence-in-depth pattern but their
underlying lookups (TopUp vs SubscriptionOrder) could diverge in
subtle ways without coverage on both.

setupWaffoPancakeTestDB now also migrates SubscriptionOrder.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Upstream main moved 29 commits ahead. The 9 conflicting files all needed
manual reconciliation because upstream added overlapping features:

- payment_setting.compliance_*: new compliance gate (RootAuth must
  confirm legal terms before any payment surface unlocks). Kept the
  new isPaymentComplianceConfirmed() guard in isWaffoPancakeTopUpEnabled
  alongside our credentials-presence check; added confirmPayment-
  ComplianceForTest helper to the Pancake webhook test.

- paymentReturnPath helper (theme-aware return URL builder): upstream
  reintroduced getWaffoPancakeReturnURL via this helper. We had removed
  the function because SuccessURL is now bound to the OnetimeProduct at
  creation time, not per-checkout — dropped the upstream snippet, kept
  our admin-flow ReturnURL plumbing.

- isPaymentComplianceOptionKey / isPositiveOptionValue (new helpers in
  controller/option.go): kept upstream's additions. Did NOT keep
  upstream's revived isVisiblePublicKeyOption (it references
  WaffoPancakeWebhookPublicKey / WaffoPancakeWebhookTestKey, which our
  branch removed; the helper has zero call sites).

- web/classic admin Tabs got a card-style wrapper from upstream; kept
  that wrapper AND our uncommented Waffo Pancake tab.

- PaymentSettingsSection props: kept BOTH our waffoPancakeProvisioned-
  StoreID/ProductID AND upstream's complianceDefaults — independent
  additive props.

- i18n locales: alphabetical inserts on each side, kept both.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two SVG variants for theme-aware rendering — a black-W-on-transparent
glyph for light themes and a white-W-on-transparent glyph for dark
themes. The W letterform occupies only ~40% of the SVG viewBox
vertically (it's a wide, short letterform), so the wrapper applies
transform: scale(2) to bring the rendered glyph height in line with
Stripe's S and Creem's Landmark icons in the payment-method buttons.

Default frontend switches variants via Tailwind's dark: prefix (no JS);
classic frontend reads the current theme via the existing useActualTheme
hook and selects the src accordingly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…dings with peers

Both Waffo sections used <SettingsSection>, which renders an h3 in
text-base font-semibold. The other integrations on the page (Stripe,
Creem, Epay, General) all use an inline h3 in text-lg font-medium
straight off the parent form, with sibling Separators creating the
visual rhythm.

Switched both Waffo sections to the inline heading pattern and added
pt-4 to their root containers so the gap above each heading matches
the inside-form rhythm (the parent SettingsSection uses space-y-4
between children where the inner form uses space-y-8 — pt-4 closes
the gap).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@seefs001 seefs001 merged commit 19f1821 into QuantumNous:main May 22, 2026
1 check passed
Hill-waffo added a commit to Hill-waffo/new-api that referenced this pull request May 22, 2026
…cake-sdk-migration

PR QuantumNous#4935 was merged at 19f1821. Subsequent main commit f2c7647
("enforce Waffo subscription compliance and product ID update")
added requirePaymentCompliance(c) to SubscriptionRequestWaffoPancakePay.

Conflicts came from this branch's three post-PR commits sitting on top
of the pre-merge state while main now has the merged-then-extended
version. Resolution kept the post-PR work intact:

- go.mod / go.sum: SDK v0.3.1 (main is still on v0.2.0)
- controller/subscription_payment_waffo_pancake.go: keep
  requirePaymentCompliance check from main AND OrderMerchantExternalID
  from this branch
- controller/topup_waffo_pancake.go: same merge of both sides
- service/waffo_pancake.go: keep all OrderMerchantExternalID plumbing
- service/waffo_pancake_test.go: keep updated test fixtures

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Hill-waffo Hill-waffo mentioned this pull request May 22, 2026
11 tasks
yiranxiaohui added a commit to yiranxiaohui/new-api that referenced this pull request May 25, 2026
Range: 18282e6..3b9ed0a8 (upstream/main as of fetch)

Highlights:
- feat: support request_header key source (QuantumNous#4903)
- feat: Waffo Pancake gateway + admin catalog binding (QuantumNous#4935)
- perf: optimize request metadata extraction, drop dead batch
  helpers in relay/channel/openai/helper.go (QuantumNous#5009)
- perf: reduce heap residency for large base64 relay requests
- fix(channel): evict auto-disabled multi-key channels from cache (QuantumNous#4983)
- fix: resolve model owned_by from active channels (QuantumNous#4416) — introduces
  channelOwnerName/getPreferredModelOwners/buildOpenAIModel + ListModels refactor
- fix: GetAllChannels respects group filter (QuantumNous#4847, QuantumNous#4885)
- fix(auth): expose register_enabled, aff_code, localize reset (QuantumNous#4871, QuantumNous#4945, QuantumNous#4769)
- fix(webhook): processing + Waffo subscription compliance (QuantumNous#5047, QuantumNous#5038)
- refactor(ui): system settings drill-in sidebar + log filter responsiveness

Conflicts resolved:
- controller/model.go: kept local hiddenMappedModels filter
  (resolveAccessibleModelGroups + getHiddenMappedModelNamesForGroups)
  on top of upstream's ListModels refactor; adopted upstream
  channelOwnerName helper.
- relay/channel/openai/helper.go: adopted upstream (HEAD's
  processChatCompletions/processCompletions were dead code after
  upstream's perf refactor in QuantumNous#5009).

Local patches verified intact: Username + fillTopUpUsernames (model/topup.go,
locked by topup_username_test.go), HideUpstreamErrors, Claude developer-role
normalization, Gemini role fallback, Model Chat header nav entry,
channel affinity auto-clear.

Note: go build not run (no Go toolchain in this environment); CI to verify.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
xyfacai pushed a commit to xyfacai/new-api that referenced this pull request May 30, 2026
SamuelSxy pushed a commit to SamuelSxy/new-api-rh that referenced this pull request Jun 7, 2026
fx247562340 pushed a commit to fx247562340/vancine-platform that referenced this pull request Jun 11, 2026
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