OrderService.cs 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885
  1. using Ant.Core.Utils;
  2. using Ant.Core.WebApi.Enum;
  3. using Ant.Core.WebApi.Model;
  4. using Aop.Api.Util;
  5. using Central.Control.WebApi.Config;
  6. using Central.Control.WebApi.DbEntity;
  7. using Central.Control.WebApi.EFDbContext;
  8. using Central.Control.WebApi.Enum;
  9. using Central.Control.WebApi.Log4net;
  10. using Central.Control.WebApi.Models.Request;
  11. using Central.Control.WebApi.Models.Response;
  12. using Central.Control.WebApi.Service.Interface;
  13. using Com.Alipay;
  14. using Com.Alipay.Business;
  15. using Com.Alipay.Domain;
  16. using Com.Alipay.Model;
  17. using F2FPayDll.Domain;
  18. using Newtonsoft.Json;
  19. using System;
  20. using System.Collections.Generic;
  21. using System.Collections.Specialized;
  22. using System.Configuration;
  23. using System.IO;
  24. using System.Linq;
  25. using System.Text;
  26. using System.Web;
  27. namespace Central.Control.WebApi.Service
  28. {
  29. /// <summary>
  30. ///
  31. /// </summary>
  32. public class OrderService: IOrderService
  33. {
  34. IAlipayTradeService serviceClient = F2FBiz.CreateClientInstance(AliPayConfig.serverUrl, AliPayConfig.appId, AliPayConfig.merchant_private_key, AliPayConfig.version,
  35. AliPayConfig.sign_type, AliPayConfig.alipay_public_key, AliPayConfig.charset);
  36. /// <summary>
  37. /// 支付宝回写地址
  38. /// </summary>
  39. public readonly static string aliNotifyUrl = ConfigurationManager.AppSettings["aliNotifyUrl"].ToString();
  40. /// <summary>
  41. /// 支付中间页地址
  42. /// </summary>
  43. public readonly static string payRedirectUrl = ConfigurationManager.AppSettings["payredirecturl"].ToString();
  44. private readonly IDbContext _dbContent;
  45. private readonly IUserService _userService;
  46. private readonly ILog4NetHelper _log4NetHelper;
  47. private readonly IYWLogService _ywLogService;
  48. /// <summary>
  49. ///
  50. /// </summary>
  51. /// <param name="dbContent"></param>
  52. /// <param name="userService"></param>
  53. /// <param name="log4NetHelper"></param>
  54. /// <param name="ywLogService"></param>
  55. public OrderService(
  56. IDbContext dbContent,
  57. IUserService userService,
  58. ILog4NetHelper log4NetHelper,
  59. IYWLogService ywLogService)
  60. {
  61. _dbContent = dbContent;
  62. _userService = userService;
  63. _log4NetHelper = log4NetHelper;
  64. _ywLogService = ywLogService;
  65. }
  66. /// <summary>
  67. /// 下单
  68. /// </summary>
  69. /// <param name="req"></param>
  70. /// <returns></returns>
  71. public ApiResult<OrderResponseDto> Order(OrderRequestDto req)
  72. {
  73. #region 1、验证参数
  74. if (req == null
  75. || string.IsNullOrWhiteSpace(req.ProductId)
  76. || req.Count <= 0)
  77. {
  78. return new ApiResult<OrderResponseDto>(ApiStatusCode.InvalidParameter, "请求参数不正确");
  79. }
  80. #endregion
  81. #region 2、验证相应数据和库存
  82. var productInfo = (from p in _dbContent.Set<YW_Product>().Where(p => p.IsDelete == 0 && p.Sale == Enum.SaleEnum.On)
  83. join dp in _dbContent.Set<YW_DeviceProduct>().Where(p => p.IsDelete == 0) on p.Id equals dp.ProductId
  84. where p.Id == req.ProductId
  85. select new {
  86. Product = p,
  87. DeviceProduct = dp
  88. }).FirstOrDefault();
  89. if (productInfo == null)
  90. {
  91. return new ApiResult<OrderResponseDto>(ApiStatusCode.RecordNotFound, "未找到下单的商品");
  92. }
  93. #endregion
  94. #region 3、包装盒库存验证
  95. var session = _userService.GetLoginSession();
  96. // 修改
  97. var devicePacking = _dbContent.Set<YW_DevicePacking>().FirstOrDefault(p => p.IsDelete == 0 && p.DeviceId == session.UserId);
  98. if (devicePacking == null)
  99. {
  100. return new ApiResult<OrderResponseDto>(ApiStatusCode.Forbidden, "当前机器未放置或者未设置打包盒包,请联系工作人员添加包装盒");
  101. }
  102. // int orderCount = req.OrderInfo.Sum(p => p.Count);
  103. int packingCount = devicePacking.Stock * devicePacking.Capacity;
  104. if (packingCount < req.Count)
  105. {
  106. return new ApiResult<OrderResponseDto>(ApiStatusCode.Forbidden, "包装盒库存不足,请联系工作人员添加包装盒");
  107. }
  108. // 计算使用的包装盒数量(减包装盒库存)
  109. var exceptStock = req.Count / devicePacking.Capacity;
  110. var yushu = req.Count % devicePacking.Capacity;
  111. if (yushu > 0)
  112. {
  113. exceptStock++;
  114. }
  115. // 扣库存
  116. devicePacking.Stock -= exceptStock;
  117. #endregion
  118. #region 4、商品库存验证
  119. var orderId = IdGenerator.NewId();
  120. if (productInfo.DeviceProduct.Stock < req.Count)
  121. {
  122. return new ApiResult<OrderResponseDto>(ApiStatusCode.Forbidden, $"{productInfo.Product.Name}库存不足,请修改后重新下单");
  123. }
  124. // 扣库存
  125. var deviceProduct = productInfo.DeviceProduct;
  126. deviceProduct.Stock -= req.Count;
  127. #endregion
  128. #region 5、下单
  129. YW_Order order = new YW_Order()
  130. {
  131. Id = orderId,
  132. Name = productInfo.Product.Name,
  133. DeviceId = session.UserId,
  134. Price = deviceProduct.Price * req.Count,
  135. PayStatus = PayStatusEnum.UnPay,
  136. OrderStatus = OrderStatusEnum.UnPay,
  137. // PaySerialId = Snowflake.Instance().GetId().ToString(), // 支付流水号,支付完成会回写
  138. CreateBY = session.UserId
  139. };
  140. // orderDetails
  141. var orderDetails = new YW_OrderDetails()
  142. {
  143. Id = IdGenerator.NewId(),
  144. OrderId = orderId,
  145. ProductId = productInfo.Product.Id,
  146. Name = productInfo.Product.Name,
  147. Code = productInfo.Product.Code,
  148. Img = productInfo.Product.Img,
  149. Info = productInfo.Product.Info,
  150. Price = productInfo.DeviceProduct.Price,
  151. BuyingPrice = productInfo.Product.BuyingPrice,
  152. Count = req.Count,
  153. CreateBY = session.UserId
  154. };
  155. YW_OrderProcess orderProcess = new YW_OrderProcess()
  156. {
  157. Id = IdGenerator.NewId(),
  158. OrderId = order.Id,
  159. DeviceId = session.UserId,
  160. CurrentOrderStatus = OrderStatusEnum.UnPay,
  161. Message = "下单成功",
  162. CreateBY = session.UserId
  163. };
  164. #endregion
  165. #region 6、存库
  166. _dbContent.Set<YW_Order>().Add(order);
  167. _dbContent.Set<YW_OrderProcess>().Add(orderProcess);
  168. _dbContent.Set<YW_OrderDetails>().Add(orderDetails);
  169. // 数据库执行 库存已经改过了,会自动保存
  170. _dbContent.SaveChanges();
  171. #endregion
  172. // 写入订单日志
  173. _ywLogService.WriteOrderLog(order.Id, "下单", "下单成功", session.UserId);
  174. return new ApiResult<OrderResponseDto>(new OrderResponseDto()
  175. {
  176. OrderId = order.Id,
  177. Price = order.Price,
  178. QrCode = string.Format(payRedirectUrl, order.Id)
  179. });
  180. }
  181. /// <summary>
  182. /// 获取待烧烤的商品列表
  183. /// <summary>
  184. /// <returns></returns>
  185. public ApiResult<List<QueueOrderResponseDto>> GetQueueOrders()
  186. {
  187. var session = _userService.GetLoginSession();
  188. var orders = _dbContent.Set<YW_Order>()
  189. .Where(p => p.IsDelete == 0
  190. && p.PayStatus == PayStatusEnum.Paid
  191. && p.OrderStatus == OrderStatusEnum.Paid
  192. && p.DeviceId == session.UserId)
  193. .OrderBy(p => p.CreateDT).ToList();
  194. var orderIds = orders.Select(p => p.Id).ToList();
  195. var orderDetails = _dbContent.Set<YW_OrderDetails>().Where(p => p.IsDelete == 0 && orderIds.Contains(p.OrderId)).ToList();
  196. List<QueueOrderResponseDto> result = new List<QueueOrderResponseDto>();
  197. orders.ForEach(item =>
  198. {
  199. var currentOrderDetails = orderDetails.FirstOrDefault(p => p.ProductId == item.Id);
  200. var img = string.Empty;
  201. if (!string.IsNullOrWhiteSpace(currentOrderDetails?.Img))
  202. {
  203. img = $"{ProductService.ImagePrefix}{currentOrderDetails.Img}";
  204. }
  205. QueueOrderResponseDto resultItem = new QueueOrderResponseDto()
  206. {
  207. OrderId = item.Id,
  208. TotalPrice = item.Price,
  209. Name = item.Name,
  210. Code = currentOrderDetails?.Code,
  211. Img = img,
  212. Price = currentOrderDetails?.Price ?? 0,
  213. Count = currentOrderDetails?.Count ?? 0
  214. };
  215. result.Add(resultItem);
  216. });
  217. return new ApiResult<List<QueueOrderResponseDto>>(result);
  218. }
  219. /// <summary>
  220. /// 取消未支付订单
  221. /// </summary>
  222. /// <returns></returns>
  223. public ApiResult CancelUnPayOrder(string orderId)
  224. {
  225. var session = _userService.GetLoginSession();
  226. var order = _dbContent.Set<YW_Order>().FirstOrDefault(p => p.IsDelete == 0 && p.Id == orderId && p.DeviceId == session.UserId);
  227. if (order == null)
  228. {
  229. return new ApiResult(ApiStatusCode.InvalidParameter, "取消的订单不存在");
  230. }
  231. if (order.PayStatus != PayStatusEnum.UnPay && order.OrderStatus != OrderStatusEnum.UnPay)
  232. {
  233. return new ApiResult(ApiStatusCode.InvalidParameter, "当前订单不是未支付状态,不能取消");
  234. }
  235. order.PayStatus = PayStatusEnum.Cancel;
  236. order.OrderStatus = OrderStatusEnum.Cancel;
  237. _dbContent.SaveChanges();
  238. return new ApiResult();
  239. }
  240. /// <summary>
  241. /// 回写流程状态
  242. /// </summary>
  243. /// <param name="req"></param>
  244. /// <returns></returns>
  245. public ApiResult OrderProcess(OrderProcessRequestDto req)
  246. {
  247. if (req == null)
  248. {
  249. return new ApiResult(ApiStatusCode.InvalidParameter, "请求参数不正确");
  250. }
  251. var order = _dbContent.Set<YW_Order>().FirstOrDefault(p => p.Id == req.OrderId);
  252. if (order == null)
  253. {
  254. return new ApiResult(ApiStatusCode.InvalidParameter, "未找到相应订单");
  255. }
  256. if (order.PayStatus != PayStatusEnum.Paid)
  257. {
  258. return new ApiResult(ApiStatusCode.InvalidParameter, "未支付订单不能回写流程");
  259. }
  260. if (order.OrderStatus == OrderStatusEnum.UnPay
  261. || order.OrderStatus == OrderStatusEnum.Cancel)
  262. {
  263. return new ApiResult(ApiStatusCode.InvalidParameter, "未支付或已取消订单不能回写流程");
  264. }
  265. var session = _userService.GetLoginSession();
  266. // 修改订单状态
  267. order.OrderStatus = req.OrderStatus;
  268. // 记录订单流程
  269. YW_OrderProcess orderProcess = new YW_OrderProcess()
  270. {
  271. Id = IdGenerator.NewId(),
  272. OrderId = req.OrderId,
  273. DeviceId = session.UserId,
  274. CurrentOrderStatus = req.OrderStatus,
  275. Message = req.Message,
  276. CreateBY = session.UserId
  277. };
  278. _dbContent.Set<YW_OrderProcess>().Add(orderProcess);
  279. _dbContent.SaveChanges();
  280. // 写入订单日志
  281. _ywLogService.WriteOrderLog(order.Id, "流程回写", $"回写流程状态{req.OrderStatus.ToString()}", session.UserId);
  282. return new ApiResult();
  283. }
  284. /// <summary>
  285. /// 获取支付宝支付链接
  286. /// </summary>
  287. /// <param name="id"></param>
  288. /// <returns></returns>
  289. public string AliPayProcess(string id)
  290. {
  291. // 1、先查询订单是否存在
  292. var order = _dbContent.Set<YW_Order>().FirstOrDefault(p => p.IsDelete == 0 && p.Id == id);
  293. if (order == null)
  294. {
  295. return "http://api.rgtech.ltd/html/ordernotexist.html";
  296. }
  297. string result = "http://api.rgtech.ltd/html/error.html";
  298. // 查询订单详情
  299. var orderDetails = _dbContent.Set<YW_OrderDetails>().Where(p => p.IsDelete == 0 && p.OrderId == order.Id).ToList();
  300. #region 拼写支付宝支付参数
  301. AlipayTradePrecreateContentBuilder builder = new AlipayTradePrecreateContentBuilder();
  302. //收款账号
  303. builder.seller_id = AliPayConfig.pid;
  304. //订单编号
  305. builder.out_trade_no = order.Id;
  306. //订单总金额
  307. builder.total_amount = order.Price.ToString();
  308. //参与优惠计算的金额
  309. //builder.discountable_amount = "";
  310. //不参与优惠计算的金额
  311. //builder.undiscountable_amount = "";
  312. //订单名称
  313. builder.subject = order.Name;
  314. //自定义超时时间
  315. builder.timeout_express = "10m";
  316. //订单描述
  317. builder.body = "";
  318. //门店编号,很重要的参数,可以用作之后的营销
  319. builder.store_id = order.DeviceId;
  320. //操作员编号,很重要的参数,可以用作之后的营销
  321. //builder.operator_id = "test";
  322. //传入商品信息详情
  323. List<GoodsInfo> gList = orderDetails?.Select(od => new GoodsInfo()
  324. {
  325. goods_id = od.Id,
  326. goods_name = od.Name,
  327. price = od.Price.ToString(),
  328. quantity = od.Count.ToString()
  329. }).ToList() ?? new List<GoodsInfo>();
  330. builder.goods_detail = gList;
  331. //系统商接入可以填此参数用作返佣
  332. //ExtendParams exParam = new ExtendParams();
  333. //exParam.sysServiceProviderId = "20880000000000";
  334. //builder.extendParams = exParam;
  335. #endregion
  336. //如果需要接收扫码支付异步通知,那么请把下面两行注释代替本行。
  337. //推荐使用轮询撤销机制,不推荐使用异步通知,避免单边账问题发生。
  338. //AlipayF2FPrecreateResult precreateResult = new AlipayF2FPrecreateResult();
  339. AlipayF2FPrecreateResult precreateResult = serviceClient.tradePrecreate(builder, aliNotifyUrl);//商户接收异步通知的地址,采用异步回写通知
  340. switch (precreateResult.Status)
  341. {
  342. case ResultEnum.SUCCESS:
  343. result = precreateResult.response.QrCode;
  344. break;
  345. case ResultEnum.FAILED:
  346. case ResultEnum.UNKNOWN:
  347. default:
  348. // 请求失败,记录日志
  349. _log4NetHelper.Error($"[AliPayProcess]-tradePrecreate失败:{JsonConvert.SerializeObject(precreateResult)}");
  350. break;
  351. }
  352. return result;
  353. }
  354. /// <summary>
  355. /// 支付宝支付回写(已测试好,勿动)
  356. /// </summary>
  357. /// <returns></returns>
  358. public string AliPayNotify()
  359. {
  360. _log4NetHelper.Info("[AliPayNotify]-start");
  361. SortedDictionary<string, string> sPara = GetRequestPost();
  362. _log4NetHelper.Info("[AliPayNotify]-[原始请求参数]-" + JsonConvert.SerializeObject(sPara));
  363. Notify aliNotify = new Notify(AliPayConfig.charset, AliPayConfig.sign_type, AliPayConfig.pid, AliPayConfig.mapiUrl, AliPayConfig.alipay_public_key);
  364. string notifyId = HttpContext.Current.Request.Form["notify_id"];
  365. string sign = HttpContext.Current.Request.Form["sign"];
  366. _log4NetHelper.Info($"[AliPayNotify]-[基础参数]-notify_id:{notifyId};sign{sign}");
  367. //对异步通知进行验签
  368. bool verifyResult = aliNotify.Verify(sPara, notifyId, sign);
  369. if (verifyResult && CheckParams()) //验签成功 && 关键业务参数校验成功
  370. {
  371. _log4NetHelper.Info("[AliPayNotify]-验签成功");
  372. string paySerialId = HttpContext.Current.Request.Form["trade_no"] ?? string.Empty;
  373. string outTradeNo = HttpContext.Current.Request.Form["out_trade_no"] ?? string.Empty;
  374. string payResponse = JsonConvert.SerializeObject(sPara);
  375. // 写入支付日志-里面有trycatch
  376. _ywLogService.WritePayCallLog(paySerialId, outTradeNo, PayWayEnum.AliPay, payResponse);
  377. PayStatusEnum payStatus = PayStatusEnum.Fail;
  378. if (HttpContext.Current.Request.Form["trade_status"] == "TRADE_SUCCESS")
  379. {
  380. payStatus = PayStatusEnum.Paid;
  381. }
  382. PayWriteBackRequestDto payWriteBack = new PayWriteBackRequestDto()
  383. {
  384. PaySerialId = paySerialId,
  385. OrderId = outTradeNo,
  386. PayStatus = payStatus,
  387. PayWay = PayWayEnum.AliPay,
  388. BuyerId = HttpContext.Current.Request.Form["buyer_id"] ?? string.Empty,
  389. BuyAccount = HttpContext.Current.Request.Form["buyer_logon_id"] ?? string.Empty,
  390. SellerId = HttpContext.Current.Request.Form["seller_id"] ?? string.Empty,
  391. SellAccount = HttpContext.Current.Request.Form["seller_email"] ?? string.Empty,
  392. TotalAmount = ConvertHelper.ToDecimal(HttpContext.Current.Request.Form["total_amount"]),
  393. PayAmount = ConvertHelper.ToDecimal(HttpContext.Current.Request.Form["buyer_pay_amount"]),
  394. PayTime = ConvertHelper.ToDateTime(HttpContext.Current.Request.Form["gmt_payment"]),
  395. PayResponse = payResponse
  396. };
  397. // 回写到实际表中
  398. var result = PayWriteBack(payWriteBack);
  399. if (!result.IsSuccess)
  400. {
  401. // 失败了
  402. _log4NetHelper.Info($"[AliPayNotify]-[支付回写验证失败]-{result.Message}");
  403. return "false";
  404. }
  405. return "sucess";
  406. }
  407. else
  408. {
  409. _log4NetHelper.Info("[AliPayNotify]-验签失败!!!");
  410. return "false";
  411. }
  412. }
  413. /// <summary>
  414. /// 申请退款
  415. /// </summary>
  416. /// <returns></returns>
  417. public ApiResult OrderRefund(string orderId)
  418. {
  419. var order = _dbContent.Set<YW_Order>().FirstOrDefault(p => p.IsDelete == 0 && p.Id == orderId);
  420. if (order.PayStatus != PayStatusEnum.RefundApply)
  421. {
  422. // 订单状态不可退款
  423. return new ApiResult(ApiStatusCode.InvalidParameter, "当前订单状态不可退款");
  424. }
  425. ApiResult result = null;
  426. switch (order.PayWay)
  427. {
  428. case PayWayEnum.WeChatPay:
  429. result = new ApiResult(ApiStatusCode.Forbidden, "微信支付暂未接入,无法发起退款");
  430. break;
  431. case PayWayEnum.AliPay:
  432. // 支付宝退款
  433. result = AliPayRefund(order.Id, order.PaySerialId, order.Price, "客户发起退款");
  434. break;
  435. default:
  436. result = new ApiResult(ApiStatusCode.Forbidden, "未知支付方式,无法在线自动退款");
  437. break;
  438. }
  439. if (result.IsSuccess)
  440. {
  441. // 修改订单状态
  442. order.PayStatus = PayStatusEnum.Refunding;
  443. order.OrderStatus = OrderStatusEnum.Cancel;
  444. order.ModifyDT = DateTime.Now;
  445. order.ModifyBY = "用户申请";
  446. _dbContent.SaveChanges();
  447. // 写入订单流程记录
  448. _ywLogService.WriteOrderProcess(order.Id, order.DeviceId, OrderStatusEnum.Cancel, "申请退款", "用户申请");
  449. }
  450. return result;
  451. }
  452. /// <summary>
  453. /// 轮询查询退款信息,并更新订单的退款记录
  454. /// </summary>
  455. /// <returns></returns>
  456. public ApiResult OrderRefundLoop()
  457. {
  458. // 1、查询出所有退款中的订单
  459. // 2、请求支付宝退款接口查询是否已退款成功
  460. // 3、退款成功或失败时,回写到订单中
  461. var modifyDt = DateTime.Now.AddMinutes(-2);// 查询2分钟以前的订单,因为退款需要一定时间,马上查询会返回失败
  462. var order = _dbContent.Set<YW_Order>().Where(p => p.IsDelete == 0 && p.PayStatus == PayStatusEnum.RefundApply && p.ModifyDT < modifyDt).OrderBy(p => p.ModifyDT).FirstOrDefault();
  463. if (order == null)
  464. {
  465. return new ApiResult(ApiStatusCode.InvalidParameter, "无退款中订单");
  466. }
  467. // 更新modifydt以便下次捞取别的订单
  468. order.ModifyDT = DateTime.Now;
  469. ApiResult result = null;
  470. switch (order.PayWay)
  471. {
  472. case PayWayEnum.WeChatPay:
  473. result = new ApiResult(ApiStatusCode.Forbidden, "微信支付暂未接入,无法进行查询");
  474. break;
  475. case PayWayEnum.AliPay:
  476. // 查询支付宝退款
  477. result = AliPayRefundQuery(order.Id, order.PaySerialId, order.DeviceId);
  478. break;
  479. default:
  480. result = new ApiResult(ApiStatusCode.Forbidden, "未知支付方式,无法进行查询");
  481. break;
  482. }
  483. if (result.IsSuccess)
  484. {
  485. // 修改订单状态
  486. order.PayStatus = PayStatusEnum.Refunded;
  487. order.OrderStatus = OrderStatusEnum.Cancel;
  488. order.ModifyDT = DateTime.Now;
  489. order.ModifyBY = "用户申请";
  490. // 写入订单流程记录
  491. _ywLogService.WriteOrderProcess(order.Id, order.DeviceId, OrderStatusEnum.Cancel, "退款成功", "用户申请");
  492. }
  493. else
  494. {
  495. // 退款失败,将订单更新成错误,并记录订单日志
  496. order.PayStatus = PayStatusEnum.Fail;
  497. order.OrderStatus = OrderStatusEnum.Cancel;
  498. order.ModifyDT = DateTime.Now;
  499. order.ModifyBY = "用户申请";
  500. // 写入订单日志
  501. _ywLogService.WriteOrderLog(order.Id, "订单退款失败", result.Message, "用户申请");
  502. }
  503. _dbContent.SaveChanges();
  504. return result;
  505. }
  506. #region privatemethods
  507. /// <summary>
  508. /// 支付回写(回写到实际表中)
  509. /// </summary>
  510. /// <param name="req"></param>
  511. /// <returns></returns>
  512. private ApiResult PayWriteBack(PayWriteBackRequestDto req)
  513. {
  514. if (req == null
  515. || string.IsNullOrWhiteSpace(req.PaySerialId))
  516. {
  517. return new ApiResult(ApiStatusCode.InvalidParameter, "请求参数不正确");
  518. }
  519. var order = _dbContent.Set<YW_Order>().FirstOrDefault(p => p.Id == req.OrderId);
  520. if (order == null)
  521. {
  522. return new ApiResult(ApiStatusCode.InvalidParameter, "未找到相应订单");
  523. }
  524. if (order.Price != req.TotalAmount)
  525. {
  526. // 写入订单日志
  527. string msg = $"回写总金额与订单金额不匹配,订单号{order.Id};订单价格{order.Price};支付流水号{req.PaySerialId};支付订单总金额{req.TotalAmount}";
  528. _ywLogService.WriteOrderLog(order.Id, "支付回写", msg, req.PayWay.ToString());
  529. return new ApiResult(ApiStatusCode.InvalidParameter, msg);
  530. }
  531. if (order.Price != req.PayAmount)
  532. {
  533. // 写入订单日志
  534. string msg = $"回写支付金额与订单金额不匹配,订单号{order.Id};订单价格{order.Price};支付流水号{req.PaySerialId};支付订单已付金额{req.PayAmount}";
  535. _ywLogService.WriteOrderLog(order.Id, "支付回写", msg, req.PayWay.ToString());
  536. return new ApiResult(ApiStatusCode.InvalidParameter, msg);
  537. }
  538. var status = GetStatusByPayWriteBack(req.PayStatus);
  539. // 修改订单状态
  540. order.PayStatus = status.Item1;
  541. order.OrderStatus = status.Item2;
  542. order.PaySerialId = req.PaySerialId;
  543. // 记录订单支付流程
  544. YW_PayCall payCall = new YW_PayCall()
  545. {
  546. Id = IdGenerator.NewId(),
  547. OrderId = req.OrderId,
  548. PayStatus = status.Item1,
  549. PaySerialId = req.PaySerialId,
  550. PayWay = req.PayWay,
  551. BuyerId = req.BuyerId,
  552. BuyAccount = req.BuyAccount,
  553. SellerId = req.SellerId,
  554. SellAccount = req.SellAccount,
  555. PayAmount = req.PayAmount,
  556. PayTime = req.PayTime,
  557. PayResponse = req.PayResponse,
  558. CreateBY = req.BuyAccount
  559. };
  560. _dbContent.Set<YW_PayCall>().Add(payCall);
  561. // 写入订单流程信息
  562. _ywLogService.WriteOrderProcess(req.OrderId, order.DeviceId, order.OrderStatus, "订单回写成功", "支付回写");
  563. _dbContent.SaveChanges();
  564. // 记录订单日志
  565. _ywLogService.WriteOrderLog(order.Id, "支付回写", "回写成功", req.PayWay.ToString());
  566. return new ApiResult();
  567. }
  568. /// <summary>
  569. /// 支付宝退款
  570. /// </summary>
  571. /// <param name="orderId"></param>
  572. /// <param name="paySerialId"></param>
  573. /// <param name="refundAmount"></param>
  574. /// <param name="refundReason"></param>
  575. /// <returns></returns>
  576. private ApiResult AliPayRefund(string orderId,string paySerialId,decimal refundAmount, string refundReason)
  577. {
  578. AlipayTradeRefundContentBuilder builder = new AlipayTradeRefundContentBuilder()
  579. {
  580. trade_no = paySerialId,
  581. out_trade_no = orderId,
  582. refund_amount = refundAmount.ToString(),
  583. out_request_no = string.Empty,
  584. refund_reason = refundReason,
  585. };
  586. ApiResult result = null;
  587. // 支付宝退款
  588. _log4NetHelper.Error($"[AliPayRefund]-tradeRefund-start:request:{JsonConvert.SerializeObject(builder)}");
  589. AlipayF2FRefundResult refundResult = serviceClient.tradeRefund(builder);
  590. _log4NetHelper.Error($"[AliPayRefund]-tradeRefund-end:request:{JsonConvert.SerializeObject(refundResult)}");
  591. switch (refundResult.Status)
  592. {
  593. case ResultEnum.SUCCESS:
  594. // 退款成功-记录日志
  595. _ywLogService.WriteOrderLog(orderId, "申请退款", $"申请退款成功:退款金额{refundResult.response?.SendBackFee}", "用户申请");
  596. result = new ApiResult();
  597. break;
  598. default:
  599. result = new ApiResult(ApiStatusCode.InternalServerError, "请求支付宝退款失败" + refundResult.response?.SubMsg);
  600. // 退款失败-记录订单日志
  601. _ywLogService.WriteOrderLog(orderId, "申请退款", $"申请退款失败:{refundResult.response?.SubMsg}", "用户申请");
  602. // 请求失败,记录log4net日志
  603. _log4NetHelper.Error($"[AliPayRefund]-tradeRefund失败:request:{JsonConvert.SerializeObject(builder)};response:{JsonConvert.SerializeObject(refundResult)}");
  604. break;
  605. }
  606. return result;
  607. }
  608. /// <summary>
  609. /// 支付宝退款查询
  610. /// </summary>
  611. /// <param name="orderId"></param>
  612. /// <param name="paySerialId"></param>
  613. /// <param name="deviceId"></param>
  614. /// <returns></returns>
  615. private ApiResult AliPayRefundQuery(string orderId, string paySerialId, string deviceId)
  616. {
  617. var builder = new AlipayTradeFastpayRefundQueryContentBuilder()
  618. {
  619. trade_no = paySerialId,
  620. out_trade_no = orderId,
  621. out_request_no = deviceId
  622. };
  623. ApiResult result = null;
  624. // 支付宝退款
  625. _log4NetHelper.Error($"[AliPayRefund]-AliPayRefundQuery-start:request:{JsonConvert.SerializeObject(builder)}");
  626. var refundQueryResult = serviceClient.tradeFastpayRefundQuery(builder);
  627. _log4NetHelper.Error($"[AliPayRefund]-AliPayRefundQuery-end:request:{JsonConvert.SerializeObject(refundQueryResult)}");
  628. switch (refundQueryResult.Status)
  629. {
  630. case ResultEnum.SUCCESS:
  631. string content = "成功";
  632. if (refundQueryResult.response.RefundStatus == "REFUND_SUCCESS")
  633. {
  634. // 退款成功
  635. result = new ApiResult();
  636. content = $"退款成功:退款金额{refundQueryResult.response?.RefundAmount}";
  637. }
  638. else
  639. {
  640. // 退款失败
  641. result = new ApiResult(ApiStatusCode.InternalServerError, "请求支付宝退款失败" + refundQueryResult.response?.SubMsg);
  642. content = $"退款失败:{refundQueryResult.response?.SubMsg}";
  643. }
  644. // 记录订单日志
  645. _ywLogService.WriteOrderLog(orderId, "退款查询", content, "用户申请");
  646. break;
  647. default:
  648. result = new ApiResult(ApiStatusCode.InternalServerError, "请求支付宝退款失败" + refundQueryResult.response?.SubMsg);
  649. // 退款失败-记录订单日志
  650. _ywLogService.WriteOrderLog(orderId, "退款查询", $"退款失败:{refundQueryResult.response?.SubMsg}", "用户申请");
  651. // 请求失败,记录log4net日志
  652. _log4NetHelper.Error($"[AliPayRefund]-AliPayRefundQuery失败:request:{JsonConvert.SerializeObject(builder)};response:{JsonConvert.SerializeObject(refundQueryResult)}");
  653. break;
  654. }
  655. return result;
  656. }
  657. /// <summary>
  658. /// 支付测试
  659. /// </summary>
  660. /// <returns></returns>
  661. public ApiResult<AlipayF2FPrecreateResult> PayTest()
  662. {
  663. AlipayTradePrecreateContentBuilder builder = BuildPrecreateContent();
  664. string out_trade_no = builder.out_trade_no;
  665. //如果需要接收扫码支付异步通知,那么请把下面两行注释代替本行。
  666. //推荐使用轮询撤销机制,不推荐使用异步通知,避免单边账问题发生。
  667. // AlipayF2FPrecreateResult precreateResult = serviceClient.tradePrecreate(builder);
  668. string notify_url = aliNotifyUrl; //商户接收异步通知的地址
  669. AlipayF2FPrecreateResult precreateResult = serviceClient.tradePrecreate(builder, notify_url);
  670. //以下返回结果的处理供参考。
  671. //payResponse.QrCode即二维码对于的链接
  672. //将链接用二维码工具生成二维码打印出来,顾客可以用支付宝钱包扫码支付。
  673. string result = "";
  674. return new ApiResult<AlipayF2FPrecreateResult>(precreateResult);
  675. //switch (precreateResult.Status)
  676. //{
  677. // case ResultEnum.SUCCESS:
  678. // DoWaitProcess(precreateResult);
  679. // txtRCCode.Text = precreateResult.response.QrCode;
  680. // break;
  681. // case ResultEnum.FAILED:
  682. // result = precreateResult.response.Body;
  683. // Response.Redirect("result.aspx?Text=" + result);
  684. // break;
  685. // case ResultEnum.UNKNOWN:
  686. // if (precreateResult.response == null)
  687. // {
  688. // result = "配置或网络异常,请检查后重试";
  689. // }
  690. // else
  691. // {
  692. // result = "系统异常,请更新外部订单后重新发起请求";
  693. // }
  694. // Response.Redirect("result.aspx?Text=" + result);
  695. // break;
  696. //}
  697. }
  698. /// <summary>
  699. /// 构造支付请求数据
  700. /// </summary>
  701. /// <returns>请求数据集</returns>
  702. private AlipayTradePrecreateContentBuilder BuildPrecreateContent()
  703. {
  704. AlipayTradePrecreateContentBuilder builder = new AlipayTradePrecreateContentBuilder();
  705. //收款账号
  706. builder.seller_id = AliPayConfig.pid;
  707. //订单编号-----------------------
  708. builder.out_trade_no = DateTime.Now.ToString("yyyyMMddHHmmss") + "0000" + (new Random()).Next(1, 10000).ToString();
  709. //订单总金额-----------------------------
  710. builder.total_amount = "0.02";
  711. //参与优惠计算的金额
  712. //builder.discountable_amount = "";
  713. //不参与优惠计算的金额
  714. //builder.undiscountable_amount = "";
  715. //订单名称-------------------------------
  716. builder.subject = "smj_webapi测试订单";
  717. //自定义超时时间
  718. builder.timeout_express = "5m";
  719. //订单描述
  720. builder.body = "";
  721. //门店编号,很重要的参数,可以用作之后的营销--------------------
  722. builder.store_id = "test store id";
  723. //操作员编号,很重要的参数,可以用作之后的营销---------------------
  724. builder.operator_id = "test";
  725. //传入商品信息详情
  726. List<GoodsInfo> gList = new List<GoodsInfo>();
  727. GoodsInfo goods = new GoodsInfo();
  728. goods.goods_id = "goods id";
  729. goods.goods_name = "goods name";
  730. goods.price = "0.02";
  731. goods.quantity = "1";
  732. gList.Add(goods);
  733. builder.goods_detail = gList;
  734. //系统商接入可以填此参数用作返佣
  735. //ExtendParams exParam = new ExtendParams();
  736. //exParam.sysServiceProviderId = "20880000000000";
  737. //builder.extendParams = exParam;
  738. return builder;
  739. }
  740. /// <summary>
  741. ///
  742. /// </summary>
  743. /// <param name="payStatus"></param>
  744. /// <returns></returns>
  745. private Tuple<PayStatusEnum, OrderStatusEnum> GetStatusByPayWriteBack(PayStatusEnum payStatus)
  746. {
  747. // 临时:payStatus 0失败,1成功
  748. OrderStatusEnum orderStatus = OrderStatusEnum.UnPay;
  749. switch (payStatus)
  750. {
  751. case PayStatusEnum.Paid:// 成功
  752. orderStatus = OrderStatusEnum.Paid;
  753. break;
  754. default:// 失败
  755. break;
  756. }
  757. return new Tuple<PayStatusEnum, OrderStatusEnum>(payStatus, orderStatus);
  758. }
  759. /// <summary>
  760. /// 获取支付宝POST过来通知消息,并以“参数名=参数值”的形式组成数组
  761. /// </summary>
  762. /// <returns>request回来的信息组成的数组</returns>
  763. private SortedDictionary<string, string> GetRequestPost()
  764. {
  765. int i = 0;
  766. SortedDictionary<string, string> sArray = new SortedDictionary<string, string>();
  767. NameValueCollection coll;
  768. //Load Form variables into NameValueCollection variable.
  769. coll = HttpContext.Current.Request.Form;
  770. // Get names of all forms into a string array.
  771. String[] requestItem = coll.AllKeys;
  772. for (i = 0; i < requestItem.Length; i++)
  773. {
  774. sArray.Add(requestItem[i], HttpContext.Current.Request.Form[requestItem[i]]);
  775. }
  776. return sArray;
  777. }
  778. /// <summary>
  779. /// 对支付宝异步通知的关键参数进行校验
  780. /// </summary>
  781. /// <returns></returns>
  782. private bool CheckParams()
  783. {
  784. bool ret = true;
  785. //获得调用方的appid;
  786. //如果是非授权模式,appid是商户的appid;如果是授权模式(token调用),appid是系统商的appid
  787. string app_id = HttpContext.Current.Request.Form["app_id"];
  788. if (app_id != AliPayConfig.appId)
  789. {
  790. ret = false;
  791. _log4NetHelper.Info($"[支付回写][appid认证失败]:request_appid:{app_id};sys_appid:{AliPayConfig.appId}");
  792. }
  793. return ret;
  794. }
  795. #endregion
  796. }
  797. }