338 lines
16 KiB
C#
338 lines
16 KiB
C#
using Newtonsoft.Json;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.IO;
|
||
using System.Linq;
|
||
using System.Security.Cryptography;
|
||
using System.Text;
|
||
using WebService.SDK.PayCommon;
|
||
|
||
namespace WebService.SDK.CCBPay
|
||
{
|
||
public class CCBPayAPI
|
||
{
|
||
/// <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.CCBPayResultModel ScanPay(CCBPayConfig payConfig,
|
||
MobilePayConfig.PayMerchant merchantInfo, string authCode,
|
||
string merchantOrderCode, string payAmount)
|
||
{
|
||
return ScanPay(payConfig, merchantInfo, authCode, merchantOrderCode, payAmount, null);
|
||
}
|
||
/// <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.CCBPayResultModel ScanPay(CCBPayConfig payConfig,
|
||
MobilePayConfig.PayMerchant merchantInfo, string authCode,
|
||
string merchantOrderCode, string payAmount, List<Model.GoodsDetailAlipayModel> goodsDetails)
|
||
{
|
||
if (payConfig == null)
|
||
{
|
||
return default(Model.CCBPayResultModel);
|
||
}
|
||
if (merchantInfo == null || string.IsNullOrWhiteSpace(merchantInfo.MerchantCode) ||
|
||
string.IsNullOrWhiteSpace(merchantInfo.MerchantKey) ||
|
||
string.IsNullOrWhiteSpace(merchantInfo.MerchantPosCode))
|
||
{
|
||
return default(Model.CCBPayResultModel);
|
||
}
|
||
if (!decimal.TryParse(payAmount, out decimal _PayAmount) || _PayAmount <= 0)
|
||
{
|
||
return default(Model.CCBPayResultModel);
|
||
}
|
||
Dictionary<string, string> _ApiDictionary = new Dictionary<string, string>
|
||
{
|
||
//{ "MERCHANTID", merchantInfo.MerchantCode },//平台商户号
|
||
//{ "POSID", merchantInfo.MerchantPosCode },//柜台号
|
||
//{ "BRANCHID", payConfig.Organization },//分行号
|
||
//{ "TERMNO1", "" },//终端编号1
|
||
//{ "TERMNO2", "" }, //终端编号2
|
||
{ "MERFLAG", "1" },//商户类型
|
||
{ "ORDERID", merchantOrderCode },//订单号
|
||
{ "QRCODE", authCode},//用户付款码
|
||
{ "AMOUNT", payAmount },//交易金额
|
||
{ "TXCODE", "PAY100" }//接口名称,固定:PAY100
|
||
};
|
||
if (goodsDetails != null)
|
||
{
|
||
for (int i = 0; i < goodsDetails.Count; i++)
|
||
{
|
||
goodsDetails[i].quantity = (int)goodsDetails[i].quantity;
|
||
goodsDetails[i].price = decimal.Parse(goodsDetails[i].price.ToString("F2"));
|
||
if (string.IsNullOrWhiteSpace(goodsDetails[i].goods_name))
|
||
{
|
||
goodsDetails[i].goods_name = goodsDetails[i].goods_id;
|
||
}
|
||
}
|
||
_ApiDictionary.Add("goods_detail_ali", JsonConvert.SerializeObject(goodsDetails));//支付宝优惠券商品参数
|
||
}
|
||
LogHelper.WriteReceiveLog("支付反扫-传第三方参数:" +
|
||
_ApiDictionary.Select(pair => pair.Key + "=" + pair.Value).
|
||
DefaultIfEmpty("").Aggregate((a, b) => a + "&" + b));
|
||
|
||
//测试用日志记录
|
||
LogHelper.WriteSendLog("支付调用的服务地址" + payConfig.ScanURL);
|
||
|
||
return JsonConvert.DeserializeObject<Model.CCBPayResultModel>(
|
||
CommonHelper.HttpUrlPost($"MERCHANTID={merchantInfo.MerchantCode}" +
|
||
$"&POSID={merchantInfo.MerchantPosCode}&BRANCHID={payConfig.Organization}" +
|
||
$"&ccbParam={ApiParamSign(_ApiDictionary, merchantInfo.MerchantKey)}",
|
||
payConfig.ScanURL, "application/x-www-form-urlencoded", 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.CCBPayResultModel QueryPay(CCBPayConfig payConfig,
|
||
MobilePayConfig.PayMerchant merchantInfo, string merchantOrderCode,
|
||
string platformOrderCode, string payType, int queryCount)
|
||
{
|
||
if (payConfig == null)
|
||
{
|
||
return default(Model.CCBPayResultModel);
|
||
}
|
||
if (merchantInfo == null || string.IsNullOrWhiteSpace(merchantInfo.MerchantCode) ||
|
||
string.IsNullOrWhiteSpace(merchantInfo.MerchantKey) ||
|
||
string.IsNullOrWhiteSpace(merchantInfo.MerchantPosCode))
|
||
{
|
||
return default(Model.CCBPayResultModel);
|
||
}
|
||
int QRCodeType = 2;
|
||
switch (payType.ToUpper())
|
||
{
|
||
case "WECHAT":
|
||
QRCodeType = 2;
|
||
break;
|
||
case "ALIPAY":
|
||
QRCodeType = 3;
|
||
break;
|
||
case "UNIONPAY":
|
||
QRCodeType = 4;
|
||
break;
|
||
}
|
||
Dictionary<string, string> _ApiDictionary = new Dictionary<string, string>
|
||
{
|
||
//{ "MERCHANTID", merchantInfo.MerchantCode },//平台商户号
|
||
//{ "POSID", merchantInfo.MerchantPosCode },//柜台号
|
||
//{ "BRANCHID", payConfig.Organization },//分行号
|
||
//{ "TERMNO1", "" },//终端编号1
|
||
//{ "TERMNO2", "" }, //终端编号2
|
||
{ "MERFLAG", "1" },//商户类型
|
||
{ "ORDERID", merchantOrderCode },//订单号
|
||
{ "QRYTIME", queryCount.ToString() },//查询次数
|
||
{ "QRCODETYPE", QRCodeType.ToString() },//二维码类型
|
||
{ "TXCODE", "PAY101" }
|
||
};
|
||
LogHelper.WriteReceiveLog("支付查询-传第三方参数:" +
|
||
_ApiDictionary.Select(pair => pair.Key + "=" + pair.Value).
|
||
DefaultIfEmpty("").Aggregate((a, b) => a + "&" + b));
|
||
return JsonConvert.DeserializeObject<Model.CCBPayResultModel>(
|
||
CommonHelper.HttpUrlPost($"MERCHANTID={merchantInfo.MerchantCode}" +
|
||
$"&POSID={merchantInfo.MerchantPosCode}&BRANCHID={payConfig.Organization}" +
|
||
$"&ccbParam={ApiParamSign(_ApiDictionary, merchantInfo.MerchantKey)}",
|
||
payConfig.ScanURL, "application/x-www-form-urlencoded", 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.CCBRefundModel RefundPay(CCBPayConfig payConfig,
|
||
MobilePayConfig.PayMerchant merchantInfo, string platformOrderCode,
|
||
string merchantOrderCode, string refundOrderCode, string payAmount)
|
||
{
|
||
if (payConfig == null)
|
||
{
|
||
return default(Model.CCBRefundModel);
|
||
}
|
||
if (merchantInfo == null || string.IsNullOrWhiteSpace(merchantInfo.MerchantCode))
|
||
{
|
||
return default(Model.CCBRefundModel);
|
||
}
|
||
if (!decimal.TryParse(payAmount, out decimal _PayAmount) || _PayAmount <= 0)
|
||
{
|
||
return default(Model.CCBRefundModel);
|
||
}
|
||
Dictionary<string, string> _ApiDictionary = new Dictionary<string, string>
|
||
{
|
||
{ "CUST_ID", merchantInfo.MerchantCode },//建设银行平台商户号
|
||
{ "ORDER", merchantOrderCode },//原商户订单号
|
||
{ "MONEY", _PayAmount.ToString() } //退款款交易金额
|
||
};
|
||
string str_Refund = $"{merchantInfo.MerchantCode}&{merchantOrderCode}&{_PayAmount.ToString()}";
|
||
_ApiDictionary.Add("fundParams", Encry(str_Refund, "503252CD487D3B0ADA6CD0A42EF28CF8"));
|
||
LogHelper.WriteReceiveLog("支付退款-传第三方参数:" +
|
||
_ApiDictionary.Select(pair => pair.Key + "=" + pair.Value).
|
||
DefaultIfEmpty("").Aggregate((a, b) => a + "&" + b));
|
||
|
||
//测试用日志记录
|
||
LogHelper.WriteSendLog("退款调用的服务地址" + payConfig.PayRefundURL);
|
||
|
||
return JsonConvert.DeserializeObject<Model.CCBRefundModel>(CommonHelper.HttpUrlPost(
|
||
$"fundParams={System.Web.HttpUtility.UrlEncode(Encry(str_Refund, "503252CD487D3B0ADA6CD0A42EF28CF8"))}",
|
||
payConfig.PayRefundURL, "application/x-www-form-urlencoded", payConfig.PlatformTimeout, Encoding.Default, false));
|
||
}
|
||
/// <summary>
|
||
/// 建设银行支付平台参数签名
|
||
/// </summary>
|
||
/// <param name="signParm">待签名参数集</param>
|
||
/// <param name="mchKey">签名密钥</param>
|
||
/// <returns></returns>
|
||
private static string ApiParamSign(Dictionary<string, string> signParm, string mchKey)
|
||
{
|
||
try
|
||
{
|
||
//拼接待签名参数字符串
|
||
string parameter = signParm.Select(pair => $"{pair.Key}={pair.Value}").
|
||
DefaultIfEmpty("").Aggregate((a, b) => $"{a}&{b}");
|
||
return new CCB_B2CPay_Util.CCBPayUtil().makeCCBParam(parameter, mchKey);
|
||
}
|
||
catch
|
||
{
|
||
return "";
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 建设银行参数签名加密
|
||
/// </summary>
|
||
/// <param name="encryptString">待加密字符串</param>
|
||
/// <param name="sKey">密钥</param>
|
||
/// <returns></returns>
|
||
private static string CCBDESEncrypt(string encryptString, string sKey)
|
||
{
|
||
byte[] byKey = null;
|
||
byte[] IV = { 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF };
|
||
try
|
||
{
|
||
byKey = Encoding.UTF8.GetBytes(sKey.Substring(0, 8));
|
||
DESCryptoServiceProvider des = new DESCryptoServiceProvider();
|
||
des.Mode = CipherMode.ECB;
|
||
byte[] inputByteArray = Encoding.GetEncoding("utf-16be").GetBytes(encryptString);
|
||
MemoryStream ms = new MemoryStream();
|
||
// byte[] bytes = new byte[inputByteArray.Length + 2];
|
||
List<byte> bytes = new List<byte>();
|
||
bytes.Add(0xfe);
|
||
bytes.Add(0xff);
|
||
bytes.AddRange(inputByteArray.ToList());
|
||
|
||
CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(byKey, IV), CryptoStreamMode.Write);
|
||
cs.Write(bytes.ToArray(), 0, bytes.Count);
|
||
cs.FlushFinalBlock();
|
||
var resultStr = Convert.ToBase64String(ms.ToArray());
|
||
int count = resultStr.Length / 76;
|
||
count += resultStr.Length % 76 > 0 ? 1 : 0;
|
||
StringBuilder result = new StringBuilder();
|
||
for (int i = 0; i < count; i++)
|
||
{
|
||
if (resultStr.Length > (i + 1) * 76)
|
||
result.Append(resultStr.Substring(i * 76, 76) + Environment.NewLine);//此处不能撤销换行
|
||
else
|
||
result.Append(resultStr.Substring(i * 76, resultStr.Length - i * 76));
|
||
}
|
||
return result.ToString().Replace("+", ",");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
throw ex;
|
||
}
|
||
}
|
||
|
||
/// <summary>AES加密</summary>
|
||
/// <param name="text">明文</param>
|
||
/// <param name="key">密钥,长度为16的字符串</param> s
|
||
/// <returns>密文</returns>
|
||
public static string Encry(string text, string key)
|
||
{
|
||
string iv = key;
|
||
if (key.Length > 16)
|
||
{
|
||
// IV为商户MD5密钥后16位
|
||
iv = key.Substring(key.Length - 16);
|
||
// RES的KEY 为商户MD5密钥的前16位
|
||
key = key.Substring(0, 16);
|
||
}
|
||
return EncodeAES(text, key, iv);
|
||
}
|
||
|
||
/// <summary>AES加密</summary>
|
||
/// <param name="text">明文</param>
|
||
/// <param name="key">密钥,长度为16的字符串</param>
|
||
/// <param name="iv">偏移量,长度为16的字符串</param>
|
||
/// <returns>密文</returns>
|
||
public static string EncodeAES(string text, string key, string iv)
|
||
{
|
||
|
||
RijndaelManaged rijndaelCipher = new RijndaelManaged();
|
||
rijndaelCipher.Mode = CipherMode.CBC;
|
||
rijndaelCipher.Padding = PaddingMode.PKCS7;
|
||
rijndaelCipher.KeySize = 128;
|
||
rijndaelCipher.BlockSize = 128;
|
||
byte[] pwdBytes = System.Text.Encoding.UTF8.GetBytes(key);
|
||
byte[] keyBytes = new byte[16];
|
||
int len = pwdBytes.Length;
|
||
if (len > keyBytes.Length)
|
||
len = keyBytes.Length;
|
||
System.Array.Copy(pwdBytes, keyBytes, len);
|
||
rijndaelCipher.Key = keyBytes;
|
||
rijndaelCipher.IV = Encoding.UTF8.GetBytes(iv);
|
||
ICryptoTransform transform = rijndaelCipher.CreateEncryptor();
|
||
byte[] plainText = Encoding.UTF8.GetBytes(text);
|
||
byte[] cipherBytes = transform.TransformFinalBlock(plainText, 0, plainText.Length);
|
||
return Convert.ToBase64String(cipherBytes);
|
||
}
|
||
|
||
/// <summary>AES解密</summary>
|
||
/// <param name="text">密文</param>
|
||
/// <param name="key">密钥,长度为16的字符串</param>
|
||
/// <param name="iv">偏移量,长度为16的字符串</param>
|
||
/// <returns>明文</returns>
|
||
public static string DecodeAES(string text, string key, string iv)
|
||
{
|
||
RijndaelManaged rijndaelCipher = new RijndaelManaged();
|
||
rijndaelCipher.Mode = CipherMode.CBC;
|
||
rijndaelCipher.Padding = PaddingMode.PKCS7;
|
||
rijndaelCipher.KeySize = 128;
|
||
rijndaelCipher.BlockSize = 128;
|
||
byte[] encryptedData = Convert.FromBase64String(text);
|
||
byte[] pwdBytes = System.Text.Encoding.UTF8.GetBytes(key);
|
||
byte[] keyBytes = new byte[16];
|
||
int len = pwdBytes.Length;
|
||
if (len > keyBytes.Length)
|
||
len = keyBytes.Length;
|
||
System.Array.Copy(pwdBytes, keyBytes, len);
|
||
rijndaelCipher.Key = keyBytes;
|
||
rijndaelCipher.IV = Encoding.UTF8.GetBytes(iv);
|
||
ICryptoTransform transform = rijndaelCipher.CreateDecryptor();
|
||
byte[] plainText = transform.TransformFinalBlock(encryptedData, 0, encryptedData.Length);
|
||
return Encoding.UTF8.GetString(plainText);
|
||
}
|
||
}
|
||
}
|