.net core/c# 支付寶支付和微信支付


前言:最近做了App的支付功能,雖然最后的代碼只有巴掌大,但是踩坑的經歷真的是異常豐富,問就是官方坑爹的版本升級和core少的可憐的參考資料(望天)。 

閱讀本文章您將收獲到:1.微信和支付寶支付的保姆級后端教程

                                         2.官方文檔解析

                                        3.獨家的踩坑提示

                                        4.一個本地可運行的demo

注意事項:1. 僅后端代碼部分,不涉及任何前端

                  2.僅APP支付

代碼demo:      https://files-cdn.cnblogs.com/files/rulasann/PayUtil.zip  (已重新上傳)

 

 


 

part1:支付寶App支付

 1.官方文檔:

    https://opendocs.alipay.com/apis/api_1/alipay.trade.app.pay

 2.參數說明(請結合官方文檔閱讀):

    有兩種參數類型,公共參數和請求參數,如果調用官方的SDK,公共參數中的sign不需要自己簽。例:

IAopClient client = new DefaultAopClient("https://openapi.alipay.com/gateway.do", "app_id", "merchant_private_key", "json", "1.0", "RSA2", "alipay_public_key", "GBK", false);

    公共參數中的biz_content即為請求參數,這里可不參照官方給的示例寫,他們有封裝好的model。例:

//以下為發起請求的最簡參數
AlipayTradeAppPayModel model = new AlipayTradeAppPayModel(); model.TotalAmount = "0.01"; // 訂單總金額,單位為元,精確到小數點后兩位,取值范圍[0.01,100000000] //model.Body = ""; // 商品描述 model.Subject = "交易標題"; // 商品標題/交易標題/訂單標題/訂單關鍵字等 model.OutTradeNo = ""; // 商戶訂單號,由商家自定義,需保證商家系統中唯一。僅支持數字、字母、下划線 model.ProductCode = "QUICK_MSECURITY_PAY"; // 銷售產品碼,商家和支付寶簽約的產品碼。QUICK_MSECURITY_PAY:App支付。 request.SetBizModel(model); // 將業務model載入到request

    關於notify_url,非必填,如果需要,可以這樣寫:

request.SetNotifyUrl(notify_url)

3.完整請求示例,需Nuget引用AlipaySDKNet.Standard,其次建議用一個實體類封裝配置信息:

 1 public class AliPayBasicItem
 2     {
 3         /// <summary>
 4         /// 開發者的應用ID , 必填
 5         /// </summary>
 6         public static string app_id = "";
 7 
 8         /// <summary>
 9         /// 請求使用的編碼格式
10         /// </summary>
11         public static string charset = "utf-8";
12 
13         /// <summary>
14         /// 僅支持"JSON",非必填
15         /// </summary>
16         public static string format = "json";
17 
18         /// <summary>
19         /// 簽名算法
20         /// </summary>
21         public static string sign_type = "RSA2";
22 
23         /// <summary>
24         /// 調用的接口版本 
25         /// </summary>
26         public static string version = "1.0";
27 
28         /// <summary>
29         /// 支付寶請求url
30         /// </summary>
31         public static string url = "https://openapi.alipay.com/gateway.do";
32 
33         /// <summary>
34         /// 商戶私鑰 (必填)
35         /// </summary>
36         public static string merchant_private_key = "";
37 
38         /// <summary>
39         /// 支付寶公鑰 (必填)
40         /// </summary>
41         public static string alipay_public_key = "";
42 
43         /// <summary>
44         /// 支付完成后的通知地址 非必填
45         /// </summary>
46         public static string pay_notify_url = "";
47 
48         /// <summary>
49         /// 幣種
50         /// </summary>
51         public static string currency = "CNY";
52 
53         /// <summary>
54         /// 退款完成后的通知地址 非必填
55         /// </summary>
56         //public static string refund_notify_url = "";
57 
58         /// <summary>
59         /// 應用名稱
60         /// </summary>
61         public static string app_name = "mc";
62 
63         /// <summary>
64         /// 簽約號 (必填)
65         /// </summary>
66         public static string pid = "";
67     }
View Code
 1 public string AppPay()
 2         {
 3             IAopClient client = new DefaultAopClient(AliPayBasicItem.url, AliPayBasicItem.app_id, AliPayBasicItem.merchant_private_key, AliPayBasicItem.format, AliPayBasicItem.version, AliPayBasicItem.sign_type, AliPayBasicItem.alipay_public_key, AliPayBasicItem.charset, false);
 4             AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
 5 
 6             AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
 7             model.TotalAmount = "0.01"; // 訂單總金額,單位為元,精確到小數點后兩位,取值范圍[0.01,100000000]
 8             //model.Body = ""; // 商品描述
 9             model.Subject = "交易標題"; // 商品標題/交易標題/訂單標題/訂單關鍵字等
10             model.OutTradeNo = ""; // 商戶訂單號,由商家自定義,需保證商家系統中唯一。僅支持數字、字母、下划線
11             model.ProductCode = "QUICK_MSECURITY_PAY"; // 銷售產品碼,商家和支付寶簽約的產品碼。QUICK_MSECURITY_PAY:App支付。
12             request.SetBizModel(model);  // 將業務model載入到request
13             //request.SetNotifyUrl(AliPayBasicItem.pay_notify_url); 
14 
15             AlipayTradeAppPayResponse response = client.SdkExecute(request);
16             var info = response.Body;
17 
18             return info   // 將這里的info直接返給前端          
19         }
View Code

