前面讨论了微信支付,接下来聊聊支付宝的APP支付(新款支付宝支付)。其实这些支付原理都一样,只不过具体到每个支付平台,所使用的支付配置参数不同,返回至支付端的下单参数也不同。
话不多说,直接上代码。
在App.Pay项目中使用NuGet管理器添加引用Alipay.AopSdk,也可以不添加引用,将官方SDK源码放至项目中。
添加完引用后,我们就可以开工了,新建文件夹AliPay,在文件夹中新建AliPayConfig类,存放支付宝APP支付所需的参数,同样,这些参数我也放在了配置文件中。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using System.Web.Configuration; 7 8 namespace App.Pay.AliPay 9 { 10 public class AliPayConfig 11 { 12 //支付宝网关地址 13 public static string serviceUrl = WebConfigurationManager.AppSettings["aliServiceUrl"].ToString(); 14 15 //应用ID 16 public static string appId = WebConfigurationManager.AppSettings["aliAppId"].ToString(); 17 18 //开发者私钥,由开发者自己生成 19 public static string privateKey = WebConfigurationManager.AppSettings["aliPrivateKey"].ToString(); 20 21 //支付宝的应用公钥 22 public static string publicKey = WebConfigurationManager.AppSettings["aliPublicKey"].ToString(); 23 24 //支付宝的支付公钥 25 public static string payKey = WebConfigurationManager.AppSettings["aliPayKey"].ToString(); 26 27 //服务器异步通知页面路径 28 public static string notify_url = WebConfigurationManager.AppSettings["aliNotifyUrl"].ToString(); 29 30 //页面跳转同步通知页面路径 31 public static string return_url = WebConfigurationManager.AppSettings["aliReturnUrl"].ToString(); 32 33 //参数返回格式,只支持json 34 public static string format = WebConfigurationManager.AppSettings["aliFormat"].ToString(); 35 36 // 调用的接口版本,固定为:1.0 37 public static string version = WebConfigurationManager.AppSettings["aliVersion"].ToString(); 38 39 // 商户生成签名字符串所使用的签名算法类型,目前支持RSA2和RSA,推荐使用RSA2 40 public static string signType = WebConfigurationManager.AppSettings["aliSignType"].ToString(); 41 42 // 字符编码格式 目前支持utf-8 43 public static string charset = WebConfigurationManager.AppSettings["aliCharset"].ToString(); 44 45 // false 表示不从文件加载密钥 46 public static bool keyFromFile = false; 47 48 // 日志记录 49 public static string LogPath = WebConfigurationManager.AppSettings["AliLog"].ToString(); 50 } 51 }
支付宝支付中有个沙箱测试环境,我们可以先在沙箱环境下调通整个流程(沙箱支付宝里面的钱是虚拟的哦)。介绍一下这几个支付参数。
①aliServiceUrl支付宝网关地址,固定不变的,沙箱环境下用沙箱的,正式环境下用正式的。
②aliAppId支付宝APPID,aliPrivateKey支付宝应用私钥,aliPublicKey支付宝应用公钥,aliPayKey支付宝公钥
aliPublicKey和aliPayKey是不一样的,一个是应用公钥,一个是支付宝公钥,回调接口中验签使用的是支付宝公钥
③aliNotifyUrl服务器通知,aliReturnUrl网页重定向通知(暂时没有用到)。主要使用到的还是aliNotifyUrl,买家付完款后(trade_status=WAIT_SELLER_SEND_GOODS),支付宝服务端会自动向商户后台发送支付回调通知,同样,商户在支付回调通知中修改订单相关状态,反馈给支付宝success,表示成功接收到回调,这个状态下支付宝不会再继续通知商户后台。
④aliFormat、aliVersion、aliSignType、aliCharset这几个参数都是固定不变的,签名的时候使用。
1 <!--支付宝app支付--> 2 <add key="aliServiceUrl" value=""/> 3 <add key="aliAppId" value="" /> 4 <add key="aliPrivateKey" value=""/> 5 <add key="aliPublicKey" value="" /> 6 <add key="aliPayKey" value="" /> 7 <add key="aliNotifyUrl" value="" /> 8 <add key="aliReturnUrl" value="" /> 9 <add key="aliFormat" value="json" /> 10 <add key="aliVersion" value="1.0" /> 11 <add key="aliSignType" value="RSA2" /> 12 <add key="aliCharset" value="utf-8" />
新建AliPay类
1 using Aop.Api; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace App.Pay.AliPay 9 { 10 public class AliPay 11 { 12 public static IAopClient GetAlipayClient() 13 { 14 string serviceUrl = AliPayConfig.serviceUrl; 15 16 string appId = AliPayConfig.appId; 17 18 string privateKey = AliPayConfig.privateKey; 19 20 string publivKey = AliPayConfig.publicKey; 21 22 string format = AliPayConfig.format; 23 24 string version = AliPayConfig.version; 25 26 string signType = AliPayConfig.signType; 27 28 string charset = AliPayConfig.charset; 29 30 bool keyFromFile = AliPayConfig.keyFromFile; 31 32 33 IAopClient client = new DefaultAopClient(serviceUrl, appId, privateKey, format, version, signType, publivKey, charset, keyFromFile); ; 34 35 return client; 36 } 37 } 38 }
接下来就是业务中的具体调用
1 using Aop.Api; 2 using Aop.Api.Domain; 3 using Aop.Api.Request; 4 using Aop.Api.Response; 5 using Aop.Api.Util; 6 using App.Common.Extension; 7 using App.Pay.AliPay; 8 using System; 9 using System.Collections.Generic; 10 using System.Collections.Specialized; 11 using System.Linq; 12 using System.Web; 13 using System.Web.Mvc; 14 15 namespace App.WebTest.Controllers 16 { 17 public class AliPayController : BaseController 18 { 19 /// <summary> 20 /// 订单编号 21 /// </summary> 22 /// <param name="oidStr"></param> 23 /// <returns></returns> 24 public ActionResult AliPay(string oidStr) 25 { 26 #region 验证订单有效 27 28 if (string.IsNullOrEmpty(oidStr)) 29 { 30 return Json(false, "OrderError"); 31 } 32 33 int[] oIds = Serialize.JsonTo<int[]>(oidStr); 34 35 decimal payPrice = 0; 36 37 ///订单验证,统计订单总金额 38 39 #endregion 40 41 #region 统一下单 42 try 43 { 44 var notify_url = AliPayConfig.notify_url; 45 var return_url = AliPayConfig.return_url; 46 IAopClient client = Pay.AliPay.AliPay.GetAlipayClient(); 47 AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest(); 48 //SDK已经封装掉了公共参数,这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。 49 AlipayTradeAppPayModel model = new AlipayTradeAppPayModel(); 50 model.Subject = "商品购买"; 51 model.TotalAmount = payPrice.ToString("F2"); 52 model.ProductCode = "QUICK_MSECURITY_PAY"; 53 Random rd = new Random(); 54 var payNum = DateTime.Now.ToString("yyyyMMddHHmmss") + rd.Next(0, 1000).ToString().PadLeft(3, '0'); 55 model.OutTradeNo = payNum; 56 model.TimeoutExpress = "30m"; 57 request.SetBizModel(model); 58 request.SetNotifyUrl(notify_url); 59 //request.SetReturnUrl(return_url); 60 //这里和普通的接口调用不同,使用的是sdkExecute 61 AlipayTradeAppPayResponse response = client.SdkExecute(request); 62 63 //统一下单 64 //OrderBll.Value.UpdateOrderApp(oIds, payNum); 65 66 return Json(true, new { response.Body }, "OK"); 67 } 68 catch (Exception ex) 69 { 70 return Json(new { Result = false, msg = "缺少参数" }); 71 } 72 #endregion 73 } 74 75 /// <summary> 76 /// 页面跳转同步通知页面 77 /// </summary> 78 /// <returns></returns> 79 public ActionResult ReturnUrl() 80 { 81 Pay.Log Log = new Pay.Log(Pay.AliPay.AliPayConfig.LogPath); 82 Log.Info("ReturnUrl", "支付页面同步回调"); 83 //将同步通知中收到的所有参数都存放到map中 84 IDictionary<string, string> map = GetRequestGet(); 85 if (map.Count > 0) //判断是否有带返回参数 86 { 87 try 88 { 89 //支付宝的公钥 90 string alipayPublicKey = AliPayConfig.payKey; 91 string signType = AliPayConfig.signType; 92 string charset = AliPayConfig.charset; 93 bool keyFromFile = false; 94 // 获取支付宝GET过来反馈信息 95 bool verify_result = AlipaySignature.RSACheckV1(map, alipayPublicKey, charset, signType, keyFromFile); 96 if (verify_result) 97 { 98 // 验证成功 99 return Json(new { Result = true, msg = "验证成功" }); 100 } 101 else 102 { 103 Log.Error("AliPayNotifyUrl", "支付验证失败"); 104 return Json(new { Result = false, msg = "验证失败" }); 105 } 106 } 107 catch (Exception e) 108 { 109 //throw new Exception(e.Message); 110 return Json(new { Result = false, msg = "验证失败" }); 111 Log.Error("AliPayNotifyUrl", "支付验证失败"); 112 } 113 } 114 else 115 { 116 return Json(new { Result = false, msg = "无返回参数" }); 117 } 118 } 119 120 /// <summary> 121 /// 服务器异步通知页面 122 /// </summary> 123 public void AliPayNotifyUrl() 124 { 125 Pay.Log Log = new Pay.Log(AliPayConfig.LogPath); 126 Log.Info("AliPayNotifyUrl", "支付页面异步回调"); 127 IDictionary<string, string> map = GetRequestPost(); 128 129 if (map.Count > 0) 130 { 131 try 132 { 133 string alipayPublicKey = AliPayConfig.payKey; 134 string signType = AliPayConfig.signType; 135 string charset = AliPayConfig.charset; 136 bool keyFromFile = false; 137 138 bool verify_result = AlipaySignature.RSACheckV1(map, alipayPublicKey, charset, signType, keyFromFile); 139 Log.Info("AliPayNotifyUrl验签", verify_result + ""); 140 141 //验签成功后,按照支付结果异步通知中的描述,对支付结果中的业务内容进行二次校验,校验成功后再response中返回success并继续商户自身业务处理,校验失败返回false 142 if (verify_result) 143 { 144 //商户订单号 145 string out_trade_no = map["out_trade_no"]; 146 //支付宝交易号 147 string trade_no = map["trade_no"]; 148 //交易创建时间 149 string gmt_create = map["gmt_create"]; 150 //交易付款时间 151 string gmt_payment = map["gmt_payment"]; 152 //通知时间 153 string notify_time = map["notify_time"]; 154 //通知类型 trade_status_sync 155 string notify_type = map["notify_type"]; 156 //通知校验ID 157 string notify_id = map["notify_id"]; 158 //开发者的app_id 159 string app_id = map["app_id"]; 160 //卖家支付宝用户号 161 string seller_id = map["seller_id"]; 162 //买家支付宝用户号 163 string buyer_id = map["buyer_id"]; 164 //实收金额 165 string receipt_amount = map["receipt_amount"]; 166 //交易状态 167 string return_code = map["trade_status"]; 168 169 //交易状态TRADE_FINISHED的通知触发条件是商户签约的产品不支持退款功能的前提下,买家付款成功; 170 //或者,商户签约的产品支持退款功能的前提下,交易已经成功并且已经超过可退款期限 171 //状态TRADE_SUCCESS的通知触发条件是商户签约的产品支持退款功能的前提下,买家付款成功 172 if (return_code == "TRADE_FINISHED" || return_code == "TRADE_SUCCESS") 173 { 174 string msg; 175 176 Log.Error("AliPayNotifyUrl", receipt_amount + "==" + trade_no + "==" + return_code + "==" + out_trade_no + "==" + gmt_payment); 177 178 //判断该笔订单是否在商户网站中已经做过处理 179 ///支付回调的业务处理 180 //bool res = OrderBll.Value.CompleteAliPay(receipt_amount, trade_no, return_code, out_trade_no, gmt_payment, out msg); 181 bool res = true; 182 183 if (res == false) 184 { 185 Response.Write("添加支付信息失败!"); 186 } 187 Log.Error("AliPayNotifyUrl", "支付成功"); 188 Response.Write("success"); //请不要修改或删除 189 } 190 } 191 else 192 { 193 //验证失败 194 Log.Error("AliPayNotifyUrl", "支付验证失败"); 195 Response.Write("验证失败!"); 196 } 197 } 198 catch (Exception e) 199 { 200 Response.Write("添加支付信息失败!"); 201 Log.Error("AliPayNotifyUrl", "添加支付信息失败"); 202 } 203 } 204 else 205 { 206 //无返回参数 207 Response.Write("无返回参数!"); 208 Log.Error("AliPayNotifyUrl", "无返回参数"); 209 } 210 } 211 //[AllowUser] 212 //public ActionResult TestAliPay() 213 //{ 214 215 // var receipt_amount = "0.01"; 216 // var trade_no = "20181226220013......."; 217 // var return_code = "TRADE_SUCCESS"; 218 // var out_trade_no = "20181226103124129"; 219 // var gmt_payment = "2018-12-26 10:31:29"; 220 221 // string msg = ""; 222 // bool res = OrderBll.Value.CompleteAliPay(receipt_amount, trade_no, return_code, out_trade_no, gmt_payment, out msg); 223 224 // return Json(res); 225 //} 226 227 /// <summary> 228 /// 获取支付宝Get过来的通知消息,并以“参数名=参数值”的形式组成数组 229 /// </summary> 230 /// <returns></returns> 231 public IDictionary<string, string> GetRequestGet() 232 { 233 Pay.Log Log = new Pay.Log(Pay.AliPay.AliPayConfig.LogPath); 234 int i = 0; 235 IDictionary<string, string> sArry = new Dictionary<string, string>(); 236 NameValueCollection coll; 237 coll = Request.QueryString; 238 239 String[] requstItem = coll.AllKeys; 240 241 for (i = 0; i < requstItem.Length; i++) 242 { 243 Log.Info("GetRequestGet", requstItem[i] + ":" + Request.QueryString[requstItem[i]]); 244 sArry.Add(requstItem[i], Request.QueryString[requstItem[i]]); 245 } 246 247 return sArry; 248 } 249 250 /// <summary> 251 /// 获取支付宝POST过来通知消息,并以“参数名=参数值”的形式组成数组 252 /// </summary> 253 /// <returns>request回来的信息组成的数组</returns> 254 public IDictionary<string, string> GetRequestPost() 255 { 256 Pay.Log Log = new Pay.Log(Pay.AliPay.AliPayConfig.LogPath); 257 int i = 0; 258 IDictionary<string, string> sArray = new Dictionary<string, string>(); 259 NameValueCollection coll; 260 261 //Load Form variables into NameValueCollection variable. 262 coll = Request.Form; 263 264 // Get names of all forms into a string array. 265 String[] requestItem = coll.AllKeys; 266 for (i = 0; i < requestItem.Length; i++) 267 { 268 Log.Info("GetRequestPost", requestItem[i] + ":" + Request.Form[requestItem[i]]); 269 sArray.Add(requestItem[i], Request.Form[requestItem[i]]); 270 } 271 272 return sArray; 273 } 274 } 275 }