PaymentService.cs 13 KB

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