4.響應:

有坑注意:官方給的響應示例是個json,實際上我們拿到的(即上面示例中的info)並不長這樣,如下圖所示,是加密過的,看不懂沒關系,不需要處理,直接返給前端就好,由前端拿着那長串去喚起支付,至此支付過程結束。

 5.支付結果處理:

如果notify_url 賦了值,支付寶會將支付結果post給這個接口,處理一下接收數據

② 可主動調用查詢接口去獲取支付結果 https://opendocs.alipay.com/apis/api_1/alipay.trade.query ,寫法跟支付類似,封裝的model為AlipayTradeQueryModel

 


 

 part2:微信App支付

 1.官方文檔:

   https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_2_1.shtml

   有坑注意:微信支付接口已經升級到v3版本,網上找的示例可能還是老版本,區別是,老版本為MD5加密,參數格式為XML,新版本為SHA2加密,參數格式為json

 2.參數說明:

   官方給的請求參數如下圖,沒什么好說的,就一個json:

   

   *金額total是int類型;

   *notify_url與支付寶不同,微信是必填,官方建議是https且無端口號,實際上http加有端口號也是可以的(不建議哈);

3.簽名:

   需手動對接口參數簽名,簽名過程參考:https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay4_0.shtml ,以下以微信支付為例:

①拼接待簽名字符串:請求方法\n+URL\n+時間戳\n+隨機字符串\n+請求參數\n

TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
string time_str = Convert.ToInt64(ts.TotalSeconds).ToString();
string sign_url = "/v3/pay/transactions/app";
string nonce_str = Guid.NewGuid().ToString().Replace("-", "");
string body_str = "";  // 上面的參數
string to_sign = "POST" + "\n" + sign_url + "\n" + time_str + "\n" + nonce_str + "\n" + body_str + "\n";

②使用商戶私鑰對待簽名串進行SHA256 with RSA簽名,並對簽名結果進行Base64編碼得到簽名值

byte[] keyData = Convert.FromBase64String(key);
using (CngKey cngKey = CngKey.Import(keyData, CngKeyBlobFormat.Pkcs8PrivateBlob))
using (RSACng rsa = new RSACng(cngKey))
{
    byte[] data = System.Text.Encoding.UTF8.GetBytes(to_sign);
    string sign = Convert.ToBase64String(rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1));
}

