From 98f4a8d07aff9f13785b51635d7d16ede0ecccc0 Mon Sep 17 00:00:00 2001 From: qzl Date: Thu, 30 Apr 2026 11:07:57 +0800 Subject: [PATCH] fix: update production app configuration --- .env.example | 14 ++------ apps/README.md | 15 +++++---- apps/assets/legal/en/about_us.md | 2 +- apps/assets/legal/en/privacy_policy.md | 4 +-- apps/assets/legal/en/terms_of_service.md | 2 +- apps/assets/legal/zh/about_us.md | 2 +- apps/assets/legal/zh/privacy_policy.md | 4 +-- apps/assets/legal/zh/terms_of_service.md | 2 +- apps/assets/legal/zh_Hant/about_us.md | 2 +- apps/assets/legal/zh_Hant/privacy_policy.md | 4 +-- apps/assets/legal/zh_Hant/terms_of_service.md | 2 +- apps/lib/core/config/env.dart | 12 ++----- backend/src/core/agentscope/runtime/tasks.py | 32 ++++++++++++------- backend/src/core/config/settings.py | 10 +----- backend/src/v1/agent/dependencies.py | 2 +- backend/src/v1/agent/router.py | 2 +- backend/src/v1/payments/apple_verifier.py | 12 ------- backend/src/v1/payments/service.py | 2 -- .../unit/payments/test_payment_service.py | 28 ++++++++++++++-- docs/protocols/common/http-error-codes.md | 1 - docs/protocols/payments/apple-iap-protocol.md | 10 +++++- 21 files changed, 84 insertions(+), 80 deletions(-) diff --git a/.env.example b/.env.example index 9cdf0f7..24bf2de 100644 --- a/.env.example +++ b/.env.example @@ -91,12 +91,6 @@ ERYAO_LLM__PROVIDER_KEYS__DEEPSEEK= ERYAO_POINTS_POLICY__REGISTER_BONUS_POINTS=60 ERYAO_POINTS_POLICY__REGISTER_BONUS_HMAC_KEY=replace-with-strong-random-key -############ -# 敏感词配置 -############ -ERYAO_SENSITIVE_WORD__USE_ALIYUN=true -ERYAO_SENSITIVE_WORD__FALLBACK_TO_LOCAL=true - ############ # CORS 配置 ############ @@ -112,13 +106,9 @@ ERYAO_TEST__CODE=123456 # Apple IAP 配置 ############ ERYAO_APPLE_IAP__BUNDLE_ID=com.meeyao.qianwen +# Apple IAP 环境识别。auto 表示以后端验签后的 Apple transaction environment 为准。 +ERYAO_APPLE_IAP__ENVIRONMENT=auto # Server API 密钥(可选,用于主动查询交易状态) ERYAO_APPLE_IAP__SERVER_API_KEY_ID= ERYAO_APPLE_IAP__SERVER_API_PRIVATE_KEY= ERYAO_APPLE_IAP__SERVER_API_ISSUER_ID= -# 沙盒测试账号(仅用于手动测试,不用于后端验证) -ERYAO_APPLE_IAP__SANDBOX_TESTER_EMAIL= -ERYAO_APPLE_IAP__SANDBOX_TESTER_PASSWORD= -# Server Notifications V2 URL(在 App Store Connect 中配置) -# 格式: https:///api/v1/payments/apple/notifications -ERYAO_APPLE_IAP__SERVER_NOTIFICATIONS_URL= diff --git a/apps/README.md b/apps/README.md index a032a7f..fbbd671 100644 --- a/apps/README.md +++ b/apps/README.md @@ -2,9 +2,15 @@ Flutter client for `觅爻签问`. -## Debug startup with backend injection +## Backend URL -This app supports injecting backend URL at startup (same pattern as social-app): +Default backend URL: + +```text +https://api.meeyao.com +``` + +This app also supports injecting backend URL at startup (same pattern as social-app): - Dart read path: `lib/core/config/env.dart` - Injection key: `BACKEND_URL` @@ -21,7 +27,4 @@ flutter run --dart-define=BACKEND_URL=http://192.168.1.100:5775 ./tool/run-dev.sh --backend-url http://192.168.1.100:5775 ``` -If `BACKEND_URL` is not provided, fallback is: - -- Android emulator: `http://10.0.2.2:5775` -- Others: `http://localhost:5775` +If `BACKEND_URL` is not provided, the app uses the production backend URL above. diff --git a/apps/assets/legal/en/about_us.md b/apps/assets/legal/en/about_us.md index da7bf64..af65574 100644 --- a/apps/assets/legal/en/about_us.md +++ b/apps/assets/legal/en/about_us.md @@ -12,7 +12,7 @@ MeeYao Divination is designed based on traditional oriental culture. Our core go **Developer:** Ann Lee -**Contact Email:** ann@xumee.com +**Contact Email:** ann@xunmee.com --- diff --git a/apps/assets/legal/en/privacy_policy.md b/apps/assets/legal/en/privacy_policy.md index bf93fb2..7d0a817 100644 --- a/apps/assets/legal/en/privacy_policy.md +++ b/apps/assets/legal/en/privacy_policy.md @@ -116,7 +116,7 @@ In accordance with CCPA/CPRA and U.S. local privacy laws, you enjoy the followin You can submit data requests through the only dedicated contact method: -- **Contact Email**: ann@xumee.com +- **Contact Email**: ann@xunmee.com I will respond to your legitimate request within 45 days, and properly verify your identity to ensure data security before processing. @@ -152,7 +152,7 @@ This Privacy Policy may be updated irregularly to adapt to platform rules and le If you have any questions, suggestions or privacy-related complaints about this Privacy Policy, please contact me: -**Developer Email**: ann@xumee.com +**Developer Email**: ann@xunmee.com If you are a California resident and dissatisfied with the processing result, you can consult the local privacy regulatory authority. diff --git a/apps/assets/legal/en/terms_of_service.md b/apps/assets/legal/en/terms_of_service.md index e18bf46..634c97b 100644 --- a/apps/assets/legal/en/terms_of_service.md +++ b/apps/assets/legal/en/terms_of_service.md @@ -118,4 +118,4 @@ I reserve the right to revise and update these Terms of Service at any time. Mat If you have questions, feedback or legal inquiries about these Terms, please contact: - **Developer**: Individual Independent Developer -- **Contact Email**: ann@xumee.com +- **Contact Email**: ann@xunmee.com diff --git a/apps/assets/legal/zh/about_us.md b/apps/assets/legal/zh/about_us.md index f8c1d46..01b3acf 100644 --- a/apps/assets/legal/zh/about_us.md +++ b/apps/assets/legal/zh/about_us.md @@ -12,7 +12,7 @@ **开发者**:Ann Lee -**联系邮箱**:ann@xumee.com +**联系邮箱**:ann@xunmee.com --- diff --git a/apps/assets/legal/zh/privacy_policy.md b/apps/assets/legal/zh/privacy_policy.md index 500a059..9094c48 100644 --- a/apps/assets/legal/zh/privacy_policy.md +++ b/apps/assets/legal/zh/privacy_policy.md @@ -116,7 +116,7 @@ 您可以通过唯一指定联系方式提交数据请求: -- **联系邮箱**:ann@xumee.com +- **联系邮箱**:ann@xunmee.com 我将在 45 天内回复您的合法请求,并在处理前妥善验证您的身份以确保数据安全。 @@ -152,7 +152,7 @@ 如果您对本隐私政策有任何疑问、建议或隐私相关投诉,请联系我: -**开发者邮箱**:ann@xumee.com +**开发者邮箱**:ann@xunmee.com 如果您是加州居民且对处理结果不满意,可咨询当地隐私监管机构。 diff --git a/apps/assets/legal/zh/terms_of_service.md b/apps/assets/legal/zh/terms_of_service.md index d503c04..696bd11 100644 --- a/apps/assets/legal/zh/terms_of_service.md +++ b/apps/assets/legal/zh/terms_of_service.md @@ -118,4 +118,4 @@ 如果您对本条款有疑问、反馈或法律咨询,请联系: - **开发者**:独立个人开发者 -- **联系邮箱**:ann@xumee.com +- **联系邮箱**:ann@xunmee.com diff --git a/apps/assets/legal/zh_Hant/about_us.md b/apps/assets/legal/zh_Hant/about_us.md index 15e8453..5b36c43 100644 --- a/apps/assets/legal/zh_Hant/about_us.md +++ b/apps/assets/legal/zh_Hant/about_us.md @@ -12,7 +12,7 @@ **開發者**:Ann Lee -**聯繫郵箱**:ann@xumee.com +**聯繫郵箱**:ann@xunmee.com --- diff --git a/apps/assets/legal/zh_Hant/privacy_policy.md b/apps/assets/legal/zh_Hant/privacy_policy.md index 33ca2d0..92e15fa 100644 --- a/apps/assets/legal/zh_Hant/privacy_policy.md +++ b/apps/assets/legal/zh_Hant/privacy_policy.md @@ -116,7 +116,7 @@ 您可以通過唯一指定聯繫方式提交數據請求: -- **聯繫郵箱**:ann@xumee.com +- **聯繫郵箱**:ann@xunmee.com 我將在 45 天內回覆您的合法請求,並在處理前妥善驗證您的身份以確保數據安全。 @@ -152,7 +152,7 @@ 如果您對本隱私政策有任何疑問、建議或隱私相關投訴,請聯繫我: -**開發者郵箱**:ann@xumee.com +**開發者郵箱**:ann@xunmee.com 如果您是加州居民且對處理結果不滿意,可諮詢當地隱私監管機構。 diff --git a/apps/assets/legal/zh_Hant/terms_of_service.md b/apps/assets/legal/zh_Hant/terms_of_service.md index 4b3a20d..3cd3393 100644 --- a/apps/assets/legal/zh_Hant/terms_of_service.md +++ b/apps/assets/legal/zh_Hant/terms_of_service.md @@ -118,4 +118,4 @@ 如果您對本條款有疑問、反饋或法律諮詢,請聯繫: - **開發者**:獨立個人開發者 -- **聯繫郵箱**:ann@xumee.com +- **聯繫郵箱**:ann@xunmee.com diff --git a/apps/lib/core/config/env.dart b/apps/lib/core/config/env.dart index 27c0d9c..c9a7b8d 100644 --- a/apps/lib/core/config/env.dart +++ b/apps/lib/core/config/env.dart @@ -1,19 +1,13 @@ -import 'dart:io'; - class Env { + static const _productionBackendUrl = 'https://api.meeyao.com'; + static String get backendUrl { final injected = const String.fromEnvironment('BACKEND_URL'); if (injected.isNotEmpty && injected != 'false') { return injected; } - if (Platform.isAndroid) { - return 'http://10.0.2.2:5775'; - } - if (Platform.isIOS) { - return 'http://192.168.1.63:5775'; - } - return 'http://localhost:5775'; + return _productionBackendUrl; } static Future init() async {} diff --git a/backend/src/core/agentscope/runtime/tasks.py b/backend/src/core/agentscope/runtime/tasks.py index e398b61..d47051b 100644 --- a/backend/src/core/agentscope/runtime/tasks.py +++ b/backend/src/core/agentscope/runtime/tasks.py @@ -32,6 +32,22 @@ _MAX_CONTEXT_ATTACHMENTS = 3 _RUNTIME_AGENT_OUTPUT_ADAPTER = TypeAdapter(RuntimeAgentOutput) +def create_context_messages_cache() -> Any: + from core.agentscope.caches.context_messages_cache import ( + create_context_messages_cache as factory, + ) + + return factory() + + +def create_attachment_content_cache() -> Any: + from core.agentscope.caches.attachment_content_cache import ( + create_attachment_content_cache as factory, + ) + + return factory() + + def _serialize_tool_agent_output( *, metadata: AgentChatMessageMetadata | None, @@ -203,12 +219,6 @@ async def _build_recent_context_messages( context_config: "MessageContextConfig", ) -> list[Msg]: from agentscope.message import Msg - from core.agentscope.caches.attachment_content_cache import ( - create_attachment_content_cache, - ) - from core.agentscope.caches.context_messages_cache import ( - create_context_messages_cache, - ) from core.agentscope.services.context_service import AgentContextService from services.base.supabase import supabase_service from v1.agent.repository import AgentRepository @@ -348,12 +358,10 @@ async def _build_recent_context_messages( async def run_agentscope_task(command: dict[str, Any]) -> dict[str, object]: - from core.agentscope.events import ( - AgentScopeAgUiCodec, - AgentScopeEventPipeline, - RedisStreamBus, - SqlAlchemyEventStore, - ) + from core.agentscope.events.agui_codec import AgentScopeAgUiCodec + from core.agentscope.events.pipeline import AgentScopeEventPipeline + from core.agentscope.events.redis_bus import RedisStreamBus + from core.agentscope.events.store import SqlAlchemyEventStore from core.config.settings import config from core.db.session import AsyncSessionLocal from services.base.redis import get_or_init_redis_client diff --git a/backend/src/core/config/settings.py b/backend/src/core/config/settings.py index 7c945c5..ed237fb 100644 --- a/backend/src/core/config/settings.py +++ b/backend/src/core/config/settings.py @@ -183,11 +183,6 @@ class DatabaseSettings(BaseModel): ) -class SensitiveWordSettings(BaseModel): - use_aliyun: bool = True - fallback_to_local: bool = True - - class TestSettings(BaseModel): email: str = "" code: str = "" @@ -232,12 +227,10 @@ class AppleIapSettings(BaseModel): bundle_id: str = Field(default="com.meeyao.qianwen", min_length=1) root_cert_url: str = "https://www.apple.com/certificateauthority/AppleIncRootCertificate.cer" jws_x5c_cert_url: str = "https://api.storekit.itunes.apple.com/v1/verificationKeys" + environment: Literal["auto", "sandbox", "production"] = "auto" server_api_issuer_id: str | None = None server_api_key_id: str | None = None server_api_private_key: SecretStr | None = None - sandbox_tester_email: str | None = None - sandbox_tester_password: SecretStr | None = None - server_notifications_url: str | None = None def _resolve_env_files() -> list[str]: @@ -283,7 +276,6 @@ class Settings(BaseSettings): storage: StorageSettings = StorageSettings() llm: LlmSettings = LlmSettings() database: DatabaseSettings = DatabaseSettings() - sensitive_word: SensitiveWordSettings = Field(default_factory=SensitiveWordSettings) test: TestSettings = Field(default_factory=TestSettings) taskiq: TaskiqSettings = Field(default_factory=TaskiqSettings) agent_runtime: AgentRuntimeSettings = Field(default_factory=AgentRuntimeSettings) diff --git a/backend/src/v1/agent/dependencies.py b/backend/src/v1/agent/dependencies.py index 693074c..7fd516f 100644 --- a/backend/src/v1/agent/dependencies.py +++ b/backend/src/v1/agent/dependencies.py @@ -9,7 +9,7 @@ from fastapi import Depends from redis.asyncio import Redis from sqlalchemy.ext.asyncio import AsyncSession -from core.agentscope.events import RedisStreamBus +from core.agentscope.events.redis_bus import RedisStreamBus from core.agentscope.tools.tool_result_storage import ( create_tool_result_storage, ) diff --git a/backend/src/v1/agent/router.py b/backend/src/v1/agent/router.py index f5a4b2a..e6cd7aa 100644 --- a/backend/src/v1/agent/router.py +++ b/backend/src/v1/agent/router.py @@ -9,7 +9,7 @@ from typing import Annotated from ag_ui.core import RunAgentInput from core.http.errors import ApiProblemError, problem_payload -from core.agentscope.events import to_sse_event +from core.agentscope.events.sse import to_sse_event from core.agentscope.schemas.agui_input import ( parse_run_input, validate_run_request_messages_contract, diff --git a/backend/src/v1/payments/apple_verifier.py b/backend/src/v1/payments/apple_verifier.py index b7ffca0..1858705 100644 --- a/backend/src/v1/payments/apple_verifier.py +++ b/backend/src/v1/payments/apple_verifier.py @@ -47,7 +47,6 @@ class AppleJwsVerifier: *, expected_bundle_id: str, expected_product_id: str, - expected_environment: str, ) -> VerifiedTransaction | VerificationError: try: unverified_header = jwt.get_unverified_header(signed_transaction_info) @@ -148,17 +147,6 @@ class AppleJwsVerifier: detail=f"Invalid environment: {environment}", ) - if environment != expected_environment: - logger.error( - "Environment mismatch: expected=%s got=%s", - expected_environment, - environment, - ) - return VerificationError( - code="PAYMENT_ENVIRONMENT_MISMATCH", - detail=f"Environment mismatch: expected={expected_environment} got={environment}", - ) - revocation_date_raw = payload.get("revocationDate") revocation_date: int | None = ( int(revocation_date_raw) if revocation_date_raw is not None else None diff --git a/backend/src/v1/payments/service.py b/backend/src/v1/payments/service.py index 2a48ff4..ec8a99e 100644 --- a/backend/src/v1/payments/service.py +++ b/backend/src/v1/payments/service.py @@ -114,12 +114,10 @@ class PaymentService: ) expected_bundle_id = config.apple_iap.bundle_id - expected_environment = "Sandbox" if config.runtime.environment != "prod" else "Production" result = self._verifier.verify_signed_transaction( request.signed_transaction_info, expected_bundle_id=expected_bundle_id, expected_product_id=product_mapping.app_store_product_id, - expected_environment=expected_environment, ) if isinstance(result, VerificationError): diff --git a/backend/tests/unit/payments/test_payment_service.py b/backend/tests/unit/payments/test_payment_service.py index a25c8fa..d2488e4 100644 --- a/backend/tests/unit/payments/test_payment_service.py +++ b/backend/tests/unit/payments/test_payment_service.py @@ -82,10 +82,8 @@ class _FakeVerifier: *, expected_bundle_id: str, expected_product_id: str, - expected_environment: str, ) -> VerifiedTransaction | VerificationError: del signed_transaction_info, expected_bundle_id, expected_product_id - del expected_environment return self._result @@ -290,6 +288,32 @@ class TestPaymentServiceSuccessfulGrant: assert len(points_repo.appended_ledger) == 1 assert len(payment_repo.inserted_transactions) == 1 + @pytest.mark.asyncio + async def test_grants_production_transaction_from_verified_payload(self) -> None: + payment_repo = _FakePaymentRepository() + service = PaymentService( + payment_repo=payment_repo, + points_repo=_FakePointsRepository(), + verifier=_FakeVerifier( + result=_make_verified_transaction(environment="Production") + ), + ) + request = VerifyTransactionRequest( + productCode="starter_pack", + appStoreProductId="com.meeyao.qianwen.starter_pack", + transactionId="2000000123456789", + signedTransactionInfo="fake_jws", + ) + + result = await service.verify_and_grant( + user_id=uuid4(), + user_email="test@example.com", + request=request, + ) + + assert result.status == "granted" + assert payment_repo.inserted_transactions[0].environment == "Production" + class TestPaymentServiceStarterPackIneligible: @pytest.mark.asyncio diff --git a/docs/protocols/common/http-error-codes.md b/docs/protocols/common/http-error-codes.md index 1a8bae7..a276165 100644 --- a/docs/protocols/common/http-error-codes.md +++ b/docs/protocols/common/http-error-codes.md @@ -108,7 +108,6 @@ This document is the source of truth for backend RFC7807 `code` values consumed |---|---:|---|---| | `PAYMENT_PRODUCT_NOT_FOUND` | 404 | `productCode` does not exist or is not enabled | Refresh packages and show product-unavailable message | | `PAYMENT_PRODUCT_MISMATCH` | 422 | Client product ID does not match backend/Apple verification result | Block grant and prompt retry | -| `PAYMENT_ENVIRONMENT_MISMATCH` | 422 | Transaction environment (Sandbox/Production) does not match server environment | Show purchase-verification-failed message | | `PAYMENT_TRANSACTION_INVALID` | 422 | Apple signed transaction invalid, signature verification failed, or payload malformed | Show purchase-verification-failed message | | `PAYMENT_TRANSACTION_REVOKED` | 409 | Transaction has been revoked or refunded, grant not allowed | Show purchase-unavailable message | | `PAYMENT_TRANSACTION_CONFLICT` | 409 | Transaction already processed by another user or in conflicting state | Prompt to contact support or refresh balance | diff --git a/docs/protocols/payments/apple-iap-protocol.md b/docs/protocols/payments/apple-iap-protocol.md index ee488d2..b847326 100644 --- a/docs/protocols/payments/apple-iap-protocol.md +++ b/docs/protocols/payments/apple-iap-protocol.md @@ -14,6 +14,7 @@ Protocol verification status: - Current strategy: additive evolution (`backward-compatible`). - Breaking change requires explicit migration + rollback notes (`requires-migration`). +- Apple IAP environment is auto-detected from verified Apple transaction payloads; the verify API is not split by Sandbox/Production. ## Route overview @@ -70,7 +71,6 @@ Verify and grant credits for an Apple IAP transaction. |---|---:|---| | `PAYMENT_PRODUCT_NOT_FOUND` | 404 | `productCode` does not exist or is not enabled | | `PAYMENT_PRODUCT_MISMATCH` | 422 | Client product ID does not match backend/Apple verification result | -| `PAYMENT_ENVIRONMENT_MISMATCH` | 422 | Transaction environment (Sandbox/Production) does not match server environment | | `PAYMENT_TRANSACTION_INVALID` | 422 | Apple signed transaction invalid, signature verification failed, or payload malformed | | `PAYMENT_TRANSACTION_REVOKED` | 409 | Transaction has been revoked or refunded, grant not allowed | | `PAYMENT_TRANSACTION_CONFLICT` | 409 | Transaction already processed by another user or in conflicting state | @@ -119,6 +119,14 @@ product_mappings: - Backend tracks purchase via `register_bonus_claims.has_purchased_starter_pack` - If already purchased, returns `PAYMENT_STARTER_PACK_INELIGIBLE` (409) +## Environment handling + +- `signedTransactionInfo` is verified server-side and its Apple-provided `environment` field is the source of truth. +- Valid environments are `Sandbox` and `Production`. +- TestFlight and App Review Sandbox transactions are accepted when Apple signs them as `Sandbox`. +- App Store Production transactions are accepted when Apple signs them as `Production`. +- Backend configuration defaults to `apple_iap.environment=auto`; it must not force all verifications into one Apple environment. + ## Ledger integration - Successful purchases create a ledger entry with: