.net core 支付寶,微信支付 一


源碼: https://github.com/aspros-luo/Qwerty.Payment/tree/develop

 支付寶支付:參考支付寶sdk及文檔,https://docs.open.alipay.com/194

前言:

目前實現支付寶Native支付,手機網站支付,App支付,支付回調,退款申請,退款查詢

Native支付及手機支付是由前端加基礎數據傳入后端,后端加簽拼裝成html以二維碼或form表單呈現

APP支付由后端加簽,返回加簽結果給app,app直接調用sdk完成支付

1:設置支付需要的config信息,考慮到會有不同appId,所以需要設置appId,私鑰和公鑰

public static class AliPayConfig
    {
        public static void Init(string appId, string privateKey, string aliPublicKey, string returnUrl, string notifyUrl)
        {
            AppId = appId;
            PrivateKey = privateKey;
            AliPublicKey = aliPublicKey;
            ReturnUrl = string.IsNullOrWhiteSpace(returnUrl) ? notifyUrl : returnUrl;
            NotifyUrl = notifyUrl;
        }

        public static string AppId { get; private set; }
        //public static string Gateway { get; private set; } = "https://openapi.alipay.com/gateway.do";
        internal static string Gateway { get; private set; } = "https://openapi.alipaydev.com/gateway.do";
        public static string PrivateKey { get; private set; }
        public static string AliPublicKey { get; private set; }
        public static string ReturnUrl { get; private set; }
        public static string NotifyUrl { get; private set; }
    }
View Code

2:通用方法,組裝數據,排序,加簽,驗簽等

2.1:拼裝數據類

主要用於字典排序拼裝

public static string BuildParamStr(Dictionary<string, string> param)
        {
            if (param == null || param.Count == 0)
            {
                return "";
            }
            var ascDic = param.OrderBy(o => o.Key).ToDictionary(o => o.Key, p => p.Value);
            var sb = new StringBuilder();
            foreach (var item in ascDic)
            {
                if (!string.IsNullOrEmpty(item.Value))
                {
                    sb.Append(item.Key).Append("=").Append(item.Value).Append("&");
                }
            }
            return sb.ToString().Substring(0, sb.ToString().Length - 1);
        }
View Code

主要用於掃碼,jsapi手機網站支付拼裝成一個form表單,(支付寶sdk中也有)

public static string BuildHtmlRequest(IDictionary<string, string> sParaTemp, string strMethod, string strButtonValue)
        {
            //待請求參數數組
            var dicPara = sParaTemp;
            var sbHtml = new StringBuilder();
            //sbHtml.Append("<head><meta http-equiv=\"Content-Type\" content=\"text/html\" charset= \"" + charset + "\" /></head>");
            sbHtml.Append("<form id='alipaysubmit' name='alipaysubmit' action='"+AliPayConfig.Gateway+"?charset=utf-8' method='" + strMethod + "'>");
            foreach (var temp in dicPara)
            {
                sbHtml.Append("<input  name='" + temp.Key + "' value='" + temp.Value + "'/>");
            }
            //submit按鈕控件請不要含有name屬性
            sbHtml.Append("<input type='submit' value='" + strButtonValue + "' style='display:none;'></form>");
            // sbHtml.Append("<input type='submit' value='" + strButtonValue + "'></form></div>");
            //表單實現自動提交
            sbHtml.Append("<script>document.forms['alipaysubmit'].submit();</script>");
            return sbHtml.ToString();
        }
View Code

2.2:簽名類,RSA256簽名,支付寶推薦rsa256簽名方式,私鑰是java格式,這里引用一個nuget包,轉換成dotnet格式私鑰

Org.BouncyCastle

/// <summary>
    /// Rsa 工具類
    /// </summary>
    internal static class GenerateRsaAssist
    {
        /// <summary>
        /// 加簽
        /// </summary>
        /// <returns></returns>
        public static string RasSign(string content, string privateKey, SignType signType)
        {
            var singerType = "";
            if (signType == SignType.Rsa2)
            {
                singerType = "SHA256WithRSA";
            }
            if (signType == SignType.Rsa)
            {
                singerType = "SHA1withRSA";
            }
            var signer = SignerUtilities.GetSigner(singerType);
            var privateKeyParam = (RsaPrivateCrtKeyParameters)PrivateKeyFactory.CreateKey(Convert.FromBase64String(privateKey));
            signer.Init(true, privateKeyParam);
            var plainBytes = Encoding.UTF8.GetBytes(content);
            signer.BlockUpdate(plainBytes, 0, plainBytes.Length);
            var signBytes = signer.GenerateSignature();
            return Convert.ToBase64String(signBytes);
        }
        /// <summary>
        /// 驗簽
        /// </summary>
        /// <returns></returns>
        public static bool VerifySign(string content, string publicKey, string signData, SignType signType)
        {
            var singerType = "";
            if (signType == SignType.Rsa2)
            {
                singerType = "SHA256WithRSA";
            }
            if (signType == SignType.Rsa)
            {
                singerType = "SHA1withRSA";
            }
            var signer = SignerUtilities.GetSigner(singerType);
            var publicKeyParam = (RsaKeyParameters)PublicKeyFactory.CreateKey(Convert.FromBase64String(publicKey));
            signer.Init(false, publicKeyParam);
            var signBytes = Convert.FromBase64String(signData);
            var plainBytes = Encoding.UTF8.GetBytes(content);
            signer.BlockUpdate(plainBytes, 0, plainBytes.Length);
            var ret = signer.VerifySignature(signBytes);
            return ret;
        }
    }
