Commit Graph
1191 Commits
Author SHA1 Message Date
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
erio e1fe15010c fix: add payment fields to UpdateSettingsRequest, remove as any casts and dead code
- Add payment fields to UpdateSettingsRequest TypeScript interface
- Remove duplicate payment_enabled_types assignment in saveSettings
- Remove all (payload as any) casts for payment fields
- Remove unused parseTypes function from providerConfig.ts
2026-04-07 03:03:09 +08:00
erio 7eaccdf125 fix: supportedTypes emit type string -> string[] 2026-04-07 02:55:28 +08:00
erio 57cb01cd49 refactor(payment): use string[] for supported_types throughout frontend+backend API
- Backend API returns/accepts supported_types as string[] (converts to/from DB comma string)
- Frontend ProviderInstance.supported_types is string[], no more parseTypes
- Remove all split/join conversions for supported_types
- payment_enabled_types also uses string[] consistently
2026-04-07 02:54:03 +08:00
erio 18107819da fix: remove unused parseTypes import, fix string[] type mismatch 2026-04-07 02:45:16 +08:00
erio 957afed0f0 fix(payment): use string[] directly for payment_enabled_types, remove all split/join conversions 2026-04-07 02:43:00 +08:00
erio 862f1c846b fix: parseTypes handles array input defensively (prevents .split crash) 2026-04-07 02:34:58 +08:00
erio 49c683de62 fix: convert payment_enabled_types array→string before Object.assign to prevent .split() crash 2026-04-07 02:26:35 +08:00
erio 627d15f79c fix(i18n): add missing common.saved and common.deleted keys 2026-04-07 02:22:17 +08:00
erio 626da67801 fix: SettingsForm type uses Omit for payment_enabled_types string vs string[] 2026-04-07 02:15:50 +08:00
erio 5c5c4e232c fix: add payment fields to frontend SystemSettings type 2026-04-07 02:14:26 +08:00
erio 21b76c7b0c feat(payment): integrate payment config into system settings API
- Backend: payment fields added to GET/PUT /admin/settings (full replace)
- Frontend: single API call for all settings (no separate payment config API)
- Payment page: show "充值未开放" when no payment methods available
- Pending order check when disabling provider
2026-04-07 02:12:34 +08:00
erio d02f8a3e2f fix(payment): structured error responses + disable provider card hint overlay
- Backend: PENDING_ORDERS error uses reason+metadata per CLAUDE.md spec
- Block disabling provider when it has pending orders
- ProviderCard: remove bottom hint area, use opacity + title tooltip instead
2026-04-07 01:49:45 +08:00
erio 757396d1c0 fix: use Record<string, unknown> for plan create/update API params 2026-04-07 01:38:03 +08:00
erio 756cd2bad4 fix: plan API accepts features as string, fix TypeScript types 2026-04-07 01:36:45 +08:00
erio bb187fc701 fix(payment): send features as string not array when creating/updating plan 2026-04-07 01:35:18 +08:00
erio f3ea2f9f55 fix(payment): 0 means no limit, not a default value
- min/max/daily = 0 means no limit (no enforcement)
- Quick amounts: show all when no limit set
- Amount validation: skip check when limit is 0
- Placeholder: adaptive (shows range, ≥min, ≤max, or "输入金额")
2026-04-07 01:27:44 +08:00
erio 06b844851f fix(payment): empty global min/max/daily displays correctly as empty
- Backend returns 0 for unset min/max/daily (not filled defaults)
- Frontend form: 0 displays as empty with placeholder "留空表示不限制"
- Save: empty → 0 → backend stores "" → returns 0 on next load
- Payment page: 0 fallback to sensible defaults (min=1, max=unlimited)
2026-04-07 01:21:54 +08:00
erio ac00a0aeb3 fix(payment): full-replace config update + fix min/max defaults
- Payment config update is now full-replace (not patch): all fields sent every time
- 0 values for min/max/daily = clear (use default: min=1, max=unlimited)
- Payment page: provider-level limits override global, proper fallback chain
- Fix quick amounts disappearing when global min/max is empty
2026-04-07 01:11:20 +08:00
erio ed03fbf7ba fix: add help_image_url/help_text to AdminPaymentConfig TypeScript types 2026-04-07 00:57:00 +08:00
erio a481724140 fix(payment): add missing help_image_url/help_text fields + fix config merge on update
- Add help_image_url and help_text to PaymentConfig, UpdateRequest, read/write logic
- Add these fields to frontend paymentPayload save and load
- Fix provider config update: merge new config with existing (preserves sensitive
  fields that frontend skips as ••••••••, prevents data loss on edit)
