373 lines
18 KiB
C#
373 lines
18 KiB
C#
using Newtonsoft.Json;
|
||
using Org.BouncyCastle.Crypto;
|
||
using Org.BouncyCastle.Crypto.Parameters;
|
||
using Org.BouncyCastle.Security;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Net;
|
||
using System.Net.Security;
|
||
using System.Security.Cryptography;
|
||
using System.Security.Cryptography.X509Certificates;
|
||
using System.Text;
|
||
using WebService.SDK.PayCommon;
|
||
|
||
namespace WebService.SDK.GZNXPay
|
||
{
|
||
public class GZNXPayAPI
|
||
{
|
||
/// <summary>
|
||
/// 贵州农信被扫交易接口
|
||
/// </summary>
|
||
/// <param name="payConfig">接口配置</param>
|
||
/// <param name="merchantInfo">平台商户信息</param>
|
||
/// <param name="authCode">用户付款授权码</param>
|
||
/// <param name="merchantOrderCode">商户交易订单号</param>
|
||
/// <param name="payAmount">交易金额(元)</param>
|
||
/// <returns></returns>
|
||
public static Model.GZNXPayResultModel ScanPay(GZNXPayConfig payConfig,
|
||
MobilePayConfig.PayMerchant merchantInfo, string authCode,
|
||
string merchantOrderCode, string payAmount)
|
||
{
|
||
if (payConfig == null)
|
||
{
|
||
return default(Model.GZNXPayResultModel);
|
||
}
|
||
if (merchantInfo == null || string.IsNullOrWhiteSpace(merchantInfo.MerchantCode))
|
||
{
|
||
return default(Model.GZNXPayResultModel);
|
||
}
|
||
if (!decimal.TryParse(payAmount, out decimal _PayAmount) || _PayAmount <= 0)
|
||
{
|
||
return default(Model.GZNXPayResultModel);
|
||
}
|
||
string _SeqNo = Guid.NewGuid().ToString("N");
|
||
//生成交易报文头参数
|
||
SortedDictionary<string, object> _Head = GZNXPayHead(payConfig,
|
||
merchantInfo, _SeqNo, "siip_order_create_and_pay");
|
||
//生成交易报文体参数
|
||
SortedDictionary<string, object> _ApiDictionary = new SortedDictionary<string, object>
|
||
{
|
||
{ "ext_order_no", merchantOrderCode },//商户订单号
|
||
{ "termcode", "0" },//终端号;固定:0
|
||
{ "term_ip", "" },//终端ip
|
||
{ "operator", "0" },//操作员;固定:0
|
||
{ "total_amt", (_PayAmount*100).ToString("F0") },//订单金额;单位:分
|
||
{ "no_discount_amt", "0" }, //不打折金额;单位:分
|
||
{ "pay_code_type", "AUTO" },//付款码类型;固定:AUTO
|
||
{ "auth_code", authCode },//用户付款码
|
||
{ "notify_url", "" },//支付成功结果通知地址
|
||
{ "remark", "" },//备注
|
||
{ "is_marketing", "N" },//是否可用营销优惠产品
|
||
{ "goods_sku", "" },//商品SKU编码
|
||
{ "is_credit_card", "Y" },//是否支持信用卡付款
|
||
{ "add_data", "" }//自定义附加参数
|
||
};
|
||
LogHelper.WriteReceiveLog("支付反扫-传第三方参数:【head】" +
|
||
_Head.Select(pair => pair.Key + "=" + pair.Value).
|
||
DefaultIfEmpty("").Aggregate((a, b) => a + "&" + b) + "【body】" +
|
||
_ApiDictionary.Select(pair => pair.Key + "=" + pair.Value).
|
||
DefaultIfEmpty("").Aggregate((a, b) => a + "&" + b));
|
||
//调用银行接口请求交易并返回结果
|
||
return JsonConvert.DeserializeObject<Model.GZNXPayResultModel>(
|
||
HttpUrlPost(ApiParamSign(_Head, _ApiDictionary, payConfig.PlatformKey),
|
||
payConfig.ScanURL, "application/json", payConfig.PlatformTimeout));
|
||
}
|
||
/// <summary>
|
||
/// 贵州农信交易查询接口
|
||
/// </summary>
|
||
/// <param name="payConfig">接口配置</param>
|
||
/// <param name="merchantInfo">平台商户信息</param>
|
||
/// <param name="merchantOrderCode">商户交易订单号</param>
|
||
/// <param name="platformOrderCode">平台交易订单号</param>
|
||
/// <param name="payType">交易金额</param>
|
||
/// <param name="queryCount">累计查询次数</param>
|
||
/// <returns></returns>
|
||
public static Model.GZNXPayResultModel QueryPay(GZNXPayConfig payConfig,
|
||
MobilePayConfig.PayMerchant merchantInfo, string merchantOrderCode,
|
||
string platformOrderCode, string payType, int queryCount)
|
||
{
|
||
if (payConfig == null)
|
||
{
|
||
return default(Model.GZNXPayResultModel);
|
||
}
|
||
if (merchantInfo == null || string.IsNullOrWhiteSpace(merchantInfo.MerchantCode))
|
||
{
|
||
return default(Model.GZNXPayResultModel);
|
||
}
|
||
string _SeqNo = Guid.NewGuid().ToString("N");
|
||
//生成交易查询报文头
|
||
SortedDictionary<string, object> _Head = GZNXPayHead(payConfig,
|
||
merchantInfo, _SeqNo, "siip_order_result_query");
|
||
//生成交易查询报文体
|
||
SortedDictionary<string, object> _ApiDictionary = new SortedDictionary<string, object>
|
||
{
|
||
{ "query_type", "ext_order_no" },//查询类型 main_order_no:平台订单号 ext_order_no:商户订单号
|
||
{ "query_no", merchantOrderCode }//查询号码
|
||
};
|
||
LogHelper.WriteReceiveLog("支付查询-传第三方参数:【head】" +
|
||
_Head.Select(pair => pair.Key + "=" + pair.Value).
|
||
DefaultIfEmpty("").Aggregate((a, b) => a + "&" + b) + "【body】" +
|
||
_ApiDictionary.Select(pair => pair.Key + "=" + pair.Value).
|
||
DefaultIfEmpty("").Aggregate((a, b) => a + "&" + b));
|
||
//请求银行交易查询接口并返回结果
|
||
return JsonConvert.DeserializeObject<Model.GZNXPayResultModel>(
|
||
HttpUrlPost(ApiParamSign(_Head, _ApiDictionary, payConfig.PlatformKey),
|
||
payConfig.ScanURL, "application/json", payConfig.PlatformTimeout));
|
||
}
|
||
|
||
/// <summary>
|
||
/// 贵州农信交易撤销(退款)接口
|
||
/// </summary>
|
||
/// <param name="payConfig">接口配置</param>
|
||
/// <param name="merchantInfo">平台商户信息</param>
|
||
/// <param name="platformOrderCode">平台交易订单号</param>
|
||
/// <param name="merchantOrderCode">商户交易订单号</param>
|
||
/// <param name="refundOrderCode">退款订单号</param>
|
||
/// <param name="payAmount">退款金额</param>
|
||
/// <returns></returns>
|
||
public static Model.GZNXRefundModel PayRefund(GZNXPayConfig payConfig,
|
||
MobilePayConfig.PayMerchant merchantInfo, string platformOrderCode,
|
||
string merchantOrderCode, string refundOrderCode, string payAmount)
|
||
{
|
||
if (payConfig == null)
|
||
{
|
||
return default(Model.GZNXRefundModel);
|
||
}
|
||
if (merchantInfo == null || string.IsNullOrWhiteSpace(merchantInfo.MerchantCode))
|
||
{
|
||
return default(Model.GZNXRefundModel);
|
||
}
|
||
if (!decimal.TryParse(payAmount, out decimal _PayAmount) || _PayAmount <= 0)
|
||
{
|
||
return default(Model.GZNXRefundModel);
|
||
}
|
||
|
||
string _SeqNo = Guid.NewGuid().ToString("N");
|
||
//生成交易退款报文头
|
||
SortedDictionary<string, object> _Head = GZNXPayHead(payConfig,
|
||
merchantInfo, _SeqNo, "siip_order_refund");
|
||
//生成交易退款报文体
|
||
SortedDictionary<string, object> _ApiDictionary = new SortedDictionary<string, object>
|
||
{
|
||
{ "main_order_no", platformOrderCode },//行业应用平台订单号
|
||
{ "ext_refund_no", refundOrderCode },//业务系统退款单号
|
||
{ "term_ip", "" },//终端ip
|
||
{ "term_code", "0" },//终端号
|
||
{ "operator", "0" },//操作员
|
||
{ "refund_amt", (_PayAmount*100).ToString("F0") }, //退款金额
|
||
{ "refund_reason", "" } //退款理由
|
||
};
|
||
|
||
LogHelper.WriteReceiveLog("支付退款-传第三方参数:【head】" +
|
||
_Head.Select(pair => pair.Key + "=" + pair.Value).
|
||
DefaultIfEmpty("").Aggregate((a, b) => a + "&" + b) + "【body】" +
|
||
_ApiDictionary.Select(pair => pair.Key + "=" + pair.Value).
|
||
DefaultIfEmpty("").Aggregate((a, b) => a + "&" + b));
|
||
//请求银行交易退款接口并返回结果
|
||
return JsonConvert.DeserializeObject<Model.GZNXRefundModel>(
|
||
HttpUrlPost(ApiParamSign(_Head, _ApiDictionary, payConfig.PlatformKey),
|
||
payConfig.ScanURL, "application/json", payConfig.PlatformTimeout));
|
||
}
|
||
|
||
/// <summary>
|
||
/// 贵州农信交易撤销查询接口
|
||
/// </summary>
|
||
/// <param name="payConfig">接口配置</param>
|
||
/// <param name="merchantInfo">平台商户信息</param>
|
||
/// <param name="merchantOrderCode">商户交易订单号</param>
|
||
/// <param name="platformOrderCode">平台交易订单号</param>
|
||
/// <param name="payAmount">交易金额</param>
|
||
public static Model.GZNXRefundModel PayQueryRefund(GZNXPayConfig payConfig,
|
||
MobilePayConfig.PayMerchant merchantInfo, string merchantOrderCode,
|
||
string platformOrderCode, string payAmount)
|
||
{
|
||
if (payConfig == null)
|
||
{
|
||
return default(Model.GZNXRefundModel);
|
||
}
|
||
if (merchantInfo == null || string.IsNullOrWhiteSpace(merchantInfo.MerchantCode))
|
||
{
|
||
return default(Model.GZNXRefundModel);
|
||
}
|
||
string _SeqNo = Guid.NewGuid().ToString("N");
|
||
//生成退款查询报文头
|
||
SortedDictionary<string, object> _Head = GZNXPayHead(payConfig,
|
||
merchantInfo, _SeqNo, "siip_order_result_query");
|
||
//生成退款查询报文体
|
||
SortedDictionary<string, object> _ApiDictionary = new SortedDictionary<string, object>
|
||
{
|
||
{ "main_order_no", platformOrderCode },//平台订单号
|
||
{ "query_type", "EXT_REFUND_NO" },//查询类型
|
||
{ "query_no", merchantOrderCode }//查询号码
|
||
};
|
||
|
||
LogHelper.WriteReceiveLog("交易撤销查询-传第三方参数:【head】" +
|
||
_Head.Select(pair => pair.Key + "=" + pair.Value).
|
||
DefaultIfEmpty("").Aggregate((a, b) => a + "&" + b) + "【body】" +
|
||
_ApiDictionary.Select(pair => pair.Key + "=" + pair.Value).
|
||
DefaultIfEmpty("").Aggregate((a, b) => a + "&" + b));
|
||
//调用银行接口查询退款状态并返回结果
|
||
return JsonConvert.DeserializeObject<Model.GZNXRefundModel>(
|
||
HttpUrlPost(ApiParamSign(_Head, _ApiDictionary, payConfig.PlatformKey),
|
||
payConfig.ScanURL, "application/json", payConfig.PlatformTimeout));
|
||
}
|
||
|
||
/// <summary>
|
||
/// 贵州农信平台报文头公用方法
|
||
/// </summary>
|
||
/// <param name="payConfig">接口配置</param>
|
||
/// <param name="merchantInfo">平台商户信息</param>
|
||
/// <param name="merchantSeqNo">交易流水号(唯一值不重复)</param>
|
||
/// <param name="msgCode">接口交易码(贵州农信提供)</param>
|
||
/// <returns></returns>
|
||
public static SortedDictionary<string, object> GZNXPayHead(GZNXPayConfig payConfig,
|
||
MobilePayConfig.PayMerchant merchantInfo, string merchantSeqNo, string msgCode)
|
||
{
|
||
//组装并返回报文头
|
||
return new SortedDictionary<string, object>
|
||
{
|
||
{ "msg_code", msgCode }, //交易码
|
||
{ "seq_no", merchantSeqNo }, //流水号
|
||
{ "req_time", DateTime.Now.ToString("yyyyMMddHHmmss") }, //请求时间
|
||
{ "version", "3.0.1" }, //版本号
|
||
{ "charset", "UTF-8" }, //字符集
|
||
{ "app_id", payConfig.PlatformAPPID },
|
||
{ "mercode", payConfig.Organization },
|
||
{ "sign_type", payConfig.PlatformSignType },
|
||
{ "sub_mercode", merchantInfo.MerchantCode } //子商户号
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// 贵州农信支付平台签名
|
||
/// </summary>
|
||
/// <param name="head">报文头</param>
|
||
/// <param name="body">报文体</param>
|
||
/// <param name="privateKey">私钥</param>
|
||
/// <returns></returns>
|
||
public static string ApiParamSign(SortedDictionary<string, object> head,
|
||
SortedDictionary<string, object> body, string privateKey)
|
||
{
|
||
//生成报文头数据字符串
|
||
string _AgwHead = JsonConvert.SerializeObject(head);
|
||
//生成报文体数据字符串
|
||
string _AgwBody = JsonConvert.SerializeObject(body);
|
||
//生成最终发送的数据内容
|
||
return JsonConvert.SerializeObject(new SortedDictionary<string, SortedDictionary<string, object>>
|
||
{
|
||
{
|
||
"AgwHead",
|
||
new SortedDictionary<string, object>(head)
|
||
{
|
||
{ "signature", Sign($"head={_AgwHead}&body={_AgwBody}", privateKey) }//计算并添加签名信息
|
||
}
|
||
},
|
||
{ "AgwBody", new SortedDictionary<string, object>(body) }
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// RSA签名方法
|
||
/// </summary>
|
||
/// <param name="contentForSign">待签名字符串</param>
|
||
/// <param name="privateKey">RSA私钥</param>
|
||
/// <returns></returns>
|
||
public static string Sign(string contentForSign, string privateKey)
|
||
{
|
||
//转换成适用于.Net的秘钥
|
||
var netKey = RSAPrivateKeyJava2DotNet(privateKey);
|
||
var rsa = new RSACryptoServiceProvider();
|
||
rsa.FromXmlString(netKey);
|
||
//创建一个空对象
|
||
var rsaClear = new RSACryptoServiceProvider();
|
||
var paras = rsa.ExportParameters(true);
|
||
rsaClear.ImportParameters(paras);
|
||
//签名返回
|
||
using (var sha256 = new SHA256CryptoServiceProvider())
|
||
{
|
||
var signData = rsa.SignData(Encoding.UTF8.GetBytes(contentForSign), sha256);
|
||
return Convert.ToBase64String(signData);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 转换Java私钥为.NET私钥
|
||
/// </summary>
|
||
/// <param name="privateKey">私钥字符串</param>
|
||
/// <returns></returns>
|
||
public static string RSAPrivateKeyJava2DotNet(string privateKey)
|
||
{
|
||
RsaPrivateCrtKeyParameters privateKeyParam = (RsaPrivateCrtKeyParameters)PrivateKeyFactory.CreateKey(Convert.FromBase64String(privateKey));
|
||
//返回.NET格式RSA私钥串
|
||
return $"<RSAKeyValue><Modulus>{Convert.ToBase64String(privateKeyParam.Modulus.ToByteArrayUnsigned())}</Modulus>" +
|
||
$"<Exponent>{Convert.ToBase64String(privateKeyParam.PublicExponent.ToByteArrayUnsigned())}</Exponent>" +
|
||
$"<P>{Convert.ToBase64String(privateKeyParam.P.ToByteArrayUnsigned())}</P>" +
|
||
$"<Q>{Convert.ToBase64String(privateKeyParam.Q.ToByteArrayUnsigned())}</Q>" +
|
||
$"<DP>{Convert.ToBase64String(privateKeyParam.DP.ToByteArrayUnsigned())}</DP>" +
|
||
$"<DQ>{Convert.ToBase64String(privateKeyParam.DQ.ToByteArrayUnsigned())}</DQ>" +
|
||
$"<InverseQ>{Convert.ToBase64String(privateKeyParam.QInv.ToByteArrayUnsigned())}</InverseQ>" +
|
||
$"<D>{Convert.ToBase64String(privateKeyParam.Exponent.ToByteArrayUnsigned())}</D></RSAKeyValue>";
|
||
}
|
||
|
||
#region 方法 -> HttpUrlPost
|
||
/// <summary>
|
||
/// HttpUrlPost方法(支持HTTPS)
|
||
/// </summary>
|
||
/// <param name="postData"></param>
|
||
/// <param name="requestUrl"></param>
|
||
/// <param name="contentType"></param>
|
||
/// <param name="timeout"></param>
|
||
/// <returns></returns>
|
||
public static string HttpUrlPost(string postData, string requestUrl, string contentType = "application/json", int timeout = 0)
|
||
{
|
||
try
|
||
{
|
||
//SSL证书验证设置
|
||
ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(CheckValidationResult);
|
||
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3
|
||
| SecurityProtocolType.Tls
|
||
| (SecurityProtocolType)0x300 //Tls11
|
||
| (SecurityProtocolType)0xC00; //Tls12
|
||
//创建HTTP请求实例
|
||
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(requestUrl);
|
||
if (timeout > 0)
|
||
{
|
||
request.Timeout = timeout * 1000;//设置连接超时等待时间
|
||
}
|
||
request.Method = "POST";//使用POST模式进行请求
|
||
request.KeepAlive = false;
|
||
request.ContentType = contentType;//设置HTTP请求标头
|
||
CookieContainer cookieContainer = new CookieContainer();
|
||
request.CookieContainer = cookieContainer;
|
||
byte[] postBytes = Encoding.UTF8.GetBytes(postData);
|
||
request.ContentLength = postBytes.Length;
|
||
//发送请求数据至指定服务
|
||
using (System.IO.Stream reqStream = request.GetRequestStream())
|
||
{
|
||
reqStream.Write(postBytes, 0, postBytes.Length);
|
||
}
|
||
|
||
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
|
||
{
|
||
//在这里对接收到的页面内容进行处理
|
||
//直到request.GetResponse()程序才开始向目标网页发送post请求
|
||
System.IO.Stream responseStream = response.GetResponseStream();
|
||
System.IO.StreamReader reader = new System.IO.StreamReader(responseStream, Encoding.GetEncoding("utf-8"));
|
||
string val = reader.ReadToEnd();
|
||
return val;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
return ex.Message;
|
||
}
|
||
}
|
||
private static bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
|
||
{
|
||
return true;
|
||
}
|
||
#endregion
|
||
}
|
||
}
|