基于CardPointe处理Google Pay数据时解密失败问题排查
CardConnect集成Google Pay生产环境解密失败(Errorcode 24)
我在网站上使用CardConnect网关集成Google Pay,JavaScript端Google Pay API已实现且运行正常,同时开发了将Google Pay payload数据解密并在CardPointe中进行令牌化的代码(data变量为Google Pay返回的payload)。开发遵循CardPointe官方指南,且网站已通过Google审核。
该功能在Google Pay测试模式下可正常运行,但切换至生产模式时,调用接口返回InternalServerError,错误信息为:
{"message":"Decryption failure","errorcode":24}
令牌化实现代码
public async Task<string> TokenizeGooglePayDataCardPointe(string data) { Admins adminBRs = new Admins(_context); string uri = adminBRs.GetPageSetting("Donations", "CardPointeURL"); if (!uri.StartsWith("http")) uri = "https://" + uri; if (!uri.EndsWith("/")) uri += "/"; string uri2 = uri + "cardsecure/api/v1/ccn/tokenize"; dynamic obj = new System.Dynamic.ExpandoObject(); obj.devicedata = data; obj.encryptionhandler = "EC_GOOGLE_PAY"; string content = JsonConvert.SerializeObject(obj); var contentString = new StringContent(content, System.Text.Encoding.UTF8, "application/json"); ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls13 | SecurityProtocolType.Tls12; try { using (HttpClient client = new HttpClient()) { // Set request headers. client.DefaultRequestHeaders.Accept.Clear(); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); HttpResponseMessage response = await client.PostAsync(uri2, contentString); if (response != null) { if (response.IsSuccessStatusCode) { if (response.Content != null) { string jsonString = response.Content.ReadAsStringAsync().Result; JObject results = JsonConvert.DeserializeObject<JObject>(jsonString); JToken errorCode = results.GetValue("errorcode"); JToken message = results.GetValue("message"); if (errorCode.ToString() == "0") { JToken token = results.GetValue("token"); return token.ToString(); } else { return "Error: " + errorCode.ToString() + " " + message.ToString(); } } else { LogInfo.LogText(_context, "Google Pay tokenization error: no reponse content"); return "Error: No content returned"; } } else { if (response.StatusCode == HttpStatusCode.RequestTimeout) { LogInfo.LogText(_context, "Google Pay tokenization error: timeout"); return "Error: timeout"; } else { string jsonString = ""; if (response.Content != null) jsonString = response.Content.ReadAsStringAsync().Result; LogInfo.LogText(_context, "Google Pay tokenization error: " + response.StatusCode.ToString() + " " + response.ReasonPhrase + " " + jsonString + " " + data); return "Error: status " + response.StatusCode.ToString(); } } } else { return "Error: no response"; } } } catch (Exception ex) { LogInfo.LogError(_context, ex); return "Error"; } }
测试模式payload示例
{"signature":"MEYCIQCxG3oEWSF2NhoONkB6O7h3sbgHBQ9cQZDI9a1zkaL5PwIhAJY7g5ekerICbmzcD9ocvbsrSC7pup5zLUkBjQmE4jhD","intermediateSigningKey":{"signedKey":"{\"keyValue\":\"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEaYdF4BdKTIsIKDczrxSvOOU6sF6DTxkJCv5RZX140mswhcpFwfxz2GlfFRcKyOFYBCn/tFk+fOBUVxrnIIchIQ\\u003d\\u003d\",\"keyExpiration\":\"1718470102231\"}","signatures":["MEQCIELE+SRM6YsF7hL2YHEq7lkN4iM13kPGG3Gxyf3ubJvhAiBJhPFJBtywGWJPWI0O465P7QUnSB7P0zl+wtQtVYE4fQ\u003d\u003d"]},"protocolVersion":"ECv2","signedMessage":"{\"encryptedMessage\":\"k67b8MkJ/xyd2xvzwQ4aJllYcBp8Zm6DRqXanyN113kEzzUhgwrxZCAidWaWJZhUQiFm/OfrwLP1rW/UzFoHULU0tO5GQDRAQDK40A00tTzeoKZGpKeXo4uaHXUcJvpbVHTVbqL5BwR3v9O4OvyRD7/X6J1pv60RzTsxpd0+sGEnNziDVGEB2lK4y1MPxWN3NF/6IbG5XidK5ZIVv8l5D9QafZoDyARYQnup7i7jDOX2yRO5H/NJYPr2r71Cu50jmHiIyl9LCLhazBqKtlxqd0APYbrjBqEeyuaaq8USSCPJTDEoZ1bljjMiyd4/xBJY5pOqH2KFyspsb3MYZK3zWkmOrk+ui85DC7G/Hf28ZvNZjy1eq7MVPhLFGGczARH2IP8cQRZN/wBbPutK2MbHZW7LFZQ/dWA90LJY11WbniX9FZSukuQurWIS95FpLD1k6bgoMkhK5P713w\\u003d\\u003d\",\"ephemeralPublicKey\":\"BC4OLFg8aE9uZk2H2mlzu1a+aavaq2ZtEqOpiNaGWFv170lm0RfeffIYquzYw3Ybkp5WOTa/NIaUik122BuFegM\\u003d\",\"tag\":\"RY65Wy6zyDSj9zIpGfvzlSgzClfsvNqFALvcqsGhzYo\\u003d\"}"}
生产模式payload示例
{"signature":"MEQCIAo9FwgaZ8/vYsV2HhTzx3r3YALGnguYp3qLJwMUYd/pAiBTdDDrWWq4WmLCkbQBWB71vvtPpdwUXw/zNv527WfylQ\u003d\u003d","intermediateSigningKey":{"signedKey":"{\"keyValue\":\"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEqaR+yoImVfXIW+CRXutpeiGFmiC0FLtnKURmYF0LIbw3SEIQLe8sKwBu7Wa2VEjLTMVEMXnnSXDoy4EBfwOSTg\\u003d\\u003d\",\"keyExpiration\":\"1718469250000\"}","signatures":["MEUCIFSNA9M0hkBvdNmw7tWb9vb8vHeyoeWku2rwjtgh9j8OAiEA/cmTXhrqnAmr9inO9C6tsfjqrBpqvvIC2MVpRsaYI00\u003d"]},"protocolVersion":"ECv2","signedMessage":"{\"encryptedMessage\":\"XKUiZp1NtZAgEfJLx5IjdFAArvd4jXz62lF0ltmICvcdGk8F2T5zibO22tXFa7pZw6yfcDh43S+bVmVll9MpCdrg8Yk9T/nGIxqBwZoDLr9E53LJSs3s8mVz1skhWtOEd/Z89eE/GRml1qkqpiRpgYuohdGaC0Ea2EIE3dw8RIhHrevlwSFp1gRnmsL36SVboV7unyv95T/jQPVdwidm5rnjN2eZKOSAhPT71e5gNNMxuwZDD27ifq1WCpLPX02BIJzvpRHB6tGlB04/KPg758eqvBfL1VQuCasjDZ7LPlkO9eb3XFbLxMoxSnMOErXADvNOJhYZuaqJ9sKq7jpGbeUZjEoXd9Rw9C6EdRUhDQwb0ZTtkcoFVU+cgC+Xe2zD5KVQjaHgT9mX/zklHlosBX029LbOsbzhafLwn1lJH2PtGEbv3QkpVfRfXd/bYwv1mmV/NJ8cIo9K\",\"ephemeralPublicKey\":\"BK4KiIMRRVGwkKrbyHsFFzsFB09eyelyQKxp6Tqr3/68UXXpJ/I5Ub75O1BbvZvKwawndHSHaCguQaM83COAYxg\\u003d\",\"tag\":\"cWo2XNrVH98EVNdNLnZniWXKU2CAcImRKgHHdviQVwU\\u003d\"}"}
排查思路
- 检查devicedata序列化逻辑:当前代码直接将
data字符串赋值给obj.devicedata后序列化,可能导致双重序列化问题(把JSON字符串转义为普通字符串)。CardPointe接口可能期望devicedata是JSON对象而非字符串,可尝试先反序列化data再赋值:var deviceDataObj = JsonConvert.DeserializeObject(data); obj.devicedata = deviceDataObj; - 验证生产环境CardPointe端点:确认生产环境的
CardPointeURL指向正确的生产域名,测试环境和生产环境的端点通常不同,避免调用测试端点导致密钥不匹配。 - 检查加密密钥配置:生产环境必须使用CardConnect提供的生产专用加密密钥,测试密钥无法解密生产环境的Google Pay payload,确认后台配置的密钥与生产环境绑定。
- 对比测试/生产请求体差异:打印并对比两种模式下最终发送的
content字符串,检查devicedata的格式、转义字符是否符合接口要求,可通过日志记录完整请求体。 - 确认Google Pay生产配置:验证Google Pay生产环境是否绑定了正确的CardConnect商户ID,支付网关配置是否完整,避免测试到生产的配置遗漏。
- 测试TLS版本兼容性:尝试仅保留
SecurityProtocolType.Tls12,部分生产端点对TLS1.3支持可能存在问题。
内容的提问来源于stack exchange,提问作者nuander
相关产品推荐
相关产品推荐

