Commit Graph
1233 Commits
Author SHA1 Message Date
erio 5f4378b4ae feat(payment): show group details and quota info on subscription plan cards
Extend checkout API to return group name, rate multiplier, daily/weekly/monthly
limits and supported model scopes. Redesign SubscriptionPlanCard to display
platform badge, group quota info, and model scope tags.
2026-04-09 00:00:25 +08:00
erio e86c7005b5 fix(payment): subscription plans grid layout from top-left 2026-04-08 22:16:29 +08:00
erio 3f5e277855 refactor(payment): inline subscription flow and simplify payment modes
- EasyPay: remove redirect mode, keep only qrcode and popup
- Stripe: remove mode config entirely, always use inline Payment Element
- Subscription: replace dialog with inline confirm UI (plan summary +
  method selector + fee breakdown), same flow as top-up
- Move PaymentStatusPanel/StripePaymentInline to shared section for
  both recharge and subscription payments
- Hide tabs and help card during payment/subscription confirm phases
2026-04-08 22:07:42 +08:00
erio 8e02b123c8 fix(payment): Stripe redirect methods (Alipay) open in new window
When user selects a redirect-based payment method (e.g. Alipay) in the
inline Stripe Element and clicks pay, the payment page opens in a new
window instead of navigating the current page away. The main page
switches to waiting/polling mode. Inline methods (card, WeChat) still
complete directly on the page.
2026-04-08 21:41:24 +08:00
erio d911245679 fix(payment): add cancel order to Stripe inline + widen popup window 2026-04-08 21:30:07 +08:00
erio 384a1b3070 feat(payment): embed Stripe Payment Element inline instead of popup
Stripe payment now shows the Payment Element directly on the purchase
page. User selects payment method (Alipay/WeChat/Card) inline, then
confirms. Redirect-based methods (Alipay) navigate to the payment
page; non-redirect methods (Card/WeChat) complete inline.
2026-04-08 21:26:17 +08:00
erio dc731465d5 fix(ui): widen payment popup window from 520px to 680px 2026-04-08 21:20:11 +08:00
erio aab31329af fix(payment): use global amount range for quick buttons, not per-method
Quick amount buttons and custom input always use the global min/max
range (union of all methods). Payment method availability is
determined by whether the selected amount fits each method's limits.
2026-04-08 21:10:24 +08:00
erio c846eaa444 fix(ui): widen cancel rate limit select dropdowns to prevent text truncation 2026-04-08 21:07:48 +08:00
erio fafde2a493 feat(payment): configurable payment mode (qrcode/redirect/popup)
- EasyPay supports 3 modes: qrcode (API/QR), redirect (new tab), popup (small window)
- Stripe supports 2 modes: popup (small window, default), redirect (new tab)
- Backend passes payment_mode through to create-order response
- Frontend opens pay URL based on configured mode
2026-04-08 18:05:24 +08:00
erio 6dcf14f46a refactor(payment): replace dialog overlay with inline payment status
Payment waiting/QR/success states now render inline on the page
instead of as a modal overlay dialog. The page content switches
between selection mode and payment status mode.
2026-04-08 17:33:01 +08:00
erio be82609939 fix(payment): audit fixes for alipay/wxpay/stripe payment providers
Backend:
- Extract YuanToFen/FenToYuan to payment/amount.go using shopspring/decimal
- Require alipay publicKey in config validation
- Fix wxpay webhook response to return JSON per V3 spec
- Remove wxpay certSerial fallback to publicKeyId
- Define magic strings as named constants in wxpay/alipay providers
- Add slog warning for wxpay H5→Native payment downgrade
- Make EncryptionKey validation return error on invalid (non-empty) key
- Make decryptConfig propagate errors instead of returning nil
- Add idempotency check in doBalance to prevent stuck FAILED retries

