已经好久没有搞过.NET了,朋友有一个网站 ,需要接入支付宝功能,重新对接了一下。

1、开发前,需要一个企业支付宝账号登录,获取 PID和 配置密钥

官方文档:https://docs.open.alipay.com/62/104743/

2、下载官方的服务端代码

官方下载地址:https://docs.open.alipay.com/270/106291/

这里要注意一下,官方的服务端代码,是没有工程文件的,需要在vs2013中,打开-网站,加载整个文件。配置pid和ras密钥,是可以跑起来的,效果还不错,支付和退款的功能都有了。

3、官方给过来的是webform的代码,改成MVC也很简单,代码如下

支付和回调通知:

namespace YinCai.Web.Controllers
{
public class AlipayController : BaseController
{
//
// GET: /Alipay/
public ActionResult Pay(int Id)
{
ScOrder orderModel = BLLSession.IScOrderService.GetOne(m => m.OrderID == Id); DefaultAopClient client = new DefaultAopClient(AlipayConfig.gatewayUrl, AlipayConfig.app_id, AlipayConfig.private_key, "json", "1.0", AlipayConfig.sign_type, AlipayConfig.alipay_public_key, AlipayConfig.charset, false); // 外部订单号,商户网站订单系统中唯一的订单号
string out_trade_no = orderModel.OrderNo.ToString(); // 订单名称
string subject = orderModel.OrderName; // 付款金额
string total_amout = orderModel.PayPrice.ToString(); // 商品描述
string body = ""; // 组装业务参数model
AlipayTradePagePayModel model = new AlipayTradePagePayModel();
model.Body = body;
model.Subject = subject;
model.TotalAmount = total_amout;
model.OutTradeNo = out_trade_no;
model.ProductCode = "FAST_INSTANT_TRADE_PAY"; AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
// 设置同步回调地址
request.SetReturnUrl(Common.ConfigHelper.AlipayReturnUrl);
// 设置异步通知接收地址
request.SetNotifyUrl(Common.ConfigHelper.AlipayNotifyUrl);
// 将业务model载入到request
request.SetBizModel(model); AlipayTradePagePayResponse response = null;
try
{
response = client.pageExecute(request, null, "post");
string rep = response.Body.Replace("<input", "<input type='hidden'");
return Content(rep);
}
catch (Exception exp)
{
Common.LogHelper.WriteLog(exp, "支付宝跳转失败!");
throw exp;
}
} // 支付宝异步通知
// gmt_create=2019-09-01+14%3a12%3a35&charset=UTF-8&gmt_payment=2019-09-01+14%3a12%3a38&notify_time=2019-09-01+14%3a12%3a39&subject=1&sign=XNAQMiLoXKxUoZEfiSkNxmiBJDnw5xszNiFuu20XQzmnLbwuwDXVFsaYaR8OtV6qD%2bsymeZjSfm0%2fvAY1q7V2QiyTtMqnvBG23nbeklLxGvY2GKOKnULI8M7Wl9dJs70bbMsENhEY1ATOLpqven2dcJJkLIwIHec%2fKgU4EdrEbR7nNaa89m3iDkO6wd3uKyrBvhfE208kPbJc62AGPa46V1HvzmtL66kbgPVBG9K7yI5En%2ba1y6fMs%2fmOJecrNt%2bozXcw0Bkw2P47a9A5drkH1wakkiK3a4SOsJekb7mr1jtphSLIaq5SoImwZsFLtnM6Hjrl8lhUTjl9jQrwPAFzw%3d%3d&buyer_id=2088302047105765&invoice_amount=0.01&version=1.0&notify_id=2019090100222141238005760509611566&fund_bill_list=%5b%7b%22amount%22%3a%220.01%22%2c%22fundChannel%22%3a%22ALIPAYACCOUNT%22%7d%5d&notify_type=trade_status_sync&out_trade_no=2019090110003&total_amount=0.01&trade_status=TRADE_SUCCESS&trade_no=2019090122001405760595489512&auth_app_id=2019082266394313&receipt_amount=0.01&point_amount=0.00&buyer_pay_amount=0.01&app_id=2019082266394313&sign_type=RSA2&seller_id=2088631096812893
public ActionResult Notify()
{
Common.LogHelper.WriteInfoLog("支付宝回调通知---" + Request.Form.ToString());
/* 实际验证过程建议商户添加以下校验。
1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号,
2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额),
3、校验通知中的seller_id(或者seller_email) 是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email)
4、验证app_id是否为该商户本身。
*/
long orderno = long.Parse(Request["out_trade_no"]);
string appId = Request["app_id"];
//string seller_id = Request.Form["seller_id"];
string totalAmount = Request["total_amount"];
ScOrder orderModel = BLLSession.IScOrderService.GetOne(m => m.OrderNo == orderno);
if (orderModel == null || appId != AlipayConfig.app_id || orderModel.PayPrice != decimal.Parse(totalAmount))
{
Common.LogHelper.WriteLog("支付宝回调信息异常" + Request.Form.ToString());
return Content("fail"); ;
} Dictionary<string, string> sArray = GetRequestPost();
if (sArray.Count != )
{
bool flag = AlipaySignature.RSACheckV1(sArray, AlipayConfig.alipay_public_key, AlipayConfig.charset, AlipayConfig.sign_type, false);
if (flag)
{
//交易状态
//判断该笔订单是否在商户网站中已经做过处理
//如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序
//请务必判断请求时的total_amount与通知时获取的total_fee为一致的
//如果有做过处理,不执行商户的业务程序 //注意:
//退款日期超过可退款期限后(如三个月可退款),支付宝系统发送该交易状态通知
string trade_status = Request["trade_status"];
orderModel.PayNotifyTime = DateTime.Now;
BLLSession.IScOrderService.ModifyModel(orderModel); Common.LogHelper.WriteInfoLog("支付宝验签成功,支付成功---" + orderno);
return Content("success");
}
else
{
//Response.Write("fail");
Common.LogHelper.WriteLog("支付宝回调通知验签失败---" + Request.Form.ToString());
return Content("fail");
}
}
return Content("fail");
} // 支付宝同步通知,支付成功页 ReturnUrl
// /alipay/PaySuccess/2019090110021?charset=UTF-8&out_trade_no=2019090110021&method=alipay.trade.page.pay.return&total_amount=0.01&sign=fUsyiAQiFSpHOmBn%2Bd0QZbdfdGyXtfIHYeZziKIpJGJpa38L7Jx1C4jnaCRuYuqTnMvawwg0ExqXpT6T88BPTdjK5Rcm9tL6zL8U8N%2B7lHEMyPMT2LaiuFAGzmnlSZyVJleGdUEzgvQZ3jbX9OdZEpLFOVyRnj8Ax%2B%2BlW5M9oInyncCg6VBw2%2BbDG5m8epPyBFUIBWxQINcyKrCDbDFwVIxGjji%2FW11adC7il8earoOB4gXKArxVB7SVYZcHXjiONZJEXyRMMuscD4ZV%2FPwH3Wtf5BUv%2BHtHqJWr8a5u9ijZCI7GzZwUdDYKnOamqp7HMFychsrUgAAcVPfdjAxe%2Bg%3D%3D&trade_no=2019090122001405760597459839&auth_app_id=2019082266394313&version=1.0&app_id=2019082266394313&sign_type=RSA2&seller_id=2088631096812893&timestamp=2019-09-01+18%3A18%3A20
public ActionResult PaySuccess()
{
Common.LogHelper.WriteLog("支付宝同步验证参数----" + Request.RawUrl);
long orderno = long.Parse(Request["out_trade_no"]);
string appId = Request["app_id"];
string totalAmount = Request["total_amount"];
ScOrder orderModel = BLLSession.IScOrderService.GetOne(m => m.OrderNo == orderno);
if (orderModel == null || appId != AlipayConfig.app_id || orderModel.PayPrice != decimal.Parse(totalAmount))
{
Common.LogHelper.WriteLog("支付宝同步验证异常----" + Request.RawUrl);
return RedirectToAction("payfail","alipay");
} /* 实际验证过程建议商户添加以下校验。
1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号,
2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额),
3、校验通知中的seller_id(或者seller_email) 是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email)
4、验证app_id是否为该商户本身。
*/
Dictionary<string, string> sArray = GetRequestGet();
if (sArray.Count != )
{
bool flag = AlipaySignature.RSACheckV1(sArray, AlipayConfig.alipay_public_key, AlipayConfig.charset, AlipayConfig.sign_type, false);
if (flag)
{
string trade_no = Request["trade_no"];
orderModel.OrderStatus = (int)OrderStateEnum.Paid;
orderModel.PayType = (int)PayTypeEnum.Alipay;
orderModel.PayTime = DateTime.Now;
orderModel.PayTradeNo = trade_no;
BLLSession.IScOrderService.ModifyModel(orderModel);
}
else
{
Common.LogHelper.WriteLog("支付宝同步验证失败----" + Request.RawUrl);
return RedirectToAction("payfail", "alipay");
}
} return View(orderModel);
} public ActionResult PayFail()
{
return View();
} #region --参数获取组装
// get 方式组装回调参数
public Dictionary<string, string> GetRequestGet()
{
int i = ;
Dictionary<string, string> sArray = new Dictionary<string, string>();
NameValueCollection coll;
//coll = Request.Form;
coll = Request.QueryString;
String[] requestItem = coll.AllKeys;
for (i = ; i < requestItem.Length; i++)
{
sArray.Add(requestItem[i], Request.QueryString[requestItem[i]]);
}
return sArray;
} // post 方式组装回调参数
public Dictionary<string, string> GetRequestPost()
{
int i = ;
Dictionary<string, string> sArray = new Dictionary<string, string>();
NameValueCollection coll;
//coll = Request.Form;
coll = Request.Form;
String[] requestItem = coll.AllKeys;
for (i = ; i < requestItem.Length; i++)
{
sArray.Add(requestItem[i], Request.Form[requestItem[i]]);
}
return sArray;
}
#endregion
}
}

