最近維護一個C#開發的H5老系統,是由上屆同事留下的系統,我也不知道爲啥要用C#開發,Java多香。
代碼給我後花了一天時間熟悉了一下這套系統的架構以及下訂單的邏輯和支付。
廢話不多說,本文只講支付相關的內容,不涉及業務,以及其他不相關的內容。
我就直接貼代碼了,代碼都有註釋,一步一步看一定看得懂。
前端代碼
@using Theia.Swd.H5Pay.Models.Business.School.Result
@model dynamic
@{
ViewBag.Title = "待支付訂單";
var data = ViewData["data"] as ResultForGetHtOrder;
}
<div class="shopping">
<div class="shop-group-item">
<strong>合計:<i class="total" id="AllTotal">@data.price</i></strong>
</div>
<button id="butSubmit" class="settlement" onclick="subumitpay()">結算</button>
</div>
</div>
<script>
function subumitpay() {
//按鈕置灰
disabledControl();
var orderid = "@data.orderid";//你的訂單號
var is_weixin = (function(){return
navigator.userAgent.toLowerCase().indexOf('micromessenger') !== -1})();
if (!is_weixin) {//支付寶支付
window.location.href = "/PaymentData/ReturnToAlipay?
orderId="+orderId;
return;
}//否則就是微信支付
var url = "/PaymentData/SubmitStudentRechargeOrder";
var v = {};
v.orderId = orderId;
jQuery.post(url,
v,
function (r) {
enabledControl();
if (!r.Status) {
mui.alert(r.Message);
return;
}
pay(r.Data,orderId);
});
}
function disabledControl() {
$("#butSubmit").attr("disabled", "disabled");
}
function enabledControl() {
$("#butSubmit").removeAttr("disabled");
}
function pay(data,orderId) {
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {
"appId": data.AppId, //公衆號名稱,由商戶傳入
"timeStamp": data.TimeStamp, //時間戳,自1970年以來的秒數
"nonceStr": data.NonceStr, //隨機串
"package": data.Package,
"signType": "MD5", //微信簽名方式:
"paySign": data.PaySign //微信簽名
},
function (res) {
alert(res.err_msg);
if (res.err_msg === "get_brand_wcpay_request:ok") {
// 使用以上方式判斷前端返回,微信團隊鄭重提示:
//res.err_msg將在用戶支付成功後返回ok,但並不保證它絕對可靠。
//成功後跳轉到自定義支付成功的頁面
window.location.href = "/Payment/PayInfoResult?orderId="+orderId;
}
});
}
</script>
首先來看微信支付的邏輯
public ActionResult SubmitStudentRechargeOrder()
{
try
{
//訂單id
var orderId = WebUtil.GetRequest<string>("orderId");
LogUtil.Info("調用微信支付接口:" + orderId);
var service = new PaymentService();
//根據訂單id去數據庫裏查詢訂單信息
var orderResult = service.GetStudentRechargeOrderInfo(new
ParamForGetOrderInfo()
{
OrderId = orderId,
UserId = WebUtil.GetUserId()
});
if (orderResult == null || !orderResult.Status)
{
LogUtil.Info("沒有找到訂單");
return false;
}
//訂單信息
var order = orderResult.Data;
LogUtil.Info("創建微信單訂單");
//調用微信支付接口
return CreateWechatOrder(order);
}
catch (Exception e)
{
LogUtil.Error("異常:" + e.Message);
return Json(ResponseData<ResultForWeixinMpPayment>.Exception(MessageConstant.GetErrorMessage(e)), JsonRequestBehavior.AllowGet);
}
}
private ActionResult CreateWechatOrder(ResultForGetStudentRechargeOrderInfo order)
{
//微信掃碼進來的我把用戶的openid放進了session
var opendid = WebUtil.GetOpenId();
//取訂單表裏的金額乘100 變成分
var totalFee = (int)(Convert.ToDecimal(order.Order.OrderAmount) * 100);
LogUtil.Info("進入微信支付頁面-金額" + totalFee);
//取時間戳 必傳
var timeStamp = TenPayV3Util.GetTimestamp();
//nonceStr 必傳
var nonceStr = TenPayV3Util.GetNoncestr();
var xmlDataInfo = new TenPayV3UnifiedorderRequestData(
ConfigurationManager.AppSettings["WechatAppId"],//微信公衆的APPID放在配置文件
ConfigurationManager.AppSettings["WechatMchId"],//商戶的MchId放在配置文件
order.Order.OrderTitle,//商品名稱
order.OrderId,//訂單id
totalFee,//金額
"127.0.0.1",//Request.UserHostAddress,
ConfigurationManager.AppSettings["WechatNotify"],//通知微信的回調地址
TenPayV3Type.JSAPI,
opendid,//用戶的openid
ConfigurationManager.AppSettings["WechatKey"],//商戶的密鑰
nonceStr
);
//進行簽名
var unifiedorderResult = TenPayV3.Unifiedorder(xmlDataInfo);
if (unifiedorderResult.return_code.ToUpper() == "FAIL")//簽名失敗
{
throw new Exception(unifiedorderResult.return_msg);
}
//取package
var package = $"prepay_id={unifiedorderResult.prepay_id}";
//paySign
var paySign = TenPayV3.GetJsPaySign(ConfigurationManager.AppSettings["WechatAppId"], timeStamp, nonceStr, package, ConfigurationManager.AppSettings["WechatKey"]);
//封裝成一個data返回給前臺
var result = new ResultForWeixinMpPayment
{
AppId = ConfigurationManager.AppSettings["WechatAppId"],
TimeStamp = timeStamp,
NonceStr = nonceStr,
Package = package,
PaySign = paySign
};
return Json(ResponseData<ResultForWeixinMpPayment>.Success(data: result), JsonRequestBehavior.AllowGet);
}
}
用戶支付成功後,我們後臺傳給了微信的一個回調接口。微信自動會執行這個回調接口。
所以我們還需要寫一個回調的接口。
using System;
using System.Configuration;
using System.Data.Entity.Migrations;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Web.Http;
using System.Xml.Serialization;
using Newtonsoft.Json;
using Senparc.Weixin.MP.TenPayLibV3;
using Theia.Swd.H5Pay.Framework.Data;
using Theia.Swd.H5Pay.Models.Business.Payment.Enum;
using Theia.Swd.H5Pay.Notify.Models;
namespace Theia.Swd.H5Pay.Notify.Controllers
{
public class WechatNotifyController : ApiController
{
[HttpGet, HttpPost]
public HttpResponseMessage WeixinNotify(HttpRequestMessage requestMessage)
{
try
{
var content = requestMessage.Content.ReadAsStringAsync().Result;
if (string.IsNullOrEmpty(content))
{
return ResponseFail();
}
ParamForWeixinNotify model;
using (var rdr = new StringReader(content))
{
var xmlSerializer = new XmlSerializer(typeof(ParamForWeixinNotify));
model = (ParamForWeixinNotify)xmlSerializer.Deserialize(rdr);
}
if (model == null)
{
return ResponseFail();
}
var result = Notify(model);
return new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(ConvertWeixinNotifyResultToXml(result), Encoding.UTF8, "application/xml")
};
}
catch (Exception ex)
{
//_logger.Info("WeixinNotify()異常", ex);
return ResponseFail();
}
}
public ReturnForWeixinNotify Notify(ParamForWeixinNotify param)
{
LogUtil_1.Info("進入微信Notify");
if (param.return_code == "FAIL")
{
LogUtil_1.Info("--param.return_code == FAIL--");
return ReturnForWeixinNotify.Fail(msg: param.return_msg);
}
if (param.result_code == "FAIL")
{
LogUtil_1.Info("--param.result_code == FAIL--");
return ReturnForWeixinNotify.Fail(msg: param.err_code_des);
}
if (string.IsNullOrEmpty(param.out_trade_no))
{
LogUtil_1.Info("--交易號爲空--");
return ReturnForWeixinNotify.Fail(msg: "交易號爲空");
}
var r = Query(new ParamForDoQuery()
{
OutTradeNo = param.out_trade_no
});
if (r == null || !r.TradeStatus)
{
LogUtil_1.Info("訂單未交易成功");
return ReturnForWeixinNotify.Fail(msg: "訂單未交易成功");
}
var message = string.Empty;
if (!CheckToDb(r.OutTradeNo, r.TotalFee, out message))
{
return ReturnForWeixinNotify.Fail(msg: message);
}
return ReturnForWeixinNotify.Success();
}
public bool CheckToDb(string orderId, double price, out string message)
{
try
{
using (var context = new DbContext())
{
var order = context.T_Order.FirstOrDefault(m => m.OrderID == orderId);
if (order == null)
{
LogUtil_1.Info("沒有找到訂單" + orderId);
}
else
{
LogUtil_1.Info("微信訂單支付id" + orderId);
if (order.OrderStatus == (int)OrderStatusEnum.Success)
{
message = "交易成功";
LogUtil_1.Info(message);
return true;
}
if (Math.Abs(order.OrderAmount - Convert.ToDecimal(price)) > 0.00m)
{
message = "金額不正確";
LogUtil_1.Info(message);
return false;
}
order.PaymentType = (int)PaymentTypeEnum.WechatMp;//支付類型
order.OrderStatus = (int)OrderStatusEnum.Success;//支付狀態
order.FinishedDate = DateTime.Now;//支付時間
context.T_Order.AddOrUpdate(order);//更新數據庫狀態
context.SaveChanges();//提交
}
}
message = string.Empty;
LogUtil_1.Info("微信支付成功");
return true;
}
catch (Exception e)
{
message = e.Message;
LogUtil_1.Info("微信支付異常"+message);
return false;
}
}
public ReturnForWeixinQuery Query(ParamForDoQuery paramForDoPayment)
{
//var tenPayV3Info = TenPayV3InfoCollection.Data[ConfigurationManager.AppSettings["TenPayV3_MchId"]];
var nonceStr = TenPayV3Util.GetNoncestr();
var outTradeNo = paramForDoPayment.OutTradeNo;
var datainfo = new TenPayV3OrderQueryRequestData(ConfigurationManager.AppSettings["WechatAppId"], ConfigurationManager.AppSettings["WechatMchId"],
"", nonceStr, outTradeNo, ConfigurationManager.AppSettings["WechatKey"]);
var weixinResult = TenPayV3.OrderQuery(datainfo);
var result = new ReturnForWeixinQuery();
if (weixinResult.return_code == "SUCCESS" && weixinResult.trade_state == "SUCCESS")
{
result.TradeStatus = true;
result.Message = weixinResult.return_msg;
result.OutTradeNo = weixinResult.out_trade_no;
result.TotalFee = Convert.ToDouble(Convert.ToDecimal(weixinResult.total_fee) / 100);
return result;
}
if (weixinResult.return_code == "SUCCESS" && weixinResult.trade_state != "SUCCESS")
{
result.TradeStatus = false;
result.Message = weixinResult.err_code_des;
if (string.IsNullOrEmpty(result.Message))
{
result.Message = weixinResult.return_msg;
}
if (string.IsNullOrEmpty(result.Message))
{
result.Message = weixinResult.trade_state_desc;
}
return result;
}
result.TradeStatus = false;
result.Message = weixinResult.return_msg;
return result;
}
/// <summary>
/// 迴應失敗
/// </summary>
/// <returns></returns>
private HttpResponseMessage ResponseFail()
{
return new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent("Fail")
};
}
private string ConvertWeixinNotifyResultToXml(ReturnForWeixinNotify param)
{
if (param == null)
{
param = new ReturnForWeixinNotify();
}
StringBuilder sb = new StringBuilder();
sb.Append("<xml>");
sb.AppendFormat("<return_code><![CDATA[{0}]]></return_code>", param.return_code);
sb.AppendFormat("<return_msg><![CDATA[{0}]]></return_msg>", param.return_msg);
sb.Append("</xml>");
return sb.ToString();
}
}
[XmlRoot("xml")]
public class ParamForWeixinNotify
{
[XmlElement("return_code")]
public string return_code { get; set; }
[XmlElement("result_code")]
public string result_code { get; set; }
[XmlElement("return_msg")]
public string return_msg { get; set; }
[XmlElement("err_code")]
public string err_code { get; set; }
[XmlElement("err_code_des")]
public string err_code_des { get; set; }
[XmlElement("appid")]
public string appid { get; set; }
[XmlElement("mch_id")]
public string mch_id { get; set; }
[XmlElement("total_fee")]
public string total_fee { get; set; }
[XmlElement("out_trade_no")]
public string out_trade_no { get; set; }
[XmlElement("attach")]
public string attach { get; set; }
[XmlElement("time_end")]
public string time_end { get; set; }
}
[XmlRoot("xml")]
public class ReturnForWeixinNotify
{
/// <summary>
/// 返回碼
/// </summary>
[XmlElement("return_code")]
public string return_code { get; set; }
/// <summary>
/// 返回信息
/// </summary>
[XmlElement("return_msg")]
public string return_msg { get; set; }
public static ReturnForWeixinNotify Fail(string code = "FAIL", string msg = "FAIL")
{
return new ReturnForWeixinNotify
{
return_code = code,
return_msg = msg
};
}
public static ReturnForWeixinNotify Success(string code = "SUCCESS", string msg = "OK")
{
return new ReturnForWeixinNotify
{
return_code = code,
return_msg = msg
};
}
}
public class ParamForDoQuery
{
/// <summary>
/// 外部交易號
/// </summary>
public string OutTradeNo { get; set; }
}
public class ReturnForWeixinQuery
{
/// <summary>
/// 外部交易號
/// </summary>
public string OutTradeNo { get; set; }
/// <summary>
/// 交易狀態
/// </summary>
public bool TradeStatus { get; set; }
/// <summary>
/// 返回信息
/// </summary>
public string Message { get; set; }
/// <summary>
/// 總金額
/// </summary>
public double TotalFee { get; set; }
}
}
接下來就是支付寶,跟微信一樣的邏輯。
public ActionResult ReturnToAlipay()
{
try
{
//訂單id
var orderId = WebUtil.GetRequest<string>("orderId");
LogUtil.Info("調用支付寶支付接口"+ orderId);
var service = new PaymentService();
//查詢用戶信息
var orderResult = service.GetStudentRechargeOrderInfo(new ParamForGetOrderInfo()
{
OrderId = orderId,
UserId = WebUtil.GetUserId()
});
if (orderResult == null || !orderResult.Status)
{
LogUtil.Info("未找到該訂單信息");
return Content("未找到該訂單信息");
}
var order = orderResult.Data;//訂單信息
LogUtil.Info("創建支付寶訂單");
return CreateAlipayOrder(order);
}
}
catch (Exception e)
{
LogUtil.Error("異常:"+e.Message);
return Content(ResponseData<ResultForWeixinMpPayment>.Exception(MessageConstant.GetErrorMessage(e)).Message);
}
}
private ActionResult CreateAlipayOrder(ResultForGetStudentRechargeOrderInfo order)
{
order.Order.OrderTitle = "測試付款";
IAopClient client = new DefaultAopClient(
"https://openapi.alipay.com/gateway.do",//通知地址
ConfigurationManager.AppSettings["AlipayAppId"],//支付寶商戶appid
ConfigurationManager.AppSettings["AlipayMerchantPrivateKey"],//支付寶密鑰
"JSON",
"1.0",
"RSA2",
ConfigurationManager.AppSettings["AlipayAlipayPublicKey"],//支付寶公鑰
"utf-8",
false
);
AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest();
request.SetReturnUrl(ConfigurationManager.AppSettings["AlipaySuccessUrl"]);//同步通知支付成功的頁面
request.SetNotifyUrl(ConfigurationManager.AppSettings["AlipayNotifyUrl"]);//異步請求回調接口
request.BizContent = "{" +
" \"out_trade_no\":\"" + order.OrderId + "\"," +
" \"total_amount\":\"" + order.Order.OrderAmount.ToString("F2") + "\"," +
" \"subject\":\"" + order.Order.OrderTitle + "\"," +
//" \"subject\":\" 測試付款 \"," +
" \"product_code\":\"QUICK_WAP_PAY\"" +
" }";//填充業務參數;
AlipayTradeWapPayResponse response = client.pageExecute(request);
LogUtil.Info("進入支付寶支付頁面-"+"訂單:"+order.OrderId+"");
return Content($@"
<html>
<head>
<style>
*{{width:0px;height:0px;border:0px;}}
</style>
</head>
<body>
{response.Body}
</body>
</html>
");
}
用戶支付寶成功後接下來就是回調的接口。
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration;
using System.Data.Entity.Migrations;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using Aop.Api.Util;
using Lib.Helper;
using Theia.Swd.H5Pay.Entities;
using Theia.Swd.H5Pay.Framework.Data;
using Theia.Swd.H5Pay.Models.Business.Payment.Enum;
using Theia.Swd.H5Pay.Models.Common.Enum;
using Theia.Swd.H5Pay.Notify.Models;
namespace Theia.Swd.H5Pay.Notify.Controllers
{
public class AlipayNotifyController : ApiController
{
protected System.Web.HttpRequest _request = System.Web.HttpContext.Current.Request;
public readonly string ServerUrl = "https://openapi.alipay.com/gateway.do";
public readonly string AppId = ConfigurationManager.AppSettings["AlipayAppId"]; //開發者的應用ID
public readonly string Format = "JSON";
public readonly string Charset = "utf-8";
public readonly string SignType = "RSA2"; //簽名格式
public readonly string Version = "1.0";
public readonly string SuccessUrl = ConfigurationManager.AppSettings["AlipaySuccessUrl"];
public readonly string NotifyUrl = ConfigurationManager.AppSettings["AlipayNotifyUrl"];
//商戶私鑰
static string MerchantPrivateKey = ConfigurationManager.AppSettings["AlipayMerchantPrivateKey"];
//支付寶公鑰
static string AlipayPublicKey = ConfigurationManager.AppSettings["AlipayAlipayPublicKey"];
private HttpResponseMessage ResponseMessage(string message)
{
return new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(message)
};
}
[HttpGet, HttpPost]
public HttpResponseMessage Notify()
{
LogUtil_1.Info("進入支付寶Notify");
SortedDictionary<string, string> sPara = GetRequestPost();//將post請求過來的參數傳化爲SortedDictionary
if (sPara.Count <= 0)
{
LogUtil_1.Info("無通知參數");
return ResponseMessage("無通知參數");
}
//AlipayTradeWayPayServer pay = new AlipayTradeWayPayServer();
var verifyerifyResult = Verify(sPara);//驗籤
if (!verifyerifyResult)
{
LogUtil_1.Info("fail");
return ResponseMessage("fail");
}
try
{
//商戶訂單號
var out_trade_no = _request.Form["out_trade_no"];
//支付寶交易號
var trade_no = _request.Form["trade_no"];
//支付金額
var total_amount = ConverterHelper.ConvertType<double>(_request.Form["total_amount"]);
//實收金額
//decimal receipt_amount = _request.Form["receipt_amount"].ConvertType(Decimal.Zero);
//交易狀態
var trade_status = _request.Form["trade_status"];
//賣家支付寶賬號
var seller_id = _request.Form["seller_id"];
//商品描述
var body = _request.Form["body"];
//交易創建時間
var gmt_create = DateTime.Parse(_request.Form["gmt_create"]);
//交易付款時間
var gmt_payment = DateTime.Parse(_request.Form["gmt_payment"]);
var appid = _request.Form["app_id"];
//WriteError("驗證參數開始");
if (_request.Form["trade_status"] != "TRADE_SUCCESS")
{
//AlipayWayPayPO model = CreateAlipayWayPay(out_trade_no, trade_no, trade_status, gmt_create, gmt_payment);
//pay.PaySuccess(out_trade_no, model, Server.MapPath("~/" + DateTime.Today.ToString("yyMMdd") + ".txt"));//修改訂單
//注意:
//付款完成後,支付寶系統發送該交易狀態通知
LogUtil_1.Info("商戶驗證失敗");
return ResponseMessage("商戶驗證失敗");
}
var message = string.Empty;
if (!CheckToDb(out_trade_no, total_amount, seller_id, out message))
{
return ResponseMessage(message);
}
//——請根據您的業務邏輯來編寫程序(以上代碼僅作參考)——
//_response.Write("success"); //請不要修改或刪除
return ResponseMessage("success");
}
catch (Exception ex)
{
return ResponseMessage(ex.Message);
}
}
public bool CheckToDb(string orderId, double price, string sellerId, out string message)
{
try
{
using (var context = new DbContext())
{
var order = context.T_Order.FirstOrDefault(m => m.OrderID == orderId);
if (order == null)
{
message = "找不到訂單";
LogUtil_1.Info(message);
return false;
}
if (order.OrderStatus == (int)OrderStatusEnum.Success)
{
message = "交易成功";
LogUtil_1.Info(message);
return true;
}
if (Math.Abs(order.OrderAmount - Convert.ToDecimal(price)) > 0.00m)
{
message = "金額不正確";
LogUtil_1.Info(message);
return false;
}
using (var trans = context.Database.BeginTransaction())
{
order.PaymentType = (int)PaymentTypeEnum.Alipay;//支付類型
order.OrderStatus = (int)OrderStatusEnum.Success;//支付狀態
order.FinishedDate = DateTime.Now;//支付時間
context.T_Order.AddOrUpdate(order);//更新
context.SaveChanges();
trans.Commit();
}
}
}
message = string.Empty;
LogUtil_1.Info("支付寶支付成功");
return true;
}
catch (Exception e)
{
message = e.Message;
LogUtil_1.Info("支付寶支付異常"+message);
return false;
}
}
private SortedDictionary<string, string> GetRequestPost()
{
int i = 0;
SortedDictionary<string, string> sArray = new SortedDictionary<string, string>();
NameValueCollection coll;
coll = _request.Form;
String[] requestItem = coll.AllKeys;
for (i = 0; i < requestItem.Length; i++)
{
sArray.Add(requestItem[i], _request.Form[requestItem[i]]);
}
return sArray;
}
public Boolean Verify(SortedDictionary<string, string> inputPara)
{
Dictionary<string, string> sPara = new Dictionary<string, string>();
Boolean verifyResult = AlipaySignature.RSACheckV1(inputPara, AlipayPublicKey, Charset, SignType, false);
return verifyResult;
}
}
}
好了全部流程就是這樣了,有什麼問題可以聯繫我哦!