添加银联商务网关

This commit is contained in:
Varorbc
2020-01-04 18:58:48 +08:00
parent 503ee543d1
commit 592aa6e152
23 changed files with 1355 additions and 3 deletions
+6
View File
@@ -38,6 +38,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PaySharp.Allinpay", "src\PaySharp.Allinpay\PaySharp.Allinpay.csproj", "{5D183F2A-BBB4-4B3E-9951-4584A70ECB60}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PaySharp.Netpay", "src\PaySharp.Netpay\PaySharp.Netpay.csproj", "{1F2BA5BA-6915-4D5C-AD42-55AF4A42FCB2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -84,6 +86,10 @@ Global
{5D183F2A-BBB4-4B3E-9951-4584A70ECB60}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5D183F2A-BBB4-4B3E-9951-4584A70ECB60}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5D183F2A-BBB4-4B3E-9951-4584A70ECB60}.Release|Any CPU.Build.0 = Release|Any CPU
{1F2BA5BA-6915-4D5C-AD42-55AF4A42FCB2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1F2BA5BA-6915-4D5C-AD42-55AF4A42FCB2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1F2BA5BA-6915-4D5C-AD42-55AF4A42FCB2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1F2BA5BA-6915-4D5C-AD42-55AF4A42FCB2}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
+17 -3
View File
@@ -360,9 +360,23 @@ namespace PaySharp.Core
/// <returns></returns>
public string ToUrl(bool isUrlEncode = true)
{
return string.Join("&",
_values
.Select(a => $"{a.Key}={(isUrlEncode ? WebUtility.UrlEncode(a.Value.ToString()) : a.Value.ToString())}"));
var values = _values.Select(a =>
{
var value = a.Value.ToString();
if (a.Value is bool)
{
value = value.ToLower();
}
if (isUrlEncode)
{
value = WebUtility.UrlEncode(value);
}
return $"{a.Key}={value}";
});
return string.Join("&", values);
}
/// <summary>
+53
View File
@@ -0,0 +1,53 @@
using System;
using System.ComponentModel.DataAnnotations;
using PaySharp.Core;
using PaySharp.Core.Utils;
namespace PaySharp.Netpay.Domain
{
/// <summary>
/// 基础模型
/// </summary>
public class BaseModel
{
/// <summary>
/// 消息编号
/// </summary>
[StringLength(64, ErrorMessage = "消息编号最大长度为64位")]
public string MsgId { get; set; } = Util.GenerateNonceStr();
/// <summary>
/// 消息类型
/// </summary>
/// <remarks>
/// 微信:wx.appPreOrder(存量商户需改为新的msgType)
/// 支付宝:trade.precreate
/// 全民付:qmf.order
/// 银联云闪付(走银联全渠道):uac.appOrder
/// ApplePay支付:applepay.order
/// 查询订单:query
/// </remarks>
[Required(ErrorMessage = "请设置消息类型")]
[StringLength(64, MinimumLength = 1, ErrorMessage = "消息类型,最小长度为1位,最大长度为64位")]
public string MsgType { get; set; }
/// <summary>
/// 报文请求时间
/// </summary>
[Required(ErrorMessage = "请设置报文请求时间")]
public string RequestTimestamp { get; set; } = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
/// <summary>
/// 商户的交易订单号
/// </summary>
[ReName(Name = "merOrderId")]
[StringLength(32, MinimumLength = 6, ErrorMessage = "商户的交易订单号,最小长度为6位,最大长度为32位")]
[Required(ErrorMessage = "请设置商户的交易订单号")]
public string OutTradeNo { get; set; }
/// <summary>
/// 请求系统预留字段
/// </summary>
public string SrcReserve { get; set; }
}
}
+71
View File
@@ -0,0 +1,71 @@
using PaySharp.Core;
namespace PaySharp.Netpay.Domain
{
/// <summary>
/// 商品模型
/// </summary>
public class Goods
{
/// <summary>
/// 折扣
/// </summary>
public long Discount { get; set; }
/// <summary>
/// 单位
/// </summary>
public string Unit { get; set; }
/// <summary>
/// 商品编号
/// </summary>
[ReName(Name = "goodsId")]
public string Id { get; set; }
/// <summary>
/// 商品名称
/// </summary>
[ReName(Name = "goodsName")]
public string Name { get; set; }
/// <summary>
/// 商品数量
/// </summary>
public long Quantity { get; set; }
/// <summary>
/// 商品单价(分)
/// </summary>
public long Price { get; set; }
/// <summary>
/// 商品分类
/// </summary>
[ReName(Name = "goodsCategory")]
public string Category { get; set; }
/// <summary>
/// 商品说明
/// </summary>
public string Body { get; set; }
/// <summary>
/// 子商户号
/// </summary>
[ReName(Name = "subMerchantId")]
public string SubMchId { get; set; }
/// <summary>
/// 商户子订单号
/// </summary>
[ReName(Name = "merOrderId")]
public string OutTradeNo { get; set; }
/// <summary>
/// 子商户商品总额
/// </summary>
[ReName(Name = "subOrderAmount")]
public int TotalAmount { get; set; }
}
}
+25
View File
@@ -0,0 +1,25 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using PaySharp.Core;
namespace PaySharp.Netpay.Domain
{
public class QueryModel : BaseModel, IValidatableObject
{
/// <summary>
/// 交易单号
/// </summary>
[ReName(Name = "targetOrderId")]
public string TradeNo { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (string.IsNullOrEmpty(OutTradeNo) && string.IsNullOrEmpty(TradeNo))
{
yield return new ValidationResult("商户订单号和交易单号不能同时为空");
}
yield return ValidationResult.Success;
}
}
}
+24
View File
@@ -0,0 +1,24 @@
using PaySharp.Core;
namespace PaySharp.Netpay.Domain
{
public class SubOrders
{
/// <summary>
/// 子商户号
/// </summary>
[ReName(Name = "mid")]
public string MchId { get; set; }
/// <summary>
/// 商户子订单号
/// </summary>
[ReName(Name = "merOrderId")]
public string OutTradeNo { get; set; }
/// <summary>
/// 子商户分账金额
/// </summary>
public int TotalAmount { get; set; }
}
}
@@ -0,0 +1,115 @@
using System;
using System.ComponentModel.DataAnnotations;
using PaySharp.Core;
namespace PaySharp.Netpay.Domain
{
public class UnifiedPayModel : BaseModel
{
/// <summary>
/// 商品信息
/// </summary>
public Goods Goods { get; set; }
/// <summary>
/// 商户附加数据
/// </summary>
[StringLength(255, ErrorMessage = "商户附加数据最大长度为64位")]
public string AttachedData { get; set; }
/// <summary>
/// 订单过期时间
/// </summary>
public string ExpireTime { get; set; } = DateTime.Now.AddMinutes(30).ToString("yyyy-MM-dd HH:mm:ss");
/// <summary>
/// 商品标记,用于优惠活动
/// </summary>
[StringLength(32, ErrorMessage = "商品标记最大长度为32位")]
public string GoodsTag { get; set; }
/// <summary>
/// 商品交易单号
/// 跟goods字段二选一,商品信息通过goods.add接口提前上送
/// </summary>
public string GoodsTradeNo { get; set; }
/// <summary>
/// 商品描述
/// </summary>
[ReName(Name = "orderDesc")]
[StringLength(255, ErrorMessage = "商品描述最大长度为255位")]
public string Body { get; set; }
/// <summary>
/// 订单原始金额,单位分,用于记录前端系统打折前的金额
/// </summary>
public int OriginalAmount { get; set; }
/// <summary>
/// 商品编号
/// </summary>
public string ProductId { get; set; }
/// <summary>
/// 支付总金额,单位为分
/// </summary>
/// <remarks>
/// 若<see cref="DivisionFlag"/>为true,则:totalAmount = subOrders字段中的所有totalAmount值之和
/// 加上platformAmount值 = goods中的所有subOrderAmount值之和。
/// </remarks>
public int TotalAmount { get; set; }
/// <summary>
/// 分账标记
/// 暂时只支持微信(WXPay.jsPay)、支付宝(trade.jsPay)支付 和 银联云闪付(uac.appOrder)
/// 若为true,则goods字段和subOrders字段不能同时为空;且secureTransaction字段上送false或不上送。
/// </summary>
public bool DivisionFlag { get; set; }
/// <summary>
/// 平台商户分账金额
/// </summary>
/// <remarks>若分账标记传,则分账金额必传</remarks>
public int? PlatformAmount { get; set; }
/// <summary>
/// 在传分账标记的情况下,若传子商户号,子商户分账金额必传,即ubOrders每个元素的mid和totalAmount非空 且 mid不超过20个。
/// 分账方案subOrders里子商户分账总额+platformAmount要与支付总额totalAmount相等
/// </summary>
public SubOrders SubOrders { get; set; }
/// <summary>
/// 交易类型
/// 微信必传:APP
/// </summary>
public string TradeType { get; set; }
/// <summary>
/// 商户用户号
/// </summary>
[ReName(Name = "merchantUserId")]
[StringLength(32, ErrorMessage = "商户用户号最大长度为32位")]
public string MchUserId { get; set; }
/// <summary>
/// 手机号
/// </summary>
[StringLength(11, ErrorMessage = "手机号最大长度为11位")]
public string Mobile { get; set; }
/// <summary>
/// 标识是否是担保交易
/// 若上送为true,则交易的金额将会被暂缓结算。
/// 调用担保完成接口后,完成部分金额会在t+1日结算给商户,剩余部分金额退还用户。
/// 调用担保撤销接口,则全部资金退还给用户。
/// 30天后 没有主动调用担保完成 且 没有主动调用担保撤销的交易 将会自动按撤销处理。
/// </summary>
public bool SecureTransaction { get; set; }
/// <summary>
/// 是否需要限制信用卡支付
/// </summary>
public bool LimitCreditCard { get; set; }
}
}
+73
View File
@@ -0,0 +1,73 @@
using System.ComponentModel.DataAnnotations;
using PaySharp.Core;
namespace PaySharp.Netpay
{
public class Merchant : IMerchant
{
#region 属性
/// <summary>
/// 终端号
/// </summary>
[ReName(Name = "tid")]
[Required(ErrorMessage = "请输入终端号")]
[StringLength(32, MinimumLength = 2, ErrorMessage = "终端号最大长度为32位")]
public string AppId { get; set; }
/// <summary>
/// 微信子AppId
/// </summary>
/// <remarks>只对微信支付有效</remarks>
[StringLength(32, ErrorMessage = "微信子AppId最大长度为32位")]
public string SubAppId { get; set; }
/// <summary>
/// 商户号
/// </summary>
[ReName(Name = "mid")]
[Required(ErrorMessage = "请设置商户号")]
[StringLength(32, MinimumLength = 8, ErrorMessage = "商户号最大长度为32位")]
public string MchId { get; set; }
/// <summary>
/// 机构商户号
/// </summary>
[ReName(Name = "instMid")]
[StringLength(32, MinimumLength = 8, ErrorMessage = "机构商户号最大长度为32位")]
public string InstMchId { get; set; }
/// <summary>
/// MD5交易密钥
/// </summary>
[Required(ErrorMessage = "请设置MD5交易密钥")]
[Ignore]
public string Key { get; set; }
/// <summary>
/// 来源
/// </summary>
[ReName(Name = "msgSrc")]
[Required(ErrorMessage = "请设置来源")]
public string Source { get; set; }
/// <summary>
/// 签名类型
/// </summary>
public string SignType => "MD5";
/// <summary>
/// 返回地址
/// </summary>
[ReName(Name = "showUrl")]
public string ReturnUrl { get; set; }
/// <summary>
/// 网关回发通知URL
/// </summary>
[Required(ErrorMessage = "请输入网关回发通知URL")]
public string NotifyUrl { get; set; }
#endregion
}
}
+102
View File
@@ -0,0 +1,102 @@
#if NETCOREAPP3_1
using Microsoft.Extensions.Options;
#endif
using System.Threading.Tasks;
using PaySharp.Core;
using PaySharp.Core.Exceptions;
using PaySharp.Core.Request;
using PaySharp.Core.Utils;
using PaySharp.Netpay.Response;
namespace PaySharp.Netpay
{
/// <summary>
/// 银联商务网关
/// </summary>
public sealed class NetpayGateway : BaseGateway
{
#region 私有字段
private readonly Merchant _merchant;
#endregion
#region 构造函数
/// <summary>
/// 初始化银联商务网关
/// </summary>
/// <param name="merchant">商户数据</param>
public NetpayGateway(Merchant merchant)
: base(merchant)
{
_merchant = merchant;
}
#if NETCOREAPP3_1
/// <summary>
/// 初始化银联商务网关
/// </summary>
/// <param name="merchant">商户数据</param>
public NetpayGateway(IOptions<Merchant> merchant)
: this(merchant.Value)
{
}
#endif
#endregion
#region 属性
public override string GatewayUrl { get; set; } = "https://qr.chinaums.com";
public new NotifyResponse NotifyResponse => (NotifyResponse)base.NotifyResponse;
protected override bool IsPaySuccess => NotifyResponse.TradeStatus == "TRADE_SUCCESS";
protected override bool IsRefundSuccess => NotifyResponse.TradeStatus == "TRADE_REFUND";
protected override bool IsCancelSuccess { get; }
protected override string[] NotifyVerifyParameter => new string[]
{
"tid", "mid", "merOrderId"
};
#endregion
#region 方法
protected override async Task<bool> ValidateNotifyAsync()
{
base.NotifyResponse = await GatewayData.ToObjectAsync<NotifyResponse>(StringCase.Camel);
base.NotifyResponse.Raw = GatewayData.Raw;
if (!SubmitProcess.CheckSign(GatewayData, _merchant.Key, NotifyResponse.Sign))
{
throw new GatewayException("签名不一致");
}
return true;
}
protected override void WriteSuccessFlag()
{
HttpUtil.Write("SUCCESS");
}
protected override void WriteFailureFlag()
{
HttpUtil.Write("FAILED");
}
public override TResponse Execute<TModel, TResponse>(Request<TModel, TResponse> request)
{
return SubmitProcess.Execute(_merchant, request, GatewayUrl);
}
#endregion
}
}
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="../../package.props" />
<PropertyGroup>
<TargetFrameworks>net45;netcoreapp3.1</TargetFrameworks>
<Title>PaySharp.Netpay</Title>
<Description>通联收银宝</Description>
<PackageTags>dotnetcore;pay;Netpay;</PackageTags>
<PackageReleaseNotes>
</PackageReleaseNotes>
</PropertyGroup>
<Import Project="../../output.props" />
<Import Project="../../dependencies.props" />
</Project>
Binary file not shown.
@@ -0,0 +1,60 @@
#if NET45
using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
using PaySharp.Netpay;
namespace PaySharp.Core.Mvc
{
public static class PaySharpConfigExtensions
{
public static IGateways RegisterNetpay(this IGateways gateways, Action<Merchant> action)
{
if (action != null)
{
var merchant = new Merchant();
action(merchant);
gateways.Add(new NetpayGateway(merchant));
}
return gateways;
}
public static IGateways RegisterNetpay(this IGateways gateways)
{
var merchants = (List<Hashtable>)ConfigurationManager.GetSection("paySharp/Netpays");
if (merchants == null)
{
return gateways;
}
foreach (var item in merchants)
{
var netpayGateway = new NetpayGateway(new Merchant
{
AppId = item["appId"].ToString(),
SubAppId = item["subAppId"].ToString(),
MchId = item["mchId"].ToString(),
InstMchId = item["instMchId"].ToString(),
Key = item["key"].ToString(),
Source = item["source"].ToString(),
NotifyUrl = item["notifyUrl"].ToString(),
ReturnUrl = item["returnUrl"].ToString()
});
var gatewayUrl = item["gatewayUrl"].ToString();
if (!string.IsNullOrEmpty(gatewayUrl))
{
netpayGateway.GatewayUrl = gatewayUrl;
}
gateways.Add(netpayGateway);
}
return gateways;
}
}
}
#endif
@@ -0,0 +1,29 @@
using PaySharp.Core.Request;
using PaySharp.Core.Response;
using PaySharp.Core.Utils;
namespace PaySharp.Netpay.Request
{
public class BaseRequest<TModel, TResponse> : Request<TModel, TResponse> where TResponse : IResponse
{
public BaseRequest()
{
RequestUrl = "/netpay-route-server/api/";
}
public override void AddGatewayData(TModel model)
{
base.AddGatewayData(model);
GatewayData.Add(model, StringCase.Camel);
}
internal virtual void Execute(Merchant merchant)
{
if (!string.IsNullOrEmpty(NotifyUrl))
{
GatewayData.Add("notifyUrl", NotifyUrl);
}
}
}
}
@@ -0,0 +1,13 @@
using PaySharp.Netpay.Domain;
using PaySharp.Netpay.Response;
namespace PaySharp.Netpay.Request
{
public class QueryRequest : BaseRequest<QueryModel, QueryResponse>
{
internal override void Execute(Merchant merchant)
{
GatewayData.Remove("notifyUrl");
}
}
}
@@ -0,0 +1,9 @@
using PaySharp.Netpay.Domain;
using PaySharp.Netpay.Response;
namespace PaySharp.Netpay.Request
{
public class UnifiedPayRequest : BaseRequest<UnifiedPayModel, UnifiedPayResponse>
{
}
}
@@ -0,0 +1,162 @@
using PaySharp.Core;
using PaySharp.Core.Request;
using PaySharp.Core.Response;
namespace PaySharp.Netpay.Response
{
public abstract class BaseResponse : IResponse
{
/// <summary>
/// 平台错误码
/// </summary>
public string ErrCode { get; set; }
/// <summary>
/// 平台错误信息
/// </summary>
public string ErrMsg { get; set; }
/// <summary>
/// 消息编号
/// </summary>
public string MsgId { get; set; }
/// <summary>
/// 来源
/// </summary>
[ReName(Name = "msgSrc")]
public string Source { get; set; }
/// <summary>
/// 消息类型
/// </summary>
public string MsgType { get; set; }
/// <summary>
/// 请求系统预留字段
/// </summary>
public string SrcReserve { get; set; }
/// <summary>
/// 报文响应时间
/// </summary>
public string ResponseTimestamp { get; set; }
/// <summary>
/// 商户名称
/// </summary>
[ReName(Name = "merName")]
public string MchName { get; set; }
/// <summary>
/// 商户号
/// </summary>
[ReName(Name = "mid")]
public string MchId { get; set; }
/// <summary>
/// 商户订单号
/// </summary>
[ReName(Name = "merOrderId")]
public string OutTradeNo { get; set; }
/// <summary>
/// 终端号
/// </summary>
[ReName(Name = "tid")]
public string AppId { get; set; }
/// <summary>
/// 平台流水号
/// </summary>
[ReName(Name = "seqId")]
public string TradeNo { get; set; }
/// <summary>
/// 清分ID,如果来源方传了bankRefId就等于bankRefId,否则等于seqId
/// </summary>
public string SettleRefId { get; set; }
/// <summary>
/// 交易状态
/// NEW_ORDER 新订单
/// UNKNOWN 不明确的交易状态
/// TRADE_CLOSED 在指定时间段内未支付时关闭的交易;在交易完成撤销成功时关闭的交易;支付失败的交易。
/// WAIT_BUYER_PAY  交易创建,等待买家付款。
/// TRADE_SUCCESS 支付成功
/// TRADE_REFUND 订单转入退货流程
/// </summary>
[ReName(Name = "status")]
public string TradeStatus { get; set; }
/// <summary>
/// 支付总金额
/// </summary>
public int TotalAmount { get; set; }
/// <summary>
/// 连接系统
/// </summary>
public string ConnectSys { get; set; }
/// <summary>
/// 渠道订单号
/// </summary>
[ReName(Name = "targetOrderId")]
public string ChannelTradeNo { get; set; }
/// <summary>
/// 渠道代码
/// Alipay 1.0 支付宝1.0协议
/// Alipay 2.0 支付宝2.0协议
/// WXPay 微信
/// YQB 壹钱包
/// QMF 全民付远程快捷
/// UnionPay 银联钱包
/// BaiDu 百度钱包
/// JD 京东钱包
/// SF 顺丰顺手付
/// COMM 交通银行
/// BestPay 翼支付
/// ACP 银联全渠道立码付
/// NetPayBills 银商网付平台账单模块
/// NetPayGtwy 银商网付平台网关模块
/// QmfWebPay POS通插件WEB版
/// UAC 银联全渠道
/// </summary>
[ReName(Name = "targetSys")]
public string ChannelCode { get; set; }
/// <summary>
/// 渠道状态
/// </summary>
[ReName(Name = "targetStatus")]
public string ChannelStatus { get; set; }
/// <summary>
/// 渠道商户号
/// </summary>
[ReName(Name = "targetMid")]
public string ChannelMchId { get; set; }
/// <summary>
/// 营销联盟优惠金额
/// </summary>
[ReName(Name = "yxlmAmount")]
public int MarketingAffiliateAmount { get; set; }
/// <summary>
/// 签名
/// </summary>
public string Sign { get; set; }
/// <summary>
/// 原始值
/// </summary>
public string Raw { get; set; }
internal GatewayData GatewayData { get; set; }
internal abstract void Execute<TModel, TResponse>(Merchant merchant, Request<TModel, TResponse> request) where TResponse : IResponse;
}
}
@@ -0,0 +1,257 @@
using System;
using PaySharp.Core;
using PaySharp.Core.Response;
namespace PaySharp.Netpay.Response
{
public class NotifyResponse : IResponse
{
/// <summary>
/// 商户号
/// </summary>
[ReName(Name = "mid")]
public string MchId { get; set; }
/// <summary>
/// 终端号
/// </summary>
[ReName(Name = "tid")]
public string AppId { get; set; }
/// <summary>
/// 机构商户号
/// </summary>
[ReName(Name = "instMid")]
public string InstMchId { get; set; }
/// <summary>
/// 商户附加数据
/// </summary>
public string AttachedData { get; set; }
/// <summary>
/// 银行卡号
/// </summary>
public string BankCardNo { get; set; }
/// <summary>
/// 支付渠道列表
/// </summary>
public string BillFunds { get; set; }
/// <summary>
/// 支付渠道描述
/// </summary>
public string BillFundsDesc { get; set; }
/// <summary>
/// 买家编号
/// </summary>
public string BuyerId { get; set; }
/// <summary>
/// 子买家编号
/// </summary>
public string SubBuyerId { get; set; }
/// <summary>
/// 买家用户名
/// </summary>
public string BuyerUsername { get; set; }
/// <summary>
/// 买家付款的金额
/// </summary>
public int BuyerPayAmount { get; set; }
/// <summary>
/// 支付总金额
/// </summary>
public int TotalAmount { get; set; }
/// <summary>
/// 发票金额
/// </summary>
public int InvoiceAmount { get; set; }
/// <summary>
/// 实收金额
/// </summary>
public int ReceiptAmount { get; set; }
/// <summary>
/// 退款金额
/// </summary>
public int RefundAmount { get; set; }
/// <summary>
/// 退款说明
/// </summary>
public string RefundDesc { get; set; }
/// <summary>
/// 渠道退款号
/// </summary>
[ReName(Name = "refundTargetOrderId")]
public string ChannelRefundNo { get; set; }
/// <summary>
/// 退款时间
/// </summary>
[ReName(Name = "refundPayTime")]
public DateTime? RefundTime { get; set; }
/// <summary>
/// 退款结算日期
/// </summary>
[ReName(Name = "refundPayTime")]
public DateTime? RefundSettleDate { get; set; }
/// <summary>
/// 商户订单号
/// </summary>
[ReName(Name = "merOrderId")]
public string OutTradeNo { get; set; }
/// <summary>
/// 支付时间
/// </summary>
public DateTime PayTime { get; set; }
/// <summary>
/// 检索参考号,用在银联体系交易中
/// </summary>
public string RefId { get; set; }
/// <summary>
/// 平台流水号
/// </summary>
[ReName(Name = "seqId")]
public string TradeNo { get; set; }
/// <summary>
/// 结算日期
/// </summary>
public DateTime SettleDate { get; set; }
/// <summary>
/// 交易状态
/// NEW_ORDER 新订单
/// UNKNOWN 不明确的交易状态
/// TRADE_CLOSED 在指定时间段内未支付时关闭的交易;在交易完成撤销成功时关闭的交易;支付失败的交易。
/// WAIT_BUYER_PAY  交易创建,等待买家付款。
/// TRADE_SUCCESS 支付成功
/// TRADE_REFUND 订单转入退货流程
/// </summary>
[ReName(Name = "status")]
public string TradeStatus { get; set; }
/// <summary>
/// 连接系统
/// </summary>
public string ConnectSys { get; set; }
/// <summary>
/// 渠道订单号
/// </summary>
[ReName(Name = "targetOrderId")]
public string ChannelTradeNo { get; set; }
/// <summary>
/// 渠道代码
/// Alipay 1.0 支付宝1.0协议
/// Alipay 2.0 支付宝2.0协议
/// WXPay 微信
/// YQB 壹钱包
/// QMF 全民付远程快捷
/// UnionPay 银联钱包
/// BaiDu 百度钱包
/// JD 京东钱包
/// SF 顺丰顺手付
/// COMM 交通银行
/// BestPay 翼支付
/// ACP 银联全渠道立码付
/// NetPayBills 银商网付平台账单模块
/// NetPayGtwy 银商网付平台网关模块
/// QmfWebPay POS通插件WEB版
/// UAC 银联全渠道
/// </summary>
[ReName(Name = "targetSys")]
public string ChannelCode { get; set; }
/// <summary>
/// 营销联盟优惠金额
/// </summary>
[ReName(Name = "yxlmAmount")]
public int MarketingAffiliateAmount { get; set; }
/// <summary>
/// 签名
/// </summary>
public string Sign { get; set; }
/// <summary>
/// 商户出资优惠金额
/// </summary>
public int CouponMerchantContribute { get; set; }
/// <summary>
/// 其他出资优惠金额
/// </summary>
public int CouponOtherContribute { get; set; }
/// <summary>
/// 微信活动编号
/// </summary>
public string ActivityIds { get; set; }
/// <summary>
/// 订单详情
/// </summary>
[ReName(Name = "orderDesc")]
public string Body { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public string CreateTime { get; set; }
/// <summary>
/// 商户UUID
/// </summary>
[ReName(Name = "mchntUuid")]
public string MchUuid { get; set; }
/// <summary>
/// 商户所属分支机构代码
/// </summary>
public string SubInst { get; set; }
/// <summary>
/// 退货外部订单号
/// </summary>
public string RefundExtOrderId { get; set; }
/// <summary>
/// 商品交易单号
/// </summary>
public string GoodsTradeNo { get; set; }
/// <summary>
/// 外部订单号
/// </summary>
public string ExtOrderId { get; set; }
/// <summary>
/// 担保交易状态
/// </summary>
public string SecureStatus { get; set; }
/// <summary>
/// 担保完成金额
/// </summary>
public string CompleteAmount { get; set; }
public string Raw { get; set; }
}
}
@@ -0,0 +1,99 @@
using System;
using PaySharp.Core;
using PaySharp.Core.Request;
namespace PaySharp.Netpay.Response
{
public class QueryResponse : BaseResponse
{
/// <summary>
/// 机构商户号
/// </summary>
[ReName(Name = "instMid")]
public string InstMchId { get; set; }
/// <summary>
/// 检索参考号,用在银联体系交易中
/// </summary>
public string RefId { get; set; }
/// <summary>
/// 买家编号
/// </summary>
public string BuyerId { get; set; }
/// <summary>
/// 子买家编号
/// </summary>
public string SubBuyerId { get; set; }
/// <summary>
/// 买家用户名
/// </summary>
public string BuyerUsername { get; set; }
/// <summary>
/// 买家付款的金额
/// </summary>
public int BuyerPayAmount { get; set; }
/// <summary>
/// 银行卡号
/// </summary>
public string BankCardNo { get; set; }
/// <summary>
/// 银行信息
/// </summary>
public string BankInfo { get; set; }
/// <summary>
/// 支付渠道列表
/// </summary>
public string BillFunds { get; set; }
/// <summary>
/// 支付渠道描述
/// </summary>
public string BillFundsDesc { get; set; }
/// <summary>
/// 优惠金额
/// </summary>
public int CouponAmount { get; set; }
/// <summary>
/// 发票金额
/// </summary>
public int InvoiceAmount { get; set; }
/// <summary>
/// 实收金额
/// </summary>
public int ReceiptAmount { get; set; }
/// <summary>
/// 退款金额
/// </summary>
public int RefundAmount { get; set; }
/// <summary>
/// 支付时间
/// </summary>
public DateTime PayTime { get; set; }
/// <summary>
/// 结算日期
/// </summary>
public DateTime SettleDate { get; set; }
/// <summary>
/// 微信活动编号
/// </summary>
public string ActivityIds { get; set; }
internal override void Execute<TModel, TResponse>(Merchant merchant, Request<TModel, TResponse> request)
{
}
}
}
@@ -0,0 +1,31 @@
using PaySharp.Core.Request;
namespace PaySharp.Netpay.Response
{
public class UnifiedPayResponse : BaseResponse
{
/// <summary>
/// JSAPI支付用的请求报文,带有签名信息
/// </summary>
public string JsPayRequest { get; set; }
/// <summary>
/// APP支付用的请求报文,带有签名信息
/// </summary>
public string AppPayRequest { get; set; }
/// <summary>
/// 支付ID,用于APP支付和公众号支付
/// </summary>
public string PrepayId { get; set; }
/// <summary>
/// 支付二维码,内容为URL,由终端转换成二维码显示
/// </summary>
public string QrCode { get; set; }
internal override void Execute<TModel, TResponse>(Merchant merchant, Request<TModel, TResponse> request)
{
}
}
}
@@ -0,0 +1,46 @@
#if NETCOREAPP3_1
using System;
using Microsoft.Extensions.Configuration;
using PaySharp.Netpay;
using PaySharp.Core;
namespace Microsoft.Extensions.DependencyInjection
{
public static class ServiceCollectionExtensions
{
public static IGateways UseNetpay(this IGateways gateways, Action<Merchant> action)
{
if (action != null)
{
var merchant = new Merchant();
action(merchant);
gateways.Add(new NetpayGateway(merchant));
}
return gateways;
}
public static IGateways UseNetpay(this IGateways gateways, IConfiguration configuration)
{
var merchants = configuration.GetSection("PaySharp:Netpays").Get<Merchant[]>();
if (merchants != null)
{
for (var i = 0; i < merchants.Length; i++)
{
var netpayGateway = new NetpayGateway(merchants[i]);
var gatewayUrl = configuration.GetSection($"PaySharp:Netpays:{i}:GatewayUrl").Value;
if (!string.IsNullOrEmpty(gatewayUrl))
{
netpayGateway.GatewayUrl = gatewayUrl;
}
gateways.Add(netpayGateway);
}
}
return gateways;
}
}
}
#endif
+75
View File
@@ -0,0 +1,75 @@
using PaySharp.Core;
using PaySharp.Core.Exceptions;
using PaySharp.Core.Request;
using PaySharp.Core.Response;
using PaySharp.Core.Utils;
using PaySharp.Netpay.Request;
using PaySharp.Netpay.Response;
namespace PaySharp.Netpay
{
internal static class SubmitProcess
{
private static string _gatewayUrl;
internal static TResponse Execute<TModel, TResponse>(Merchant merchant, Request<TModel, TResponse> request, string gatewayUrl = null) where TResponse : IResponse
{
AddMerchant(merchant, request, gatewayUrl);
var sign = BuildSign(request.GatewayData, merchant.Key);
request.GatewayData.Add("sign", sign);
var result = HttpUtil.Post(request.RequestUrl, request.GatewayData.ToJson());
var gatewayData = new GatewayData();
gatewayData.FromJson(result);
var baseResponse = (BaseResponse)(object)gatewayData.ToObject<TResponse>(StringCase.Camel);
baseResponse.Raw = result;
baseResponse.GatewayData = gatewayData;
if (baseResponse.ErrCode == "SUCCESS")
{
sign = gatewayData.GetStringValue("sign");
if (!string.IsNullOrEmpty(sign) && !CheckSign(gatewayData, merchant.Key, sign))
{
throw new GatewayException("签名验证失败");
}
baseResponse.Execute(merchant, request);
}
return (TResponse)(object)baseResponse;
}
private static void AddMerchant<TModel, TResponse>(Merchant merchant, Request<TModel, TResponse> request, string gatewayUrl) where TResponse : IResponse
{
if (!string.IsNullOrEmpty(gatewayUrl))
{
_gatewayUrl = gatewayUrl;
}
if (!request.RequestUrl.StartsWith("http"))
{
request.RequestUrl = _gatewayUrl + request.RequestUrl;
}
request.GatewayData.Add(merchant, StringCase.Camel);
((BaseRequest<TModel, TResponse>)request).Execute(merchant);
}
internal static string BuildSign(GatewayData gatewayData, string key)
{
gatewayData.Remove("sign");
var data = gatewayData.ToUrl(false) + key;
var sign = EncryptUtil.MD5(data);
return sign;
}
internal static bool CheckSign(GatewayData gatewayData, string key, string sign)
{
return BuildSign(gatewayData, key) == sign;
}
}
}
@@ -0,0 +1,69 @@
using System;
using PaySharp.Netpay;
using PaySharp.Netpay.Domain;
using PaySharp.Netpay.Request;
using Xunit;
using Xunit.Abstractions;
namespace PaySharp.UnitTest
{
public class NetpayGatewayTest
{
private readonly ITestOutputHelper _output;
private readonly NetpayGateway _netpayGateway;
private readonly Merchant _merchant;
private string OutTradeNo => DateTime.Now.ToString("3194yyyyMMddhhmmss");
public NetpayGatewayTest(ITestOutputHelper output)
{
_output = output;
_merchant = new Merchant
{
AppId = "00000001",
MchId = "898310148160568",
Key = "fcAmtnx7MwismjWNhNKdHC44mNXtnEQeJkRrhKJwyrW2ysRR",
Source = "WWW.TEST.COM",
NotifyUrl = "http://localhost:61337/Notify"
};
_netpayGateway = new NetpayGateway(_merchant)
{
GatewayUrl = "https://qr-test2.chinaums.com"
};
}
[Fact]
public void TestUnifiedPay()
{
var request = new UnifiedPayRequest();
request.AddGatewayData(new UnifiedPayModel
{
MsgType = "trade.precreate",
Body = "银联商务统一支付测试",
TotalAmount = 1,
OriginalAmount = 1,
OutTradeNo = OutTradeNo
});
var response = _netpayGateway.Execute(request);
_output.WriteLine(response.QrCode);
Assert.NotNull(response.QrCode);
}
[Fact]
public void TestQuery()
{
var request = new QueryRequest();
request.AddGatewayData(new QueryModel()
{
MsgType = "query",
OutTradeNo = "319420200104045313"
});
var response = _netpayGateway.Execute(request);
Assert.Equal(1, response.TotalAmount);
}
}
}
@@ -26,6 +26,7 @@
<ItemGroup>
<ProjectReference Include="..\..\src\PaySharp.Alipay\PaySharp.Alipay.csproj" />
<ProjectReference Include="..\..\src\PaySharp.Allinpay\PaySharp.Allinpay.csproj" />
<ProjectReference Include="..\..\src\PaySharp.Netpay\PaySharp.Netpay.csproj" />
<ProjectReference Include="..\..\src\PaySharp.Unionpay\PaySharp.Unionpay.csproj" />
<ProjectReference Include="..\..\src\PaySharp.Wechatpay\PaySharp.Wechatpay.csproj" />
</ItemGroup>