Commit Graph
2489 Commits
Author SHA1 Message Date
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 42a0b523fa chore: bump version to 0.1.108.87 2026-04-08 13:07:24 +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 ffe3b07d0d chore: bump version to 0.1.108.86 2026-04-08 12:36:33 +08:00
erio 27d3232333 test(payment): add unit tests for limits aggregation and type grouping
30 test cases covering:
- unionFloat: min/max merge semantics, unlimited propagation
- pcAggregateMethodLimits: single/multi instance, unlimited, invalid JSON
- pcGroupByPaymentType: Stripe isolation from alipay/wxpay groups
- pcComputeGlobalRange: widest range, partial unlimited
- pcInstanceTypeLimits: parsing, missing type, invalid JSON
- GetBasePaymentType: all type constants including composites
2026-04-08 02:58:34 +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 0bb1bfea3e chore: bump version to 0.1.108.85 2026-04-08 02:20:12 +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 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 96f2fcdda3 fix(payment): upgrade stripe-go v82 to v85 for API version 2026-03-25.dahlia 2026-04-07 23:08:49 +08:00
erio 439fbec790 chore: bump version to 0.1.108.84 2026-04-07 20:19:15 +08:00
erio 62398107ec fix(payment): set MinAmount default to 1, prevent zero-amount orders 2026-04-07 19:19:34 +08:00
erio 72022c2d63 fix(ci): fix TestParsePaymentConfig default values and renew xlsx audit exceptions 2026-04-07 18:50:44 +08:00
erio 4e68e1497a fix(ci): fix TestParsePaymentConfig default values and renew xlsx audit exceptions 2026-04-07 18:33:00 +08:00
erio 4b30186adb chore: bump version to 0.1.108.83 2026-04-07 18:30:05 +08:00
erio b35843ee8f chore: bump version to 0.1.108.82 2026-04-07 17:50:55 +08:00
erio f9e581bb82 fix(payment): fully URL-decode EasyPay callback params before signature verification
Upstream proxy double-encodes GET query params. url.ParseQuery only
decodes once, leaving values like %E5%BF%AB instead of actual UTF-8.
Add fullyDecodeURL to repeatedly decode until stable before signing.
2026-04-07 16:39:34 +08:00
erio e2e5c814bc chore: bump version to 0.1.108.80 2026-04-07 16:30:58 +08:00
erio 8f08d8a912 fix(payment): fix EasyPay webhook double-URL-encoding signature failure
RawQuery from GET callbacks can be double-URL-encoded by upstream
proxies. Use Query().Encode() to rebuild from fully decoded params,
ensuring VerifyNotification computes the signature on correct values.
2026-04-07 16:27:53 +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 f7efa15ec7 fix(csp): auto-inject Stripe domains into CSP regardless of config source
enhanceCSPPolicy now adds https://*.stripe.com to script-src and
frame-src when not already present, ensuring Stripe.js loads even
when the CSP policy comes from database settings.
2026-04-07 15:29:15 +08:00
erio 4de9163e55 fix(payment): remove cid param from EasyPay redirect payments 2026-04-07 15:07:57 +08:00
erio 0a4ea55636 fix(payment): add Stripe domains to CSP and show upstream payment errors
- Add https://*.stripe.com to script-src and frame-src in default CSP
  policy so Stripe.js can load in the browser
- Show upstream payment gateway error details instead of generic
  "temporarily unavailable" message
2026-04-07 14:36:53 +08:00
erio e6042e3e8e fix(channel): add missing features column to List query
The paginated List query was selecting 9 columns but scanning 10 fields,
missing c.features. GetByID and ListAll already included it correctly.
2026-04-07 13:47:12 +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 bc87384ea2 chore: bump version to 0.1.108.70 2026-04-07 11:45:49 +08:00
erio 1455ade5cd fix(payment): use selected instance config for CreatePayment
Root cause: invokeProvider used the registry's provider (created once
at startup from an arbitrary instance) instead of the selected
instance's config. This meant payments always used one instance's
credentials regardless of which instance was selected.

Fix: create a fresh provider from the selected instance's decrypted
config for each payment call, ensuring correct credentials (PID, PKey,
CID, Stripe keys, etc.).

Also fix SelectInstance for Stripe: when paymentType equals providerKey
(e.g. "stripe"), all instances of that provider are candidates. The
DB stores sub-types (card,alipay,wxpay,link) not "stripe" itself.
2026-04-07 11:45:49 +08:00
erio 126b86f269 chore: bump version to 0.1.108.69 2026-04-07 11:37:10 +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 610408a4d0 chore: bump version to 0.1.108.68 2026-04-07 11:01:54 +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 56dbbabf1c chore: bump version to 0.1.108.67 2026-04-07 10:57:12 +08:00
erio b781129ea5 chore: bump version to 0.1.108.66 2026-04-07 10:38:42 +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 d98b4ffaad fix(payment): expose Stripe publishable key in payment config API
GetPaymentConfig now loads publishable key from the first enabled Stripe
provider instance, so the frontend can initialize Stripe.js.
2026-04-07 03:35:44 +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 11701e9b5e fix: guard against accidental payment config wipe + cleanup from review
- Skip UpdatePaymentConfig when no payment fields provided (prevents wipe)
- Remove unused defaultMinRechargeAmount/defaultMaxRechargeAmount constants
- Fix mergeConfig comment to match actual behavior
2026-04-07 03:05: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