前面讨论了微信支付,接下来聊聊支付宝的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 AliPayConfig11 {12 //支付宝网关地址13 public static string serviceUrl = WebConfigurationManager.AppSettings["aliServiceUrl"].ToString();14 15 //应用ID16 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 //参数返回格式,只支持json34 public static string format = WebConfigurationManager.AppSettings["aliFormat"].ToString();35 36 // 调用的接口版本,固定为:1.037 public static string version = WebConfigurationManager.AppSettings["aliVersion"].ToString();38 39 // 商户生成签名字符串所使用的签名算法类型,目前支持RSA2和RSA,推荐使用RSA240 public static string signType = WebConfigurationManager.AppSettings["aliSignType"].ToString();41 42 // 字符编码格式 目前支持utf-843 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 }
View Code
支付宝支付中有个沙箱测试环境,我们可以先在沙箱环境下调通整个流程(沙箱支付宝里面的钱是虚拟的哦)。介绍一下这几个支付参数。
①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" />
View Code
新建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 AliPay11 {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 }
View Code
接下来就是业务中的具体调用
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 else102 {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 else115 {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 try132 {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并继续商户自身业务处理,校验失败返回false142 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_sync155 string notify_type = map["notify_type"];156 //通知校验ID157 string notify_id = map["notify_id"];158 //开发者的app_id159 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 else192 {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 else205 {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 }
View Code