退款:

        // 退款成功返回
//{"alipay_trade_refund_response":{"code":"10000","msg":"Success","buyer_logon_id":"jys***@163.com","buyer_user_id":"2088302047105765","fund_change":"Y","gmt_refund_pay":"2019-09-01 16:08:02","out_trade_no":"2019090110004","refund_fee":"0.01","send_back_fee":"0.00","trade_no":"2019090122001405760597435123"},"sign":"IL+00djGKL/7UBxQS9oTPwKySlzHrnZLtzsqbH1ZkyAgv8E2Hl+PZZtbVAnH7+7XtpfLDxwY+4EyN0EmL75reU3fiBmpmL9JROsCQy2hQG4WmeSB2GKg2kFyAgmOh7uoYbKW/HWV35NfxdpoCZNdGyYWWhxIz0qui2xIH3u46WPBN5Yk5uMmhNNzUP8pW18XxssI7htqD3ey7qagntwPFTlL5JeUmz8Fbusk+xQJ3p6wj58IHetm2Mio1KDSEZblE/2+T7ZkgrBrQqw77Y4DpCi8m98MFMdAMCQ/3qiWH65H4cPPXXrQ2pprVLA6m8DjmPhzsuYk7noyHYJo4uMFwg=="}
[HttpPost]
public ActionResult Refund(int Id)
{
ScOrder orderModel = BLLSession.IScOrderService.GetOne(m=>m.OrderID == Id);
if (orderModel == null)
{
return JsonMsgErr("退款出错拉");
} DefaultAopClient client = new DefaultAopClient(AlipayConfig.gatewayUrl, AlipayConfig.app_id, AlipayConfig.private_key, "json", "1.0", AlipayConfig.sign_type, AlipayConfig.alipay_public_key, AlipayConfig.charset, false); // 商户订单号,和交易号不能同时为空
string out_trade_no = orderModel.OrderNo.ToString(); // 支付宝交易号,和商户订单号不能同时为空
string trade_no = orderModel.PayTradeNo; // 退款金额,不能大于订单总金额
string refund_amount = orderModel.PayPrice.ToString(); // 退款原因
string refund_reason = "正常退款"; // 退款单号,同一笔多次退款需要保证唯一,部分退款该参数必填。
string out_request_no = orderModel.OrderNo.ToString(); AlipayTradeRefundModel model = new AlipayTradeRefundModel();
model.OutTradeNo = out_trade_no;
model.TradeNo = trade_no;
model.RefundAmount = refund_amount;
model.RefundReason = refund_reason;
model.OutRequestNo = out_request_no; AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
request.SetBizModel(model); AlipayTradeRefundResponse response = null;
try
{
response = client.Execute(request);
Common.LogHelper.WriteInfoLog("支付宝退款成功---" + response.Body); RefundModel refundModel = JsonConvert.DeserializeObject<RefundModel>(response.Body);
string code = refundModel.alipay_trade_refund_response.code;
// 可以重复退款,所以不需要判断fund_change
string fund_change = refundModel.alipay_trade_refund_response.fund_change;
if (code == "")
{
orderModel.RefundTime = DateTime.Now;
orderModel.RefundReason = "正常退款";
orderModel.OrderStatus = (int)OrderStateEnum.Refund;
BLLSession.IScOrderService.ModifyModel(orderModel);
return JsonMsgOk("退款成功");
}
}
catch (Exception exp)
{
throw exp;
} return JsonMsgOk("退款失败");
}

