1. 23 Apr, 2026 3 commits
    • erio's avatar
      revert: remove fork-only changes from release sync · 67518a59
      erio authored
      Revert payment/wechat, sora/claude-max cleanup, fork-only migrations,
      and cosmetic changes that were brought in by the release sync commit.
      Keep only channel-monitor related improvements:
      - PublicSettingsInjectionPayload named struct with drift test
      - ChannelMonitorRunner graceful shutdown in wire
      - image_output_price in SupportedModelChip
      - Simplified buildSelfNavItems in AppSidebar
      - Gateway WARN logs for 503 branches
      67518a59
    • erio's avatar
      chore: remove test files deleted in release · 49787269
      erio authored
      HelpTooltip.spec.ts and PaymentProviderDialog.spec.ts were removed
      in release/custom-0.1.115; commit the deletion.
      49787269
    • erio's avatar
      sync: bring over remaining release/custom-0.1.115 changes · 748a84d8
      erio authored
      - Extract PublicSettingsInjectionPayload named struct with drift test
      - Add channel_monitor_default_interval_seconds to SSR injection
      - Add image_output_price to SupportedModelChip
      - Simplify AppSidebar buildSelfNavItems (admins see available channels)
      - Add gateway WARN logs for 503 no-available-accounts branches
      - Wire ChannelMonitorRunner into provideCleanup for graceful shutdown
      - Add migrations 130/131 (CC template userid fix + mimicry field cleanup)
      - Clean up fork-only features (sora, claude max simulation, client affinity)
      - Remove ~320 obsolete i18n keys
      - Add codexUsage utility, WechatServiceButton, BulkEditAccountModal
      - Tidy go.sum
      748a84d8
  2. 22 Apr, 2026 2 commits
  3. 21 Apr, 2026 3 commits
  4. 20 Apr, 2026 5 commits
    • IanShaw027's avatar
      fix payment resume result consistency · a27a7add
      IanShaw027 authored
      a27a7add
    • IanShaw027's avatar
      7ef7fd19
    • erio's avatar
      feat(payment): i18n payment error codes and label localization · 40d4e167
      erio authored
      Pairs with the backend structured payment errors (reason + metadata). The
      frontend now maps reason codes to localized messages with metadata as
      interpolation variables, and automatically localizes raw config-field names
      (e.g. "certSerial" → "证书序列号") using the existing UI-label i18n
      namespace.
      
      - frontend/src/utils/apiError.ts
        - extractApiErrorCode now prefers the string `reason` over the numeric HTTP
          `code`; reason is granular enough to drive i18n lookup, HTTP code is not.
        - New extractApiErrorMetadata to pull interpolation params off the error.
        - New extractI18nErrorMessage(err, t, namespace, fallback): looks up
          `<namespace>.<REASON>` in i18n and substitutes metadata. Before
          substitution, `metadata.key` and `metadata.keys` (slash-joined) are
          re-translated through `admin.settings.payment.field_<key>` so users see
          "缺少必填项:证书序列号" instead of "缺少必填项:certSerial".
      
      - frontend/src/i18n/locales/{zh,en}.ts
        - Add payment.errors entries for every structured reason code returned by
          the backend (PAYMENT_DISABLED, INVALID_AMOUNT, TOO_MANY_PENDING,
          DAILY_LIMIT_EXCEEDED, NO_AVAILABLE_INSTANCE, PAYMENT_PROVIDER_MISCONFIGURED,
          WXPAY_CONFIG_MISSING_KEY / INVALID_KEY_LENGTH / INVALID_KEY, NOT_FOUND,
          FORBIDDEN, CONFLICT, INVALID_ORDER_TYPE, INVALID_STATUS,
          BALANCE_NOT_ENOUGH, REFUND_AMOUNT_EXCEEDED, REFUND_FAILED, and more),
          with placeholders for template variables.
      
      - 13 payment-related Vue files
        - Migrate catch-block error reporting from extractApiErrorMessage to
          extractI18nErrorMessage(err, t, 'payment.errors', fallback).
        - Remove the ad-hoc paymentErrorMap computed in SettingsView.vue, which the
          new helper supersedes (it reads i18n directly via t).
      
      - frontend/src/components/payment/providerConfig.ts
        - wxpay: publicKey and publicKeyId are now required (was optional), matching
          the pubkey-only verifier direction; certSerial is already required.
      
      This PR is drop-in safe: reason-preferring extractApiErrorCode is backward
      compatible with callers that pass their own i18nMap, and error codes missing
      from i18n fall back to the existing message-based path.
      40d4e167
    • IanShaw027's avatar
      feat: wire payment return url payloads · b51bc7ee
      IanShaw027 authored
      b51bc7ee
    • IanShaw027's avatar
      feat: rebuild auth identity foundation flow · e9de839d
      IanShaw027 authored
      e9de839d
  5. 18 Apr, 2026 2 commits
    • erio's avatar
      feat(payment): redact provider secrets in admin config API · 235f7108
      erio authored
      Admin GET /api/v1/admin/payment/providers previously returned every
      config value — including privateKey / apiV3Key / secretKey etc. —
      verbatim. Any future XSS on the admin UI would hand attackers the
      full set of production payment credentials, and the plaintext values
      sat unnecessarily in browser memory for every operator.
      
      Treat those fields as write-only from the admin surface:
      
      - decryptAndMaskConfig() strips sensitive keys from the GET response.
        The authoritative list is an explicit per-provider registry that
        mirrors the frontend's PROVIDER_CONFIG_FIELDS sensitive flag:
          alipay   → privateKey, publicKey, alipayPublicKey
          wxpay    → privateKey, apiV3Key, publicKey
          stripe   → secretKey, webhookSecret (publishableKey stays plain)
          easypay  → pkey
        Payment runtime still reads the full config via decryptConfig, so
        nothing at the gateway changes.
      
      - mergeConfig() treats an empty value for a sensitive key as "leave
        unchanged" — the admin UI omits unchanged secrets so operators can
        tweak non-sensitive settings without re-entering credentials.
      
      - Admin dialog (PaymentProviderDialog.vue):
        * secret inputs get autocomplete="new-password", data-1p-ignore,
          data-lpignore and data-bwignore so password managers do not
          offer to save provider credentials
        * in edit mode the required-field check skips sensitive fields
          (empty is the "keep existing" signal) and the placeholder shows
          "leave empty to keep" instead of the default example value
        * create mode still requires every non-optional field, including
          secrets, since there is nothing to preserve
      
      - Unit test renamed to TestIsSensitiveProviderConfigField, covers
        the per-provider registry and specifically asserts that Stripe's
        publishableKey is NOT treated as a secret.
      235f7108
    • erio's avatar
      fix(payment): alipay redirect-only flow, H5 detection and popup sizing · c3cb0280
      erio authored
      The native Alipay provider previously tried to embed the payment page
      URL into a QR code on the client — the URL is not a scannable payload
      so the QR never worked. Merchants also hit a H5 detection mismatch
      whenever the backend UA sniffer missed iPadOS 13+ or embedded browsers,
      and the popup window was too small for Alipay's standard checkout
      layout (QR + account-login panel on the right), forcing the user to
      scroll horizontally and vertically.
      
      Changes:
      
      Backend
      - alipay.go: drop QR-on-URL path. Use redirect-only flow —
        alipay.trade.page.pay for PC (returns a gateway URL the browser
        opens in a new window) and alipay.trade.wap.pay for H5 (returns a
        URL the browser jumps to). Both flows produce pages on
        openapi.alipaydev.com / excashier.alipay.com; the client never
        renders a QR itself.
      - payment_handler.go: add optional is_mobile bool to
        CreateOrderRequest so the frontend can declare the device
        explicitly. Server still falls back to UA sniffing when absent.
      
      Frontend
      - types/payment.ts, PaymentView.vue: declare is_mobile in
        CreateOrderRequest and pass the computed isMobileDevice() value.
      - providerConfig.ts: replace the two fixed POPUP_WINDOW_FEATURES
        constants with getPaymentPopupFeatures(), which prefers 1250×900
        (Alipay's checkout footprint), clamps to window.screen.avail* and
        centers the popup so it never overflows on smaller laptops.
      - PaymentQRDialog.vue, PaymentStatusPanel.vue, StripePaymentInline.vue,
        PaymentView.vue: use the new helper at all popup call sites.
      c3cb0280
  6. 14 Apr, 2026 4 commits
    • erio's avatar
      fix(payment): integrate recharge fee rate in order flow and fix UI display · e761d38f
      erio authored
      Backend:
      - Use cfg.RechargeFeeRate in order creation instead of hardcoded 0
      - Remove dead getFeeRate stub method
      - All amounts computed server-side: order_amount, pay_amount, fee_rate
      
      Frontend - PaymentView:
      - Read recharge_fee_rate from checkout-info API (not per-method)
      - Show fee breakdown only when fee_rate > 0
      - Show credited amount only when multiplier ≠ 1
      
      Frontend - Order display (user + admin):
      - Fix fee_rate * 100 bug (fee_rate is already a percentage)
      - OrderTable: show pay_amount as primary, fee/credited as sub-lines
      - AdminOrderDetail: full breakdown (base/fee/paid/credited)
      - AdminRefundDialog: label "到账金额" for clarity
      - PaymentResultView: show pay_amount with fee info
      
      Types + i18n:
      - Add recharge_fee_rate to CheckoutInfoResponse
      - Add fee_rate to CreateOrderResult
      - Add translations: creditedAmount, fee, baseAmount, includedInPayAmount
      e761d38f
    • erio's avatar
      feat(payment): add recharge fee rate setting and fix provider card UI · 98140f6c
      erio authored
      - Add recharge_fee_rate system setting (percentage fee on top of recharge amount)
      - Full backend chain: config constant, PaymentConfig struct, update validation,
        read/write persistence, DTO, handler GET/PUT responses
      - Frontend: settings input with preview, i18n (zh/en), API types
      - Fix provider card toggle layout: labels above switches to save width
      - Fix Chinese translation: "EasyPay" → "易支付" in provider description
      98140f6c
    • erio's avatar
      feat(payment): balance recharge multiplier and refund amount separation · 60a4b931
      erio authored
      - Add balance_recharge_multiplier system setting (e.g. 1.2 = charge 100 get 120)
      - Separate order_amount (credited balance) from pay_amount (actual payment)
      - Refund calculates gateway amount proportionally from pay_amount
      - Frontend shows both amounts in order details, payment status, refund dialog
      - Admin settings UI for configuring recharge multiplier
      60a4b931
    • erio's avatar
      feat: add per-provider allow_user_refund control and align wildcard matching · f1297a36
      erio authored
      allow_user_refund:
      - Add allow_user_refund field to PaymentProviderInstance ent schema
      - Migration 103: ALTER TABLE payment_provider_instances ADD COLUMN
      - Cascade logic: disabling refund_enabled auto-disables allow_user_refund
      - User refund validation: check provider instance allows user refund
      - Admin refund validation: check provider instance allows admin refund
      - Subscription refund: deduct days on refund, rollback on failure
      - New endpoint: GET /payment/orders/refund-eligible-providers
      - Frontend: ToggleSwitch in ProviderCard/Dialog, cascade in SettingsView
      
      Wildcard matching:
      - Change findPricingForModel from "longest prefix wins" to "config order
        priority (first match wins)", aligning with channel service behavior
      f1297a36
  7. 13 Apr, 2026 1 commit
  8. 11 Apr, 2026 2 commits
    • erio's avatar
      refactor(payment): code standards fixes and regression repairs · e3a000e0
      erio authored
      Backend:
      - Split payment_order.go (546→314 lines) into payment_order_lifecycle.go
      - Replace magic strings with constants in factory, easypay, webhook handler
      - Add rate limit/validity unit constants in payment_order_lifecycle, payment_service
      - Fix critical regression: add PaymentEnabled to GetPublicSettings response
      - Add missing migration 099_fix_migrated_purchase_menu_label_icon.sql
      
      Frontend:
      - Fix StripePopupView.vue: replace `as any` with typed interface, use extractApiErrorMessage
      - Fix AdminOrderTable.vue: replace hardcoded column labels with i18n t() calls
      - Fix SubscriptionsView.vue: replace hardcoded Today/Tomorrow with i18n
      - Extract duplicate statusBadgeClass/canRefund/formatOrderDateTime to orderUtils.ts
      - Add missing i18n keys: common.today, common.tomorrow, payment.orders.orderType/actions
      - Remove dead PurchaseSubscriptionView.vue (replaced by PaymentView)
      e3a000e0
    • erio's avatar
      feat(payment): add complete payment system with multi-provider support · 63d1860d
      erio authored
      Add a full payment and subscription system supporting EasyPay (Alipay/WeChat),
      Stripe, and direct Alipay/WeChat Pay providers with multi-instance load balancing.
      63d1860d