Frontend:
- Fix dashboard currency symbol from $ to ¥
- Fix AdminPaymentPlansView any type to proper SubscriptionPlan type
- Make quick amount buttons follow selected payment method limits
- Center help image with larger height and text below
2026-04-08 17:11:32 +08:00
erio 93a43950fe feat(payment): open Stripe and redirect payments in popup window
Instead of navigating the main page away during payment, open Stripe
and EasyPay redirect payments in a popup window while showing a
waiting dialog on the purchase page. Extract POPUP_WINDOW_FEATURES
constant to providerConfig.ts.
2026-04-08 16:33:01 +08:00
erio 0ed9816487 feat(payment): click-to-preview help image with fullscreen overlay 2026-04-08 14:06:19 +08:00
erio 4049b2f695 feat(payment): inline QR code dialog for scan-to-pay mode
QR code payments now display in an inline dialog on the payment page
with countdown and status polling. Success is shown directly without
page navigation. Redirect mode (pay_url only) still navigates to the
polling page as before.
2026-04-08 13:29:02 +08:00
erio 7295fce5fd fix(payment): add help_image_url to checkout-info API response
The help image configured in admin was missing from the checkout page
because it was not included in the checkoutInfoResponse struct.
2026-04-08 13:16:09 +08:00
erio f0aea13ded feat(payment): redesign subscription plan cards and fix features parsing
- Fix features field sent as raw string causing character-by-character iteration
- Redesign SubscriptionPlanCard with gradient accent bar, checkmark feature list,
  discount badge, and polished hover/button effects
- Adaptive plan grid layout (1/2/3 columns based on plan count)
- Improve subscription confirm dialog with better price display
2026-04-08 13:07:06 +08:00
erio 3acb3b056e feat(payment): add /checkout-info API, simplify PaymentView to single call
Backend: new GET /payment/checkout-info returns methods (with limits),
global_min/max, plans (with platform), balance_disabled, help_text,
and stripe_publishable_key in one response.

Frontend: PaymentView now calls getCheckoutInfo() once instead of
fetchConfig + getLimits + fetchPlans separately. Removed plansLoading
state and loadPlans watcher. Reduced from 317 to 301 lines.
2026-04-08 02:49:26 +08:00
erio c016bdba13 refactor(payment): replace magic strings with constants, fix catch types
Backend:
- Add ProviderStatus* constants (pending/paid/success/failed/refunded)
- Add DefaultLoadBalanceStrategy, ConfigKeyPublishableKey constants
- GetBasePaymentType: use Type* constants instead of raw strings
- Webhook handler: use payment.Type* for provider keys, extract
  webhookLogTruncateLen constant
- All 4 providers: replace status string literals with ProviderStatus*

Frontend:
- All catch blocks: add `: unknown` type annotation
- stores/payment.ts: replace `any` with proper type for plan parsing
- PaymentProviderDialog: replace `as any` with `as SelectOption[]`
2026-04-08 02:39:47 +08:00
erio 36b14b0584 fix(payment): Stripe instance polluting alipay/wxpay limits groups
Root cause: Stripe instance had supported_types="card,alipay,link,wxpay"
but only card/link were mapped to "stripe" group. Stripe's alipay/wxpay
leaked into independent groups, and since Stripe had no limits configured,
it triggered the "any unlimited → all zeros" early return, making ALL
method limits show as zero.

Fix: pcGroupByPaymentType now routes ALL types from Stripe provider
instances to the "stripe" group (by checking ProviderKey, not sub-type).
Frontend: Stripe provider dialog shows single "Stripe" limits entry
instead of per-sub-type entries.
2026-04-08 02:35:22 +08:00
erio bd43ed23fd feat(payment): union-based limits aggregation and amount-method filtering
Backend: change limits aggregation from intersection (most restrictive) to
union (least restrictive) across provider instances. Since the load balancer
can route to any instance, users should see the widest possible range.
Add global_min/global_max precomputed by backend for quick amount buttons.

Frontend: auto-disable payment methods that can't handle the entered amount,
auto-switch to first available method, show warning when no method fits.

Refactor: extract ChannelLimits named type, unionFloat helper to eliminate
repeated min/max/daily logic, split limits code to payment_config_limits.go,
replace magic strings with payment.Type* constants.
2026-04-08 02:18:02 +08:00
erio 14be762062 fix(ui): toggle switches for enabled/refund, 3-col quick amounts, fix amounts list 2026-04-08 01:43:54 +08:00
erio 74f40cbab5 fix(ui): provider dialog layout - mode+types same row, toggles below name 2026-04-08 01:37:09 +08:00
erio 1ab77a86a5 fix(payment): critical fixes, constants, type safety, and order recovery
Backend:
- Fix order physical deletion → status update to FAILED
- Fix sync.Once race condition → mutex + bool pattern
- Fix encryption key error silently ignored in wire.go
- Fix ProviderInstanceResponse missing payment_mode field
- Fix fullyDecodeURL infinite loop → single decode
- Fix io.ReadAll without size limit → LimitReader
- Fix webhook log exposing full rawBody → truncate + debug level
- Recover cancelled/expired orders on webhook payment success

