using System; using System.Collections.Generic; using System.Text; using System.Web; using System.IO; using System.Threading.Tasks; using Newtonsoft.Json; using Alipay.EasySDK.Kernel.Util; using Tea; namespace Alipay.EasySDK.Kernel { /// /// Tea DSL编排所需实现的原子方法 /// public class Client { /// /// 构造成本较高的一些参数缓存在上下文中 /// private readonly Context context; /// /// 注入的可选额外文本参数集合 /// private readonly Dictionary optionalTextParams = new Dictionary(); /// /// 注入的可选业务参数集合 /// private readonly Dictionary optionalBizParams = new Dictionary(); /// /// 构造函数 /// /// 上下文对象 public Client(Context context) { this.context = context; } /// /// 注入额外文本参数 /// /// 参数名称 /// 参数的值 /// 本客户端本身,便于链路调用 public Client InjectTextParam(String key, String value) { optionalTextParams.Add(key, value); return this; } /// /// 注入额外业务参数 /// /// 参数名称 /// 参数的值 /// 本客户端本身,便于链式调用 public Client InjectBizParam(String key, Object value) { optionalBizParams.Add(key, value); return this; } /// /// 获取Config中的配置项 /// /// 配置项的名称 /// 配置项的值 public string GetConfig(string key) { return context.GetConfig(key); } /// /// 是否是证书模式 /// /// true:是;false:不是 public bool IsCertMode() { return context.CertEnvironment != null; } /// /// 获取时间戳,格式yyyy-MM-dd HH:mm:ss /// /// 当前时间戳 public string GetTimestamp() { return DateTime.UtcNow.AddHours(8).ToString("yyyy-MM-dd HH:mm:ss"); } /// /// 计算签名,注意要去除key或value为null的键值对 /// /// 系统参数集合 /// 业务参数集合 /// 其他额外文本参数集合 /// 私钥 /// 签名值的Base64串 public string Sign(Dictionary systemParams, Dictionary bizParams, Dictionary textParams, string privateKey) { IDictionary sortedMap = GetSortedMap(systemParams, bizParams, textParams); StringBuilder content = new StringBuilder(); foreach (var pair in sortedMap) { if (!string.IsNullOrEmpty(pair.Key) && !string.IsNullOrEmpty(pair.Value)) { content.Append(pair.Key).Append("=").Append(pair.Value).Append("&"); } } if (content.Length > 0) { //去除尾巴上的& content.Remove(content.Length - 1, 1); } return Signer.Sign(content.ToString(), privateKey); } private IDictionary GetSortedMap(Dictionary systemParams, Dictionary bizParams, Dictionary textParams) { AddOtherParams(textParams, bizParams); IDictionary sortedMap = new SortedDictionary(systemParams, StringComparer.Ordinal); if (bizParams != null && bizParams.Count != 0) { sortedMap.Add(AlipayConstants.BIZ_CONTENT_FIELD, JsonUtil.ToJsonString(bizParams)); } if (textParams != null) { foreach (var pair in textParams) { sortedMap.Add(pair.Key, pair.Value); } } SetNotifyUrl(sortedMap); return sortedMap; } private void SetNotifyUrl(IDictionary paramters) { if (GetConfig(AlipayConstants.NOTIFY_URL_CONFIG_KEY) != null && !paramters.ContainsKey(AlipayConstants.NOTIFY_URL_FIELD)) { paramters.Add(AlipayConstants.NOTIFY_URL_FIELD, GetConfig(AlipayConstants.NOTIFY_URL_CONFIG_KEY)); } } /// /// 获取商户应用公钥证书序列号,从证书模式运行时环境对象中直接读取 /// /// 商户应用公钥证书序列号 public string GetMerchantCertSN() { if (context.CertEnvironment == null) { return null; } return context.CertEnvironment.MerchantCertSN; } /// /// 获取支付宝根证书序列号,从证书模式运行时环境对象中直接读取 /// /// 支付宝根证书序列号 public string GetAlipayRootCertSN() { if (context.CertEnvironment == null) { return null; } return context.CertEnvironment.RootCertSN; } /// /// 将业务参数和其他额外文本参数按www-form-urlencoded格式转换成HTTP Body中的字节数组,注意要做URL Encode /// /// 业务参数 /// HTTP Body中的字节数组 public byte[] ToUrlEncodedRequestBody(Dictionary bizParams) { IDictionary sortedMap = GetSortedMap(new Dictionary(), bizParams, null); return AlipayConstants.DEFAULT_CHARSET.GetBytes(BuildQueryString(sortedMap)); } private string BuildQueryString(IDictionary sortedMap) { StringBuilder content = new StringBuilder(); int index = 0; foreach (var pair in sortedMap) { if (!string.IsNullOrEmpty(pair.Key) && !string.IsNullOrEmpty(pair.Value)) { content.Append(index == 0 ? "" : "&") .Append(pair.Key) .Append("=") .Append(HttpUtility.UrlEncode(pair.Value, AlipayConstants.DEFAULT_CHARSET)); index++; } } return content.ToString(); } /// /// 生成随机分界符,用于multipart格式的HTTP请求Body的多个字段间的分隔 /// /// 随机分界符 public string GetRandomBoundary() { return DateTime.Now.Ticks.ToString("X"); } /// /// 字符串拼接 /// /// 字符串a /// 字符串b /// 字符串a和b拼接后的字符串 public string ConcatStr(string a, string b) { return a + b; } /// /// 将其他额外文本参数和文件参数按multipart/form-data格式转换成HTTP Body中的字节数组流 /// /// 其他额外文本参数 /// 业务文件参数 /// HTTP Body中multipart格式的分隔符 /// Multipart格式的字节流 public Stream ToMultipartRequestBody(Dictionary textParams, Dictionary fileParams, string boundary) { MemoryStream stream = new MemoryStream(); //补充其他额外参数 AddOtherParams(textParams, null); foreach (var pair in textParams) { if (!string.IsNullOrEmpty(pair.Key) && !string.IsNullOrEmpty(pair.Value)) { MultipartUtil.WriteToStream(stream, MultipartUtil.GetEntryBoundary(boundary)); MultipartUtil.WriteToStream(stream, MultipartUtil.GetTextEntry(pair.Key, pair.Value)); } } //组装文件参数 foreach (var pair in fileParams) { if (!string.IsNullOrEmpty(pair.Key) && pair.Value != null) { MultipartUtil.WriteToStream(stream, MultipartUtil.GetEntryBoundary(boundary)); MultipartUtil.WriteToStream(stream, MultipartUtil.GetFileEntry(pair.Key, pair.Value)); MultipartUtil.WriteToStream(stream, File.ReadAllBytes(pair.Value)); } } //添加结束标记 MultipartUtil.WriteToStream(stream, MultipartUtil.GetEndBoundary(boundary)); stream.Seek(0, SeekOrigin.Begin); return stream; } /// /// 将网关响应发序列化成Map,同时将API的接口名称和响应原文插入到响应Map的method和body字段中 /// /// HTTP响应 /// 调用的OpenAPI的接口名称 /// 响应反序列化的Map public Dictionary ReadAsJson(TeaResponse response, string method) { string responseBody = TeaCore.GetResponseBody(response); Dictionary dictionary = JsonConvert.DeserializeObject>(responseBody); dictionary.Add(AlipayConstants.BODY_FIELD, responseBody); dictionary.Add(AlipayConstants.METHOD_FIELD, method); return DictionaryUtil.ObjToDictionary(dictionary); } /// /// 适配Tea DSL自动生成的代码 /// public async Task> ReadAsJsonAsync(TeaResponse response, string method) { return ReadAsJson(response, method); } /// /// 从响应Map中提取支付宝公钥证书序列号 /// /// 响应Map /// 支付宝公钥证书序列号 public string GetAlipayCertSN(Dictionary respMap) { return (string)respMap[AlipayConstants.ALIPAY_CERT_SN_FIELD]; } /// /// 获取支付宝公钥,从证书运行时环境对象中直接读取 /// 如果缓存的用户指定的支付宝公钥证书的序列号与网关响应中携带的支付宝公钥证书序列号不一致,需要报错给出提示或自动更新支付宝公钥证书 /// /// 网关响应中携带的支付宝公钥证书序列号 /// 支付宝公钥 public string ExtractAlipayPublicKey(string alipayCertSN) { if (context.CertEnvironment == null) { return null; } return context.CertEnvironment.GetAlipayPublicKey(alipayCertSN); } /// /// 验证签名 /// /// 响应Map,可以从中提取出sign和body /// 支付宝公钥 /// true:验签通过;false:验签不通过 public bool Verify(Dictionary respMap, string alipayPublicKey) { string sign = (string)respMap[AlipayConstants.SIGN_FIELD]; string content = SignContentExtractor.GetSignSourceData((string)respMap[AlipayConstants.BODY_FIELD], (string)respMap[AlipayConstants.METHOD_FIELD]); return Signer.Verify(content, sign, alipayPublicKey); } /// /// 从响应Map中提取返回值对象的Map,并将响应原文插入到body字段中 /// /// 响应Map /// 返回值对象Map public Dictionary ToRespModel(Dictionary respMap) { string methodName = (string)respMap[AlipayConstants.METHOD_FIELD]; string responseNodeName = methodName.Replace('.', '_') + AlipayConstants.RESPONSE_SUFFIX; string errorNodeName = AlipayConstants.ERROR_RESPONSE; //先找正常响应节点 foreach (var pair in respMap) { if (responseNodeName.Equals(pair.Key)) { Dictionary model = (Dictionary)pair.Value; model.Add(AlipayConstants.BODY_FIELD, respMap[AlipayConstants.BODY_FIELD]); return model; } } //再找异常响应节点 foreach (var pair in respMap) { if (errorNodeName.Equals(pair.Key)) { Dictionary model = (Dictionary)pair.Value; model.Add(AlipayConstants.BODY_FIELD, respMap[AlipayConstants.BODY_FIELD]); return model; } } throw new Exception("响应格式不符合预期,找不到" + responseNodeName + "节点"); } /// /// 生成页面类请求所需URL或Form表单 /// /// GET或POST,决定是生成URL还是Form表单 /// 系统参数集合 /// 业务参数集合 /// 其他额外文本参数集合 /// 所有参数的签名值 /// 生成的URL字符串或表单 public string GeneratePage(string method, Dictionary systemParams, Dictionary bizParams, Dictionary textParams, string sign) { if (AlipayConstants.GET.Equals(method)) { //采集并排序所有参数 IDictionary sortedMap = GetSortedMap(systemParams, bizParams, textParams); sortedMap.Add(AlipayConstants.SIGN_FIELD, sign); //将所有参数置于URL中 return GetGatewayServerUrl() + "?" + BuildQueryString(sortedMap); } else if (AlipayConstants.POST.Equals(method)) { //将系统参数、额外文本参数排序后置于URL中 IDictionary urlParams = GetSortedMap(systemParams, null, textParams); urlParams.Add(AlipayConstants.SIGN_FIELD, sign); string actionUrl = GetGatewayServerUrl() + "?" + BuildQueryString(urlParams); //将业务参数排序后置于form表单中 AddOtherParams(null, bizParams); IDictionary formParams = new SortedDictionary() { { AlipayConstants.BIZ_CONTENT_FIELD, JsonUtil.ToJsonString(bizParams)} }; return PageUtil.BuildForm(actionUrl, formParams); } else { throw new Exception("_generatePage中method只支持传入GET或POST"); } } /// /// 生成订单串 /// /// 系统参数集合 /// 业务参数集合 /// 其他文本参数集合 /// 所有参数的签名值 /// 订单串 public string GenerateOrderString(Dictionary systemParams, Dictionary bizParams, Dictionary textParams, string sign) { //采集并排序所有参数 IDictionary sortedMap = GetSortedMap(systemParams, bizParams, textParams); sortedMap.Add(AlipayConstants.SIGN_FIELD, sign); //将所有参数置于URL中 return BuildQueryString(sortedMap); } private string GetGatewayServerUrl() { return GetConfig(AlipayConstants.PROTOCOL_CONFIG_KEY) + "://" + GetConfig(AlipayConstants.HOST_CONFIG_KEY) + "/gateway.do"; } /// /// AES加密 /// /// 明文 /// 密钥 /// 密文 public string AesEncrypt(string plainText, string key) { return AES.Encrypt(plainText, key); } /// /// AES解密 /// /// 密文 /// 密钥 /// 明文 public string AesDecrypt(string chiperText, string key) { return AES.Decrypt(chiperText, key); } /// /// 对支付类请求的异步通知的参数集合进行验签 /// /// 参数集合 /// 支付宝公钥 /// true:验证成功;false:验证失败 public bool VerifyParams(Dictionary parameters, string alipayPublicKey) { return Signer.VerifyParams(parameters, alipayPublicKey); } /// /// 获取SDK版本信息 /// /// SDK版本信息 public string GetSdkVersion() { return context.SdkVersion; } /// /// 将随机顺序的Map转换为有序的Map /// /// 随机顺序的Map /// 有序的Map public Dictionary SortMap(Dictionary input) { //GO语言的Map是随机顺序的,每次访问顺序都不同,才需排序 return input; } private void AddOtherParams(Dictionary textParams, Dictionary bizParams) { //为null表示此处不是扩展此类参数的时机 if (textParams != null) { foreach (var pair in optionalTextParams) { if (!textParams.ContainsKey(pair.Key)) { textParams.Add(pair.Key, pair.Value); } } SetNotifyUrl(textParams); } //为null表示此处不是扩展此类参数的时机 if (bizParams != null) { foreach (var pair in optionalBizParams) { if (!bizParams.ContainsKey(pair.Key)) { bizParams.Add(pair.Key, pair.Value); } } } } } }