2026-04-07 00:55:13 +08:00
erio 8648950f44 fix(payment): card/link are Stripe sub-methods, not top-level providers
- Remove card/link from global allPaymentTypes (they belong inside Stripe provider)
- Resolve i18n labels for Stripe sub-methods in provider dialog and card
2026-04-07 00:46:11 +08:00
erio f906b5f1f3 feat(payment): add Link payment method for Stripe
- Add TypeLink payment type
- Stripe provider now supports card/alipay/wxpay/link
- Add Link i18n labels and PaymentMethodSelector styles
2026-04-07 00:32:25 +08:00
erio 55b526582b feat(payment): Stripe multi-method support + card type + hide single-type selector
- Add TypeCard payment type for bank card payments
- Stripe provider now supports card/alipay/wxpay payment methods
- Map payment types to Stripe payment_method_types (card, alipay, wechat_pay)
- Hide supported types selector for single-type providers (alipay/wxpay official)
- Add card i18n labels and PaymentMethodSelector styles
2026-04-07 00:23:54 +08:00
erio ec88ca6c57 fix(payment): add placeholder hint on per-provider limit inputs 2026-04-07 00:01:45 +08:00
erio b1a8252f25 feat(payment): independent callback URLs + sensitive field visibility toggle
- Split callback URLs into two independent inputs (notify/return each with own base URL + fixed path)
- Add eye icon toggle for sensitive fields (PKey etc.) to reveal/hide values
- Bump version to 0.1.108.35
2026-04-06 23:58:16 +08:00
erio 5e723b2911 fix(payment): collapsible limits, reject <=0, hint uses global config
- Limits section collapsible (default collapsed, auto-expand if has values)
- Empty input = use global config; reject values <= 0
- Input min changed from 0 to 1, placeholder changed to empty
- Hint: "留空表示使用全局配置"
2026-04-06 23:30:57 +08:00
erio 438782d536 fix(payment): split callback URL into base URL + fixed path, wider dialog
- Callback URLs now split: editable base URL + read-only fixed path
- Base URL defaults to current domain, user can change it
- notifyUrl/returnUrl computed as baseUrl + fixed webhook path
- Edit loads existing base URL by extracting it from stored notifyUrl
- Dialog width set to wide for more space
- Strict validation: all required fields must be filled on both create/edit
2026-04-06 23:15:32 +08:00
erio 34bd16ea3c fix(payment): strict validation — all required fields must be filled on edit
- Edit mode: sensitive fields show ••••••••, must remain filled to save
- Non-sensitive fields pre-filled from backend on edit
- Empty defaults (notifyUrl/returnUrl) auto-filled if backend has no value
- Submit filters out ••••••••  to keep existing backend credentials
- All required fields validated regardless of create/edit mode
2026-04-06 23:02:34 +08:00
erio eff86c9a72 fix(payment): return decrypted non-sensitive config on provider list
- Backend: add ListProviderInstancesWithConfig that decrypts config
  and masks sensitive fields (keys, secrets) as "••••••••"
- Frontend: pre-fill non-sensitive config values when editing provider
  (PID, API base URL, notify URL, return URL, channel IDs etc.)
- Sensitive fields left empty for user to re-enter if needed
2026-04-06 22:54:17 +08:00
erio 9bc8a4b4df fix(payment): reduce provider dialog width to default 2026-04-06 22:37:55 +08:00
erio c891439d2a refactor(payment): extract provider management into components + add limits UI
Components extracted from SettingsView:
- providerConfig.ts: shared constants, types, helpers
- PaymentProviderList.vue: provider card list with header
- ProviderCard.vue: single provider card with inline toggles
- ToggleSwitch.vue: reusable toggle switch component
- PaymentProviderDialog.vue: create/edit dialog with config + limits

Other changes:
- Add per-payment-type limits config UI (singleMin, singleMax, dailyLimit)
- Remove generic cid from EasyPay config (keep cidAlipay/cidWxpay only)
- Backend: remove paymentMode constants, determine mode by PaymentType
- Backend: add validateProviderRequest for create
- Add limits field to ProviderInstance type
- SettingsView reduced by ~200 lines of provider management code
2026-04-06 22:34:06 +08:00
erio c3a5cc685e fix(payment): merge supported types badges into provider card top row 2026-04-06 22:09:17 +08:00
erio 6676d1f7ca feat(payment): auto-fill callback URLs + complete provider config fields
- notifyUrl/returnUrl auto-fill with current domain + webhook path on create
- Default paths: /api/v1/payment/webhook/{provider} and /payment/result
- Add missing EasyPay config: cid, cidAlipay, cidWxpay (optional channel IDs)
- Stripe: show webhook URL hint with copyable endpoint below config
- Fields with defaults show placeholder text for reference
- Also apply defaults when switching provider type in dialog
2026-04-06 21:50:54 +08:00
erio 12369ae821 fix(payment): provider card toggle badges for supported types + strict validation
- Replace text "alipay,wxpay" with clickable toggle badges on provider cards
- Badges use same style as dialog; click to enable/disable types via API
- Prevent disabling all types (at least one required)
- Strict create validation: all non-optional config fields must be filled
2026-04-06 21:37:46 +08:00
erio 29c3670c62 fix(payment): remove paymentMode config, rename easypay to 跳转, add validation
Backend:
- Remove paymentMode config; determine mode by req.PaymentType directly
  (easypay=redirect/submit.php, alipay/wxpay=API/mapi.php)