③設置請求頭:Authorization: 認證類型 簽名信息

    認證類型為:WECHATPAY2-SHA256-RSA2048

    簽名信息為:商戶號mchid+商戶API證書serial_no+請求隨機串nonce_str+時間戳timestamp+簽名值sign

string value = $"WECHATPAY2-SHA256-RSA2048 " + "mchid=\"" + mch_id + "\",serial_no=\"" + serial_no + "\",nonce_str=\"" + nonce_str + "\",timestamp=\"" + time_str + "\",signature=\"" + sign + "\"";
request.Headers.Add("Authorization", value);

   有坑注意:請求頭還需有以下設置,不然會報400

request.ContentType = "application/json;charset=utf-8";
request.Headers.Add("Accept", "application/json");
request.Headers.Add("User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)");

4.返回值處理:

  請參考https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_2_4.shtml

  以上是前端調起支付要用到的字段,由后端通過返回值拼接處理而成

5.完整代碼示例:

①要用到的實體類 

  1  public class WxPayBasicItem
  2     {
  3         /// <summary>
  4         /// 小程序ID
  5         /// </summary>
  6         public static string appid = "";
  7 
  8         /// <summary>
  9         /// 商戶號
 10         /// </summary>
 11         public static string mch_id = "";
 12 
 13         /// <summary>
 14         /// 商戶證書序列號
 15         /// </summary>
 16         public static string serial_no = "";
 17 
 18         /// <summary>
 19         /// 商戶私鑰
 20         /// </summary>
 21         public static string private_key = "";
 22 
 23         /// <summary>
 24         /// 小程序支付請求url
 25         /// </summary>
 26         //public static string order_url = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";
 27 
 28         /// <summary>
 29         /// app支付請求url
 30         /// </summary>
 31         public static string app_url = "https://api.mch.weixin.qq.com/v3/pay/transactions/app";
 32 
 33         /// <summary>
 34         /// 預支付完成后的通知地址
 35         /// </summary>
 36         public static string order_notify_url = "https://weixin.qq.com/"; 
 37 
 38         /// <summary>
 39         /// 交易類型 小程序取值"JSAPI"
 40         /// </summary>
 41         //public static string trade_type = "JSAPI";
 42 
 43         /// <summary>
 44         /// 查詢訂單請求url
 45         /// </summary>
 46         public static string query_url = "https://api.mch.weixin.qq.com/v3/pay/transactions/id";
 47 
 48         /// <summary>
 49         /// 應用密鑰 
 50         /// </summary>
 51         //public static string secret = "";
 52 
 53         /// <summary>
 54         /// 授權登錄url
 55         /// </summary>
 56         public static string auth_login_url = "https://api.weixin.qq.com/sns/oauth2/access_token";
 57 
 58         /// <summary>
 59         /// 微信獲取用戶信息url
 60         /// </summary>
 61         public static string wx_user_url = "https://api.weixin.qq.com/sns/userinfo";
 62     }
 63 
 64     /// <summary>
 65     /// App預支付參數
 66     /// </summary>
 67     public class AppPaymentData
 68     {
 69         /// <summary>
 70         /// 小程序ID
 71         /// </summary>
 72         //[Required]
 73         public string appid { get; set; }
 74 
 75         /// <summary>
 76         /// 商戶號
 77         /// </summary>
 78         public string mchid { get; set; }
 79 
 80         /// <summary>
 81         /// 商品描述 商品簡單描述,不超過128字節
 82         /// </summary>
 83         public string description { get; set; }
 84 
 85         /// <summary>
 86         /// 商戶訂單號
 87         /// </summary>
 88         public string out_trade_no { get; set; }
 89 
 90         /// <summary>
 91         /// 交易結束時間 yyyyMMddHHmmss,非必填
 92         /// </summary>
 93         //public string time_expire { get; set; }
 94 
 95         /// <summary>
 96         /// 附加數據 非必填
 97         /// </summary>
 98         //public string attach { get; set; }
 99 
100         /// <summary>
101         /// 異步通知地址 通知url必須為外網可訪問的url,不能攜帶參數。
102         /// </summary>
103         public string notify_url { get; set; }
104 
105         /// <summary>
106         /// 訂單優惠標記
107         /// </summary>
108         //public string goods_tag { get; set; }
109 
110         /// <summary>
111         /// 訂單金額信息
112         /// </summary>
113         public PaymentDataAmount amount { get; set; }
114 
115         /// <summary>
116         /// 場景信息
117         /// </summary>
118         //public PaymentDataScene scene_info { get; set; }
119 
120         /// <summary>
121         /// 結算信息
122         /// </summary>
123         //public PaymentDataSettle settle_info { get; set; }
124     }
125 
126     /// <summary>
127     /// 訂單金額
128     /// </summary>
129     public class PaymentDataAmount
130     {
131         /// <summary>
132         /// 總金額 訂單總金額,單位為分
133         /// </summary>
134         public int total { get; set; }
135 
136         /// <summary>
137         /// 貨幣類型 非必填,默認"CNY"
138         /// </summary>
139         public string currency { get; set; }
140     }
141 
142     /// <summary>
143     /// App調起支付參數
144     /// </summary>
145     public class AppPayBackItem
146     {
147         /// <summary>
148         /// 應用id
149         /// </summary>
150         public string appid { get; set; }
151 
152         /// <summary>
153         /// 商戶號
154         /// </summary>
155         public string partnerid { get; set; }
156 
157         /// <summary>
158         /// 預支付交易會話ID
159         /// </summary>
160         public string prepayid { get; set; }
161 
162         /// <summary>
163         /// 訂單詳情擴展字符串
164         /// </summary>
165         public string package { get; set; }
166 
167         /// <summary>
168         /// 隨機字符串
169         /// </summary>
170         public string noncestr { get; set; }
171 
172         /// <summary>
173         /// 時間戳
174         /// </summary>
175         public string timestamp { get; set; }
176 
177         /// <summary>
178         /// 簽名
179         /// </summary>
180         public string sign { get; set; }
181 
182     }
View Code

