using System; using System.Collections.Generic; using System.Web; using System.IO; using System.Text; using LitJson; namespace Mtxfw.Utility { /// /// 微信小程序支付结果类 /// 用于封装创建JSAPI支付订单后的返回结果 /// public class MiniProgramPayResult { /// /// 支付订单创建是否成功 /// true: 成功 false: 失败 /// public bool Success { get; set; } /// /// 结果消息描述 /// 成功时为"下单成功",失败时为具体错误信息 /// public string Message { get; set; } /// /// 时间戳 /// 自1970年1月1日以来的秒数,用于小程序端调起支付 /// public string TimeStamp { get; set; } /// /// 随机字符串 /// 32位以内的随机串,用于签名计算 /// public string NonceStr { get; set; } /// /// 订单详情扩展字符串 /// 格式: prepay_id=xxx,小程序端调起支付时需要此参数 /// public string Package { get; set; } /// /// 签名 /// 用于小程序端调起支付时的安全校验 /// public string PaySign { get; set; } /// /// 签名类型 /// 固定值: MD5 /// public string SignType { get; set; } /// /// 预支付交易会话标识 /// 微信返回的预支付ID,有效期2小时 /// public string PrepayId { get; set; } /// /// 商户订单号 /// 商户系统内部的订单号,要求32个字符内,唯一性 /// public string OutTradeNo { get; set; } /// /// 微信支付订单号 /// 微信支付系统生成的订单号,查询订单时使用 /// public string TransactionId { get; set; } /// /// 错误码 /// 支付失败时的错误代码,如: NOTENOUGH(余额不足) /// public string ErrCode { get; set; } /// /// 错误码描述 /// 支付失败时的错误详细信息 /// public string ErrCodeDes { get; set; } } /// /// 微信小程序支付回调通知结果类 /// 用于封装处理支付回调通知后的返回结果 /// public class MiniProgramPayNotifyResult { /// /// 支付回调处理是否成功 /// true: 成功 false: 失败 /// public bool Success { get; set; } /// /// 结果消息描述 /// public string Message { get; set; } /// /// 商户订单号 /// 商户系统内部的订单号 /// public string OutTradeNo { get; set; } /// /// 微信支付订单号 /// 微信支付系统生成的订单号 /// public string TransactionId { get; set; } /// /// 订单总金额 /// 单位: 分,需要转换为元显示 /// public int TotalFee { get; set; } /// /// 用户标识 /// 用户在商户appid下的唯一标识 /// public string OpenId { get; set; } /// /// 支付完成时间 /// 格式: yyyyMMddHHmmss /// public string TimeEnd { get; set; } /// /// 银行类型 /// 银行编码,如: CMC(招商银行) /// public string BankType { get; set; } /// /// 返回给微信的XML响应 /// 用于告知微信支付后台处理结果 /// public string ResultXml { get; set; } } /// /// 微信小程序支付核心类 /// 提供创建JSAPI支付订单、处理支付回调、验证支付结果等核心功能 /// public class MiniProgramPay { /// /// 配置对象,包含微信支付相关配置信息 /// private Config _config; /// /// 接口超时时间,单位: 秒,默认6秒 /// private int _timeout = 6; /// /// 构造函数 /// /// 配置对象,包含appid、商户号、密钥等信息 public MiniProgramPay(Config config) { _config = config; } /// /// 构造函数(带超时设置) /// /// 配置对象 /// 接口超时时间,单位: 秒 public MiniProgramPay(Config config, int timeout) { _config = config; _timeout = timeout; } /// /// 创建JSAPI支付订单 /// 调用微信统一下单接口,获取小程序调起支付所需的参数 /// /// 用户标识,用户在商户appid下的唯一标识,必填 /// 商户订单号,商户系统内部订单号,要求32个字符内,必填 /// 订单总金额,单位: 分,必填 /// 商品描述,如: "腾讯充值中心-QQ会员充值",必填 /// 附加数据,在查询API和支付通知中原样返回,可选 /// 支付结果通知回调地址,可选,不传则使用配置中的地址 /// MiniProgramPayResult对象,包含支付参数或错误信息 public MiniProgramPayResult CreateJsApiOrder(string openid, string outTradeNo, int totalFee, string body, string attach = "", string notifyUrl = "") { MiniProgramPayResult result = new MiniProgramPayResult(); try { // 参数校验 - openid必填 if (string.IsNullOrEmpty(openid)) { result.Success = false; result.Message = "openid不能为空"; return result; } // 参数校验 - 商户订单号必填 if (string.IsNullOrEmpty(outTradeNo)) { result.Success = false; result.Message = "商户订单号不能为空"; return result; } // 参数校验 - 支付金额必须大于0 if (totalFee <= 0) { result.Success = false; result.Message = "支付金额必须大于0"; return result; } // 参数校验 - 商品描述必填 if (string.IsNullOrEmpty(body)) { result.Success = false; result.Message = "商品描述不能为空"; return result; } // 构建统一下单请求参数 WxPayData data = new WxPayData(); data.SetValue("body", body); // 商品描述 // 附加数据,可选 if (!string.IsNullOrEmpty(attach)) { data.SetValue("attach", attach); } data.SetValue("out_trade_no", outTradeNo); // 商户订单号 data.SetValue("total_fee", totalFee); // 订单总金额,单位: 分 data.SetValue("time_start", DateTime.Now.ToString("yyyyMMddHHmmss")); // 交易起始时间 data.SetValue("time_expire", DateTime.Now.AddMinutes(10).ToString("yyyyMMddHHmmss")); // 交易结束时间,10分钟后过期 data.SetValue("trade_type", "JSAPI"); // 交易类型,小程序支付固定为JSAPI data.SetValue("openid", openid); // 用户标识 // 支付结果通知回调地址,可选 if (!string.IsNullOrEmpty(notifyUrl)) { data.SetValue("notify_url", HttpUtility.UrlEncode(notifyUrl)); } // 调用微信统一下单接口 WxPayData unifiedOrderResult = WxPayApi.UnifiedOrder(_config, data, _timeout); // 检查统一下单是否成功 if (!unifiedOrderResult.IsSet("appid") || !unifiedOrderResult.IsSet("prepay_id") || unifiedOrderResult.GetValue("prepay_id").ToString() == "") { result.Success = false; result.Message = "统一下单失败"; // 获取微信返回的错误信息 if (unifiedOrderResult.IsSet("return_msg")) { result.Message = unifiedOrderResult.GetValue("return_msg").ToString(); } // 获取错误码描述 if (unifiedOrderResult.IsSet("err_code_des")) { result.ErrCodeDes = unifiedOrderResult.GetValue("err_code_des").ToString(); } // 获取错误码 if (unifiedOrderResult.IsSet("err_code")) { result.ErrCode = unifiedOrderResult.GetValue("err_code").ToString(); } Log.Error("MiniProgramPay", "UnifiedOrder failed: " + unifiedOrderResult.ToJson()); return result; } // 获取预支付交易会话标识 string prepayId = unifiedOrderResult.GetValue("prepay_id").ToString(); result.PrepayId = prepayId; result.OutTradeNo = outTradeNo; // 构建小程序调起支付所需的参数 WxPayData jsApiParam = new WxPayData(); jsApiParam.SetValue("appId", unifiedOrderResult.GetValue("appid")); // 公众号ID jsApiParam.SetValue("timeStamp", WxPayApi.GenerateTimeStamp()); // 时间戳 jsApiParam.SetValue("nonceStr", WxPayApi.GenerateNonceStr()); // 随机字符串 jsApiParam.SetValue("package", "prepay_id=" + prepayId); // 订单详情 jsApiParam.SetValue("signType", "MD5"); // 签名类型 jsApiParam.SetValue("paySign", jsApiParam.MakeSign(_config)); // 签名 // 设置返回结果 result.Success = true; result.Message = "下单成功"; result.TimeStamp = jsApiParam.GetValue("timeStamp").ToString(); result.NonceStr = jsApiParam.GetValue("nonceStr").ToString(); result.Package = jsApiParam.GetValue("package").ToString(); result.PaySign = jsApiParam.GetValue("paySign").ToString(); result.SignType = "MD5"; Log.Info("MiniProgramPay", "CreateJsApiOrder success, outTradeNo: " + outTradeNo); return result; } catch (WxPayException ex) { // 微信支付异常处理 result.Success = false; result.Message = ex.Message; Log.Error("MiniProgramPay", "WxPayException: " + ex.Message); return result; } catch (Exception ex) { // 系统异常处理 result.Success = false; result.Message = "系统异常"; Log.Error("MiniProgramPay", "Exception: " + ex.ToString()); return result; } } /// /// 处理支付回调通知 /// 接收微信支付后台发送的支付结果,验证签名并返回处理结果 /// /// HTTP请求输入流,包含微信发送的XML数据 /// MiniProgramPayNotifyResult对象,包含支付结果信息 public MiniProgramPayNotifyResult ProcessNotify(Stream inputStream) { MiniProgramPayNotifyResult result = new MiniProgramPayNotifyResult(); try { // 读取微信支付后台发送的XML数据 int count = 0; byte[] buffer = new byte[1024]; StringBuilder builder = new StringBuilder(); while ((count = inputStream.Read(buffer, 0, 1024)) > 0) { builder.Append(Encoding.UTF8.GetString(buffer, 0, count)); } inputStream.Flush(); inputStream.Close(); inputStream.Dispose(); string notifyData = builder.ToString(); Log.Info("MiniProgramPay", "Receive notify data: " + notifyData); // 检查回调数据是否为空 if (string.IsNullOrEmpty(notifyData)) { result.Success = false; result.Message = "回调数据为空"; result.ResultXml = BuildNotifyResponse(false, "回调数据为空"); return result; } // 解析XML数据并验证签名 WxPayData data = new WxPayData(); try { data.FromXml(_config, notifyData); } catch (WxPayException ex) { // 签名验证失败 result.Success = false; result.Message = "签名验证失败"; result.ResultXml = BuildNotifyResponse(false, ex.Message); Log.Error("MiniProgramPay", "Sign check failed: " + ex.Message); return result; } // 检查返回状态码是否为SUCCESS if (!data.IsSet("return_code") || data.GetValue("return_code").ToString() != "SUCCESS") { result.Success = false; result.Message = "返回状态码不正确"; result.ResultXml = BuildNotifyResponse(false, "返回状态码不正确"); return result; } // 检查业务结果是否为SUCCESS if (!data.IsSet("result_code") || data.GetValue("result_code").ToString() != "SUCCESS") { result.Success = false; result.Message = "业务结果不正确"; if (data.IsSet("err_code_des")) { result.Message = data.GetValue("err_code_des").ToString(); } result.ResultXml = BuildNotifyResponse(false, result.Message); return result; } // 检查微信订单号是否存在 if (!data.IsSet("transaction_id")) { result.Success = false; result.Message = "微信订单号不存在"; result.ResultXml = BuildNotifyResponse(false, "微信订单号不存在"); return result; } // 提取支付结果数据 string transactionId = data.GetValue("transaction_id").ToString(); // 微信支付订单号 string outTradeNo = data.IsSet("out_trade_no") ? data.GetValue("out_trade_no").ToString() : ""; // 商户订单号 int totalFee = data.IsSet("total_fee") ? int.Parse(data.GetValue("total_fee").ToString()) : 0; // 订单总金额,单位: 分 string openid = data.IsSet("openid") ? data.GetValue("openid").ToString() : ""; // 用户标识 string timeEnd = data.IsSet("time_end") ? data.GetValue("time_end").ToString() : ""; // 支付完成时间 string bankType = data.IsSet("bank_type") ? data.GetValue("bank_type").ToString() : ""; // 银行类型 // 验证订单真实性 - 通过查询订单接口验证 bool queryResult = VerifyPaymentResult(transactionId); if (!queryResult) { result.Success = false; result.Message = "订单查询验证失败"; result.ResultXml = BuildNotifyResponse(false, "订单查询验证失败"); Log.Error("MiniProgramPay", "Order verify failed, transactionId: " + transactionId); return result; } // 设置返回结果 result.Success = true; result.Message = "支付成功"; result.TransactionId = transactionId; result.OutTradeNo = outTradeNo; result.TotalFee = totalFee; result.OpenId = openid; result.TimeEnd = timeEnd; result.BankType = bankType; result.ResultXml = BuildNotifyResponse(true, "OK"); Log.Info("MiniProgramPay", "ProcessNotify success, transactionId: " + transactionId + ", outTradeNo: " + outTradeNo); return result; } catch (Exception ex) { // 系统异常处理 result.Success = false; result.Message = "系统异常"; result.ResultXml = BuildNotifyResponse(false, "系统异常"); Log.Error("MiniProgramPay", "ProcessNotify exception: " + ex.ToString()); return result; } } /// /// 验证支付结果(通过微信订单号) /// 调用微信订单查询接口验证订单是否支付成功 /// /// 微信支付订单号 /// true: 验证成功 false: 验证失败 public bool VerifyPaymentResult(string transactionId) { try { if (string.IsNullOrEmpty(transactionId)) { return false; } // 构建查询订单请求参数 WxPayData req = new WxPayData(); req.SetValue("transaction_id", transactionId); // 调用订单查询接口 WxPayData res = WxPayApi.OrderQuery(_config, req, _timeout); // 检查查询结果 // return_code: 返回状态码,SUCCESS/FAIL // result_code: 业务结果,SUCCESS/FAIL // trade_state: 交易状态,SUCCESS—支付成功 if (res.IsSet("return_code") && res.GetValue("return_code").ToString() == "SUCCESS" && res.IsSet("result_code") && res.GetValue("result_code").ToString() == "SUCCESS" && res.IsSet("trade_state") && res.GetValue("trade_state").ToString() == "SUCCESS") { Log.Info("MiniProgramPay", "VerifyPaymentResult success, transactionId: " + transactionId); return true; } Log.Error("MiniProgramPay", "VerifyPaymentResult failed, transactionId: " + transactionId + ", response: " + res.ToJson()); return false; } catch (Exception ex) { Log.Error("MiniProgramPay", "VerifyPaymentResult exception: " + ex.ToString()); return false; } } /// /// 验证支付结果(通过商户订单号) /// 调用微信订单查询接口验证订单是否支付成功 /// /// 商户订单号 /// true: 验证成功 false: 验证失败 public bool VerifyPaymentResultByOutTradeNo(string outTradeNo) { try { if (string.IsNullOrEmpty(outTradeNo)) { return false; } // 构建查询订单请求参数 WxPayData req = new WxPayData(); req.SetValue("out_trade_no", outTradeNo); // 调用订单查询接口 WxPayData res = WxPayApi.OrderQuery(_config, req, _timeout); // 检查查询结果 if (res.IsSet("return_code") && res.GetValue("return_code").ToString() == "SUCCESS" && res.IsSet("result_code") && res.GetValue("result_code").ToString() == "SUCCESS" && res.IsSet("trade_state") && res.GetValue("trade_state").ToString() == "SUCCESS") { Log.Info("MiniProgramPay", "VerifyPaymentResultByOutTradeNo success, outTradeNo: " + outTradeNo); return true; } Log.Error("MiniProgramPay", "VerifyPaymentResultByOutTradeNo failed, outTradeNo: " + outTradeNo + ", response: " + res.ToJson()); return false; } catch (Exception ex) { Log.Error("MiniProgramPay", "VerifyPaymentResultByOutTradeNo exception: " + ex.ToString()); return false; } } /// /// 查询订单 /// 通过微信订单号或商户订单号查询订单状态 /// /// 微信支付订单号,与outTradeNo二选一 /// 商户订单号,与transactionId二选一 /// WxPayData对象,包含订单详细信息 public WxPayData QueryOrder(string transactionId = "", string outTradeNo = "") { try { WxPayData req = new WxPayData(); // 优先使用微信订单号查询 if (!string.IsNullOrEmpty(transactionId)) { req.SetValue("transaction_id", transactionId); } else if (!string.IsNullOrEmpty(outTradeNo)) { req.SetValue("out_trade_no", outTradeNo); } else { throw new WxPayException("transaction_id和out_trade_no至少填写一个"); } // 调用订单查询接口 WxPayData res = WxPayApi.OrderQuery(_config, req, _timeout); return res; } catch (Exception ex) { Log.Error("MiniProgramPay", "QueryOrder exception: " + ex.ToString()); throw; } } /// /// 关闭订单 /// 当订单长时间未支付时,可调用此接口关闭订单 /// 注意:订单生成后不能马上调用关闭订单接口,最短调用时间间隔为5分钟 /// /// 商户订单号 /// WxPayData对象,包含关闭结果 public WxPayData CloseOrder(string outTradeNo) { try { if (string.IsNullOrEmpty(outTradeNo)) { throw new WxPayException("商户订单号不能为空"); } // 构建关闭订单请求参数 WxPayData req = new WxPayData(); req.SetValue("out_trade_no", outTradeNo); // 调用关闭订单接口 WxPayData res = WxPayApi.CloseOrder(_config, req, _timeout); return res; } catch (Exception ex) { Log.Error("MiniProgramPay", "CloseOrder exception: " + ex.ToString()); throw; } } /// /// 申请退款 /// 当交易发生错误或交易失败时,商户可通过此接口退款 /// 注意:退款需要商户证书 /// /// 商户订单号 /// 商户退款单号,商户系统内部的退款单号,要求唯一 /// 订单总金额,单位: 分 /// 退款金额,单位: 分,不能大于订单总金额 /// 操作员ID,可选,默认为商户号 /// WxPayData对象,包含退款结果 public WxPayData Refund(string outTradeNo, string outRefundNo, int totalFee, int refundFee, string opUserId = "") { try { // 参数校验 if (string.IsNullOrEmpty(outTradeNo)) { throw new WxPayException("商户订单号不能为空"); } if (string.IsNullOrEmpty(outRefundNo)) { throw new WxPayException("商户退款单号不能为空"); } if (totalFee <= 0) { throw new WxPayException("订单总金额必须大于0"); } if (refundFee <= 0) { throw new WxPayException("退款金额必须大于0"); } if (refundFee > totalFee) { throw new WxPayException("退款金额不能大于订单总金额"); } // 构建退款请求参数 WxPayData req = new WxPayData(); req.SetValue("out_trade_no", outTradeNo); // 商户订单号 req.SetValue("out_refund_no", outRefundNo); // 商户退款单号 req.SetValue("total_fee", totalFee); // 订单总金额,单位: 分 req.SetValue("refund_fee", refundFee); // 退款金额,单位: 分 // 操作员ID,默认为商户号 if (string.IsNullOrEmpty(opUserId)) { req.SetValue("op_user_id", _config.webPARTNER); } else { req.SetValue("op_user_id", opUserId); } // 调用退款接口 WxPayData res = WxPayApi.Refund(_config, req, _timeout); return res; } catch (Exception ex) { Log.Error("MiniProgramPay", "Refund exception: " + ex.ToString()); throw; } } /// /// 构建回调通知响应XML /// 用于返回给微信支付后台的处理结果 /// /// 处理是否成功 /// 消息内容 /// XML格式的响应字符串 public string BuildNotifyResponse(bool success, string message) { WxPayData res = new WxPayData(); res.SetValue("return_code", success ? "SUCCESS" : "FAIL"); res.SetValue("return_msg", message); return res.ToXml(); } /// /// 生成商户订单号 /// 格式: 商户号 + 时间戳 + 3位随机数 /// /// 配置对象 /// 商户订单号字符串 public static string GenerateOutTradeNo(Config config) { var ran = new Random(); return string.Format("{0}{1}{2}", config.webPARTNER, DateTime.Now.ToString("yyyyMMddHHmmss"), ran.Next(999)); } /// /// 获取支付结果JSON字符串 /// 用于返回给前端小程序的支付参数 /// /// 支付结果对象 /// JSON格式的字符串 public string GetPayResultJson(MiniProgramPayResult result) { if (result.Success) { // 成功时返回支付参数 return JsonMapper.ToJson(new { status = 1, // 状态码,1表示成功 msg = result.Message, // 消息 timeStamp = result.TimeStamp, // 时间戳 noncestr = result.NonceStr, // 随机字符串 package = result.Package, // 订单详情 paySign = result.PaySign, // 签名 signType = result.SignType, // 签名类型 prepayId = result.PrepayId, // 预支付ID outTradeNo = result.OutTradeNo // 商户订单号 }); } else { // 失败时返回错误信息 return JsonMapper.ToJson(new { status = 0, // 状态码,0表示失败 msg = result.Message, // 错误消息 errCode = result.ErrCode, // 错误码 errCodeDes = result.ErrCodeDes // 错误描述 }); } } } }