Apple Pay 支付集成
交易步骤:
1、浏览并选购商品:用户通过手机客户端与商户系统交互浏览选购商品,客户端向商户系统发送
购买商品请求数据。
2、生成并推送订单信息请求数据:商户系统根据选购商品请求数据生成订单数据集,把构造完成
的订单信息数据集合按照移动支付系统的订单信息推送接口,通过商户后台提交(POST)的方式传递
给移动支付系统。
3、移动支付系统对请求数据进行处理:移动支付系统得到这些集合后,会先进行安全校验等验证,
一系列验证通过后便会处理这次发送过来的数据请求。
4、返回交易流水号:移动支付系统返回商户系统交易流水号应答。
5、转发交易流水号:商户系统转发交易流水号至手机客户端。
6、调用支付控件,发起支付请求数据:手机客户端收到交易流水号等要素,调起支付控件,用户
输入支付信息后,客户端按照移动支付系统的支付接口规则组装并发送支付请求数据至移动支付系统。
7、移动支付系统对请求数据进行处理:移动支付系统得到这些集合后,会先进行安全校验等验证,
一系列验证通过后便会处理这次发送过来的数据请求。
8、返回支付结果:移动支付系统返回结果至手机客户端。
9、返回支付结果:支付控件返回商户客户端支付结果(目前为了兼容控件第一期,未采用frontEndUrl
方式来返回结果)。
10、移动支付系统后台异步返回处理的结果数据:对于成功处理完成的交易,移动支付系统服务器
主动发起通知,调用商户在请求时设定好的后台通知地址路径(参数backEndUrl), 把支付结果数据反
馈给商户。
11 商户对获取的返回结果数据进行处理:商户在前台通知处理页面(参数frontEndUrl 指定页面)
或服务器后台通知页面(参数backEndUrl 指定页面)获取移动支付返回的结果数据后,可以结合自身
网站的业务逻辑进行数据处理(如:订单更新等操作)。以后台通知为准。若未收到后台通知,需要发
起交易信息查询请求。相关说明:
商户后台接收到移动支付系统支付成功通知交易后,需返回全渠道系统后台确认已收到应答。
生成TN
/// <summary> /// ApplePay支付接口服务 /// </summary> [RoutePrefix("api/applepay")] public class ApplePayController : ApiController { private static readonly Logger logger = LogManager.GetCurrentClassLogger(); /// <summary> /// ApplePay支付接口服务 /// </summary> private readonly IApplePayService _applePayService; /// <summary> /// 构造函数 /// </summary> /// <param name="applePayService">ApplePay支付接口服务</param> public ApplePayController(IApplePayService applePayService) { _applePayService = applePayService; } /// <summary> /// 生成交易流水号[即TN] /// </summary> /// <param name="payTradeNo">参数</param> /// <returns></returns> [HttpPost] [Route("generate_trade_no")] public async Task<IHttpActionResult> GenerateTradeNoAsync(PayPalData payPal) { var data = await _applePayService.GenerateTradeNoAsync(payPal); return Ok(new { IsError = data.IsError, Msg = data.Msg, Data = data.Data }); } } /// <summary> /// 生成交易流水号[即TN] /// </summary> /// <param name="payPal">参数</param> /// <returns></returns> public async Task<WebAPIResponse> GenerateTradeNoAsync(PayPalData payPal) { try { var param = new Dictionary<string, string>(); //版本号 param["version"] = "5.0.0"; //编码方式 param["encoding"] = "UTF-8"; //交易类型 param["txnType"] = "01"; //交易子类 param["txnSubType"] = "01"; //业务类型 param["bizType"] = "000201"; //签名方法 param["signMethod"] = "01"; //渠道类型 param["channelType"] = "08"; //接入类型 param["accessType"] = "0"; //前台通知地址 //param["frontUrl"] = SDKConfig.FrontUrl; //后台通知地址 param["backUrl"] = SDKConfig.BackUrl; //交易币种 param["currencyCode"] = "156"; //商户号 param["merId"] = SDKConfig.MerId; //商户订单号,8-32位数字字母,不能含“-”或“_” param["orderId"] = payPal.OutTradeNO; //订单发送时间,参考取法: DateTime.Now.ToString("yyyyMMddHHmmss") param["txnTime"] = DateTime.Now.ToString("yyyyMMddHHmmss"); //交易金额,单位分 param["txnAmt"] = payPal.TotalFee; //请求方保留域,透传字段,查询、通知、对账文件中均会原样出现,如有需要请启用并修改自己希望透传的数据 param["reqReserved"] = payPal.Resv; //签名 AcpService.Sign(param, System.Text.Encoding.UTF8); string url = SDKConfig.AppRequestUrl; var rspData = AcpService.Post(param, url, System.Text.Encoding.UTF8); if (rspData.Count == 0) { return await Task.Run(() => { return new WebAPIResponse { IsError = true, Msg = "请求失败。<br>\n", Data = string.Empty }; }).ContinueWith(t => t.Result); } if (!AcpService.Validate(rspData, System.Text.Encoding.UTF8)) { return await Task.Run(() => { return new WebAPIResponse { IsError = true, Msg = "商户端验证返回报文签名失败。<br>\n", Data = string.Empty }; }).ContinueWith(t => t.Result); } string respcode = rspData["respCode"]; if (respcode != "00") { logger.Error("失败:" + rspData["respMsg"] + "。<br>\n"); return await Task.Run(() => { return new WebAPIResponse { IsError = true, Msg = "失败:" + rspData["respMsg"] + "。<br>\n", Data = string.Empty }; }).ContinueWith(t => t.Result); } return await Task.Run(() => { return new WebAPIResponse { IsError = false, Msg = "后续请将此tn传给手机开发,由他们用此tn调起控件后完成支付", Data = rspData["tn"] }; }).ContinueWith(t => t.Result); } catch (Exception ex) { logger.Fatal(ex, "GenerateTradeNoAsync Exception: " + ex.Message); return new WebAPIResponse { IsError = false, Msg = "GenerateTradeNoAsync Exception: " + ex.Message, Data = string.Empty }; } }
异步通知
/// <summary> /// 苹果支付 /// </summary> public class ApplePayController : Controller { private static readonly Logger logger = LogManager.GetCurrentClassLogger(); /// <summary> /// 支付服务 /// </summary> private readonly IPayPalService _payPalService; /// <summary> /// ApplePay支付服务 /// </summary> private readonly IApplePayService _applePayService; /// <summary> /// 构造函数 /// </summary> /// <param name="payPalService">整合支付服务</param> /// <param name="applePayService">ApplePay支付服务</param> public ApplePayController(IPayPalService payPalService, IApplePayService applePayService) { _payPalService = payPalService; _applePayService = applePayService; } /// <summary> /// ApplePay支付异步回调 /// </summary> /// <returns></returns> public async Task<ActionResult> Notify() { var param = new Dictionary<string, string>(); //param = @"accessType=0&bizType=000301&certId=69597475696¤cyCode=156&encoding=UTF-8&merId=301310048990295&orderId=000098393411&queryId=201512280008212498278&reqReserved=041601&respCode=00&respMsg=Success!&respTime=20151228000822&settleAmt=10400&settleCurrencyCode=156&settleDate=1228&traceNo=249827&traceTime=20151228000821&txnAmt=10400&txnSubType=01&txnTime=20151228000821&txnType=01&version=3.0.0&signature=cQHM+yW2G34Clkv5zm56XDnd8VhnC1aly5kmjfQ0mUyZb9mN79DJPvC5a98GXwPn7EBn7e1DUNs0J8Nr6FGIXc4A50QltH6njNsiSZMtIR6wlwPqqhga/AprJ4JulL2h6uv0ITPw9b5oHZx81oMpblzM+ZTamP8ZMWmH65ctrl4Qog+U09bXIGOEnezwHYG7Nz8/TumZeSch0TvR0S/vw61u6u45e81FmZ1oarE165QZ+jIfkiRnqI7/iGy4Xa2iSV7qCnNusTgMUg0JVZroBEfr5rA60+0FEzOFzLvae8yVLbsJ454hB4IyaltX34gQJAjpgBZ66dvJo2lGMCe1QQ==".ConvertStringToDictionary(); var item = Request.Form.AllKeys; for (int i = 0; i < item.Length; i++) { param.Add(item[i], Request.Form[item[i]]); } //判断是否有带返回参数 if (param.Count <= 0) { return Json(new { IsError = true, ErrorMsg = "NO DATA !!!", Data = string.Empty }, JsonRequestBehavior.AllowGet); } logger.Info("【 ApplePayController Notify SDKUtil.ConvertDictionaryToString : 请求报文=[" + param.ConvertDictionaryToString() + "]\n"); //验证签名 if (!AcpService.Validate(param, System.Text.Encoding.UTF8)) { logger.Error("ApplePayController Notify VERIFY FAIL SDKUtil.ConvertDictionaryToString : " + param.ConvertDictionaryToString()); return Json(new { IsError = true, ErrorMsg = "DATA VERIFY FAIL !!!", Data = string.Empty }, JsonRequestBehavior.AllowGet); } //验证数据 [重要!!!!] if (!param.ContainsKey("orderId") || !param.ContainsKey("respCode") || !param["respCode"].Equals("00")) { return Json(new { IsError = true, ErrorMsg = "DATA VERIFY FAIL !!!", Data = string.Empty }, JsonRequestBehavior.AllowGet); } //查询支付参数【确保来源是合并支付】 var payPal = _payPalService.Query(param["orderId"]); if (payPal.IsNull() || payPal.NotifyUrl.IsNullOrEmpty() || payPal.OutTradeNO.IsNullOrEmpty()) { logger.Error("ApplePayController Notify NOT FIND ORDER SDKUtil.ConvertDictionaryToString : " + param.ConvertDictionaryToString()); return Json(new { IsError = true, ErrorMsg = "NOT FIND ORDER !!!", Data = string.Empty }, JsonRequestBehavior.AllowGet); } //记录日志 payPal.TradeNO = param["queryId"]; //支付金额分转换元 payPal.TotalFee = (Convert.ToInt32(param["txnAmt"]) / 100M).ToStringSafe(); payPal.Service = payPal.Service + "_Notify"; payPal.CreateTime = DateTime.Now; payPal.Memo = param.ConvertDictionaryToJson(); payPal.Resv = param["reqReserved"]; payPal.Memo = param.ConvertDictionaryToJson(); bool flag = _payPalService.Save(payPal); if (!flag) { logger.Error("ApplePayController Notify SAVE PayPalData FAIL SDKUtil.ConvertDictionaryToString : " + param.ConvertDictionaryToString()); return Json("DATA SAVE PayPalData FAIL !!!", JsonRequestBehavior.AllowGet); } try { //调用业务接口 logger.Info("ApplePayController PostJsonAsync req: " + payPal.SerializeJson(System.Text.Encoding.UTF8)); var data = await payPal.NotifyUrl.PostJsonAsync(payPal).ReceiveJson<WebAPIResponse>(); logger.Info("ApplePayController PostJsonAsync resp: " + data.SerializeJson(System.Text.Encoding.UTF8) + " 】"); if (data.IsError) { logger.Fatal("!!! ApplePayController PostJsonAsync : " + payPal.SerializeJson(System.Text.Encoding.UTF8)); return Json("DATA PostJsonAsync FAIL FAIL !!!", JsonRequestBehavior.AllowGet); } } catch (Exception ex) { logger.Fatal(ex, "ApplePayController PostJsonAsync Exception : " + ex.Message + " payPal : " + payPal.SerializeJson(System.Text.Encoding.UTF8)); return Content("exception: " + ex.Message); } return await Task.Run(() => { return Content("success"); }).ContinueWith(t => t.Result); } }
交易状态查询
/// <summary> /// 交易状态查询 /// </summary> /// <param name="orderNo">订单号</param> /// <returns></returns> public async Task<WebAPIResponse> QueryTradeStatusAsync(string orderNo) { try { var param = new Dictionary<string, string>(); //版本号 param["version"] = "5.0.0"; //编码方式 param["encoding"] = "UTF-8"; //证书ID param["certId"] = CertUtil.GetSignCertId(); //签名方法 param["signMethod"] = "01"; //交易类型 param["txnType"] = "00"; //交易子类 param["txnSubType"] = "00"; //业务类型 param["bizType"] = "000000"; //接入类型 param["accessType"] = "0"; //渠道类型 param["channelType"] = "07"; //商户号 param["merId"] = SDKConfig.MerId; //商户订单号,8-32位数字字母,不能含“-”或“_” param["orderId"] = orderNo; //订单发送时间,参考取法: DateTime.Now.ToString("yyyyMMddHHmmss") param["txnTime"] = DateTime.Now.ToString("yyyyMMddHHmmss"); // 签名 AcpService.Sign(param, System.Text.Encoding.UTF8); string url = SDKConfig.SingleQueryUrl; var rspData = AcpService.Post(param, url, System.Text.Encoding.UTF8); if (rspData.Count == 0) { return await Task.Run(() => { return new WebAPIResponse { IsError = true, Msg = "请求失败。<br>\n", Data = string.Empty }; }).ContinueWith(t => t.Result); } if (!AcpService.Validate(rspData, System.Text.Encoding.UTF8)) { return await Task.Run(() => { return new WebAPIResponse { IsError = true, Msg = "商户端验证返回报文签名失败。<br>\n", Data = string.Empty }; }).ContinueWith(t => t.Result); } string respcode = rspData["respCode"]; if (respcode != "00") { logger.Error("失败:" + rspData["respMsg"] + "。<br>\n"); return await Task.Run(() => { return new WebAPIResponse { IsError = true, Msg = "失败:" + rspData["respMsg"] + "。<br>\n", Data = string.Empty }; }).ContinueWith(t => t.Result); } string origRespCode = rspData["origRespCode"]; if (origRespCode != "00") { logger.Error("稍后查询:" + rspData["origRespMsg"] + "。<br>\n"); return await Task.Run(() => { return new WebAPIResponse { IsError = true, Msg = "稍后查询:" + rspData["origRespMsg"] + "。<br>\n", Data = string.Empty }; }).ContinueWith(t => t.Result); } return await Task.Run(() => { return new WebAPIResponse { IsError = false, Msg = "交易成功!!!", Data = string.Empty }; }).ContinueWith(t => t.Result); } catch (Exception ex) { logger.Fatal(ex, "QueryTradeStatusAsync Exception: " + ex.Message); return new WebAPIResponse { IsError = true, Msg = "QueryTradeStatusAsync Exception: " + ex.Message, Data = string.Empty }; } }
退货
/// <summary> /// 退货交易 /// </summary> /// <param name="orderNo">订单号</param> /// <param name="origQryId">原消费的queryId</param> /// <param name="txnAmt">交易金额</param> /// <returns></returns> public async Task<WebAPIResponse> RefundTradeNoAsync(string orderNo, string origQryId, string txnAmt) { try { var param = new Dictionary<string, string>(); //商户号 param["merId"] = SDKConfig.MerId; //版本号 param["version"] = "5.0.0"; //编码方式 param["encoding"] = "UTF-8"; //签名方法 param["signMethod"] = "01"; //交易类型 param["txnType"] = "04"; //交易子类 param["txnSubType"] = "00"; //业务类型 param["bizType"] = "000201"; //接入类型 param["accessType"] = "0"; //渠道类型 param["channelType"] = "07"; //后台通知地址 //param["backUrl"] = SDKConfig.BackUrl; //商户订单号,8-32位数字字母,不能含“-”或“_” param["orderId"] = orderNo; //原消费的queryId,可以从查询接口或者通知接口中获取 param["origQryId"] = origQryId; //订单发送时间 param["txnTime"] = DateTime.Now.ToString("yyyyMMddHHmmss"); //交易金额,退货总金额需要小于等于原消费 param["txnAmt"] = txnAmt; //请求方保留域,透传字段,查询、通知、对账文件中均会原样出现,如有需要请启用并修改自己希望透传的数据 //param["reqReserved"] = "透传信息"; // 签名 AcpService.Sign(param, System.Text.Encoding.UTF8); string url = SDKConfig.BackTransUrl; var rspData = AcpService.Post(param, url, System.Text.Encoding.UTF8); if (rspData.Count == 0) { return await Task.Run(() => { return new WebAPIResponse { IsError = true, Msg = "请求失败。<br>\n", Data = string.Empty }; }).ContinueWith(t => t.Result); } if (!AcpService.Validate(rspData, System.Text.Encoding.UTF8)) { return await Task.Run(() => { return new WebAPIResponse { IsError = true, Msg = "商户端验证返回报文签名失败。<br>\n", Data = string.Empty }; }).ContinueWith(t => t.Result); } string respcode = rspData["respCode"]; if (respcode != "00") { logger.Error("失败:" + rspData["respMsg"] + "。<br>\n"); return await Task.Run(() => { return new WebAPIResponse { IsError = true, Msg = "失败:" + rspData["respMsg"] + "。<br>\n", Data = string.Empty }; }).ContinueWith(t => t.Result); } return await Task.Run(() => { return new WebAPIResponse { IsError = false, Msg = "订单号:" + orderNo + " 退货受理成功。<br>\n", Data = string.Empty }; }).ContinueWith(t => t.Result); } catch (Exception ex) { logger.Fatal(ex, "RefundTradeNoAsync Exception: " + ex.Message); return new WebAPIResponse { IsError = true, Msg = "RefundTradeNoAsync Exception: " + ex.Message, Data = string.Empty }; } }