344 lines
16 KiB
C#
344 lines
16 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.IO;
|
||
using System.Linq;
|
||
using System.Net.Http;
|
||
using System.Text;
|
||
using System.Threading.Tasks;
|
||
using Microsoft.AspNetCore.Authorization;
|
||
using Microsoft.AspNetCore.Http;
|
||
using Microsoft.AspNetCore.Mvc;
|
||
using Microsoft.Extensions.Logging;
|
||
using New_College.Common;
|
||
using New_College.IRepository;
|
||
using New_College.IServices;
|
||
using New_College.Model;
|
||
using New_College.Model.Models;
|
||
using New_College.Model.ViewModels;
|
||
|
||
namespace New_College.Api.Controllers.Front
|
||
{
|
||
[Route("api/front/[controller]/[action]")]
|
||
[ApiController]
|
||
[Authorize]
|
||
public class WeixinPayController : ControllerBase
|
||
{
|
||
private readonly IV_OrderInfoRepository v_OrderInfoRepository;
|
||
private readonly IV_CustomerInfoRepository v_CustomerInfoRepository;
|
||
private readonly IV_VipCardInfoRepository v_VipCardInfoRepository;
|
||
private readonly IV_VipCardTypeRepository v_VipCardTypeRepository;
|
||
|
||
private readonly ILogger<V_OrderInfo> logger;
|
||
public WeixinPayController(IV_OrderInfoRepository IV_OrderInfoRepository
|
||
, IV_CustomerInfoRepository IV_CustomerInfoRepository
|
||
, IV_VipCardInfoRepository IV_VipCardInfoRepository
|
||
, IV_VipCardTypeRepository IV_VipCardTypeRepository
|
||
, ILogger<V_OrderInfo> loggers)
|
||
{
|
||
v_OrderInfoRepository = IV_OrderInfoRepository;
|
||
v_CustomerInfoRepository = IV_CustomerInfoRepository;
|
||
v_VipCardInfoRepository = IV_VipCardInfoRepository;
|
||
v_VipCardTypeRepository = IV_VipCardTypeRepository;
|
||
logger = loggers;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 下订单
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
[HttpPost]
|
||
public async Task<WeixinPayResult> UnifiedOrder(UnifiedOrderQuery query)
|
||
{
|
||
var resp = new WeixinPayResult();
|
||
try
|
||
{
|
||
Random rd = new Random();
|
||
//HttpContext.GetClientUserIp();
|
||
//外部商户订单号
|
||
var payNum = DateTime.Now.ToString("yyyyMMddHHmmss") + rd.Next(0, 1000).ToString().PadLeft(3, '0');
|
||
var data = new WxPayData();
|
||
data.SetValue("body", "壹志愿好帮手VIP购买");
|
||
data.SetValue("out_trade_no", payNum);
|
||
data.SetValue("detail", "志愿好帮手VIP卡购买");
|
||
data.SetValue("total_fee", Convert.ToInt32(query.total_fee * 100));
|
||
data.SetValue("time_start", DateTime.Now.ToString("yyyyMMddHHmmss"));
|
||
//data.SetValue("time_expire", DateTime.Now.AddMinutes(10).ToString("yyyyMMddHHmmss"));
|
||
data.SetValue("notify_url", WeixinConfig.NotifyUrl);
|
||
//data.SetValue("goods_tag", "test");
|
||
data.SetValue("trade_type", "JSAPI");
|
||
data.SetValue("openid", query.Phone);
|
||
//可以将用户Id和订单Id同时封装在attach中
|
||
data.SetValue("attach", string.Format("{0}|{1}", query.Phone, payNum));
|
||
WxPayData result = UnifiedOrder(data);
|
||
if (!result.IsSet("appid") || !result.IsSet("prepay_id") || result.GetValue("prepay_id").ToString() == "")
|
||
{
|
||
resp.err_code_des = result.GetValue("err_code_des").ToString();
|
||
resp.err_code = result.GetValue("err_code").ToString();
|
||
resp.result_code = result.GetValue("result_code").ToString();
|
||
return resp;
|
||
}
|
||
else
|
||
{
|
||
resp.nonce_str = result.GetValue("nonce_str").ToString();
|
||
resp.appid = result.GetValue("appid").ToString();
|
||
resp.mchi_id = result.GetValue("mch_id").ToString();
|
||
resp.result_code = result.GetValue("result_code").ToString();
|
||
resp.prepay_id = "prepay_id=" + result.GetValue("prepay_id").ToString();
|
||
//resp.code_url = result.GetValue("code_url").ToString();
|
||
resp.trade_type = result.GetValue("trade_type").ToString();
|
||
var signType = "MD5";
|
||
var timeStamp = ((DateTime.Now.Ticks - TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1)).Ticks) / 10000).ToString();
|
||
WxPayData applet = new WxPayData();
|
||
applet.SetValue("appId", resp.appid);
|
||
applet.SetValue("nonceStr", resp.nonce_str);
|
||
applet.SetValue("package", resp.prepay_id);
|
||
applet.SetValue("signType", signType);
|
||
applet.SetValue("timeStamp", timeStamp);
|
||
resp.sign = applet.MakeSign();
|
||
resp.timeStamp = timeStamp;
|
||
//NLogHelper.WriteInfo("Signurl" + Newtonsoft.Json.JsonConvert.SerializeObject(resp), DevAuthorNameEnum.Michael, false);
|
||
// WebHookHelper.WebHookmarkdownSend("Signurl" + Newtonsoft.Json.JsonConvert.SerializeObject(resp));
|
||
var customer = await v_CustomerInfoRepository.Query(x => x.OpenId == query.Phone && x.IsDelete == false);
|
||
if (customer.Count <= 0)
|
||
return new WeixinPayResult() { err_code_des = "用户出现错误" };
|
||
//添加一张卡
|
||
var cardtype = await v_VipCardTypeRepository.QueryById(query.CardTypeId);
|
||
if (cardtype == null)
|
||
return new WeixinPayResult() { err_code_des = "卡出现错误" };
|
||
var code = RadomHelper.GetGuid();
|
||
var addcard = await v_VipCardInfoRepository.Add(new V_VipCardInfo()
|
||
{
|
||
CardTypeId = query.CardTypeId,
|
||
CardTypeName = cardtype.Name,
|
||
Code = code,
|
||
IsBind = 1,
|
||
Money = query.total_fee,
|
||
Day = cardtype.Day,
|
||
EndTime = DateTime.Now.AddDays(cardtype.Day)
|
||
});
|
||
var baseResult = await v_OrderInfoRepository.Add(new V_OrderInfo
|
||
{
|
||
out_trade_no = payNum,
|
||
// PayType = 2,
|
||
CardTypeId = query.CardTypeId,
|
||
// Status = 1,
|
||
CustomerId = customer.FirstOrDefault().Id,
|
||
Price = query.total_fee,
|
||
PayPrice = query.total_fee,
|
||
Name = "壹志愿好帮手VIP购买",
|
||
CardNo = code,
|
||
CardId = addcard
|
||
});
|
||
if (baseResult > 0)
|
||
{
|
||
//订单号
|
||
applet.SetValue("orderid", baseResult);
|
||
resp.orderid = baseResult.ToString();
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
//WebHookHelper.WebHookmarkdownSend(ex.ToString());
|
||
|
||
//NLogHelper.WriteError(ex, DevAuthorNameEnum.Michael, false);
|
||
}
|
||
return resp;
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 支付返回值
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
[HttpPost]
|
||
public async Task<IActionResult> PayNotify()
|
||
{
|
||
logger.LogInformation("开始回调PayNotify");
|
||
|
||
WxPayData res = new WxPayData();
|
||
|
||
//接收从微信后台POST过来的数据
|
||
//转换数据格式并验证签名
|
||
WxPayData data = new WxPayData();
|
||
using (var reader = new StreamReader(Request.Body))
|
||
{
|
||
var builder = reader.ReadToEnd();
|
||
//WebHookHelper.WebHookmarkdownSend(builder.ToString());
|
||
try
|
||
{
|
||
data.FromXml(builder.ToString());
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
//若签名错误,则立即返回结果给微信支付后台
|
||
|
||
res.SetValue("return_code", "FAIL");
|
||
res.SetValue("return_msg", ex.Message);
|
||
// NLogHelper.WriteError("Sign check error : " + res.ToXml(), DevAuthorNameEnum.Michael, false);
|
||
//WebHookHelper.WebHookmarkdownSend(string.Format("return_msg:{0}", ex.Message));
|
||
return Content(res.ToXml(), "text/xml");
|
||
}
|
||
|
||
};
|
||
// WebHookHelper.WebHookmarkdownSend("Check sign success");
|
||
//检查支付结果中transaction_id是否存在
|
||
if (!data.IsSet("transaction_id"))
|
||
{
|
||
//若transaction_id不存在,则立即返回结果给微信支付后台
|
||
res.SetValue("return_code", "FAIL");
|
||
res.SetValue("return_msg", "支付结果中微信订单号不存在");
|
||
//WebHookHelper.WebHookmarkdownSend("支付结果中微信订单号不存在");
|
||
return Content(res.ToXml(), "text/xml");
|
||
}
|
||
// 执行订单状态操作
|
||
string out_trade_no = data.GetValue("out_trade_no").ToString();
|
||
logger.LogInformation("开始回调PayNotify"+ out_trade_no);
|
||
var info = await v_OrderInfoRepository.Query(x => x.out_trade_no == out_trade_no);
|
||
if (info.Count <= 0)
|
||
{
|
||
res.SetValue("return_code", "FAIL");
|
||
res.SetValue("return_msg", "在自有平台未找到该订单号");
|
||
//WebHookHelper.WebHookmarkdownSend("支付结果中微信订单号不存在");
|
||
return Content(res.ToXml(), "text/xml");
|
||
}
|
||
var oneinfo = info.FirstOrDefault();
|
||
oneinfo.Status = EnumOrderType.payoff;
|
||
var rep = await v_OrderInfoRepository.Update(oneinfo);
|
||
if (rep)
|
||
{
|
||
//修改用户信息 修改为VIp
|
||
var customerinfo = await v_CustomerInfoRepository.QueryById(oneinfo.CustomerId);
|
||
customerinfo.IsVIP = true;
|
||
customerinfo.VipCode = oneinfo.CardNo;
|
||
await v_CustomerInfoRepository.Update(customerinfo);
|
||
//支付成功后根据用户code找到对应用户修改vip 状态
|
||
res.SetValue("return_code", "SUCCESS");
|
||
res.SetValue("return_msg", "OK");
|
||
}
|
||
else
|
||
{
|
||
// string attach = data.GetValue("attach").ToString();
|
||
// WebHookHelper.WebHookmarkdownSend(attach);
|
||
res.SetValue("return_code", "FAIL");
|
||
res.SetValue("return_msg", "在自有平台未找到该订单号");
|
||
}
|
||
|
||
return Content(res.ToXml(), "text/xml");
|
||
}
|
||
|
||
|
||
/**
|
||
*
|
||
* 统一下单
|
||
* @param WxPaydata inputObj 提交给统一下单API的参数
|
||
* @param int timeOut 超时时间
|
||
* @throws WePayException
|
||
* @return 成功时返回,其他抛异常
|
||
*/
|
||
public static WxPayData UnifiedOrder(WxPayData inputObj, int timeOut = 60)
|
||
{
|
||
string url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
|
||
//检测必填参数
|
||
if (!inputObj.IsSet("out_trade_no"))
|
||
{
|
||
throw new Exception("缺少统一支付接口必填参数out_trade_no!");
|
||
}
|
||
else if (!inputObj.IsSet("body"))
|
||
{
|
||
throw new Exception("缺少统一支付接口必填参数body!");
|
||
}
|
||
else if (!inputObj.IsSet("total_fee"))
|
||
{
|
||
throw new Exception("缺少统一支付接口必填参数total_fee!");
|
||
}
|
||
else if (!inputObj.IsSet("trade_type"))
|
||
{
|
||
throw new Exception("缺少统一支付接口必填参数trade_type!");
|
||
}
|
||
|
||
//关联参数
|
||
if (inputObj.GetValue("trade_type").ToString() == "JSAPI" && !inputObj.IsSet("openid"))
|
||
{
|
||
throw new Exception("统一支付接口中,缺少必填参数openid!trade_type为JSAPI时,openid为必填参数!");
|
||
}
|
||
if (inputObj.GetValue("trade_type").ToString() == "NATIVE" && !inputObj.IsSet("product_id"))
|
||
{
|
||
throw new Exception("统一支付接口中,缺少必填参数product_id!trade_type为JSAPI时,product_id为必填参数!");
|
||
}
|
||
|
||
//异步通知url未设置,则使用配置文件中的url
|
||
if (!inputObj.IsSet("notify_url"))
|
||
{
|
||
inputObj.SetValue("notify_url", WeixinConfig.NotifyUrl);//异步通知url
|
||
}
|
||
|
||
inputObj.SetValue("appid", WeixinConfig.Appid);//公众账号ID
|
||
inputObj.SetValue("mch_id", WeixinConfig.MCHID);//商户号
|
||
inputObj.SetValue("spbill_create_ip", WePayConfig.IP);//终端ip
|
||
inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串
|
||
|
||
//签名
|
||
inputObj.SetValue("sign", inputObj.MakeSign());
|
||
string xml = inputObj.ToXml();
|
||
|
||
var start = DateTime.Now;
|
||
|
||
// Log.Info("XcxPayApi", "UnfiedOrder request : " + xml);
|
||
string response = HttpPost(xml, url, "application/xml", timeOut);
|
||
//Log.Info("XcxPayApi", "UnfiedOrder response : " + response);
|
||
// WebHookHelper.WebHookmarkdownSend(response);
|
||
|
||
var end = DateTime.Now;
|
||
int timeCost = (int)((end - start).TotalMilliseconds);
|
||
|
||
WxPayData result = new WxPayData();
|
||
result.FromXml(response);
|
||
// ReportCostTime(url, timeCost, result);//测速上报网络不好时使用
|
||
return result;
|
||
}
|
||
/// <summary>
|
||
/// 生成随机数
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
public static string GenerateNonceStr()
|
||
{
|
||
return Guid.NewGuid().ToString().Replace("-", "");
|
||
}
|
||
/// <summary>
|
||
/// POST请求
|
||
/// </summary>
|
||
/// <param name="postData"></param>
|
||
/// <param name="url"></param>
|
||
/// <param name="contentType">application/xml、application/json、application/text、application/x-www-form-urlencoded</param>
|
||
/// <param name="timeOut"></param>
|
||
/// <param name="headers"></param>
|
||
/// <returns></returns>
|
||
public static string HttpPost(string postData, string url, string contentType = null, int timeOut = 30, Dictionary<string, string> headers = null)
|
||
{
|
||
postData = postData ?? "";
|
||
|
||
var httpClientHandler = new HttpClientHandler
|
||
{
|
||
ServerCertificateCustomValidationCallback = (message, certificate2, arg3, arg4) => true
|
||
};
|
||
using (HttpClient httpClient = new HttpClient(httpClientHandler))
|
||
{
|
||
if (headers != null)
|
||
{
|
||
foreach (var header in headers)
|
||
httpClient.DefaultRequestHeaders.Add(header.Key, header.Value);
|
||
}
|
||
using (HttpContent client = new StringContent(postData, Encoding.UTF8))
|
||
{
|
||
if (contentType != null)
|
||
client.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(contentType);
|
||
|
||
HttpResponseMessage response = httpClient.PostAsync(url, client).Result;
|
||
return response.Content.ReadAsStringAsync().Result;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|