diff --git a/sample/ICanPay.Demo/Controllers/CancelController.cs b/sample/ICanPay.Demo/Controllers/CancelController.cs index 6de0485..5985910 100644 --- a/sample/ICanPay.Demo/Controllers/CancelController.cs +++ b/sample/ICanPay.Demo/Controllers/CancelController.cs @@ -1,5 +1,6 @@ using ICanPay.Core; using Microsoft.AspNetCore.Mvc; +using System; namespace ICanPay.Demo.Controllers { @@ -14,7 +15,7 @@ namespace ICanPay.Demo.Controllers public IActionResult Index(string id) { - var notify = CancelAlipayOrder(id); + var notify = CancelUnionpayOrder(id); return Json(notify); } @@ -44,5 +45,21 @@ namespace ICanPay.Demo.Controllers OutTradeNo = id }); } + + /// + /// 撤销银联的订单 + /// + private Unionpay.Notify CancelUnionpayOrder(string id) + { + var gateway = gateways.Get(); + + return (Unionpay.Notify)gateway.Cancel(new Unionpay.Auxiliary + { + OutRefundNo = DateTime.Now.ToString("yyyyMMddHHmmss"), + TxnTime = "20171117133822", + TradeNo = id, + RefundAmount = 0.01 + }); + } } } diff --git a/sample/ICanPay.Demo/Controllers/QueryController.cs b/sample/ICanPay.Demo/Controllers/QueryController.cs index 833f27e..e0bbbd2 100644 --- a/sample/ICanPay.Demo/Controllers/QueryController.cs +++ b/sample/ICanPay.Demo/Controllers/QueryController.cs @@ -14,7 +14,7 @@ namespace ICanPay.Demo.Controllers public IActionResult Index(string id) { - var notify = QueryAlipayOrder(id); + var notify = QueryUnionpayOrder(id); return Json(notify); } @@ -54,7 +54,8 @@ namespace ICanPay.Demo.Controllers return (Unionpay.Notify)gateway.Query(new Unionpay.Auxiliary { - OutTradeNo = id + OutTradeNo = id, + TxnTime="20171117133822" }); } } diff --git a/sample/ICanPay.Demo/Controllers/RefundController.cs b/sample/ICanPay.Demo/Controllers/RefundController.cs index d9cde47..6583d16 100644 --- a/sample/ICanPay.Demo/Controllers/RefundController.cs +++ b/sample/ICanPay.Demo/Controllers/RefundController.cs @@ -1,5 +1,6 @@ using ICanPay.Core; using Microsoft.AspNetCore.Mvc; +using System; namespace ICanPay.Demo.Controllers { @@ -14,7 +15,7 @@ namespace ICanPay.Demo.Controllers public IActionResult Index(string id) { - var notify = RefundAlipayOrder(id); + var notify = RefundUnionpayOrder(id); return Json(notify); } @@ -46,5 +47,21 @@ namespace ICanPay.Demo.Controllers OutTradeNo = id }); } + + /// + /// 银联的订单的退款 + /// + private Unionpay.Notify RefundUnionpayOrder(string id) + { + var gateway = gateways.Get(); + + return (Unionpay.Notify)gateway.Refund(new Unionpay.Auxiliary + { + OutRefundNo = DateTime.Now.ToString("yyyyMMddHHmmss"), + TxnTime = "20171117133822", + TradeNo = id, + RefundAmount = 0.01 + }); + } } } diff --git a/src/ICanPay.Core/Gateways/GatewayBase.cs b/src/ICanPay.Core/Gateways/GatewayBase.cs index 653998a..6a51099 100644 --- a/src/ICanPay.Core/Gateways/GatewayBase.cs +++ b/src/ICanPay.Core/Gateways/GatewayBase.cs @@ -48,6 +48,17 @@ namespace ICanPay.Core Merchant = merchant; } + /// + /// 构造函数 + /// + /// 商户数据 + /// 网关数据 + protected GatewayBase(IMerchant merchant, GatewayData gatewayData) + { + Merchant = merchant; + GatewayData = gatewayData; + } + /// /// 构造函数 /// diff --git a/src/ICanPay.Core/Gateways/GatewayData.cs b/src/ICanPay.Core/Gateways/GatewayData.cs index 7786aae..223affe 100644 --- a/src/ICanPay.Core/Gateways/GatewayData.cs +++ b/src/ICanPay.Core/Gateways/GatewayData.cs @@ -40,11 +40,23 @@ namespace ICanPay.Core #region 构造函数 + /// + /// 构造函数 + /// public GatewayData() { _values = new SortedDictionary(); } + /// + /// 构造函数 + /// + /// 排序策略 + public GatewayData(IComparer comparer) + { + _values = new SortedDictionary(comparer); + } + #endregion #region 方法 diff --git a/src/ICanPay.Unionpay/Auxiliary.cs b/src/ICanPay.Unionpay/Auxiliary.cs new file mode 100644 index 0000000..55252e9 --- /dev/null +++ b/src/ICanPay.Unionpay/Auxiliary.cs @@ -0,0 +1,97 @@ +using ICanPay.Core; +using System; +using System.ComponentModel.DataAnnotations; + +namespace ICanPay.Unionpay +{ + public class Auxiliary : IAuxiliary + { + /// + /// 商户订单号,不应含“-”或“_” + /// + [Necessary(GatewayAuxiliaryType.Query)] + [ReName(Name = Constant.ORDERID)] + [StringLength(40, MinimumLength = 8, ErrorMessage = "商户订单号最小长度为8位,最大长度为40位")] + public string OutTradeNo { get; set; } + + /// + /// 撤销单号 + /// + [Necessary(GatewayAuxiliaryType.Cancel)] + [ReName(Name = Constant.ORDERID)] + [StringLength(40, MinimumLength = 8, ErrorMessage = "商户订单号最小长度为8位,最大长度为40位")] + public string OutRefundNo { get; set; } + + /// + /// 订单发送时间 格式 年年年年月月日日时时分分秒秒 + /// + [Required] + public string TxnTime { get; set; } + + /// + /// 原消费的QueryId + /// + [Necessary(GatewayAuxiliaryType.Cancel)] + [ReName(Name = Constant.ORIGQRYID)] + [StringLength(21, MinimumLength = 21, ErrorMessage = "原消费的QueryId长度为21位")] + public string TradeNo { get; set; } + + /// + /// 需要退款的金额,单位为元 + /// + [ReName(Name = Constant.TXNAMT)] + [Necessary(GatewayAuxiliaryType.Cancel, ErrorMessage = "请设置退款金额")] + public double? RefundAmount + { + get => _refundAmount; + set + { + if (value.HasValue) + { + _refundAmount = value.Value * 100; + } + } + } + private double? _refundAmount; + + /// + /// 商户自定义保留域,交易应答时会原样返回 + /// + [StringLength(1024, ErrorMessage = "商户自定义保留域最大长度为1024位")] + public string ReqReserved { get; set; } + + public string RefundNo { get; set; } + + /// + /// 退款的原因说明 + /// + [StringLength(256, ErrorMessage = "退款的原因说明最大长度为256位")] + public string RefundReason { get; set; } + + /// + /// 账单类型,商户通过接口或商户经开放平台授权后其所属服务商通过接口可以获取以下 + /// 账单类型:trade、signcustomer;trade指商户基于支付宝交易收单的业务账单; + /// signcustomer是指基于商户支付宝余额收入及支出等资金变动的帐务账单; + /// + [StringLength(10, ErrorMessage = "账单类型最大长度为10位")] + [Necessary(GatewayAuxiliaryType.BillDownload, ErrorMessage = "请设置账单类型")] + public string BillType { get; set; } + + /// + /// 账单时间:日账单格式为yyyy-MM-dd,月账单格式为yyyy-MM。 + /// + [StringLength(15, ErrorMessage = "账单时间最大长度为15位")] + [Necessary(GatewayAuxiliaryType.BillDownload, ErrorMessage = "请设置账单时间")] + public string BillDate { get; set; } + + public bool Validate(GatewayAuxiliaryType gatewayAuxiliaryType) + { + //if (string.IsNullOrEmpty(OutTradeNo) && string.IsNullOrEmpty(TradeNo)) + //{ + // throw new ArgumentNullException("商户订单号和支付宝订单号不可同时为空"); + //} + + return true; + } + } +} diff --git a/src/ICanPay.Unionpay/Constant.cs b/src/ICanPay.Unionpay/Constant.cs index 130e44f..a1ce1af 100644 --- a/src/ICanPay.Unionpay/Constant.cs +++ b/src/ICanPay.Unionpay/Constant.cs @@ -13,5 +13,6 @@ namespace ICanPay.Unionpay public const string RESPCODE = "respCode"; public const string RESPMSG = "respMsg"; public const string ORDERDESC = "orderDesc"; + public const string ORIGQRYID = "origQryId"; } } diff --git a/src/ICanPay.Unionpay/Merchant.cs b/src/ICanPay.Unionpay/Merchant.cs index 6912c39..c6237f6 100644 --- a/src/ICanPay.Unionpay/Merchant.cs +++ b/src/ICanPay.Unionpay/Merchant.cs @@ -150,6 +150,6 @@ namespace ICanPay.Unionpay /// /// 渠道类型 /// - public string ChannelType => "08"; + public string ChannelType { get; internal set; } = "08"; } } diff --git a/src/ICanPay.Unionpay/Notify.cs b/src/ICanPay.Unionpay/Notify.cs index 20c4d49..0fec13e 100644 --- a/src/ICanPay.Unionpay/Notify.cs +++ b/src/ICanPay.Unionpay/Notify.cs @@ -260,5 +260,20 @@ namespace ICanPay.Unionpay /// 二维码 /// public string QrCode { get; set; } + + /// + /// 发卡机构识别模式 + /// + public string IssuerIdentifyMode { get; set; } + + /// + /// 原交易应答码 + /// + public string OrigRespCode { get; set; } + + /// + /// 原交易应答信息 + /// + public string OrigRespMsg { get; set; } } } diff --git a/src/ICanPay.Unionpay/Order.cs b/src/ICanPay.Unionpay/Order.cs index 3f02cda..788b133 100644 --- a/src/ICanPay.Unionpay/Order.cs +++ b/src/ICanPay.Unionpay/Order.cs @@ -115,5 +115,11 @@ namespace ICanPay.Unionpay /// 终端号 /// public string TermId { get; set; } + + /// + /// 二维码编号 + /// + [Necessary(GatewayTradeType.Barcode)] + public string QrNo { get; set; } } } diff --git a/src/ICanPay.Unionpay/UnionpayGateway.cs b/src/ICanPay.Unionpay/UnionpayGateway.cs index b0b329b..175d4db 100644 --- a/src/ICanPay.Unionpay/UnionpayGateway.cs +++ b/src/ICanPay.Unionpay/UnionpayGateway.cs @@ -11,7 +11,8 @@ namespace ICanPay.Unionpay /// public class UnionpayGateway : GatewayBase, - IFormPayment, IAppPayment,IScanPayment + IFormPayment, IAppPayment, IScanPayment, IBarcodePayment, + IQuery, ICancel, IRefund { #region 私有字段 @@ -19,6 +20,7 @@ namespace ICanPay.Unionpay private const string FRONTGATEWAYURL = "https://gateway.test.95516.com/gateway/api/frontTransReq.do"; private const string APPGATEWAYURL = "https://gateway.test.95516.com/gateway/api/appTransReq.do"; private const string BACKGATEWAYURL = "https://gateway.test.95516.com/gateway/api/backTransReq.do"; + private const string QUERYGATEWAYURL = "https://gateway.test.95516.com/gateway/api/queryTrans.do"; private readonly Merchant _merchant; @@ -31,7 +33,7 @@ namespace ICanPay.Unionpay /// /// 商户数据 public UnionpayGateway(Merchant merchant) - : base(merchant) + : base(merchant, new GatewayData(StringComparer.Ordinal)) { _merchant = merchant; _merchant.CertId = Util.GetCertId(merchant.CertPath, merchant.CertPwd); @@ -127,6 +129,100 @@ namespace ICanPay.Unionpay #endregion + #region 条码支付 + + public void BuildBarcodePayment() + { + InitBarcodePayment(); + + Commit(); + } + + public void InitBarcodePayment() + { + Merchant.TxnSubType = "06"; + Merchant.BizType = "000000"; + Merchant.ChannelType = "07"; + InitOrderParameter(); + GatewayUrl = BACKGATEWAYURL; + } + + #endregion + + #region 查询订单 + + public INotify BuildQuery(IAuxiliary auxiliary) + { + InitQuery(auxiliary); + + Commit(); + + return Notify; + } + + public void InitQuery(IAuxiliary auxiliary) + { + Merchant.TxnType = "00"; + Merchant.BizType = "000000"; + GatewayUrl = QUERYGATEWAYURL; + + InitAuxiliaryParameter(auxiliary); + } + + #endregion + + #region 撤销订单 + + public INotify BuildCancel(IAuxiliary auxiliary) + { + InitCancel(auxiliary); + + Commit(); + + return Notify; + } + + public void InitCancel(IAuxiliary auxiliary) + { + Merchant.TxnType = "31"; + GatewayUrl = BACKGATEWAYURL; + + InitAuxiliaryParameter(auxiliary); + } + + #endregion + + #region 订单退款 + + public INotify BuildRefund(IAuxiliary auxiliary) + { + InitRefund(auxiliary); + + Commit(); + + return Notify; + } + + public void InitRefund(IAuxiliary auxiliary) + { + Merchant.TxnType = "04"; + GatewayUrl = BACKGATEWAYURL; + + InitAuxiliaryParameter(auxiliary); + } + + #endregion + + private void InitAuxiliaryParameter(IAuxiliary auxiliary) + { + Merchant.TxnSubType = "00"; + Merchant.ChannelType = "07"; + + GatewayData.Add(Merchant, StringCase.Camel); + GatewayData.Add(auxiliary, StringCase.Camel); + GatewayData.Add(Constant.SIGNATURE, BuildSign()); + } + private void InitOrderParameter() { GatewayData.Add(Merchant, StringCase.Camel); diff --git a/src/ICanPay.Wechatpay/Auxiliary.cs b/src/ICanPay.Wechatpay/Auxiliary.cs index 01e9bab..2a7fb1e 100644 --- a/src/ICanPay.Wechatpay/Auxiliary.cs +++ b/src/ICanPay.Wechatpay/Auxiliary.cs @@ -46,11 +46,22 @@ namespace ICanPay.Wechatpay private double _amount; /// - /// 退款金额 + /// 退款金额,单位元 /// [ReName(Name = Constant.REFUND_FEE)] [Necessary(GatewayAuxiliaryType.Refund)] - public double? RefundAmount { get; set; } + public double? RefundAmount + { + get => _refundAmount; + set + { + if (value.HasValue) + { + _refundAmount = value.Value * 100; + } + } + } + private double? _refundAmount; /// /// 货币种类