团餐订单
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.

WechatPayService.cs 23 KiB

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