②工具類

 1 public static class WxPayMethod
 2     {
 3         /// <summary>
 4         /// 微信app下單
 5         /// </summary>
 6         public static string PayTest(AppPaymentData item)
 7         {
 8             var param = JsonConvert.SerializeObject(item);
 9 
10             string sign_url = WxPayBasicItem.app_url.Split(".com")[1].ToString();
11             string time_str = GetTimeStamp();
12             string nonce_str = GetNonceStr();
13             string body_str = param;
14 
15             string to_sign_info = "POST" + "\n" + sign_url + "\n" + time_str + "\n" + nonce_str + "\n" + body_str + "\n";
16             string sign_info = RsaSign(to_sign_info, WxPayBasicItem.private_key);
17 
18             HttpWebRequest request = (HttpWebRequest)WebRequest.Create(WxPayBasicItem.app_url);
19             request.Method = "POST";
20             string value = $"WECHATPAY2-SHA256-RSA2048 " + "mchid=\"" + WxPayBasicItem.mch_id + "\",serial_no=\"" + WxPayBasicItem.serial_no + "\",nonce_str=\"" + nonce_str + "\",timestamp=\"" + time_str + "\",signature=\"" + sign_info + "\"";
21             request.Headers.Add("Authorization", value);
22             request.ContentType = "application/json;charset=utf-8";
23             request.Headers.Add("Accept", "application/json");
24             request.Headers.Add("User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)");
25 
26             byte[] bs = Encoding.UTF8.GetBytes(param);
27             request.ContentLength = bs.Length;
28             if (bs.Length > 0)
29             {
30                 using (Stream reqStream = request.GetRequestStream())
31                 {
32                     reqStream.Write(bs, 0, bs.Length);
33                     reqStream.Close();
34                 }
35             }
36             HttpWebResponse response = (HttpWebResponse)request.GetResponse();
37             if (response.StatusCode == HttpStatusCode.OK)
38             {
39                 using (Stream mystream = response.GetResponseStream())
40                 {
41                     using (StreamReader reader = new StreamReader(mystream))
42                     {
43                         var info = reader.ReadToEnd();
44                         return info;
45                     }
46                 }
47             }
48             else
49             {
50                 return "";
51             }
52         }
53 
54         /// <summary>
55         /// Rsa簽名
56         /// </summary>
57         /// <param name="str"></param>
58         /// <param name="key"></param>
59         /// <returns></returns>
60         public static string RsaSign(string str, string key)
61         {
62             var result = string.Empty;
63 
64             byte[] keyData = Convert.FromBase64String(key);
65             using (CngKey cngKey = CngKey.Import(keyData, CngKeyBlobFormat.Pkcs8PrivateBlob))
66             using (RSACng rsa = new RSACng(cngKey))
67             {
68                 byte[] data = System.Text.Encoding.UTF8.GetBytes(str);
69                 result = Convert.ToBase64String(rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1));
70             }
71 
72             return result;
73         }
74 
75         /// <summary>
76         /// 生成隨機字符串
77         /// </summary>
78         /// <returns></returns>
79         public static string GetNonceStr()
80         {
81             return Guid.NewGuid().ToString().Replace("-", "");
82         }
83 
84         /// <summary>
85         /// 生成時間戳,標准北京時間,時區為東八區,自1970年1月1日 0點0分0秒以來的秒數
86         /// </summary>
87         /// <returns></returns>
88         public static string GetTimeStamp()
89         {
90             TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
91             return Convert.ToInt64(ts.TotalSeconds).ToString();
92         }
93     }
View Code