Frontend:
- Extract METHOD_ORDER to providerConfig.ts, remove duplicates
- Add PAYMENT_MODE_REDIRECT/API constants, use in all components
- Fix any types → unknown in StripePaymentView, PaymentView
- Fix hardcoded English text → i18n keys
- Fix Vue Router query as string → String()
- Fix METHOD_ICONS.easypay reference to non-existent key
2026-04-08 01:24:41 +08:00
erio 47cb495ede feat(payment): separate payment mode (redirect/api) from payment methods
- Add payment_mode field to payment_provider_instances table
- EasyPay provider uses config paymentMode instead of TypeEasyPay
- Remove 'easypay' from supported_types, user-facing payment methods
- Admin dialog: add payment mode selector (redirect/QR) for EasyPay
- ProviderCard: display mode label alongside provider type
- User payment page: only shows alipay/wxpay/stripe (no more 'easypay')
- Migration: auto-convert existing easypay instances to redirect mode
2026-04-08 00:37:14 +08:00
erio f15500cd44 refactor(ui): extract btn-stripe shared class, reuse btn-primary/btn-secondary 2026-04-07 23:57:00 +08:00
erio f1cb798856 fix(ui): use themed colors for QR page buttons 2026-04-07 23:34:25 +08:00
erio ad19fb62f5 fix(payment): revert Stripe to same-window navigation, use Stripe brand color for pay button 2026-04-07 23:30:41 +08:00
erio cfdc594e4f feat(payment): dynamic limit placeholders and open Stripe in new window 2026-04-07 23:21:46 +08:00
erio 8e2977d404 fix(ui): show icon + text labels in order actions column 2026-04-07 22:48:45 +08:00
erio 10851f1c8c fix(payment): set Stripe payment method order to alipay/wechat/card/link 2026-04-07 22:39:12 +08:00
erio f92397642a feat(i18n): add error code mapping for payment API errors 2026-04-07 22:03:11 +08:00
erio f3276f1c17 fix(ui): improve cancel rate limit layout to natural language style 2026-04-07 21:55:20 +08:00
erio a0b4bd065a fix(payment): prioritize actual order status over URL params in result page 2026-04-07 21:48:07 +08:00
erio 439fbec790 chore: bump version to 0.1.108.84 2026-04-07 20:19:15 +08:00
erio 3759a5c53e fix(payment): use official SVG icon files for payment methods 2026-04-07 20:04:59 +08:00
erio 8f16b01943 feat(payment): standalone result page, method order & QR logos
- Payment result page is now a standalone page without business layout
- Support EasyPay return_url params (out_trade_no, trade_status)
- Payment methods sorted: EasyPay → Alipay → WeChat → Stripe
- Alipay/WeChat use official brand SVG icons
- QR code displays Alipay/WeChat logo in center with scan prompt
2026-04-07 17:50:19 +08:00
erio 63ead5e88a fix(ui): move reopen payment button below countdown 2026-04-07 16:30:18 +08:00
erio 5a2d6dd839 feat(payment): show order status page after EasyPay redirect payment
- After opening pay_url in new window, navigate to order status page
  with countdown timer and order polling (reuses QRCode view)
- QRCode view now shows "pay in new window" UI when no QR code present
- Add verbose logging for webhook signature verification failures
2026-04-07 16:03:02 +08:00
erio 18cd8bd63b fix(stripe): await nextTick before mounting Stripe payment element
Set loading=false and await nextTick() before calling mount() so the
#stripe-payment-element DOM node exists when Stripe.js tries to use it.
2026-04-07 15:43:30 +08:00
erio 27887164e4 feat(payment): subscription plan cards colored by group platform
- Backend GetPlans API enriches plans with group_platform field
- SubscriptionPlanCard uses platform-based color scheme (border, badge, price, features, button)
- anthropic=amber, openai=emerald, antigravity=purple, gemini=blue
2026-04-07 13:39:36 +08:00
erio fd8d3e6de5 feat(payment): cancel rate limit, easypay multi-CID, payment UX improvements
- EasyPay redirect passes all configured CIDs (cidAlipay, cidWxpay) comma-separated
- Payment redirect opens in new window instead of current window redirect
- Order action column uses styled icon buttons with tooltip
- Pending order limit error returns i18n-friendly message with metadata
- Cancel order rate limit feature (ported from sub2apipay) with rolling/fixed window modes
- Admin settings UI for cancel rate limit configuration
- Fix provider dialog triggering unintended settings save on open
- Provider save now correctly calls saveSettings after provider update
- API client interceptor forwards reason and metadata fields for structured error handling
2026-04-07 13:29:39 +08:00
erio 37292367c9 feat(payment): UI improvements for admin payment pages
- Plan table: group column shows GroupBadge with platform color instead
  of raw ID, column header changed to "分组"
- Plan dialog: group info preview uses GroupBadge, removes redundant
  platform/rate fields
- Action buttons: all payment admin tables (plans, orders, providers)
  now use vertically stacked icon+text buttons matching the app style
- Added i18n keys: payment.admin.group, common.view
2026-04-07 11:36:36 +08:00
erio 6c5a2452f2 refactor(payment): unify all JSON tags to snake_case
Backend request/response structs used camelCase JSON tags while the
rest of the codebase uses snake_case (from Ent ORM entities). This
caused field name mismatches — notably the admin refund deduct_balance
field was silently ignored.

Backend: changed all camelCase JSON tags in payment handler/service
structs to snake_case (CreateOrderRequest, CreateOrderResponse,
CreatePlanRequest, UpdatePlanRequest, CreateProviderInstanceRequest,
UpdateProviderInstanceRequest, AdminProcessRefundRequest, RefundResult).

Frontend: updated all API call payloads and response field accesses
to use snake_case, removed manual camelCase conversion in
buildPlanPayload, toggleForSale, provider dialog emit, etc.
2026-04-07 11:27:00 +08:00
erio 011ca6da93 fix(payment): parse money field in EasyPay webhook, cancel button text
- EasyPay VerifyNotification was not parsing the 'money' field, causing
  Amount=0 in PaymentNotification → amount mismatch error on callback
- Cancel button on QR page now shows "取消订单" instead of generic "取消"
2026-04-07 11:01:32 +08:00
erio c48c003f3f fix(payment): plan save with camelCase keys, group badge, toggle switch
- Fix plan create/update: send camelCase keys (groupId, validityDays,
  forSale, etc.) matching backend JSON tags instead of snake_case
- Show GroupBadge with platform color instead of raw group ID in table
- Replace checkbox with toggle switch for for_sale, support quick
  toggle directly from the plan list
- Parse features string to array when loading plans (both admin and user)
- Fix price display to use ¥ (CNY) instead of $
2026-04-07 10:56:03 +08:00
erio 0cca4524c9 fix(payment): webhook GET support, Stripe as single method, QR page improvements
- EasyPay webhook: add GET route + read params from URL query (fix 404)
- Stripe: expose as single "stripe" method to users, sub-types (card/link/
  alipay/wxpay) passed to PaymentIntent internally via instance config
- QR code page: use order expiresAt for countdown, add cancel button
- InstanceSelection carries SupportedTypes for provider-specific routing
2026-04-07 10:38:21 +08:00
erio 432ed5e649 fix(payment): critical fixes from agent audit
- Fix CreateOrderResult field names (snake_case → camelCase to match backend)
- Fix MethodLimits JSON tags to consistent snake_case
- Fix Stripe webhook header case sensitivity (lowercase keys for map lookup)
- Fix MaxAmount=0 backend validation (0 = no limit, not reject all)
- Fix structured error for INVALID_AMOUNT per CLAUDE.md spec
2026-04-07 03:28:30 +08:00
erio 4e494e484a fix: sync CreateOrderRequest field names in payment store 2026-04-07 03:22:33 +08:00
erio 82cc410cdf fix(payment): fix order creation + show actual provider types on payment page
- Fix CreateOrderRequest field name mismatch (payment_type → paymentType)
- Payment page now discovers available types from providers (not global config)
- Backend GetLimits returns map keyed by payment type
- EasyPay shows 3 buttons (跳转/支付宝/微信), Stripe shows card/alipay/wxpay/link
- Auto-select first method after limits loaded
2026-04-07 03:20:26 +08:00