Merge branch 'hermes/wechat'

# Conflicts:
#	.hermes/shared-memory/decision-log.md
#	docs/technical/MY_TAB_ACCOUNT_RECHARGE_IMPLEMENTATION_2026-04-25.md
#	docs/technical/OIDC_JWT_CLAIMS_DESIGN_2026-04-21.md
#	server-rs/crates/module-runtime/src/errors.rs
#	src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx
#	src/components/rpg-entry/RpgEntryHomeView.tsx
This commit is contained in:
2026-05-15 11:32:51 +08:00
23 changed files with 2325 additions and 107 deletions

View File

@@ -245,6 +245,18 @@ pub struct WechatMiniProgramPayParamsResponse {
pub pay_sign: String,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct WechatH5PaymentResponse {
pub h5_url: String,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct WechatNativePaymentResponse {
pub code_url: String,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ProfileRechargeCenterResponse {
@@ -272,6 +284,10 @@ pub struct CreateProfileRechargeOrderResponse {
pub center: ProfileRechargeCenterResponse,
#[serde(default)]
pub wechat_mini_program_pay_params: Option<WechatMiniProgramPayParamsResponse>,
#[serde(default)]
pub wechat_h5_payment: Option<WechatH5PaymentResponse>,
#[serde(default)]
pub wechat_native_payment: Option<WechatNativePaymentResponse>,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
@@ -1381,6 +1397,60 @@ mod tests {
assert_eq!(payload.payment_channel, None);
}
#[test]
fn create_profile_recharge_order_response_serializes_web_wechat_payloads() {
let order = ProfileRechargeOrderResponse {
order_id: "rcgtest001".to_string(),
product_id: "points_60".to_string(),
product_title: "60泥点".to_string(),
kind: "points".to_string(),
amount_cents: 600,
status: "pending".to_string(),
payment_channel: "wechat_native".to_string(),
paid_at: None,
provider_transaction_id: None,
created_at: "2026-05-15T10:00:00Z".to_string(),
points_delta: 0,
membership_expires_at: None,
};
let center = ProfileRechargeCenterResponse {
wallet_balance: 0,
membership: ProfileMembershipResponse {
status: "normal".to_string(),
tier: "normal".to_string(),
started_at: None,
expires_at: None,
updated_at: None,
},
point_products: vec![],
membership_products: vec![],
benefits: vec![],
latest_order: None,
has_points_recharged: false,
};
let payload = serde_json::to_value(CreateProfileRechargeOrderResponse {
order,
center,
wechat_mini_program_pay_params: None,
wechat_h5_payment: Some(WechatH5PaymentResponse {
h5_url: "https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb".to_string(),
}),
wechat_native_payment: Some(WechatNativePaymentResponse {
code_url: "weixin://pay.weixin.qq.com/bizpayurl/up?pr=test".to_string(),
}),
})
.expect("payload should serialize");
assert_eq!(
payload["wechatH5Payment"]["h5Url"],
json!("https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb")
);
assert_eq!(
payload["wechatNativePayment"]["codeUrl"],
json!("weixin://pay.weixin.qq.com/bizpayurl/up?pr=test")
);
}
#[test]
fn profile_feedback_response_uses_camel_case_fields() {
let payload = serde_json::to_value(SubmitProfileFeedbackResponse {