fix: 修复微信支付回跳刷新与查单确认

This commit is contained in:
2026-05-14 23:52:01 +08:00
parent cf3dcc6195
commit 2801b55d2f
21 changed files with 880 additions and 119 deletions

View File

@@ -10,12 +10,12 @@ use module_runtime::{
RuntimeProfileFeedbackEvidenceSnapshot, RuntimeProfileFeedbackSubmissionRecord,
RuntimeProfileInviteCodeRecord, RuntimeProfileMembershipBenefitRecord,
RuntimeProfileRechargeCenterRecord, RuntimeProfileRechargeOrderRecord,
RuntimeProfileRechargeProductRecord, RuntimeProfileRedeemCodeMode,
RuntimeProfileRedeemCodeRecord, RuntimeProfileRewardCodeRedeemRecord,
RuntimeProfileTaskCenterRecord, RuntimeProfileTaskClaimRecord, RuntimeProfileTaskConfigRecord,
RuntimeProfileTaskCycle, RuntimeProfileTaskItemRecord, RuntimeProfileTaskStatus,
RuntimeProfileWalletLedgerSourceType, RuntimeReferralInviteCenterRecord,
RuntimeTrackingScopeKind,
RuntimeProfileRechargeOrderStatus, RuntimeProfileRechargeProductRecord,
RuntimeProfileRedeemCodeMode, RuntimeProfileRedeemCodeRecord,
RuntimeProfileRewardCodeRedeemRecord, RuntimeProfileTaskCenterRecord,
RuntimeProfileTaskClaimRecord, RuntimeProfileTaskConfigRecord, RuntimeProfileTaskCycle,
RuntimeProfileTaskItemRecord, RuntimeProfileTaskStatus, RuntimeProfileWalletLedgerSourceType,
RuntimeReferralInviteCenterRecord, RuntimeTrackingScopeKind,
};
use serde::Deserialize;
use serde_json::{Value, json};
@@ -25,10 +25,10 @@ use shared_contracts::runtime::{
AdminDisableProfileTaskConfigRequest, AdminUpsertProfileInviteCodeRequest,
AdminUpsertProfileRedeemCodeRequest, AdminUpsertProfileTaskConfigRequest,
AnalyticsBucketMetricResponse, AnalyticsMetricQueryResponse, ClaimProfileTaskRewardResponse,
CreateProfileRechargeOrderRequest, CreateProfileRechargeOrderResponse,
PROFILE_FEEDBACK_STATUS_OPEN, PROFILE_TASK_CYCLE_DAILY, PROFILE_TASK_STATUS_CLAIMABLE,
PROFILE_TASK_STATUS_CLAIMED, PROFILE_TASK_STATUS_DISABLED, PROFILE_TASK_STATUS_INCOMPLETE,
PROFILE_WALLET_LEDGER_SOURCE_TYPE_ASSET_OPERATION_CONSUME,
ConfirmWechatProfileRechargeOrderResponse, CreateProfileRechargeOrderRequest,
CreateProfileRechargeOrderResponse, PROFILE_FEEDBACK_STATUS_OPEN, PROFILE_TASK_CYCLE_DAILY,
PROFILE_TASK_STATUS_CLAIMABLE, PROFILE_TASK_STATUS_CLAIMED, PROFILE_TASK_STATUS_DISABLED,
PROFILE_TASK_STATUS_INCOMPLETE, PROFILE_WALLET_LEDGER_SOURCE_TYPE_ASSET_OPERATION_CONSUME,
PROFILE_WALLET_LEDGER_SOURCE_TYPE_ASSET_OPERATION_REFUND,
PROFILE_WALLET_LEDGER_SOURCE_TYPE_DAILY_TASK_REWARD,
PROFILE_WALLET_LEDGER_SOURCE_TYPE_INVITE_INVITEE_REWARD,
@@ -63,7 +63,10 @@ use crate::{
http_error::AppError,
request_context::RequestContext,
state::AppState,
wechat_pay::{build_wechat_payment_request, current_unix_micros, map_wechat_pay_error},
wechat_pay::{
WechatPayNotifyOrder, build_wechat_payment_request, current_unix_micros,
map_wechat_pay_error,
},
};
pub async fn get_profile_dashboard(
@@ -244,6 +247,106 @@ pub async fn create_profile_recharge_order(
))
}
pub async fn confirm_wechat_profile_recharge_order(
State(state): State<AppState>,
Extension(request_context): Extension<RequestContext>,
Extension(authenticated): Extension<AuthenticatedAccessToken>,
Path(order_id): Path<String>,
) -> Result<Json<Value>, Response> {
let user_id = authenticated.claims().user_id().to_string();
let (center, order) = state
.spacetime_client()
.get_profile_recharge_order(order_id.clone())
.await
.map_err(|error| {
runtime_profile_error_response(
&request_context,
map_runtime_profile_client_error(error),
)
})?;
if order.user_id != user_id {
return Err(runtime_profile_error_response(
&request_context,
AppError::from_status(StatusCode::NOT_FOUND).with_message("充值订单不存在"),
));
}
if order.payment_channel != PROFILE_RECHARGE_PAYMENT_CHANNEL_WECHAT_MINI_PROGRAM {
return Err(runtime_profile_error_response(
&request_context,
AppError::from_status(StatusCode::BAD_REQUEST)
.with_message("该充值订单不是微信小程序支付订单"),
));
}
if order.status == RuntimeProfileRechargeOrderStatus::Paid {
return Ok(json_success_body(
Some(&request_context),
ConfirmWechatProfileRechargeOrderResponse {
order: build_profile_recharge_order_response(order),
center: build_profile_recharge_center_response(center),
},
));
}
if order.status != RuntimeProfileRechargeOrderStatus::Pending {
return Ok(json_success_body(
Some(&request_context),
ConfirmWechatProfileRechargeOrderResponse {
order: build_profile_recharge_order_response(order),
center: build_profile_recharge_center_response(center),
},
));
}
let wechat_order = state
.wechat_pay_client()
.query_order_by_out_trade_no(&order.order_id)
.await
.map_err(|error| {
runtime_profile_error_response(&request_context, map_wechat_pay_error(error))
})?;
if wechat_order.out_trade_no != order.order_id {
return Err(runtime_profile_error_response(
&request_context,
AppError::from_status(StatusCode::BAD_GATEWAY)
.with_message("微信支付查单返回的商户订单号与本地订单不一致")
.with_details(json!({ "provider": "wechat_pay" })),
));
}
if wechat_order.trade_state != "SUCCESS" {
return Ok(json_success_body(
Some(&request_context),
ConfirmWechatProfileRechargeOrderResponse {
order: build_profile_recharge_order_response(order),
center: build_profile_recharge_center_response(center),
},
));
}
let paid_at_micros = paid_at_micros_from_wechat_order(&wechat_order);
let (center, order) = state
.spacetime_client()
.mark_profile_recharge_order_paid(
wechat_order.out_trade_no,
paid_at_micros,
wechat_order.transaction_id,
)
.await
.map_err(|error| {
runtime_profile_error_response(
&request_context,
map_runtime_profile_client_error(error),
)
})?;
Ok(json_success_body(
Some(&request_context),
ConfirmWechatProfileRechargeOrderResponse {
order: build_profile_recharge_order_response(order),
center: build_profile_recharge_center_response(center),
},
))
}
pub async fn submit_profile_feedback(
State(state): State<AppState>,
Extension(request_context): Extension<RequestContext>,
@@ -801,6 +904,15 @@ async fn resolve_wechat_identity_for_payment(
.with_message("当前账号缺少微信小程序身份,请在小程序内重新登录后再支付"))
}
fn paid_at_micros_from_wechat_order(order: &WechatPayNotifyOrder) -> i64 {
order
.success_time
.as_deref()
.and_then(|value| parse_rfc3339(value).ok())
.map(offset_datetime_to_unix_micros)
.unwrap_or_else(current_unix_micros)
}
fn build_profile_recharge_center_response(
record: RuntimeProfileRechargeCenterRecord,
) -> ProfileRechargeCenterResponse {
@@ -1260,6 +1372,7 @@ mod tests {
let app = build_router(AppState::new(AppConfig::default()).expect("state should build"));
let response = app
.clone()
.oneshot(
Request::builder()
.method("GET")
@@ -1271,6 +1384,20 @@ mod tests {
.expect("request should succeed");
assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
let confirm_response = app
.clone()
.oneshot(
Request::builder()
.method("POST")
.uri("/api/profile/recharge/orders/rcgtest001/wechat/confirm")
.body(Body::empty())
.expect("request should build"),
)
.await
.expect("request should succeed");
assert_eq!(confirm_response.status(), StatusCode::UNAUTHORIZED);
}
#[tokio::test]