View Code

3:基礎類

通用方法基本完成后剩下的只要基礎類來組裝數據了

3.1:添加公共請求參數類

internal class AliPayCommonModel
    {
        public string app_id { get; private set; } = AliPayConfig.AppId;
        public string method { get; private set; }
        public string format { get; private set; } = "JSON";
        public string return_url { get; private set; } = AliPayConfig.ReturnUrl;
        public string charset { get; private set; } = "utf-8";
        public string sign_type { get; private set; } = "RSA2";
        public string timestamp { get; private set; } = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss}";
        public string version { get; private set; } = "1.0";
        public string notify_url { get;private set; }= AliPayConfig.NotifyUrl;
        public string biz_content { get; private set; }
        /// <summary>
        /// 設置支付方式
        /// </summary>
        /// <param name="payMethod"></param>
        internal void SetMethod(string payMethod)
        {
            method = payMethod;
        }
        /// <summary>
        /// 設置支付主題內容
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="pay"></param>
        internal void SetBizContent<T>(T pay)
        {
            var str = pay.GetType().GetProperties().OrderBy(o=>o.Name).Aggregate("", (current, item) => current + $"\"{item.Name}\":\"{item.GetValue(pay)}\",");
            biz_content ="{"+ str.Substring(0,str.Length-1)+"}";
        }

        //public void SetBizContent(AliRefundModel refund)
        //{
        //    var str = refund.GetType().GetProperties().OrderBy(o => o.Name).Aggregate("", (current, item) => current + $"\"{item.Name}\":\"{item.GetValue(refund)}\",");
        //    biz_content = "{" + str.Substring(0, str.Length - 1) + "}";
        //}

        //public void SetBizContent(AliRefundQueryModel refundQuery)
        //{
        //    var str = refundQuery.GetType().GetProperties().OrderBy(o => o.Name).Aggregate("", (current, item) => current + $"\"{item.Name}\":\"{item.GetValue(refundQuery)}\",");
        //    biz_content = "{" + str.Substring(0, str.Length - 1) + "}";
        //}

    }
View Code

公共參數類里包含兩個方法,設置不同支付方式及設置支付主體

3.2:添加支付主體類

    public class AliPayModel
    {
        /// <summary>
        /// 商戶交易訂單號
        /// </summary>
        public string out_trade_no { get; set; }
        /// <summary>
        /// 支付類型
        /// </summary>
        public string product_code { get; private set; } = "FAST_INSTANT_TRADE_PAY";
        /// <summary>
        /// 支付金額
        /// </summary>
        public string total_amount { get; set; }
        /// <summary>
        /// 標題
        /// </summary>
        public string subject { get; set; }
        /// <summary>
        /// 有效時間
        /// </summary>
        public string timeout_express { get; set; } = "30m";
        /// <summary>
        /// 設置支付方式
        /// </summary>
        /// <param name="code"></param>
        internal void SetProductCode(string code)
        {
            product_code = code;
        }
    }
View Code

支付主體里也有一個方法設置銷售產品碼,用於設置商家和支付寶簽約的產品碼

4:添加網絡請求類,沒什么特別的,將鍵值數據傳入支付寶請求網關,主要用於退款申請,及退款查詢,native支付及手機網站支付都只是在后端加簽sign后拼裝成html輸出的

    internal class HttpUtil
    {
        //private static readonly string _defaultUserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)";

        //private static bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
        //{
        //    return true; //總是接受     
        //}

        internal static async Task<HttpResponseMessage> CreatePostHttpResponse(string url, IDictionary<string, string> parameters)
        {
            var listKeyValues = parameters.Keys.Select(key => new KeyValuePair<string, string>(key, parameters[key])).ToList();
            using (var client = new HttpClient())
            {
                var httpContent = new FormUrlEncodedContent(listKeyValues);
                var response = await client.PostAsync(url, httpContent);
                return response;
            }
        }
    }