交易状态问题解析

那既然退款的回调地址和支付时的回调地址是一致的,我如何区分一次回调是支付还是退款呢?退款的异步通知参数中一定会返回out_biz_no(商户业务号)、refund_fee(总退款金额)、gmt_refund(交易退款时间)。

一、交易创建的时间
不同的支付接口交易的创建时间也不同,以下是详细的支付接口创建时间:
   1、当面付
    条码支付接口(alipay.trade.pay(统一收单交易支付接口)):商家扫用户支付条码,接口调用成功后创建交易
    扫码支付接口(alipay.trade.precreate(统一收单线下交易预创建)):用户扫商家收款二维码,唤起收银台成功后,创建交易
   统一收单交易创建接口(alipay.trade.create):调用接口成功后订单创建;

2、APP支付接口(alipay.trade.app.pay):
支付宝钱包支付:用户点击支付,唤起支付宝收银台后,输入正确完整的支付密码后创建交易; 
H5页面登录支付:用户点击支付,输入账户与密码登录成功后,创建交易;

3、手机网站支付接口(alipay.trade.wap.pay):

支付宝钱包支付:用户点击支付,唤起支付宝收银台后,输入正确完整的支付密码后创建交易; 
H5页面登录支付:用户点击支付,输入账户与密码登录成功后,创建交易;

