fix wechat pay request headers

This commit is contained in:
2026-05-14 20:51:32 +08:00
parent 5c5a8d4a40
commit bca439726d
3 changed files with 72 additions and 16 deletions

View File

@@ -25,7 +25,9 @@ const WECHAT_PAY_PROVIDER_MOCK: &str = "mock";
const WECHAT_PAY_PROVIDER_REAL: &str = "real";
const WECHAT_PAY_BODY_SIGNATURE_METHOD: &str = "WECHATPAY2-SHA256-RSA2048";
const WECHAT_PAY_PAY_SIGN_TYPE: &str = "RSA";
const WECHAT_PAY_NOTIFY_SUCCESS: &str = "<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>";
const WECHAT_PAY_ACCEPT_HEADER: &str = "application/json";
const WECHAT_PAY_CONTENT_TYPE_HEADER: &str = "application/json";
const WECHAT_PAY_USER_AGENT: &str = "Genarrative-WechatPay/1.0";
const WECHAT_PAY_APP_ID_MAX_CHARS: usize = 32;
const WECHAT_PAY_MCH_ID_MAX_CHARS: usize = 32;
const WECHAT_PAY_DESCRIPTION_MAX_CHARS: usize = 127;
@@ -277,18 +279,17 @@ impl RealWechatPayClient {
&nonce,
&body,
)?;
let response = self
.client
.post(&self.jsapi_endpoint)
.header("Authorization", authorization)
.header("Accept", "application/json")
.header("Content-Type", "application/json")
.body(body)
.send()
.await
.map_err(|error| {
WechatPayError::RequestFailed(format!("微信支付 JSAPI 下单请求失败:{error}"))
})?;
let response = with_wechat_pay_jsapi_headers(
self.client
.post(&self.jsapi_endpoint)
.header("Authorization", authorization),
)
.body(body)
.send()
.await
.map_err(|error| {
WechatPayError::RequestFailed(format!("微信支付 JSAPI 下单请求失败:{error}"))
})?;
let status = response.status();
let response_text = response.text().await.map_err(|error| {
WechatPayError::Deserialize(format!("微信支付 JSAPI 下单响应读取失败:{error}"))
@@ -438,7 +439,7 @@ pub async fn handle_wechat_pay_notify(
State(state): State<AppState>,
headers: HeaderMap,
body: Bytes,
) -> Result<&'static str, AppError> {
) -> Result<StatusCode, AppError> {
let notify = state
.wechat_pay_client()
.parse_notify(&headers, &body)
@@ -449,7 +450,7 @@ pub async fn handle_wechat_pay_notify(
trade_state = notify.trade_state.as_str(),
"收到非成功微信支付通知"
);
return Ok(WECHAT_PAY_NOTIFY_SUCCESS);
return Ok(StatusCode::NO_CONTENT);
}
let paid_at_micros = notify
@@ -476,7 +477,7 @@ pub async fn handle_wechat_pay_notify(
"微信支付通知已确认订单入账"
);
Ok(WECHAT_PAY_NOTIFY_SUCCESS)
Ok(StatusCode::NO_CONTENT)
}
pub fn map_wechat_pay_error(error: WechatPayError) -> AppError {
@@ -532,6 +533,16 @@ fn map_wechat_pay_notify_error(error: WechatPayError) -> AppError {
map_wechat_pay_error(error)
}
fn with_wechat_pay_jsapi_headers(builder: reqwest::RequestBuilder) -> reqwest::RequestBuilder {
builder
.header(reqwest::header::ACCEPT, WECHAT_PAY_ACCEPT_HEADER)
.header(
reqwest::header::CONTENT_TYPE,
WECHAT_PAY_CONTENT_TYPE_HEADER,
)
.header(reqwest::header::USER_AGENT, WECHAT_PAY_USER_AGENT)
}
fn build_mock_pay_params(order_id: &str) -> WechatMiniProgramPayParamsResponse {
let time_stamp = OffsetDateTime::now_utc().unix_timestamp().to_string();
let nonce_str = "mock-nonce".to_string();
@@ -931,6 +942,40 @@ mod tests {
);
}
#[test]
fn jsapi_order_request_sets_wechat_required_http_headers() {
let request = with_wechat_pay_jsapi_headers(
reqwest::Client::new()
.post("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi")
.header(
"Authorization",
"WECHATPAY2-SHA256-RSA2048 mchid=\"1900000001\"",
),
)
.build()
.expect("request should build");
let headers = request.headers();
assert_eq!(
headers
.get(reqwest::header::ACCEPT)
.and_then(|value| value.to_str().ok()),
Some(WECHAT_PAY_ACCEPT_HEADER)
);
assert_eq!(
headers
.get(reqwest::header::CONTENT_TYPE)
.and_then(|value| value.to_str().ok()),
Some(WECHAT_PAY_CONTENT_TYPE_HEADER)
);
assert_eq!(
headers
.get(reqwest::header::USER_AGENT)
.and_then(|value| value.to_str().ok()),
Some(WECHAT_PAY_USER_AGENT)
);
}
#[test]
fn parse_mock_notify_defaults_success_state() {
let notify =