diff --git a/server-rs/crates/platform-auth/src/lib.rs b/server-rs/crates/platform-auth/src/lib.rs index 03898a03a..c20ba0ee4 100644 --- a/server-rs/crates/platform-auth/src/lib.rs +++ b/server-rs/crates/platform-auth/src/lib.rs @@ -362,17 +362,45 @@ struct WechatPhoneNumberResponse { phone_info: Option, } +// 微信成功响应按官方文档必须包含这三个字段: +// https://developers.weixin.qq.com/miniprogram/dev/server/API/user-info/phone-number/api_getphonenumber.html#Res-phone-info-Object-Payload #[derive(Debug, Deserialize)] struct WechatPhoneNumberInfo { - #[serde(default)] - #[serde(alias = "phoneNumber")] - phone_number: Option, - #[serde(default)] - #[serde(alias = "purePhoneNumber")] - pure_phone_number: Option, - #[serde(default)] - #[serde(alias = "countryCode")] - country_code: Option, + #[serde(rename = "phoneNumber")] + phone_number: String, + #[serde(rename = "purePhoneNumber")] + pure_phone_number: String, + #[serde(rename = "countryCode")] + country_code: String, +} + +fn normalize_wechat_phone_number_info( + phone_info: WechatPhoneNumberInfo, +) -> Result { + let pure_phone_number = phone_info.pure_phone_number.trim(); + if pure_phone_number.is_empty() { + return Err(WechatProviderError::MissingProfile( + "微信手机号授权失败:缺少纯手机号".to_string(), + )); + } + let country_code = phone_info.country_code.trim(); + if country_code.is_empty() { + return Err(WechatProviderError::MissingProfile( + "微信手机号授权失败:缺少国家码".to_string(), + )); + } + let phone_number = phone_info.phone_number.trim(); + if phone_number.is_empty() { + return Err(WechatProviderError::MissingProfile( + "微信手机号授权失败:缺少完整手机号".to_string(), + )); + } + + Ok(WechatPhoneNumberProfile { + phone_number: phone_number.to_string(), + pure_phone_number: Some(pure_phone_number.to_string()), + country_code: Some(country_code.to_string()), + }) } #[derive(Debug, Deserialize)] @@ -1118,20 +1146,7 @@ impl RealWechatProvider { let phone_info = payload.phone_info.ok_or_else(|| { WechatProviderError::MissingProfile("微信手机号授权失败:缺少手机号信息".to_string()) })?; - let phone_number = phone_info - .pure_phone_number - .clone() - .or(phone_info.phone_number.clone()) - .filter(|value| !value.trim().is_empty()) - .ok_or_else(|| { - WechatProviderError::MissingProfile("微信手机号授权失败:缺少手机号".to_string()) - })?; - - Ok(WechatPhoneNumberProfile { - phone_number, - pure_phone_number: phone_info.pure_phone_number, - country_code: phone_info.country_code, - }) + normalize_wechat_phone_number_info(phone_info) } async fn request_mini_program_access_token( @@ -2074,7 +2089,7 @@ mod tests { "errcode": 0, "errmsg": "ok", "phone_info": { - "phoneNumber": "+8613800138000", + "phoneNumber": "13800138000", "purePhoneNumber": "13800138000", "countryCode": "86" } @@ -2083,9 +2098,55 @@ mod tests { .expect("wechat phone number response should parse"); let phone_info = payload.phone_info.expect("phone info should exist"); - assert_eq!(phone_info.phone_number.as_deref(), Some("+8613800138000")); - assert_eq!(phone_info.pure_phone_number.as_deref(), Some("13800138000")); - assert_eq!(phone_info.country_code.as_deref(), Some("86")); + assert_eq!(phone_info.phone_number, "13800138000"); + assert_eq!(phone_info.pure_phone_number, "13800138000"); + assert_eq!(phone_info.country_code, "86"); + let profile = normalize_wechat_phone_number_info(phone_info) + .expect("wechat phone profile should normalize"); + assert_eq!(profile.phone_number, "13800138000"); + assert_eq!(profile.pure_phone_number.as_deref(), Some("13800138000")); + assert_eq!(profile.country_code.as_deref(), Some("86")); + } + + #[test] + fn wechat_phone_number_success_response_requires_country_code() { + let error = serde_json::from_str::( + r#"{ + "errcode": 0, + "phone_info": { + "phoneNumber": "+8613800138000", + "purePhoneNumber": "13800138000" + } + }"#, + ) + .expect_err("missing provider country code should fail deserialization"); + + assert!(error.to_string().contains("countryCode")); + } + + #[test] + fn wechat_phone_number_error_response_may_omit_phone_info() { + let payload = serde_json::from_str::( + r#"{ + "errcode": 40029, + "errmsg": "invalid code" + }"#, + ) + .expect("wechat error response should remain parseable"); + + assert!(payload.phone_info.is_none()); + } + + #[test] + fn wechat_phone_number_profile_requires_non_empty_phone_number() { + let error = normalize_wechat_phone_number_info(WechatPhoneNumberInfo { + phone_number: " ".to_string(), + pure_phone_number: "13800138000".to_string(), + country_code: "86".to_string(), + }) + .expect_err("empty provider phone number should fail closed"); + + assert!(matches!(error, WechatProviderError::MissingProfile(_))); } #[test]