4、电脑网站支付接口(alipay.trade.page.pay):
支付宝钱包扫码支付:电脑网站生成二维码,用户使用支付宝钱包扫码唤起收银台后创建交易;
 PC端登录支付:用户成功输入登录密码成功登录后创建交易;

注:请求参数中传入订单超时时间timeout_express 参数,这个时间一般是在交易创建成功后才开始计时的,但是电脑网站支付接口如果传入了订单超时时间,是从生成二维码展示页面开始计时的不是按照交易创建的时间开始计算的

二、交易状态的迁移
 交易状态迁移图:

1、交易创建成功后,用户支付成功,交易状态转为TRADE_SUCCESS(交易成功)
 2、交易创建成功后,用户未付款交易超时关闭交易状态转为TRADE_CLOSED(交易关闭)
 3、交易成功后,交易全额退款交易状态转为TRADE_CLOSED(交易关闭)
 4、交易创建成功后,用户支付成功后,若用户商品不支持退款,交易状态直接转为TRADE_FINISHED(交易完成)
 5、交易成功后,默认退款时间三个月内没有退款,交易状态转为TRADE_FINISHED(交易完成)不可退款

注:交易成功后部分退款,交易状态仍为TRADE_SUCCESS(交易成功),如果一直部分退款退完所有交易金额则交易状态转为TRADE_CLOSED(交易关闭),如果未退完所有交易金额,三个月后交易状态转为TRADE_FINISHED(交易完成)不可退款

三、交易状态与异步通知
异步通知的触发是依据交易的状态来触发的,不同的支付接口触发条件也不相同,新版本的支付接口触发条件如下:
 1、当面付的支付接口
    默认TRADE_SUCCESS(交易成功)触发,其余交易状态均不触发异步通知

2、APP支付接口
    默认TRADE_SUCCESS(交易成功),TRADE_CLOSED(交易关闭),TRADE_FINISHED(交易完成)三种状态均会触发异步通知,WAIT_BUYER_PAY(交易创建)不触发异步通知

3、手机网站支付接口
    默认TRADE_SUCCESS(交易成功),TRADE_CLOSED(交易关闭)两种状态触发异步通知,WAIT_BUYER_PAY(交易创建)和TRADE_FINISHED(交易完成)不触发异步通知

4、电脑网站支付接口
默认TRADE_SUCCESS(交易成功)状态触发异步通知,TRADE_CLOSED(交易关闭),TRADE_FINISHED(交易完成),WAIT_BUYER_PAY(交易创建)不触发异步通知

5、alipay.trade.refund(交易退款接口)异步触发
 退款的异步通知是依据支付接口的触发条件来触发的,异步通知也是发送到支付接口传入的异步地址上。
  (1)部分退款:部分退款交易状态是处于TRADE_SUCCESS(交易成功),此时因部分退款导致交易金额变动,会触发异步通知
  (2)全额退款:交易成功后全额退款,交易状态会转为TRADE_CLOSED(交易关闭),此时根据不同的支付接口触发条件也不同,例如APP支付接口TRADE_CLOSED(交易关闭)状态触发异步,此时就会收到全额退款的异步通知。而电脑网站支付TRADE_CLOSED(交易关闭)状态不会触发异步,就不会有全额退款的异步通知

注:退款的异步信息和正常支付的异步信息的返回信息是不一样的,退款的信息中会有refund_fee 退款总金额参数,在正常支付成功的异步信息中是没有的

四、与交易状态有关的常见的报错
 1、Q:调用交易查询或是关闭接口报错:交易不存在
    A:这个报错一般就是交易没有创建所以调用查询接口报错:交易不存在,建议参考本帖第一点排查支付接口是否成功创建订单

2、Q:支付成功后多次收到异步通知,但是已经成功返回了success,还是重复通知
    A:支付成功多次触发异步建议参考第三点是否是其他交易状态触发的,所有交易状态触发的异步通知都必须返回success才不会多次通知

3、Q:支付接口报错:交易买家不匹配
    A:一般这个报错是用户使用自己的账号支付创建了订单,以电脑网站支付为例:用户扫码成功交易创建,但是未支付,在订单超时时间内,商户使用相同的外部订单号再次生成二维码,由另一位用户扫码支付就会出现这个报错,即该订单已与第一位扫码支付的用户绑定,只能有该商户发起支付,出现这个报错商户更改外部订单号重新创建交易即可

交易状态参考:https://openclub.alipay.com/club/history/read/5407

支付成功回调与退款回调区别:https://www.cnblogs.com/volcano-liu/p/10000046.html