View Code

5:接口,及實現類

定義支付方式接口,目前涵蓋native,手機網站,app支付,退款申請,退款查詢,當面付功能暫未實現

public interface IAliPayService
    {
        /// <summary>
        /// page支付,支付信息組成頁面表單數據,用於pc支付
        /// </summary>
        /// <returns></returns>
        AliPayRequest NativePay(AliPayModel payModel);
        /// <summary>
        /// app支付,app端發送加簽字段,返回簽名數據
        /// </summary>
        /// <returns></returns>
        AliPayRequest AppPay(string preSign);
        /// <summary>
        /// jsapi支付,用於網頁支付
        /// </summary>
        /// <returns></returns>
        AliPayRequest JsApiPay(AliPayModel payModel);
        /// <summary>
        /// 退款申請接口,用戶發起退款申請
        /// </summary>
        /// <returns></returns>
        Task<AliRefundResponse> AliRefund(AliRefundModel refundModel);
        /// <summary>
        /// 退款查詢接口,用於確認退款是否成功
        /// </summary>
        /// <returns></returns>
        Task<AliRefundQueryResponse> AliRefundQuery(AliRefundQueryModel refundQueryModel);
        /// <summary>
        /// 支付回掉接口
        /// </summary>
        /// <returns></returns>
        AliNotifyRequest AliNotify(Stream aliReturnData);
    }
View Code