- EasyPay.SupportedTypes() always returns [easypay, alipay, wxpay]
- Add validateProviderRequest() for create: name, providerKey, supportedTypes required

Frontend:
- Remove paymentMode dropdown from EasyPay config fields
- Show "跳转" label for easypay type in provider supported types toggle
- Add red * to required fields: provider name, provider key, supported types
- Add strict validation in handleSaveProvider before API call
- Add validation i18n keys for zh/en
2026-04-06 21:17:53 +08:00
erio 2f43e5a8fb fix(payment): redesign provider cards — full-width, inline toggles, greyed-out when type disabled
- Provider cards: one per row (full width), remove 2-column grid
- Add inline toggle switches for enabled/disabled and refund on each card
- Toggle switches call API directly without opening edit dialog
- When provider type is disabled above, entire card is greyed out with
  pointer-events-none and shows hint text
- Rename "启用的支付方式" → "服务商"
2026-04-06 21:07:36 +08:00
erio bfcc487a6b feat(payment): add EasyPay dual-mode support (redirect + API)
- Backend: EasyPay provider supports paymentMode config (redirect/api)
  - redirect: builds submit.php signed URL, browser redirects to hosted page
  - api: calls mapi.php, returns payurl/qrcode (existing behavior)
- Add TypeEasyPay payment type constant
- Fix notify/return URL fallback to instance config
- Handle empty TradeNo for redirect mode (SetNillable)
- Frontend: add paymentMode dropdown in EasyPay provider config
- Auto-update supported_types when paymentMode changes
- Add easypay icon/color in PaymentMethodSelector
- Add i18n for easypay, paymentMode labels
- Rename "启用的支付方式" to "服务商"
- Migration 098: clean easypay from ENABLED_PAYMENT_TYPES
2026-04-06 20:57:45 +08:00
erio d8f2fd3c94 fix(frontend): unify purchase icon with subscriptions (CreditCardIcon)
Use the same CreditCardIcon for both "我的订阅" and "充值/订阅" sidebar
items. Remove unused RechargeSubscriptionIcon.
2026-04-06 19:57:42 +08:00
erio 32907268f9 fix(frontend): mark provider config fields required/optional correctly
- apiBase, notifyUrl, returnUrl: remove optional flag (required — backend
  relies on these in provider config, no fallback)
- Only wxpay publicKeyId + certSerial remain optional
- Show red * for required fields, (可选) for optional fields
2026-04-06 19:43:50 +08:00
erio 91413cf410 feat(payment): order safety protection for provider/plan CRUD
Backend (matching sub2apipay behavior):
- UpdateProviderInstance: block credential changes (key/secret/password
  fields) when instance has PENDING/PAID/RECHARGING orders (409 Conflict)
- DeleteProviderInstance: block deletion when pending orders exist (409)
- DeletePlan: block deletion when pending orders exist (409)
- Non-credential changes (name/enabled/sort) still allowed with pending orders

Frontend:
- Filter empty config values before sending (avoid overwriting existing
  credentials when editing a provider without re-entering all secrets)
2026-04-06 19:31:26 +08:00
erio f69293c998 fix(frontend): provider creation — fix JSON field names, i18n, remove extra fields
Root cause: frontend sent snake_case (provider_key, supported_types) but
backend expects camelCase (providerKey, supportedTypes) → internal error.

Changes:
- Fix handleSaveProvider to send camelCase payload matching Go struct
- Remove CID/CID Alipay/CID Wxpay fields (not in sub2apipay reference)
- Data-driven config fields: PROVIDER_CONFIG_FIELDS per provider type
  (matches sub2apipay exactly: easypay=5 fields, alipay=5, wxpay=8, stripe=3)
- Full i18n for all config field labels (zh: 异步通知地址/私钥/商户号/etc)
- Dynamic providerConfig as Record<string,string> instead of hardcoded reactive
2026-04-06 19:04:01 +08:00
erio 515f4aa0ce fix(frontend): remove balance-disabled checkbox, disable provider add when no types enabled
- Remove redundant "禁用余额充值" checkbox
- Disable "添加服务商" button when no payment types are enabled
- Show hint "请先在上方启用至少一种支付方式" when empty + no types
2026-04-06 18:28:17 +08:00