.NET MVC 支付宝支付(即时到账)的更多相关文章

  1. 支付宝PC即时到账和手机网站支付同步

    前几个月做了一个旅游网站,有PC站和手机站,涉及支付宝支付功能. 要求:PC站下的单,用户用手机登录也能支付;同理,手机站下的单,PC端登录也能支付. 附支付宝开放平台网址:即时到账 ,手机网站支付. ...

  2. java 支付宝 第三方即时到账支付 接口

    alipay 的几个内核功能文件:=================================================================================== ...

  3. ***CodeIgniter框架集成支付宝即时到账支付SDK

    本文为CI集成支付宝即时到账支付接口 1.下载支付宝官方demo ;即时到账交易接口(create_direct_pay_by_user)(DEMO下载) 原文地址:https://doc.open. ...

  4. 支付宝即时到账DEMO配置与使用

    支付宝网页即时到账功能,可让用户在线向开发者的支付宝账号支付资金,交易资金即时到账,帮助开发者快速回笼资金. 当用户进行支付操作时候可以直接跳转到支付宝支付页面进行支付 1. 准备 关于支付宝签约即时 ...

  5. 支付宝即时到账接口开发 - DEMO讲解

    支付宝即时到帐接口 环境要求 PHP5.0以上,且需要开启curl.openssl. 文档地址: https://doc.open.alipay.com/doc2/detail?treeId=62&a ...

  6. XorPay 个人支付平台增加 个人支付宝支付接口

      XorPay 今天新增 个人支付宝当面付 接口,欢迎大家使用. 「 XorPay 支付平台」 已经同时支持 个人微信支付接口 和 个人支付宝接口. 个人可用的 支付宝/微信支付 接口,支持 当面付 ...

  7. 实战 Spring MVC接入支付宝即时到账 (部分代码)

    下面就拿我项目中的部分代码来实践一下. 支付请求 首先,是提交表单 fund.jsp(这里我表单只需要用户填交易金额,其他的订单号之类的全部后台生成) <form id="deposi ...

  8. ThinkPHP 3.2 支付宝即时到账接口开发

    前言: 一.支付流程 构造请求参数 向支付宝网关发送请求 生成支付宝页面 支付宝交易结果 二.构建支付类 1.官方即时到账文档地址: https://doc.open.alipay.com/doc2/ ...

  9. PHP实现支付宝即时到账功能

    本文实例为大家分享了PHP支付宝即时到账功能的实现代码,供大家参考,具体内容如下 首先需要下载即时到账交易接口,传送门https://doc.open.alipay.com/doc2/detail?t ...

随机推荐

  1. Mac下多版本pip共存

    Mac下多版本pip共存 来自于官方的解释, pip是python包管理工具, 该工具提供了对python包的查找, 下载, 安装, 卸载等功能python第三方工具包多数依赖于pip进行安装, 如 ...

  2. 目标检测的评价标准mAP, Precision, Recall, Accuracy

    目录 metrics 评价方法 TP , FP , TN , FN 概念 计算流程 Accuracy , Precision ,Recall Average Precision PR曲线 AP计算 A ...

  3. 维护带修改区间 K 小值

    就是在原来的主席树模板上加上一条将x上的值修改为k 待我们仔细想想之前静态的区间Kth怎么实现的... 我们仍然需要维护前缀和,而只是在以前的代码里面加上单点修改的操作,那么你要每次要修改的前缀和就有 ...

  4. luoguP1195 口袋的天空

    生成树一 题目描述 给你云朵的个数NN,再给你MM个关系,表示哪些云朵可以连在一起. 现在小杉要把所有云朵连成KK个棉花糖,一个棉花糖最少要用掉一朵云,小杉想知道他怎么连,花费的代价最小. 链接 分析 ...

  5. DNS分离解析

    实验环境: 一台内网(client)1块网卡:一台网关(dns)2块网卡:一台外网1块网卡 DNS服务器开启路由转发 [root@localhost ~]# vi /etc/sysctl.conf n ...

  6. JWT(Json Web Token):一种在Web应用中安全传递信息的规范 转载

    文本将介绍一种在Web应用中安全传递信息的方式,称为JWT. 本文内容是对JWT官网介绍说明的英文翻译而来,由于本文英文水平有限,如有错误,还请指出,谢谢. What is JSON Web Toke ...

  7. jQuery的DataTables中的TableTools的基本使用

    DataTables的TableTools插件提供复制,导出excel.pdf,打印等功能. DataTables官网:http://datatables.net TableTools示例:http: ...

  8. it's over | 2019 CSP-S 第一轮认证

    不知道自己有没有凉,毕竟我们省这么弱(据说有的省80都悬... 其实这几天对初赛基本没什么感觉,可能是没给自己多大压力吧,倒是班上的一群同学似乎比我们还着急,我们的数学课代表兼数竞大佬特意给我画了吉祥 ...

  9. UDF——读取文件作为边界条件(类似profile的效果)

    本文编译工具:VC++ UDF Studio 该插件可以直接在Visual Studio中一键编译.加载.调试UDF源码,极大提高编写排错效率,且支持C++,MFC,Windows API和第三方库, ...

  10. Mac上Hive安装配置

    Mac上Hive安装配置 1.安装 下载hive,地址:http://mirror.bit.edu.cn/apache/hive/ 之前我配置了集群,tjt01.tjt02.tjt03,这里hive安 ...