Order query:
- 彩虹易支付 and ZPay read act=order parameters from the URL query only;
a body-only request is answered with "No Act!", so order status queries
(reconcile, payment status checks) never reached the order. Parameters
are now sent in the URL query and mirrored in the form body for forks
that read the body.
- Decode status/money/trade_no leniently: PHP deployments commonly return
integers as strings ("status":"1"), which previously failed the whole
response decode. JSON null falls back to the nested data object.
- Treat a non-success code as a query error instead of an unpaid order,
and require an echoed out_trade_no to match the queried order.
- Strip the request URL (which carries the merchant key) from transport
errors before they are returned.
Notifications:
- Add payment.NotificationConfirmer. After a notification is verified, the
webhook asks the same provider instance to confirm it; EasyPay queries
the upstream order and requires it to be paid with a matching amount and
trade_no (a missing notify trade_no is filled from the query). Failures
are recorded as PAYMENT_UPSTREAM_UNCONFIRMED and answered with 502 so the
gateway retries; the order stays pending and reconcile still applies.
- EasyPay notifications must carry the instance's configured pid, and
field values other than name must be plain tokens.
- Return URLs must use a plain hostname or IP literal without userinfo.
- The SDK returns signed business-failure responses (for example
ACQ.SYSTEM_ERROR) with a nil error and empty fields. QueryRefund now
checks the response code and returns an error, so the order stays
REFUND_PENDING instead of being read as "no refund record" and failed.
- A missing refund record is treated as failed only after a 10 minute
grace period from when the refund became pending. Alipay notes that a
query sent too soon can miss a refund that later succeeds. The pending
time comes from the REFUND_PENDING audit entry.
- Alipay: record the generated out_request_no as the refund ID and
implement QueryRefund (alipay.trade.fastpay.refund.query), so refunds
that return fund_change=N can be finalized from the admin refund query.
Refunds recorded before this change return REFUND_QUERY_UNSUPPORTED.
- Fulfillment: mark paid orders with an unsupported order type as FAILED
with an audit entry instead of erroring, so payment callbacks are
acknowledged; RetryFulfillment rejects them with INVALID_ORDER_TYPE.
- Claim REFUND_PENDING -> REFUND_FAILED, return the held deduction and
write the audit in one transaction. If the deduction cannot be
returned, the order is still marked failed together with a
REFUND_ROLLBACK_FAILED audit so a retried refund skips the deduction.
- Return an error when the REFUND_PENDING audit cannot be loaded or
parsed instead of falling back to the legacy finalization path.
The /payment/stripe route no longer carries client_secret in its query.
The page reads it from the recovery snapshot written when the order was
created and requires the snapshot order to match order_id, consistent
with AirwallexPaymentView.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Validate order_type (balance/subscription) at order creation and in
fulfillment; other values are rejected with INVALID_ORDER_TYPE.
- Keep the balance/subscription deduction while a gateway refund is
pending. The deduction is recorded in the REFUND_PENDING audit (written
in the same transaction as the status change), not applied again on
successful finalization, and returned once if the gateway reports
failure. Legacy pending audits keep the previous behaviour.
- REFUND_PENDING orders are finalized via refund query only;
PrepareRefund/ExecuteRefund no longer accept them.
- Invalidate the billing balance cache after refund deductions/returns.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Revert 679a7186f ("feat(accounts): compact account search and filters"),
which moved the type / privacy / group filters behind a collapsed
"More filters" panel on /admin/accounts.
Polish on top of the revert:
- Widen the privacy select (w-48) so "全部Privacy状态" / "All Privacy States"
no longer truncates.
- Narrow the search input to w-52 so all six controls fit on one row at 1440px.
- Below sm, lay the filters out as a two-column grid that fills the width
instead of fixed-width selects with a ragged edge.
- Restore runtime default passthroughs and Gemini 3.1 Pro aliases for
explicit Antigravity model_mapping. Mappings saved before the Gemini
3.x Flash / 3.1 Pro releases still depend on them; dropping them would
silently remove those models from existing accounts on upgrade.
- Capability sync now skips injected-only passthrough targets, so models
the upstream catalog omits no longer mark the sync as partial.
- Price Antigravity claude-{opus,sonnet}-5-5-{high,medium,low} at the base
Opus/Sonnet 5.5 card instead of substring-matching claude-opus-5 /
claude-sonnet-4-5 (1.25x-3x overbilling).
Resolve Gemini 4 Argon pricing conflicts in favor of main (#8078), which
already provides the base and Cyber Permissive price cards, thinking-tier
pricing aliases, and the local pricing snapshot.
Keep OpenAI non-API-key accounts on native search before channel inheritance. Cover 72 combinations of account types, modes, channel flags and global enablement in the shared interception gate.
Validation: the regression test fails in six OAuth/setup-token channel-enabled cases on the original code and passes with this guard. Related WebSearch unit tests pass. The broad service/handler test run was blocked by automatic approval review due to an unrelated daily-cloudcode-pa.googleapis.com request with unverified payload/credential use.
Chat Completions conversion drops Anthropic web_search tools, so Claude
Code's WebSearch on OpenAI-platform groups (e.g. DeepSeek upstreams)
got no web_search_tool_result and reported 0 results. Run the existing
search emulation in ForwardAsAnthropic and let OpenAI API Key accounts
opt in via extra.web_search_emulation.