{"record":{"id":"b4f683037d76eca7","repo":"JeffreySu/WeiXinMPSDK","slug":"appid-wxcardapiticketcontainer-register","errorCode":null,"errorMessage":"此appId尚未注册，请先使用WxCardApiTicketContainer.Register完成注册（全局执行一次即可）！","messagePattern":"此appId尚未注册，请先使用WxCardApiTicketContainer\\.Register完成注册（全局执行一次即可）！","errorType":"exception","errorClass":"UnRegisterAppIdException","httpStatus":null,"severity":"error","filePath":"src/Senparc.Weixin.MP/Senparc.Weixin.MP/Containers/WxCardApiTicketContainer.cs","lineNumber":152,"sourceCode":"        /// <param name=\"appId\"></param>\n        /// <param name=\"getNewTicket\">是否强制重新获取新的Ticket</param>\n        /// <returns></returns>\n        public static string GetWxCardApiTicket(string appId, bool getNewTicket = false)\n        {\n            return GetWxCardApiTicketResult(appId, getNewTicket).ticket;\n        }\n\n        /// <summary>\n        /// 获取可用Ticket\n        /// </summary>\n        /// <param name=\"appId\"></param>\n        /// <param name=\"getNewTicket\">是否强制重新获取新的Ticket</param>\n        /// <returns></returns>\n        public static JsApiTicketResult GetWxCardApiTicketResult(string appId, bool getNewTicket = false)\n        {\n            if (!CheckRegistered(appId))\n            {\n                throw new UnRegisterAppIdException(null, \"此appId尚未注册，请先使用WxCardApiTicketContainer.Register完成注册（全局执行一次即可）！\");\n            }\n\n            WxCardApiTicketBag wxCardApiTicketBag = TryGetItem(appId);\n            using (Cache.BeginCacheLock(LockResourceName, appId))//同步锁\n            {\n                wxCardApiTicketBag = TryGetItem(appId);//获锁后重新读取并二次检查过期状态\n                if (getNewTicket || wxCardApiTicketBag.WxCardApiTicketExpireTime <= SystemTime.Now)\n                {\n                    //已过期，重新获取\n                    wxCardApiTicketBag.WxCardApiTicketResult = CommonApi.GetTicket(wxCardApiTicketBag.AppId,\n                                                                                   wxCardApiTicketBag.AppSecret,\n                                                                                   \"wx_card\");\n                    wxCardApiTicketBag.WxCardApiTicketExpireTime = ApiUtility.GetExpireTime(wxCardApiTicketBag.WxCardApiTicketResult.expires_in);\n                    Update(wxCardApiTicketBag, null);\n                }\n            }\n            return wxCardApiTicketBag.WxCardApiTicketResult;\n        }","sourceCodeStart":134,"sourceCodeEnd":170,"githubUrl":"https://github.com/JeffreySu/WeiXinMPSDK/blob/be573f6f94bdbf718dd5f6cdecb137fbc7ff651e/src/Senparc.Weixin.MP/Senparc.Weixin.MP/Containers/WxCardApiTicketContainer.cs#L134-L170","documentation":"WxCardApiTicketContainer caches WeChat card api_tickets per appId, but only for appIds explicitly registered via WxCardApiTicketContainer.Register(appId, ...). GetWxCardApiTicketResult checks CheckRegistered(appId) first and throws UnRegisterAppIdException when the appId was never registered. Registration populates the container's credential/config needed to obtain and cache the ticket.","triggerScenarios":"Calling WxCardApiTicketContainer.GetWxCardApiTicketResult(appId) (or the wrapper GetWxCardApiTicket) with an appId that was never passed to WxCardApiTicketContainer.Register in the current application domain, or calling it before the registration code runs, or after an app restart that lost the registration (e.g. non-persistent container storage).","commonSituations":"Startup code that registers JsApiTicketContainer but forgets WxCardApiTicketContainer (they are separate containers); calling ticket APIs in a background job or new AppDomain where global registration never executed; multi-tenant apps using an appId that was registered under a different process; using GetWxCardApiTicketResultAsync without equivalent registration.","solutions":["Before requesting the ticket, register the appId once at application startup: WxCardApiTicketContainer.Register(appId, appSecret, name/credential config).","Guard the call with WxCardApiTicketContainer.CheckRegistered(appId) and register on demand if it returns false.","Verify the registration code path actually executes in this process (e.g. Application_Start / Program startup), not only in another environment.","If using distributed cache, ensure the registration's credential data is reachable from all nodes, or register on each node."],"exampleFix":"// before\nvar ticket = WxCardApiTicketContainer.GetWxCardApiTicketResult(appId);\n// after\n// Program/App startup, run once:\nWxCardApiTicketContainer.Register(appId, appSecret);\nvar ticket = WxCardApiTicketContainer.GetWxCardApiTicketResult(appId);","handlingStrategy":"validation","validationCode":"if (!WxCardApiTicketContainer.CheckRegistered(appId))\n{\n    WxCardApiTicketContainer.Register(appId, appSecret);\n}","typeGuard":null,"tryCatchPattern":"try\n{\n    var ticket = WxCardApiTicketContainer.GetWxCardApiTicketResult(appId);\n}\ncatch (UnRegisterAppIdException ex)\n{\n    // register the appId and retry once\n    WxCardApiTicketContainer.Register(appId, appSecret);\n    var ticket = WxCardApiTicketContainer.GetWxCardApiTicketResult(appId);\n}","preventionTips":["Register every appId for ALL required containers (AccessToken, JsApiTicket, WxCardApiTicket) in a single startup routine.","Use a central configuration loop that registers each configured appId once at app start.","Write a unit/integration test that calls CheckRegistered for every appId in config before ticket APIs are used.","Document that container registration is per-process and must be repeated after restarts."],"tags":["wechat-sdk","csharp","registration","appid","cache-container"],"backgroundTag":"missing-required-config","analyzedSha":"be573f6f94bdbf718dd5f6cdecb137fbc7ff651e","analyzedAt":"2026-09-12T10:01:50.733Z","contentChangedAt":"2026-09-12T10:01:50.733Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}