|
|
using Aop.Api;
|
|
|
using Aop.Api.Request;
|
|
|
using Aop.Api.Response;
|
|
|
using NLog;
|
|
|
using POSV.ShoppingCart;
|
|
|
using POSV.Utils;
|
|
|
using System;
|
|
|
using System.Collections.Generic;
|
|
|
using System.IO;
|
|
|
using System.Linq;
|
|
|
using System.Security.Cryptography.X509Certificates;
|
|
|
using System.Text;
|
|
|
|
|
|
namespace POSV.PayApi
|
|
|
{
|
|
|
public class WeixinApi
|
|
|
{
|
|
|
private static Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
|
|
|
|
|
private static Dictionary<AccountTypeEnum, Tuple<string, string, string, string, string>> clientDic = new Dictionary<AccountTypeEnum, Tuple<string, string, string, string, string>>();
|
|
|
|
|
|
/// <summary>
|
|
|
/// 获取微信账户信息
|
|
|
/// </summary>
|
|
|
/// <param name="accountType"></param>
|
|
|
/// <returns></returns>
|
|
|
public static Tuple<string, string, string, string, string> GetWeixinInfo(AccountTypeEnum accountType)
|
|
|
{
|
|
|
Tuple<string, string, string, string, string> result = null;
|
|
|
switch (accountType)
|
|
|
{
|
|
|
case AccountTypeEnum.门店:
|
|
|
{
|
|
|
if (!clientDic.ContainsKey(AccountTypeEnum.门店))
|
|
|
{
|
|
|
var payMode = OrderUtils.GetPayMode("05");
|
|
|
var parameter = payMode.Body;
|
|
|
string appId = parameter.ContainsKey("appid") ? parameter["appid"].ToString() : "";
|
|
|
string mchId = parameter.ContainsKey("mchid") ? parameter["mchid"].ToString() : "";
|
|
|
string subMchId = parameter.ContainsKey("submchid") ? parameter["submchid"].ToString() : "";
|
|
|
string key = parameter.ContainsKey("appsecret") ? parameter["appsecret"].ToString() : "";
|
|
|
string certText = payMode.CertText;
|
|
|
|
|
|
clientDic.Add(AccountTypeEnum.门店, new Tuple<string, string, string, string, string>(appId, mchId, subMchId, key, certText));
|
|
|
}
|
|
|
return clientDic[AccountTypeEnum.门店];
|
|
|
}
|
|
|
break;
|
|
|
}
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 微信退款申请
|
|
|
/// </summary>
|
|
|
/// <param name="accountType"></param>
|
|
|
/// <param name="tradeNo"></param>
|
|
|
/// <param name="totalAmount"></param>
|
|
|
/// <param name="refundNo"></param>
|
|
|
/// <param name="reason"></param>
|
|
|
/// <returns></returns>
|
|
|
public static Tuple<bool, string, OrderRefundResult> RefundByWeixin(AccountTypeEnum accountType, string tradeNo, decimal totalAmount, decimal refundAmount, string refundNo, string reason)
|
|
|
{
|
|
|
lock (Global.Instance.SyncLock)
|
|
|
{
|
|
|
try
|
|
|
{
|
|
|
logger.Info("订单[{0}]开始发起微信退款......", tradeNo);
|
|
|
|
|
|
string nonceStr = "x86" + ObjectId.GenerateNewStringId();
|
|
|
string totalAmountStr = Convert.ToInt32(totalAmount * 100) + "";
|
|
|
string RefundAmountStr = Convert.ToInt32(refundAmount * 100) + "";
|
|
|
var accountInfo = GetWeixinInfo(accountType);
|
|
|
if(accountInfo != null)
|
|
|
{
|
|
|
if (!string.IsNullOrEmpty(accountInfo.Item5))
|
|
|
{
|
|
|
string appId = accountInfo.Item1;
|
|
|
string mchId = accountInfo.Item2;
|
|
|
string subMchId = accountInfo.Item3;
|
|
|
string appSecret = accountInfo.Item4;
|
|
|
string certText = accountInfo.Item5;
|
|
|
|
|
|
WxPayV3OrderRefundRequestData request = new WxPayV3OrderRefundRequestData(appId, mchId, subMchId, appSecret, nonceStr, null, tradeNo, refundNo, totalAmountStr, RefundAmountStr, "SNY", reason);
|
|
|
byte[] certDataArray = Convert.FromBase64String(certText);
|
|
|
|
|
|
X509Certificate2 cert = new X509Certificate2(certDataArray, mchId);
|
|
|
var refundResult = WxPayV3.OrderRefund(request, cert);
|
|
|
logger.Info("订单[{0}]微信退款请求结果:{1}", tradeNo, JsonUtils.Serialize(refundResult));
|
|
|
if ("SUCCESS".Equals(refundResult.return_code))
|
|
|
{
|
|
|
if ("SUCCESS".Equals(refundResult.result_code))
|
|
|
{
|
|
|
return new Tuple<bool, string, OrderRefundResult>(true, null, refundResult);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
return new Tuple<bool, string, OrderRefundResult>(false, refundResult.err_code + refundResult.err_code_des, null);
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
return new Tuple<bool, string, OrderRefundResult>(false, refundResult.return_msg, null);
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
return new Tuple<bool, string, OrderRefundResult>(false, "未配置微信支付证书(退款必须)", null);
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
return new Tuple<bool, string, OrderRefundResult>(false, "未配置微信支付参数", null);
|
|
|
}
|
|
|
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
logger.Error(ex, "订单[{0}]调用微信退款接口异常", tradeNo);
|
|
|
return new Tuple<bool, string, OrderRefundResult>(false, "调用微信退款接口异常", null);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 微信退款结果查询
|
|
|
/// </summary>
|
|
|
/// <param name="tradeNo"></param>
|
|
|
/// <returns></returns>
|
|
|
public static Tuple<bool, string, RefundQueryResult> QueryWeixinRefundResult(AccountTypeEnum accountType, string tradeNo, string refundNo)
|
|
|
{
|
|
|
lock (Global.Instance.SyncLock)
|
|
|
{
|
|
|
try
|
|
|
{
|
|
|
logger.Info("订单[{0}]开始发起微信退款查询请求......", tradeNo);
|
|
|
string nonceStr = "x86" + ObjectId.GenerateNewStringId();
|
|
|
var accountInfo = GetWeixinInfo(accountType);
|
|
|
if (accountInfo != null)
|
|
|
{
|
|
|
string appId = accountInfo.Item1;
|
|
|
string mchId = accountInfo.Item2;
|
|
|
string subMchId = accountInfo.Item3;
|
|
|
string appSecret = accountInfo.Item4;
|
|
|
|
|
|
WxPayV3RefundQueryRequestData request = new WxPayV3RefundQueryRequestData(appId, mchId, subMchId, appSecret, nonceStr, null, tradeNo, refundNo, null);
|
|
|
var queryResult = WxPayV3.RefundQuery(request);
|
|
|
logger.Info("订单[{0}]微信退款查询结果:{1}", tradeNo, JsonUtils.Serialize(queryResult));
|
|
|
if ("SUCCESS".Equals(queryResult.return_code))
|
|
|
{
|
|
|
if ("SUCCESS".Equals(queryResult.result_code))
|
|
|
{
|
|
|
return new Tuple<bool, string, RefundQueryResult>(true, null, queryResult);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
return new Tuple<bool, string, RefundQueryResult>(false, queryResult.err_code + queryResult.err_code_des, null);
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
return new Tuple<bool, string, RefundQueryResult>(false, queryResult.return_msg, null);
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
return new Tuple<bool, string, RefundQueryResult>(false, "未配置微信支付参数", null);
|
|
|
}
|
|
|
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
logger.Error(ex, "订单[{0}]调用微信退款查询接口异常", tradeNo);
|
|
|
return new Tuple<bool, string, RefundQueryResult>(false, "调用微信退款查询接口异常", null);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 微信退款+查询结果
|
|
|
/// </summary>
|
|
|
/// <param name="reMoney"></param>
|
|
|
/// <param name="tradeNo"></param>
|
|
|
/// <param name="reTradeNo"></param>
|
|
|
/// <returns></returns>
|
|
|
public static Tuple<bool, string> RefundAndQuery(AccountTypeEnum accountType, string tradeNo, decimal totalAmount, decimal refundAmount, string reTradeNo, string reason, int timeout = 60)
|
|
|
{
|
|
|
try
|
|
|
{
|
|
|
var refundResponse = RefundByWeixin(accountType, tradeNo, totalAmount, refundAmount, reTradeNo, reason);
|
|
|
if (refundResponse.Item1)//请求成功
|
|
|
{
|
|
|
//int queryCount = 0;
|
|
|
//int sleepTime = 3000;
|
|
|
//if(timeout < 60)
|
|
|
//{//微信退款较慢,显示最小60秒查询
|
|
|
// timeout = 60;
|
|
|
//}
|
|
|
//queryCount = (timeout * 1000 / sleepTime);
|
|
|
|
|
|
//int i = 0; //60秒超时
|
|
|
//while (i < queryCount)
|
|
|
//{
|
|
|
// var queryRefundResponse = QueryWeixinRefundResult(accountType, tradeNo, reTradeNo);
|
|
|
// if (queryRefundResponse.Item1 && queryRefundResponse.Item3.refund_status_list.Count > 0 && queryRefundResponse.Item3.refund_status_list[0] == "SUCCESS")
|
|
|
// {
|
|
|
// return new Tuple<bool, string>(true, "退款成功!");
|
|
|
// }
|
|
|
// i++;
|
|
|
// System.Threading.Thread.Sleep(3000);//睡眠3秒
|
|
|
//}
|
|
|
//if (i == queryCount)
|
|
|
//{
|
|
|
// return new Tuple<bool, string>(false, "查询退款结果超时,退单失败!");
|
|
|
//}
|
|
|
return new Tuple<bool, string>(true, "退款成功!");
|
|
|
}
|
|
|
else if ("ERROR订单已全额退款".Equals(refundResponse.Item2))
|
|
|
{
|
|
|
return new Tuple<bool, string>(true, "退款成功!");
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
//请求失败
|
|
|
return new Tuple<bool, string>(false, refundResponse.Item2);
|
|
|
}
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
logger.Error(ex, "微信退款发生异常");
|
|
|
return new Tuple<bool, string>(false, "退款发生异常!");
|
|
|
}
|
|
|
|
|
|
return new Tuple<bool, string>(false, "退款失败!");
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
}
|