团餐订单
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

528 lines
23 KiB

  1. using BPA.KitChen.GroupMealOrder.Core;
  2. using BPA.KitChen.GroupMealOrder.Core.CacheOption;
  3. using BPA.KitChen.GroupMealOrder.Core.Common;
  4. using BPA.KitChen.GroupMealOrder.Core.Entity;
  5. using BPA.KitChen.GroupMealOrder.SqlSugar;
  6. using BPA.KitChen.WeChat.WechatServer.Dtos;
  7. using Essensoft.Paylink.WeChatPay.V2;
  8. using Essensoft.Paylink.WeChatPay.V2.Request;
  9. using Essensoft.Paylink.WeChatPay.V2.Response;
  10. using Furion;
  11. using Furion.DependencyInjection;
  12. using Furion.FriendlyException;
  13. using Microsoft.AspNetCore.Hosting;
  14. using Microsoft.AspNetCore.Http;
  15. using Microsoft.AspNetCore.Mvc;
  16. using Microsoft.Extensions.Caching.Memory;
  17. using System;
  18. using System.Collections.Generic;
  19. using System.Linq;
  20. using System.Threading.Tasks;
  21. namespace BPA.KitChen.WeChat.WechatServer.Service
  22. {
  23. /// <summary>
  24. /// 微信支付
  25. /// </summary>
  26. public class WechatPayService : IWechatPayService, ITransient
  27. {
  28. private readonly IMemoryCache _memoryCache;
  29. private readonly IWeChatPayClient _client;
  30. readonly IWebHostEnvironment _env;
  31. private readonly IHttpContextAccessor _httpContextAccessor;
  32. public WechatPayService(IMemoryCache memoryCache, IHttpContextAccessor httpContextAccessor, IWeChatPayClient client, IWebHostEnvironment env)
  33. {
  34. _memoryCache = memoryCache;
  35. _client = client;
  36. _httpContextAccessor = httpContextAccessor;
  37. _env = env;
  38. }
  39. /// <summary>
  40. /// 统一下单
  41. /// </summary>
  42. /// <param name="ordernumber">自己系统的订单ID</param>
  43. /// <param name="openid"></param>
  44. /// <returns></returns>
  45. public async Task<IActionResult> PayLinkWechatPay(string openid, string ordernumber)
  46. {
  47. Console.WriteLine("进入支付接口");
  48. var appid = _httpContextAccessor.HttpContext.Request.Headers["AppId"].ToString();
  49. if (string.IsNullOrEmpty(appid))
  50. {
  51. throw Oops.Oh("AppId不能为空!");
  52. }
  53. var config = ApolloApplication.Configs.Find(x => x.Key == appid);
  54. if (config == null)
  55. {
  56. throw Oops.Oh("AppId不存在!");
  57. }
  58. try
  59. {
  60. WechatConfigOptions opt = config.WechatConfig;
  61. WechatPayConfigOptions PayOpt = config.WechatPayConfig;
  62. //查询订单
  63. var order = await SqlSugarDb.Db.Queryable<BPA_WeighOrder>()
  64. .ClearFilter()
  65. .FirstAsync(x => x.OrderNumber == ordernumber);
  66. if (order==null)
  67. {
  68. throw Oops.Oh("没有找到订单信息!");
  69. }
  70. string body = $"{CurrentUser.StoreName}点餐";
  71. string attach = CurrentUser.StoreName;
  72. string out_trade_no = order.OrderNumber;
  73. int total_fee = Convert.ToInt32(order.TotalAmount * 100);
  74. var request = new WeChatPayUnifiedOrderRequest
  75. {
  76. Body = body,
  77. OutTradeNo = out_trade_no,
  78. TotalFee = total_fee,
  79. Attach = attach,
  80. SpBillCreateIp = opt.MchIP,
  81. NotifyUrl = opt.WeChatPayNotifyUrl,
  82. TradeType = "JSAPI",
  83. OpenId =string.IsNullOrEmpty(openid)? order .CreateId: openid,
  84. };
  85. var response = await _client.ExecuteAsync(request, PayOpt);
  86. if (response.ReturnCode == WeChatPayCode.Success && response.ResultCode == WeChatPayCode.Success)
  87. {
  88. var req = new WeChatPayJsApiSdkRequest
  89. {
  90. Package = "prepay_id=" + response.PrepayId
  91. };
  92. var parameter = await _client.ExecuteAsync(req, PayOpt);
  93. parameter.Add("paymentId", out_trade_no);
  94. PaymentEntity payment = new();
  95. payment.timeStamp = parameter.FirstOrDefault(x => x.Key == "timeStamp").Value;
  96. payment.nonceStr = parameter.FirstOrDefault(x => x.Key == "nonceStr").Value;
  97. payment.package = parameter.FirstOrDefault(x => x.Key == "package").Value;
  98. payment.paySign = parameter.FirstOrDefault(x => x.Key == "paySign").Value;
  99. payment.signType = parameter.FirstOrDefault(x => x.Key == "signType").Value;
  100. payment.trade_no = parameter.FirstOrDefault(x => x.Key == "paymentId").Value;
  101. order.TradeNo = payment.trade_no;
  102. var res = await SqlSugarDb.Db.Updateable<BPA_WeighOrder>(order).ExecuteCommandAsync();
  103. if (res>0)
  104. {
  105. return new JsonResult(payment);
  106. }
  107. else
  108. {
  109. throw Oops.Oh("订单服务更新数据失败!");
  110. }
  111. }
  112. else
  113. {
  114. throw Oops.Oh(response.ReturnMsg+"-"+response.ErrCode+"-"+response.ErrCodeDes);
  115. }
  116. }
  117. catch (Exception ex)
  118. {
  119. // BPALog.WriteLog("支付接口PayLinkWechatPay报错:"+ex.ToString());
  120. throw Oops.Oh(ex.Message);
  121. }
  122. }
  123. /// <summary>
  124. /// 退款
  125. /// </summary>
  126. /// <param name="input"></param>
  127. /// <param name="config"></param>
  128. /// <returns></returns>
  129. public async Task<RefundDto> Refund(WechatRefundInput input, ApolloApplicationConfig config)
  130. {
  131. WechatConfigOptions opt = config.WechatConfig;
  132. WechatPayConfigOptions PayOpt = config.WechatPayConfig;
  133. RefundDto dto = new();
  134. var request = new WeChatPayRefundRequest
  135. {
  136. OutRefundNo = WechatCommon.GenerateOutTradeNo(),
  137. //TransactionId = input.transaction_id,
  138. OutTradeNo = input.out_trade_no,
  139. TotalFee = Convert.ToInt32(input.total_fee),
  140. RefundDesc = input.RefundDesc,
  141. RefundFee = Convert.ToInt32(input.refund_fee),
  142. NotifyUrl = opt.WeChatRefundUrl
  143. };
  144. var response = await _client.ExecuteAsync(request, PayOpt);
  145. if (response.ReturnCode == WeChatPayCode.Success && response.ResultCode == WeChatPayCode.Success)
  146. {
  147. dto.res = true;
  148. dto.response = response;
  149. return dto;
  150. }
  151. else
  152. {
  153. //查询微信订单信息
  154. var orderinfo = await PayOrderQuery(opt.Appid, input.transaction_id, "");
  155. if (orderinfo != null)
  156. {
  157. Console.WriteLine("当前状态:" + orderinfo.TradeStateDesc);
  158. int totalfee = orderinfo.TotalFee;
  159. //已经退款,更新状态
  160. if (orderinfo.TradeState == "REFUND")
  161. {
  162. //修改数据库订单状态为退款
  163. config.WechatConfig.OrderAddress = "https://bpa.black-pa.com:21527/order";
  164. //查询退款订单
  165. var order = await SqlSugarDb.Db.Queryable<BPA_RefundWeighOrder>()
  166. .FirstAsync(x => x.WeighOrderNumber == input.out_trade_no);
  167. //修改退款状态
  168. if (order == null)
  169. {
  170. throw Oops.Bah($"退款失败:没有查询到退款订单");
  171. }
  172. //MyDataResult<bool> ResResult = OrderWeApiHepler.Refundrefuc<bool>(config.WechatConfig.OrderAddress, input.transaction_id, orderinfo.OutTradeNo, 1, totalfee / 100, "微信已经退款,修订退款操作");
  173. ////如果成功
  174. //if (ResResult.isSuccess)
  175. //{
  176. //}
  177. dto.res = true;
  178. dto.response = response;
  179. return dto;
  180. }
  181. }
  182. throw Oops.Bah($"退款失败:{response.ErrCodeDes}");
  183. }
  184. }
  185. /// <summary>
  186. /// 充值
  187. /// </summary>
  188. /// <param name="input"></param>
  189. /// <returns></returns>
  190. public async Task<IActionResult> RechargePay(RechargeInput input)
  191. {
  192. Console.WriteLine("进入支付接口");
  193. var appid = _httpContextAccessor.HttpContext.Request.Headers["AppId"].ToString();
  194. if (string.IsNullOrEmpty(appid))
  195. {
  196. throw Oops.Oh("AppId不能为空!");
  197. }
  198. var config = ApolloApplication.Configs.Find(x => x.Key == appid);
  199. if (config == null)
  200. {
  201. throw Oops.Oh("AppId不存在!");
  202. }
  203. try
  204. {
  205. WechatConfigOptions opt = config.WechatConfig;
  206. WechatPayConfigOptions PayOpt = config.WechatPayConfig;
  207. var money = (input.Money / 100).ToString();
  208. string body = $"会员充值";
  209. string attach = $"储值{money}元";
  210. string out_trade_no = input.RechargeId;
  211. int total_fee = Convert.ToInt32(input.Money);
  212. var request = new WeChatPayUnifiedOrderRequest
  213. {
  214. Body = body,
  215. OutTradeNo = out_trade_no,
  216. TotalFee = total_fee,
  217. Attach = attach,
  218. SpBillCreateIp = opt.MchIP,
  219. NotifyUrl = opt.WeChatPayNotifyUrl,
  220. TradeType = "JSAPI",
  221. OpenId = input.OpenId
  222. };
  223. var response = await _client.ExecuteAsync(request, PayOpt);
  224. if (response.ReturnCode == WeChatPayCode.Success && response.ResultCode == WeChatPayCode.Success)
  225. {
  226. var req = new WeChatPayJsApiSdkRequest
  227. {
  228. Package = "prepay_id=" + response.PrepayId
  229. };
  230. var parameter = await _client.ExecuteAsync(req, PayOpt);
  231. parameter.Add("paymentId", out_trade_no);
  232. PaymentEntity payment = new();
  233. payment.timeStamp = parameter.FirstOrDefault(x => x.Key == "timeStamp").Value;
  234. payment.nonceStr = parameter.FirstOrDefault(x => x.Key == "nonceStr").Value;
  235. payment.package = parameter.FirstOrDefault(x => x.Key == "package").Value;
  236. payment.paySign = parameter.FirstOrDefault(x => x.Key == "paySign").Value;
  237. payment.signType = parameter.FirstOrDefault(x => x.Key == "signType").Value;
  238. payment.trade_no = parameter.FirstOrDefault(x => x.Key == "paymentId").Value;
  239. return new JsonResult(payment);
  240. }
  241. else
  242. {
  243. // BPALog.WriteLog("支付接口返回PayLinkWechatPay:", param: response);
  244. throw Oops.Oh(response.ReturnMsg + "-" + response.ErrCode + "-" + response.ErrCodeDes);
  245. }
  246. }
  247. catch (Exception ex)
  248. {
  249. // BPALog.WriteLog("支付接口PayLinkWechatPay报错:" + ex.ToString());
  250. throw Oops.Oh(ex.Message);
  251. }
  252. }
  253. /// <summary>
  254. /// 查询
  255. /// </summary>
  256. /// <param name="AppId">appid</param>
  257. /// <param name="transaction_id">商家支付订单号</param>
  258. /// <param name="out_trade_no">商户订单号</param>
  259. /// <returns></returns>
  260. public async Task<WeChatPayOrderQueryResponse> PayOrderQuery(string AppId,string transaction_id, string out_trade_no)
  261. {
  262. var config = ApolloApplication.Configs.Find(x => x.Key == AppId);
  263. WechatPayConfigOptions opt = config.WechatPayConfig;
  264. var request = new WeChatPayOrderQueryRequest
  265. {
  266. OutTradeNo = out_trade_no,
  267. TransactionId = transaction_id,
  268. };
  269. var response = await _client.ExecuteAsync(request, opt);
  270. return response;
  271. }
  272. /// <summary>
  273. /// 根据商家号退款
  274. /// </summary>
  275. /// <param name="appId"></param>
  276. /// <param name="ordernumid"></param>
  277. /// <returns></returns>
  278. [IfException(ErrorMessage = "无效订单")]
  279. public async Task<IActionResult> ToRefund(string appId,string ordernumid)
  280. {
  281. return null;
  282. //var config = ApolloApplication.Configs.Find(x => x.Key == appId);
  283. //MyDataResult<OrderAllInfoDto> info = OrderWeApiHepler.GetOrderInfoByNum<OrderAllInfoDto>(config.WechatConfig.OrderAddress,ordernumid);
  284. //if (!info.isSuccess)
  285. //{
  286. // throw Oops.Oh("订单服务未找到订单信息");
  287. //}
  288. //if (info.data.orderRealMoney <= 0 || string.IsNullOrEmpty(info.data.transactionId))
  289. //{
  290. // MyDataResult<bool> result = OrderWeApiHepler.Refundrefuc<bool>(config.WechatConfig.OrderAddress, info.data.transactionId, info.data.tradeNo, 1, 0, "测试数据修改为退款状态");
  291. // if (!result.isSuccess)
  292. // {
  293. // throw Oops.Oh(result.msg);
  294. // }
  295. // else
  296. // {
  297. // return new JsonResult(true);
  298. // }
  299. //}
  300. //else
  301. //{
  302. // if (info != null && (!string.IsNullOrEmpty(info.data.tradeNo) || !string.IsNullOrEmpty(info.data.transactionId)))
  303. // {
  304. // //查询微信订单信息
  305. // var orderinfo = await PayOrderQuery(appId,info.data.transactionId, info.data.tradeNo);
  306. // if (orderinfo != null)
  307. // {
  308. // Console.WriteLine("当前状态:" + orderinfo.TradeStateDesc);
  309. // int totalfee = orderinfo.TotalFee;
  310. // //已经退款,更新状态
  311. // if (orderinfo.TradeState == "REFUND")
  312. // {
  313. // BPALog.WriteLog($"{info.data.orderNumber}检测到微信平台已退款");
  314. // //修改数据库订单状态为退款
  315. // MyDataResult<bool> ResResult = OrderWeApiHepler.Refundrefuc<bool>(config.WechatConfig.OrderAddress,info.data.transactionId, info.data.tradeNo, 1, totalfee / 100, "微信已经退款,修订退款操作");
  316. // //如果成功
  317. // if (ResResult.isSuccess)
  318. // {
  319. // //_CouponService.Rebackcoupon(info.Order_Number);
  320. // BPALog.WriteLog($"{info.data.orderNumber}修改后台状态为退款成功!");
  321. // return new JsonResult(true);
  322. // }
  323. // else
  324. // {
  325. // BPALog.WriteLog($"{info.data.orderNumber}{ResResult.msg}!");
  326. // return new JsonResult(false);
  327. // }
  328. // }
  329. // else if (orderinfo.TradeState == "SUCCESS")
  330. // {
  331. // MyDataResult<bool> resultIng = OrderWeApiHepler.Refundrefuc<bool>(config.WechatConfig.OrderAddress, info.data.transactionId, info.data.tradeNo, 2, Convert.ToDecimal(totalfee) / 100, "微信查询为未退款,进行退款操作");
  332. // if (resultIng.isSuccess)
  333. // {
  334. // //调用微信退款接口
  335. // var refundres = await Refund(
  336. // new()
  337. // {
  338. // transaction_id = info.data.transactionId,
  339. // out_trade_no = info.data.tradeNo,
  340. // total_fee = totalfee,
  341. // refund_fee = totalfee,
  342. // }, config
  343. // );
  344. // //如果返回成功,优惠券回退
  345. // if (refundres.res)
  346. // {
  347. // BPALog.WriteLog($"{info.data.orderNumber}微信平台退款成功");
  348. // //修改数据库订单状态为退款
  349. // MyDataResult<bool> resultSuc = OrderWeApiHepler.Refundrefuc<bool>(config.WechatConfig.OrderAddress, info.data.transactionId, info.data.tradeNo, 1, Convert.ToDecimal(totalfee) / 100, "微信查询为未退款,进行退款操作");
  350. // //如果成功
  351. // if (resultSuc.isSuccess)
  352. // {
  353. // BPALog.WriteLog($"{info.data.orderNumber}订单平台退款状态修改成功!");
  354. // return new JsonResult(true);
  355. // }
  356. // else
  357. // {
  358. // BPALog.WriteLog($"{info.data.orderNumber}订单平台退款状态修改失败!失败原因:{resultSuc.msg}");
  359. // return new JsonResult(false);
  360. // }
  361. // }
  362. // else
  363. // {
  364. // //失败则回滚订单状态
  365. // //string errcode = refundres.GetValue("err_code").ToString();
  366. // MyDataResult<bool> result = OrderWeApiHepler.Refundrefuc<bool>(config.WechatConfig.OrderAddress,info.data.transactionId, info.data.tradeNo, 3, Convert.ToDecimal(totalfee) / 100, refundres.response.ErrCodeDes);
  367. // BPALog.WriteLog($" Refundrefuc方法执行结果:{ result.isSuccess}");
  368. // BPALog.WriteLog($" 微信平台退款失败:{refundres.response.ErrCodeDes}订单流水号:{ info.data.orderNumber}");
  369. // return new JsonResult(false);
  370. // //OrderWeApiHepler.ChangeOrderStutas(info.Order_Number, info.Order_Status, "微信平台退款失败:" + CacheHepler.GetInstance().GetRefundResultText(errcode) + $"({info.Order_Status})");
  371. // }
  372. // }
  373. // else
  374. // {
  375. // BPALog.WriteLog("调用Refundrefuc方法:"+ resultIng.msg);
  376. // return new JsonResult(false);
  377. // }
  378. // }
  379. // else
  380. // {
  381. // BPALog.WriteLog("状态为:" + orderinfo.TradeState + ",此订单无法退款");
  382. // return new JsonResult(false);
  383. // }
  384. // }
  385. // else
  386. // {
  387. // throw Oops.Oh("退款失败,没有找到订单");
  388. // }
  389. // }
  390. // else
  391. // {
  392. // throw Oops.Oh("无效订单");
  393. // }
  394. //}
  395. }
  396. /**
  397. * 生成时间戳,标准北京时间,时区为东八区,自1970年1月1日 0点0分0秒以来的秒数
  398. * @return 时间戳
  399. */
  400. public static string GenerateTimeStamp()
  401. {
  402. TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
  403. return Convert.ToInt64(ts.TotalSeconds).ToString();
  404. }
  405. /**
  406. * 生成随机串,随机串包含字母或数字
  407. * @return 随机串
  408. */
  409. public static string GenerateNonceStr()
  410. {
  411. return null;
  412. //RandomGenerator randomGenerator = new RandomGenerator();
  413. //return randomGenerator.GetRandomUInt().ToString();
  414. }
  415. /**
  416. * 参数数组转换为url格式
  417. * @param map 参数名与参数值的映射表
  418. * @return URL字符串
  419. */
  420. private string ToUrlParams(SortedDictionary<string, object> map)
  421. {
  422. string buff = "";
  423. foreach (KeyValuePair<string, object> pair in map)
  424. {
  425. buff += pair.Key + "=" + pair.Value + "&";
  426. }
  427. buff = buff.Trim('&');
  428. return buff;
  429. }
  430. /// <summary>
  431. /// 付款码支付
  432. /// </summary>
  433. /// <param name="input"></param>
  434. /// <returns></returns>
  435. /// <exception cref="NotImplementedException"></exception>
  436. public async Task<IActionResult> MicropayPay(MicropayInput input)
  437. {
  438. return null;
  439. //var config = ApolloApplication.Configs.Find(x => x.Key == input.AppId);
  440. //WechatConfigOptions opt = config.WechatConfig;
  441. //WechatPayConfigOptions PayOpt = config.WechatPayConfig;
  442. ////PayOpt.CertificatePassword = PayOpt.MchId;
  443. ////PayOpt.Certificate = SystemConfig.CertificateAddress;
  444. //string body = "商品购买";
  445. //string out_trade_no = string.Empty;
  446. //int total_fee =0;
  447. //if (!string.IsNullOrEmpty(input.OrderId))
  448. //{
  449. // MyDataResult<OrderAllInfoDto> res = OrderWeApiHepler.GetOrderInfoByNum<OrderAllInfoDto>(config.WechatConfig.OrderAddress, input.OrderId);
  450. // if (!res.isSuccess)
  451. // {
  452. // throw Oops.Oh("没有找到订单信息!");
  453. // }
  454. // total_fee = Convert.ToInt32(input.TotalFee * 100);
  455. // out_trade_no = input.OrderId;
  456. // //OrderAllInfoDto orderinfo = res.data;
  457. //}
  458. //else
  459. //{
  460. // out_trade_no = WechatCommon.GenerateOutTradeNo();
  461. // total_fee = 1;
  462. //}
  463. //var request = new WeChatPayMicroPayRequest
  464. //{
  465. // Body = body,
  466. // OutTradeNo = out_trade_no,
  467. // TotalFee = total_fee,
  468. // SpBillCreateIp = config.WechatConfig.MchIP,
  469. // AuthCode = input.AuthCode
  470. //};
  471. //var response = await _client.ExecuteAsync(request, PayOpt);
  472. //if (response.ReturnCode == WeChatPayCode.Success && response.ResultCode == WeChatPayCode.Success)
  473. //{
  474. // return new JsonResult(new MicropayOutput { Code=200, Msg ="交易成功", Success =true });
  475. //}
  476. //return new JsonResult(new MicropayOutput { Code = 500, Msg = $"交易失败-ReturnMsg:{response.ReturnMsg},ErrCodeDes:{response.ErrCodeDes}", Success = false });
  477. }
  478. public string NativePay(decimal amount, string orderid)
  479. {
  480. throw new NotImplementedException();
  481. }
  482. }
  483. }