2025-03-28 09:49:56 +08:00

373 lines
18 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
}
}