③主方法

public AppPayBackItem AppOrder()
        {
            var pay_item = new AppPaymentData();
            pay_item.amount = new PaymentDataAmount();
            string url = WxPayBasicItem.app_url;

            pay_item.appid = WxPayBasicItem.appid;
            pay_item.mchid = WxPayBasicItem.mch_id;
            pay_item.description = ""; //交易描述
            pay_item.out_trade_no = ""; //商戶訂單號,商戶系統中唯一
            pay_item.notify_url = WxPayBasicItem.order_notify_url;
            pay_item.amount.total = 1; //這里是int類型,單位為分
            pay_item.amount.currency = "CNY";

            string response = WxPayMethod.PayTest(pay_item);

            if (!string.IsNullOrEmpty(response))
            {
                //處理返回結果
                string prepayid = ((JObject)JsonConvert.DeserializeObject(response))["prepay_id"].ToString();
                var info = new AppPayBackItem();
                info.appid = WxPayBasicItem.appid;
                info.partnerid = WxPayBasicItem.mch_id;
                info.prepayid = prepayid;
                info.package = "Sign=WXPay"; // 照着填
                info.noncestr = WxPayMethod.GetNonceStr();
                info.timestamp = WxPayMethod.GetTimeStamp();
                string to_sign = info.appid + "\n" + info.timestamp + "\n" + info.noncestr + "\n" + info.prepayid + "\n";
                info.sign = WxPayMethod.RsaSign(to_sign, WxPayBasicItem.private_key);

                return  info;  //把這個返回給前端就行               
            }
            else
            {
                // 失敗
            }

        }
View Code

6.支付結果處理:與支付寶類似 

7.輔助工具建議:

①微信官方的加簽驗簽工具   (如果是自己寫簽名方法的,建議用這個測一下)

②http請求工具:Advanced-REST-client(網上一搜一大把教程,這個誠心推薦!不然微信支付能測到你懷疑人生!當然其它模擬請求工具也是可以的)


免責聲明!

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



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