PaymentService.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. using System.Collections.Concurrent;
  2. using System.Text;
  3. using System;
  4. using System.Web;
  5. using WingServerCommon.Service;
  6. using WingInterfaceLibrary.Interface;
  7. using System.Threading.Tasks;
  8. using WingInterfaceLibrary.DTO.Payment;
  9. using WingInterfaceLibrary.Request.Payment;
  10. using WingInterfaceLibrary.Result;
  11. using System.Collections.Generic;
  12. using WingInterfaceLibrary.Interface.DBInterface;
  13. using WingInterfaceLibrary.DB.Request;
  14. using WingServerCommon.Log;
  15. using WingPaymentService.Common;
  16. using WingInterfaceLibrary.Enum;
  17. using WingInterfaceLibrary.Request;
  18. using WingPaymentService.Common.WeChat;
  19. using WingInterfaceLibrary.Request.Courses;
  20. namespace WingPaymentService.Service
  21. {
  22. public class PaymentService : JsonRpcService, IPaymentService
  23. {
  24. private IEducationService _educationService;
  25. private IAuthenticationService _authenticationService;
  26. private IPaymentDBService _paymentDBService;
  27. private AlipayCommon _alipayCommon;
  28. private WeChatCommon _weChatCommon;
  29. private ConcurrentDictionary<string, string> _recordRelation = new();
  30. public override void Load(JsonRpcClientPool jsonRpcClientPool)
  31. {
  32. _alipayCommon = new AlipayCommon();
  33. _weChatCommon = new WeChatCommon();
  34. base.Load(jsonRpcClientPool);
  35. _paymentDBService = GetProxy<IPaymentDBService>();
  36. _authenticationService = GetProxy<IAuthenticationService>();
  37. _educationService = GetProxy<IEducationService>();
  38. }
  39. /// <summary>
  40. /// 购买接口
  41. /// </summary>
  42. /// <param name="request"></param>
  43. /// <returns></returns>
  44. public async Task<CreateOrderResultDTO> CreateOrderAsync(CreateOrderRequest request)
  45. {
  46. var dbRequest = new CreateOrderDBRequest();
  47. dbRequest.OrderAmount = request.OrderAmount;
  48. dbRequest.OrderTitle = request.OrderTitle;
  49. dbRequest.OrderType = request.OrderType;
  50. if (string.IsNullOrWhiteSpace(request.OrderUserCode))
  51. {
  52. request.OrderUserCode = await ValidateTokenAsync(request.Token);
  53. if (string.IsNullOrWhiteSpace(request.OrderUserCode))
  54. {
  55. ThrowCustomerException(CustomerRpcCode.UserNotFind, "UserNotFind");
  56. }
  57. }
  58. dbRequest.OrderUserCode = request.OrderUserCode;
  59. dbRequest.PayStatus = PayStatusEnum.NoPay;
  60. dbRequest.PayTime = DateTime.UtcNow;
  61. dbRequest.ProductCode = request.ProductCode;
  62. dbRequest.TPORderCode = request.TPORderCode;
  63. return await _paymentDBService.CreateOrderAsync(dbRequest);
  64. }
  65. /// <summary>
  66. /// 订单详情接口
  67. /// </summary>
  68. /// <param name="request"></param>
  69. /// <returns></returns>
  70. public async Task<OrderDetailDTO> GetOrderDetailAsync(GetOrderDetailRequest request)
  71. {
  72. return await _paymentDBService.GetOrderDetailAsync(new GetOrderDetailDBRequest { OrderCode = request.OrderCode });
  73. }
  74. /// <summary>
  75. /// 订单列表接口
  76. /// </summary>
  77. /// <param name="request"></param>
  78. /// <returns></returns>
  79. public async Task<PageResult<OrderDetailDTO>> GetOrderPagesAsync(GetOrderPagesRequest request)
  80. {
  81. return await _paymentDBService.GetOrderPagesAsync(new GetOrderPagesDBRequest
  82. {
  83. CurrentPage = request.PageIndex,
  84. PageSize = request.PageSize,
  85. KeyWord = request.KeyWord
  86. });
  87. }
  88. /// <summary>
  89. /// 发起支付请求
  90. /// </summary>
  91. /// <param name="request"></param>
  92. /// <returns></returns>
  93. public async Task<PayInfoDTO> GoToPayAsync(GoToPayRequest request)
  94. {
  95. if (!string.IsNullOrWhiteSpace(request.OrderCode))
  96. {
  97. var orderInfo = await _paymentDBService.GetOrderDetailAsync(new GetOrderDetailDBRequest { OrderCode = request.OrderCode });
  98. if (orderInfo != null && orderInfo.OrderAmount > 0)
  99. {
  100. string recordCode = Guid.NewGuid().ToString("N");
  101. (string payUrl, string recordContent) payContent = (string.Empty, string.Empty);
  102. switch (request.PayType)
  103. {
  104. case PayTypeEnum.Alipay_PAGE:
  105. _recordRelation.AddOrUpdate(recordCode, request.OrderCode, (a,b)=> { return request.OrderCode; });
  106. payContent = _alipayCommon.AlipayPagePay(orderInfo.OrderTitle, orderInfo.OrderAmount, recordCode);
  107. break;
  108. case PayTypeEnum.Alipay_WAP:
  109. _recordRelation.AddOrUpdate(recordCode, request.OrderCode, (a,b)=> { return request.OrderCode; });
  110. payContent = _alipayCommon.AlipayWapPay(orderInfo.OrderTitle, orderInfo.OrderAmount, recordCode);
  111. break;
  112. case PayTypeEnum.WeChat_PAGE:
  113. payContent = await _weChatCommon.GetWebPayUrlAsync(orderInfo.OrderTitle, orderInfo.OrderAmount, orderInfo.Code, recordCode, string.Empty, string.Empty);
  114. break;
  115. case PayTypeEnum.WeChat_WAP:
  116. payContent = await _weChatCommon.GetWapPayUrlAsync(orderInfo.OrderTitle, orderInfo.OrderAmount, orderInfo.Code, recordCode, string.Empty, string.Empty);
  117. break;
  118. }
  119. await CreatePayStatusReq(recordCode, payContent.recordContent);
  120. if (!string.IsNullOrWhiteSpace(payContent.payUrl))
  121. {
  122. return new PayInfoDTO
  123. {
  124. IsSuccess = true,
  125. PayUrl = payContent.payUrl
  126. };
  127. }
  128. }
  129. }
  130. return new PayInfoDTO
  131. {
  132. IsSuccess = false,
  133. PayUrl = string.Empty
  134. };
  135. }
  136. /// <summary>
  137. /// 支付后回调
  138. /// </summary>
  139. /// <param name="src">来源</param>
  140. /// <param name="request">请求信息</param>
  141. /// <returns></returns>
  142. public async Task<PayCallbackDTO> PayCallbackAsync(string src, string request)
  143. {
  144. bool isSuccess = false;
  145. try
  146. {
  147. switch (src)
  148. {
  149. case "alipay":
  150. isSuccess = await AlipayCallback(request);
  151. break;
  152. case "wechat":
  153. isSuccess = await WeChatCallback(request);
  154. break;
  155. }
  156. return new PayCallbackDTO { IsSuccess = isSuccess };
  157. }
  158. catch (Exception ex)
  159. {
  160. Logger.WriteLineError($"PayCallback error {ex}");
  161. return new PayCallbackDTO { IsSuccess = false };
  162. }
  163. }
  164. /// <summary>
  165. /// 支付宝回调
  166. /// </summary>
  167. /// <param name="request"></param>
  168. /// <returns></returns>
  169. private async Task<bool> AlipayCallback(string request)
  170. {
  171. Dictionary<string, string> keyValuePairs = new Dictionary<string, string>();
  172. var uriParams = HttpUtility.UrlDecode(request);
  173. if (uriParams != null)
  174. {
  175. var paramsArr = uriParams.Split('&');
  176. foreach (string param in paramsArr)
  177. {
  178. var keyValue = param.Split('=');
  179. var key = keyValue[0];
  180. var value = keyValue.Length > 1 ? keyValue[1] : string.Empty;
  181. keyValuePairs.Add(key, value);
  182. }
  183. }
  184. var recordCode = keyValuePairs.GetValueOrDefault("out_trade_no");
  185. _recordRelation.TryGetValue(recordCode, out string orderCode);
  186. var totalAmount = keyValuePairs.GetValueOrDefault("total_amount");
  187. var payStatus = keyValuePairs.GetValueOrDefault("trade_status") == "TRADE_SUCCESS" ? WingInterfaceLibrary.Enum.PayStatusEnum.Paid : WingInterfaceLibrary.Enum.PayStatusEnum.NoPay;
  188. var payTime = DateTime.UtcNow;
  189. return await PayRecordWriteRes(orderCode, recordCode, totalAmount, payStatus, payTime, request);
  190. }
  191. /// <summary>
  192. /// 微信回调
  193. /// </summary>
  194. /// <param name="request"></param>
  195. /// <returns></returns>
  196. private async Task<bool> WeChatCallback(string request)
  197. {
  198. //转换数据格式, 取消签名校验,签名调用方自己调用,兼容支付和退款
  199. WxPayData data = new WxPayData();
  200. var paramValues = data.FromXml(request, false);//回调不用签名校验
  201. var payOutTradeNo = paramValues.GetValueOrDefault("out_trade_no")?.ToString();
  202. var totalAmount = (double.Parse(paramValues.GetValueOrDefault("total_fee")?.ToString()) / 100).ToString("0.00");
  203. var payRecordCode = paramValues.GetValueOrDefault("pay_record_code")?.ToString();
  204. var payStatus = paramValues.GetValueOrDefault("result_code")?.ToString() == "SUCCESS" ? PayStatusEnum.Paid : PayStatusEnum.NoPay;
  205. var payTime = DateTime.UtcNow;
  206. return await PayRecordWriteRes(payOutTradeNo, payRecordCode, totalAmount, payStatus, payTime, request);
  207. }
  208. /// <summary>
  209. /// 支付记录
  210. /// </summary>
  211. /// <param name="orderCode">订单编码</param>
  212. /// <param name="payRecordCode">支付记录编码</param>
  213. /// <param name="totalAmount">支付金额</param>
  214. /// <param name="payStatus">支付状态</param>
  215. /// <param name="payTime">支付时间</param>
  216. /// <param name="request">支付原始请求</param>
  217. /// <returns></returns>
  218. private async Task<bool> PayRecordWriteRes(string orderCode, string payRecordCode, string totalAmount, PayStatusEnum payStatus, DateTime payTime, string res)
  219. {
  220. if (string.IsNullOrWhiteSpace(payRecordCode))
  221. {
  222. Logger.WriteLineError("Pay record code is empty");
  223. return false;
  224. }
  225. await UpdatePayStatusRes(payRecordCode, payStatus, res, payTime);
  226. if (string.IsNullOrWhiteSpace(orderCode))
  227. {
  228. Logger.WriteLineError("Order code is empty");
  229. return false;
  230. }
  231. if (payStatus == PayStatusEnum.Paid)
  232. {
  233. return await UpdateOrderStatus(orderCode, payStatus, totalAmount, payTime);
  234. }
  235. return false;
  236. }
  237. private async Task<bool> UpdateOrderStatus(string orderCode, PayStatusEnum payStatus, string totalAmount, DateTime payTime)
  238. {
  239. var orderInfo = await _paymentDBService.GetOrderDetailAsync(new GetOrderDetailDBRequest { OrderCode = orderCode });
  240. if (orderInfo.OrderAmount.ToString("0.00") != totalAmount)
  241. {
  242. Logger.WriteLineError($"OrderCode:{orderInfo.Code} pay status {payStatus}, total amount error, order amount {orderInfo.OrderAmount.ToString("0.00")} paid amount {totalAmount}");
  243. return false;
  244. }
  245. var result = await _paymentDBService.UpdateOrderAsync(new UpdateOrderDBRequest
  246. {
  247. OrderCode = orderCode,
  248. PayStatus = payStatus
  249. });
  250. await _educationService.PaymentCallbackAsync(new PaymentCallbackRequest
  251. {
  252. PaymentOrderCode = orderCode,
  253. PayStatus = payStatus,
  254. PayTime = payTime
  255. });
  256. return result.IsSuccess;
  257. }
  258. private async Task<CreatePaymentRecordResultDTO> CreatePayStatusReq(string paymentRecordCode, string payReqContent)
  259. {
  260. var createRecordInfo = new CreatePaymentRecordDBRequest();
  261. createRecordInfo.PayReqContent = payReqContent;
  262. return await _paymentDBService.CreatePaymentRecordAsync(createRecordInfo);
  263. }
  264. private async Task<UpdatePaymentRecordResultDTO> UpdatePayStatusRes(string paymentRecordCode, PayStatusEnum payStatus, string payResContent, DateTime payTime)
  265. {
  266. var updateRecordInfo = new UpdatePaymentRecordDBRequest();
  267. updateRecordInfo.Code = paymentRecordCode;
  268. updateRecordInfo.PayTime = payTime;
  269. updateRecordInfo.PayStatus = payStatus;
  270. updateRecordInfo.PayResContent = payResContent;
  271. return await _paymentDBService.UpdatePaymentRecordAsync(updateRecordInfo);
  272. }
  273. /// <summary>
  274. /// 客户端用户验证
  275. /// </summary>
  276. /// <param name="request"></param>
  277. /// <returns>User Code</returns>
  278. private async Task<string> ValidateTokenAsync(string token)
  279. {
  280. var request = new TokenRequest() { Token = token };
  281. var result = await _authenticationService.GetTokenAsync(request);
  282. return result.ClientId; //return User code
  283. }
  284. private void ThrowCustomerException(CustomerRpcCode customerRpcCodeEnum, string msg)
  285. {
  286. ThrowRpcException((int)customerRpcCodeEnum, msg);
  287. }
  288. }
  289. }