commit 2745f06caaf420a55c2d45e0ed7d30c18f6d73a5 Author: robin Date: Wed Aug 9 09:23:04 2017 +0800 svn move to git diff --git a/BWP.ABCClient.sln b/BWP.ABCClient.sln new file mode 100644 index 0000000..86522a1 --- /dev/null +++ b/BWP.ABCClient.sln @@ -0,0 +1,26 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BWP.ABCClient", "BWP.ABCClient\BWP.ABCClient.csproj", "{22FEFA9B-1D58-4E8A-A1FF-49CEED62C355}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "test", "test\test.csproj", "{9299FD15-70F9-4E71-9AB8-DC0C081FB7B5}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {22FEFA9B-1D58-4E8A-A1FF-49CEED62C355}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {22FEFA9B-1D58-4E8A-A1FF-49CEED62C355}.Debug|Any CPU.Build.0 = Debug|Any CPU + {22FEFA9B-1D58-4E8A-A1FF-49CEED62C355}.Release|Any CPU.ActiveCfg = Release|Any CPU + {22FEFA9B-1D58-4E8A-A1FF-49CEED62C355}.Release|Any CPU.Build.0 = Release|Any CPU + {9299FD15-70F9-4E71-9AB8-DC0C081FB7B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9299FD15-70F9-4E71-9AB8-DC0C081FB7B5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9299FD15-70F9-4E71-9AB8-DC0C081FB7B5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9299FD15-70F9-4E71-9AB8-DC0C081FB7B5}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/BWP.ABCClient/B2B/FundTransferRequest.cs b/BWP.ABCClient/B2B/FundTransferRequest.cs new file mode 100644 index 0000000..2c1783b --- /dev/null +++ b/BWP.ABCClient/B2B/FundTransferRequest.cs @@ -0,0 +1,90 @@ +using System.Xml.Serialization; +using BWP.ABCClient.Common; + +namespace BWP.ABCClient.B2B +{ + [XmlRoot(ElementName = "MSG")] + public class FundTransferRequest : MessageBase + { + public MessageContent Message { get; set; } + + public static FundTransferRequest New() + { + return new FundTransferRequest { + Message = new MessageContent { + Merchant = new MerchantContent { + ECMerchantType = "B2B", + MerchantID = string.Empty + }, + TrxRequest = new TrxRequestContent { + TrxType = TrxType.FundTransfer, + MerchantTrnxNo = string.Empty, + TrnxAmount = "0", + TrnxDate = string.Empty, + TrnxTime = string.Empty, + AccountDB = new AccountDBContent { + AccountDBNo = string.Empty, + AccountDBName = string.Empty, + AccountDBBank = string.Empty + }, + TrnxInfo = new TrnxInfoContent { + TrnxOpr = string.Empty + }, + ResultNotifyURL = string.Empty, + MerchantRemarks = string.Empty + } + }, + SignatureAlgorithm = "SHA1withRSA", + Signature = string.Empty + }; + } + + public class MessageContent + { + public MerchantContent Merchant { get; set; } + public TrxRequestContent TrxRequest { get; set; } + } + + public class TrxRequestContent + { + public string TrxType { get; set; } + public string MerchantTrnxNo { get; set; } + public string TrnxAmount { get; set; } + public string TrnxDate { get; set; } + public string TrnxTime { get; set; } + public AccountDBContent AccountDB { get; set; } + public TrnxInfoContent TrnxInfo { get; set; } + public string ResultNotifyURL { get; set; } + public string MerchantRemarks { get; set; } + } + + public class TrnxInfoContent + { + public string TrnxOpr { get; set; } + [XmlArrayItem(ElementName = "Remark")] + public TrnxRemark[] TrnxRemarks { get; set; } + public TrnxItem[] TrnxItems { get; set; } + } + + public class TrnxRemark + { + public string Key { get; set; } + public string Value { get; set; } + } + + public class TrnxItem + { + public string ProductID { get; set; } + public string ProductName { get; set; } + public string UnitPrice { get; set; } + public string Qty { get; set; } + } + + public class AccountDBContent + { + public string AccountDBNo { get; set; } + public string AccountDBName { get; set; } + public string AccountDBBank { get; set; } + } + } +} diff --git a/BWP.ABCClient/B2B/FundTransferResponse.cs b/BWP.ABCClient/B2B/FundTransferResponse.cs new file mode 100644 index 0000000..6cd7c25 --- /dev/null +++ b/BWP.ABCClient/B2B/FundTransferResponse.cs @@ -0,0 +1,32 @@ +using System.Xml.Serialization; +using BWP.ABCClient.Common; + +namespace BWP.ABCClient.B2B +{ + [XmlRoot(ElementName = "MSG")] + public class FundTransferResponse : MessageBase + { + public MessageContent Message { get; set; } + + public class MessageContent + { + public MerchantContent Merchant { get; set; } + public TrxResponseContent TrxResponse { get; set; } + } + + public class TrxResponseContent + { + public string ReturnCode { get; set; } + + public string ErrorMessage { get; set; } + + public string TrnxType { get; set; } + + public string TrnxAMT { get; set; } + + public string MerchantTrnxNo { get; set; } + + public string PaymentURL { get; set; } + } + } +} diff --git a/BWP.ABCClient/B2B/QueryTrnxRequest.cs b/BWP.ABCClient/B2B/QueryTrnxRequest.cs new file mode 100644 index 0000000..b6c8085 --- /dev/null +++ b/BWP.ABCClient/B2B/QueryTrnxRequest.cs @@ -0,0 +1,43 @@ +using System.Xml.Serialization; +using BWP.ABCClient.Common; + +namespace BWP.ABCClient.B2B +{ + [XmlRoot(ElementName = "MSG")] + public class QueryTrnxRequest : MessageBase + { + public MessageContent Message { get; set; } + + public static QueryTrnxRequest New() + { + return new QueryTrnxRequest { + Message = new MessageContent { + Merchant = new MerchantContent { + ECMerchantType = "B2B", + MerchantID = string.Empty + }, + TrxRequest = new TrxRequestContent { + TrxType = TrxType.QueryTrnx, + MerchantTrnxNo = string.Empty, + MerchantRemarks = string.Empty + } + }, + SignatureAlgorithm = "SHA1withRSA", + Signature = string.Empty + }; + } + + public class MessageContent + { + public MerchantContent Merchant { get; set; } + public TrxRequestContent TrxRequest { get; set; } + } + + public class TrxRequestContent + { + public string TrxType { get; set; } + public string MerchantTrnxNo { get; set; } + public string MerchantRemarks { get; set; } + } + } +} diff --git a/BWP.ABCClient/B2B/QueryTrnxResponse.cs b/BWP.ABCClient/B2B/QueryTrnxResponse.cs new file mode 100644 index 0000000..6e46cd4 --- /dev/null +++ b/BWP.ABCClient/B2B/QueryTrnxResponse.cs @@ -0,0 +1,34 @@ +using System.Xml.Serialization; +using BWP.ABCClient.Common; + +namespace BWP.ABCClient.B2B +{ + [XmlRoot(ElementName = "MSG")] + public class QueryTrnxResponse : MessageBase + { + public MessageContent Message { get; set; } + + public class MessageContent + { + public MerchantContent Merchant { get; set; } + public TrxResponseContent TrxResponse { get; set; } + } + + public class TrxResponseContent + { + public string ReturnCode { get; set; } + + public string ErrorMessage { get; set; } + + public string TrnxType { get; set; } + + public string TrnxAMT { get; set; } + + public string MerchantTrnxNo { get; set; } + + public string MerchantID { get; set; } + + public string TrnxStatus { get; set; } + } + } +} diff --git a/BWP.ABCClient/B2B/TrxStatus.cs b/BWP.ABCClient/B2B/TrxStatus.cs new file mode 100644 index 0000000..8745266 --- /dev/null +++ b/BWP.ABCClient/B2B/TrxStatus.cs @@ -0,0 +1,13 @@ +namespace BWP.ABCClient.B2B +{ + public static class TrxStatus + { + public const string CANCEL = "5"; + public const string CHECK = "1"; + public const string FAILURE = "3"; + public const string NO_RESPONSE = "9"; + public const string ORIGINAL = "0"; + public const string REJECT = "4"; + public const string SUCCESS = "2"; + } +} diff --git a/BWP.ABCClient/B2B/TrxType.cs b/BWP.ABCClient/B2B/TrxType.cs new file mode 100644 index 0000000..cd55861 --- /dev/null +++ b/BWP.ABCClient/B2B/TrxType.cs @@ -0,0 +1,17 @@ +namespace BWP.ABCClient.B2B +{ + public static class TrxType + { + public static string DownloadTrnx = "DownloadTrnx"; + public static string Freeze = "Freeze"; + public static string FreezeResult = "FreezeResult"; + public static string FreezeTransfer = "FreezeTransfer"; + public static string FundTransfer = "FundTransfer"; + public static string PartFreezeTransfer = "PartFreezeTransfer"; + public static string PartUnFreeze = "PartUnFreeze"; + public static string PayFee = "PayFee"; + public static string QueryTrnx = "QueryTrnx"; + public static string UnFreeze = "UnFreeze"; + public static string UnFreezeResult = "UnFreezeResult"; + } +} diff --git a/BWP.ABCClient/B2C/OnlineRemits/OnlineRemitBaseRequest.cs b/BWP.ABCClient/B2C/OnlineRemits/OnlineRemitBaseRequest.cs new file mode 100644 index 0000000..24c1075 --- /dev/null +++ b/BWP.ABCClient/B2C/OnlineRemits/OnlineRemitBaseRequest.cs @@ -0,0 +1,232 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using BWP.ABCClient.Businesses; +using System.Security.Cryptography; +using System.Net; +using System.IO; +using System.Security; + +namespace BWP.ABCClient.B2C.OnlineRemits +{ + public abstract class OnlineRemitBaseRequest + { + protected abstract void checkRequest(); + + protected abstract XmlDocument createMessage(); + + XmlDocument verifySign(XmlDocument aMessage) + { + XmlDocument document = aMessage.GetValue("Message"); + if (document == null) + { + throw new OnlineRemitException("1301", "网上支付平台的响应报文不完整", "无[Message]段!"); + } + if (aMessage.GetValueNoNull("Signature-Algorithm") == null) + { + throw new OnlineRemitException("1301", "网上支付平台的响应报文不完整", "无[Signature-Algorithm]段!"); + } + string data = aMessage.GetValueNoNull("Signature"); + if (data == null) + { + throw new OnlineRemitException("1301", "网上支付平台的响应报文不完整", "无[Signature]段!"); + } + byte[] rgbSignature = Convert.FromBase64String(data); + try + { + SHA1Managed managed = new SHA1Managed(); + byte[] rgbHash = managed.ComputeHash(Encoding.GetEncoding("gb2312").GetBytes(document.ToString())); + RSACryptoServiceProvider provider = new RSACryptoServiceProvider(new CspParameters { Flags = CspProviderFlags.UseMachineKeyStore }); + //RSAParameters parameters = TrustpayCertificate.GetPublicKey().ExportParameters(); + RSAParameters parameters = new RSAParameters() { Exponent = OnlineRemitConfig.GetCertificate().GetPublicKey() }; + provider.ImportParameters(parameters); + bool flag = provider.VerifyHash(rgbHash, CryptoConfig.MapNameToOID("SHA1"), rgbSignature); + managed.Clear(); + provider.Clear(); + if (!flag) + { + throw new OnlineRemitException("1302", "网上支付平台的响应报文签名验证失败"); + } + } + catch (OnlineRemitException exception) + { + throw exception; + } + catch (Exception exception2) + { + throw new OnlineRemitException("1302", "网上支付平台的响应报文签名验证失败 - " + exception2.Message); + } + return document; + } + + string ECMerchantType = "B2C"; + /// + /// 商户编号 + /// + public string MerchantID { get; set; } + + + XmlDocument sendMessage(XmlDocument aMessage) + { + Exception exception; + string s = "" + aMessage.ToString() + ""; + int length = 0; + try + { + length = Encoding.UTF8.GetBytes(s).Length; + } + catch (Exception exception1) + { + exception = exception1; + throw new OnlineRemitException("1999", "系统发生无法预期的错误", exception.Message); + } + HttpWebRequest request = null; + BufferedStream stream = null; + HttpWebResponse response = null; + string aXMLString = ""; + XmlDocument document = null; + string str3 = OnlineRemitConfig.TrustPayConnectMethod + "://" + OnlineRemitConfig.TrustPayServerName; + if ((OnlineRemitConfig.TrustPayConnectMethod.Equals("https") && (OnlineRemitConfig.TrustPayServerPort != 0x1bb)) || (OnlineRemitConfig.TrustPayConnectMethod.Equals("http") && (OnlineRemitConfig.TrustPayServerPort != 80))) + { + str3 = str3 + ":" + OnlineRemitConfig.TrustPayServerPort; + } + try + { + request = (HttpWebRequest)WebRequest.Create(str3 + OnlineRemitConfig.TrustPayTrxURL); + request.Method = "POST"; + request.ProtocolVersion = HttpVersion.Version10; + request.ContentType = "application/x-www-form-urlencoded"; + //this.iLogWriter.log("成功"); + //this.iLogWriter.logNewLine("提交交易报文:"); + byte[] bytes = Encoding.UTF8.GetBytes(s); + request.ContentLength = bytes.Length; + stream = new BufferedStream(request.GetRequestStream()); + if (!stream.CanWrite) + { + throw new OnlineRemitException("1201", "无法连线网上支付平台", "无法连线到[" + str3 + "]"); + } + stream.Write(bytes, 0, bytes.Length); + stream.Flush(); + stream.Close(); + //this.iLogWriter.log("成功"); + //this.iLogWriter.logNewLine("等待网上支付平台返回交易结果:"); + response = (HttpWebResponse)request.GetResponse(); + Stream responseStream = response.GetResponseStream(); + Encoding encoding = Encoding.GetEncoding("gb2312"); + StreamReader reader = new StreamReader(responseStream, encoding); + string str4 = null; + while ((str4 = reader.ReadLine()) != null) + { + aXMLString = aXMLString + str4; + if (str4.IndexOf("") != -1) + { + break; + } + } + response.Close(); + //this.iLogWriter.log("成功"); + //this.iLogWriter.logNewLine("返回报文:"); + //this.iLogWriter.log("\n" + aXMLString.ToString()); + if (response.StatusCode != HttpStatusCode.OK) + { + throw new OnlineRemitException("1206", "网上支付平台服务暂时停止"); + } + document = new XmlDocument(aXMLString).GetValue("MSG"); + if (document == null) + { + throw new OnlineRemitException("1205", "无法辨识网上支付平台的响应报文", "无[MSG]段!"); + } + } + catch (WebException exception2) + { + //this.iLogWriter.logNewLine(exception2.ToString()); + throw new OnlineRemitException("1201", "无法连线网上支付平台", "无法连线到[" + str3 + "], " + exception2.Message); + } + catch (IOException exception3) + { + //this.iLogWriter.logNewLine(exception3.ToString()); + throw new OnlineRemitException("1202", "提交交易时发生网络错误", "连线中断!"); + } + catch (SecurityException exception4) + { + //this.iLogWriter.logNewLine(exception4.ToString()); + throw new OnlineRemitException("1201", "无法连线网上支付平台", "进程权限太低!"); + } + catch (OnlineRemitException exception5) + { + throw exception5; + } + catch (Exception exception10) + { + exception = exception10; + //this.iLogWriter.logNewLine(exception.StackTrace); + throw new OnlineRemitException("1201", "无法连线网上支付平台", exception.StackTrace); + } + finally + { + if (stream != null) + { + try + { + stream.Close(); + } + catch (Exception) + { + } + } + if (response != null) + { + try + { + response.Close(); + } + catch (Exception) + { + } + } + } + return document; + } + + /// + /// 在消息头部加上商户信息 + /// + /// + /// + XmlDocument composeRequestMessage(XmlDocument aMessage) + { + return new XmlDocument("" + ECMerchantType + "" + MerchantID + "" + aMessage.ToString()); + } + + /// + /// 加入签名信息 + /// + /// + /// + XmlDocument fileSignMessage(XmlDocument aMessage) + { + RSACryptoServiceProvider provider = OnlineRemitConfig.GetMerchantKey(MerchantID); + byte[] rgbHash = new SHA1Managed().ComputeHash(Encoding.UTF8.GetBytes(aMessage.ToString())); + byte[] data = provider.SignHash(rgbHash, CryptoConfig.MapNameToOID("SHA1")); + string str = Convert.ToBase64String(data); + return new XmlDocument("" + aMessage.ToString() + "SHA1withRSA" + str + ""); + } + + protected XmlDocument Send() + { + checkRequest(); + var message = createMessage(); + + message = composeRequestMessage(message); + + message = fileSignMessage(message); + + message = sendMessage(message); + + message = verifySign(message); + + return message; + } + } +} diff --git a/BWP.ABCClient/B2C/OnlineRemits/OnlineRemitConfig.cs b/BWP.ABCClient/B2C/OnlineRemits/OnlineRemitConfig.cs new file mode 100644 index 0000000..1f09503 --- /dev/null +++ b/BWP.ABCClient/B2C/OnlineRemits/OnlineRemitConfig.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; + +namespace BWP.ABCClient.B2C.OnlineRemits +{ + public class OnlineRemitConfig + { + /// + /// 网上支付平台通讯方式(http / https) + /// + public static string TrustPayConnectMethod + { + get + { + return "https"; + } + } + + /// + /// 网上支付平台服务器名 + /// + public static string TrustPayServerName + { + get + { + return "www.95599.cn"; + } + } + + /// + /// 网上支付平台交易端口 + /// + public static int TrustPayServerPort + { + get + { + return 443; + } + } + + public static string TrustPayTrxURL + { + get + { + return "/b2c/trustpay/ReceiveMerchantTrxReqServlet"; + } + } + + /// + /// 得到商户的签名 + /// + /// + /// + internal static RSACryptoServiceProvider GetMerchantKey(string MerchantID) + { + throw new NotImplementedException(); + } + + /// + /// 得到农行公钥 + /// + /// + internal static X509Certificate2 GetCertificate() + { + throw new NotImplementedException(); + } + } +} diff --git a/BWP.ABCClient/B2C/OnlineRemits/OnlineRemitException.cs b/BWP.ABCClient/B2C/OnlineRemits/OnlineRemitException.cs new file mode 100644 index 0000000..13f96eb --- /dev/null +++ b/BWP.ABCClient/B2C/OnlineRemits/OnlineRemitException.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace BWP.ABCClient.B2C.OnlineRemits +{ + public class OnlineRemitException : Exception + { + public string Code { get; set; } + + public string DetailMessage { get; set; } + + + public OnlineRemitException(string code, string message, string detailMessage):base(string.Format("{0}:{1}",message,detailMessage)) + { + Code = code; + DetailMessage = detailMessage; + } + + public OnlineRemitException(string code, string message):this(code,message,"") + { + } + } +} diff --git a/BWP.ABCClient/B2C/OnlineRemits/OnlineRemitQueryResultRequest.cs b/BWP.ABCClient/B2C/OnlineRemits/OnlineRemitQueryResultRequest.cs new file mode 100644 index 0000000..75eb487 --- /dev/null +++ b/BWP.ABCClient/B2C/OnlineRemits/OnlineRemitQueryResultRequest.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using BWP.ABCClient.Businesses; +using System.Globalization; + +namespace BWP.ABCClient.B2C.OnlineRemits +{ + public class OnlineRemitQueryResultRequest:OnlineRemitBaseRequest + { + public string SerialNumber { get; set; } + + public string ReceiveAccount { get; set; } + + public DateTime? StartDate { get; set; } + + public DateTime? EndDate { get; set; } + + protected override void checkRequest() + { + if (string.IsNullOrEmpty(SerialNumber) && string.IsNullOrEmpty(ReceiveAccount)) + { + throw new OnlineRemitException("1100", "商户提交的交易资料不合法", "付款流水号和收款方账号同时为空"); + } + + if (StartDate == null) + { + throw new OnlineRemitException("1101", "商户提交的交易资料不合法", "起始日期格式不正确"); + } + + if (EndDate == null) + { + throw new OnlineRemitException("1101", "商户提交的交易资料不合法", "截止日期格式不正确"); + } + } + + protected override XmlDocument createMessage() + { + return new XmlDocument(new StringBuilder("") + .Append("") + .Append("OnlineRmtQueryResult") + .AppendFormat("{0}",SerialNumber) + .AppendFormat("{0}",ReceiveAccount) + .AppendFormat("{0}",FormatDate(StartDate.Value)) + .AppendFormat("{0}",FormatDate(EndDate.Value)) + .Append("").ToString()); + } + + private string FormatDate(DateTime date) + { + return date.ToString("yyyy/MM/dd", CultureInfo.InvariantCulture); + } + + + public OnlineRemitQueryResultResponse GetResponse() + { + var message = base.Send(); + + return new OnlineRemitQueryResultResponse(message); + } + } +} diff --git a/BWP.ABCClient/B2C/OnlineRemits/OnlineRemitQueryResultResponse.cs b/BWP.ABCClient/B2C/OnlineRemits/OnlineRemitQueryResultResponse.cs new file mode 100644 index 0000000..00b945f --- /dev/null +++ b/BWP.ABCClient/B2C/OnlineRemits/OnlineRemitQueryResultResponse.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using BWP.ABCClient.Businesses; + +namespace BWP.ABCClient.B2C.OnlineRemits +{ + public class OnlineRemitQueryResultResponse + { + private XmlDocument mMessage; + + public OnlineRemitQueryResultResponse(XmlDocument message) + { + mMessage = message; + } + } +} diff --git a/BWP.ABCClient/B2C/OnlineRemits/OnlineRemitRequest.cs b/BWP.ABCClient/B2C/OnlineRemits/OnlineRemitRequest.cs new file mode 100644 index 0000000..c41e55a --- /dev/null +++ b/BWP.ABCClient/B2C/OnlineRemits/OnlineRemitRequest.cs @@ -0,0 +1,169 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using BWP.ABCClient.Businesses; +using System.Security.Cryptography; +using System.Net; +using System.IO; +using System.Security; +using System.Security.Cryptography.X509Certificates; + +namespace BWP.ABCClient.B2C.OnlineRemits +{ + /// + /// 网付通,在线付款 + /// + public class OnlineRemitRequest:OnlineRemitBaseRequest + { + /// + /// 总笔数 + /// + public int TotalCount { get; set; } + + /// + /// 总金额 + /// + public double TotalAmount { get; set; } + + /// + /// 序号 + /// + public string SerialNumber { get; set; } + + /// + /// 备注 + /// + public string Remark { get; set; } + + /// + /// 0:无需操作员确认直接付款;1:需要操作员在商户服务系统确认后付款 + /// + public int CheckType { get; set; } + + List mDetails = new List(); + + public List Details + { + get + { + return mDetails; + } + } + + public OnlineRemitResponse GetResponse() + { + var message = base.Send(); + + OnlineRemitResponse response = new OnlineRemitResponse(message); + return response; + } + + protected override XmlDocument createMessage() + { + StringBuilder builder = new StringBuilder("").Append("").Append("").Append("OnlineRemit").Append("") + .Append("").Append(CheckType).Append("") + .Append("").Append(SerialNumber).Append("") + .Append("").Append(TotalCount).Append("") + .Append("").Append(TotalAmount).Append("") + .Append("").Append(Remark).Append(""); + builder.Append(""); + foreach (var detail in this.Details) + { + builder.Append("") + .Append("").Append(detail.NO).Append("") + .Append("").Append(detail.CardNo).Append("") + .Append("").Append(detail.CardName).Append("") + .Append("").Append(detail.Amount).Append("") + .Append("").Append(detail.Purpose).Append("") + .Append(""); + } + builder.Append("").Append(""); + return new XmlDocument(builder.ToString()); + } + + public static bool isValidAmount(double aAmount, int aExp) + { + bool flag = false; + if (aExp >= 0) + { + string str = aAmount.ToString(); + int index = str.IndexOf('.'); + if (index == -1) + { + return true; + } + if (index >= ((str.Length - aExp) - 1)) + { + flag = true; + } + } + return flag; + } + + protected override void checkRequest() + { + if (TotalCount != Details.Count) + { + throw new OnlineRemitException("1101", "商户提交的交易资料不合法", "付款总笔数与明细笔数不一致!"); + } + if (SerialNumber.Trim().Equals("")) + { + throw new OnlineRemitException("1101", "商户提交的交易资料不合法", "付款流水号不能为空!"); + } + if (TotalCount <= 0) + { + throw new OnlineRemitException("1101", "商户提交的交易资料不合法", "付款交易总笔数不能小于1笔"); + } + if (TotalCount > 100) + { + throw new OnlineRemitException("1101", "商户提交的交易资料不合法", "付款交易总笔数不能大于100笔"); + } + if (TotalAmount <= 0.0) + { + throw new OnlineRemitException("1101", "商户提交的交易资料不合法", "付款交易总金额不合法!"); + } + if (!(isValidAmount(TotalAmount, 2) && (TotalAmount > 0.0))) + { + throw new OnlineRemitException("1101", "商户提交的交易资料不合法", "付款交易总金额不合法!"); + } + + var noDic = new Dictionary(); + foreach (var detail in this.Details) + { + if (noDic.ContainsKey(detail.NO)) + { + throw new OnlineRemitException("1101", "商户提交的交易资料不合法", "批次内序号重复"); + } + else + { + noDic.Add(detail.NO, detail.NO); + } + if (detail.NO < 0 || detail.NO > 99999) + { + throw new OnlineRemitException("1101", "商户提交的交易资料不合法", "批次内序号需要在1至5位之间:" + detail.NO); + } + if (string.IsNullOrEmpty(detail.CardNo)) + { + throw new OnlineRemitException("1101", "商户提交的交易资料不合法", "付款明细卡号不能为空!"); + } + + if (string.IsNullOrEmpty(detail.CardName)) + { + throw new OnlineRemitException("1101", "商户提交的交易资料不合法", "付款明细收款人信息不能为空!"); + } + + if (!(isValidAmount(detail.Amount, 2) && (detail.Amount > 0.0))) + { + throw new OnlineRemitException("1101", "商户提交的交易资料不合法", "付款交易明细金额不合法:" + detail.Amount.ToString()); + } + + if (detail.Purpose.Length > 30) + { + throw new OnlineRemitException("1101", "商户提交的交易资料不合法", "明细内容不能超过30个字:" + detail.Purpose); + } + } + + } + } +} diff --git a/BWP.ABCClient/B2C/OnlineRemits/OnlineRemitRequest_Detail.cs b/BWP.ABCClient/B2C/OnlineRemits/OnlineRemitRequest_Detail.cs new file mode 100644 index 0000000..0251b5b --- /dev/null +++ b/BWP.ABCClient/B2C/OnlineRemits/OnlineRemitRequest_Detail.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace BWP.ABCClient.B2C.OnlineRemits +{ + public class OnlineRemitRequest_Detail + { + /// + /// 序号 + /// + /// + /// 格式化为3位 + /// + public int NO { get; set; } + + /// + /// 收款方账号 + /// + public string CardNo { get; set; } + + /// + /// 收款方户名 + /// + public string CardName { get; set; } + + /// + /// 金额 + /// + public double Amount { get; set; } + + /// + /// 用途 + /// + public string Purpose { get; set; } + } +} diff --git a/BWP.ABCClient/B2C/OnlineRemits/OnlineRemitResponse.cs b/BWP.ABCClient/B2C/OnlineRemits/OnlineRemitResponse.cs new file mode 100644 index 0000000..7dcc5e7 --- /dev/null +++ b/BWP.ABCClient/B2C/OnlineRemits/OnlineRemitResponse.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using BWP.ABCClient.Businesses; + +namespace BWP.ABCClient.B2C.OnlineRemits +{ + /// + /// 网付通返回 + /// + public class OnlineRemitResponse + { + public string ErrorMessage + { + get; + private set; + } + + public string ReturnCode + { + get; + private set; + } + + XmlDocument mMessage = new XmlDocument(""); + public const string RC_SUCCESS = "0000"; + + public OnlineRemitResponse(XmlDocument message) + { + mMessage = message; + } + + protected internal OnlineRemitResponse() + { + } + + public OnlineRemitResponse(string aReturnCode, string aErrorMessage) + { + ReturnCode = aReturnCode; + ErrorMessage = aErrorMessage; + } + + void init(XmlDocument aXMLDocument) + { + XmlDocument returnCode = aXMLDocument.GetValue("ReturnCode"); + if (returnCode == null) + { + throw new OnlineRemitException("1303", "无法辨识网上支付平台的交易结果", "无法取得[ReturnCode]!"); + } + ReturnCode = returnCode.ToString(); + if (!Success) + { + XmlDocument errorMessage = aXMLDocument.GetValue("ErrorMessage"); + if (errorMessage != null) + { + ErrorMessage = errorMessage.ToString(); + } + else + { + ErrorMessage = "未能返回错误信息"; + } + } + } + + public bool Success + { + get + { + return RC_SUCCESS.Equals(ReturnCode); + } + } + } +} diff --git a/BWP.ABCClient/B2C/OrderContent.cs b/BWP.ABCClient/B2C/OrderContent.cs new file mode 100644 index 0000000..45c2035 --- /dev/null +++ b/BWP.ABCClient/B2C/OrderContent.cs @@ -0,0 +1,18 @@ +namespace BWP.ABCClient.B2C +{ + public class OrderContent + { + public string OrderNo { get; set; } + public string OrderAmount { get; set; } + public string OrderDesc { get; set; } + public string OrderDate { get; set; } + public string OrderTime { get; set; } + public string OrderURL { get; set; } + public string PayAmount { get; set; } + public string RefundAmount { get; set; } + public string OrderStatus { get; set; } + public OrderItem[] OrderItems { get; set; } + } + + public class OrderItem { } +} \ No newline at end of file diff --git a/BWP.ABCClient/B2C/PaymentRequest.cs b/BWP.ABCClient/B2C/PaymentRequest.cs new file mode 100644 index 0000000..cb230f7 --- /dev/null +++ b/BWP.ABCClient/B2C/PaymentRequest.cs @@ -0,0 +1,64 @@ +using System.Xml.Serialization; +using BWP.ABCClient.Common; + +namespace BWP.ABCClient.B2C +{ + [XmlRoot(ElementName = "MSG")] + public class PaymentRequest : MessageBase + { + public MessageContent Message { get; set; } + + public static PaymentRequest New() + { + return new PaymentRequest { + Message = new MessageContent { + Merchant = new MerchantContent { + ECMerchantType = "B2C", + MerchantID = string.Empty + }, + TrxRequest = new TrxRequestContent { + TrxType = TrxType.PayReq, + Order = new OrderContent { + OrderNo = string.Empty, + OrderAmount = string.Empty, + OrderDesc = string.Empty, + OrderDate = string.Empty, + OrderTime = string.Empty, + OrderURL = string.Empty, + PayAmount = null, + RefundAmount = null, + OrderStatus = null, + OrderItems = new OrderItem[]{} + }, + ProductType = "2", + PaymentType = "1", + NotifyType = "1", + ResultNotifyURL = string.Empty, + MerchantRemarks = string.Empty, + PaymentLinkType = "1" + } + }, + SignatureAlgorithm = "SHA1withRSA", + Signature = string.Empty + }; + } + + public class MessageContent + { + public MerchantContent Merchant { get; set; } + public TrxRequestContent TrxRequest { get; set; } + } + + public class TrxRequestContent + { + public string TrxType { get; set; } + public OrderContent Order { get; set; } + public string ProductType { get; set; } + public string PaymentType { get; set; } + public string NotifyType { get; set; } + public string ResultNotifyURL { get; set; } + public string MerchantRemarks { get; set; } + public string PaymentLinkType { get; set; } + } + } +} diff --git a/BWP.ABCClient/B2C/PaymentResponse.cs b/BWP.ABCClient/B2C/PaymentResponse.cs new file mode 100644 index 0000000..0906917 --- /dev/null +++ b/BWP.ABCClient/B2C/PaymentResponse.cs @@ -0,0 +1,32 @@ +using System.Xml.Serialization; +using BWP.ABCClient.Common; + +namespace BWP.ABCClient.B2C +{ + [XmlRoot(ElementName = "MSG")] + public class PaymentResponse : MessageBase + { + public MessageContent Message { get; set; } + + public class MessageContent + { + public MerchantContent Merchant { get; set; } + public TrxResponseContent TrxResponse { get; set; } + } + + public class TrxResponseContent + { + public string ReturnCode { get; set; } + + public string ErrorMessage { get; set; } + + public string TrxType { get; set; } + + public string OrderNo { get; set; } + + public string PaymentURL { get; set; } + + public string OrderAmount { get; set; } + } + } +} diff --git a/BWP.ABCClient/B2C/QueryOrderRequest.cs b/BWP.ABCClient/B2C/QueryOrderRequest.cs new file mode 100644 index 0000000..fddc69e --- /dev/null +++ b/BWP.ABCClient/B2C/QueryOrderRequest.cs @@ -0,0 +1,43 @@ +using System.Xml.Serialization; +using BWP.ABCClient.Common; + +namespace BWP.ABCClient.B2C +{ + [XmlRoot(ElementName = "MSG")] + public class QueryOrderRequest : MessageBase + { + public MessageContent Message { get; set; } + + public static QueryOrderRequest New() + { + return new QueryOrderRequest { + Message = new MessageContent { + Merchant = new MerchantContent { + ECMerchantType = "B2C", + MerchantID = string.Empty + }, + TrxRequest = new TrxRequestContent { + TrxType = TrxType.Query, + OrderNo = string.Empty, + QueryDetail = "false" + } + }, + SignatureAlgorithm = "SHA1withRSA", + Signature = string.Empty + }; + } + + public class MessageContent + { + public MerchantContent Merchant { get; set; } + public TrxRequestContent TrxRequest { get; set; } + } + + public class TrxRequestContent + { + public string TrxType { get; set; } + public string OrderNo { get; set; } + public string QueryDetail { get; set; } + } + } +} diff --git a/BWP.ABCClient/B2C/QueryOrderResponse.cs b/BWP.ABCClient/B2C/QueryOrderResponse.cs new file mode 100644 index 0000000..cc5f616 --- /dev/null +++ b/BWP.ABCClient/B2C/QueryOrderResponse.cs @@ -0,0 +1,26 @@ +using System.Xml.Serialization; +using BWP.ABCClient.Common; + +namespace BWP.ABCClient.B2C +{ + [XmlRoot(ElementName = "MSG")] + public class QueryOrderResponse : MessageBase + { + public MessageContent Message { get; set; } + + public class MessageContent + { + public MerchantContent Merchant { get; set; } + public TrxResponseContent TrxResponse { get; set; } + } + + public class TrxResponseContent + { + public string ReturnCode { get; set; } + + public string ErrorMessage { get; set; } + + public OrderContent Order { get; set; } + } + } +} diff --git a/BWP.ABCClient/B2C/SettleRequest.cs b/BWP.ABCClient/B2C/SettleRequest.cs new file mode 100644 index 0000000..78d467e --- /dev/null +++ b/BWP.ABCClient/B2C/SettleRequest.cs @@ -0,0 +1,50 @@ +using System; +using System.Xml.Serialization; +using BWP.ABCClient.Common; + +namespace BWP.ABCClient.B2C +{ + [XmlRoot(ElementName = "MSG")] + public class SettleRequest : MessageBase + { + public MessageContent Message { get; set; } + + public static SettleRequest New() + { + return new SettleRequest { + Message = new MessageContent { + Merchant = new MerchantContent { + ECMerchantType = "B2C", + MerchantID = string.Empty + }, + TrxRequest = new TrxRequestContent { + TrxType = TrxType.Settle, + SettleDate = string.Empty, + SettleStartHour = "false", + SettleEndHour = string.Empty, + SettleType = string.Empty, + ZIP = "YES" + } + }, + SignatureAlgorithm = "SHA1withRSA", + Signature = string.Empty + }; + } + + public class MessageContent + { + public MerchantContent Merchant { get; set; } + public TrxRequestContent TrxRequest { get; set; } + } + + public class TrxRequestContent + { + public string TrxType { get; set; } + public string SettleDate { get; set; } + public string SettleStartHour { get; set; } + public string SettleEndHour { get; set; } + public string SettleType { get; set; } + public string ZIP { get; set; } + } + } +} diff --git a/BWP.ABCClient/B2C/SettleResponse.cs b/BWP.ABCClient/B2C/SettleResponse.cs new file mode 100644 index 0000000..6846314 --- /dev/null +++ b/BWP.ABCClient/B2C/SettleResponse.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using System.Xml.Serialization; +using BWP.ABCClient.Common; + +namespace BWP.ABCClient.B2C +{ + [XmlRoot(ElementName = "MSG")] + public class SettleResponse : MessageBase + { + public MessageContent Message { get; set; } + + public class MessageContent + { + public MerchantContent Merchant { get; set; } + public TrxResponseContent TrxResponse { get; set; } + } + + public class TrxResponseContent + { + public string ReturnCode { get; set; } + + public string ErrorMessage { get; set; } + + private string _zipDetailRecords; + + public string ZIPDetailRecords + { + get { return _zipDetailRecords; } + set + { + _zipDetailRecords = value; + SetDetailRecords(value); + } + } + + private void SetDetailRecords(string value) + { + if (string.IsNullOrEmpty(value)) { + return; + } + string innerXml = MsgUtil.DeCompress(value); + string xml = "" + innerXml + ""; + var records = MsgUtil.GetMultiInnerXml(xml, "//Record"); + var recordList = new List(records.Length); + foreach (var record in records) { + var recordCols = record.Split(new[] {','}, StringSplitOptions.None); + recordList.Add(new Record { + Sign = recordCols[0], + OrderNumber = recordCols[1], + Amount = recordCols[2], + DaySeqNo = recordCols[3], + Date = recordCols[4] + }); + } + DetailRecords = recordList.ToArray(); + } + + public Record[] DetailRecords { get; set; } + } + } + + public class Record + { + public string Sign { get; set; } + public string OrderNumber { get; set; } + public string Amount { get; set; } + public string DaySeqNo { get; set; } + public string Date { get; set; } + } +} \ No newline at end of file diff --git a/BWP.ABCClient/B2C/TrxType.cs b/BWP.ABCClient/B2C/TrxType.cs new file mode 100644 index 0000000..623989a --- /dev/null +++ b/BWP.ABCClient/B2C/TrxType.cs @@ -0,0 +1,16 @@ +namespace BWP.ABCClient.B2C +{ + public class TrxType + { + public const string FundPayReq = "FundPayReq"; + public const string MobilePayReq = "MobilePayReq"; + public const string MobilePayReg = "MobilePayReg"; + public const string PayReq = "PayReq"; + public const string PayResult = "PayResult"; + public const string Query = "Query"; + public const string Refund = "Refund"; + public const string Settle = "Settle"; + public const string VoidPay = "VoidPay"; + public const string VoidRefund = "VoidRefund"; + } +} diff --git a/BWP.ABCClient/BWP.ABCClient.csproj b/BWP.ABCClient/BWP.ABCClient.csproj new file mode 100644 index 0000000..1f677ba --- /dev/null +++ b/BWP.ABCClient/BWP.ABCClient.csproj @@ -0,0 +1,151 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {22FEFA9B-1D58-4E8A-A1FF-49CEED62C355} + Library + Properties + BWP.ABCClient + BWP.ABCClient + v4.0 + 512 + BwpApp + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\..\..\tsref\Debug\Forks.Utils.dll + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + ..\..\..\tsref\Debug\Wpf.dll + False + + + False + ..\..\..\tsref\Debug\Wpf.System.dll + False + + + + + tslib_version.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/BWP.ABCClient/Businesses/B2BDealQuery.cs b/BWP.ABCClient/Businesses/B2BDealQuery.cs new file mode 100644 index 0000000..2819d2d --- /dev/null +++ b/BWP.ABCClient/Businesses/B2BDealQuery.cs @@ -0,0 +1,58 @@ +using System; +using BWP.ABCClient.B2B; +using BWP.ABCClient.Businesses.Type; +using BWP.ABCClient.Exceptions; + +namespace BWP.ABCClient.Businesses +{ + public class B2BDealQuery : BusinessBase + { + public B2BDealQuery() : base(BusinessTypes.B2B) + { + RequestObj = QueryTrnxRequest.New(); + } + + public string MerchantTrnxNo { get; set; } + + public DealStatusEnum DealStatus { get; set; } + + public bool DealIsSuccess { get; set; } + + protected override void AfterRequest() + { + Log.AddNewLine(string.Format("交易结果:[{0}]", ResponseObj.Message.TrxResponse.ReturnCode)); + Log.AddNewLine(string.Format("返回信息:[{0}]", ResponseObj.Message.TrxResponse.ErrorMessage)); + if (ResponseObj.Message.TrxResponse.ReturnCode != "0000") { + Log.Commit(); + throw new ResponseException(ResponseObj.Message.TrxResponse.ReturnCode, ResponseObj.Message.TrxResponse.ErrorMessage); + } + Log.Commit(); + DealIsSuccess = ResponseObj.Message.TrxResponse.TrnxStatus == ((int)DealStatusEnum.交易成功).ToString(); + DealStatusEnum dealStatus; + if (DealStatusEnum.TryParse(ResponseObj.Message.TrxResponse.TrnxStatus, out dealStatus)) { + DealStatus = dealStatus; + } + } + + protected override void BeforeRequest() + { + if (string.IsNullOrEmpty(MerchantTrnxNo)) { + throw new Exception("查询的交易号不能为空,B2BGathering.MerchantTrnxNo"); + } + RequestObj.Message.Merchant.MerchantID = MerchantID; + RequestObj.Message.TrxRequest.MerchantTrnxNo = MerchantTrnxNo; + RequestObj.Message.TrxRequest.MerchantRemarks = Remarks; + } + } + + public enum DealStatusEnum + { + 交易作废 = 5, + 复核中 = 1, + 交易失败 = 3, + 交易无响应 = 9, + 原始请求 = 0, + 交易被驳回 = 4, + 交易成功 = 2 + } +} \ No newline at end of file diff --git a/BWP.ABCClient/Businesses/B2BReceivingPayment.cs b/BWP.ABCClient/Businesses/B2BReceivingPayment.cs new file mode 100644 index 0000000..24f2bc6 --- /dev/null +++ b/BWP.ABCClient/Businesses/B2BReceivingPayment.cs @@ -0,0 +1,62 @@ +using System; +using System.Globalization; +using BWP.ABCClient.B2B; +using BWP.ABCClient.Businesses.Type; +using BWP.ABCClient.Exceptions; + +namespace BWP.ABCClient.Businesses +{ + public class B2BReceivingPayment : BusinessBase + { + public B2BReceivingPayment() : base(BusinessTypes.B2B) + { + RequestObj = FundTransferRequest.New(); + } + + public string AccountNo { get; set; } + public string AccountName { get; set; } + public string AccountBank { get; set; } + public string Operator { get; set; } + + public string PaymentUrl { get; set; } + + protected override void AfterRequest() + { + Log.AddNewLine(string.Format("交易结果:[{0}]", ResponseObj.Message.TrxResponse.ReturnCode)); + Log.AddNewLine(string.Format("返回信息:[{0}]", ResponseObj.Message.TrxResponse.ErrorMessage)); + if (ResponseObj.Message.TrxResponse.ReturnCode != "0000") { + Log.Commit(); + throw new ResponseException(ResponseObj.Message.TrxResponse.ReturnCode, ResponseObj.Message.TrxResponse.ErrorMessage); + } + Log.Commit(); + PaymentUrl = ResponseObj.Message.TrxResponse.PaymentURL; + } + + protected override void BeforeRequest() + { + if (string.IsNullOrEmpty(SequenceNo)) { + throw new Exception("交易流水号不能为空,SequenceNo"); + } + if (string.IsNullOrEmpty(ResultNotifyURL)) { + throw new Exception("接受交易结果信息Url不能为空,ResultNotifyURL"); + } + if (AccountNo == null) { + throw new Exception("收款方帐号不能为空,AccountNo"); + } + if (Date == null) { + throw new Exception("交易日期不能为空,Date"); + } + RequestObj.Message.Merchant.MerchantID = MerchantID; + RequestObj.Message.TrxRequest.AccountDB.AccountDBNo = AccountNo; + RequestObj.Message.TrxRequest.AccountDB.AccountDBName = AccountName; + RequestObj.Message.TrxRequest.AccountDB.AccountDBBank = AccountBank; + RequestObj.Message.TrxRequest.TrnxDate = Date.Value.ToString("yyyy/MM/dd", DateTimeFormatInfo.InvariantInfo); + RequestObj.Message.TrxRequest.TrnxTime = Date.Value.ToString("HH:MM:ss", DateTimeFormatInfo.InvariantInfo); + RequestObj.Message.TrxRequest.TrnxAmount = Amount.ToString("f2"); + RequestObj.Message.TrxRequest.MerchantTrnxNo = SequenceNo; + RequestObj.Message.TrxRequest.TrnxInfo.TrnxOpr = Operator; + RequestObj.Message.TrxRequest.ResultNotifyURL = ResultNotifyURL; + RequestObj.Message.TrxRequest.MerchantRemarks = Remarks; + } + } +} \ No newline at end of file diff --git a/BWP.ABCClient/Businesses/B2COrderQuery.cs b/BWP.ABCClient/Businesses/B2COrderQuery.cs new file mode 100644 index 0000000..2d89d17 --- /dev/null +++ b/BWP.ABCClient/Businesses/B2COrderQuery.cs @@ -0,0 +1,53 @@ +using System; +using BWP.ABCClient.B2C; +using BWP.ABCClient.Businesses.Type; +using BWP.ABCClient.Exceptions; + +namespace BWP.ABCClient.Businesses +{ + public class B2COrderQuery : BusinessBase + { + public B2COrderQuery() + : base(BusinessTypes.B2C) + { + RequestObj = QueryOrderRequest.New(); + } + + public string OrderStatus { get; set; } + + public bool OrderIsFinished { get; set; } + + protected override void AfterRequest() + { + Log.AddNewLine(string.Format("交易结果:[{0}]", ResponseObj.Message.TrxResponse.ReturnCode)); + Log.AddNewLine(string.Format("返回信息:[{0}]", ResponseObj.Message.TrxResponse.ErrorMessage)); + if (ResponseObj.Message.TrxResponse.ReturnCode != "0000") { + Log.Commit(); + throw new ResponseException(ResponseObj.Message.TrxResponse.ReturnCode, ResponseObj.Message.TrxResponse.ErrorMessage); + } + Log.Commit(); + OrderStatus = ResponseObj.Message.TrxResponse.Order.OrderStatus; + OrderIsFinished = OrderStatus == OrderStatusEnum.PAY || OrderStatus == OrderStatusEnum.SETTLED; + } + + protected override void BeforeRequest() + { + if (string.IsNullOrEmpty(SequenceNo)) { + throw new Exception("查询的交易号不能为空,SequenceNo"); + } + RequestObj.Message.Merchant.MerchantID = MerchantID; + RequestObj.Message.TrxRequest.OrderNo = SequenceNo; + } + } + + public class OrderStatusEnum + { + public const string CANCEL = "00"; + public const string ISSUE = "99"; + public const string NEW = "01"; + public const string PAY = "03"; + public const string REFUND = "05"; + public const string SETTLED = "04"; + public const string WAIT = "02"; + } +} \ No newline at end of file diff --git a/BWP.ABCClient/Businesses/B2CReceivingPayment.cs b/BWP.ABCClient/Businesses/B2CReceivingPayment.cs new file mode 100644 index 0000000..26fc99f --- /dev/null +++ b/BWP.ABCClient/Businesses/B2CReceivingPayment.cs @@ -0,0 +1,56 @@ +using System; +using System.Globalization; +using BWP.ABCClient.B2C; +using BWP.ABCClient.Businesses.Type; +using BWP.ABCClient.Exceptions; + +namespace BWP.ABCClient.Businesses +{ + public class B2CReceivingPayment : BusinessBase + { + public B2CReceivingPayment() : base(BusinessTypes.B2C) + { + RequestObj = PaymentRequest.New(); + } + + public string OrderUrl { get; set; } + + public string OrderRemarks { get; set; } + + public string PaymentUrl { get; set; } + + protected override void AfterRequest() + { + Log.AddNewLine(string.Format("交易结果:[{0}]", ResponseObj.Message.TrxResponse.ReturnCode)); + Log.AddNewLine(string.Format("返回信息:[{0}]", ResponseObj.Message.TrxResponse.ErrorMessage)); + if (ResponseObj.Message.TrxResponse.ReturnCode != "0000") { + Log.Commit(); + throw new ResponseException(ResponseObj.Message.TrxResponse.ReturnCode, ResponseObj.Message.TrxResponse.ErrorMessage); + } + Log.Commit(); + PaymentUrl = ResponseObj.Message.TrxResponse.PaymentURL; + } + + protected override void BeforeRequest() + { + if (string.IsNullOrEmpty(SequenceNo)) { + throw new Exception("交易流水号不能为空,SequenceNo"); + } + if (string.IsNullOrEmpty(ResultNotifyURL)) { + throw new Exception("接受交易结果信息Url不能为空,ResultNotifyURL"); + } + if (Date == null) { + throw new Exception("交易日期不能为空,Date"); + } + RequestObj.Message.Merchant.MerchantID = MerchantID; + RequestObj.Message.TrxRequest.Order.OrderNo = SequenceNo; + RequestObj.Message.TrxRequest.Order.OrderAmount = Amount.ToString("f2"); + RequestObj.Message.TrxRequest.Order.OrderDesc = OrderRemarks; + RequestObj.Message.TrxRequest.Order.OrderDate = Date.Value.ToString("yyyy/MM/dd", DateTimeFormatInfo.InvariantInfo); + RequestObj.Message.TrxRequest.Order.OrderTime = Date.Value.ToString("HH:MM:ss", DateTimeFormatInfo.InvariantInfo); + RequestObj.Message.TrxRequest.Order.OrderURL = OrderUrl; + RequestObj.Message.TrxRequest.ResultNotifyURL = ResultNotifyURL; + RequestObj.Message.TrxRequest.MerchantRemarks = Remarks; + } + } +} \ No newline at end of file diff --git a/BWP.ABCClient/Businesses/B2CSettleByDate.cs b/BWP.ABCClient/Businesses/B2CSettleByDate.cs new file mode 100644 index 0000000..dc142de --- /dev/null +++ b/BWP.ABCClient/Businesses/B2CSettleByDate.cs @@ -0,0 +1,50 @@ +using System; +using System.Globalization; +using BWP.ABCClient.B2C; +using BWP.ABCClient.Businesses.Type; +using BWP.ABCClient.Exceptions; + +namespace BWP.ABCClient.Businesses +{ + public class B2CSettleByDate : BusinessBase + { + public B2CSettleByDate() + : base(BusinessTypes.B2C) + { + RequestObj = SettleRequest.New(); + } + + public Record[] Records { get; set; } + public string RecordsHexString { get; set; } + + protected override void AfterRequest() + { + Log.AddNewLine(string.Format("交易结果:[{0}]", ResponseObj.Message.TrxResponse.ReturnCode)); + Log.AddNewLine(string.Format("返回信息:[{0}]", ResponseObj.Message.TrxResponse.ErrorMessage)); + if (ResponseObj.Message.TrxResponse.ReturnCode != "0000") { + Log.Commit(); + throw new ResponseException(ResponseObj.Message.TrxResponse.ReturnCode, ResponseObj.Message.TrxResponse.ErrorMessage); + } + Log.Commit(); + Records = ResponseObj.Message.TrxResponse.DetailRecords; + RecordsHexString = ResponseObj.Message.TrxResponse.ZIPDetailRecords; + } + + protected override void BeforeRequest() + { + if (Date == null) { + throw new Exception("查询的日期不能为空,Date"); + } + RequestObj.Message.Merchant.MerchantID = MerchantID; + RequestObj.Message.TrxRequest.SettleDate = Date.Value.ToString("yyyy/MM/dd", CultureInfo.InvariantCulture); + RequestObj.Message.TrxRequest.SettleType = SettleType.TRX; + } + } + + public class SettleType + { + public const string SETTLE = "SETTLE"; + public const string TRX = "TRX"; + public const string TRX_BYHOUR = "TRXBYHOUR"; + } +} \ No newline at end of file diff --git a/BWP.ABCClient/Businesses/BusinessBase.cs b/BWP.ABCClient/Businesses/BusinessBase.cs new file mode 100644 index 0000000..cad85cb --- /dev/null +++ b/BWP.ABCClient/Businesses/BusinessBase.cs @@ -0,0 +1,111 @@ +using System; +using System.Text; +using BWP.ABCClient.Businesses.Type; +using BWP.ABCClient.Common; +using BWP.ABCClient.Exceptions; + +namespace BWP.ABCClient.Businesses +{ + public class BusinessBase + where T : MessageBase + where T1 : MessageBase + { + private readonly IBusinessType _businessType; + public string MerchantID { get; set; } + + public string RequestUrl { get; set; } + + public bool IsTestState { get; set; } + + public T RequestObj { get; set; } + + public T1 ResponseObj { get; set; } + + public DateTime? Date { get; set; } + public string SequenceNo { get; set; } + public decimal Amount { get; set; } + public string Remarks { get; set; } + public string ResultNotifyURL { get; set; } + + protected AbcLog Log; + + public BusinessBase(IBusinessType businessInfo) + { + _businessType = businessInfo; + Log = new AbcLog(businessInfo.Name); + } + + public virtual void Request() + { + + + if (RequestObj == null) { + throw new Exception("RequestObj不能为空"); + } + if (string.IsNullOrEmpty(RequestUrl)) { + throw new Exception("RequestUrl不能为空"); + } + if (string.IsNullOrEmpty(MerchantID)) { + throw new Exception("MerchantID不能为空"); + } + + BeforeRequest(); + RSAService.Sign(RequestObj, MerchantID, _businessType.SignEncoding, _businessType.IsMarket); + var request = new Request { + RequestEncoding = _businessType.RequestEncoding, + ResponseEncoding = _businessType.ResponseEncoding, + Url = RequestUrl, + Message = SendXml + }; + Log.AddNewLine(string.Format("{0}交易开始:==========================", string.IsNullOrEmpty(SequenceNo) ? string.Empty : "流水号[" + SequenceNo + "]")); + if (_businessType.CompressEnabled) { + Log.AddNewLine("压缩前的报文"); + Log.AddNewLine(MsgUtil.MsgToBareXml(RequestObj)); + } + + Log.AddNewLine("提交的报文:"); + Log.AddNewLine(request.Message); + Log.AddNewLine(string.Format("连线网上支付平台:[{0}]", RequestUrl)); + string result = request.Run(); + string receivedXml = GetReceivedXml(result); + Log.AddNewLine("返回报文:"); + Log.AddNewLine(receivedXml); + Verify(receivedXml); + ResponseObj = MsgUtil.ParseXmlToMsg(receivedXml); + AfterRequest(); + } + + protected void Verify(string receivedXml) + { + if (!RSAService.Verify(receivedXml, _businessType.VerifyEncoding, _businessType.IsMarket, IsTestState)) { + Log.AddNewLine("网上支付平台的响应报文签名验证失败"); + Log.Commit(); + throw new RSAException("网上支付平台的响应报文签名验证失败"); + } + } + + protected virtual void BeforeRequest() { } + + protected virtual void AfterRequest() { } + + private string GetReceivedXml(string result) + { + if (_businessType.CompressEnabled) { + result = MsgUtil.DeCompress(result); + } + return result; + } + + public string SendXml + { + get + { + var result = MsgUtil.MsgToBareXml(RequestObj); + if (_businessType.CompressEnabled) { + result = MsgUtil.Compress(result); + } + return result; + } + } + } +} \ No newline at end of file diff --git a/BWP.ABCClient/Businesses/IReturnedResult.cs b/BWP.ABCClient/Businesses/IReturnedResult.cs new file mode 100644 index 0000000..45e7f90 --- /dev/null +++ b/BWP.ABCClient/Businesses/IReturnedResult.cs @@ -0,0 +1,15 @@ +namespace BWP.ABCClient.Businesses +{ + public interface IReturnedResult + { + bool IsValid { get; set; } + + string ReturnCode { get; set; } + + string ErrorMessage { get; set; } + + bool IsSuccess { get; set; } + + string SequenceNo { get; set; } + } +} \ No newline at end of file diff --git a/BWP.ABCClient/Businesses/MarketOrderApply.cs b/BWP.ABCClient/Businesses/MarketOrderApply.cs new file mode 100644 index 0000000..0a6722d --- /dev/null +++ b/BWP.ABCClient/Businesses/MarketOrderApply.cs @@ -0,0 +1,65 @@ +using System; +using BWP.ABCClient.Businesses.Type; +using BWP.ABCClient.Exceptions; +using BWP.ABCClient.Market; + +namespace BWP.ABCClient.Businesses +{ + public class MarketOrderApply : BusinessBase + { + public MarketOrderApply() + : base(BusinessTypes.Market) + { + RequestObj = OrderApplyRequest.New(); + } + + public string BuyerCustomer { get; set; } + + public string BuyerCustName { get; set; } + + public string SalerCustomer { get; set; } + + public string SalerCustName { get; set; } + + public string PaymentUrl { get; set; } + + protected override void AfterRequest() + { + var errorMessage = ResponseObj.Message.Control.ReturnMessage ?? string.Empty ; + Log.AddNewLine(string.Format("交易结果:[{0}]", ResponseObj.Message.Control.ReturnCode)); + Log.AddNewLine(string.Format("返回信息:[{0}]", errorMessage)); + Log.Commit(); + if (ResponseObj.Message.Control.ReturnCode != "0000") { + throw new ResponseException(ResponseObj.Message.Control.ReturnCode, errorMessage); + } + PaymentUrl=ResponseObj.Message.Parameters.ReturnURL; + } + + protected override void BeforeRequest() + { + if (string.IsNullOrEmpty(SequenceNo)) { + throw new Exception("交易流水号不能为空,SequenceNo"); + } + if (string.IsNullOrEmpty(BuyerCustomer)) { + throw new Exception("付款帐户不能为空,BuyerCustomer"); + } + if (string.IsNullOrEmpty(BuyerCustName)) { + throw new Exception("付款人不能为空,BuyerCustName"); + } + if (string.IsNullOrEmpty(SalerCustomer)) { + throw new Exception("收款帐户不能为空,SalerCustomer"); + } + if (string.IsNullOrEmpty(SalerCustName)) { + throw new Exception("收款人不能为空,SalerCustName"); + } + RequestObj.Message.Control.MerchantID = MerchantID; + RequestObj.Message.Control.MerchantTrxNo = SequenceNo; + RequestObj.Message.Parameters.OrderNo = SequenceNo; + RequestObj.Message.Parameters.BuyerCustName = BuyerCustName; + RequestObj.Message.Parameters.BuyerCustomer = BuyerCustomer; + RequestObj.Message.Parameters.SalerCustName = SalerCustName; + RequestObj.Message.Parameters.SalerCustomer = SalerCustomer; + RequestObj.Message.Parameters.PayAmount = Amount.ToString("f2"); + } + } +} diff --git a/BWP.ABCClient/Businesses/MarketOrderPay.cs b/BWP.ABCClient/Businesses/MarketOrderPay.cs new file mode 100644 index 0000000..572e5d2 --- /dev/null +++ b/BWP.ABCClient/Businesses/MarketOrderPay.cs @@ -0,0 +1,49 @@ +using System; +using BWP.ABCClient.Businesses.Type; +using BWP.ABCClient.Exceptions; +using BWP.ABCClient.Market; + +namespace BWP.ABCClient.Businesses +{ + public class MarketOrderPay : BusinessBase + { + public MarketOrderPay() + : base(BusinessTypes.Market) + { + RequestObj = OrderPayRequest.New(); + } + + public string OrderNo { get; set; } + + public string CustSignInfo { get; set; } + + public string CustSignInfo2 { get; set; } + + protected override void AfterRequest() + { + var errorMessage = ResponseObj.Message.Control.ReturnMessage ?? string.Empty; + Log.AddNewLine(string.Format("交易结果:[{0}]", ResponseObj.Message.Control.ReturnCode)); + Log.AddNewLine(string.Format("返回信息:[{0}]", errorMessage)); + Log.Commit(); + if (ResponseObj.Message.Control.ReturnCode != "0000") { + throw new ResponseException(ResponseObj.Message.Control.ReturnCode, errorMessage); + } + } + + protected override void BeforeRequest() + { + if (string.IsNullOrEmpty(SequenceNo)) { + throw new Exception("交易流水号不能为空,SequenceNo"); + } + if (string.IsNullOrEmpty(OrderNo)) { + throw new Exception("订单号不能为空,OrderNo"); + } + RequestObj.Message.Control.MerchantID = MerchantID; + RequestObj.Message.Control.MerchantTrxNo = SequenceNo; + RequestObj.Message.Parameters.OrderNo = OrderNo; + RequestObj.Message.Parameters.PayAmount = Amount.ToString("f2"); + RequestObj.Message.Parameters.CustSignInfo = CustSignInfo; + RequestObj.Message.Parameters.CustSignInfo2 = CustSignInfo2; + } + } +} \ No newline at end of file diff --git a/BWP.ABCClient/Businesses/MarketReceivingPayment.cs b/BWP.ABCClient/Businesses/MarketReceivingPayment.cs new file mode 100644 index 0000000..34f3825 --- /dev/null +++ b/BWP.ABCClient/Businesses/MarketReceivingPayment.cs @@ -0,0 +1,54 @@ +using System; +using BWP.ABCClient.Businesses.Type; +using BWP.ABCClient.Exceptions; +using BWP.ABCClient.Market; + +namespace BWP.ABCClient.Businesses +{ + public class MarketReceivingPayment : BusinessBase + { + public MarketReceivingPayment() + : base(BusinessTypes.Market) + { + RequestObj = PayRequest.New(); + } + + public string CustomerSignInfo { get; set; } + + public string CustomerNo { get; set; } + + public string OrderMessage { get; set; } + + protected override void AfterRequest() + { + var errorMessage = (ResponseObj.Message.Control.ReturnMessage ?? string.Empty) + (ResponseObj.Message.Control.ErrorMessage ?? string.Empty); + Log.AddNewLine(string.Format("交易结果:[{0}]", ResponseObj.Message.Control.ReturnCode)); + Log.AddNewLine(string.Format("返回信息:[{0}]", errorMessage)); + if (ResponseObj.Message.Control.ReturnCode != "0000") { + Log.Commit(); + throw new ResponseException(ResponseObj.Message.Control.ReturnCode, errorMessage); + } + Log.Commit(); + } + + protected override void BeforeRequest() + { + if (string.IsNullOrEmpty(SequenceNo)) { + throw new Exception("交易流水号不能为空,SequenceNo"); + } + if (string.IsNullOrEmpty(CustomerSignInfo)) { + throw new Exception("客户签名不能为空,CustomerSignInfo"); + } + if (string.IsNullOrEmpty(CustomerNo)) { + throw new Exception("客户号不能为空,CustomerSignInfo"); + } + RequestObj.Message.Control.MerchantID = MerchantID; + RequestObj.Message.Control.MerchantTrxNo = SequenceNo; + RequestObj.Message.Parameters.CustomerNo = CustomerNo; + RequestObj.Message.Parameters.CustSignInfo = CustomerSignInfo; + RequestObj.Message.Parameters.CustSignInfo2 = CustomerSignInfo; + RequestObj.Message.Parameters.PayAmount = Amount.ToString("f2"); + RequestObj.Message.Parameters.OrderMg = OrderMessage; + } + } +} \ No newline at end of file diff --git a/BWP.ABCClient/Businesses/MarketSignOff.cs b/BWP.ABCClient/Businesses/MarketSignOff.cs new file mode 100644 index 0000000..3c7d498 --- /dev/null +++ b/BWP.ABCClient/Businesses/MarketSignOff.cs @@ -0,0 +1,51 @@ +using System; +using BWP.ABCClient.Businesses.Type; +using BWP.ABCClient.Exceptions; +using BWP.ABCClient.Market; + +namespace BWP.ABCClient.Businesses +{ + public class MarketSignOff : BusinessBase + { + public MarketSignOff() : base(BusinessTypes.Market) + { + RequestObj = SignOffRequest.New(); + } + + public string MerchantName { get; set; } + + public string CustomerSignInfo { get; set; } + + public string CustomerName { get; set; } + + public string CustomerNo { get; set; } + + protected override void AfterRequest() + { + var errorMessage = ResponseObj.Message.Control.ReturnMessage ?? string.Empty + ResponseObj.Message.Control.ErrorMessage ?? string.Empty; + Log.AddNewLine(string.Format("交易结果:[{0}]", ResponseObj.Message.Control.ReturnCode)); + Log.AddNewLine(string.Format("返回信息:[{0}]", errorMessage)); + if (ResponseObj.Message.Control.ReturnCode != "0000") { + Log.Commit(); + throw new ResponseException(ResponseObj.Message.Control.ReturnCode, errorMessage); + } + Log.Commit(); + } + + protected override void BeforeRequest() + { + if (string.IsNullOrEmpty(SequenceNo)) { + throw new Exception("交易流水号不能为空,SequenceNo"); + } + if (string.IsNullOrEmpty(CustomerSignInfo)) { + throw new Exception("客户签名不能为空,CustmerSignInfo"); + } + RequestObj.Message.Control.MerchantID = MerchantID; + RequestObj.Message.Control.MerchantTrxNo = SequenceNo; + RequestObj.Message.Parameters.CustName = CustomerName; + RequestObj.Message.Parameters.CustSignInfo = CustomerSignInfo; + RequestObj.Message.Parameters.MerchantName = MerchantName; + RequestObj.Message.Parameters.CustomerNo = CustomerNo; + } + } +} \ No newline at end of file diff --git a/BWP.ABCClient/Businesses/MarketSignUp.cs b/BWP.ABCClient/Businesses/MarketSignUp.cs new file mode 100644 index 0000000..b7a1156 --- /dev/null +++ b/BWP.ABCClient/Businesses/MarketSignUp.cs @@ -0,0 +1,56 @@ +using System; +using BWP.ABCClient.Businesses.Type; +using BWP.ABCClient.Exceptions; +using BWP.ABCClient.Market; + +namespace BWP.ABCClient.Businesses +{ + public class MarketSignUp : BusinessBase + { + public MarketSignUp() + : base(BusinessTypes.Market) + { + RequestObj = SignUpRequest.New(); + NeedTwoCheck = null; + } + + public string MerchantName { get; set; } + + public string CustomerSignInfo { get; set; } + + public string CustomerName { get; set; } + + public string NeedTwoCheck { get; set; } + + protected override void AfterRequest() + { + var errorMessage = ResponseObj.Message.Control.ReturnMessage ?? string.Empty + ResponseObj.Message.Control.ErrorMessage ?? string.Empty; + Log.AddNewLine(string.Format("交易结果:[{0}]", ResponseObj.Message.Control.ReturnCode)); + Log.AddNewLine(string.Format("返回信息:[{0}]", errorMessage)); + if (ResponseObj.Message.Control.ReturnCode != "0000") { + Log.Commit(); + throw new ResponseException(ResponseObj.Message.Control.ReturnCode, errorMessage + MsgUtil.MsgToBareXml(RequestObj)); + } + Log.Commit(); + } + + protected override void BeforeRequest() + { + if (string.IsNullOrEmpty(SequenceNo)) { + throw new Exception("交易流水号不能为空,SequenceNo"); + } + if (string.IsNullOrEmpty(CustomerSignInfo)) { + throw new Exception("客户签名不能为空,CustmerSignInfo"); + } + if (string.IsNullOrEmpty(MerchantName)) { + throw new Exception("商户名不能为空,MerchantName"); + } + RequestObj.Message.Control.MerchantID = MerchantID; + RequestObj.Message.Control.MerchantTrxNo = SequenceNo; + RequestObj.Message.Parameters.CustName = CustomerName; + RequestObj.Message.Parameters.CustSignInfo = CustomerSignInfo; + RequestObj.Message.Parameters.NeedTwoCheck = NeedTwoCheck; + RequestObj.Message.Parameters.MerchantName = MerchantName; + } + } +} \ No newline at end of file diff --git a/BWP.ABCClient/Businesses/ResultB2BReturned.cs b/BWP.ABCClient/Businesses/ResultB2BReturned.cs new file mode 100644 index 0000000..88345b3 --- /dev/null +++ b/BWP.ABCClient/Businesses/ResultB2BReturned.cs @@ -0,0 +1,46 @@ +using BWP.ABCClient.Businesses.Type; + +namespace BWP.ABCClient.Businesses +{ + public class ResultB2BReturned : IReturnedResult + { + private readonly string _xml; + + public ResultB2BReturned(string base64string) + { + var log = new AbcLog("b2bReturn"); + log.AddNewLine("TrustPayClient ASP V2.0.1 交易开始=========================="); + log.AddNewLine("接收到的支付结果通知:\n[" + base64string + "]"); + _xml = MsgUtil.FromBase64StringToXml(base64string); + log.AddNewLine("经过Base64解码后的支付结果通知:\n[" + _xml + "]"); + log.AddNewLine("验证支付结果通知的签名:"); + IsValid = RSAService.Verify(_xml, BusinessTypes.B2B.VerifyEncoding, false, false); + if (IsValid) + log.AddNewLine("验证通过!\n 经过验证的支付结果通知:\n[" + _xml + "]"); + else + log.AddNewLine("验证失败!"); + + log.Commit(); + ReturnCode = MsgUtil.GetInnerXml(_xml, "ReturnCode"); + ErrorMessage = MsgUtil.GetInnerXml(_xml, "ErrorMessage"); + IsSuccess = ReturnCode == "0000" ; + + SequenceNo = MsgUtil.GetInnerXml(_xml, "MerchantTrnxNo"); + } + + public string Xml + { + get { return _xml; } + } + + public bool IsValid { get; set; } + + public string ReturnCode { get; set; } + + public string ErrorMessage { get; set; } + + public bool IsSuccess { get; set; } + + public string SequenceNo { get; set; } + } +} \ No newline at end of file diff --git a/BWP.ABCClient/Businesses/ResultB2CReturned.cs b/BWP.ABCClient/Businesses/ResultB2CReturned.cs new file mode 100644 index 0000000..1fbe5a3 --- /dev/null +++ b/BWP.ABCClient/Businesses/ResultB2CReturned.cs @@ -0,0 +1,46 @@ +using BWP.ABCClient.Businesses.Type; + +namespace BWP.ABCClient.Businesses +{ + public class ResultB2CReturned : IReturnedResult + { + private readonly string _xml; + + public ResultB2CReturned(string base64string) + { + var log = new AbcLog("b2cReturn"); + log.AddNewLine("TrustPayClient ASP V2.0.1 交易开始=========================="); + log.AddNewLine("接收到的支付结果通知:\n[" + base64string + "]"); + _xml = MsgUtil.FromBase64StringToXml(base64string); + log.AddNewLine("经过Base64解码后的支付结果通知:\n[" + _xml + "]"); + log.AddNewLine("验证支付结果通知的签名:"); + IsValid = RSAService.Verify(_xml, BusinessTypes.B2C.VerifyEncoding, false, false); + if (IsValid) + log.AddNewLine("验证通过!\n 经过验证的支付结果通知:\n[" + _xml + "]"); + else + log.AddNewLine("验证失败!"); + + log.Commit(); + ReturnCode = MsgUtil.GetInnerXml(_xml, "ReturnCode"); + ErrorMessage = MsgUtil.GetInnerXml(_xml, "ErrorMessage"); + IsSuccess = ReturnCode == "0000"; + + SequenceNo = MsgUtil.GetInnerXml(_xml, "OrderNo"); + } + + public string Xml + { + get { return _xml; } + } + + public bool IsValid { get; set; } + + public string ReturnCode { get; set; } + + public string ErrorMessage { get; set; } + + public bool IsSuccess { get; set; } + + public string SequenceNo { get; set; } + } +} \ No newline at end of file diff --git a/BWP.ABCClient/Businesses/Type/B2B.cs b/BWP.ABCClient/Businesses/Type/B2B.cs new file mode 100644 index 0000000..3d0f93a --- /dev/null +++ b/BWP.ABCClient/Businesses/Type/B2B.cs @@ -0,0 +1,43 @@ +using System; +using System.Text; + +namespace BWP.ABCClient.Businesses.Type +{ + public class B2B : IBusinessType + { + public Encoding RequestEncoding + { + get { return Encoding.UTF8; } + } + + public Encoding ResponseEncoding + { + get { return Encoding.UTF8; } + } + + public Encoding SignEncoding + { + get { return Encoding.UTF8; } + } + + public Encoding VerifyEncoding + { + get { return Encoding.UTF8; } + } + + public bool CompressEnabled + { + get { return false; } + } + + public bool IsMarket + { + get { return false; } + } + + public string Name + { + get { return "b2b"; } + } + } +} diff --git a/BWP.ABCClient/Businesses/Type/B2C.cs b/BWP.ABCClient/Businesses/Type/B2C.cs new file mode 100644 index 0000000..f84c333 --- /dev/null +++ b/BWP.ABCClient/Businesses/Type/B2C.cs @@ -0,0 +1,43 @@ +using System; +using System.Text; + +namespace BWP.ABCClient.Businesses.Type +{ + public class B2C : IBusinessType + { + public Encoding RequestEncoding + { + get { return Encoding.UTF8; } + } + + public Encoding ResponseEncoding + { + get { return Encoding.GetEncoding("GB2312"); } + } + + public Encoding SignEncoding + { + get { return Encoding.UTF8; } + } + + public Encoding VerifyEncoding + { + get { return Encoding.GetEncoding("GB2312"); } + } + + public bool CompressEnabled + { + get { return false; } + } + + public bool IsMarket + { + get { return false; } + } + + public string Name + { + get { return "b2c"; } + } + } +} diff --git a/BWP.ABCClient/Businesses/Type/BusinessTypes.cs b/BWP.ABCClient/Businesses/Type/BusinessTypes.cs new file mode 100644 index 0000000..0c24c5f --- /dev/null +++ b/BWP.ABCClient/Businesses/Type/BusinessTypes.cs @@ -0,0 +1,24 @@ +namespace BWP.ABCClient.Businesses.Type +{ + public static class BusinessTypes + { + private static IBusinessType _b2bInfo; + private static IBusinessType _b2cInfo; + private static IBusinessType _marketInfo; + + public static IBusinessType B2B + { + get { return _b2bInfo ?? (_b2bInfo = new Type.B2B() ); } + } + + public static IBusinessType B2C + { + get { return _b2cInfo ?? (_b2cInfo = new Type.B2C()); } + } + + public static IBusinessType Market + { + get { return _marketInfo ?? (_marketInfo = new Market()); } + } + } +} diff --git a/BWP.ABCClient/Businesses/Type/IBusinessType.cs b/BWP.ABCClient/Businesses/Type/IBusinessType.cs new file mode 100644 index 0000000..f23544a --- /dev/null +++ b/BWP.ABCClient/Businesses/Type/IBusinessType.cs @@ -0,0 +1,15 @@ +using System.Text; + +namespace BWP.ABCClient.Businesses.Type +{ + public interface IBusinessType + { + Encoding RequestEncoding { get; } + Encoding ResponseEncoding { get; } + Encoding SignEncoding { get; } + Encoding VerifyEncoding { get; } + bool CompressEnabled { get; } + bool IsMarket { get; } + string Name { get; } + } +} \ No newline at end of file diff --git a/BWP.ABCClient/Businesses/Type/Market.cs b/BWP.ABCClient/Businesses/Type/Market.cs new file mode 100644 index 0000000..81492a8 --- /dev/null +++ b/BWP.ABCClient/Businesses/Type/Market.cs @@ -0,0 +1,43 @@ +using System; +using System.Text; + +namespace BWP.ABCClient.Businesses.Type +{ + public class Market : IBusinessType + { + public Encoding RequestEncoding + { + get { return Encoding.GetEncoding("GB2312"); } + } + + public Encoding ResponseEncoding + { + get { return Encoding.GetEncoding("GB2312"); } + } + + public Encoding SignEncoding + { + get { return Encoding.GetEncoding("GB2312"); } + } + + public Encoding VerifyEncoding + { + get { return Encoding.GetEncoding("GB2312"); } + } + + public bool CompressEnabled + { + get { return true; } + } + + public bool IsMarket + { + get { return true; } + } + + public string Name + { + get { return "market"; } + } + } +} diff --git a/BWP.ABCClient/Businesses/XMLDocument.cs b/BWP.ABCClient/Businesses/XMLDocument.cs new file mode 100644 index 0000000..1f02e83 --- /dev/null +++ b/BWP.ABCClient/Businesses/XMLDocument.cs @@ -0,0 +1,118 @@ +using System.Collections; + +namespace BWP.ABCClient.Businesses +{ + + public class XmlDocument + { + private string _xmlString; + + public XmlDocument() + { + _xmlString = ""; + } + + public XmlDocument(string xmlString) + { + _xmlString = ""; + Init(xmlString); + } + + public XmlDocument DeleteFirstTagDocument() + { + string str = GetFirstTagName(); + int index = _xmlString.IndexOf("<" + str + ">"); + int num2 = _xmlString.IndexOf(""); + if (num2 > index) { + _xmlString = _xmlString.Substring((num2 + str.Length) + 3); + } + return this; + } + + public ArrayList GetDocuments(string aTag) + { + string xmlString = _xmlString; + var list = new ArrayList(); + while (true) { + int index = xmlString.IndexOf("<" + aTag.Trim() + ">"); + int num2 = xmlString.IndexOf(""); + if (((index == -1) || (num2 == -1)) || (index > num2)) { + return list; + } + var document = new XmlDocument(xmlString.Substring(index, ((num2 + aTag.Length) + 3) - index)); + list.Add(document); + xmlString = xmlString.Substring(num2 + 1); + } + } + + public string GetFirstTagName() + { + string str = null; + int index = _xmlString.IndexOf('<'); + int num2 = _xmlString.IndexOf('>'); + if (num2 > index) { + str = _xmlString.Substring(index + 1, num2 - (index + 1)); + } + return str; + } + + public XmlDocument GetFormatDocument(string aSpace) + { + return GetFormatDocument(0, aSpace); + } + + private XmlDocument GetFormatDocument(int aLevel, string aSpace) + { + string str2; + string str = aSpace; + for (int i = 0; i < aLevel; i++) { + str = str + aSpace; + } + if (GetFirstTagName() == null) { + return this; + } + string xmlString = "\n"; + for (var document = new XmlDocument(_xmlString); (str2 = document.GetFirstTagName()) != null; document = document.DeleteFirstTagDocument()) { + var document2 = document.GetValue(str2); + var str4 = ""; + if (document2.GetFirstTagName() != null) { + str4 = str; + } + xmlString = string.Concat(new object[] { xmlString, str, "<", str2, ">", document2.GetFormatDocument(aLevel + 1, aSpace), str4, "\n" }); + } + return new XmlDocument(xmlString); + } + + public XmlDocument GetValue(string aTag) + { + XmlDocument document = null; + int index = _xmlString.IndexOf("<" + aTag.Trim() + ">"); + int num2 = _xmlString.IndexOf(""); + if (((index >= 0) && (num2 >= 0)) && (index < num2)) { + document = new XmlDocument(_xmlString.Substring((index + aTag.Length) + 2, num2 - ((index + aTag.Length) + 2))); + } + return document; + } + + public virtual string GetValueNoNull(string aTag) + { + var str = ""; + var document = GetValue(aTag); + if (document != null) { + str = document.ToString(); + } + return str; + } + + public XmlDocument Init(string xmlString) + { + _xmlString = xmlString; + return this; + } + + public override string ToString() + { + return _xmlString; + } + } +} diff --git a/BWP.ABCClient/CertManager.cs b/BWP.ABCClient/CertManager.cs new file mode 100644 index 0000000..f17e10b --- /dev/null +++ b/BWP.ABCClient/CertManager.cs @@ -0,0 +1,165 @@ +using System; +using System.IO; +using System.Security.Cryptography.X509Certificates; +using System.Security.Permissions; +using System.Threading; +using BWP.ABCClient.Exceptions; +using TSingSoft.WebPluginFramework.TimerTasks; + +namespace BWP.ABCClient +{ + public class CertManager + { + public static CertManager MainSystemCertManager + { + get + { + return new CertManager("MainSystemABCCerts"); + } + } + + CertManager(string storeName) + { + _store = new X509Store(storeName, StoreLocation.LocalMachine); + new StorePermission(StorePermissionFlags.AllFlags).Assert(); + } + + private readonly X509Store _store; + + public CertManager():this("ABCCerts") + { + } + + volatile static object _lockObj = new object(); + + public void SetupPfx(Stream stream, string password) + { + if (!Monitor.TryEnter(_lockObj)) + { + throw new ApplicationException("另一名操作员正在导入证书,请稍后再试"); + } + try + { + _store.Open(OpenFlags.ReadWrite); + using (var binaryReader = new BinaryReader(stream)) + { + var bytes = binaryReader.ReadBytes((int)stream.Length); + var cert = new X509Certificate2(bytes, password); + if (_store.Certificates.Find(X509FindType.FindBySerialNumber, cert.SerialNumber, false).Count == 0) + { + _store.Add(new X509Certificate2(bytes, password, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet)); + } + } + _store.Close(); + } + finally + { + Monitor.Exit(_lockObj); + } + } + + public void SetupPfx(string fileName, string password) + { + using (var stream = new FileStream(fileName, FileMode.Open, FileAccess.Read)) + { + SetupPfx(stream, password); + } + } + + public X509Certificate2 Fetch(string merchantID) + { + return Fetch(X509FindType.FindBySubjectName, merchantID); + } + + public X509Certificate2 Fetch(X509FindType findType, string findValue) + { + _store.Open(OpenFlags.ReadOnly); + var certs = _store.Certificates.Find(findType, findValue, false); + _store.Close(); + if (certs.Count == 0) { + throw new RSAException("找不到商户号对应的证书"); + } + if (certs.Count > 1) { + throw new RSAException("同一个商户号有多张证书"); + } + return certs[0]; + } + + public bool TryFetch(string merchantID, out X509Certificate2 cert) + { + cert = null; + _store.Open(OpenFlags.ReadOnly); + var certs = _store.Certificates.Find(X509FindType.FindBySubjectName, merchantID, false); + _store.Close(); + if (certs.Count == 0) { + return false; + } + cert = certs[0]; + return true; + } + + public void Remove(string merchantID) + { + var cert = Fetch(merchantID); + _store.Open(OpenFlags.ReadWrite); + _store.Remove(cert); + _store.Close(); + } + + public void Remove(X509Certificate2 cert) + { + _store.Open(OpenFlags.ReadWrite); + _store.Remove(cert); + _store.Close(); + } + + public void ClearAll() + { + _store.Open(OpenFlags.ReadWrite); + _store.RemoveRange(_store.Certificates); + _store.Close(); + } + + public X509Certificate2Collection FetchAll() + { + _store.Open(OpenFlags.ReadWrite); + var certs = _store.Certificates; + _store.Close(); + return certs; + } + + public void Export(string merchantID, string password, Stream stream) + { + var cert = Fetch(merchantID); + var bytes = cert.Export(X509ContentType.Pfx, password); + stream.Write(bytes, 0, bytes.Length); + } + + public void SetupABCPubKey(string fileName) + { + _store.Open(OpenFlags.ReadWrite); + _store.Add(new X509Certificate2(fileName)); + _store.Close(); + } + + public X509Certificate2 GetABCPubKey() + { + _store.Open(OpenFlags.ReadOnly); + var certs = _store.Certificates.Find(X509FindType.FindBySerialNumber, "7b97ca10275a0000d99d", false); + if (certs.Count == 0) { + throw new RSAException("找不到农行公钥"); + } + return certs[0]; + } + + public X509Certificate2 GetABCTestPubKey() + { + _store.Open(OpenFlags.ReadOnly); + var certs = _store.Certificates.Find(X509FindType.FindBySerialNumber, "50 3a ca 10 53 5b 00 00 02 7f", false); + if (certs.Count == 0) { + throw new RSAException("找不到农行公钥"); + } + return certs[0]; + } + } +} \ No newline at end of file diff --git a/BWP.ABCClient/Common/MerchantContent.cs b/BWP.ABCClient/Common/MerchantContent.cs new file mode 100644 index 0000000..0f8fa59 --- /dev/null +++ b/BWP.ABCClient/Common/MerchantContent.cs @@ -0,0 +1,8 @@ +namespace BWP.ABCClient.Common +{ + public class MerchantContent + { + public string ECMerchantType { get; set; } + public string MerchantID { get; set; } + } +} \ No newline at end of file diff --git a/BWP.ABCClient/Common/MessageBase.cs b/BWP.ABCClient/Common/MessageBase.cs new file mode 100644 index 0000000..6e62540 --- /dev/null +++ b/BWP.ABCClient/Common/MessageBase.cs @@ -0,0 +1,18 @@ +using System.Xml.Serialization; + +namespace BWP.ABCClient.Common +{ + public class MessageBase + { + [XmlElement(ElementName = "Signature-Algorithm")] + public string SignatureAlgorithm { get; set; } + + /// + /// 签名 + /// + /// + /// 使用私钥加密消息后得到的一个签名 + /// + public string Signature { get; set; } + } +} \ No newline at end of file diff --git a/BWP.ABCClient/Common/PaymentURLResponse.cs b/BWP.ABCClient/Common/PaymentURLResponse.cs new file mode 100644 index 0000000..eaeb086 --- /dev/null +++ b/BWP.ABCClient/Common/PaymentURLResponse.cs @@ -0,0 +1,27 @@ +using System.Xml.Serialization; + +namespace BWP.ABCClient.Common +{ + [XmlRoot(ElementName = "MSG")] + public class PaymentURLResponse : MessageBase + { + public MessageContent Message { get; set; } + + public class MessageContent + { + public MerchantContent Merchant { get; set; } + public TrxResponseContent TrxResponse { get; set; } + } + + public class TrxResponseContent + { + public string ReturnCode { get; set; } + + public string ErrorMessage { get; set; } + + public string TrnxType { get; set; } + + public string PaymentURL { get; set; } + } + } +} diff --git a/BWP.ABCClient/Exceptions/CertException.cs b/BWP.ABCClient/Exceptions/CertException.cs new file mode 100644 index 0000000..78c2081 --- /dev/null +++ b/BWP.ABCClient/Exceptions/CertException.cs @@ -0,0 +1,11 @@ +using System; + +namespace BWP.ABCClient.Exceptions +{ + public class RSAException : Exception + { + public RSAException(string message) : base(message) + { + } + } +} \ No newline at end of file diff --git a/BWP.ABCClient/Exceptions/HttpException.cs b/BWP.ABCClient/Exceptions/HttpException.cs new file mode 100644 index 0000000..93d4308 --- /dev/null +++ b/BWP.ABCClient/Exceptions/HttpException.cs @@ -0,0 +1,12 @@ +using System; + +namespace BWP.ABCClient.Exceptions +{ + public class HttpException : Exception + { + public HttpException(string message) : base(message) + { + + } + } +} \ No newline at end of file diff --git a/BWP.ABCClient/Exceptions/ResponseException.cs b/BWP.ABCClient/Exceptions/ResponseException.cs new file mode 100644 index 0000000..3cb5cbc --- /dev/null +++ b/BWP.ABCClient/Exceptions/ResponseException.cs @@ -0,0 +1,20 @@ +using System; + +namespace BWP.ABCClient.Exceptions +{ + public class ResponseException : Exception + { + private readonly string _errorCode; + + public ResponseException(string errorCode, string message) + : base(errorCode + ":" + message) + { + _errorCode = errorCode; + } + + public string ErrorCode + { + get { return _errorCode; } + } + } +} \ No newline at end of file diff --git a/BWP.ABCClient/Log/AbcLog.cs b/BWP.ABCClient/Log/AbcLog.cs new file mode 100644 index 0000000..46011a2 --- /dev/null +++ b/BWP.ABCClient/Log/AbcLog.cs @@ -0,0 +1,61 @@ +using System; +using System.IO; +using System.Text; +using Forks.Utils.IO; +using TSingSoft.WebPluginFramework; + +namespace BWP.ABCClient +{ + public class AbcLog + { + private readonly string _filePath; + private readonly StringBuilder _msg = new StringBuilder(); + + public AbcLog(string type) + { +#if DEBUG + _filePath = Path.Combine("../../abclog/", DateTime.Today.ToString("yyyyMMdd") + type + "log.txt"); +#else + _filePath = Path.Combine(Wpf.Settings.ConfigFolder + "../../abclog/", DateTime.Today.ToString("yyyyMMdd") + type + "log.txt"); +#endif + + if (!FS.FileExists(_filePath)) + FS.CreateDirectory(Path.GetDirectoryName(_filePath)); + } + + public void Add(string msg) + { + _msg.Append(msg); + } + + public void AddNewLine(string msg) + { + _msg.AppendLine(); + _msg.Append(string.Format("{0} {1}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), msg)); + } + + public void Commit() + { + try { + + File.AppendAllText(_filePath, _msg.ToString(), Encoding.UTF8); + _msg.Clear(); + + } catch (Exception) + { } + + } + + public string Load() + { + return File.ReadAllText(_filePath, Encoding.UTF8); + } + + public void Clear() + { + FS.WriteAllText(_filePath, string.Empty); + } + + } + +} diff --git a/BWP.ABCClient/Market/MarketResponse.cs b/BWP.ABCClient/Market/MarketResponse.cs new file mode 100644 index 0000000..18a6f97 --- /dev/null +++ b/BWP.ABCClient/Market/MarketResponse.cs @@ -0,0 +1,49 @@ +using System.Xml.Serialization; +using BWP.ABCClient.Common; + +namespace BWP.ABCClient.Market +{ + [XmlRoot(ElementName = "MSG")] + public class MarketResponse : MessageBase + { + public MessageContent Message { get; set; } + + public class MessageContent + { + public ControlContent Control { get; set; } + public ParametersContent Parameters { get; set; } + public string Resultsets { get; set; } + } + + public class ControlContent + { + public string MerchantTrxNo { get; set; } + + public string Version { get; set; } + + public string ChannelType { get; set; } + + public string BusinessID { get; set; } + + public string FunctionID { get; set; } + + public string MerchantID { get; set; } + + public string ReturnCode { get; set; } + + public string ReturnMessage { get; set; } + public string ErrorMessage { get; set; } + + public string ClientIP { get; set; } + } + + public class ParametersContent + { + public string OrderMg { get; set; } + public string CustomerNo { get; set; } + public string CustSignInfo2 { get; set; } + public string PayAmount { get; set; } + public string CustSignInfo { get; set; } + } + } +} diff --git a/BWP.ABCClient/Market/OrderApplyRequest.cs b/BWP.ABCClient/Market/OrderApplyRequest.cs new file mode 100644 index 0000000..14c1590 --- /dev/null +++ b/BWP.ABCClient/Market/OrderApplyRequest.cs @@ -0,0 +1,72 @@ +using System.Xml.Serialization; +using BWP.ABCClient.Common; + +namespace BWP.ABCClient.Market +{ + + [XmlRoot(ElementName = "MSG")] + public class OrderApplyRequest : MessageBase + { + public MessageContent Message { get; set; } + + public static OrderApplyRequest New() + { + return new OrderApplyRequest { + Message = new MessageContent { + Control = new ControlContent { + MerchantTrxNo = string.Empty, + Version = "Java_V1.0", + ChannelType = "01", + BusinessID = "MARKET", + FunctionID = "0025", + MerchantID = string.Empty + }, + Parameters = new ParametersContent { + OrderMg = "http://127.0.0.1:18080/marketclientCS/MerchantResult.jsp", + FeeFlag = "0", + OrderType = "C", + PayAmount = string.Empty, + OrderNo = string.Empty, + BuyerCustName = string.Empty, + BuyerCustomer = string.Empty, + SalerCustName = string.Empty, + SalerCustomer = string.Empty + }, + Resultsets = string.Empty + }, + SignatureAlgorithm = "SHA1withRSA", + Signature = string.Empty + }; + } + + public class MessageContent + { + public ControlContent Control { get; set; } + public ParametersContent Parameters { get; set; } + public string Resultsets { get; set; } + } + + public class ControlContent + { + public string MerchantTrxNo { get; set; } + public string Version { get; set; } + public string ChannelType { get; set; } + public string BusinessID { get; set; } + public string FunctionID { get; set; } + public string MerchantID { get; set; } + } + + public class ParametersContent + { + public string FeeFlag { get; set; } + public string BuyerCustomer { get; set; } + public string OrderMg { get; set; } + public string PayAmount { get; set; } + public string OrderType { get; set; } + public string OrderNo { get; set; } + public string SalerCustomer { get; set; } + public string SalerCustName { get; set; } + public string BuyerCustName { get; set; } + } + } +} diff --git a/BWP.ABCClient/Market/OrderApplyResponse.cs b/BWP.ABCClient/Market/OrderApplyResponse.cs new file mode 100644 index 0000000..dce6095 --- /dev/null +++ b/BWP.ABCClient/Market/OrderApplyResponse.cs @@ -0,0 +1,44 @@ +using System.Xml.Serialization; +using BWP.ABCClient.Common; + +namespace BWP.ABCClient.Market +{ + [XmlRoot(ElementName = "MSG")] + public class OrderApplyResponse : MessageBase + { + public MessageContent Message { get; set; } + + public class MessageContent + { + public ControlContent Control { get; set; } + public ParametersContent Parameters { get; set; } + public string Resultsets { get; set; } + } + + public class ControlContent + { + public string MerchantTrxNo { get; set; } + + public string Version { get; set; } + + public string ChannelType { get; set; } + + public string BusinessID { get; set; } + + public string FunctionID { get; set; } + + public string MerchantID { get; set; } + + public string ReturnCode { get; set; } + + public string ReturnMessage { get; set; } + + } + + public class ParametersContent + { + public string OrderDate { get; set; } + public string ReturnURL { get; set; } + } + } +} diff --git a/BWP.ABCClient/Market/OrderPayRequest.cs b/BWP.ABCClient/Market/OrderPayRequest.cs new file mode 100644 index 0000000..2233aa2 --- /dev/null +++ b/BWP.ABCClient/Market/OrderPayRequest.cs @@ -0,0 +1,61 @@ +using System.Xml.Serialization; +using BWP.ABCClient.Common; + +namespace BWP.ABCClient.Market +{ + [XmlRoot(ElementName = "MSG")] + public class OrderPayRequest : MessageBase + { + public MessageContent Message { get; set; } + + public static OrderPayRequest New() + { + return new OrderPayRequest { + Message = new MessageContent { + Control = new ControlContent { + MerchantTrxNo = string.Empty, + Version = "Java_V1.0", + ChannelType = "01", + BusinessID = "MARKET", + FunctionID = "0026", + MerchantID = string.Empty + }, + Parameters = new ParametersContent { + PayAmount = string.Empty, + OrderNo = string.Empty, + CustSignInfo = string.Empty, + CustSignInfo2 = string.Empty, + }, + Resultsets = string.Empty + }, + SignatureAlgorithm = "SHA1withRSA", + Signature = string.Empty + }; + } + + public class MessageContent + { + public ControlContent Control { get; set; } + public ParametersContent Parameters { get; set; } + public string Resultsets { get; set; } + } + + public class ControlContent + { + public string MerchantTrxNo { get; set; } + public string Version { get; set; } + public string ChannelType { get; set; } + public string BusinessID { get; set; } + public string FunctionID { get; set; } + public string MerchantID { get; set; } + } + + public class ParametersContent + { + public string CustSignInfo2 { get; set; } + public string PayAmount { get; set; } + public string OrderNo { get; set; } + public string CustSignInfo { get; set; } + } + } +} diff --git a/BWP.ABCClient/Market/OrderPayResponse.cs b/BWP.ABCClient/Market/OrderPayResponse.cs new file mode 100644 index 0000000..c159e42 --- /dev/null +++ b/BWP.ABCClient/Market/OrderPayResponse.cs @@ -0,0 +1,41 @@ +using System.Xml.Serialization; +using BWP.ABCClient.Common; + +namespace BWP.ABCClient.Market +{ + [XmlRoot(ElementName = "MSG")] + public class OrderPayResponse : MessageBase + { + public MessageContent Message { get; set; } + + public class MessageContent + { + public ControlContent Control { get; set; } + public string Parameters { get; set; } + public string Resultsets { get; set; } + } + + public class ControlContent + { + public string MerchantTrxNo { get; set; } + + public string ReturnMessage { get; set; } + + public string ChannelType { get; set; } + + public string Version { get; set; } + + public string ClientIP { get; set; } + + public string BusinessID { get; set; } + + public string FunctionID { get; set; } + + public string ReturnCode { get; set; } + + public string MerchantID { get; set; } + + } + + } +} diff --git a/BWP.ABCClient/Market/PayRequest.cs b/BWP.ABCClient/Market/PayRequest.cs new file mode 100644 index 0000000..657a3b4 --- /dev/null +++ b/BWP.ABCClient/Market/PayRequest.cs @@ -0,0 +1,68 @@ +using System.Xml.Serialization; +using BWP.ABCClient.Common; + +namespace BWP.ABCClient.Market +{ + [XmlRoot(ElementName = "MSG")] + public class PayRequest : MessageBase + { + public MessageContent Message { get; set; } + + public static PayRequest New() + { + return new PayRequest { + Message = new MessageContent { + Control = new ControlContent { + MerchantTrxNo = string.Empty, + Version = "Java_V1.0", + ChannelType = "01", + BusinessID = "MARKET", + FunctionID = "0009", + MerchantID = string.Empty + }, + Parameters = new ParametersContent { + OrderMg = "实时支付", + CustomerNo = string.Empty, + CustSignInfo2 = null, + PayAmount = string.Empty, + CustSignInfo = string.Empty + }, + Resultsets = string.Empty + }, + SignatureAlgorithm = "SHA1withRSA", + Signature = string.Empty + }; + } + + public class MessageContent + { + public ControlContent Control { get; set; } + public ParametersContent Parameters { get; set; } + public string Resultsets { get; set; } + } + + public class ControlContent + { + public string MerchantTrxNo { get; set; } + + public string Version { get; set; } + + public string ChannelType { get; set; } + + public string BusinessID { get; set; } + + public string FunctionID { get; set; } + + public string MerchantID { get; set; } + } + + public class ParametersContent + { + public string OrderMg { get; set; } + public string CustomerNo { get; set; } + public string CustSignInfo2 { get; set; } + public string PayAmount { get; set; } + public string CustSignInfo { get; set; } + } + } +} diff --git a/BWP.ABCClient/Market/SignOffRequest.cs b/BWP.ABCClient/Market/SignOffRequest.cs new file mode 100644 index 0000000..0e585f3 --- /dev/null +++ b/BWP.ABCClient/Market/SignOffRequest.cs @@ -0,0 +1,69 @@ +using System.Xml.Serialization; +using BWP.ABCClient.Common; + +namespace BWP.ABCClient.Market +{ + [XmlRoot(ElementName = "MSG")] + public class SignOffRequest : MessageBase + { + public MessageContent Message { get; set; } + + public static SignOffRequest New() + { + return new SignOffRequest { + Message = new MessageContent { + Control = new ControlContent { + MerchantTrxNo = string.Empty, + Version = "Java_V1.0", + ChannelType = "01", + BusinessID = "MARKET", + FunctionID = "0001", + MerchantID = string.Empty + }, + Parameters = new ParametersContent { + CustName = string.Empty, + MerchantName = string.Empty, + CustomerNo = string.Empty, + CustSignInfo = string.Empty + }, + Resultsets = string.Empty + }, + SignatureAlgorithm = "SHA1withRSA", + Signature = string.Empty + }; + } + + public class MessageContent + { + public ControlContent Control { get; set; } + public ParametersContent Parameters { get; set; } + public string Resultsets { get; set; } + } + + public class ControlContent + { + public string MerchantTrxNo { get; set; } + + public string Version { get; set; } + + public string ChannelType { get; set; } + + public string BusinessID { get; set; } + + public string FunctionID { get; set; } + + public string MerchantID { get; set; } + } + + public class ParametersContent + { + public string CustName { get; set; } + + public string MerchantName { get; set; } + + public string CustSignInfo { get; set; } + + public string CustomerNo { get; set; } + } + } +} diff --git a/BWP.ABCClient/Market/SignUpRequest.cs b/BWP.ABCClient/Market/SignUpRequest.cs new file mode 100644 index 0000000..e8179a0 --- /dev/null +++ b/BWP.ABCClient/Market/SignUpRequest.cs @@ -0,0 +1,69 @@ +using System.Xml.Serialization; +using BWP.ABCClient.Common; + +namespace BWP.ABCClient.Market +{ + [XmlRoot(ElementName = "MSG")] + public class SignUpRequest : MessageBase + { + public MessageContent Message { get; set; } + + public static SignUpRequest New() + { + return new SignUpRequest { + Message = new MessageContent { + Control = new ControlContent { + MerchantTrxNo = string.Empty, + Version = "Java_V1.0", + ChannelType = "01", + BusinessID = "MARKET", + FunctionID = "0000", + MerchantID = string.Empty + }, + Parameters = new ParametersContent { + CustName = string.Empty, + MerchantName = string.Empty, + CustSignInfo = string.Empty, + NeedTwoCheck = null + }, + Resultsets = string.Empty + }, + SignatureAlgorithm = "SHA1withRSA", + Signature = string.Empty + }; + } + + public class MessageContent + { + public ControlContent Control { get; set; } + public ParametersContent Parameters { get; set; } + public string Resultsets { get; set; } + } + + public class ControlContent + { + public string MerchantTrxNo { get; set; } + + public string Version { get; set; } + + public string ChannelType { get; set; } + + public string BusinessID { get; set; } + + public string FunctionID { get; set; } + + public string MerchantID { get; set; } + } + + public class ParametersContent + { + public string CustName { get; set; } + + public string MerchantName { get; set; } + + public string CustSignInfo { get; set; } + + public string NeedTwoCheck { get; set; } + } + } +} diff --git a/BWP.ABCClient/MsgUtil.cs b/BWP.ABCClient/MsgUtil.cs new file mode 100644 index 0000000..d369b63 --- /dev/null +++ b/BWP.ABCClient/MsgUtil.cs @@ -0,0 +1,108 @@ +using System; +using System.IO; +using System.IO.Compression; +using System.Text; +using System.Xml; +using System.Xml.Serialization; +using XmlDocument = BWP.ABCClient.Businesses.XmlDocument; + +namespace BWP.ABCClient +{ + public class MsgUtil + { + public class BareXmlTextWriter : XmlTextWriter + { + public BareXmlTextWriter(TextWriter writer) : base(writer) {} + + public override void WriteStartDocument() {} + + public override void WriteStartDocument(bool bare) {} + + public override void WriteEndElement() + { + WriteFullEndElement(); + } + } + + public static string MsgToBareXml(object obj) + { + return MsgToBareXml(obj, Formatting.None, char.MinValue); + } + + public static string MsgToBareXml(object obj, Formatting formatting, char indentChar) + { + StringBuilder sb = new StringBuilder(); + using (var stringWriter = new StringWriter(sb)) { + using (BareXmlTextWriter writer = new BareXmlTextWriter(stringWriter)) { + writer.Formatting = formatting; + writer.IndentChar = indentChar; + var ns = new XmlSerializerNamespaces(); + ns.Add(string.Empty, string.Empty); + new XmlSerializer(obj.GetType()).Serialize(writer, obj, ns); + } + } + return sb.ToString(); + } + + public static T ParseXmlToMsg(string xml) + { + using (TextReader reader = new StringReader(xml)) { + return (T)new XmlSerializer(typeof(T)).Deserialize(reader); + } + } + + public static string GetInnerXml(string xml, string xpath) + { + var aMessage = new XmlDocument(xml); + return aMessage.GetValueNoNull(xpath); + } + + public static string Compress(string uncompressedString) + { + byte[] byteData = Encoding.GetEncoding("GB18030").GetBytes(uncompressedString); + using (MemoryStream srcStream = new MemoryStream(byteData)) { + using (var destStream = new MemoryStream()) { + using (GZipStream compress = new GZipStream(destStream, CompressionMode.Compress)) { + srcStream.CopyTo(compress); + } + return Convert.ToBase64String(destStream.ToArray()); + } + } + } + + public static string DeCompress(string comppressedString) + { + byte[] byteInput = Convert.FromBase64String(comppressedString); + using (var srcStream = new MemoryStream(byteInput)) { + using (var destStream = new MemoryStream()) { + using (GZipStream decompress = new GZipStream(srcStream, CompressionMode.Decompress)) { + decompress.CopyTo(destStream); + var resultBytes = destStream.ToArray(); + return Encoding.GetEncoding("GB18030").GetString(resultBytes, 0, resultBytes.Length); + } + } + } + } + + public static string[] GetMultiInnerXml(string xml, string xpath) + { + var xmlDoc = new System.Xml.XmlDocument(); + xmlDoc.LoadXml(xml); + var nodes = xmlDoc.SelectNodes(xpath); + if (nodes == null) { + return null; + } + var result = new string[nodes.Count]; + for (int i = 0; i < result.Length; i++) { + result[i] = nodes[i].InnerXml; + } + return result; + } + + public static string FromBase64StringToXml(string msg) + { + byte[] bytes = Convert.FromBase64String(msg); + return Encoding.GetEncoding("gb2312").GetString(bytes, 0, bytes.Length); + } + } +} diff --git a/BWP.ABCClient/Properties/AssemblyInfo.cs b/BWP.ABCClient/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..4d54b5c --- /dev/null +++ b/BWP.ABCClient/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("BWP.ABCClient")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("BWP.ABCClient")] +[assembly: AssemblyCopyright("Copyright © BWP 2011")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("49a33933-5a6f-4d22-98aa-b462f5a974d0")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] diff --git a/BWP.ABCClient/RSAService.cs b/BWP.ABCClient/RSAService.cs new file mode 100644 index 0000000..f14b76b --- /dev/null +++ b/BWP.ABCClient/RSAService.cs @@ -0,0 +1,74 @@ +using System; +using System.Security.Cryptography; +using System.Text; +using BWP.ABCClient.Common; + +namespace BWP.ABCClient +{ + public static class RSAService + { + public static void Sign(MessageBase msgObj, string merchantID, Encoding msgEncoding, bool isMarket) + { + string xml = MsgUtil.MsgToBareXml(msgObj); + var message = MsgUtil.GetInnerXml(xml, "Message"); + message = isMarket ? string.Format("{0}", message) : message; + msgObj.Signature = Sign(message, merchantID, msgEncoding); + } + + public static string Sign(string msg, string merchantID) + { + return Sign(msg, merchantID, Encoding.Default); + } + + public static string Sign(string msg, string merchantID, Encoding msgEncoding) + { + var cert = new CertManager().Fetch(merchantID); + var privateKey = (RSACryptoServiceProvider)cert.PrivateKey; + if (privateKey == null) + { + throw new Exception("商户的证书中没有私钥,请上传有效的证书"); + } + byte[] rgbHash = new SHA1Managed().ComputeHash(msgEncoding.GetBytes(msg)); + byte[] data = privateKey.SignHash(rgbHash, cert.SignatureAlgorithm.Value); + return Convert.ToBase64String(data); + } + + public static bool Verify(string returnMsg, string abcSign) + { + return Verify(returnMsg, abcSign, Encoding.Default, false); + } + + public static bool Verify(string returnMsg, string abcSign, Encoding msgEncoding, bool isTest) + { + var certMgr = new CertManager(); + var cert = isTest ? certMgr.GetABCTestPubKey() : certMgr.GetABCPubKey(); + var publicKey = (RSACryptoServiceProvider)cert.PublicKey.Key; + var singature = Convert.FromBase64String(abcSign); + var msgHash = new SHA1Managed().ComputeHash(msgEncoding.GetBytes(returnMsg)); + return publicKey.VerifyHash(msgHash, cert.SignatureAlgorithm.Value, singature); + } + + public static bool Verify(string receivedXml, Encoding msgEncoding, bool isMarket, bool isTest) + { + string message = MsgUtil.GetInnerXml(receivedXml, "Message"); + message = isMarket ? string.Format("{0}", message) : message; + string signature = MsgUtil.GetInnerXml(receivedXml, "Signature"); + + return Verify(message, signature, msgEncoding, isTest); + } + + public static bool TestVerify(string returnMsg, string abcSign) + { + return TestVerify(returnMsg, abcSign, Encoding.Default); + } + + public static bool TestVerify(string returnMsg, string abcSign, Encoding msgEncoding) + { + var cert = new CertManager().GetABCTestPubKey(); + var publicKey = (RSACryptoServiceProvider)cert.PublicKey.Key; + var singature = Convert.FromBase64String(abcSign); + var msgHash = new SHA1Managed().ComputeHash(msgEncoding.GetBytes(returnMsg)); + return publicKey.VerifyHash(msgHash, cert.SignatureAlgorithm.Value, singature); + } + } +} \ No newline at end of file diff --git a/BWP.ABCClient/Request.cs b/BWP.ABCClient/Request.cs new file mode 100644 index 0000000..81df996 --- /dev/null +++ b/BWP.ABCClient/Request.cs @@ -0,0 +1,50 @@ +using System.IO; +using System.Net; +using System.Text; +using BWP.ABCClient.Exceptions; + +namespace BWP.ABCClient +{ + public class Request + { + public string Message { get; set; } + + public string Url { get; set; } + + public Encoding RequestEncoding { get; set; } + + public Encoding ResponseEncoding { get; set; } + + public string ProxyIP { get; set; } + public int ProxyPort { get; set; } + + public string Run() + { + ServicePointManager.ServerCertificateValidationCallback = delegate { + return true; + }; + var request = (HttpWebRequest)WebRequest.Create(Url); + if (!string.IsNullOrEmpty(ProxyIP) && ProxyPort != 0) { + request.Proxy = new WebProxy(ProxyIP, ProxyPort); + } + request.Method = "POST"; + request.ProtocolVersion = HttpVersion.Version10; + request.ContentType = "application/x-www-form-urlencoded"; + byte[] bytes = RequestEncoding.GetBytes(Message); + request.ContentLength = bytes.Length; + using (var stream = request.GetRequestStream()) { + stream.Write(bytes, 0, bytes.Length); + } + + var response = (HttpWebResponse)request.GetResponse(); + if (response.StatusCode != HttpStatusCode.OK) { + throw new HttpException("网上支付平台服务暂时停止 " + response.StatusCode); + } + using (var responseStream = response.GetResponseStream()) { + using (var reader = new StreamReader(responseStream, ResponseEncoding)) { + return reader.ReadToEnd(); + } + } + } + } +} \ No newline at end of file diff --git a/test/B2BReceivingPaymentTest.cs b/test/B2BReceivingPaymentTest.cs new file mode 100644 index 0000000..bf03a20 --- /dev/null +++ b/test/B2BReceivingPaymentTest.cs @@ -0,0 +1,57 @@ +using System; +using System.Text; +using BWP.ABCClient.Businesses; +using NUnit.Framework; + +namespace BWP.ABCClient +{ + [TestFixture] + public class B2BReceivingPaymentTest + { + [Test] + public void AssignValue() + { + EnsureCertsExists(); + B2BReceivingPayment collections = new B2BReceivingPayment(); + collections.MerchantID = "337100000000066"; + collections.Date = DateTime.Now; + collections.Amount = 234.5m; + collections.AccountNo = "622587263489712639846"; + collections.AccountName = string.Empty; + collections.AccountBank = string.Empty; + collections.ResultNotifyURL = "http://a.b.c.d"; + collections.Remarks = string.Empty; + collections.Operator = string.Empty; + collections.SequenceNo = "209329384902834"; + collections.RequestUrl = "http://localhost:5000/B2B/"; + using (new MockWebServer("http://localhost:5000/B2B/", Encoding.UTF8, Encoding.UTF8, false)) { + collections.Request(); + } + } + + [Test] + public void ReturnValue() + { + EnsureCertsExists(); + B2BReceivingPayment collections = new B2BReceivingPayment(); + collections.MerchantID = "337100000000066"; + collections.Date = DateTime.Now; + collections.Amount = 234.5m; + collections.AccountNo = "622587263489712639846"; + collections.ResultNotifyURL = "http://a.b.c.d"; + collections.SequenceNo = "209329384902834"; + collections.RequestUrl = "http://localhost:5000/B2B/"; + using (new MockWebServer("http://localhost:5000/B2B/", Encoding.UTF8, Encoding.UTF8, false)) { + collections.Request(); + } + Assert.AreEqual("https://easyabc.95599.cn/b2b/NotCheckStatus/PaymentModeAct.ebf?TOKEN=12975716546177309876", collections.PaymentUrl); + } + + public static void EnsureCertsExists() + { + var certManager = new CertManager(); + certManager.SetupPfx("Certs/asB.pfx", "14814622"); + certManager.SetupABCPubKey("Certs/TrustPay.cer"); + } + } +} diff --git a/test/BusinessTest.cs b/test/BusinessTest.cs new file mode 100644 index 0000000..d81e9e0 --- /dev/null +++ b/test/BusinessTest.cs @@ -0,0 +1,97 @@ +using System; +using System.Text; +using BWP.ABCClient.B2B; +using BWP.ABCClient.B2C; +using BWP.ABCClient.Businesses; +using BWP.ABCClient.Businesses.Type; +using BWP.ABCClient.Common; +using BWP.ABCClient.Market; +using NUnit.Framework; + +namespace BWP.ABCClient +{ + [TestFixture] + public class BusinessTest + { + [Test] + public void B2CRequest() + { + EnsureCertsExists(); + using (new MockWebServer("http://localhost:5000/B2C/", Encoding.UTF8, Encoding.GetEncoding("GB2312"), false)) { + var business = new BusinessBase(BusinessTypes.B2C); + business.MerchantID = "103452083980419"; + business.RequestUrl = "http://localhost:5000/B2C/"; + business.RequestObj = PaymentRequest.New(); + business.Request(); + var responseObj = business.ResponseObj; + Assert.AreEqual("https://easyabc.95599.cn/b2c/NotCheckStatus/PaymentModeAct.ebf?TOKEN=12977496798933397376", responseObj.Message.TrxResponse.PaymentURL); + } + } + + [Test] + public void B2BRequest() + { + EnsureCertsExists(); + using (new MockWebServer("http://localhost:5000/B2B/", Encoding.UTF8, Encoding.UTF8, false)) { + var business = new BusinessBase(BusinessTypes.B2B); + business.MerchantID = "337100000000066"; + business.RequestUrl = "http://localhost:5000/B2B/"; + business.RequestObj = FundTransferRequest.New(); + business.Request(); + var responseObj = business.ResponseObj; + Assert.AreEqual("https://easyabc.95599.cn/b2b/NotCheckStatus/PaymentModeAct.ebf?TOKEN=12975716546177309876", responseObj.Message.TrxResponse.PaymentURL); + } + } + + [Test] + public void MarketRequest() + { + EnsureCertsExists(); + Encoding gb2312Encoding = Encoding.GetEncoding("GB2312"); + using (new MockWebServer("http://localhost:5000/Market/", gb2312Encoding, gb2312Encoding, true)) { + var business = new BusinessBase(BusinessTypes.Market); + business.MerchantID = "337100000000066"; + business.RequestUrl = "http://localhost:5000/Market/"; + business.RequestObj = PayRequest.New(); + business.Request(); + var responseObj = business.ResponseObj; + Assert.AreEqual("337199901033E01", responseObj.Message.Control.MerchantID); + } + } + + [Test] + public void MarketOrderApplyRequest() + { + EnsureCertsExists(); + Encoding gb2312Encoding = Encoding.GetEncoding("GB2312"); + var orderApplyRequest=OrderApplyRequest.New(); + orderApplyRequest.Message.Control.MerchantTrxNo = "Req110607008"; + orderApplyRequest.Message.Control.MerchantID = "337199901033E01"; + orderApplyRequest.Message.Parameters.BuyerCustomer = "84000471247"; + orderApplyRequest.Message.Parameters.PayAmount = "0.01"; + orderApplyRequest.Message.Parameters.OrderNo = "00000000011"; + orderApplyRequest.Message.Parameters.SalerCustomer = "84003761702"; + orderApplyRequest.Message.Parameters.SalerCustName = "张冠群"; + orderApplyRequest.Message.Parameters.BuyerCustName = "宋元华"; + + using (new MockWebServer("http://localhost:5000/MarketOrderApply/", gb2312Encoding, gb2312Encoding, true)) { + var business = new BusinessBase(BusinessTypes.Market); + business.MerchantID = "337199901033E01"; + business.RequestUrl = "http://localhost:5000/MarketOrderApply/"; + business.RequestObj = orderApplyRequest; + business.Request(); + var responseObj = business.ResponseObj; + Console.WriteLine(MsgUtil.MsgToBareXml(orderApplyRequest)); + Assert.AreEqual("337199901033E01", responseObj.Message.Control.MerchantID); + } + } + + private void EnsureCertsExists() { + var certManager = new CertManager(); + certManager.SetupPfx("Certs/asC.pfx", "14814622"); + certManager.SetupPfx("Certs/asB.pfx", "14814622"); + certManager.SetupABCPubKey("Certs/TrustPay.cer"); + certManager.SetupABCPubKey("Certs/TrustPayTest.cer"); + } + } +} diff --git a/test/CertManagerTest.cs b/test/CertManagerTest.cs new file mode 100644 index 0000000..bee6446 --- /dev/null +++ b/test/CertManagerTest.cs @@ -0,0 +1,93 @@ +using System; +using System.IO; +using System.Security.Cryptography; +using System.Xml.Serialization; +using NUnit.Framework; + +namespace BWP.ABCClient +{ + [TestFixture] + public class CertManagerTest + { + [SetUp] + public void Setup() + { + var manager = new CertManager(); + manager.ClearAll(); + } + + [Test] + public void SetupLocation() + { + var manager = new CertManager(); + const string fileName = "Certs/asB.pfx"; + const string password = "14814622"; + manager.SetupPfx(fileName, password); + var cert = manager.Fetch("337100000000066"); + var rsa = cert.PrivateKey as RSACryptoServiceProvider; + } + + [Test] + public void ClearAll() + { + var manager = new CertManager(); + const string fileName = "Certs/asB.pfx"; + const string password = "14814622"; + manager.SetupPfx(fileName, password); + manager.ClearAll(); + var certs = manager.FetchAll(); + Assert.IsTrue(certs.Count == 0); + } + + [Test] + public void SetupPfxFile() + { + var manager = new CertManager(); + const string fileName = "Certs/asB.pfx"; + const string password = "14814622"; + manager.SetupPfx(fileName, password); + var cert = manager.Fetch("337100000000066"); + Assert.AreEqual("7B97CA10275A0121D1B9", cert.SerialNumber); + } + + [Test] + public void Remove() + { + var manager = new CertManager(); + const string fileName = "Certs/asB.pfx"; + const string password = "14814622"; + manager.SetupPfx(fileName, password); + manager.Remove("337100000000066"); + try { + manager.Fetch("337100000000066"); + Assert.Fail("error"); + } catch (Exception e) { + Assert.AreEqual("找不到商户号对应的证书", e.Message); + } + } + + [Test] + public void SetupPfxStream() + { + var manager = new CertManager(); + using (var stream = File.OpenRead("Certs/asB.pfx")) { + const string password = "14814622"; + manager.SetupPfx(stream, password); + } + var cert = manager.Fetch("337100000000066"); + Assert.AreEqual("7B97CA10275A0121D1B9", cert.SerialNumber); + } + + [Test] + public void ExportCert() + { + var manager = new CertManager(); + const string fileName = "Certs/asB.pfx"; + const string password = "14814622"; + manager.SetupPfx(fileName, password); + using (var stream = File.OpenWrite("Certs/asBclone.pfx")) { + manager.Export("337100000000066", password, stream); + } + } + } +} \ No newline at end of file diff --git a/test/Certs/PDM.pfx b/test/Certs/PDM.pfx new file mode 100644 index 0000000..f8f7b92 Binary files /dev/null and b/test/Certs/PDM.pfx differ diff --git a/test/Certs/TrustPay.cer b/test/Certs/TrustPay.cer new file mode 100644 index 0000000..6ec2584 Binary files /dev/null and b/test/Certs/TrustPay.cer differ diff --git a/test/Certs/TrustPayTest.cer b/test/Certs/TrustPayTest.cer new file mode 100644 index 0000000..3f3199a Binary files /dev/null and b/test/Certs/TrustPayTest.cer differ diff --git a/test/Certs/asB.pfx b/test/Certs/asB.pfx new file mode 100644 index 0000000..0e5691f Binary files /dev/null and b/test/Certs/asB.pfx differ diff --git a/test/Certs/asC.pfx b/test/Certs/asC.pfx new file mode 100644 index 0000000..206cbaa Binary files /dev/null and b/test/Certs/asC.pfx differ diff --git a/test/LogTest.cs b/test/LogTest.cs new file mode 100644 index 0000000..ea7da4c --- /dev/null +++ b/test/LogTest.cs @@ -0,0 +1,78 @@ +using System; +using System.Text; +using System.Threading; +using BWP.ABCClient.Businesses; +using BWP.ABCClient.Businesses.Type; +using NUnit.Framework; + +namespace BWP.ABCClient +{ + [TestFixture] + public class LogTest + { + [Test] + public void CommitAndLoadTest() + { + var log = new AbcLog("test"); + log.Clear(); + log.AddNewLine("开始交易:"); + log.AddNewLine("检查配置:"); + log.Add("正确"); + log.Commit(); + Assert.AreEqual(string.Format("\r\n{0} 开始交易:\r\n{0} 检查配置:正确", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")), log.Load()); + } + + [Test] + public void ThreadTest() + { + new AbcLog("test").Clear(); + var t = new Thread(ThreadProc); + t.Start(); + for (int i = 0; i < 9; i++) { + var log = new AbcLog("test"); + log.AddNewLine("接受农行回馈:" + i); + log.Add("正确"); + log.Commit(); + Thread.Sleep(1); + } + t.Join(); + Console.WriteLine(new AbcLog("test").Load()); + } + + static void ThreadProc() + { + for (var i = 0; i < 10; i++) { + var log = new AbcLog("test"); + log.AddNewLine("开始交易:" + i); + log.AddNewLine("检查配置:"); + log.Add("正确"); + log.Commit(); + Thread.Sleep(1); + } + } + + + [Test] + public void B2BLogTest() + { + var log = new AbcLog(BusinessTypes.B2B.Name); + log.Clear(); + B2BReceivingPaymentTest.EnsureCertsExists(); + var collections = new B2BReceivingPayment(); + collections.MerchantID = "337100000000066"; + collections.Date = DateTime.Now; + collections.Amount = 234.5m; + collections.AccountNo = "622587263489712639846"; + collections.ResultNotifyURL = "http://a.b.c.d"; + collections.SequenceNo = "209329384902834"; + collections.RequestUrl = "http://localhost:5000/B2B/"; + + using (new MockWebServer("http://localhost:5000/B2B/", Encoding.UTF8, Encoding.UTF8, false)) { + collections.Request(); + ; + } + Console.WriteLine(log.Load()); + } + + } +} diff --git a/test/MockWebServer.cs b/test/MockWebServer.cs new file mode 100644 index 0000000..38de057 --- /dev/null +++ b/test/MockWebServer.cs @@ -0,0 +1,86 @@ +using System; +using System.IO; +using System.Text; +using System.Net; +using System.Threading; + +namespace BWP.ABCClient +{ + public sealed class MockWebServer : IDisposable + { + private readonly HttpListener _listener; + private readonly Thread _thread; + private readonly Encoding _reqEncoding; + private Encoding _respEncoding; + private readonly bool _compressResp; + + public MockWebServer() : this("http://localhost:5000/", Encoding.UTF8, Encoding.UTF8, false) + { + + } + + public MockWebServer(string prefix, Encoding reqEncoding, Encoding respEncoding, bool compressResp) + { + try { + _listener = new HttpListener(); + _listener.Prefixes.Add(prefix); + _listener.Start(); + Console.WriteLine("MockWebServer Listening... "); + _thread = new Thread(Listen); + _thread.Start(); + } catch (Exception e) { + Console.WriteLine(e); + } + _reqEncoding = reqEncoding; + _respEncoding = respEncoding; + _compressResp = compressResp; + } + + private void Listen() + { + while (true) { + HttpListenerContext context = _listener.GetContext(); + + HttpListenerRequest request = context.Request; + string receivedData; + using (var inputStream = request.InputStream) { + using (var inputReader = new StreamReader(inputStream, _reqEncoding)) { + receivedData = inputReader.ReadToEnd(); + } + } + Console.WriteLine("ReceivedData:"); + Console.WriteLine(receivedData); + string responseStr = string.Empty; + if (request.RawUrl.Contains("B2C")) { + responseStr = "B2C1034520839804100000交易成功PayReq634333749713468745https://easyabc.95599.cn/b2c/NotCheckStatus/PaymentModeAct.ebf?TOKEN=12977496798933397376170940.8SHA1withRSAeAy2xu/VmFnkHEY5sAxwmV/NfbXUDRzLH3IJa7oXOMuGGHHkXfYlwFfe2HesKq6gQgfFcfIsNs0bEc8r6EDkzZVETuiVviQxahqnqr0+GRnfly5OwNELtFCbtJ5PdzuWjme6SbNPSTqMUN6bRWzgr/UXrkuo3uyJOIIyCFGsFbs="; + _respEncoding = Encoding.GetEncoding("gb2312"); + } + if (request.RawUrl.Contains("B2B")) { + responseStr = "B2B3371000000000660000交易成功FundTransfer.1634331972002042500https://easyabc.95599.cn/b2b/NotCheckStatus/PaymentModeAct.ebf?TOKEN=12975716546177309876SHA1withRSA977AkRZUxtHflGP4fyvfNFpcmjuD0goKvRWD+w5g/bx45ZiCkGIki3wDwtgSNBA+QDBs/cyYAsR6AxuIs9CC0obMeSMrMBbixKJgJkNKO0UPlIU4seJ5rSlYKT1HX7tNE1SI7/VEmsv9i0r110hQjkSjzFhVrv9jRlMhNxSopbg="; + } + if (request.RawUrl.Contains("Market")) { + responseStr = "RSU11040600101Java_V1.0187.61.1.1MARKET00000000337199901033E01SHA1withRSAevJk/wQM5BjR72vHSCd1wgXSK8ZSRCVHbKv15faI1dY5kr4oz7ThBftaqcKqV7548QoDrMT0M3nITOffUU2CkLqb4TJcLAFZm+UVX2p/+1J2VPR0pBOnVLde8F6ADtnSV4IvqOCSkRi1Zoymr6UQ1gVw9p1Mh+TgOd6K85Zhitk="; + } + if (request.RawUrl.Contains("MarketOrderApply")) { + responseStr = "Req110607008交易成功01Java_V1.0MARKET00250000337199901033E012011-06-07 11:13:18https://www.95599.cn/market/MarketOrderPay.ebf?token=13074163984216493171SHA1withRSAMI99KF8wCoZR9DDBVEZWPFPJqJJFHV4Eci7/LJKP46fmaK3N4y9nWXPhJ+STnAZ5xwvjpRrgZqzcwVEY4acI0EqaUfADvzVV5jIgXatFG8gO1UhL3YpfK7HJX5lXdBcSN72QNuJA1M9ZbaH6KDhs3V/fWYrlLfTrdHnfjKssxMY="; + } + + if (_compressResp) { + responseStr = MsgUtil.Compress(responseStr); + } + HttpListenerResponse response = context.Response; + byte[] buffer = _respEncoding.GetBytes(responseStr); + response.ContentLength64 = buffer.Length; + using (Stream output = response.OutputStream) { + output.Write(buffer, 0, buffer.Length); + } + } + } + + public void Dispose() + { + _thread.Abort(); + _listener.Stop(); + } + } +} \ No newline at end of file diff --git a/test/MsgUtilTest.cs b/test/MsgUtilTest.cs new file mode 100644 index 0000000..2669f0b --- /dev/null +++ b/test/MsgUtilTest.cs @@ -0,0 +1,67 @@ +using System; +using BWP.ABCClient.B2B; +using NUnit.Framework; + +namespace BWP.ABCClient +{ + [TestFixture] + public class MsgUtilTest + { + [Test] + public void ParseReturnMsg() + { + string msg = "PE1TRz48TWVzc2FnZT48VHJ4UmVzcG9uc2U+PFJldHVybkNvZGU+MDAwMDwvUmV0dXJuQ29kZT48RXJyb3JNZXNzYWdlPjwvRXJyb3JNZXNzYWdlPjxFQ01lcmNoYW50VHlwZT5CMkM8L0VDTWVyY2hhbnRUeXBlPjxNZXJjaGFudElEPjEwMzQ1MjA4Mzk4MDQwNDwvTWVyY2hhbnRJRD48VHJ4VHlwZT5QYXlSZXN1bHQ8L1RyeFR5cGU+PE9yZGVyTm8+VGVzdE9yZGVyPC9PcmRlck5vPjxBbW91bnQ+MTAwLjA8L0Ftb3VudD48QmF0Y2hObz4wMDAwMDE8L0JhdGNoTm8+PFZvdWNoZXJObz4xMjM0NTY8L1ZvdWNoZXJObz48SG9zdERhdGU+MjAwOS8wOS8wODwvSG9zdERhdGU+PEhvc3RUaW1lPjEwOjU1OjUwPC9Ib3N0VGltZT48TWVyY2hhbnRSZW1hcmtzPlRoaXMgaXMgdGVzdCE8L01lcmNoYW50UmVtYXJrcz48UGF5VHlwZT5QQVkwMTwvUGF5VHlwZT48Tm90aWZ5VHlwZT4wPC9Ob3RpZnlUeXBlPjwvVHJ4UmVzcG9uc2U+PC9NZXNzYWdlPjxTaWduYXR1cmUtQWxnb3JpdGhtPlNIQTF3aXRoUlNBPC9TaWduYXR1cmUtQWxnb3JpdGhtPjxTaWduYXR1cmU+SVE4Z2pFSlRvK0FXQWxqWGVhcVVwdW43Z1JCNnNWdEhRSS9pNFdqUTNISExqZmphMTF3RE5VRVUyVzBVRUNsQnp5RFZuc0tRUHE2MmliM21YRjgrSnJZTWdpYW53WDNBNlRJbkxXMXJQTldVV1AwNGl5UVIvTUxuMHNaTjJ1S2JFbU5SWXRURjNPQ0lsTHFEblppYk1tSEZPTzhnektneVBxeEtaUkRRaHRZPTwvU2lnbmF0dXJlPjwvTVNHPg=="; + Console.WriteLine(MsgUtil.FromBase64StringToXml(msg)); + } + + [Test] + public void SelectMultiNode() + { + string str = "123134456"; + string[] values = MsgUtil.GetMultiInnerXml(str, "//Record"); + Assert.AreEqual("123", values[0]); + Assert.AreEqual("134", values[1]); + Assert.AreEqual("456", values[2]); + } + + [Test] + public void MsgToXml() + { + var msg = FundTransferRequest.New(); + var xml = MsgUtil.MsgToBareXml(msg); + Console.WriteLine(xml); + + msg = MsgUtil.ParseXmlToMsg(xml); + Console.WriteLine(msg.Message.Merchant.ECMerchantType); + + var response = new FundTransferResponse(); + response.Message = new FundTransferResponse.MessageContent(); + response.Message.TrxResponse = new FundTransferResponse.TrxResponseContent(); + response.Message.TrxResponse.ReturnCode = "0000"; + xml = MsgUtil.MsgToBareXml(response); + Console.WriteLine(xml); + + var obj = MsgUtil.ParseXmlToMsg(xml); + Assert.AreEqual("0000", obj.Message.TrxResponse.ReturnCode); + } + + [Test] + public void GetInnerXml() + { + var msg = "B2C103452083980409PayReq6343338239222187452540112011/02/1516:02:29http://119.167.225.52/B2/InternetSale/Pub/B2CReceive.aspx?id=10821211http://119.167.225.52/B2/InternetSale/Pub/B2CReceive.aspx1SHA1withRSANj7TTt4jyHTH93TekdWXr98tFKbjz/aRx3PBaE0SA6ns4bwqLbpI/27ljN90tElzqVnZWyHIKjDjo25SpPkgXlCsQGHtQcLbA6ZVXVqH18SjLxc4baUyEokMlnpbPYHAiOufOG3Ss8bI3/QlCbESV+7aeOwBfkQESAYvzsZWSWE="; + var expectmsg = "B2C103452083980409PayReq6343338239222187452540112011/02/1516:02:29http://119.167.225.52/B2/InternetSale/Pub/B2CReceive.aspx?id=10821211http://119.167.225.52/B2/InternetSale/Pub/B2CReceive.aspx1"; + + Assert.AreEqual(expectmsg, MsgUtil.GetInnerXml(msg, "Message")); + + expectmsg = "Nj7TTt4jyHTH93TekdWXr98tFKbjz/aRx3PBaE0SA6ns4bwqLbpI/27ljN90tElzqVnZWyHIKjDjo25SpPkgXlCsQGHtQcLbA6ZVXVqH18SjLxc4baUyEokMlnpbPYHAiOufOG3Ss8bI3/QlCbESV+7aeOwBfkQESAYvzsZWSWE="; + Assert.AreEqual(expectmsg, MsgUtil.GetInnerXml(msg, "Signature")); + } + + [Test] + public void CompressDeCompress() + { + string str = "B2C1034520839804090000交易成功PayReq634333823922218745https://easyabc.95599.cn/b2c/NotCheckStatus/PaymentModeAct.ebf?TOKEN=12977568750460430844254011"; + Assert.AreEqual(str, MsgUtil.DeCompress(MsgUtil.Compress(str))); + } + } +} \ No newline at end of file diff --git a/test/Properties/AssemblyInfo.cs b/test/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..28863b4 --- /dev/null +++ b/test/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("test")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("test")] +[assembly: AssemblyCopyright("Copyright © 2011")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("38052bef-0132-48b6-a766-0e3b52f90baf")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] diff --git a/test/RSAServiceTest.cs b/test/RSAServiceTest.cs new file mode 100644 index 0000000..0ab628c --- /dev/null +++ b/test/RSAServiceTest.cs @@ -0,0 +1,54 @@ +using System; +using System.Text; +using BWP.ABCClient.Businesses.Type; +using NUnit.Framework; + +namespace BWP.ABCClient +{ + [TestFixture] + public class RSAServiceTest + { + [SetUp] + public void Setup() + { + new CertManager().ClearAll(); + } + + [Test] + public void Sign() + { + string msg = "B2C103452083980409PayReq6343338239222187452540112011/02/1516:02:29http://119.167.225.52/B2/InternetSale/Pub/B2CReceive.aspx?id=10821211http://119.167.225.52/B2/InternetSale/Pub/B2CReceive.aspx1"; + string sign = "yGt5aaDrHmWBFb5Lk5WZlLmpuJXe4B25/t4FJzvQlAeKA+cqegiddcpfjGbv3AmspoRqZYNTEfnyLSOjZ8iz9L1U5nl415CzNml2uTLx1aiX4SpHSQASlu6fGqgO2yl2PrABslo+/njsy1tkzwFGm+y1gpjPNykkOWpaYyUjnfw="; + var certManager = new CertManager(); + certManager.SetupPfx("Certs/asC.pfx", "14814622"); + var signedMsg = RSAService.Sign(msg, "103452083980419"); + Console.WriteLine(signedMsg); + Assert.AreEqual(sign, signedMsg); + } + + [Test] + public void Verify() + { + string returnMsg = "B2C1034520839804090000交易成功PayReq634333823922218745https://easyabc.95599.cn/b2c/NotCheckStatus/PaymentModeAct.ebf?TOKEN=12977568750460430844254011"; + string abcSign = "3+0sE/7PTfYfHYqHUwWoFzUVvr0h2HUaXpq5pr+r3+DhyvufhVUPq1We9a0E+DzqzwnW3ZD5EYLhA204o4oRiLauEzM1cj8ddXZJGAtk5ftv1OGVCV+bvts/Ei9FQp8ws5b7pNqAvIyoSbIToartR7AJ42RJsT5DxXBtFy0Y2oQ="; + + var certManager = new CertManager(); + certManager.SetupABCPubKey("Certs/TrustPay.cer"); + var pass = RSAService.Verify(returnMsg, abcSign); + Assert.IsTrue(pass); + } + + [Test] + public void MarketVerify() + { + string returnMsg = "1301643918078商户状态不允许交易!01Java_V1.0187.61.1.1MARKET0000500105337199901033E01"; + string abcSign = "0m9ZR3zXZPKNa7z7Tr3DqHSoDx8NzgHeI0UCmlnpCqBGZS/YcGNZ+UThdVgfBacC27ELhtnRQaAzduQ0zzROs/Cpqm6h8BXfbhaU6Qr+Pp7Qfsu13XuBGr0iCfesFY5yoUlBnedUxCz6GOt9hGhVpPGsROvXU1cKIyafEGtpn9k="; + + var certManager = new CertManager(); + certManager.SetupABCPubKey("Certs/TrustPay.cer"); + var pass = RSAService.Verify(returnMsg, abcSign ); + Assert.IsTrue(pass); + } + + } +} diff --git a/test/RequestTest.cs b/test/RequestTest.cs new file mode 100644 index 0000000..c57ed13 --- /dev/null +++ b/test/RequestTest.cs @@ -0,0 +1,27 @@ +using System; +using System.Text; +using NUnit.Framework; + +namespace BWP.ABCClient +{ + [TestFixture] + public class RequestTest + { + private const string MSG = "SHA1withRSAB2BFundTransfer0充值时间人民币01"; + + [Test] + public void Request() + { + using (new MockWebServer()) { + var request = new Request { + Url = "http://localhost:5000/", + RequestEncoding = Encoding.UTF8, + ResponseEncoding = Encoding.UTF8, + Message = MSG + }; + string receivedMsg = request.Run(); + Console.WriteLine("client received:" + receivedMsg); + } + } + } +} diff --git a/test/ResultReturnedTest.cs b/test/ResultReturnedTest.cs new file mode 100644 index 0000000..d5584cb --- /dev/null +++ b/test/ResultReturnedTest.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using BWP.ABCClient.Businesses; +using NUnit.Framework; + +namespace BWP.ABCClient +{ + [TestFixture] + public class ResultReturnedTest + { + + [Test] + public void B2BTest() + { + const string msg = "PE1TRz48TWVzc2FnZT48VHJ4UmVzcG9uc2U+PE1lcmNoYW50SUQ+MzM3MTAwMDAwMDAwMDU2PC9NZXJjaGFudElEPjxDb3Jwb3JhdGlvbkN1c3RvbWVyTm8+Mzc5OTk2NzE5NjU8L0NvcnBvcmF0aW9uQ3VzdG9tZXJObz48TWVyY2hhbnRUcm54Tm8+NjM0Mzc0MTU0NDI5MzE3NTAwPC9NZXJjaGFudFRybnhObz48VHJueFNOPjwvVHJueFNOPjxUcm54VHlwZT5GVU5EX1RSQU5TRkVSPC9Ucm54VHlwZT48VHJueEFNVD4xODU3MTIxLjAwPC9Ucm54QU1UPjxPcmdpbmFsRnJlZXplTm8+PC9PcmdpbmFsRnJlZXplTm8+PEZyZWV6ZU5vPjwvRnJlZXplTm8+PEFjY291bnRObz48L0FjY291bnRObz48QWNjb3VudE5hbWU+PC9BY2NvdW50TmFtZT48QWNjb3VudEJhbms+PC9BY2NvdW50QmFuaz48QWNjb3VudERCTm8+MTUtNDkxMTAxMDQwMDIwNTIzPC9BY2NvdW50REJObz48QWNjb3VudERCTmFtZT7Busm9wfm6zb7FwPvKs8a309DP3rmry748L0FjY291bnREQk5hbWU+PEFjY291bnREQkJhbms+MzQyNzc8L0FjY291bnREQkJhbms+PFRybnhUaW1lPjIwMTEtMDQtMDMgMDg6MTg6NTI8L1RybnhUaW1lPjxUcm54U3RhdHVzPjA8L1RybnhTdGF0dXM+PFJldHVybkNvZGU+PC9SZXR1cm5Db2RlPjwvVHJ4UmVzcG9uc2U+PC9NZXNzYWdlPjxTaWduYXR1cmUtQWxnb3JpdGhtPlNIQTF3aXRoUlNBPC9TaWduYXR1cmUtQWxnb3JpdGhtPjxTaWduYXR1cmU+Y2sra3V6Mk9KRVpwTWlhLzBva3JPOTAvZWdVTnZDeUN2cjdZRXN0SXA3aGtYeGEzbDJDWVNMd1NpczlHU0p3OVYrQitlVHkrRGRZUDF0cWZlaHRRZmEyZWQxMENRR2Z0eTViL3F4R2VRL3ZrR1NTOUNOTWxDNjdOV1VMTnhCaVdxNkFVQkwybndUbkVmRzV3VXZjZ3BrTHE2VWF6MklUekhBTWZselBmU1ZJPTwvU2lnbmF0dXJlPjwvTVNHPg=="; + var certManager = new CertManager(); + certManager.SetupABCPubKey("Certs/TrustPay.cer"); + var result = new ResultB2BReturned(msg); + Console.WriteLine(result.Xml); + Assert.IsTrue(result.IsValid); + Assert.AreEqual(@"33710000000005637999671965634374154429317500FUND_TRANSFER1857121.0015-491101040020523梁山六和九利食品有限公司342772011-04-03 08:18:520SHA1withRSAck+kuz2OJEZpMia/0okrO90/egUNvCyCvr7YEstIp7hkXxa3l2CYSLwSis9GSJw9V+B+eTy+DdYP1tqfehtQfa2ed10CQGfty5b/qxGeQ/vkGSS9CNMlC67NWULNxBiWq6AUBL2nwTnEfG5wUvcgpkLq6Uaz2ITzHAMflzPfSVI=", result.Xml); + + } + + [Test] + public void B2BTest2() + { + const string msg = "PE1TRz48TWVzc2FnZT48VHJ4UmVzcG9uc2U+PE1lcmNoYW50SUQ+MzM3MTAwMDAwMDAwMDc1PC9NZXJjaGFudElEPjxDb3Jwb3JhdGlvbkN1c3RvbWVyTm8+MzM5OTkwODc1NDA8L0NvcnBvcmF0aW9uQ3VzdG9tZXJObz48TWVyY2hhbnRUcm54Tm8+NjM0NDUzNzMwNTA3NDI3NTAwPC9NZXJjaGFudFRybnhObz48VHJueFNOPjkwMTEwNzA0MTA0OTQ5NDA1NzE8L1RybnhTTj48VHJueFR5cGU+RlVORF9UUkFOU0ZFUjwvVHJueFR5cGU+PFRybnhBTVQ+MTUwMDAwMDwvVHJueEFNVD48T3JnaW5hbEZyZWV6ZU5vPiA8L09yZ2luYWxGcmVlemVObz48RnJlZXplTm8+IDwvRnJlZXplTm8+PEFjY291bnRObz4xOS0wODAyMDEwNDAwNTc5MzQ8L0FjY291bnRObz48QWNjb3VudE5hbWU+urzW3c/owPvT8Mje1sbGt9PQz965q8u+PC9BY2NvdW50TmFtZT48QWNjb3VudEJhbms+1eO9rbfW0NA8L0FjY291bnRCYW5rPjxBY2NvdW50REJObz4xNS03NDA4MDEwNDAwMDE3MzM8L0FjY291bnREQk5vPjxBY2NvdW50REJOYW1lPrH11t3B+brNwqG078WpxMHT0M/euavLvjwvQWNjb3VudERCTmFtZT48QWNjb3VudERCQmFuaz7Jvbart9bQ0DwvQWNjb3VudERCQmFuaz48VHJueFRpbWU+MjAxMS03LTQgMTA6NDk6NDk8L1RybnhUaW1lPjxUcm54U3RhdHVzPjI8L1RybnhTdGF0dXM+PFJldHVybkNvZGU+MDAwMDwvUmV0dXJuQ29kZT48L1RyeFJlc3BvbnNlPjwvTWVzc2FnZT48U2lnbmF0dXJlLUFsZ29yaXRobT5TSEExd2l0aFJTQTwvU2lnbmF0dXJlLUFsZ29yaXRobT48U2lnbmF0dXJlPndBdHJYWFVMYkkvZDdvSWhma25KNm1sb1YzL2tEdFBFWWROd2h6R0swL2VtTUZxTGlTckgvbS93d3FiakNKZnNLajUxdW1NazI0TUY1QXY0Ui94YUZnYVJlQUNwYStET0x6SE9RTjlCV3ByWUF0SDJ6VjBub1BablJQU1hIQkVpSXowYTYwTFptZkc0emVNbE82Z045bWJGYXA2dzh3VWFnOGFNR3VBYmRGOD08L1NpZ25hdHVyZT48L01TRz4="; + var certManager = new CertManager(); + certManager.SetupABCPubKey("Certs/TrustPay.cer"); + var result = new ResultB2BReturned(msg); + Console.WriteLine(result.Xml); + Assert.IsTrue(result.IsValid); + Assert.AreEqual(@"337100000000075339990875406344537305074275009011070410494940571FUND_TRANSFER1500000 19-080201040057934杭州翔利羽绒制品有限公司浙江分行15-740801040001733滨州六和隆达农牧有限公司山东分行2011-7-4 10:49:4920000SHA1withRSAwAtrXXULbI/d7oIhfknJ6mloV3/kDtPEYdNwhzGK0/emMFqLiSrH/m/wwqbjCJfsKj51umMk24MF5Av4R/xaFgaReACpa+DOLzHOQN9BWprYAtH2zV0noPZnRPSXHBEiIz0a60LZmfG4zeMlO6gN9mbFap6w8wUag8aMGuAbdF8=", result.Xml); + + } + + + + + [Test] + public void B2CTest() + { + const string msg = "PE1TRz48TWVzc2FnZT48VHJ4UmVzcG9uc2U+PFJldHVybkNvZGU+MDAwMDwvUmV0dXJuQ29kZT48RXJyb3JNZXNzYWdlPjwvRXJyb3JNZXNzYWdlPj" + "xFQ01lcmNoYW50VHlwZT5CMkM8L0VDTWVyY2hhbnRUeXBlPjxNZXJjaGFudElEPjEwMzQ1MjA4Mzk4MDQwNDwvTWVyY2hhbnRJRD48VHJ4VHlwZT5QYXlSZXN1bHQ8L1RyeFR5" + "cGU+PE9yZGVyTm8+VGVzdE9yZGVyPC9PcmRlck5vPjxBbW91bnQ+MTAwLjA8L0Ftb3VudD48QmF0Y2hObz4wMDAwMDE8L0JhdGNoTm8+PFZvdWNoZXJObz4xMjM0NTY8L1ZvdW" + "NoZXJObz48SG9zdERhdGU+MjAwOS8wOS8wODwvSG9zdERhdGU+PEhvc3RUaW1lPjEwOjU1OjUwPC9Ib3N0VGltZT48TWVyY2hhbnRSZW1hcmtzPlRoaXMgaXMgdGVzdCE8L01l" + "cmNoYW50UmVtYXJrcz48UGF5VHlwZT5QQVkwMTwvUGF5VHlwZT48Tm90aWZ5VHlwZT4wPC9Ob3RpZnlUeXBlPjwvVHJ4UmVzcG9uc2U+PC9NZXNzYWdlPjxTaWduYXR1cmUtQWx" + "nb3JpdGhtPlNIQTF3aXRoUlNBPC9TaWduYXR1cmUtQWxnb3JpdGhtPjxTaWduYXR1cmU+SVE4Z2pFSlRvK0FXQWxqWGVhcVVwdW43Z1JCNnNWdEhRSS9pNFdqUTNISExqZmphMT" + "F3RE5VRVUyVzBVRUNsQnp5RFZuc0tRUHE2MmliM21YRjgrSnJZTWdpYW53WDNBNlRJbkxXMXJQTldVV1AwNGl5UVIvTUxuMHNaTjJ1S2JFbU5SWXRURjNPQ0lsTHFEblppYk1tS" + "EZPTzhnektneVBxeEtaUkRRaHRZPTwvU2lnbmF0dXJlPjwvTVNHPg=="; + ; + var certManager = new CertManager(); + certManager.SetupABCPubKey("Certs/TrustPay.cer"); + var result = new ResultB2CReturned(msg); + Assert.IsTrue(result.IsValid); + Console.WriteLine(result.Xml); + Assert.AreEqual(@"0000B2C103452083980404PayResultTestOrder100.00000011234562009/09/0810:55:50This is test!PAY010SHA1withRSAIQ8gjEJTo+AWAljXeaqUpun7gRB6sVtHQI/i4WjQ3HHLjfja11wDNUEU2W0UEClBzyDVnsKQPq62ib3mXF8+JrYMgianwX3A6TInLW1rPNWUWP04iyQR/MLn0sZN2uKbEmNRYtTF3OCIlLqDnZibMmHFOO8gzKgyPqxKZRDQhtY=", result.Xml); + + } + } +} diff --git a/test/Test.cs b/test/Test.cs new file mode 100644 index 0000000..ff054f3 --- /dev/null +++ b/test/Test.cs @@ -0,0 +1,153 @@ +using System; +using System.IO; +using System.IO.Compression; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Threading; +using NUnit.Framework; + +namespace BWP.ABCClient +{ + [TestFixture] + public class Test + { + [Test] + public void B2BMockWebServer() + { + new MockWebServer("http://localhost:5000/", Encoding.UTF8, Encoding.UTF8, false); + Thread.Sleep(300000); + } + + [Test] + public void RSASendReveive() + { + const string original = "message that will be sent"; + + Sender mySender = new Sender(); + Receiver myReceiver = new Receiver(); + + byte[] toEncrypt = Encoding.Default.GetBytes(original); + + byte[] encrypted = mySender.EncryptData(myReceiver.PublicKey, toEncrypt); + + byte[] signature = mySender.HashAndSign(encrypted); + + Console.WriteLine("Original: {0}", original); + + if (myReceiver.VerifyHash(mySender.PublicKey, encrypted, signature)) { + myReceiver.DecryptData(encrypted); + } else { + Console.WriteLine("Invalid signature"); + } + } + + [Test] + public void TryReadPfx() + { + try { + X509Store store = new X509Store("MY", StoreLocation.CurrentUser); + store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly); + X509Certificate2Collection collection = store.Certificates; + X509Certificate2Collection fcollection = collection.Find(X509FindType.FindByTimeValid, DateTime.Now, false); + X509Certificate2Collection scollection = X509Certificate2UI.SelectFromCollection(fcollection, "Test Certificate Select", "Select a certificate from the following list to get information on that certificate", X509SelectionFlag.MultiSelection); + Console.WriteLine("Number of certificates: {0}{1}", scollection.Count, Environment.NewLine); + foreach (X509Certificate2 x509 in scollection) { + byte[] rawdata = x509.RawData; + Console.WriteLine("Content Type: {0}{1}", X509Certificate2.GetCertContentType(rawdata), Environment.NewLine); + Console.WriteLine("Friendly Name: {0}{1}", x509.FriendlyName, Environment.NewLine); + Console.WriteLine("Certificate Verified?: {0}{1}", x509.Verify(), Environment.NewLine); + Console.WriteLine("颁发给: {0}{1}", x509.Subject, Environment.NewLine); + Console.WriteLine("Simple Name: {0}{1}", x509.GetNameInfo(X509NameType.SimpleName, true), Environment.NewLine); + Console.WriteLine("Signature Algorithm: {0}{1}", x509.SignatureAlgorithm.FriendlyName, Environment.NewLine); + Console.WriteLine("Private Key: {0}{1}", x509.PrivateKey.ToXmlString(false), Environment.NewLine); + Console.WriteLine("Public Key: {0}{1}", x509.PublicKey.Key.ToXmlString(false), Environment.NewLine); + Console.WriteLine("Certificate Archived?: {0}{1}", x509.Archived, Environment.NewLine); + Console.WriteLine("Length of Raw Data: {0}{1}", x509.RawData.Length, Environment.NewLine); + X509Certificate2UI.DisplayCertificate(x509); + x509.Reset(); + } + store.Close(); + } catch (CryptographicException) { + Console.WriteLine("Information could not be written out for this certificate."); + } + } + + [Test] + public void VerifySignedByABC() + { + string returnMsg = "B2C1034520839804090000交易成功PayReq634333823922218745https://easyabc.95599.cn/b2c/NotCheckStatus/PaymentModeAct.ebf?TOKEN=12977568750460430844254011"; + string ABCSignature = "3+0sE/7PTfYfHYqHUwWoFzUVvr0h2HUaXpq5pr+r3+DhyvufhVUPq1We9a0E+DzqzwnW3ZD5EYLhA204o4oRiLauEzM1cj8ddXZJGAtk5ftv1OGVCV+bvts/Ei9FQp8ws5b7pNqAvIyoSbIToartR7AJ42RJsT5DxXBtFy0Y2oQ="; + var cert = new X509Certificate2("Certs/TrustPay.cer"); + var publicKey = (RSACryptoServiceProvider)cert.PublicKey.Key; + var singature = Convert.FromBase64String(ABCSignature); + var msgHash = new SHA1Managed().ComputeHash(Encoding.Default.GetBytes(returnMsg)); + Assert.IsTrue(publicKey.VerifyHash(msgHash, cert.SignatureAlgorithm.Value, singature)); + } + } + + internal class Sender + { + private readonly RSACryptoServiceProvider _privateKey; + private readonly RSACryptoServiceProvider _publicKey; + + public RSACryptoServiceProvider PublicKey + { + get { return _publicKey; } + } + + public Sender() + { + var cert = new X509Certificate2("Certs/asB.pfx", "14814622"); + + _privateKey = (RSACryptoServiceProvider)cert.PrivateKey; + _publicKey = (RSACryptoServiceProvider)cert.PublicKey.Key; + } + + public byte[] HashAndSign(byte[] encryptedData) + { + byte[] hashedData = new SHA1Managed().ComputeHash(encryptedData); + return _privateKey.SignHash(hashedData, CryptoConfig.MapNameToOID("SHA1")); + } + + public byte[] EncryptData(RSACryptoServiceProvider receiverPublicKey, byte[] toEncrypt) + { + return receiverPublicKey.Encrypt(toEncrypt, false); + } + } + + internal class Receiver + { + private readonly RSACryptoServiceProvider _privateKey; + private readonly RSACryptoServiceProvider _publicKey; + + public RSACryptoServiceProvider PublicKey + { + get { return _publicKey; } + } + + public Receiver() + { + var cert = new X509Certificate2("Certs/asC.pfx", "14814622"); + + _privateKey = (RSACryptoServiceProvider)cert.PrivateKey; + _publicKey = (RSACryptoServiceProvider)cert.PublicKey.Key; + } + + //Manually performs hash and then verifies hashed value. + public bool VerifyHash(RSACryptoServiceProvider senderPublicKey, byte[] encryptedData, byte[] signature) + { + byte[] hashedData = new SHA1Managed().ComputeHash(encryptedData); + return senderPublicKey.VerifyHash(hashedData, CryptoConfig.MapNameToOID("SHA1"), signature); + } + + //Decrypt using the private key data. + public void DecryptData(byte[] encryptedData) + { + byte[] fromEncrypt = _privateKey.Decrypt(encryptedData, false); + string roundTrip = Encoding.Default.GetString(fromEncrypt); + + Console.WriteLine("RoundTrip: {0}", roundTrip); + } + } +} \ No newline at end of file diff --git a/test/test.csproj b/test/test.csproj new file mode 100644 index 0000000..e7268c7 --- /dev/null +++ b/test/test.csproj @@ -0,0 +1,110 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {9299FD15-70F9-4E71-9AB8-DC0C081FB7B5} + Library + Properties + BWP.ABCClient + test + v4.0 + 512 + BwpApp + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + + + + tslib_version.cs + + + + + + + + + + + + + + + + {22FEFA9B-1D58-4E8A-A1FF-49CEED62C355} + BWP.ABCClient + + + + + Always + + + Always + + + Always + + + Always + + + Always + + + + + \ No newline at end of file