實現接口類(敲黑板,畫重點)

   public class AliPayService : IAliPayService
    {
        public AliPayRequest NativePay(AliPayModel payModel)
        {
            payModel.SetProductCode("FAST_INSTANT_TRADE_PAY");

            var common = new AliPayCommonModel();
            common.SetMethod("alipay.trade.page.pay");
            common.SetBizContent(payModel);

            var parameters = common.GetType().GetProperties().OrderBy(o => o.Name).ToDictionary(item => item.Name, item => item.GetValue(common).ToString());
            var str = BuildData.BuildParamStr(parameters);

            var sign = GenerateRsaAssist.RasSign(str, AliPayConfig.PrivateKey, SignType.Rsa2);
            parameters.Add("sign", sign);

            try
            {
                var from = BuildData.BuildHtmlRequest(parameters, "post", "post");
                return new AliPayRequest { IsSuccess = true, PreSign = str, Sign = sign, Result = from };
            }
            catch (Exception e)
            {
                return new AliPayRequest { IsSuccess = false, PreSign = str, Sign = sign, Result = e.Message };
            }
        }

        public AliPayRequest AppPay(string preSign)
        {
            try
            {
                //payModel.SetProductCode("QUICK_MSECURITY_PAY");
                //var common = new AliPayCommonModel();
                //common.SetMethod("alipay.trade.app.pay");
                //common.SetBizContent(payModel);
                //var parameters = common.GetType().GetProperties().OrderBy(o => o.Name).ToDictionary(item => item.Name, item => item.GetValue(common).ToString());
                //var str = BuildData.BuildParamStr(parameters);
                var sign = GenerateRsaAssist.RasSign(preSign, AliPayConfig.PrivateKey, SignType.Rsa2);
                //return UrlEncoder.Default.Encode(str)+$"&sign={sign}";
                sign = UrlEncoder.Default.Encode(sign);
                return new AliPayRequest { IsSuccess = true, PreSign = preSign, Sign = sign, Result = sign };
            }
            catch (Exception e)
            {
                return new AliPayRequest { IsSuccess = false, PreSign = preSign, Sign = "", Result = e.Message };
            }

        }

        public AliPayRequest JsApiPay(AliPayModel payModel)
        {
            payModel.SetProductCode("QUICK_WAP_WAY");

            var common = new AliPayCommonModel();
            common.SetMethod("alipay.trade.wap.pay");
            common.SetBizContent(payModel);

            var parameters = common.GetType().GetProperties().OrderBy(o => o.Name).ToDictionary(item => item.Name, item => item.GetValue(common).ToString());
            var str = BuildData.BuildParamStr(parameters);

            var sign = GenerateRsaAssist.RasSign(str, AliPayConfig.PrivateKey, SignType.Rsa2);
            parameters.Add("sign", sign);

            try
            {
                var from = BuildData.BuildHtmlRequest(parameters, "post", "post");
                return new AliPayRequest { IsSuccess = true, PreSign = str, Sign = sign, Result = from };
            }
            catch (Exception e)
            {
                return new AliPayRequest { IsSuccess = false, PreSign = str, Sign = sign, Result = e.Message };
            }
        }

        public async Task<AliRefundResponse> AliRefund(AliRefundModel refundModel)
        {
            var common = new AliPayCommonModel();
            common.SetMethod("alipay.trade.refund");
            common.SetBizContent(refundModel);

            var parameters = common.GetType().GetProperties().OrderBy(o => o.Name).ToDictionary(item => item.Name, item => item.GetValue(common).ToString());
            var str = BuildData.BuildParamStr(parameters);

            var sign = GenerateRsaAssist.RasSign(str, AliPayConfig.PrivateKey, SignType.Rsa2);
            parameters.Add("sign", sign);

            var response = await HttpUtil.CreatePostHttpResponse(AliPayConfig.Gateway, parameters);
            var result = await response.Content.ReadAsStringAsync();

            var jsonResult = JsonConvert.DeserializeObject<AliRefundResponse>(result);
            return jsonResult;
        }

        public async Task<AliRefundQueryResponse> AliRefundQuery(AliRefundQueryModel refundQueryModel)
        {
            var common = new AliPayCommonModel();
            common.SetMethod("alipay.trade.fastpay.refund.query");
            common.SetBizContent(refundQueryModel);
            var parameters = common.GetType().GetProperties().OrderBy(o => o.Name).ToDictionary(item => item.Name, item => item.GetValue(common).ToString());
            var str = BuildData.BuildParamStr(parameters);
            var sign = GenerateRsaAssist.RasSign(str, AliPayConfig.PrivateKey, SignType.Rsa2);
            parameters.Add("sign", sign);
            var response = await HttpUtil.CreatePostHttpResponse(AliPayConfig.Gateway, parameters);
            var result = await response.Content.ReadAsStringAsync();
            var jsonResult = JsonConvert.DeserializeObject<AliRefundQueryResponse>(result);
            return jsonResult;
        }

        public AliNotifyRequest AliNotify(Stream aliReturnData)
        {
            try
            {
                //獲取回調參數
                var s = aliReturnData;
                int count;
                var buffer = new byte[1024];
                var builder = new StringBuilder();
                while ((count = s.Read(buffer, 0, 1024)) > 0)
                {
                    builder.Append(Encoding.UTF8.GetString(buffer, 0, count));
                }
                s.Flush();
                s.Dispose();
                //request 接收的字符串含有urlencode,這里需要decode一下
                var alipayReturnData = builder.ToString().Split('&').ToDictionary(a => a.Split('=')[0], a => System.Net.WebUtility.UrlDecode(a.Split('=')[1]));
                //獲取sign
                var sign = alipayReturnData["sign"];
                //去除sign及signtype
                alipayReturnData.Remove("sign");
                alipayReturnData.Remove("sign_type");
                //獲取支付寶訂單號及商戶交易訂單號
                var tradeNo = alipayReturnData["trade_no"];
                var tradeIds = alipayReturnData["out_trade_no"];

                var dic = alipayReturnData.ToDictionary(d => d.Key, d => d.Value);

                var preSign = BuildData.BuildParamStr(dic);
                //驗簽
                var result = GenerateRsaAssist.VerifySign(preSign, AliPayConfig.AliPublicKey, sign, SignType.Rsa2);

                return result
                    ?
                    new AliNotifyRequest { IsVerify = true, PayNo = tradeNo, TradeIds = tradeIds, PayTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), Sign = sign, Content = preSign }
                    :
                    new AliNotifyRequest { IsVerify = false, PayNo = tradeNo, TradeIds = "", PayTime = "", Sign = sign, Content = preSign };
            }
            catch (Exception e)
            {
                return new AliNotifyRequest { IsVerify = false, PayNo = "", TradeIds = "", PayTime = "", Sign = "", Content = e.Message };
            }
        }
    }
View Code

a.接口參數主體為支付主體類,方法實現前,主體設置銷售產品碼

native支付:FAST_INSTANT_TRADE_PAY

手機網站支付:QUICK_WAP_WAY(舊版手機網站支付未涵蓋)

b.設置公共參數,設置接口名

native支付接口名:alipay.trade.page.pay

手機網站支付接口名:alipay.trade.wap.pay

c.app支付比較特殊,需要Ios或Android將數據拼接完成后傳入后端加簽返回給app,再由app支付sdk調起

需要注意的一個問題是, var sign = GenerateRsaAssist.RasSign(preSign, AliPayConfig.PrivateKey, SignType.Rsa2);

加簽得到的sign需要urlencode一下再返回,不然app調起會出錯

 sign = UrlEncoder.Default.Encode(sign);

 

退款神器,及退款查詢也需要添加主體類,這里就不再贅述

下一篇應該補上微信支付

支付寶支付如果有遺漏內容,請告知

如果發現任何問題也煩請指正。

long may the sunshine ~!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM