初始提交
This commit is contained in:
@@ -0,0 +1,195 @@
|
||||
using Hncore.Infrastructure.Common;
|
||||
using Hncore.Infrastructure.Extension;
|
||||
using Hncore.Infrastructure.Serializer;
|
||||
using Hncore.Infrastructure.WebApi;
|
||||
using Hncore.Pass.PaymentCenter.Domain;
|
||||
using Hncore.Pass.PaymentCenter.Pay.WxPay;
|
||||
using Hncore.Pass.PaymentCenter.Request.WechatJsPay;
|
||||
using Hncore.Pass.PaymentCenter.Service;
|
||||
using Hncore.Pass.PaymentCenter.WxPay.WechatJsPay;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using System.Threading.Tasks;
|
||||
using Aliyun.Acs.Core;
|
||||
using Aliyun.Acs.Core.Exceptions;
|
||||
using Aliyun.Acs.Core.Http;
|
||||
using Aliyun.Acs.Core.Profile;
|
||||
using Alipay.AopSdk.Core.Domain;
|
||||
using Alipay.AopSdk.Core.Request;
|
||||
using Alipay.AopSdk.Core;
|
||||
using System.Collections.Generic;
|
||||
using Alipay.AopSdk.Core.Util;
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// 微信小程序、公众号支付
|
||||
/// </summary>
|
||||
[ApiVersion("1.0")]
|
||||
[Route("api/paymentcenter/v{version:apiVersion}/AliPay/[action]")]
|
||||
public class AliPayController : PaymentControllerBase
|
||||
{
|
||||
private PaymentRecordService m_PaymentRecordService;
|
||||
private WxPayClient m_WxPayClient;
|
||||
DefaultAopClient _aopClient;
|
||||
public AliPayController(PaymentRecordService paymentRecordService
|
||||
, WxPayClient _WxPayClient, TenantService _TenantService, IConfiguration _Configuration):base(_TenantService, _Configuration)
|
||||
{
|
||||
m_PaymentRecordService = paymentRecordService;
|
||||
m_WxPayClient = _WxPayClient;
|
||||
}
|
||||
[HttpPost, InternalApiAuth]
|
||||
public async Task<ApiResult> CreateOrder([FromBody] CreateOrderRequest request)
|
||||
{
|
||||
|
||||
LogHelper.Trace($"微信支付,收到支付请求", request.ToJson(true));
|
||||
request.AppId = "wx18683d61ce7da361";
|
||||
//增加支付记录
|
||||
var entity = request.MapTo<PaymentRecord>();
|
||||
entity.Openid = request.UserOpenId;
|
||||
PaymentRecord paymentRecord = await m_PaymentRecordService.CreatePaymentRecord(entity);
|
||||
paymentRecord.PaymentChannel = PaymentChannel.WxPay;
|
||||
//todo
|
||||
var mchInfo = await GetMchInfoAsync(paymentRecord.TenantId
|
||||
, paymentRecord.StoreId
|
||||
, paymentRecord.PaymentChannel);
|
||||
|
||||
//发起支付
|
||||
|
||||
var APP_ID = "";
|
||||
var APP_PRIVATE_KEY = "";
|
||||
var CHARSET = "";
|
||||
var ALIPAY_PUBLIC_KEY = "";
|
||||
|
||||
// 组装业务参数model
|
||||
AlipayTradePagePayModel model = new AlipayTradePagePayModel
|
||||
{
|
||||
Body = request.Body,
|
||||
Subject = request.Body,
|
||||
TotalAmount =request.TotalFee.ToString(),
|
||||
OutTradeNo = request.OrderId,
|
||||
ProductCode = "FAST_INSTANT_TRADE_PAY"//QUICK_WAP_PAY
|
||||
};
|
||||
|
||||
AlipayTradePagePayRequest aliRequest = new AlipayTradePagePayRequest();
|
||||
// 设置同步回调地址
|
||||
aliRequest.SetReturnUrl($"http://{Request.Host}/Pay/Callback");
|
||||
// 设置异步通知接收地址
|
||||
aliRequest.SetNotifyUrl("");
|
||||
// 将业务model载入到request
|
||||
aliRequest.SetBizModel(model);
|
||||
|
||||
var _aopClient = new DefaultAopClient("https://openapi.alipay.com/gateway.do", APP_ID, APP_PRIVATE_KEY);
|
||||
|
||||
var response = _aopClient.PageExecute(aliRequest);
|
||||
|
||||
return Success();
|
||||
}
|
||||
|
||||
#region 支付异步回调通知
|
||||
|
||||
/// <summary>
|
||||
/// 支付异步回调通知 需配置域名 因为是支付宝主动post请求这个action 所以要通过域名访问或者公网ip
|
||||
/// </summary>
|
||||
public async void Notify()
|
||||
{
|
||||
/* 实际验证过程建议商户添加以下校验。
|
||||
1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号,
|
||||
2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额),
|
||||
3、校验通知中的seller_id(或者seller_email) 是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email)
|
||||
4、验证app_id是否为该商户本身。
|
||||
*/
|
||||
Dictionary<string, string> sArray = GetRequestPost();
|
||||
if (sArray.Count != 0)
|
||||
{
|
||||
// bool flag = AlipaySignature.RSACheckV1(sArray, Options.AlipayPublicKey, Options.CharSet, Options.SignType, false);
|
||||
bool flag = AlipaySignature.RSACheckV1(sArray, "", "", "", false);
|
||||
if (flag)
|
||||
{
|
||||
//交易状态
|
||||
//判断该笔订单是否在商户网站中已经做过处理
|
||||
//如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序
|
||||
//请务必判断请求时的total_amount与通知时获取的total_fee为一致的
|
||||
//如果有做过处理,不执行商户的业务程序
|
||||
|
||||
//注意:
|
||||
//退款日期超过可退款期限后(如三个月可退款),支付宝系统发送该交易状态通知
|
||||
Console.WriteLine(Request.Form["trade_status"]);
|
||||
|
||||
await Response.WriteAsync("success");
|
||||
}
|
||||
else
|
||||
{
|
||||
await Response.WriteAsync("fail");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 支付同步回调
|
||||
|
||||
/// <summary>
|
||||
/// 支付同步回调
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
public IActionResult Callback()
|
||||
{
|
||||
/* 实际验证过程建议商户添加以下校验。
|
||||
1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号,
|
||||
2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额),
|
||||
3、校验通知中的seller_id(或者seller_email) 是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email)
|
||||
4、验证app_id是否为该商户本身。
|
||||
*/
|
||||
Dictionary<string, string> sArray = GetRequestGet();
|
||||
if (sArray.Count != 0)
|
||||
{
|
||||
bool flag = AlipaySignature.RSACheckV1(sArray, "", "", "", false);
|
||||
if (flag)
|
||||
{
|
||||
Console.WriteLine($"同步验证通过,订单号:{sArray["out_trade_no"]}");
|
||||
// ViewData["PayResult"] = "同步验证通过";
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"同步验证失败,订单号:{sArray["out_trade_no"]}");
|
||||
// ViewData["PayResult"] = "同步验证失败";
|
||||
}
|
||||
}
|
||||
return Content("");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
private Dictionary<string, string> GetRequestGet()
|
||||
{
|
||||
Dictionary<string, string> sArray = new Dictionary<string, string>();
|
||||
|
||||
ICollection<string> requestItem = Request.Query.Keys;
|
||||
foreach (var item in requestItem)
|
||||
{
|
||||
sArray.Add(item, Request.Query[item]);
|
||||
|
||||
}
|
||||
return sArray;
|
||||
|
||||
}
|
||||
private Dictionary<string, string> GetRequestPost()
|
||||
{
|
||||
Dictionary<string, string> sArray = new Dictionary<string, string>();
|
||||
|
||||
ICollection<string> requestItem = Request.Form.Keys;
|
||||
foreach (var item in requestItem)
|
||||
{
|
||||
sArray.Add(item, Request.Form[item]);
|
||||
|
||||
}
|
||||
return sArray;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,272 @@
|
||||
using Hncore.Infrastructure.Common;
|
||||
using Hncore.Infrastructure.Extension;
|
||||
using Hncore.Infrastructure.Serializer;
|
||||
using Hncore.Infrastructure.WebApi;
|
||||
using Hncore.Pass.PaymentCenter.Domain;
|
||||
using Hncore.Pass.PaymentCenter.Pay.WxPay;
|
||||
using Hncore.Pass.PaymentCenter.Response;
|
||||
using Hncore.Pass.PaymentCenter.Response.PaymentRecord;
|
||||
using Hncore.Pass.PaymentCenter.Service;
|
||||
using Hncore.Pass.PaymentCenter.WeiFuTong;
|
||||
using Hncore.Pass.PaymentCenter.WeiFuTong.Notify;
|
||||
using Hncore.Pass.PaymentCenter.WxPay.WechatJsPay;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.Controllers
|
||||
{
|
||||
[ApiVersion("1.0")]
|
||||
[Route("api/paymentcenter/v{version:apiVersion}/Payment/[action]")]
|
||||
public class PaymentController : PaymentControllerBase
|
||||
{
|
||||
private PaymentRecordService m_PaymentRecordService;
|
||||
private WeiFuTongService m_WeiFuTongService;
|
||||
private InternalNotifySerivce m_InternalNotifySerivce;
|
||||
|
||||
private WxPayClient m_WxPayClient { get; set; }
|
||||
|
||||
public PaymentController(
|
||||
PaymentRecordService _PaymentRecordService
|
||||
, InternalNotifySerivce internalNotifySerivce
|
||||
, TenantService _TenantService
|
||||
, WxPayClient _WxPayClient
|
||||
, WeiFuTongService weiFuTongService
|
||||
, IConfiguration _Configuration) : base(_TenantService, _Configuration)
|
||||
{
|
||||
// m_WeiFuTongService = weiFuTongService;
|
||||
m_PaymentRecordService = _PaymentRecordService;
|
||||
m_InternalNotifySerivce = internalNotifySerivce;
|
||||
m_WxPayClient = _WxPayClient;
|
||||
}
|
||||
|
||||
private async Task<QueryOrderResponse> QueryOrderByPaymentRecordAsync(PaymentRecord paymentRecord)
|
||||
{
|
||||
QueryOrderResponse result = null;
|
||||
|
||||
//威富通的订单
|
||||
if (paymentRecord.PaymentChannel == PaymentChannel.WeiFuTong
|
||||
|| paymentRecord.PaymentChannel == PaymentChannel.QuanFuTong
|
||||
&& paymentRecord.FromPos==0)
|
||||
{
|
||||
var mchInfo = await GetMchInfoAsync(paymentRecord.TenantId
|
||||
, paymentRecord.StoreId
|
||||
, paymentRecord.PaymentChannel);
|
||||
|
||||
result = await m_WeiFuTongService.QueryOrderAsync(paymentRecord, mchInfo);
|
||||
|
||||
await UofCommitAsync();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通用订单查询
|
||||
/// </summary>
|
||||
/// <param name="orderNo"></param>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
public async Task<ApiResult> QueryOrder(string orderNo = "", int? recordId = 0)
|
||||
{
|
||||
PaymentRecord paymentRecord = null;
|
||||
|
||||
if (recordId == null || recordId == 0)
|
||||
{
|
||||
paymentRecord = await m_PaymentRecordService.Query(false)
|
||||
.OrderByDescending(t => t.Id)
|
||||
.FirstOrDefaultAsync(t => t.OrderId == orderNo);
|
||||
}
|
||||
else if (recordId > 0)
|
||||
{
|
||||
paymentRecord = await m_PaymentRecordService.GetById(recordId.ToInt());
|
||||
}
|
||||
|
||||
CheckHelper.NotNull(paymentRecord, "支付记录不存在");
|
||||
|
||||
var result = await QueryOrderByPaymentRecordAsync(paymentRecord);
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
return Error("未知的支付渠道");
|
||||
}
|
||||
|
||||
return Success(result);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 根据orderid查询一条支付记录
|
||||
/// </summary>
|
||||
/// <param name="orderId"></param>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
public async Task<ApiResult> GetDetail([FromQuery] string orderId)
|
||||
{
|
||||
return Success(await QueryPaymentRecordByOrderIdResponse.Query(
|
||||
m_PaymentRecordService.Query(false)
|
||||
, orderId)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 威富通、全付通 接收通知
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpPost]
|
||||
public async Task<string> HWCNotify()
|
||||
{
|
||||
try
|
||||
{
|
||||
string xml = "";
|
||||
|
||||
if (Request.Body.CanSeek)
|
||||
{
|
||||
Request.Body.Position = 0;
|
||||
}
|
||||
|
||||
using (System.IO.StreamReader reader = new System.IO.StreamReader(Request.Body))
|
||||
{
|
||||
xml = reader.ReadToEnd();
|
||||
}
|
||||
|
||||
LogHelper.Trace("收到威富通支付通知,原始数据:", $"{xml}");
|
||||
|
||||
|
||||
var notifyEntity = XML.XmlDeserialize<NotifyResponse>(xml);
|
||||
|
||||
// 给支付平台的订单号为支付记录id
|
||||
int paymentRecordId = notifyEntity.OutTradeNo.ToInt();
|
||||
|
||||
var paymentRecord = await m_PaymentRecordService.GetById(paymentRecordId);
|
||||
|
||||
var mchInfo = await GetMchInfoAsync(paymentRecord.TenantId, paymentRecord.StoreId,
|
||||
paymentRecord.PaymentChannel);
|
||||
|
||||
Util.CheckSignFromXml(xml, mchInfo);
|
||||
|
||||
if (notifyEntity.PayResult == 0) //支付成功
|
||||
{
|
||||
paymentRecord.SetPaySuccessed(notifyEntity.TransactionId, notifyEntity.GetPaySuccessTime());
|
||||
paymentRecord.SetPaymentTypeFromTradeType(notifyEntity.TradeType);
|
||||
}
|
||||
else
|
||||
{
|
||||
paymentRecord.SetPayFailed();
|
||||
}
|
||||
|
||||
await m_PaymentRecordService.Update(paymentRecord);
|
||||
|
||||
var status = notifyEntity.PayResult == 0 ? "成功" : "失败";
|
||||
|
||||
|
||||
LogHelper.Info("威富通回调信息:",
|
||||
$"支付记录Id:{paymentRecord.Id}\n订单号:{paymentRecord.OrderId}\n交易号:{paymentRecord.TransactionId}\n支付状态:{status}\n发起时间:{paymentRecord.RequestTime.Format("yyyy-MM-dd HH:mm:ss")}\n付款成功时间:{notifyEntity.GetPaySuccessTime().Format("yyyy-MM-dd HH:mm:ss")}\n收到通知时间:{DateTime.Now.Format("yyyy-MM-dd HH:mm:ss")}\n总用时:{(DateTime.Now - paymentRecord.RequestTime).TotalSeconds}秒");
|
||||
|
||||
if (notifyEntity.PayResult == 0)
|
||||
{
|
||||
//发起内部通知
|
||||
await m_InternalNotifySerivce.Notify(paymentRecord);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LogHelper.Error("威富通通知处理失败", e);
|
||||
|
||||
return "fail";
|
||||
}
|
||||
|
||||
return "success";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 微信支付回调 接收通知
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpPost, AllowAnonymous]
|
||||
public async Task<string> Notify()
|
||||
{
|
||||
try
|
||||
{
|
||||
string xml = "";
|
||||
LogHelper.Trace("微信支付回调开始","Notify");
|
||||
if (Request.Body.CanSeek)
|
||||
{
|
||||
Request.Body.Position = 0;
|
||||
}
|
||||
|
||||
using (System.IO.StreamReader reader = new System.IO.StreamReader(Request.Body))
|
||||
{
|
||||
xml = reader.ReadToEnd();
|
||||
}
|
||||
|
||||
LogHelper.Trace("微信支付回调,原始数据:", $"{xml}");
|
||||
|
||||
WxPayChecker payData = new WxPayChecker();
|
||||
|
||||
payData.FromXmlNoCheckSign(xml);
|
||||
|
||||
if (!payData.IsSet("out_trade_no"))
|
||||
{
|
||||
return FailXml();
|
||||
}
|
||||
|
||||
// 给支付平台的订单号为支付记录id
|
||||
string orderId = payData["out_trade_no"];
|
||||
|
||||
string TransactionId = payData["transaction_id"];
|
||||
|
||||
var paymentRecord = await m_PaymentRecordService.GetOneByOrderId(orderId);
|
||||
if (paymentRecord.TransactionId.NotHas())
|
||||
{
|
||||
paymentRecord.TransactionId = TransactionId;
|
||||
await m_PaymentRecordService.Update(paymentRecord);
|
||||
}
|
||||
|
||||
var mchInfo = await GetMchInfoAsync(paymentRecord.TenantId, paymentRecord.StoreId,
|
||||
paymentRecord.PaymentChannel);
|
||||
payData.MchKey = mchInfo.Key;
|
||||
|
||||
var queryRet = await m_WxPayClient.OrderQuery(new WxJsPayOrderQueryRequest()
|
||||
{
|
||||
AppId = paymentRecord.Appid,
|
||||
MchId = mchInfo.MchId,
|
||||
NonceStr = payData.GenerateNonceStr(),
|
||||
TransactionId = TransactionId
|
||||
}, mchInfo);
|
||||
if (!queryRet) return FailXml();
|
||||
|
||||
if (!payData.CheckSign()) return FailXml();
|
||||
|
||||
paymentRecord.PaymentStatus = PaymentStatus.OkPay;
|
||||
paymentRecord.UpdateTime = DateTime.Now;
|
||||
paymentRecord.PaymentCompletionTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
|
||||
await m_PaymentRecordService.Update(paymentRecord);
|
||||
//发起内部通知
|
||||
await m_InternalNotifySerivce.Notify(paymentRecord);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LogHelper.Error("微信支付通知处理失败", e);
|
||||
|
||||
return FailXml();
|
||||
}
|
||||
|
||||
return SuccessXml();
|
||||
}
|
||||
|
||||
public string FailXml()
|
||||
{
|
||||
return "<xml><return_code>FAIL</return_code> </xml>";
|
||||
}
|
||||
|
||||
private string SuccessXml()
|
||||
{
|
||||
return "<xml><return_code>SUCCESS</return_code> </xml>";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
using Hncore.Infrastructure.Data;
|
||||
using Hncore.Infrastructure.DDD;
|
||||
using Hncore.Infrastructure.Extension;
|
||||
using Hncore.Infrastructure.WebApi;
|
||||
using Hncore.Pass.PaymentCenter.Domain;
|
||||
using Hncore.Pass.PaymentCenter.Model;
|
||||
using Hncore.Pass.PaymentCenter.Service;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.Controllers
|
||||
{
|
||||
public class PaymentControllerBase : HncoreControllerBase
|
||||
{
|
||||
TenantService m_TenantService;
|
||||
|
||||
|
||||
IConfiguration m_Configuration;
|
||||
|
||||
public PaymentControllerBase(TenantService _TenantService, IConfiguration _Configuration)
|
||||
{
|
||||
m_TenantService = _TenantService;
|
||||
m_Configuration = _Configuration;
|
||||
}
|
||||
|
||||
[NonAction]
|
||||
protected async Task<MchInfo> GetMchInfoAsync(int tenantId, int storeId,
|
||||
PaymentChannel paymentChannel)
|
||||
{
|
||||
if (paymentChannel == PaymentChannel.WxPay)
|
||||
{
|
||||
MchInfo mchInfo;
|
||||
|
||||
mchInfo = await RedisHelper.GetAsync<MchInfo>(GetWxPayCacheKey(tenantId, storeId));
|
||||
|
||||
if (mchInfo == null)
|
||||
{
|
||||
|
||||
var config = await m_TenantService.GetById(tenantId);
|
||||
|
||||
if (config == null
|
||||
|| !config.MchId.Has()
|
||||
|| !config.MchKey.Has())
|
||||
{
|
||||
BusinessException.Throw("支付信息未设置");
|
||||
}
|
||||
|
||||
mchInfo = new MchInfo()
|
||||
{
|
||||
GroupNo = config?.MchId,
|
||||
Key = config?.MchKey,
|
||||
MchId = config?.MchId
|
||||
};
|
||||
|
||||
await RedisHelper.SetAsync(GetWxPayCacheKey(tenantId, storeId), mchInfo);
|
||||
|
||||
return mchInfo;
|
||||
}
|
||||
|
||||
return mchInfo;
|
||||
|
||||
}
|
||||
|
||||
BusinessException.Throw("未知的支付渠道");
|
||||
return null;
|
||||
}
|
||||
|
||||
[NonAction]
|
||||
protected string GetWxPayCacheKey(int ownerId, int projectCode)
|
||||
{
|
||||
return $"PaymentCenter:WxPayMchInfo:{ownerId}:{projectCode}";
|
||||
}
|
||||
/// <summary>
|
||||
/// 微信支付 接收通知url
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected string GetNotifyUrl()
|
||||
{
|
||||
return Request.HttpContext.RequestServices.GetService<IConfiguration>()["NotifyUrl"];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
using Hncore.Infrastructure.Common;
|
||||
using Hncore.Infrastructure.Extension;
|
||||
using Hncore.Infrastructure.Serializer;
|
||||
using Hncore.Infrastructure.WebApi;
|
||||
using Hncore.Pass.PaymentCenter.Domain;
|
||||
using Hncore.Pass.PaymentCenter.Pay.WxPay;
|
||||
using Hncore.Pass.PaymentCenter.Request.WechatJsPay;
|
||||
using Hncore.Pass.PaymentCenter.Service;
|
||||
using Hncore.Pass.PaymentCenter.WxPay.WechatJsPay;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// 微信小程序、公众号支付
|
||||
/// </summary>
|
||||
[ApiVersion("1.0")]
|
||||
[Route("api/paymentcenter/v{version:apiVersion}/WechatJsPay/[action]")]
|
||||
public class WechatJsPayController : PaymentControllerBase
|
||||
{
|
||||
private PaymentRecordService m_PaymentRecordService;
|
||||
private WxPayClient m_WxPayClient;
|
||||
|
||||
public WechatJsPayController(PaymentRecordService paymentRecordService
|
||||
, WxPayClient _WxPayClient, TenantService _TenantService, IConfiguration _Configuration):base(_TenantService, _Configuration)
|
||||
{
|
||||
m_PaymentRecordService = paymentRecordService;
|
||||
m_WxPayClient = _WxPayClient;
|
||||
}
|
||||
[HttpPost, InternalApiAuth]
|
||||
public async Task<ApiResult> CreateOrder([FromBody] CreateOrderRequest request)
|
||||
{
|
||||
|
||||
LogHelper.Trace($"微信支付,收到支付请求", request.ToJson(true));
|
||||
request.AppId = "wx18683d61ce7da361";
|
||||
//增加支付记录
|
||||
var entity = request.MapTo<PaymentRecord>();
|
||||
entity.Openid = request.UserOpenId;
|
||||
PaymentRecord paymentRecord = await m_PaymentRecordService.CreatePaymentRecord(entity);
|
||||
paymentRecord.PaymentChannel = PaymentChannel.WxPay;
|
||||
//todo
|
||||
var mchInfo = await GetMchInfoAsync(paymentRecord.TenantId
|
||||
, paymentRecord.StoreId
|
||||
, paymentRecord.PaymentChannel);
|
||||
|
||||
//发起支付
|
||||
//todo
|
||||
//WxJsPayCreateOrderRequest payRequest = new WxJsPayCreateOrderRequest()
|
||||
//{
|
||||
// AppId = request.AppId,
|
||||
// Body = request.Body,
|
||||
// MchId = mchInfo.MchId,
|
||||
// NotifyUrl = GetNotifyUrl(),
|
||||
// OutTradeNo = request.OrderId,
|
||||
// StoreId = request.StoreId,
|
||||
// TenantId = request.TenantId,
|
||||
// TotalFee = request.TotalFee,
|
||||
// UserOpenId = request.UserOpenId
|
||||
//};
|
||||
//var createOrderRes = await m_WxPayClient.JsPayCreateOrderAsync(payRequest, mchInfo);
|
||||
|
||||
|
||||
var payRequest = new WxScanPayCreateOrderRequest()
|
||||
{
|
||||
AppId = request.AppId,
|
||||
Body = request.Body,
|
||||
MchId = mchInfo.MchId,
|
||||
NotifyUrl = GetNotifyUrl(),
|
||||
OutTradeNo = request.OrderId,
|
||||
StoreId = request.StoreId,
|
||||
TenantId = request.TenantId,
|
||||
TotalFee = request.TotalFee,
|
||||
ProductId ="1"
|
||||
};
|
||||
|
||||
var createOrderRes = await m_WxPayClient.ScanPayCreateOrderAsync(payRequest, mchInfo);
|
||||
|
||||
|
||||
LogHelper.Trace("PayCenter.CreateOrder", createOrderRes);
|
||||
return Success(createOrderRes);
|
||||
}
|
||||
}
|
||||
}
|
||||
27
Services/Hncore.Pass.PaymentCenter/Domain/CancelStatus.cs
Normal file
27
Services/Hncore.Pass.PaymentCenter/Domain/CancelStatus.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.Domain.Refund
|
||||
{
|
||||
/// <summary>
|
||||
/// 撤销状态
|
||||
/// </summary>
|
||||
public enum CancelStatus
|
||||
{
|
||||
[Display(Name ="未知")]None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// 撤销中
|
||||
/// </summary>
|
||||
[Display(Name = "撤销中")] Canceling = 1,
|
||||
|
||||
/// <summary>
|
||||
/// 撤销成功
|
||||
/// </summary>
|
||||
[Display(Name = "撤销成功")] Success = 2,
|
||||
|
||||
/// <summary>
|
||||
/// 撤销失败
|
||||
/// </summary>
|
||||
[Display(Name = "撤销失败")] Fail = 3
|
||||
}
|
||||
}
|
||||
289
Services/Hncore.Pass.PaymentCenter/Domain/Enum.cs
Normal file
289
Services/Hncore.Pass.PaymentCenter/Domain/Enum.cs
Normal file
@@ -0,0 +1,289 @@
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using NPOI.SS.Formula.Functions;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.Domain
|
||||
{
|
||||
/// <summary>
|
||||
/// 支付状态
|
||||
/// </summary>
|
||||
///
|
||||
public enum PaymentStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// 未支付
|
||||
/// </summary>
|
||||
[Display(Name = "未支付")] NotPay = 10,
|
||||
|
||||
/// <summary>
|
||||
/// 已支付
|
||||
/// </summary>
|
||||
[Display(Name = "已支付")] OkPay = 20,
|
||||
|
||||
/// <summary>
|
||||
/// 过期
|
||||
/// </summary>
|
||||
[Display(Name = "过期")] Expire = 30,
|
||||
|
||||
/// <summary>
|
||||
/// 支付失败
|
||||
/// </summary>
|
||||
[Display(Name = "支付失败")] Fail = 40,
|
||||
|
||||
/// <summary>
|
||||
/// 支付成功,回调失败
|
||||
/// </summary>
|
||||
[Display(Name = "支付成功,回调失败")] CallbackFail = 50,
|
||||
|
||||
/// <summary>
|
||||
/// 支付中
|
||||
/// </summary>
|
||||
[Display(Name = "支付中")] Paying = 60
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 支付类型
|
||||
/// </summary>
|
||||
public enum PaymentType
|
||||
{
|
||||
[Display(Name = "未知")] None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// 线下支付-现金
|
||||
/// </summary>
|
||||
[Display(Name = "线下支付-现金")] OfflinePayCash = 10,
|
||||
|
||||
/// <summary>
|
||||
/// 线下支付-支票
|
||||
/// </summary>
|
||||
[Display(Name = "线下支付-支票")] OfflinePayCheck = 20,
|
||||
|
||||
/// <summary>
|
||||
/// 线下支付-银行转账
|
||||
/// </summary>
|
||||
[Display(Name = "线下支付-银行转账")] OfflinePayBank = 30,
|
||||
|
||||
/// <summary>
|
||||
/// 线下支付-pos机刷卡
|
||||
/// </summary>
|
||||
[Display(Name = "线下支付-pos机刷卡")] OfflinePayPOS = 40,
|
||||
|
||||
/// <summary>
|
||||
/// 线下支付-支付宝直接转账
|
||||
/// </summary>
|
||||
[Display(Name = "线下支付-支付宝直接转账")] OfflinePayAlipay = 50,
|
||||
|
||||
/// <summary>
|
||||
/// 线下支付-微信直接转账
|
||||
/// </summary>
|
||||
[Display(Name = "线下支付-微信直接转账")] OfflinePayWechat = 60,
|
||||
|
||||
/// <summary>
|
||||
/// 线上支付-微信支付
|
||||
/// </summary>
|
||||
[Display(Name = "线上支付-微信支付")] OnlinePayWechart = 70,
|
||||
|
||||
/// <summary>
|
||||
/// 线上支付-POS机储蓄卡刷卡
|
||||
/// </summary>
|
||||
[Display(Name = "线上支付-POS机储蓄卡刷卡")] OnlinePosDeposit = 80,
|
||||
|
||||
/// <summary>
|
||||
/// 线上支付-POS机信用卡刷卡
|
||||
/// </summary>
|
||||
[Display(Name = "线上支付-POS机信用卡刷卡")] OnlinePosCredit = 90,
|
||||
|
||||
/// <summary>
|
||||
/// 线上支付-支付宝
|
||||
/// </summary>
|
||||
[Display(Name = "线上支付-支付宝")] OnlineAlipay = 100,
|
||||
|
||||
/// <summary>
|
||||
/// 其他支付方式
|
||||
/// </summary>
|
||||
[Display(Name = "其他支付方式")] Other = 250
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 支付方式
|
||||
/// </summary>
|
||||
public enum PaymentMethod
|
||||
{
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// 微信付款码支付
|
||||
/// </summary>
|
||||
[Display(Name = "微信付款码")] WechatSwipeCardPay = 1,
|
||||
|
||||
/// <summary>
|
||||
/// 微信扫码支付
|
||||
/// </summary>
|
||||
[Display(Name = "微信扫码")] WechatQrPay = 2,
|
||||
|
||||
/// <summary>
|
||||
/// 支付宝付款码支付
|
||||
/// </summary>
|
||||
[Display(Name = "支付宝付款码")] AliSwipeCardPay = 3,
|
||||
|
||||
/// <summary>
|
||||
/// 支付宝扫码支付
|
||||
/// </summary>
|
||||
[Display(Name = "支付宝付款码")] AliQrPay = 4,
|
||||
|
||||
/// <summary>
|
||||
/// 微信公众号支付
|
||||
/// </summary>
|
||||
[Display(Name = "微信公众号支付")] WechatJsAppPay = 5,
|
||||
|
||||
/// <summary>
|
||||
/// 支付宝服务窗支付
|
||||
/// </summary>
|
||||
[Display(Name = "支付宝服务窗支付")] AliJsPay = 6
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 订单类型
|
||||
/// </summary>
|
||||
///
|
||||
public enum OrderType
|
||||
{
|
||||
/// <summary>
|
||||
/// 短信订单
|
||||
/// </summary>
|
||||
[Display(Name = "短信订单")] SmsOrder = 0,
|
||||
|
||||
/// <summary>
|
||||
/// 产品订单
|
||||
/// </summary>
|
||||
[Display(Name = "产品订单")] Product = 1,
|
||||
|
||||
/// <summary>
|
||||
/// 续费订单
|
||||
/// </summary>
|
||||
[Display(Name = "续费订单")] RenewProduct = 2,
|
||||
|
||||
/// <summary>
|
||||
/// 物业缴费订单
|
||||
/// </summary>
|
||||
[Display(Name = "物业缴费订单")] Property = 3,
|
||||
|
||||
/// <summary>
|
||||
/// 停车券订单
|
||||
/// </summary>
|
||||
[Display(Name = "停车券订单")] ParkingCoupon = 4,
|
||||
|
||||
/// <summary>
|
||||
/// 月租车
|
||||
/// </summary>
|
||||
[Display(Name = "月租车订单")] MonthCar = 5,
|
||||
|
||||
/// <summary>
|
||||
/// 增值服务
|
||||
/// </summary>
|
||||
[Display(Name = "增值服务")] NodeProduct = 6,
|
||||
|
||||
/// <summary>
|
||||
/// 停车场临时缴费
|
||||
/// </summary>
|
||||
[Display(Name = "停车场临时缴费")] ParkingPay = 7,
|
||||
|
||||
/// <summary>
|
||||
/// 其它缴费
|
||||
/// </summary>
|
||||
[Display(Name = "其它缴费")] Other = 255
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 支付渠道
|
||||
/// </summary>
|
||||
public enum PaymentChannel
|
||||
{
|
||||
/// <summary>
|
||||
/// 全付通
|
||||
/// </summary>
|
||||
[Display(Name = "全付通")] QuanFuTong = 0,
|
||||
|
||||
/// <summary>
|
||||
/// 汇旺财
|
||||
/// </summary>
|
||||
[Display(Name = "威富通")] WeiFuTong = 10,
|
||||
|
||||
/// <summary>
|
||||
/// 信E付
|
||||
/// </summary>
|
||||
[Description("信E付")] EPay = 20,
|
||||
|
||||
/// <summary>
|
||||
/// 银联聚合支付
|
||||
/// </summary>
|
||||
[Description("银联聚合支付")] UnionpayAggregateRoot = 30,
|
||||
|
||||
/// <summary>
|
||||
/// 微信原生支付
|
||||
/// </summary>
|
||||
[Display(Name = "微信原生支付")] WxPay = 40,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 缴费方式(订单类别)
|
||||
/// </summary>
|
||||
public enum PayModel
|
||||
{
|
||||
/// <summary>
|
||||
/// 停车缴费(11-50)
|
||||
/// </summary>
|
||||
[Display(Name = "停车场临时缴费")] ParkingPay = 10,
|
||||
|
||||
/// <summary>
|
||||
/// 停车场买卡(01-10)
|
||||
/// </summary>
|
||||
[Display(Name = "停车券订单")] ParkingCoupon = 20,
|
||||
|
||||
///// <summary>
|
||||
///// 广告发布(51-70)
|
||||
///// </summary>
|
||||
//[Display(Name = "广告订单")]
|
||||
//AdPay = 30,
|
||||
/// <summary>
|
||||
/// 物业缴费(71-99)
|
||||
/// </summary>
|
||||
[Display(Name = "物业收费订单")] PropertyPay = 40,
|
||||
|
||||
/// <summary>
|
||||
/// 其他临时收费
|
||||
/// </summary>
|
||||
[Display(Name = "其它临时收费")] OtherPay = 50,
|
||||
|
||||
/// <summary>
|
||||
/// 直接收费
|
||||
/// </summary>
|
||||
[Display(Name = "直接收费")] DirectPay = 60,
|
||||
|
||||
/// <summary>
|
||||
/// 月租车订单
|
||||
/// </summary>
|
||||
[Display(Name = "月租车订单")] MonthCar = 70
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 内部通知状态
|
||||
/// </summary>
|
||||
public enum CallbackStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// 已创建
|
||||
/// </summary>
|
||||
[Display(Name = "未执行")] NoStart = 10,
|
||||
|
||||
/// <summary>
|
||||
/// 执行中
|
||||
/// </summary>
|
||||
[Display(Name = "执行中")] InProcess = 20,
|
||||
|
||||
/// <summary>
|
||||
/// 已完成
|
||||
/// </summary>
|
||||
[Display(Name = "已完成")] Finished = 30
|
||||
}
|
||||
}
|
||||
31
Services/Hncore.Pass.PaymentCenter/Domain/Manager.cs
Normal file
31
Services/Hncore.Pass.PaymentCenter/Domain/Manager.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.Domain
|
||||
{
|
||||
public partial class Manager
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int TenantId { get; set; }
|
||||
public DateTime CreateTime { get; set; }
|
||||
public DateTime UpdateTime { get; set; }
|
||||
public int DeleteTag { get; set; }
|
||||
public int CreatorId { get; set; }
|
||||
public int UpdatorId { get; set; }
|
||||
public string LoginCode { get; set; }
|
||||
public string Password { get; set; }
|
||||
public int RoleId { get; set; }
|
||||
public int State { get; set; }
|
||||
public string PhotoUrl { get; set; }
|
||||
public string WxOpenid { get; set; }
|
||||
public string WxNickName { get; set; }
|
||||
public string WxImage { get; set; }
|
||||
public int? Source { get; set; }
|
||||
public string Phone { get; set; }
|
||||
public string ManagerCode { get; set; }
|
||||
public string RealName { get; set; }
|
||||
public string Email { get; set; }
|
||||
public sbyte? IsRoot { get; set; }
|
||||
public int? SystemId { get; set; }
|
||||
}
|
||||
}
|
||||
21
Services/Hncore.Pass.PaymentCenter/Domain/NotifyType.cs
Normal file
21
Services/Hncore.Pass.PaymentCenter/Domain/NotifyType.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using Hncore.Infrastructure.Extension;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Hncore.Pass.PaymentCenter.Domain.Refund;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.Domain
|
||||
{
|
||||
/// <summary>
|
||||
/// 通知类型
|
||||
/// </summary>
|
||||
public enum NotifyType
|
||||
{
|
||||
|
||||
[Display(Name = "未知")] None = 0,
|
||||
|
||||
[Display(Name = "未知")] Success = 1,
|
||||
[Display(Name = "未知")] Faild = 2,
|
||||
|
||||
}
|
||||
}
|
||||
70
Services/Hncore.Pass.PaymentCenter/Domain/PaymentContext.cs
Normal file
70
Services/Hncore.Pass.PaymentCenter/Domain/PaymentContext.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using Hncore.Infrastructure.EF;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.Domain
|
||||
{
|
||||
public partial class PaymentContext : DbContextBase
|
||||
{
|
||||
public PaymentContext(DbContextOptions<PaymentContext> options, IHttpContextAccessor httpContextAccessor) :
|
||||
base(options, httpContextAccessor)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual DbSet<PaymentRecord> PaymentRecord { get; set; }
|
||||
public virtual DbSet<Manager> Manager { get; set; }
|
||||
public virtual DbSet<PaymentNotify> PaymentRecordNotify { get; set; }
|
||||
public virtual DbSet<RefundRecord> PaymentRefundRecord { get; set; }
|
||||
public virtual DbSet<TenantEntity> Tenant { get; set; }
|
||||
public virtual DbSet<TenantStore> TenantStore { get; set; }
|
||||
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
modelBuilder.Entity<Manager>(entity =>
|
||||
{
|
||||
entity.ToTable("manager");
|
||||
entity.HasKey(p => p.Id);
|
||||
entity.Property(e => e.Id).ValueGeneratedOnAdd();
|
||||
});
|
||||
|
||||
modelBuilder.Entity<PaymentNotify>(entity =>
|
||||
{
|
||||
entity.ToTable("payment_record_notify");
|
||||
entity.HasKey(p => p.Id);
|
||||
entity.Property(e => e.Id).ValueGeneratedOnAdd();
|
||||
});
|
||||
|
||||
modelBuilder.Entity<RefundRecord>(entity =>
|
||||
{
|
||||
entity.ToTable("payment_refund_record");
|
||||
entity.HasKey(p => p.Id);
|
||||
entity.Property(e => e.Id).ValueGeneratedOnAdd();
|
||||
});
|
||||
|
||||
modelBuilder.Entity<TenantEntity>(entity =>
|
||||
{
|
||||
entity.ToTable("tenant");
|
||||
entity.HasKey(p => p.Id);
|
||||
entity.Property(e => e.Id).ValueGeneratedOnAdd();
|
||||
});
|
||||
|
||||
modelBuilder.Entity<TenantStore>(entity =>
|
||||
{
|
||||
entity.ToTable("tenant_store");
|
||||
|
||||
entity.HasKey(p => p.Id);
|
||||
entity.Property(e => e.Id).ValueGeneratedOnAdd();
|
||||
});
|
||||
modelBuilder.Entity<PaymentRecord>(entity =>
|
||||
{
|
||||
entity.ToTable("payment_record");
|
||||
|
||||
entity.HasKey(p => p.Id);
|
||||
entity.Property(e => e.Id).ValueGeneratedOnAdd();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
20
Services/Hncore.Pass.PaymentCenter/Domain/PaymentNotify.cs
Normal file
20
Services/Hncore.Pass.PaymentCenter/Domain/PaymentNotify.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.Domain
|
||||
{
|
||||
public partial class PaymentNotify
|
||||
{
|
||||
public uint Id { get; set; }
|
||||
public int PaymentRecordId { get; set; }
|
||||
public string Url { get; set; }
|
||||
public string PostData { get; set; }
|
||||
public string ResponseData { get; set; }
|
||||
public int RetryCount { get; set; }
|
||||
public DateTime CreateTime { get; set; }
|
||||
public DateTime UpdateTime { get; set; }
|
||||
public int DeleteTag { get; set; }
|
||||
|
||||
public NotifyType NotifyType { get; set; }
|
||||
}
|
||||
}
|
||||
197
Services/Hncore.Pass.PaymentCenter/Domain/PaymentRecord.cs
Normal file
197
Services/Hncore.Pass.PaymentCenter/Domain/PaymentRecord.cs
Normal file
@@ -0,0 +1,197 @@
|
||||
using Hncore.Infrastructure.Common;
|
||||
using Hncore.Infrastructure.Serializer;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.Domain
|
||||
{
|
||||
public partial class PaymentRecord
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int TenantId { get; set; }
|
||||
public int StoreId { get; set; }
|
||||
public DateTime RequestTime { get; set; } = DateTime.Now;
|
||||
public string RequestParams { get; set; }
|
||||
public PaymentStatus PaymentStatus { get; set; }
|
||||
public string PaymentNumber { get; set; }
|
||||
public string PaymentCompletionTime { get; set; }
|
||||
public int PaymentTotal { get; set; }
|
||||
public PaymentType PaymentType { get; set; }
|
||||
public int CallbackNumber { get; set; }
|
||||
public string CallbackUrl { get; set; }
|
||||
public string Openid { get; set; }
|
||||
public string Appid { get; set; }
|
||||
public string AppName { get; set; }
|
||||
public string Body { get; set; }
|
||||
public string OrderId { get; set; }
|
||||
public DateTime? CreateTime { get; set; } = DateTime.Now;
|
||||
public DateTime? UpdateTime { get; set; } = DateTime.Now;
|
||||
public int DeleteTag { get; set; } = 0;
|
||||
public int BusinessType { get; set; }
|
||||
public OrderType OrderType { get; set; }
|
||||
public int FromPos { get; set; }
|
||||
public int TaskStatus { get; set; }
|
||||
public PaymentChannel PaymentChannel { get; set; }
|
||||
public string GenSource { get; set; }
|
||||
public DateTime? PaySuccessTime { get; set; }
|
||||
public string RefundTotalFee { get; set; }
|
||||
public PaymentMethod PaymentMethod { get; set; }
|
||||
public CallbackStatus CallbackStatus { get; set; }
|
||||
public string TransactionId { get; set; }
|
||||
|
||||
public string Attach { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 设置为支付成功
|
||||
/// </summary>
|
||||
/// <param name="transactionId">平台交易单号</param>
|
||||
/// <param name="paySuccessTime">付款成功时间</param>
|
||||
public void SetPaySuccessed(string transactionId, DateTime? paySuccessTime)
|
||||
{
|
||||
this.PaymentStatus = PaymentStatus.OkPay;
|
||||
this.UpdateTime = DateTime.Now;
|
||||
this.PaymentCompletionTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
if (paySuccessTime != null)
|
||||
{
|
||||
this.PaySuccessTime = paySuccessTime;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(transactionId))
|
||||
{
|
||||
this.TransactionId = transactionId;
|
||||
}
|
||||
|
||||
LogHelper.Info("支付记录设置为支——付成功", this.ToJson());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置为支付中
|
||||
/// </summary>
|
||||
public void SetPaying()
|
||||
{
|
||||
if (this.PaymentStatus == PaymentStatus.OkPay)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.PaymentStatus = PaymentStatus.Paying;
|
||||
this.UpdateTime = DateTime.Now;
|
||||
|
||||
LogHelper.Info("支付记录设置为——支付中", this.ToJson());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置为支付失败
|
||||
/// </summary>
|
||||
public void SetPayFailed()
|
||||
{
|
||||
if (this.PaymentStatus == PaymentStatus.OkPay)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.PaymentStatus = PaymentStatus.Fail;
|
||||
this.UpdateTime = DateTime.Now;
|
||||
this.PaymentCompletionTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据支付平台的tradetype设置PaymentType
|
||||
/// </summary>
|
||||
/// <param name="tradeType"></param>
|
||||
public void SetPaymentTypeFromTradeType(string tradeType)
|
||||
{
|
||||
if (this.PaymentChannel == PaymentChannel.UnionpayAggregateRoot)
|
||||
{
|
||||
this.PaymentType = PaymentType.OfflinePayCash;
|
||||
|
||||
if (tradeType == "pay.weixin.micropay") //微信付款码支付
|
||||
{
|
||||
this.PaymentType = PaymentType.OnlinePayWechart;
|
||||
this.PaymentMethod = PaymentMethod.WechatSwipeCardPay;
|
||||
}
|
||||
|
||||
if (tradeType == "pay.alipay.micropay") //支付宝付款码
|
||||
{
|
||||
this.PaymentType = PaymentType.OnlineAlipay;
|
||||
this.PaymentMethod = PaymentMethod.AliSwipeCardPay;
|
||||
}
|
||||
|
||||
if (tradeType == "pay.alipay.jspay") //支付宝扫码
|
||||
{
|
||||
this.PaymentType = PaymentType.OnlineAlipay;
|
||||
this.PaymentMethod = PaymentMethod.AliQrPay;
|
||||
}
|
||||
|
||||
if (tradeType == "pay.weixin.jspay") //微信扫码
|
||||
{
|
||||
this.PaymentType = PaymentType.OnlinePayWechart;
|
||||
this.PaymentMethod = PaymentMethod.WechatJsAppPay;
|
||||
}
|
||||
|
||||
//支付宝支付窗支付
|
||||
if (tradeType == "pay.alipay.native")
|
||||
{
|
||||
this.PaymentType = PaymentType.OnlineAlipay;
|
||||
this.PaymentMethod = PaymentMethod.AliJsPay;
|
||||
}
|
||||
}
|
||||
|
||||
if ((this.PaymentChannel == PaymentChannel.WeiFuTong || this.PaymentChannel == PaymentChannel.QuanFuTong)
|
||||
&& this.FromPos ==1)
|
||||
{
|
||||
this.PaymentType = PaymentType.OfflinePayCash;
|
||||
|
||||
//微信付款码支付
|
||||
if (tradeType == "pay.weixin.micropay" || tradeType == "pay.wechat.micropay")
|
||||
{
|
||||
this.PaymentType = PaymentType.OnlinePayWechart;
|
||||
this.PaymentMethod = PaymentMethod.WechatSwipeCardPay;
|
||||
}
|
||||
|
||||
//微信公众号支付
|
||||
if (tradeType == "pay.weixin.jspay" || tradeType == "pay.wechat.jspay")
|
||||
{
|
||||
this.PaymentType = PaymentType.OnlinePayWechart;
|
||||
this.PaymentMethod = PaymentMethod.WechatJsAppPay;
|
||||
}
|
||||
|
||||
//微信扫码支付
|
||||
if (tradeType == "pay.weixin.native" || tradeType == "pay.wechat.native")
|
||||
{
|
||||
this.PaymentType = PaymentType.OnlinePayWechart;
|
||||
this.PaymentMethod = PaymentMethod.WechatQrPay;
|
||||
}
|
||||
|
||||
//支付宝付款码支付
|
||||
if (tradeType == "pay.alipay.micropay")
|
||||
{
|
||||
this.PaymentType = PaymentType.OnlineAlipay;
|
||||
this.PaymentMethod = PaymentMethod.AliSwipeCardPay;
|
||||
}
|
||||
|
||||
//支付宝扫码支付
|
||||
if (tradeType == "pay.alipay.native")
|
||||
{
|
||||
this.PaymentType = PaymentType.OnlineAlipay;
|
||||
this.PaymentMethod = PaymentMethod.AliQrPay;
|
||||
}
|
||||
|
||||
//银联支付
|
||||
if (tradeType == "pay.unionpay.micropay")
|
||||
{
|
||||
this.PaymentType = PaymentType.OnlinePayWechart;
|
||||
}
|
||||
|
||||
//支付宝支付窗支付
|
||||
if (tradeType == "pay.alipay.jspay")
|
||||
{
|
||||
this.PaymentType = PaymentType.OnlineAlipay;
|
||||
this.PaymentMethod = PaymentMethod.AliJsPay;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
120
Services/Hncore.Pass.PaymentCenter/Domain/RefundRecord.cs
Normal file
120
Services/Hncore.Pass.PaymentCenter/Domain/RefundRecord.cs
Normal file
@@ -0,0 +1,120 @@
|
||||
using Hncore.Infrastructure.Extension;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Hncore.Pass.PaymentCenter.Domain.Refund;
|
||||
namespace Hncore.Pass.PaymentCenter.Domain
|
||||
{
|
||||
public partial class RefundRecord
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public DateTime? CreateTime { get; set; }
|
||||
public DateTime? UpdateTime { get; set; }
|
||||
public int? DeleteTag { get; set; }
|
||||
public int? TenantId { get; set; }
|
||||
public int? StoreId { get; set; }
|
||||
public int? PaymentRecordId { get; set; }
|
||||
public DateTime? RequestTime { get; set; }
|
||||
public string OrderId { get; set; }
|
||||
public string TransactionId { get; set; }
|
||||
public string RefundId { get; set; }
|
||||
public string TransactionRefundId { get; set; }
|
||||
public RefundStatus RefundStatus { get; set; }
|
||||
public int? RefundFee { get; set; }
|
||||
public int? OrderTotalFee { get; set; }
|
||||
public PaymentChannel PaymentChannel { get; set; }
|
||||
public int PaymentMethod { get; set; }
|
||||
public PaymentType PaymentType { get; set; }
|
||||
public DateTime? RefundSuccessTime { get; set; }
|
||||
public string RefundReason { get; set; }
|
||||
public DateTime? ApplyCancelTime { get; set; }
|
||||
public string CancelReason { get; set; }
|
||||
public CancelStatus CancelStatus { get; set; }
|
||||
public DateTime? QueryJobPollTime { get; set; }
|
||||
public DateTime? StatementJobPollTime { get; set; }
|
||||
public string CompanyName { get; set; }
|
||||
public int? OperatorId { get; set; }
|
||||
public string OperatorName { get; set; }
|
||||
public int? OffLineRefund { get; set; }
|
||||
public int TaskStates { get; set; }
|
||||
public string Bak { get; set; }
|
||||
public string CallbackUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 设置为退款成功
|
||||
/// </summary>
|
||||
/// <param name="transactionRefundId">交易平台退款单号</param>
|
||||
/// <param name="refundSuccessTime">退款到账时间</param>
|
||||
public void SetRefundSuccessed(string transactionRefundId, DateTime? refundSuccessTime)
|
||||
{
|
||||
this.RefundStatus = RefundStatus.Success;
|
||||
this.UpdateTime = DateTime.Now;
|
||||
|
||||
if (refundSuccessTime != null)
|
||||
{
|
||||
this.RefundSuccessTime = refundSuccessTime;
|
||||
}
|
||||
|
||||
if (transactionRefundId.Has())
|
||||
{
|
||||
this.TransactionRefundId = transactionRefundId;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置为退款失败
|
||||
/// </summary>
|
||||
public void SetRefundFailed()
|
||||
{
|
||||
this.RefundStatus = RefundStatus.Fail;
|
||||
this.UpdateTime = DateTime.Now;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置为撤销成功
|
||||
/// </summary>
|
||||
public void SetCancelSuccessed()
|
||||
{
|
||||
// 撤销中,未到账
|
||||
if (this.CancelStatus == CancelStatus.Canceling && this.RefundStatus != RefundStatus.Success)
|
||||
{
|
||||
this.CancelStatus = CancelStatus.Success;
|
||||
this.RefundStatus = RefundStatus.Fail;
|
||||
this.UpdateTime = DateTime.Now;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置为撤销失败
|
||||
/// </summary>
|
||||
public void SetCancelFailed()
|
||||
{
|
||||
// 撤销中,已到账
|
||||
if (this.CancelStatus == CancelStatus.Canceling && this.RefundStatus == RefundStatus.Success)
|
||||
{
|
||||
this.CancelStatus = CancelStatus.Fail;
|
||||
this.UpdateTime = DateTime.Now;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重置状态,失败的单子重新发起退款
|
||||
/// </summary>
|
||||
public void ReSetStatus(int refundFee)
|
||||
{
|
||||
this.RefundStatus = RefundStatus.Processing;
|
||||
this.CancelStatus = CancelStatus.None;
|
||||
this.RequestTime = DateTime.Now;
|
||||
this.RefundSuccessTime = null;
|
||||
this.RefundFee = refundFee;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置新的refundId
|
||||
/// </summary>
|
||||
public void SetNewRefundId()
|
||||
{
|
||||
this.RefundId = Guid.NewGuid().ToString().Replace("-", "");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
40
Services/Hncore.Pass.PaymentCenter/Domain/RefundStatus.cs
Normal file
40
Services/Hncore.Pass.PaymentCenter/Domain/RefundStatus.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.Domain.Refund
|
||||
{
|
||||
/// <summary>
|
||||
/// 退款状态
|
||||
/// </summary>
|
||||
public enum RefundStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// 未知
|
||||
/// </summary>
|
||||
[Display(Name ="未知")]None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// 退款中
|
||||
/// </summary>
|
||||
[Display(Name = "退款中")] Processing = 1,
|
||||
|
||||
/// <summary>
|
||||
/// 退款成功
|
||||
/// </summary>
|
||||
[Display(Name = "退款成功")] Success = 2,
|
||||
|
||||
/// <summary>
|
||||
/// 退款失败
|
||||
/// </summary>
|
||||
[Display(Name = "退款失败")] Fail = 3,
|
||||
|
||||
/// <summary>
|
||||
/// 未确定,需要商户原退款单号重新发起
|
||||
/// </summary>
|
||||
[Display(Name = "未确定")] NotSure = 4,
|
||||
|
||||
/// <summary>
|
||||
/// 转入代发,退款到银行发现用户的卡作废或者冻结了,导致原路退款银行卡失败,资金回流到商户的现金帐号,需要商户人工干预,通过线下或者平台转账的方式进行退款
|
||||
/// </summary>
|
||||
[Display(Name = "转入代发")] Change = 5
|
||||
}
|
||||
}
|
||||
42
Services/Hncore.Pass.PaymentCenter/Domain/Tenant.cs
Normal file
42
Services/Hncore.Pass.PaymentCenter/Domain/Tenant.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using Hncore.Infrastructure.DDD;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.Domain
|
||||
{
|
||||
public partial class TenantEntity : EntityWithDelete<int>
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Logo { get; set; }
|
||||
public string ContactName { get; set; }
|
||||
public string ContactPhone { get; set; }
|
||||
public string LicenseImg { get; set; }
|
||||
public int? State { get; set; }
|
||||
public DateTime CreateTime { get; set; }
|
||||
public DateTime UpdateTime { get; set; }
|
||||
public int Deletetag { get; set; }
|
||||
public string Bak { get; set; }
|
||||
public int? Province { get; set; }
|
||||
public int? City { get; set; }
|
||||
public int? Area { get; set; }
|
||||
public string Location { get; set; }
|
||||
public string LegalPerson { get; set; }
|
||||
public string Longlatitude { get; set; }
|
||||
public string Introduction { get; set; }
|
||||
public string ScopeOperation { get; set; }
|
||||
public string Bank { get; set; }
|
||||
public string Bankcode { get; set; }
|
||||
public DateTime? Expiredtime { get; set; }
|
||||
public string BankChargeName { get; set; }
|
||||
public string BankChargeAdress { get; set; }
|
||||
public string Appid { get; set; }
|
||||
public string AppSecret { get; set; }
|
||||
public string MchId { get; set; }
|
||||
public string MchKey { get; set; }
|
||||
public string MiniAppid { get; set; }
|
||||
public string MiniAppSecret { get; set; }
|
||||
public string MiniMchId { get; set; }
|
||||
public string MiniMchKey { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
30
Services/Hncore.Pass.PaymentCenter/Domain/TenantStore.cs
Normal file
30
Services/Hncore.Pass.PaymentCenter/Domain/TenantStore.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.Domain
|
||||
{
|
||||
public partial class TenantStore
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int? TenantId { get; set; }
|
||||
public string Name { get; set; }
|
||||
public int? Type { get; set; }
|
||||
public int? Theme { get; set; }
|
||||
public int? Public { get; set; }
|
||||
public string CourseGroups { get; set; }
|
||||
public string LiveGroups { get; set; }
|
||||
public string TextGroups { get; set; }
|
||||
public string PackageGroups { get; set; }
|
||||
public string SwiperGroups { get; set; }
|
||||
public string NavGroups { get; set; }
|
||||
public string AdGroups { get; set; }
|
||||
public string ArticleGroups { get; set; }
|
||||
public string ActivityGroups { get; set; }
|
||||
public int? DeleteTag { get; set; }
|
||||
public DateTime? CreateTime { get; set; }
|
||||
public string CustomerTel { get; set; }
|
||||
public int? Appid { get; set; }
|
||||
public string AppSecret { get; set; }
|
||||
public int? IsOpen { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||
<ServerGarbageCollection>false</ServerGarbageCollection>
|
||||
<ConcurrentGarbageCollection>true</ConcurrentGarbageCollection>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.2.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.VisualStudio.Web.BrowserLink" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.2.3" />
|
||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="2.2.0" />
|
||||
<PackageReference Include="XC.RSAUtil" Version="1.1.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Infrastructure\Hncore.Infrastructure\Hncore.Infrastructure.csproj" />
|
||||
<ProjectReference Include="..\..\Infrastructure\ServiceClient\Alipay.AopSdk.Core\Alipay.AopSdk.Core.csproj" />
|
||||
<ProjectReference Include="..\..\Infrastructure\ServiceClient\PaymentCenterClient\PaymentCenterClient.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Update="appsettings.Development.json">
|
||||
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ProjectExtensions><VisualStudio><UserProperties appsettings_1Development_1json__JSONSchema="" /></VisualStudio></ProjectExtensions>
|
||||
|
||||
</Project>
|
||||
19
Services/Hncore.Pass.PaymentCenter/Map/MapConfig.cs
Normal file
19
Services/Hncore.Pass.PaymentCenter/Map/MapConfig.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using Hncore.Infrastructure.Extension;
|
||||
using Hncore.Pass.PaymentCenter.Domain;
|
||||
using Hncore.Pass.PaymentCenter.Request.WechatJsPay;
|
||||
using Hncore.Pass.PaymentCenter.Response.PaymentRecord;
|
||||
using Hncore.Pass.PaymentCenter.WxPay.WechatJsPay;
|
||||
using Hncore.Payment.Request;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.Map
|
||||
{
|
||||
public class MapConfig
|
||||
{
|
||||
public static void Config()
|
||||
{
|
||||
TinyMapperExtension.Binds<PaymentRecord, QueryPaymentRecordByOrderIdResponse>();
|
||||
TinyMapperExtension.Binds<PaymentRecord, CreateOrderRequest>();
|
||||
TinyMapperExtension.Binds<PaymentRecord, WxJsPayCreateOrderRequest>();
|
||||
}
|
||||
}
|
||||
}
|
||||
18
Services/Hncore.Pass.PaymentCenter/Model/MchInfo.cs
Normal file
18
Services/Hncore.Pass.PaymentCenter/Model/MchInfo.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
namespace Hncore.Pass.PaymentCenter.Model
|
||||
{
|
||||
public class MchInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// 连锁商户号
|
||||
/// </summary>
|
||||
public string GroupNo { get; set; } = "";
|
||||
|
||||
public string MchId { get; set; } = "";
|
||||
|
||||
public string Key { get; set; } = "";
|
||||
|
||||
public string RSAPrivateKey { get; set; } = "";
|
||||
|
||||
public string RSAPublicKey { get; set; } = "";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
using Alipay.AopSdk.Core;
|
||||
using Alipay.AopSdk.Core.Domain;
|
||||
using Alipay.AopSdk.Core.Request;
|
||||
using Hncore.Infrastructure.Common;
|
||||
using Hncore.Infrastructure.Serializer;
|
||||
using Hncore.Pass.PaymentCenter.Model;
|
||||
using Hncore.Pass.PaymentCenter.WxPay.WechatJsPay;
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.Pay.AliPay
|
||||
{
|
||||
public class AliPayClient
|
||||
{
|
||||
public static readonly string url = "https://api.mch.weixin.qq.com";
|
||||
IHttpClientFactory m_HttpClientFactory;
|
||||
public AliPayClient(IHttpClientFactory _HttpClientFactory)
|
||||
{
|
||||
m_HttpClientFactory = _HttpClientFactory;
|
||||
}
|
||||
|
||||
public async Task<string> PcPayCreateOrderAsync(WxJsPayCreateOrderRequest request, MchInfo mchInfo)
|
||||
{
|
||||
|
||||
var APP_ID = "";
|
||||
var APP_PRIVATE_KEY = "";
|
||||
var CHARSET = "";
|
||||
var ALIPAY_PUBLIC_KEY = "";
|
||||
|
||||
// 组装业务参数model
|
||||
|
||||
|
||||
AlipayTradePagePayModel model = new AlipayTradePagePayModel
|
||||
{
|
||||
Body = request.Body,
|
||||
Subject = request.Body,
|
||||
TotalAmount = request.TotalFee.ToString(),
|
||||
OutTradeNo = "",
|
||||
ProductCode = "FAST_INSTANT_TRADE_PAY"//QUICK_WAP_PAY
|
||||
};
|
||||
|
||||
AlipayTradePagePayRequest aliRequest = new AlipayTradePagePayRequest();
|
||||
// 设置同步回调地址
|
||||
// aliRequest.SetReturnUrl($"http://{Request.Host}/Pay/Callback");
|
||||
// 设置异步通知接收地址
|
||||
aliRequest.SetNotifyUrl("");
|
||||
// 将业务model载入到request
|
||||
aliRequest.SetBizModel(model);
|
||||
|
||||
var _aopClient = new DefaultAopClient("https://openapi.alipay.com/gateway.do", APP_ID, APP_PRIVATE_KEY);
|
||||
|
||||
var response = _aopClient.PageExecute(aliRequest);
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
public async Task<string> H5PayCreateOrderAsync(WxH5PayCreateOrderRequest request, MchInfo mchInfo)
|
||||
{
|
||||
//AlipayTradeWapPayModel
|
||||
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using System.Xml.Serialization;
|
||||
using Hncore.Infrastructure.Common;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.WeiFuTong.AliJsPay
|
||||
{
|
||||
/// <summary>
|
||||
/// 支付宝支付窗支付
|
||||
/// </summary>
|
||||
[XmlRoot("xml", Namespace = "")]
|
||||
public class AliJsPayRequest : WeiFuTongRequestBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 买家支付宝账号,和buger_id不能同时为空
|
||||
/// </summary>
|
||||
[XmlElement("buyer_logon_id")]
|
||||
public string BuyerLogonId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 买家支付宝用户ID,和buyer_logon_id不能同时
|
||||
/// </summary>
|
||||
[XmlElement("buyer_id")]
|
||||
public string BuyerId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 商户系统内部的订单号 ,5到32个字符、 只能包含字母数字或者下划线,区分大小写,每次下单请求确保在商户系统唯一
|
||||
/// </summary>
|
||||
[XmlElement("out_trade_no")]
|
||||
public string OutTradeNo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 商品描述
|
||||
/// </summary>
|
||||
[XmlElement("body")]
|
||||
public string Body { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 总金额,以分为单位,不允许包含任何字、符号
|
||||
/// </summary>
|
||||
[XmlElement("total_fee")]
|
||||
public int TotalFee { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 订单生成的机器 IP
|
||||
/// </summary>
|
||||
[XmlElement("mch_create_ip")]
|
||||
public string MchCreateIp { get; set; } = NetworkHelper.GetPublicIp();
|
||||
|
||||
[XmlElement("notify_url")] public string NotifyUrl { get; set; }
|
||||
|
||||
public AliJsPayRequest() : base("pay.alipay.jspay")
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.WeiFuTong.AliJsPay
|
||||
{
|
||||
[XmlRoot("xml", Namespace = "")]
|
||||
public class AliJsPayResponse: WeiFuTongResponseBase
|
||||
{
|
||||
/// <summary>
|
||||
/// JSON字符串,自行唤起支付宝钱包支付
|
||||
/// </summary>
|
||||
[XmlElement("pay_info")]
|
||||
public string PayInfo { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Hncore.Infrastructure.Common;
|
||||
using Hncore.Infrastructure.Serializer;
|
||||
using Hncore.Pass.PaymentCenter.WeiFuTong.AliJsPay;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.WeiFuTong.ClientExtension
|
||||
{
|
||||
public static class AliJsPay
|
||||
{
|
||||
/// <summary>
|
||||
/// 支付宝支付窗支付下单
|
||||
/// </summary>
|
||||
/// <param name="weiFuTongClient"></param>
|
||||
/// <param name="request"></param>
|
||||
/// <returns></returns>
|
||||
public static async Task<AliJsPayResponse> AliJsPayCreateOrderAsync(this WeiFuTongClient weiFuTongClient
|
||||
, AliJsPayRequest request)
|
||||
{
|
||||
StringBuilder log = new StringBuilder();
|
||||
|
||||
var body = request.ToXml();
|
||||
|
||||
log.Append("请求:\r\n" + body);
|
||||
|
||||
var response = await weiFuTongClient.CreateHttpClient()
|
||||
.PostAsync(request.PaymentChannel.GetUrl(), new StringContent(body));
|
||||
|
||||
var resText = await response.Content.ReadAsStringAsync();
|
||||
|
||||
log.Append("\r\n响应:" + resText);
|
||||
|
||||
LogHelper.Trace($"威富通-支付宝支付窗支付下单", log.ToString());
|
||||
|
||||
Util.CheckSignFromXml(resText, request.MchInfo);
|
||||
|
||||
var res = XML.XmlDeserialize<AliJsPayResponse>(resText);
|
||||
|
||||
res.HandleError();
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Hncore.Infrastructure.Common;
|
||||
using Hncore.Infrastructure.Serializer;
|
||||
using Hncore.Pass.PaymentCenter.WeiFuTong.QrPay;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.WeiFuTong.ClientExtension
|
||||
{
|
||||
public static class QrPay
|
||||
{
|
||||
/// <summary>
|
||||
/// 扫码支付下单
|
||||
/// </summary>
|
||||
/// <param name="weiFuTongClient"></param>
|
||||
/// <param name="request"></param>
|
||||
/// <returns></returns>
|
||||
public static async Task<QrPayCreateOrderResponse> QrPayCreateOrderAsync(this WeiFuTongClient weiFuTongClient
|
||||
, QrPayCreateOrderRequest request)
|
||||
{
|
||||
StringBuilder log = new StringBuilder();
|
||||
|
||||
var body = request.ToXml();
|
||||
|
||||
log.Append("请求:\r\n" + body);
|
||||
|
||||
var response = await weiFuTongClient.CreateHttpClient()
|
||||
.PostAsync(request.PaymentChannel.GetUrl(), new StringContent(body));
|
||||
|
||||
var resText = await response.Content.ReadAsStringAsync();
|
||||
|
||||
log.Append("\r\n响应:" + resText);
|
||||
|
||||
string target = "";
|
||||
|
||||
if (request.Service == "pay.weixin.native")
|
||||
{
|
||||
target = "微信";
|
||||
}
|
||||
else if (request.Service == "pay.alipay.native")
|
||||
{
|
||||
target = "支付宝";
|
||||
}
|
||||
|
||||
LogHelper.Trace($"威富通-{target}扫码支付下单", log.ToString());
|
||||
|
||||
Util.CheckSignFromXml(resText, request.MchInfo);
|
||||
|
||||
var res = XML.XmlDeserialize<QrPayCreateOrderResponse>(resText);
|
||||
|
||||
res.HandleError();
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Hncore.Infrastructure.Common;
|
||||
using Hncore.Infrastructure.Data;
|
||||
using Hncore.Infrastructure.Serializer;
|
||||
using Hncore.Pass.PaymentCenter.WeiFuTong.QueryOrder;
|
||||
using Hncore.Pass.PaymentCenter.WeiFuTong.SwipeCard;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.WeiFuTong.ClientExtension
|
||||
{
|
||||
public static class QueryOrderExt
|
||||
{
|
||||
/// <summary>
|
||||
/// 支付订单查询
|
||||
/// </summary>
|
||||
/// <param name="weiFuTongClient"></param>
|
||||
/// <param name="request"></param>
|
||||
/// <returns></returns>
|
||||
public static async Task<WeiFuTongQueryOrderResponse> QueryOrderAsync(this WeiFuTongClient weiFuTongClient
|
||||
, WeiFuTongQueryOrderRequest request)
|
||||
{
|
||||
StringBuilder log = new StringBuilder();
|
||||
|
||||
var body = request.ToXml();
|
||||
|
||||
log.Append("请求:\r\n" + body);
|
||||
|
||||
var response = await weiFuTongClient.CreateHttpClient()
|
||||
.PostAsync(request.PaymentChannel.GetUrl(), new StringContent(body));
|
||||
|
||||
var resText = await response.Content.ReadAsStringAsync();
|
||||
|
||||
log.Append("\r\n响应:" + resText);
|
||||
|
||||
//LogHelper.Trace("威富通支付订单查询", log.ToString());
|
||||
|
||||
if (response.StatusCode != HttpStatusCode.OK)
|
||||
{
|
||||
throw new HttpException(response.StatusCode) {Content = resText};
|
||||
}
|
||||
|
||||
Util.CheckSignFromXml(resText, request.MchInfo);
|
||||
|
||||
return XML.XmlDeserialize<WeiFuTongQueryOrderResponse>(resText);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Hncore.Infrastructure.Common;
|
||||
using Hncore.Infrastructure.Serializer;
|
||||
using Hncore.Pass.PaymentCenter.WeiFuTong.Refund;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.WeiFuTong.ClientExtension
|
||||
{
|
||||
public static class RefundExt
|
||||
{
|
||||
/// <summary>
|
||||
/// 退款请求
|
||||
/// </summary>
|
||||
/// <param name="weiFuTongClient"></param>
|
||||
/// <param name="request"></param>
|
||||
/// <returns></returns>
|
||||
public static async Task<WeiFuTongRefundResponse> RefundAsync(this WeiFuTongClient weiFuTongClient,
|
||||
WeiFuTongRefundRequest request)
|
||||
{
|
||||
StringBuilder log = new StringBuilder();
|
||||
|
||||
var body = request.ToXml();
|
||||
|
||||
log.Append("请求:\r\n" + body);
|
||||
|
||||
var response = await weiFuTongClient.CreateHttpClient()
|
||||
.PostAsync(request.PaymentChannel.GetUrl(), new StringContent(body));
|
||||
|
||||
var resText = await response.Content.ReadAsStringAsync();
|
||||
|
||||
log.Append("\r\n响应:" + resText);
|
||||
|
||||
LogHelper.Trace($"威富通发起退款", log.ToString());
|
||||
|
||||
Util.CheckSignFromXml(resText, request.MchInfo);
|
||||
|
||||
var res = XML.XmlDeserialize<WeiFuTongRefundResponse>(resText);
|
||||
|
||||
res.HandleError();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 退款查询
|
||||
/// </summary>
|
||||
/// <param name="weiFuTongClient"></param>
|
||||
/// <param name="request"></param>
|
||||
/// <returns></returns>
|
||||
public static async Task<WeiFuTongRefundQueryResponse> QueryRefundAsync(this WeiFuTongClient weiFuTongClient,
|
||||
WeiFuTongRefundQueryRequest request)
|
||||
{
|
||||
StringBuilder log = new StringBuilder();
|
||||
|
||||
var body = request.ToXml();
|
||||
|
||||
log.Append("请求:\r\n" + body);
|
||||
|
||||
var response = await weiFuTongClient.CreateHttpClient()
|
||||
.PostAsync(request.PaymentChannel.GetUrl(), new StringContent(body));
|
||||
|
||||
var resText = await response.Content.ReadAsStringAsync();
|
||||
|
||||
log.Append("\r\n响应:" + resText);
|
||||
|
||||
//LogHelper.Trace($"威富通-退款查询", log.ToString());
|
||||
|
||||
Util.CheckSignFromXml(resText, request.MchInfo);
|
||||
|
||||
var res = XML.XmlDeserialize<WeiFuTongRefundQueryResponse>(resText).ParseItemsFromXml(resText);
|
||||
|
||||
if (!(res.IsCancel() || res.IsNotExists()) && res.HasError())
|
||||
{
|
||||
res.HandleError();
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Hncore.Infrastructure.Common;
|
||||
using Hncore.Infrastructure.Data;
|
||||
using Hncore.Infrastructure.Serializer;
|
||||
using Hncore.Pass.PaymentCenter.WeiFuTong.SwipeCard;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.WeiFuTong.ClientExtension
|
||||
{
|
||||
public static class SwipeCard
|
||||
{
|
||||
/// <summary>
|
||||
/// 刷卡支付下单
|
||||
/// </summary>
|
||||
/// <param name="weiFuTongClient"></param>
|
||||
/// <param name="request"></param>
|
||||
/// <returns>need_query为Y时需要轮询查询支付状态,不为Y则支付成功</returns>
|
||||
public static async Task<SwipeCardCreateOrderResponse> SwipeCardCreateOrderAsync(
|
||||
this WeiFuTongClient weiFuTongClient
|
||||
, SwipeCardCreateOrderRequest request)
|
||||
{
|
||||
StringBuilder log = new StringBuilder();
|
||||
|
||||
var body = request.ToXml();
|
||||
|
||||
log.Append("请求:\r\n" + body);
|
||||
|
||||
var response = await weiFuTongClient.CreateHttpClient()
|
||||
.PostAsync(request.PaymentChannel.GetUrl(), new StringContent(body));
|
||||
|
||||
var resText = await response.Content.ReadAsStringAsync();
|
||||
|
||||
log.Append("\r\n响应:" + resText);
|
||||
|
||||
LogHelper.Trace("威富通-刷卡支付下单", log.ToString());
|
||||
|
||||
if (response.StatusCode != HttpStatusCode.OK)
|
||||
{
|
||||
throw new HttpException(response.StatusCode) {Content = resText};
|
||||
}
|
||||
|
||||
Util.CheckSignFromXml(resText, request.MchInfo);
|
||||
|
||||
var res = XML.XmlDeserialize<SwipeCardCreateOrderResponse>(resText);
|
||||
|
||||
if (res.HasError() && res.NeedQuery != "Y" && res.NeedQuery != "")
|
||||
{
|
||||
res.HandleError();
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Hncore.Infrastructure.Common;
|
||||
using Hncore.Infrastructure.Serializer;
|
||||
using Hncore.Pass.PaymentCenter.WeiFuTong.WechatJsPay;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.WeiFuTong.ClientExtension
|
||||
{
|
||||
public static class WechatJsPay
|
||||
{
|
||||
/// <summary>
|
||||
/// 微信小程序或公众号支付
|
||||
/// </summary>
|
||||
/// <param name="weiFuTongClient"></param>
|
||||
/// <param name="request"></param>
|
||||
/// <returns></returns>
|
||||
public static async Task<WechatJsPayCreateOrderResponse> WechatJsPayCreateOrderAsync(
|
||||
this WeiFuTongClient weiFuTongClient
|
||||
, WechatJsPayCreateOrderRequest request)
|
||||
{
|
||||
StringBuilder log = new StringBuilder();
|
||||
|
||||
var body = request.ToXml();
|
||||
|
||||
log.Append("请求:\r\n" + body);
|
||||
|
||||
var response = await weiFuTongClient.CreateHttpClient()
|
||||
.PostAsync(request.PaymentChannel.GetUrl(), new StringContent(body));
|
||||
|
||||
var resText = await response.Content.ReadAsStringAsync();
|
||||
|
||||
log.Append("\r\n响应:" + resText);
|
||||
|
||||
string appEnv = "";
|
||||
|
||||
if (request.IsMinipg == "1")
|
||||
{
|
||||
appEnv = "小程序";
|
||||
}
|
||||
else
|
||||
{
|
||||
appEnv = "公众号";
|
||||
}
|
||||
|
||||
LogHelper.Trace($"威富通-微信{appEnv}支付下单", log.ToString());
|
||||
|
||||
Util.CheckSignFromXml(resText, request.MchInfo);
|
||||
|
||||
var res = XML.XmlDeserialize<WechatJsPayCreateOrderResponse>(resText);
|
||||
|
||||
res.HandleError();
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.WeiFuTong.Notify
|
||||
{
|
||||
[XmlRoot("xml", Namespace = "")]
|
||||
public class NotifyResponse : WeiFuTongResponseBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 用户在服务商 appid 下的唯一标识
|
||||
/// </summary>
|
||||
[XmlElement("openid")]
|
||||
public string OpenId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 交易类型
|
||||
/// </summary>
|
||||
[XmlElement("trade_type")]
|
||||
public string TradeType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 用户是否关注服务商公众账号,Y-关注,N-未关注
|
||||
/// </summary>
|
||||
[XmlElement("is_subscribe")]
|
||||
public string IsSubscribe { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 支付结果:0—成功;其它—失败
|
||||
/// </summary>
|
||||
[XmlElement("pay_result")]
|
||||
public int PayResult { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 支付结果信息,支付成功时为空
|
||||
/// </summary>
|
||||
[XmlElement("pay_info")]
|
||||
public string PayInfo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 平台交易号
|
||||
/// </summary>
|
||||
[XmlElement("transaction_id")]
|
||||
public string TransactionId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 第三方订单号
|
||||
/// </summary>
|
||||
[XmlElement("out_transaction_id")]
|
||||
public string OutTransactionId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 第三方商户单号,可在支持的商户扫码退款
|
||||
/// </summary>
|
||||
[XmlElement("third_order_no")]
|
||||
public string ThirdOrderNo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 用户是否关注子公众账号,Y-关注,N-未关注,仅在公众账号类型支付有效
|
||||
/// </summary>
|
||||
[XmlElement("sub_is_subscribe")]
|
||||
public string SubIsSubscribe { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 子商户appid
|
||||
/// </summary>
|
||||
[XmlElement("sub_appid")]
|
||||
public string SubAppId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 用户在子商户appid下的唯一标识
|
||||
/// </summary>
|
||||
[XmlElement("sub_openid")]
|
||||
public string SubOpenId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 商户系统内部的定单号,32个字符内、可包含字母
|
||||
/// </summary>
|
||||
[XmlElement("out_trade_no")]
|
||||
public string OutTradeNo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 总金额,以分为单位,不允许包含任何字、符号
|
||||
/// </summary>
|
||||
[XmlElement("total_fee")]
|
||||
public int TotalFee { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 现金支付金额【微信】
|
||||
/// </summary>
|
||||
[XmlElement("cash_fee")]
|
||||
public int CashFee { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 现金券金额【微信】
|
||||
/// </summary>
|
||||
[XmlElement("coupon_fee")]
|
||||
public string CouponFee { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 货币类型,符合 ISO 4217 标准的三位字母代码,默认人民币:CNY
|
||||
/// </summary>
|
||||
[XmlElement("fee_type")]
|
||||
public string FeeType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 商家数据包,原样返回
|
||||
/// </summary>
|
||||
[XmlElement("attach")]
|
||||
public string Attach { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 付款银行
|
||||
/// </summary>
|
||||
[XmlElement("bank_type")]
|
||||
public string BankType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 银行订单号,若为微信支付则为空
|
||||
/// </summary>
|
||||
[XmlElement("bank_billno")]
|
||||
public string BankBillno { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 支付完成时间,格式为yyyyMMddHHmmss,如2009年12月27日9点10分10秒表示为20091227091010。时区为GMT+8 beijing。该时间取自平台服务器
|
||||
/// </summary>
|
||||
[XmlElement("time_end")]
|
||||
public string TimeEnd { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取支付成功时间
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public DateTime GetPaySuccessTime()
|
||||
{
|
||||
DateTime.TryParseExact(TimeEnd, "yyyyMMddHHmmss", null, DateTimeStyles.None, out var paySuccessTime);
|
||||
|
||||
return paySuccessTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using System.Xml.Serialization;
|
||||
using Hncore.Infrastructure.Common;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.WeiFuTong.QrPay
|
||||
{
|
||||
[XmlRoot("xml", Namespace = "")]
|
||||
public class QrPayCreateOrderRequest: WeiFuTongRequestBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 商户系统内部的订单号 ,5到32个字符、 只能包含字母数字或者下划线,区分大小写,每次下单请求确保在商户系统唯一
|
||||
/// </summary>
|
||||
[XmlElement("out_trade_no")]
|
||||
public string OutTradeNo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 商品描述
|
||||
/// </summary>
|
||||
[XmlElement("body")]
|
||||
public string Body { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 总金额,以分为单位,不允许包含任何字、符号
|
||||
/// </summary>
|
||||
[XmlElement("total_fee")]
|
||||
public int TotalFee { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 订单生成的机器 IP
|
||||
/// </summary>
|
||||
[XmlElement("mch_create_ip")]
|
||||
public string MchCreateIp { get; set; } = NetworkHelper.GetPublicIp();
|
||||
|
||||
[XmlElement("notify_url")]
|
||||
public string NotifyUrl { get; set; }
|
||||
|
||||
public QrPayCreateOrderRequest(string service) : base(service)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.WeiFuTong.QrPay
|
||||
{
|
||||
[XmlRoot("xml", Namespace = "")]
|
||||
public class QrPayCreateOrderResponse : WeiFuTongResponseBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 商户可用此参数自定义去生成二维码后展示出来进行扫码支付
|
||||
/// </summary>
|
||||
[XmlElement("code_url")]
|
||||
public string CodeUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 此参数的值即是根据code_url生成的可以扫码支付的二维码图片地址
|
||||
/// </summary>
|
||||
[XmlElement("code_img_url")]
|
||||
public string CodeImgUrl { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.WeiFuTong.QueryOrder
|
||||
{
|
||||
/// <summary>
|
||||
/// 威富通查询订单请求
|
||||
/// </summary>
|
||||
public class WeiFuTongQueryOrderRequest : WeiFuTongRequestBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 商户系统内部的订单号, out_trade_no和transaction_id至少一个必填,同时存在时transaction_id优先
|
||||
/// </summary>
|
||||
[XmlElement("out_trade_no")]
|
||||
public string OutTradeNo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 平台交易号, out_trade_no和transaction_id至少一个必填,同时存在时transaction_id优先。
|
||||
/// </summary>
|
||||
[XmlElement("transaction_id")]
|
||||
public string TransactionId { get; set; }
|
||||
|
||||
public WeiFuTongQueryOrderRequest()
|
||||
: base("unified.trade.query")
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Xml.Serialization;
|
||||
using Hncore.Pass.PaymentCenter.Domain;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.WeiFuTong.SwipeCard
|
||||
{
|
||||
/// <summary>
|
||||
/// 刷卡支付查询订单响应
|
||||
/// </summary>
|
||||
[XmlRoot("xml", Namespace = "")]
|
||||
public class WeiFuTongQueryOrderResponse : WeiFuTongResponseBase
|
||||
{
|
||||
// 以下字段在 status 为 0的时候有返回
|
||||
|
||||
/// <summary>
|
||||
/// 门店编号,由平台分配
|
||||
/// </summary>
|
||||
[XmlElement("mch_id")]
|
||||
public string MchId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 终端设备号
|
||||
/// </summary>
|
||||
[XmlElement("device_info")]
|
||||
public string DeviceInfo { get; set; }
|
||||
|
||||
// 以下字段在 status 和 result_code 都为 0的时候有返回
|
||||
|
||||
/// <summary>
|
||||
/// SUCCESS—支付成功
|
||||
/// REFUND—转入退款
|
||||
/// NOTPAY—未支付
|
||||
/// CLOSED—已关闭
|
||||
/// REVOKED/REVERSE—已撤销
|
||||
/// USERPAYING—用户支付中
|
||||
/// PAYERROR—支付失败(其他原因,如银行返回失败)
|
||||
/// </summary>
|
||||
[XmlElement("trade_state")]
|
||||
public string TradeState { get; set; }
|
||||
|
||||
// 以下字段在 trade_state为 SUCCESS的时候有返回
|
||||
|
||||
/// <summary>
|
||||
/// pay.wechat.micropay——微信刷卡支付
|
||||
/// pay.alipay.micropay——支付宝刷卡支付
|
||||
/// pay.jdpay.micropay——京东刷卡支付
|
||||
/// pay.qq.micropay——QQ钱包刷卡支付
|
||||
/// pay.shiming.micropay——会员卡支付
|
||||
/// pay.unionpay.micropay——银联支付
|
||||
/// pay.bestpay.micropay——翼支付
|
||||
/// </summary>
|
||||
[XmlElement("trade_type")]
|
||||
public string TradeType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 受理商户appid
|
||||
/// </summary>
|
||||
[XmlElement("appid")]
|
||||
public string AppId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 子商户appid
|
||||
/// </summary>
|
||||
[XmlElement("sub_appid")]
|
||||
public string SubAppId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 用户在受理商户 appid 下的唯一标识
|
||||
/// </summary>
|
||||
[XmlElement("openid")]
|
||||
public string OpenId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 用户在子商户appid下的唯一标识
|
||||
/// </summary>
|
||||
[XmlElement("sub_openid")]
|
||||
public string SubOpenId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 用户是否关注公众账号,Y-关注,N-未关注,仅在公众账号类型支付有效
|
||||
/// </summary>
|
||||
[XmlElement("is_subscribe")]
|
||||
public string IsSubscribe { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 用户是否关注子公众账号,Y-关注,N-未关注,仅在公众账号类型支付有效
|
||||
/// </summary>
|
||||
[XmlElement("sub_is_subscribe")]
|
||||
public string SubIsSubscribe { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 平台交易号
|
||||
/// </summary>
|
||||
[XmlElement("transaction_id")]
|
||||
public string TransactionId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 第三方订单号
|
||||
/// </summary>
|
||||
[XmlElement("out_transaction_id")]
|
||||
public string OutTransactionId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 第三方商户单号,可在支持的商户扫码退款
|
||||
/// </summary>
|
||||
[XmlElement("third_order_no")]
|
||||
public string ThirdOrderNo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 商户系统内部的定单号,32个字符内、可包含字母
|
||||
/// </summary>
|
||||
[XmlElement("out_trade_no")]
|
||||
public string OutTradeNo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 总金额,以分为单位,不允许包含任何字、符号
|
||||
/// </summary>
|
||||
[XmlElement("total_fee")]
|
||||
public int TotalFee { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 现金支付金额【微信】
|
||||
/// </summary>
|
||||
[XmlElement("cash_fee")]
|
||||
public int CashFee { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 开票金额 【支付宝】
|
||||
/// </summary>
|
||||
[XmlElement("invoice_amount")]
|
||||
public string InvoiceAmount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 实收金额【支付宝】实收金额,单位为元,两位小数。该金额为本笔交易,商户账户能够实际收到的金额
|
||||
/// </summary>
|
||||
[XmlElement("receipt_amount")]
|
||||
public string ReceiptAmount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 买家实付金额【支付宝】买家实付金额,单位为元,两位小数。该金额代表该笔交易买家实际支付的金额,不包含商户折扣等金额
|
||||
/// </summary>
|
||||
[XmlElement("buyer_pay_amount")]
|
||||
public string BuyerPayAmount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 集分宝付款金额【支付宝】
|
||||
/// </summary>
|
||||
[XmlElement("point_amount")]
|
||||
public string PointAmount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 现金券金额【微信】
|
||||
/// </summary>
|
||||
[XmlElement("coupon_fee")]
|
||||
public string CouponFee { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 货币类型,符合 ISO 4217 标准的三位字母代码,默认人民币:CNY
|
||||
/// </summary>
|
||||
[XmlElement("fee_type")]
|
||||
public string FeeType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 商家数据包,原样返回
|
||||
/// </summary>
|
||||
[XmlElement("attach")]
|
||||
public string Attach { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 付款银行
|
||||
/// </summary>
|
||||
[XmlElement("bank_type")]
|
||||
public string BankType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 银行订单号,若为微信支付则为空
|
||||
/// </summary>
|
||||
[XmlElement("bank_billno")]
|
||||
public string BankBillno { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 支付完成时间,格式为yyyyMMddHHmmss,如2009年12月27日9点10分10秒表示为20091227091010。时区为GMT+8 beijing。该时间取自平台服务器
|
||||
/// </summary>
|
||||
[XmlElement("time_end")]
|
||||
public string TimeEnd { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否支付成功
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsPaySuccess()
|
||||
{
|
||||
return !HasError() && (TradeState == "SUCCESS" || TradeState == "REFUND");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否为支付失败,未支付和支付中不认定为失败
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsPayFailed()
|
||||
{
|
||||
return !HasError()
|
||||
&& TradeState != "SUCCESS"
|
||||
&& TradeState != "NOTPAY"
|
||||
&& TradeState != "USERPAYING"
|
||||
&& TradeState != "REFUND";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取支付成功时间
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public DateTime GetPaySuccessTime()
|
||||
{
|
||||
DateTime.TryParseExact(TimeEnd, "yyyyMMddHHmmss", null, DateTimeStyles.None, out var paySuccessTime);
|
||||
|
||||
return paySuccessTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.WeiFuTong.Refund
|
||||
{
|
||||
/// <summary>
|
||||
/// 威富通退款查询请求
|
||||
/// </summary>
|
||||
[XmlRoot("xml", Namespace = "")]
|
||||
public class WeiFuTongRefundQueryRequest : WeiFuTongRequestBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 商户系统内部的订单号, out_trade_no和transaction_id至少一个必填,同时存在时transaction_id优先
|
||||
/// </summary>
|
||||
[XmlElement("out_trade_no")]
|
||||
public string OutTradeNo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 可传入平台订单号或第三方商户单号(third_order_no), out_trade_no和transaction_id至少一个必填,同时存在时transaction_id优先
|
||||
/// </summary>
|
||||
[XmlElement("transaction_id")]
|
||||
public string TransactionId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 商户退款单号,32个字符内、可包含字母,确保在商户系统唯一。
|
||||
/// </summary>
|
||||
[XmlElement("out_refund_no")]
|
||||
public string OutRefundNo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 平台退款单号refund_id、out_refund_no、out_trade_no 、transaction_id 四个参数必填一个, 如果同时存在优先级为:refund_id>out_refund_no>transaction_id>out_trade_no
|
||||
/// </summary>
|
||||
[XmlElement("refund_id")]
|
||||
public string RefundId { get; set; }
|
||||
|
||||
public WeiFuTongRefundQueryRequest() : base("unified.trade.refundquery")
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
using Hncore.Infrastructure.Extension;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.WeiFuTong.Refund
|
||||
{
|
||||
/// <summary>
|
||||
/// 威富通退款查询响应
|
||||
/// </summary>
|
||||
[XmlRoot("xml", Namespace = "")]
|
||||
public class WeiFuTongRefundQueryResponse : WeiFuTongResponseBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 平台交易号
|
||||
/// </summary>
|
||||
[XmlElement("transaction_id")]
|
||||
public string TransactionId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 商户系统内部的订单号
|
||||
/// </summary>
|
||||
[XmlElement("out_trade_no")]
|
||||
public string OutTradeNo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 退款记录数
|
||||
/// </summary>
|
||||
[XmlElement("refund_count")]
|
||||
public int RefundCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 多笔退款详情
|
||||
/// </summary>
|
||||
public List<RefundQueryItem> RefundQueryItems { get; set; } = new List<RefundQueryItem>();
|
||||
|
||||
public WeiFuTongRefundQueryResponse ParseItemsFromXml(string xmlText)
|
||||
{
|
||||
if (RefundCount <= 0)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
XmlDocument xmlDoc = new XmlDocument();
|
||||
xmlDoc.XmlResolver = null;
|
||||
xmlDoc.LoadXml(xmlText);
|
||||
XmlNode root = xmlDoc.SelectSingleNode("xml");
|
||||
|
||||
for (int i = 0; i < RefundCount; i++)
|
||||
{
|
||||
RefundQueryItems.Add(new RefundQueryItem()
|
||||
{
|
||||
OutRefundNo = root.SelectSingleNode("out_refund_no_" + i).InnerText,
|
||||
RefundId = root.SelectSingleNode("refund_id_" + i).InnerText,
|
||||
RefundChannel = root.SelectSingleNode("refund_channel_" + i).InnerText,
|
||||
RefundFee = root.SelectSingleNode("refund_fee_" + i).InnerText.ToInt(),
|
||||
RefundTime = root.SelectSingleNode("refund_time_" + i).InnerText,
|
||||
RefundStatus = root.SelectSingleNode("refund_status_" + i).InnerText
|
||||
});
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否是退款成功
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsRefundSuccess()
|
||||
{
|
||||
return !HasError()
|
||||
&& RefundQueryItems.Any()
|
||||
&& RefundQueryItems.First().RefundStatus == "SUCCESS";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否是退款失败
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsRefundFail()
|
||||
{
|
||||
return !HasError()
|
||||
&& RefundQueryItems.Any()
|
||||
&& RefundQueryItems.First().RefundStatus == "FAIL";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否是已撤销
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsCancel()
|
||||
{
|
||||
return RefundCount == 0
|
||||
&& ErrCode == "REFUND_FAIL"
|
||||
&& ErrMsg.Contains("已撤销");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 退款不存在
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsNotExists()
|
||||
{
|
||||
return HasError() &&
|
||||
(
|
||||
ErrCode.ToLower().Contains("refund not exists")
|
||||
|| ErrCode.ToLower().Contains("order not exists")
|
||||
|| ErrMsg.Contains("退款不存在")
|
||||
|| ErrMsg.Contains("订单不存在")
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public DateTime GetRefundSuccessTime()
|
||||
{
|
||||
DateTime.TryParseExact(RefundQueryItems.First().RefundTime, "yyyyMMddHHmmss", null, DateTimeStyles.None,
|
||||
out var paySuccessTime);
|
||||
|
||||
return paySuccessTime;
|
||||
}
|
||||
}
|
||||
|
||||
public class RefundQueryItem
|
||||
{
|
||||
/// <summary>
|
||||
/// 商户退款单号
|
||||
/// </summary>
|
||||
[XmlElement("out_refund_no")]
|
||||
public string OutRefundNo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 平台退款单号
|
||||
/// </summary>
|
||||
[XmlElement("refund_id")]
|
||||
public string RefundId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 退款渠道 ORIGINAL—原路退款,默认
|
||||
/// </summary>
|
||||
[XmlElement("refund_channel")]
|
||||
public string RefundChannel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 退款总金额,单位为分,可以做部分退款
|
||||
/// </summary>
|
||||
[XmlElement("refund_fee")]
|
||||
public int RefundFee { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 退款时间 yyyyMMddHHmmss
|
||||
/// </summary>
|
||||
[XmlElement("refund_time")]
|
||||
public string RefundTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// SUCCESS—退款成功
|
||||
/// FAIL—退款失败
|
||||
/// PROCESSING—退款处理中
|
||||
/// NOTSURE—未确定, 需要商户原退款单号重新发起
|
||||
/// CHANGE—转入代发,退款到银行发现用户的卡作废或者冻结了,导致原路退款银行卡失败,资金回流到商户的现金帐号,需要商户人工干预,通过线下或者平台转账的方式进行退款。
|
||||
/// </summary>
|
||||
[XmlElement("refund_status")]
|
||||
public string RefundStatus { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.WeiFuTong.Refund
|
||||
{
|
||||
/// <summary>
|
||||
/// 威富通退款请求
|
||||
/// </summary>
|
||||
public class WeiFuTongRefundRequest : WeiFuTongRequestBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 商户系统内部的订单号, out_trade_no和transaction_id至少一个必填,同时存在时transaction_id优先
|
||||
/// </summary>
|
||||
[XmlElement("out_trade_no")]
|
||||
public string OutTradeNo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 可传入平台订单号或第三方商户单号(third_order_no), out_trade_no和transaction_id至少一个必填,同时存在时transaction_id优先
|
||||
/// </summary>
|
||||
[XmlElement("transaction_id")]
|
||||
public string TransactionId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 商户退款单号,32个字符内、可包含字母,确保在商户系统唯一。同个退款单号多次请求,平台当一个单处理,只会退一次款。如果出现退款不成功,请采用原退款单号重新发起,避免出现重复退款。
|
||||
/// </summary>
|
||||
[XmlElement("out_refund_no")]
|
||||
public string OutRefundNo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 订单总金额,单位为分
|
||||
/// </summary>
|
||||
[XmlElement("total_fee")]
|
||||
public int OrderTotalFee { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 退款总金额,单位为分,可以做部分退款
|
||||
/// </summary>
|
||||
[XmlElement("refund_fee")]
|
||||
public int RefundFee { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 操作员帐号,默认为商户号
|
||||
/// </summary>
|
||||
[XmlElement("op_user_id")]
|
||||
public string OpUserId => MchId;
|
||||
|
||||
/// <summary>
|
||||
/// 退款渠道ORIGINAL-原路退款,默认
|
||||
/// </summary>
|
||||
[XmlElement("refund_channel")]
|
||||
public string RefundChannel => "ORIGINAL";
|
||||
|
||||
public WeiFuTongRefundRequest() : base("unified.trade.refund")
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.WeiFuTong.Refund
|
||||
{
|
||||
/// <summary>
|
||||
/// 威富通退款响应
|
||||
/// </summary>
|
||||
[XmlRoot("xml", Namespace = "")]
|
||||
public class WeiFuTongRefundResponse : WeiFuTongResponseBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 平台交易号
|
||||
/// </summary>
|
||||
[XmlElement("transaction_id")]
|
||||
public string TransactionId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 商户系统内部的订单号
|
||||
/// </summary>
|
||||
[XmlElement("out_trade_no")]
|
||||
public string OutTradeNo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 商户退款单号
|
||||
/// </summary>
|
||||
[XmlElement("out_refund_no")]
|
||||
public string OutRefundNo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 平台退款单号
|
||||
/// </summary>
|
||||
[XmlElement("refund_id")]
|
||||
public string RefundId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 退款渠道 ORIGINAL—原路退款,默认
|
||||
/// </summary>
|
||||
[XmlElement("refund_channel")]
|
||||
public string RefundChannel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 退款总金额,单位为分,可以做部分退款
|
||||
/// </summary>
|
||||
[XmlElement("refund_fee")]
|
||||
public int RefundFee { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
using System.Xml.Serialization;
|
||||
using Hncore.Infrastructure.Common;
|
||||
using Hncore.Pass.PaymentCenter.Domain;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.WeiFuTong.SwipeCard
|
||||
{
|
||||
/// <summary>
|
||||
/// 刷卡支付下单请求
|
||||
/// </summary>
|
||||
[XmlRoot("xml", Namespace = "")]
|
||||
public class SwipeCardCreateOrderRequest : WeiFuTongRequestBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 商户系统内部的订单号 ,5到32个字符、 只能包含字母数字或者下划线,区分大小写,每次下单请求确保在商户系统唯一
|
||||
/// </summary>
|
||||
[XmlElement("out_trade_no")]
|
||||
public string OutTradeNo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 终端设备号,商户自定义。特别说明:对于QQ钱包支付,此参数必传,否则会报错。
|
||||
/// </summary>
|
||||
[XmlElement("device_info")]
|
||||
public string DeviceInfo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 商品描述
|
||||
/// </summary>
|
||||
[XmlElement("body")]
|
||||
public string Body { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 总金额,以分为单位,不允许包含任何字、符号
|
||||
/// </summary>
|
||||
[XmlElement("total_fee")]
|
||||
public int TotalFee { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 订单生成的机器 IP
|
||||
/// </summary>
|
||||
[XmlElement("mch_create_ip")]
|
||||
public string MchCreateIp { get; set; } = NetworkHelper.GetPublicIp();
|
||||
|
||||
/// <summary>
|
||||
/// 扫码支付授权码, 设备读取用户展示的条码或者二维码信息
|
||||
/// </summary>
|
||||
[XmlElement("auth_code")]
|
||||
public string AuthCode { get; set; }
|
||||
|
||||
|
||||
public SwipeCardCreateOrderRequest()
|
||||
: base("unified.trade.micropay")
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Xml.Serialization;
|
||||
using Hncore.Pass.PaymentCenter.Domain;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.WeiFuTong.SwipeCard
|
||||
{
|
||||
/// <summary>
|
||||
/// 刷卡支付下单响应
|
||||
/// </summary>
|
||||
[XmlRoot("xml", Namespace = "")]
|
||||
public class SwipeCardCreateOrderResponse : WeiFuTongResponseBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 用来判断是否需要调用查询接口,值为Y时需要,值为N时不需要
|
||||
/// </summary>
|
||||
[XmlElement("need_query")]
|
||||
public string NeedQuery { get; set; }
|
||||
|
||||
//以下字段在 status 为 0的时候有返回
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 门店编号,由平台分配
|
||||
/// </summary>
|
||||
[XmlElement("mch_id")]
|
||||
public string MchId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 终端设备号
|
||||
/// </summary>
|
||||
[XmlElement("device_info")]
|
||||
public string DeviceInfo { get; set; }
|
||||
|
||||
|
||||
//以下字段在 status 和 result_code 都为 0的时候有返回
|
||||
|
||||
/// <summary>
|
||||
/// 用户在受理商户 appid 下的唯一标识
|
||||
/// </summary>
|
||||
[XmlElement("openid")]
|
||||
public string OpenId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 用户在子商户appid下的唯一标识
|
||||
/// </summary>
|
||||
[XmlElement("sub_openid")]
|
||||
public string SubOpenId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// pay.wechat.micropay——微信刷卡支付
|
||||
/// pay.alipay.micropay——支付宝刷卡支付
|
||||
/// pay.jdpay.micropay——京东刷卡支付
|
||||
/// pay.qq.micropay——QQ钱包刷卡支付
|
||||
/// pay.shiming.micropay——会员卡支付
|
||||
/// pay.unionpay.micropay——银联支付
|
||||
/// pay.bestpay.micropay——翼支付
|
||||
/// </summary>
|
||||
[XmlElement("trade_type")]
|
||||
public string TradeType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 用户是否关注公众账号,Y-关注,N-未关注,仅在公众账号类型支付有效
|
||||
/// </summary>
|
||||
[XmlElement("is_subscribe")]
|
||||
public string IsSubscribe { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 支付结果:0—成功;其它—失败
|
||||
/// </summary>
|
||||
[XmlElement("pay_result")]
|
||||
public int PayResult { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 支付结果信息,支付成功时为空
|
||||
/// </summary>
|
||||
[XmlElement("pay_info")]
|
||||
public string PayInfo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 平台交易号
|
||||
/// </summary>
|
||||
[XmlElement("transaction_id")]
|
||||
public string TransactionId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 第三方订单号
|
||||
/// </summary>
|
||||
[XmlElement("out_transaction_id")]
|
||||
public string OutTransactionId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 第三方商户单号,可在支持的商户扫码退款
|
||||
/// </summary>
|
||||
[XmlElement("third_order_no")]
|
||||
public string ThirdOrderNo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 用户是否关注子公众账号,Y-关注,N-未关注,仅在公众账号类型支付有效
|
||||
/// </summary>
|
||||
[XmlElement("sub_is_subscribe")]
|
||||
public string SubIsSubscribe { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 子商户appid
|
||||
/// </summary>
|
||||
[XmlElement("sub_appid")]
|
||||
public string SubAppId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 商户系统内部的定单号,32个字符内、可包含字母
|
||||
/// </summary>
|
||||
[XmlElement("out_trade_no")]
|
||||
public string OutTradeNo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 总金额,以分为单位,不允许包含任何字、符号
|
||||
/// </summary>
|
||||
[XmlElement("total_fee")]
|
||||
public int TotalFee { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 现金支付金额【微信】
|
||||
/// </summary>
|
||||
[XmlElement("cash_fee")]
|
||||
public int CashFee { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 开票金额 【支付宝】
|
||||
/// </summary>
|
||||
[XmlElement("invoice_amount")]
|
||||
public string InvoiceAmount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 集分宝付款金额【支付宝】
|
||||
/// </summary>
|
||||
[XmlElement("point_amount")]
|
||||
public string PointAmount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 现金券金额【微信】
|
||||
/// </summary>
|
||||
[XmlElement("coupon_fee")]
|
||||
public string CouponFee { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 交易支付使用的资金渠道 【支付宝】
|
||||
/// </summary>
|
||||
[XmlElement("fund_bill_list")]
|
||||
public string FundChannel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 实收金额【支付宝】实收金额,单位为元,两位小数。该金额为本笔交易,商户账户能够实际收到的金额
|
||||
/// </summary>
|
||||
[XmlElement("receipt_amount")]
|
||||
public string ReceiptAmount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 买家实付金额【支付宝】买家实付金额,单位为元,两位小数。该金额代表该笔交易买家实际支付的金额,不包含商户折扣等金额
|
||||
/// </summary>
|
||||
[XmlElement("buyer_pay_amount")]
|
||||
public string BuyerPayAmount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 货币类型,符合 ISO 4217 标准的三位字母代码,默认人民币:CNY
|
||||
/// </summary>
|
||||
[XmlElement("fee_type")]
|
||||
public string FeeType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 商家数据包,原样返回
|
||||
/// </summary>
|
||||
[XmlElement("attach")]
|
||||
public string Attach { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 付款银行
|
||||
/// </summary>
|
||||
[XmlElement("bank_type")]
|
||||
public string BankType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 银行订单号,若为微信支付则为空
|
||||
/// </summary>
|
||||
[XmlElement("bank_billno")]
|
||||
public string BankBillno { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 支付完成时间,格式为yyyyMMddHHmmss,如2009年12月27日9点10分10秒表示为20091227091010。时区为GMT+8 beijing。该时间取自平台服务器
|
||||
/// </summary>
|
||||
[XmlElement("time_end")]
|
||||
public string TimeEnd { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取支付成功时间
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public DateTime GetPaySuccessTime()
|
||||
{
|
||||
DateTime.TryParseExact(TimeEnd, "yyyyMMddHHmmss", null, DateTimeStyles.None, out var paySuccessTime);
|
||||
|
||||
return paySuccessTime;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
100
Services/Hncore.Pass.PaymentCenter/Pay/WeiFuTong/Util.cs
Normal file
100
Services/Hncore.Pass.PaymentCenter/Pay/WeiFuTong/Util.cs
Normal file
@@ -0,0 +1,100 @@
|
||||
using Hncore.Infrastructure.Common;
|
||||
using Hncore.Infrastructure.Data;
|
||||
using Hncore.Infrastructure.Extension;
|
||||
using Hncore.Pass.PaymentCenter.Model;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using XC.RSAUtil;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.WeiFuTong
|
||||
{
|
||||
public class Util
|
||||
{
|
||||
public static void CheckSignFromXml(string xmlText, MchInfo mchInfo)
|
||||
{
|
||||
XmlDocument xmlDoc = new XmlDocument();
|
||||
xmlDoc.XmlResolver = null;
|
||||
xmlDoc.LoadXml(xmlText);
|
||||
XmlNode root = xmlDoc.SelectSingleNode("xml");
|
||||
XmlNodeList xnl = root.ChildNodes;
|
||||
|
||||
if (root.SelectSingleNode("status") == null || root.SelectSingleNode("status").InnerText != "0")
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SortedDictionary<string, string> dic = new SortedDictionary<string, string>();
|
||||
string responseSign = "";
|
||||
string signType = "MD5";
|
||||
|
||||
|
||||
foreach (XmlNode xnf in xnl)
|
||||
{
|
||||
var parameter = xnf.Name;
|
||||
var parameterValue = xnf.InnerText;
|
||||
|
||||
if (parameter == "sign")
|
||||
{
|
||||
responseSign = parameterValue;
|
||||
}
|
||||
|
||||
if (parameter == "sign_type")
|
||||
{
|
||||
signType = parameterValue;
|
||||
}
|
||||
|
||||
if (parameter.Has() && parameterValue.Has() && parameter != "sign")
|
||||
{
|
||||
if (dic.Keys.Contains(parameter))
|
||||
{
|
||||
dic.Remove(parameter);
|
||||
}
|
||||
|
||||
dic.Add(parameter, parameterValue);
|
||||
}
|
||||
}
|
||||
|
||||
string sign = "";
|
||||
|
||||
foreach (var item in dic)
|
||||
{
|
||||
sign += item.Key + "=" + item.Value + "&";
|
||||
}
|
||||
|
||||
|
||||
if (signType == "MD5" || string.IsNullOrEmpty(signType))
|
||||
{
|
||||
sign += "key=" + mchInfo.Key;
|
||||
sign = SecurityHelper.GetMd5Hash(sign).ToUpper();
|
||||
|
||||
if (responseSign.ToUpper() != sign)
|
||||
{
|
||||
BusinessException.Throw("验签失败");
|
||||
}
|
||||
}
|
||||
else if (signType == "RSA_1_1")
|
||||
{
|
||||
sign = sign.Substring(0, sign.Length - 1);
|
||||
|
||||
var verify = new RsaPkcs8Util(Encoding.UTF8
|
||||
, mchInfo.RSAPublicKey
|
||||
, mchInfo.RSAPrivateKey)
|
||||
.VerifyData(sign, responseSign, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1);
|
||||
|
||||
if (!verify)
|
||||
{
|
||||
BusinessException.Throw("验签失败");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
BusinessException.Throw("未知签名方式");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
using System.Xml.Serialization;
|
||||
using Hncore.Infrastructure.Common;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.WeiFuTong.WechatJsPay
|
||||
{
|
||||
/// <summary>
|
||||
/// 微信小程序、公众号支付
|
||||
/// </summary>
|
||||
[XmlRoot("xml", Namespace = "")]
|
||||
public class WechatJsPayCreateOrderRequest : WeiFuTongRequestBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 原生JS
|
||||
/// </summary>
|
||||
[XmlElement("is_raw")]
|
||||
public string IsRaw { get; set; } = "1";
|
||||
|
||||
/// <summary>
|
||||
/// 值为1,表示小程序支付;不传或值不为1,表示公众账号内支付
|
||||
/// </summary>
|
||||
[XmlElement("is_minipg")]
|
||||
public string IsMinipg { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 商户系统内部的订单号 ,5到32个字符、 只能包含字母数字或者下划线,区分大小写,每次下单请求确保在商户系统唯一
|
||||
/// </summary>
|
||||
[XmlElement("out_trade_no")]
|
||||
public string OutTradeNo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 商品描述
|
||||
/// </summary>
|
||||
[XmlElement("body")]
|
||||
public string Body { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 微信用户关注商家公众号的openid
|
||||
/// </summary>
|
||||
[XmlElement("sub_openid")]
|
||||
public string UserOpenId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 公众账号或小程序ID
|
||||
/// </summary>
|
||||
[XmlElement("sub_appid")]
|
||||
public string AppId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 总金额,以分为单位,不允许包含任何字、符号
|
||||
/// </summary>
|
||||
[XmlElement("total_fee")]
|
||||
public int TotalFee { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 订单生成的机器 IP
|
||||
/// </summary>
|
||||
[XmlElement("mch_create_ip")]
|
||||
public string MchCreateIp { get; set; } = NetworkHelper.GetPublicIp();
|
||||
|
||||
[XmlElement("notify_url")]
|
||||
public string NotifyUrl { get; set; }
|
||||
|
||||
public WechatJsPayCreateOrderRequest() : base("pay.weixin.jspay")
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.WeiFuTong.WechatJsPay
|
||||
{
|
||||
[XmlRoot("xml", Namespace = "")]
|
||||
public class WechatJsPayCreateOrderResponse : WeiFuTongResponseBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 原生态js支付信息或小程序支付信息
|
||||
/// </summary>
|
||||
[XmlElement("pay_info")]
|
||||
public string PayInfo { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.WeiFuTong
|
||||
{
|
||||
public class WeiFuTongClient
|
||||
{
|
||||
private IHttpClientFactory _httpClientFactory;
|
||||
|
||||
public WeiFuTongClient(IHttpClientFactory httpClientFactory)
|
||||
{
|
||||
_httpClientFactory = httpClientFactory;
|
||||
}
|
||||
|
||||
public HttpClient CreateHttpClient()
|
||||
{
|
||||
var httpClient = _httpClientFactory.CreateClient();
|
||||
|
||||
httpClient.Timeout = TimeSpan.FromMinutes(5);
|
||||
|
||||
return httpClient;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using Hncore.Infrastructure.Data;
|
||||
using Hncore.Pass.PaymentCenter.Domain;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.WeiFuTong
|
||||
{
|
||||
public class WeiFuTongConfig
|
||||
{
|
||||
public const string WeiFuTongUrl = "https://pay.hstypay.com/v2/pay/gateway";
|
||||
|
||||
public const string QuanFuTongUrl = "https://pay.swiftpass.cn/pay/gateway";
|
||||
}
|
||||
|
||||
public static class Extension
|
||||
{
|
||||
public static string GetUrl(this PaymentChannel paymentChannel)
|
||||
{
|
||||
if (paymentChannel == PaymentChannel.WeiFuTong)
|
||||
{
|
||||
return WeiFuTongConfig.WeiFuTongUrl;
|
||||
}
|
||||
|
||||
if (paymentChannel == PaymentChannel.QuanFuTong)
|
||||
{
|
||||
return WeiFuTongConfig.QuanFuTongUrl;
|
||||
}
|
||||
|
||||
BusinessException.Throw("未知支付渠道");
|
||||
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Xml.Serialization;
|
||||
using Hncore.Infrastructure.Common;
|
||||
using XC.RSAUtil;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.WeiFuTong
|
||||
{
|
||||
[XmlRoot("xml", Namespace = "")]
|
||||
public class WeiFuTongDTOBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 随机字符串,不长于 32 位
|
||||
/// </summary>
|
||||
[XmlElement("nonce_str")]
|
||||
public string NonceStr { get; set; } = Guid.NewGuid().ToString().Replace("-", "");
|
||||
|
||||
/// <summary>
|
||||
/// 签名类型,取值:MD5默认:MD5
|
||||
/// </summary>
|
||||
[XmlElement("sign_type")]
|
||||
public string SignType { get; set; } = "MD5";
|
||||
|
||||
/// <summary>
|
||||
/// MD5签名结果
|
||||
/// </summary>
|
||||
[XmlElement("sign")]
|
||||
public string Sign { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 版本号,version默认值是2.0
|
||||
/// </summary>
|
||||
[XmlElement("version")]
|
||||
public string Version { get; set; } = "2.0";
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Xml.Serialization;
|
||||
using Hncore.Infrastructure.Common;
|
||||
using Hncore.Pass.PaymentCenter.Domain;
|
||||
using Hncore.Pass.PaymentCenter.Model;
|
||||
using XC.RSAUtil;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.WeiFuTong
|
||||
{
|
||||
[XmlRoot("xml", Namespace = "")]
|
||||
public class WeiFuTongRequestBase : WeiFuTongDTOBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 接口类型
|
||||
/// </summary>
|
||||
[XmlElement("service")]
|
||||
public string Service { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 门店编号,由平台分配
|
||||
/// </summary>
|
||||
[XmlElement("mch_id")]
|
||||
public string MchId { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 连锁商户号
|
||||
/// </summary>
|
||||
[XmlElement("groupno")]
|
||||
public string GroupNo { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 可选值 UTF-8 ,默认为 UTF-8
|
||||
/// </summary>
|
||||
[XmlElement("charset")]
|
||||
public string Charset { get; set; } = "UTF-8";
|
||||
|
||||
private MchInfo _mchInfo;
|
||||
|
||||
public MchInfo MchInfo
|
||||
{
|
||||
get => _mchInfo;
|
||||
set
|
||||
{
|
||||
_mchInfo = value;
|
||||
MchId = value.MchId;
|
||||
GroupNo = value.GroupNo;
|
||||
}
|
||||
}
|
||||
|
||||
private PaymentChannel _paymentChannel;
|
||||
|
||||
|
||||
public PaymentChannel PaymentChannel
|
||||
{
|
||||
get => _paymentChannel;
|
||||
set
|
||||
{
|
||||
_paymentChannel = value;
|
||||
|
||||
if (value == PaymentChannel.QuanFuTong)
|
||||
{
|
||||
SignType = "RSA_1_1";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public WeiFuTongRequestBase(string service)
|
||||
{
|
||||
Service = service;
|
||||
}
|
||||
|
||||
protected virtual SortedDictionary<string, string> ToSortDic()
|
||||
{
|
||||
var type = GetType();
|
||||
var properties = type.GetProperties();
|
||||
|
||||
SortedDictionary<string, string> dic = new SortedDictionary<string, string>();
|
||||
|
||||
foreach (var property in properties)
|
||||
{
|
||||
var xmlAttr = property.GetCustomAttributes(typeof(XmlElementAttribute), false);
|
||||
|
||||
if (!xmlAttr.Any())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string name = ((XmlElementAttribute) xmlAttr[0]).ElementName;
|
||||
|
||||
object valueObj = property.GetValue(this, null);
|
||||
|
||||
if (valueObj == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string value = valueObj.ToString();
|
||||
|
||||
if (!string.IsNullOrEmpty(value))
|
||||
{
|
||||
dic[name] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return dic;
|
||||
}
|
||||
|
||||
protected virtual void CreateSign(MchInfo mchInfo)
|
||||
{
|
||||
Sign = "";
|
||||
string sign = "";
|
||||
|
||||
foreach (var item in ToSortDic())
|
||||
{
|
||||
sign += item.Key + "=" + item.Value + "&";
|
||||
}
|
||||
|
||||
if (SignType == "MD5")
|
||||
{
|
||||
sign += "key=" + mchInfo.Key;
|
||||
sign = SecurityHelper.GetMd5Hash(sign).ToUpper();
|
||||
}
|
||||
else if (SignType == "RSA_1_1")
|
||||
{
|
||||
sign = sign.Substring(0, sign.Length - 1);
|
||||
|
||||
sign = new RsaPkcs8Util(Encoding.UTF8
|
||||
, mchInfo.RSAPublicKey
|
||||
, mchInfo.RSAPrivateKey)
|
||||
.SignData(sign, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1);
|
||||
}
|
||||
|
||||
|
||||
Sign = sign;
|
||||
}
|
||||
|
||||
public string ToXml()
|
||||
{
|
||||
CreateSign(MchInfo);
|
||||
|
||||
StringBuilder sb = new StringBuilder("<xml>\n");
|
||||
|
||||
foreach (var item in ToSortDic())
|
||||
{
|
||||
string key = item.Key;
|
||||
sb.Append("\t<").Append(key).Append("><![CDATA[").Append(item.Value).Append("]]></").Append(key)
|
||||
.Append(">\n");
|
||||
}
|
||||
|
||||
return sb.Append("</xml>").ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
using System.Xml.Serialization;
|
||||
using Hncore.Infrastructure.Data;
|
||||
using Hncore.Infrastructure.Extension;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.WeiFuTong
|
||||
{
|
||||
[XmlRoot("xml", Namespace = "")]
|
||||
public class WeiFuTongResponseBase : WeiFuTongDTOBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 0表示成功,非0表示失败此字段是通信标识,非交易标识,交易是否成功需要查看 result_code 来判断
|
||||
/// </summary>
|
||||
[XmlElement("status")]
|
||||
public string Status { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 返回信息,如非空,为错误原因签名失败参数格式校验错误
|
||||
/// </summary>
|
||||
[XmlElement("message")]
|
||||
public string Message { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 业务结果 0表示成功,非0表示失败
|
||||
/// </summary>
|
||||
[XmlElement("result_code")]
|
||||
public string ResultCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 错误代码
|
||||
/// </summary>
|
||||
[XmlElement("err_code")]
|
||||
public string ErrCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 错误代码描述
|
||||
/// </summary>
|
||||
[XmlElement("err_msg")]
|
||||
public string ErrMsg { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 可选值 UTF-8 ,默认为 UTF-8
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
[XmlElement("charset")]
|
||||
public string Charset { get; set; }
|
||||
|
||||
public virtual bool HasError()
|
||||
{
|
||||
return Status != "0" || ResultCode != "0";
|
||||
}
|
||||
|
||||
public virtual void HandleError()
|
||||
{
|
||||
if (Status != "0")
|
||||
{
|
||||
BusinessException.Throw($"支付平台通讯失败,status:{Status},message:{Message}");
|
||||
}
|
||||
|
||||
if (ResultCode != "0")
|
||||
{
|
||||
BusinessException.Throw($"支付平台业务处理失败,result_code:{ResultCode},err_msg:{ErrMsg }");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
using Hncore.Infrastructure.Common;
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.WxPay.WechatJsPay
|
||||
{
|
||||
/// <summary>
|
||||
/// 微信扫码付款
|
||||
/// </summary>
|
||||
[XmlRoot("xml", Namespace = "")]
|
||||
public class WxH5PayCreateOrderRequest : WxJsPayDataBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 商户系统内部的订单号 ,5到32个字符、 只能包含字母数字或者下划线,区分大小写,每次下单请求确保在商户系统唯一
|
||||
/// </summary>
|
||||
[XmlElement("out_trade_no")]
|
||||
public string OutTradeNo { get; set; }
|
||||
|
||||
[XmlElement("attach")]
|
||||
public string Attach { get; set; }
|
||||
/// <summary>
|
||||
/// 商品描述
|
||||
/// </summary>
|
||||
[XmlElement("body")]
|
||||
public string Body { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 总金额,以分为单位,不允许包含任何字、符号
|
||||
/// </summary>
|
||||
[XmlElement("total_fee")]
|
||||
public int TotalFee { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// H5支付
|
||||
/// </summary>
|
||||
[XmlElement("trade_type")]
|
||||
public string TradeType { get; set; } = "MWEB";
|
||||
|
||||
[XmlElement("notify_url")]
|
||||
public string NotifyUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 公众账号或小程序ID
|
||||
/// </summary>
|
||||
[XmlElement("appid")]
|
||||
public string AppId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 公众账号或小程序ID
|
||||
/// </summary>
|
||||
[XmlElement("mch_id")]
|
||||
public string MchId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 订单生成的机器 IP
|
||||
/// </summary>
|
||||
[XmlElement("spbill_create_ip")]
|
||||
public string MchCreateIp { get; set; } = NetworkHelper.GetPublicIp();
|
||||
|
||||
/// <summary>
|
||||
/// 商品Id
|
||||
/// </summary>
|
||||
[XmlElement("product_id")]
|
||||
public string ProductId { get; set; }
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 随机字符串
|
||||
/// </summary>
|
||||
[XmlElement("nonce_str")]
|
||||
public string NonceStr { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 绝对过期时间 yyyyMMddHHmmss
|
||||
/// </summary>
|
||||
[XmlElement("time_expire")]
|
||||
public string TimeExpire { get; set; }
|
||||
|
||||
|
||||
public int TenantId { get; set; }
|
||||
|
||||
public int StoreId { get; set; }
|
||||
|
||||
|
||||
public WxH5PayCreateOrderRequest()
|
||||
{
|
||||
this.NonceStr =this.GenerateNonceStr();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
using Hncore.Infrastructure.Common;
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.WxPay.WechatJsPay
|
||||
{
|
||||
/// <summary>
|
||||
/// 微信小程序、公众号支付
|
||||
/// </summary>
|
||||
[XmlRoot("xml", Namespace = "")]
|
||||
public class WxJsPayCreateOrderRequest: WxJsPayDataBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 商户系统内部的订单号 ,5到32个字符、 只能包含字母数字或者下划线,区分大小写,每次下单请求确保在商户系统唯一
|
||||
/// </summary>
|
||||
[XmlElement("out_trade_no")]
|
||||
public string OutTradeNo { get; set; }
|
||||
|
||||
[XmlElement("attach")]
|
||||
public string Attach { get; set; }
|
||||
/// <summary>
|
||||
/// 商品描述
|
||||
/// </summary>
|
||||
[XmlElement("body")]
|
||||
public string Body { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 总金额,以分为单位,不允许包含任何字、符号
|
||||
/// </summary>
|
||||
[XmlElement("total_fee")]
|
||||
public int TotalFee { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 原生JS
|
||||
/// </summary>
|
||||
[XmlElement("trade_type")]
|
||||
public string TradeType { get; set; } = "JSAPI";//NATIVE
|
||||
|
||||
[XmlElement("notify_url")]
|
||||
public string NotifyUrl { get; set; }
|
||||
/// <summary>
|
||||
/// 微信用户关注商家公众号的openid
|
||||
/// </summary>
|
||||
[XmlElement("openid")]
|
||||
public string UserOpenId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 公众账号或小程序ID
|
||||
/// </summary>
|
||||
[XmlElement("appid")]
|
||||
public string AppId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 公众账号或小程序ID
|
||||
/// </summary>
|
||||
[XmlElement("mch_id")]
|
||||
public string MchId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 订单生成的机器 IP
|
||||
/// </summary>
|
||||
[XmlElement("spbill_create_ip")]
|
||||
public string MchCreateIp { get; set; } = NetworkHelper.GetPublicIp();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 随机字符串
|
||||
/// </summary>
|
||||
[XmlElement("nonce_str")]
|
||||
public string NonceStr { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 绝对过期时间 yyyyMMddHHmmss
|
||||
/// </summary>
|
||||
[XmlElement("time_expire")]
|
||||
public string TimeExpire { get; set; }
|
||||
|
||||
|
||||
public int TenantId { get; set; }
|
||||
|
||||
public int StoreId { get; set; }
|
||||
|
||||
public WxJsPayCreateOrderRequest()
|
||||
{
|
||||
this.NonceStr =this.GenerateNonceStr();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using Hncore.Pass.PaymentCenter.WxPay.WechatJsPay;
|
||||
using Newtonsoft.Json;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.Pay.WxPay
|
||||
{
|
||||
public class WxJsPayCreateOrderResponse: WxJsPayDataBase
|
||||
{
|
||||
[XmlElement("appId")]
|
||||
public string appId { get; set; }
|
||||
[XmlElement("timeStamp")]
|
||||
public string timeStamp { get; set; }
|
||||
public string nonceStr { get; set; }
|
||||
public string prepay_id { get; set; }
|
||||
public string signType { get; set; }
|
||||
public string paySign { get; set; }
|
||||
[JsonIgnore]
|
||||
public string package => $"prepay_id={this.prepay_id}";
|
||||
}
|
||||
}
|
||||
166
Services/Hncore.Pass.PaymentCenter/Pay/WxPay/WxJsPayData.cs
Normal file
166
Services/Hncore.Pass.PaymentCenter/Pay/WxPay/WxJsPayData.cs
Normal file
@@ -0,0 +1,166 @@
|
||||
using Hncore.Infrastructure.Common;
|
||||
using Hncore.Infrastructure.Extension;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.WxPay.WechatJsPay
|
||||
{
|
||||
/// <summary>
|
||||
/// 微信小程序、公众号支付,H5支付 数据签名校验
|
||||
/// </summary>
|
||||
public class WxPayChecker: SortedDictionary<string, string>
|
||||
{
|
||||
public string SignType { get; set; } = "MD5";// "HMAC-SHA256";
|
||||
public string MchKey { get; set; }
|
||||
|
||||
/**
|
||||
* 生成时间戳,标准北京时间,时区为东八区,自1970年1月1日 0点0分0秒以来的秒数
|
||||
* @return 时间戳
|
||||
*/
|
||||
public string GenerateTimeStamp()
|
||||
{
|
||||
TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
|
||||
return Convert.ToInt64(ts.TotalSeconds).ToString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成随机串,随机串包含字母或数字
|
||||
* @return 随机串
|
||||
*/
|
||||
public string GenerateNonceStr()
|
||||
{
|
||||
return Guid.NewGuid().ToString("N");
|
||||
}
|
||||
|
||||
public virtual string CreateSign()
|
||||
{
|
||||
string sign = "";
|
||||
foreach (var item in this)
|
||||
{
|
||||
if (item.Key == "sign"|| item.Key == "paySign") continue;
|
||||
sign += item.Key + "=" + item.Value + "&";
|
||||
}
|
||||
if(this.MchKey.Has())
|
||||
sign += "key=" + this.MchKey;
|
||||
LogHelper.Trace("CreateSign Str", sign);
|
||||
if (SignType == "MD5")
|
||||
{
|
||||
sign = SecurityHelper.GetMd5Hash(sign);
|
||||
}
|
||||
else if (SignType == "HMAC-SHA256")
|
||||
{
|
||||
sign = CalcHMACSHA256Hash(sign, this.MchKey);
|
||||
}
|
||||
this["sign"] = sign;
|
||||
return sign;
|
||||
}
|
||||
|
||||
public bool IsSet(string key)
|
||||
{
|
||||
if (!this.ContainsKey(key)) return false;
|
||||
return this[key].Has();
|
||||
}
|
||||
|
||||
public virtual bool CheckSign()
|
||||
{
|
||||
//如果没有设置签名,则跳过检测
|
||||
if (!this.IsSet("sign"))
|
||||
{
|
||||
LogHelper.Error("CheckSign", "签名不存在!");
|
||||
return false;
|
||||
}
|
||||
//获取接收到的签名
|
||||
string return_sign = this["sign"];
|
||||
|
||||
//在本地计算新的签名
|
||||
string cal_sign = CreateSign();
|
||||
|
||||
if (cal_sign == return_sign)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private string CalcHMACSHA256Hash(string plaintext, string salt)
|
||||
{
|
||||
string result = "";
|
||||
var enc = Encoding.Default;
|
||||
byte[]
|
||||
baText2BeHashed = enc.GetBytes(plaintext),
|
||||
baSalt = enc.GetBytes(salt);
|
||||
System.Security.Cryptography.HMACSHA256 hasher = new HMACSHA256(baSalt);
|
||||
byte[] baHashedText = hasher.ComputeHash(baText2BeHashed);
|
||||
result = string.Join("", baHashedText.ToList().Select(b => b.ToString("x2")).ToArray());
|
||||
return result;
|
||||
}
|
||||
|
||||
public string ToXml()
|
||||
{
|
||||
CreateSign();
|
||||
StringBuilder sb = new StringBuilder("<xml>\n");
|
||||
foreach (var item in this)
|
||||
{
|
||||
string key = item.Key;
|
||||
sb.Append("\t<").Append(key).Append("><![CDATA[").Append(item.Value).Append("]]></").Append(key)
|
||||
.Append(">\n");
|
||||
}
|
||||
|
||||
return sb.Append("</xml>").ToString();
|
||||
}
|
||||
|
||||
public WxPayChecker FromXml(string xml)
|
||||
{
|
||||
if (string.IsNullOrEmpty(xml))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
LogHelper.Info("FromXml", xml);
|
||||
|
||||
this.Clear();
|
||||
XmlDocument xmlDoc = new XmlDocument() { XmlResolver = null };
|
||||
xmlDoc.LoadXml(xml);
|
||||
XmlNode xmlNode = xmlDoc.FirstChild;//获取到根节点<xml>
|
||||
XmlNodeList nodes = xmlNode.ChildNodes;
|
||||
foreach (XmlNode xn in nodes)
|
||||
{
|
||||
XmlElement xe = (XmlElement)xn;
|
||||
this[xe.Name] = xe.InnerText;//获取xml的键值对到WxPayData内部的数据中
|
||||
}
|
||||
if (!CheckSign())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public WxPayChecker FromXmlNoCheckSign(string xml)
|
||||
{
|
||||
if (string.IsNullOrEmpty(xml))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
LogHelper.Info("FromXml", xml);
|
||||
|
||||
this.Clear();
|
||||
XmlDocument xmlDoc = new XmlDocument() { XmlResolver = null };
|
||||
xmlDoc.LoadXml(xml);
|
||||
XmlNode xmlNode = xmlDoc.FirstChild;//获取到根节点<xml>
|
||||
XmlNodeList nodes = xmlNode.ChildNodes;
|
||||
foreach (XmlNode xn in nodes)
|
||||
{
|
||||
XmlElement xe = (XmlElement)xn;
|
||||
this[xe.Name] = xe.InnerText;//获取xml的键值对到WxPayData内部的数据中
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.WxPay.WechatJsPay
|
||||
{
|
||||
/// <summary>
|
||||
/// 微信小程序、公众号支付 数据签名校验
|
||||
/// </summary>
|
||||
public class WxJsPayDataBase
|
||||
{
|
||||
public string GenerateNonceStr()
|
||||
{
|
||||
return Guid.NewGuid().ToString("N");
|
||||
}
|
||||
public WxPayChecker ToPayData()
|
||||
{
|
||||
var type = GetType();
|
||||
var properties = type.GetProperties();
|
||||
|
||||
var data = new WxPayChecker();
|
||||
foreach (var property in properties)
|
||||
{
|
||||
var xmlAttr = property.GetCustomAttributes(typeof(XmlElementAttribute), false);
|
||||
|
||||
if (!xmlAttr.Any())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string name = ((XmlElementAttribute)xmlAttr[0]).ElementName;
|
||||
|
||||
object valueObj = property.GetValue(this, null);
|
||||
|
||||
if (valueObj == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string value = valueObj.ToString();
|
||||
|
||||
if (!string.IsNullOrEmpty(value))
|
||||
{
|
||||
data[name] = value;
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
50
Services/Hncore.Pass.PaymentCenter/Pay/WxPay/WxJsPayInfo.cs
Normal file
50
Services/Hncore.Pass.PaymentCenter/Pay/WxPay/WxJsPayInfo.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using Hncore.Infrastructure.Extension;
|
||||
using System;
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.WxPay.WechatJsPay
|
||||
{
|
||||
/// <summary>
|
||||
/// 微信小程序、公众号支付 数据签名校验
|
||||
/// </summary>
|
||||
[XmlRoot("xml", Namespace = "")]
|
||||
public class WxJsPayInfo: WxJsPayDataBase
|
||||
{
|
||||
public WxJsPayInfo(string appId,string signType,string package,string key)
|
||||
{
|
||||
this.appId = appId;
|
||||
this.signType = signType;
|
||||
this.package = package;
|
||||
this.nonceStr = this.GenerateNonceStr();
|
||||
this.timeStamp = DateTime.Now.GetUnixTimeStamp().ToString();
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
[XmlElement("appId")]
|
||||
public string appId { get; set; }
|
||||
|
||||
[XmlElement("timeStamp")]
|
||||
public string timeStamp { get; set; }
|
||||
|
||||
[XmlElement("nonceStr")]
|
||||
public string nonceStr { get; set; }
|
||||
|
||||
[XmlElement("signType")]
|
||||
public string signType { get; set; }
|
||||
|
||||
[XmlElement("package")]
|
||||
public string package { get; set; }
|
||||
|
||||
private string key;
|
||||
public string paySign => getSign();
|
||||
|
||||
private string getSign()
|
||||
{
|
||||
var data = this.ToPayData();
|
||||
data.SignType = this.signType;
|
||||
data.MchKey = this.key;
|
||||
return data.CreateSign();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using Hncore.Infrastructure.Common;
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.WxPay.WechatJsPay
|
||||
{
|
||||
/// <summary>
|
||||
/// 微信小程序、公众号支付
|
||||
/// </summary>
|
||||
[XmlRoot("xml", Namespace = "")]
|
||||
public class WxJsPayOrderQueryRequest : WxJsPayDataBase
|
||||
{
|
||||
[XmlElement("transaction_id")]
|
||||
public string TransactionId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 公众账号或小程序ID
|
||||
/// </summary>
|
||||
[XmlElement("appid")]
|
||||
public string AppId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 公众账号或小程序ID
|
||||
/// </summary>
|
||||
[XmlElement("mch_id")]
|
||||
public string MchId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 随机字符串
|
||||
/// </summary>
|
||||
[XmlElement("nonce_str")]
|
||||
public string NonceStr { get; set; }
|
||||
|
||||
public WxJsPayOrderQueryRequest()
|
||||
{
|
||||
this.NonceStr =this.GenerateNonceStr();
|
||||
}
|
||||
}
|
||||
}
|
||||
161
Services/Hncore.Pass.PaymentCenter/Pay/WxPay/WxPayClient.cs
Normal file
161
Services/Hncore.Pass.PaymentCenter/Pay/WxPay/WxPayClient.cs
Normal file
@@ -0,0 +1,161 @@
|
||||
using Hncore.Infrastructure.Common;
|
||||
using Hncore.Infrastructure.Serializer;
|
||||
using Hncore.Pass.PaymentCenter.Model;
|
||||
using Hncore.Pass.PaymentCenter.WxPay.WechatJsPay;
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.Pay.WxPay
|
||||
{
|
||||
public class WxPayClient
|
||||
{
|
||||
public static readonly string url = "https://api.mch.weixin.qq.com";
|
||||
IHttpClientFactory m_HttpClientFactory;
|
||||
public WxPayClient(IHttpClientFactory _HttpClientFactory)
|
||||
{
|
||||
m_HttpClientFactory = _HttpClientFactory;
|
||||
}
|
||||
|
||||
public async Task<string> CreatePayOrderAsync(WxJsPayCreateOrderRequest request, MchInfo mchInfo)
|
||||
{
|
||||
var httpClient = m_HttpClientFactory.CreateClient();
|
||||
var payData = request.ToPayData();
|
||||
payData.MchKey = mchInfo.Key;
|
||||
var body = payData.ToXml();
|
||||
httpClient.Timeout = TimeSpan.FromMinutes(5);
|
||||
var response = await httpClient
|
||||
.PostAsync($"{url}/pay/unifiedorder", new StringContent(body));
|
||||
|
||||
var resText = await response.Content.ReadAsStringAsync();
|
||||
payData.FromXml(resText);
|
||||
if (payData.IsSet("err_code_des"))
|
||||
{
|
||||
LogHelper.Error("ScanPayCreateOrderAsync Error", payData["err_code_des"]);
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (!payData.IsSet("prepay_id"))
|
||||
{
|
||||
return "";
|
||||
}
|
||||
var appId = payData["appid"];
|
||||
var package = $"prepay_id={payData["prepay_id"]}";
|
||||
var resp = new WxJsPayInfo(appId, payData.SignType, package, mchInfo.Key);
|
||||
return resp.ToJson();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public async Task<string> JsPayCreateOrderAsync(WxJsPayCreateOrderRequest request, MchInfo mchInfo)
|
||||
{
|
||||
var httpClient = m_HttpClientFactory.CreateClient();
|
||||
var payData = request.ToPayData();
|
||||
payData.MchKey = mchInfo.Key;
|
||||
var body = payData.ToXml();
|
||||
httpClient.Timeout = TimeSpan.FromMinutes(5);
|
||||
var response = await httpClient
|
||||
.PostAsync($"{url}/pay/unifiedorder", new StringContent(body));
|
||||
|
||||
var resText = await response.Content.ReadAsStringAsync();
|
||||
payData.FromXml(resText);
|
||||
if (payData.IsSet("err_code_des"))
|
||||
{
|
||||
LogHelper.Error("ScanPayCreateOrderAsync Error", payData["err_code_des"]);
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (!payData.IsSet("prepay_id"))
|
||||
{
|
||||
return "";
|
||||
}
|
||||
var appId = payData["appid"];
|
||||
var package = $"prepay_id={payData["prepay_id"]}";
|
||||
var resp = new WxJsPayInfo(appId, payData.SignType, package, mchInfo.Key);
|
||||
return resp.ToJson();
|
||||
}
|
||||
|
||||
public async Task<string> ScanPayCreateOrderAsync(WxScanPayCreateOrderRequest request, MchInfo mchInfo)
|
||||
{
|
||||
var httpClient = m_HttpClientFactory.CreateClient();
|
||||
var payData = request.ToPayData();
|
||||
payData.MchKey = mchInfo.Key;
|
||||
var body = payData.ToXml();
|
||||
LogHelper.Info("ScanPayCreateOrderAsync body", body);
|
||||
httpClient.Timeout = TimeSpan.FromMinutes(5);
|
||||
var response = await httpClient
|
||||
.PostAsync($"{url}/pay/unifiedorder", new StringContent(body));
|
||||
|
||||
var resText = await response.Content.ReadAsStringAsync();
|
||||
payData.FromXml(resText);
|
||||
if (payData.IsSet("err_code_des"))
|
||||
{
|
||||
LogHelper.Error("ScanPayCreateOrderAsync Error", payData["err_code_des"]);
|
||||
return "";
|
||||
}
|
||||
if (!payData.IsSet("code_url"))
|
||||
{
|
||||
return "";
|
||||
}
|
||||
var code_url = payData["code_url"];
|
||||
return code_url;
|
||||
}
|
||||
|
||||
public async Task<string> H5PayCreateOrderAsync(WxH5PayCreateOrderRequest request, MchInfo mchInfo)
|
||||
{
|
||||
var httpClient = m_HttpClientFactory.CreateClient();
|
||||
var payData = request.ToPayData();
|
||||
payData.MchKey = mchInfo.Key;
|
||||
var body = payData.ToXml();
|
||||
httpClient.Timeout = TimeSpan.FromMinutes(5);
|
||||
var response = await httpClient
|
||||
.PostAsync($"{url}/pay/unifiedorder", new StringContent(body));
|
||||
|
||||
var resText = await response.Content.ReadAsStringAsync();
|
||||
payData.FromXml(resText);
|
||||
if (payData.IsSet("err_code_des"))
|
||||
{
|
||||
LogHelper.Error("H5PayCreateOrderAsync Error", payData["err_code_des"]);
|
||||
return "";
|
||||
}
|
||||
if (!payData.IsSet("mweb_url"))
|
||||
{
|
||||
return "";
|
||||
}
|
||||
var code_url = payData["mweb_url"];
|
||||
return code_url;
|
||||
}
|
||||
|
||||
|
||||
public async Task<bool> OrderQuery(WxJsPayOrderQueryRequest request, MchInfo mchInfo)
|
||||
{
|
||||
var httpClient = m_HttpClientFactory.CreateClient();
|
||||
request.MchId = mchInfo.MchId;
|
||||
var payData = request.ToPayData();
|
||||
payData.MchKey = mchInfo.Key;
|
||||
var body = payData.ToXml();
|
||||
httpClient.Timeout = TimeSpan.FromMinutes(5);
|
||||
var response = await httpClient
|
||||
.PostAsync($"{url}/pay/orderquery", new StringContent(body));
|
||||
|
||||
var resText = await response.Content.ReadAsStringAsync();
|
||||
payData.FromXml(resText);
|
||||
|
||||
if (payData["return_code"].ToString() == "SUCCESS" &&
|
||||
payData["result_code"].ToString() == "SUCCESS")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
using Hncore.Infrastructure.Common;
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.WxPay.WechatJsPay
|
||||
{
|
||||
/// <summary>
|
||||
/// 微信扫码付款
|
||||
/// </summary>
|
||||
[XmlRoot("xml", Namespace = "")]
|
||||
public class WxScanPayCreateOrderRequest : WxJsPayDataBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 商户系统内部的订单号 ,5到32个字符、 只能包含字母数字或者下划线,区分大小写,每次下单请求确保在商户系统唯一
|
||||
/// </summary>
|
||||
[XmlElement("out_trade_no")]
|
||||
public string OutTradeNo { get; set; }
|
||||
|
||||
[XmlElement("attach")]
|
||||
public string Attach { get; set; }
|
||||
/// <summary>
|
||||
/// 商品描述
|
||||
/// </summary>
|
||||
[XmlElement("body")]
|
||||
public string Body { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 总金额,以分为单位,不允许包含任何字、符号
|
||||
/// </summary>
|
||||
[XmlElement("total_fee")]
|
||||
public int TotalFee { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 原生JS
|
||||
/// </summary>
|
||||
[XmlElement("trade_type")]
|
||||
public string TradeType { get; set; } = "NATIVE";
|
||||
|
||||
[XmlElement("notify_url")]
|
||||
public string NotifyUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 公众账号或小程序ID
|
||||
/// </summary>
|
||||
[XmlElement("appid")]
|
||||
public string AppId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 公众账号或小程序ID
|
||||
/// </summary>
|
||||
[XmlElement("mch_id")]
|
||||
public string MchId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 订单生成的机器 IP
|
||||
/// </summary>
|
||||
[XmlElement("spbill_create_ip")]
|
||||
public string MchCreateIp { get; set; } = NetworkHelper.GetPublicIp();
|
||||
|
||||
/// <summary>
|
||||
/// 商品Id
|
||||
/// </summary>
|
||||
[XmlElement("product_id")]
|
||||
public string ProductId { get; set; }
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 随机字符串
|
||||
/// </summary>
|
||||
[XmlElement("nonce_str")]
|
||||
public string NonceStr { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 绝对过期时间 yyyyMMddHHmmss
|
||||
/// </summary>
|
||||
[XmlElement("time_expire")]
|
||||
public string TimeExpire { get; set; }
|
||||
|
||||
|
||||
public int TenantId { get; set; }
|
||||
|
||||
public int StoreId { get; set; }
|
||||
|
||||
|
||||
public WxScanPayCreateOrderRequest()
|
||||
{
|
||||
this.NonceStr =this.GenerateNonceStr();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using Hncore.Pass.PaymentCenter.WxPay.WechatJsPay;
|
||||
using Newtonsoft.Json;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.Pay.WxPay
|
||||
{
|
||||
public class WxScanPayCreateOrderResponse : WxJsPayDataBase
|
||||
{
|
||||
[XmlElement("appId")]
|
||||
public string appId { get; set; }
|
||||
[XmlElement("timeStamp")]
|
||||
public string timeStamp { get; set; }
|
||||
public string nonceStr { get; set; }
|
||||
public string prepay_id { get; set; }
|
||||
public string signType { get; set; }
|
||||
public string paySign { get; set; }
|
||||
[JsonIgnore]
|
||||
public string package => $"prepay_id={this.prepay_id}";
|
||||
}
|
||||
}
|
||||
40
Services/Hncore.Pass.PaymentCenter/Program.cs
Normal file
40
Services/Hncore.Pass.PaymentCenter/Program.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Hncore.Infrastructure.Common;
|
||||
using Microsoft.AspNetCore;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
AppDomain.CurrentDomain.UnhandledException += (sender, e) =>
|
||||
{
|
||||
LogHelper.Fatal("未捕获的异常", e.ExceptionObject.ToString());
|
||||
};
|
||||
|
||||
TaskScheduler.UnobservedTaskException += (_, ev) =>
|
||||
{
|
||||
LogHelper.Fatal("未捕获的异常", ev.Exception);
|
||||
};
|
||||
|
||||
CreateWebHostBuilder(args).Build().Run();
|
||||
}
|
||||
|
||||
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
|
||||
WebHost.CreateDefaultBuilder(args)
|
||||
.UseStartup<Startup>()
|
||||
.ConfigureKestrel((context, options) =>
|
||||
{
|
||||
options.Limits.KeepAliveTimeout = TimeSpan.FromMinutes(20);
|
||||
options.Limits.RequestHeadersTimeout = TimeSpan.FromMinutes(20);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:2368",
|
||||
"sslPort": 44302
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"Hncore.Pass.PaymentCenter": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"applicationUrl": "http://localhost:5000"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using Hncore.Payment.Request;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.Request.WechatJsPay
|
||||
{
|
||||
public class CreateOrderRequest: CreateOrderRequestBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 支付环境
|
||||
/// </summary>
|
||||
public PayEnvironment PayEnvironment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 微信用户关注商家公众号的openid
|
||||
/// </summary>
|
||||
public string UserOpenId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 公众账号或小程序ID
|
||||
/// </summary>
|
||||
public string AppId { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 支付环境
|
||||
/// </summary>
|
||||
public enum PayEnvironment
|
||||
{
|
||||
/// <summary>
|
||||
/// 小程序支付
|
||||
/// </summary>
|
||||
WeApp=1,
|
||||
/// <summary>
|
||||
/// 公众号支付
|
||||
/// </summary>
|
||||
H5=2
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using Hncore.Pass.PaymentCenter.Domain;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.Response
|
||||
{
|
||||
public class QueryOrderResponse
|
||||
{
|
||||
public PaymentStatus PaymentStatus { get; set; }
|
||||
public PaymentType PayType { get; set; }
|
||||
public string PaymentMessage { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
using Hncore.Infrastructure.Common;
|
||||
using Hncore.Infrastructure.Extension;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.Response.PaymentRecord
|
||||
{
|
||||
public class QueryPaymentRecordByOrderIdResponse
|
||||
{
|
||||
public int ID { get; set; }
|
||||
public DateTime CreateTime { get; set; }
|
||||
public DateTime UpdateTime { get; set; }
|
||||
public int DeleteTag { get; set; }
|
||||
|
||||
public int CreatorId { get; set; }
|
||||
|
||||
|
||||
public int UpdatorId { get; set; }
|
||||
public System.Int32 OwnerId { get; set; }
|
||||
/// <summary>
|
||||
/// 小区编号
|
||||
/// </summary>
|
||||
public int projectcode { get; set; }
|
||||
public System.DateTime RequestTime { get; set; }
|
||||
public System.String RequestParams { get; set; }
|
||||
public System.Int32 PaymentStatus { get; set; }
|
||||
public System.String PaymentNumber { get; set; }
|
||||
public System.String PaymentCompletionTime { get; set; }
|
||||
public System.Int32 PaymentTotal { get; set; }
|
||||
public System.Int32 PaymentType { get; set; }
|
||||
public System.Int32 CallbackNumber { get; set; }
|
||||
public System.String CallbackUrl { get; set; }
|
||||
|
||||
public System.String Openid { get; set; }
|
||||
public System.String Appid { get; set; }
|
||||
|
||||
public System.String Appname { get; set; }
|
||||
public System.String Body { get; set; }
|
||||
|
||||
public System.String OrderId { get; set; }
|
||||
|
||||
public System.Int32 BusinessType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 订单类型
|
||||
/// </summary>
|
||||
public int orderType { get; set; }
|
||||
/// <summary>
|
||||
/// 来源Pos
|
||||
/// </summary>
|
||||
public int fromPos { get; set; }
|
||||
/// <summary>
|
||||
/// 回调任务状态
|
||||
/// </summary>
|
||||
public int CallbackStatus { get; set; }
|
||||
/// <summary>
|
||||
/// 支付渠道
|
||||
/// </summary>
|
||||
public int paymentChannel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 物业名称
|
||||
/// </summary>
|
||||
public string PropertyName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 根据orderid查询一条支付记录
|
||||
/// </summary>
|
||||
/// <param name="paymentRecordQuery"></param>
|
||||
/// <param name="orderId"></param>
|
||||
/// <returns></returns>
|
||||
public static async Task<QueryPaymentRecordByOrderIdResponse> Query(IQueryable<Domain.PaymentRecord> paymentRecordQuery
|
||||
, string orderId)
|
||||
{
|
||||
var paymentRecord = await paymentRecordQuery
|
||||
.OrderByDescending(t=>t.Id)
|
||||
.FirstOrDefaultAsync(t => t.OrderId == orderId);
|
||||
CheckHelper.NotNull(paymentRecord,"支付记录不存在");
|
||||
|
||||
return paymentRecord.MapTo<QueryPaymentRecordByOrderIdResponse>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using Hncore.Pass.PaymentCenter.Domain;
|
||||
using Hncore.Pass.PaymentCenter.Domain.Refund;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.Response
|
||||
{
|
||||
public class QueryRefundResponse
|
||||
{
|
||||
public RefundStatus RefundStatus;
|
||||
|
||||
public CancelStatus CancelStatus;
|
||||
|
||||
public string RefundMessage { get; set; }
|
||||
|
||||
public PaymentType PayType { get; set; }
|
||||
|
||||
public string RefundId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 操作管理员
|
||||
/// </summary>
|
||||
public int OperatorId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 操作管理员姓名
|
||||
/// </summary>
|
||||
public string OperatorName { get; set; } = "";
|
||||
|
||||
/// <summary>
|
||||
/// 退款原因
|
||||
/// </summary>
|
||||
public string RefundReason { get; set; } = "";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.Response.Refund
|
||||
{
|
||||
public class RefundCountResponse
|
||||
{
|
||||
/// <summary>
|
||||
/// 撤销中数量
|
||||
/// </summary>
|
||||
public int CancelingCount { get; set; }
|
||||
/// <summary>
|
||||
/// 撤销成功数量
|
||||
/// </summary>
|
||||
public int SuccessCount { get; set; }
|
||||
/// <summary>
|
||||
/// 撤销失败数量
|
||||
/// </summary>
|
||||
public int FailCount { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
using Hncore.Pass.PaymentCenter.Domain;
|
||||
using Hncore.Pass.PaymentCenter.Domain.Refund;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using static Hncore.Infrastructure.Extension.EnumExtension;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.Response.Refund
|
||||
{
|
||||
public class RefundExportResponse
|
||||
{
|
||||
private readonly List<EnumInfo> PaymentEnumList = EnumToList<PaymentType>();
|
||||
private readonly List<EnumInfo> RefundStatusEnumList = EnumToList<RefundStatus>();
|
||||
private readonly List<EnumInfo> PaymentMethodEnumList = EnumToList<PaymentMethod>();
|
||||
public string CompanyName { get; set; }
|
||||
public string ProjectName { get; set; }
|
||||
public string TransactionRefundId { get; set; }
|
||||
public string TransactionId { get; set; }
|
||||
public string PaymentRecordId { get; set; }
|
||||
public string OrderId { get; set; }
|
||||
public string RefundId { get; set; }
|
||||
public string RefundFee { get; set; }
|
||||
public int PaymentType { get; set; }
|
||||
public string PaymentTypeDesc
|
||||
{
|
||||
get { return PaymentEnumList.Where(x => x.Value == PaymentType).FirstOrDefault().Name; }
|
||||
}
|
||||
/// <summary>
|
||||
/// 支付渠道
|
||||
/// </summary>
|
||||
public int PaymentMethod { get; set; }
|
||||
public string PaymentChannelDesc
|
||||
{
|
||||
get
|
||||
{
|
||||
if (PaymentMethod == 0)
|
||||
return "";
|
||||
return PaymentMethodEnumList.Where(x => x.Value == PaymentMethod).FirstOrDefault().Name;
|
||||
}
|
||||
}
|
||||
public string OperatorName { get; set; }
|
||||
public DateTime? ApplyCancelTime { get; set; }
|
||||
public DateTime? RequestTime { get; set; }
|
||||
public DateTime? RefundSuccessTime { get; set; }
|
||||
public int RefundStatus { get; set; }
|
||||
public string RefundStatusDesc
|
||||
{
|
||||
get { return RefundStatusEnumList.Where(x => x.Value == RefundStatus).FirstOrDefault().Name; }
|
||||
}
|
||||
public string CancelReason { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Hncore.Pass.PaymentCenter.Response
|
||||
{
|
||||
public class WechatJsPayCreateOrderResponse
|
||||
{
|
||||
/// <summary>
|
||||
/// 原生态js支付信息或小程序支付信息
|
||||
/// </summary>
|
||||
public string PayInfo { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
using Hncore.Infrastructure.Common;
|
||||
using Hncore.Infrastructure.Extension;
|
||||
using Hncore.Infrastructure.Serializer;
|
||||
using Hncore.Infrastructure.Service;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.Domain
|
||||
{
|
||||
/// <summary>
|
||||
/// 内部通知
|
||||
/// </summary>
|
||||
public class InternalNotifySerivce : ServiceBase<PaymentNotify>, IFindService
|
||||
{
|
||||
private IHttpClientFactory m_httpClientFactory;
|
||||
ServiceHttpClient m_ServiceHttpClient;
|
||||
public InternalNotifySerivce(PaymentContext dbContext
|
||||
, ServiceHttpClient _ServiceHttpClient
|
||||
, IHttpContextAccessor httpContextAccessor
|
||||
, IHttpClientFactory _httpClientFactory) : base(dbContext, httpContextAccessor)
|
||||
{
|
||||
m_httpClientFactory = _httpClientFactory;
|
||||
m_ServiceHttpClient = _ServiceHttpClient;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 发起内部通知,没有支付记录
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <param name="postData"></param>
|
||||
/// <returns></returns>
|
||||
public async Task Notify(string url, object postData)
|
||||
{
|
||||
bool success;
|
||||
string responseData = "";
|
||||
|
||||
try
|
||||
{
|
||||
Stopwatch st = new Stopwatch();
|
||||
st.Start();
|
||||
|
||||
responseData = await m_httpClientFactory
|
||||
.CreateClient(TimeSpan.FromMinutes(5))
|
||||
.PostAsJsonGetString(url, postData);
|
||||
|
||||
st.Stop();
|
||||
|
||||
LogHelper.Trace("支付内部通知请求:",
|
||||
$"{url}\n\n{postData.ToJson(true)}\n\n响应:\n{responseData}\n\n用时:{st.ElapsedMilliseconds}毫秒");
|
||||
|
||||
if (responseData.ToLower() != "success")
|
||||
{
|
||||
success = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LogHelper.Error($"支付内部通知异常,{e.Message}", e);
|
||||
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (!success)
|
||||
{
|
||||
await this.Add(new PaymentNotify()
|
||||
{
|
||||
PaymentRecordId = 0,
|
||||
Url = url,
|
||||
ResponseData = responseData,
|
||||
PostData = postData.ToJson(),
|
||||
RetryCount = 0,
|
||||
NotifyType= NotifyType.Faild
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 发起内部通知,有支付记录
|
||||
/// </summary>
|
||||
/// <param name="paymentRecord"></param>
|
||||
/// <returns></returns>
|
||||
public async Task Notify(PaymentRecord paymentRecord)
|
||||
{
|
||||
if (paymentRecord.CallbackStatus == CallbackStatus.Finished)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool success;
|
||||
|
||||
string responseData = "";
|
||||
|
||||
string callBackUrl = paymentRecord.CallbackUrl;
|
||||
|
||||
if (!callBackUrl.Has())
|
||||
{
|
||||
//LogHelper.Warn("该支付记录没有回调地址", paymentRecord.ToJson(true));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!callBackUrl.StartsWith("http"))
|
||||
{
|
||||
LogHelper.Warn("该支付记录回调地址异常", paymentRecord.ToJson(true));
|
||||
return;
|
||||
}
|
||||
|
||||
callBackUrl = UrlHelper.SetUrlParam(callBackUrl, new {PaymentType = (int) paymentRecord.PaymentType});
|
||||
|
||||
var postData = new
|
||||
{
|
||||
Data = new
|
||||
{
|
||||
Attach = paymentRecord.Attach,
|
||||
OrderId = paymentRecord.OrderId
|
||||
}
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
Stopwatch st = new Stopwatch();
|
||||
st.Start();
|
||||
|
||||
var client= m_ServiceHttpClient.CreateInternalClient();
|
||||
responseData = await client.PostAsJsonGetString(callBackUrl, postData);
|
||||
|
||||
st.Stop();
|
||||
|
||||
LogHelper.Trace("支付内部通知请求:",
|
||||
$"{callBackUrl}\n\n{postData.ToJson(true)}\n\n响应:\n{responseData}\n\n用时:{st.ElapsedMilliseconds}毫秒");
|
||||
|
||||
if (responseData.ToLower() != "success")
|
||||
{
|
||||
success = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
success = true;
|
||||
paymentRecord.CallbackStatus = CallbackStatus.Finished;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LogHelper.Error($"支付内部通知异常,{e.Message}", e);
|
||||
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (!success)
|
||||
{
|
||||
var any = await this.Query(true).AnyAsync(t => t.Id == paymentRecord.Id);
|
||||
|
||||
if (!any)
|
||||
{
|
||||
await this.Add(new PaymentNotify()
|
||||
{
|
||||
PaymentRecordId = paymentRecord.Id,
|
||||
Url = callBackUrl,
|
||||
ResponseData = responseData,
|
||||
PostData = postData.ToJson(),
|
||||
RetryCount = 0,
|
||||
NotifyType=NotifyType.Faild
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
using Hncore.Infrastructure.Common;
|
||||
using Hncore.Infrastructure.Data;
|
||||
using Hncore.Infrastructure.DDD;
|
||||
using Hncore.Infrastructure.Extension;
|
||||
using Hncore.Infrastructure.Service;
|
||||
using Hncore.Infrastructure.WebApi;
|
||||
using Hncore.Pass.PaymentCenter.Domain;
|
||||
using Hncore.Payment.Request;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.Service
|
||||
{
|
||||
public class PaymentRecordService : ServiceBase<PaymentRecord>, IFindService
|
||||
{
|
||||
public PaymentRecordService(PaymentContext dbContext, IHttpContextAccessor httpContextAccessor) : base(dbContext, httpContextAccessor)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public async Task<PaymentRecord> CreatePaymentRecord(PaymentRecord record)
|
||||
{
|
||||
bool anySuccessed = await this.Query(false)
|
||||
.AnyAsync(t => t.OrderId == record.OrderId && t.PaymentStatus == PaymentStatus.OkPay);
|
||||
|
||||
if (anySuccessed)
|
||||
{
|
||||
BusinessException.Throw(ResultCode.RepeatPay, "该订单已支付完成,不能再次支付");
|
||||
}
|
||||
|
||||
await this.Add(record);
|
||||
return record;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建支付记录
|
||||
/// </summary>
|
||||
/// <param name="request"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<PaymentRecord> CreatePaymentRecord(CreateOrderRequestBase request)
|
||||
{
|
||||
//todo
|
||||
// var property = await _propertyQuery.GetOneAsync(t => t.Id == request.OwnerId);
|
||||
|
||||
// CheckHelper.NotNull(property, "物业不存在");
|
||||
|
||||
PaymentRecord paymentRecord = request.MapTo<PaymentRecord>();
|
||||
|
||||
paymentRecord.PaymentChannel = PaymentChannel.QuanFuTong;
|
||||
|
||||
if (!paymentRecord.OrderId.Has())
|
||||
{
|
||||
paymentRecord.OrderId = Guid.NewGuid().ToString().Replace("-", "");
|
||||
}
|
||||
|
||||
|
||||
await CreatePaymentRecord(paymentRecord);
|
||||
|
||||
return paymentRecord;
|
||||
}
|
||||
|
||||
public async Task<PaymentRecord> GetOne(int Id)
|
||||
{
|
||||
return await this.GetById(Id);
|
||||
}
|
||||
|
||||
public async Task<PaymentRecord> GetOneByOrderId(string orderId)
|
||||
{
|
||||
return await this.Query(false).FirstOrDefaultAsync(x => x.OrderId == orderId);
|
||||
}
|
||||
}
|
||||
}
|
||||
15
Services/Hncore.Pass.PaymentCenter/Service/TenantService.cs
Normal file
15
Services/Hncore.Pass.PaymentCenter/Service/TenantService.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using Hncore.Infrastructure.Service;
|
||||
using Hncore.Pass.PaymentCenter.Domain;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.Service
|
||||
{
|
||||
public class TenantService : ServiceBase<TenantEntity>, IFindService
|
||||
{
|
||||
PaymentContext m_DbContext;
|
||||
public TenantService(PaymentContext dbContext, IHttpContextAccessor httpContextAccessor) : base(dbContext, httpContextAccessor)
|
||||
{
|
||||
m_DbContext = dbContext;
|
||||
}
|
||||
}
|
||||
}
|
||||
160
Services/Hncore.Pass.PaymentCenter/Service/WeiFuTongService.cs
Normal file
160
Services/Hncore.Pass.PaymentCenter/Service/WeiFuTongService.cs
Normal file
@@ -0,0 +1,160 @@
|
||||
using Hncore.Infrastructure.Service;
|
||||
using Hncore.Pass.PaymentCenter.Domain;
|
||||
using Hncore.Pass.PaymentCenter.Domain.Refund;
|
||||
using Hncore.Pass.PaymentCenter.Model;
|
||||
using Hncore.Pass.PaymentCenter.Response;
|
||||
using Hncore.Pass.PaymentCenter.WeiFuTong;
|
||||
using Hncore.Pass.PaymentCenter.WeiFuTong.ClientExtension;
|
||||
using Hncore.Pass.PaymentCenter.WeiFuTong.QueryOrder;
|
||||
using Hncore.Pass.PaymentCenter.WeiFuTong.Refund;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter.Service
|
||||
{
|
||||
public class WeiFuTongService:IFindService
|
||||
{
|
||||
private WeiFuTongClient _weiFuTongClient;
|
||||
|
||||
public WeiFuTongService(WeiFuTongClient weiFuTongClient)
|
||||
{
|
||||
_weiFuTongClient = weiFuTongClient;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 订单查询
|
||||
/// </summary>
|
||||
/// <param name="orderNo"></param>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
public async Task<QueryOrderResponse> QueryOrderAsync(PaymentRecord paymentRecord
|
||||
, MchInfo mchInfo)
|
||||
{
|
||||
string message = "";
|
||||
|
||||
var res = await _weiFuTongClient.QueryOrderAsync(new WeiFuTongQueryOrderRequest()
|
||||
{
|
||||
MchInfo = mchInfo,
|
||||
OutTradeNo = paymentRecord.Id.ToString(),
|
||||
TransactionId = paymentRecord.TransactionId,
|
||||
PaymentChannel = paymentRecord.PaymentChannel
|
||||
});
|
||||
|
||||
if (res.IsPaySuccess())
|
||||
{
|
||||
message = "支付成功";
|
||||
}
|
||||
else if (res.IsPayFailed())
|
||||
{
|
||||
message = "支付失败";
|
||||
}
|
||||
else
|
||||
{
|
||||
message = "支付中";
|
||||
}
|
||||
|
||||
if (res.IsPaySuccess() && (paymentRecord.PaymentStatus != PaymentStatus.OkPay ||
|
||||
paymentRecord.PaySuccessTime == null))
|
||||
{
|
||||
paymentRecord.SetPaySuccessed(res.TransactionId, res.GetPaySuccessTime());
|
||||
}
|
||||
|
||||
if (res.IsPayFailed() && paymentRecord.PaymentStatus != PaymentStatus.Fail)
|
||||
{
|
||||
paymentRecord.SetPayFailed();
|
||||
}
|
||||
|
||||
if ((paymentRecord.PaymentType == PaymentType.None || paymentRecord.PaymentMethod == PaymentMethod.None)
|
||||
&& res.IsPaySuccess())
|
||||
{
|
||||
paymentRecord.SetPaymentTypeFromTradeType(res.TradeType);
|
||||
}
|
||||
|
||||
return new QueryOrderResponse()
|
||||
{
|
||||
PaymentStatus = paymentRecord.PaymentStatus,
|
||||
PaymentMessage = message,
|
||||
PayType = paymentRecord.PaymentType
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 退款查询
|
||||
/// </summary>
|
||||
/// <param name="refundRecord"></param>
|
||||
/// <param name="mchInfo"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<QueryRefundResponse> QueryRefundAsync(RefundRecord refundRecord, MchInfo mchInfo)
|
||||
{
|
||||
string message = "";
|
||||
|
||||
var res = await _weiFuTongClient.QueryRefundAsync(new WeiFuTongRefundQueryRequest()
|
||||
{
|
||||
MchInfo = mchInfo,
|
||||
OutTradeNo = refundRecord.PaymentRecordId.ToString(), //商户订单号
|
||||
TransactionId = refundRecord.TransactionId, //平台订单号
|
||||
RefundId = refundRecord.TransactionRefundId, //平台退款单号
|
||||
OutRefundNo = refundRecord.RefundId, //商户退款单号
|
||||
PaymentChannel = refundRecord.PaymentChannel
|
||||
});
|
||||
|
||||
if (res.IsRefundSuccess())
|
||||
{
|
||||
message = "退款成功";
|
||||
}
|
||||
else if (res.IsRefundFail() || res.IsNotExists())
|
||||
{
|
||||
message = "退款失败";
|
||||
}
|
||||
else if (res.IsCancel())
|
||||
{
|
||||
message = "已撤销";
|
||||
}
|
||||
else
|
||||
{
|
||||
message = "退款中";
|
||||
}
|
||||
|
||||
if (res.IsRefundSuccess() && (refundRecord.RefundStatus != RefundStatus.Success
|
||||
|| refundRecord.RefundSuccessTime == null))
|
||||
{
|
||||
refundRecord.SetRefundSuccessed(res.RefundQueryItems.First().RefundId, DateTime.Now);
|
||||
}
|
||||
|
||||
if (res.IsRefundSuccess() && refundRecord.CancelStatus == CancelStatus.Canceling)
|
||||
{
|
||||
refundRecord.SetCancelFailed();
|
||||
}
|
||||
|
||||
if ((res.IsRefundFail() || res.IsNotExists()) && refundRecord.RefundStatus != RefundStatus.Fail)
|
||||
{
|
||||
refundRecord.SetRefundFailed();
|
||||
}
|
||||
|
||||
if (res.IsCancel())
|
||||
{
|
||||
if (refundRecord.CancelStatus == CancelStatus.Canceling)
|
||||
{
|
||||
refundRecord.SetCancelSuccessed();
|
||||
}
|
||||
else
|
||||
{
|
||||
refundRecord.SetRefundFailed();
|
||||
}
|
||||
}
|
||||
|
||||
return new QueryRefundResponse()
|
||||
{
|
||||
CancelStatus = refundRecord.CancelStatus,
|
||||
PayType = refundRecord.PaymentType,
|
||||
RefundMessage = message,
|
||||
RefundStatus = refundRecord.RefundStatus,
|
||||
RefundId = refundRecord.RefundId,
|
||||
OperatorName = refundRecord.OperatorName,
|
||||
RefundReason = refundRecord.RefundReason
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
52
Services/Hncore.Pass.PaymentCenter/Startup.cs
Normal file
52
Services/Hncore.Pass.PaymentCenter/Startup.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using Hncore.Infrastructure.Service;
|
||||
using Hncore.Infrastructure.WebApi;
|
||||
using Hncore.Pass.PaymentCenter.Domain;
|
||||
using Hncore.Pass.PaymentCenter.Map;
|
||||
using Hncore.Pass.PaymentCenter.Pay.WxPay;
|
||||
using Hncore.Pass.PaymentCenter.WeiFuTong;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
|
||||
namespace Hncore.Pass.PaymentCenter
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
public IConfiguration Configuration { get; }
|
||||
|
||||
public Startup(IHostingEnvironment env)
|
||||
{
|
||||
Configuration = env.UseAppsettings();
|
||||
}
|
||||
|
||||
public IServiceProvider ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddDbContext<PaymentContext>(opt => { opt.UseMySql(Configuration["MySql"]); });
|
||||
|
||||
RedisHelper.Initialization(new CSRedis.CSRedisClient(Configuration["Redis"]));
|
||||
|
||||
services.AddHttpClient();
|
||||
services.AutoAddService();
|
||||
|
||||
services.AddSingleton<WeiFuTongClient>();
|
||||
services.AddSingleton<WxPayClient>();
|
||||
|
||||
return services.Init(Configuration, CompatibilityVersion.Version_2_2, new ServiceOption
|
||||
{
|
||||
UseGlobalManageAuthFilter = false,
|
||||
});
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder app, IApplicationLifetime applicationLifetime,
|
||||
ILoggerFactory loggerFactory)
|
||||
{
|
||||
app.Init(loggerFactory, applicationLifetime);
|
||||
MapConfig.Config();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"MySql": "Server=47.92.244.89;Database=property;User=root;Password=qaz123!@#;Convert Zero Datetime=True;TreatTinyAsBoolean=false;port=5000;",
|
||||
"Redis": "47.92.244.89:8088,password=123456,defaultDatabase=7,poolsize=1",
|
||||
"NotifyUrl": "http://louxiaoyuapi.Hncore.vip/api/paymentcenter/v1/Payment/HWCNotify",
|
||||
"UnionpayAggregateRootNotifyUrl": "http://louxiaoyuapi.Hncore.vip/api/paymentcenter/v1/Payment/UANotify",
|
||||
"QuanFuTong": {
|
||||
"MchId": "23",
|
||||
"RSAPrivateKey": "242424cQu9cDT9LKpoU0JwDYKnu8yk8adt81xm4JifUNe/X7X242461GusKAQwYj3fs/mvgx8NHAgMBAAECggEAWLaC1jGLgvUvTPb9EhmGuLxy/WnjskI6NwSsL/ZzaC3KV+0Mz8Eo5n4CiIr/fMSrJgAPAqyqPKWjj8JgYqnDiZ+NdAjY/ai7oMQ+ABnSg9+u+tCAUBSQZ1zSD5NmmHbUiS/4OfLcEudZvn1ItCXuUlzqEMuxfLfShDyleN5VYtIF6+OKBGji7FePQJ+2eEdfsh+b1936vztKckVkERSMRaBHCs9RTLSKRfUH4OK7z0oaYV+GK/BBPWT1ylpd+DUoJOiTT13QMP2a3VfKuaYwonI3Whp/Wwc832GWRZMF0PLVJXFiPiPaB1AK+nYO5aD+8lresoYub530VMF6rdUj+QKBgQDWp73zQNtD1Cpcvz2kKjOlwqP2ROBTmZaJzn1AJEYW4bIyDCw1ECkZmo8JYaR8JFX0siyDvgMQsYUasmHZ8qbeKmX0AFfFa26c7A2VCJqWP++Mc1f83KH4dAGplLVirCs7xXGLxAxREPJsJB0BsmRzMwby7rS3J7978VNQD37vewKBgQDPO6jdEhhJZnvJcL2Uf/XCeEfc3Z5ascveqynSgZzkAkyeRKRBJ/rnQRmTklcq97jW+WkyQaYW5VxJ6Ep76gp7ip+UCL4dcjKRLgA/lWOxUh81NjZmsUDWZLSPVJIXdLuGbUU67XRBY5+1jG6SN2oYgBzxjeH/EM58Li7KE+1rpQKBgG1aeTdyZAGzFX24Y4O9iCA6/2KXhhyw0vMTgdVqZVaE1k4Oy3qg5/9GMPZbivs/W5bUKj9XTqJzfVazZ4wrxPzLZrMedZh6eZhx3R1+i5pdqgDIRhvono8/MhWdT6acBU5m8Mh+MLQfyRB0eV6C7g+OAE1EdEYWdsOClqnjqwv/AoGBAM45bofg8+5HYDRMTzyyQhzumYT/vhA/EsRZzNrBRSHZ7+koIdT9NkBfllY2sSv/LSAxPzBWCDSMttSNp2G8vAXJI5irQpYHCK9U+cZModLaCFhpxKVZC07PFKf13xQchGj34IcNuj1AKLUANSCoqJoNE8CGEzpCvVjylHlgb0pJAoGBANWAeTb3HYnPBJIUCkao13/gIP/6/0k7Z8vOnHZgoZVoZuOTVtCc+1Mg5Ati/CKDnvrRcpAyliaintO4kiRDMQTy4+PXpJ7dExG8smLWF428rOKe3D0pgSmrlMbBLZiDyj61/lsL8HNUNjaeuSLSEXlR7Br0PErVHOFvIHfFaH29",
|
||||
"RSAPublicKey": "MIIBIjANBgkqhkiG9w034343482fo2+AyHkq11yE27IgOjSrKofgg3GWJ6SSQonYuXZ0c09chXXiZPKYe0zRbvq83kAVsYDu1sMwi8mfiVff6CIALsehs1MOjmdLW40N1CicVmJaWuh2yee+sj1/0xMOlV1LyJq63hShBD7T93qpGbHoNkpdz+BFc2byrhv1idbB4DRbUiKynzj3FX2Nz8Dv9TFQv8p2Z8dIOst890atv3P8DO7a9FI8I1reLvFDdyPawIDAQAB"
|
||||
},
|
||||
"UnionpayAggregateRoot": {
|
||||
"MchId": "dfdf",
|
||||
"Md5Key": "43434"
|
||||
},
|
||||
"EPayNotifyUrl": "https://psipbmsapi.wx.weiyu.Hncore.vip/api/v1/epay/paycallback1",
|
||||
"EPayNotifyUrl_Test": "http://ipistest.Hncore.top/api/paymentcenter/v1/epay/callback_test1", //イ簗ヤサリオ<EFBE98>リヨキ
|
||||
"EPayNoOrderBillCallbackUrl": "https://psipbmsapi.wx.weiyu.Hncore.vip/api/v1/epay/paycallbacknoorderbill1",
|
||||
"EPayRefundCallbackUrl": "https://psipbmsapi.wx.weiyu.Hncore.vip/api/v1/epay/refundcallback1",
|
||||
"WeiFuTongRefundCallbackUrl": "https://psipbmsapi.wx.weiyu.Hncore.vip/api/v1/feecenter/refundcallback1"
|
||||
}
|
||||
20
Services/Hncore.Pass.PaymentCenter/appsettings.Local.json
Normal file
20
Services/Hncore.Pass.PaymentCenter/appsettings.Local.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"MySql": "Server=rm-bp12e1533udh1827azo.mysql.rds.aliyuncs.com;Database=Hncore_property_test;User=Hncore_test;Password=Hncore_test!QAZ2wsx;Convert Zero Datetime=True;",
|
||||
"Redis": "192.168.1.245:6379,password=123456,defaultDatabase=0,poolsize=1",
|
||||
"HWCNotifyUrl": "http://ipistest.Hncore.top/api/paymentcenter/v1/Payment/HWCNotify",
|
||||
"UnionpayAggregateRootNotifyUrl": "http://ipistest.Hncore.top/api/paymentcenter/v1/Payment/UANotify",
|
||||
"QuanFuTong": {
|
||||
"MchId": "102546446546",
|
||||
"RSAPrivateKey": "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCtw6jX6yQEkL8cmDZctLoAaCU6vqEegSug9tH3xbGBA+nqpg0PHF4q8fu4NplP4wwh17d86v0/ew4cnWaSWjlVQ+GywZjtcZ8xmfhWDw/rc1UXoWT6C+TC5kucm3zzMfozV9ajIYUXDGw7ZWjuZkjDxZwS3avi56+5ewVMbQfpmW38/42VJf+P4vcQu9cDT9LKpoU0JwDYKnu8yk8adt81xm4JifUNe/X7Xgsuv6KA99zP/vTs4WRaJ53gH7mVf+8uOqPFPl9q2mL8dOw3eYysSN+FA1z+WtarthMELN6Mufgt2hV1V4hXfzpSkvLWu061GusKAQwYj3fs/mvgx8NHAgMBAAECggEAWLaC1jGLgvUvTPb9EhmGuLxy/WnjskI6NwSsL/ZzaC3KV+0Mz8Eo5n4CiIr/fMSrJgAPAqyqPKWjj8JgYqnDiZ+NdAjY/ai7oMQ+ABnSg9+u+tCAUBSQZ1zSD5NmmHbUiS/4OfLcEudZvn1ItCXuUlzqEMuxfLfShDyleN5VYtIF6+OKBGji7FePQJ+2eEdfsh+b1936vztKckVkERSMRaBHCs9RTLSKRfUH4OK7z0oaYV+GK/BBPWT1ylpd+DUoJOiTT13QMP2a3VfKuaYwonI3Whp/Wwc832GWRZMF0PLVJXFiPiPaB1AK+nYO5aD+8lresoYub530VMF6rdUj+QKBgQDWp73zQNtD1Cpcvz2kKjOlwqP2ROBTmZaJzn1AJEYW4bIyDCw1ECkZmo8JYaR8JFX0siyDvgMQsYUasmHZ8qbeKmX0AFfFa26c7A2VCJqWP++Mc1f83KH4dAGplLVirCs7xXGLxAxREPJsJB0BsmRzMwby7rS3J7978VNQD37vewKBgQDPO6jdEhhJZnvJcL2Uf/XCeEfc3Z5ascveqynSgZzkAkyeRKRBJ/rnQRmTklcq97jW+WkyQaYW5VxJ6Ep76gp7ip+UCL4dcjKRLgA/lWOxUh81NjZmsUDWZLSPVJIXdLuGbUU67XRBY5+1jG6SN2oYgBzxjeH/EM58Li7KE+1rpQKBgG1aeTdyZAGzFX24Y4O9iCA6/2KXhhyw0vMTgdVqZVaE1k4Oy3qg5/9GMPZbivs/W5bUKj9XTqJzfVazZ4wrxPzLZrMedZh6eZhx3R1+i5pdqgDIRhvono8/MhWdT6acBU5m8Mh+MLQfyRB0eV6C7g+OAE1EdEYWdsOClqnjqwv/AoGBAM45bofg8+5HYDRMTzyyQhzumYT/vhA/EsRZzNrBRSHZ7+koIdT9NkBfllY2sSv/LSAxPzBWCDSMttSNp2G8vAXJI5irQpYHCK9U+cZModLaCFhpxKVZC07PFKf13xQchGj34IcNuj1AKLUANSCoqJoNE8CGEzpCvVjylHlgb0pJAoGBANWAeTb3HYnPBJIUCkao13/gIP/6/0k7Z8vOnHZgoZVoZuOTVtCc+1Mg5Ati/CKDnvrRcpAyliaintO4kiRDMQTy4+PXpJ7dExG8smLWF428rOKe3D0pgSmrlMbBLZiDyj61/lsL8HNUNjaeuSLSEXlR7Br0PErVHOFvIHfFaH29",
|
||||
"RSAPublicKey": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqNxzebovJ6R+LF0jFyJD4vgdvj+Apmb5h+pW3T0EtDzWZAr7tyiSAtNedYvRjJCqN5cYw0rIwGMZFbD3lQHbJGC+IvpqXwPB8AWqRAwItI82fo2+AyHkq11yE27IgOjSrKofgg3GWJ6SSQonYuXZ0c09chXXiZPKYe0zRbvq83kAVsYDu1sMwi8mfiVff6CIALsehs1MOjmdLW40N1CicVmJaWuh2yee+sj1/0xMOlV1LyJq63hShBD7T93qpGbHoNkpdz+BFc2byrhv1idbB4DRbUiKynzj3FX2Nz8Dv9TFQv8p2Z8dIOst890atv3P8DO7a9FI8I1reLvFDdyPawIDAQAB"
|
||||
},
|
||||
"UnionpayAggregateRoot": {
|
||||
"MchId": "QRA491973721AAS",
|
||||
"Md5Key": "0598e402dd882cb18399ce71f3bf07da"
|
||||
},
|
||||
"EPayNotifyUrl": "https://psiptestapi.wx.weiyu.Hncore.vip/api/v1/epay/paycallback",
|
||||
"EPayNotifyUrl_Test": "http://ipistest.Hncore.top/api/paymentcenter/v1/epay/callback_test",
|
||||
"EPayNoOrderBillCallbackUrl": "https://psiptestapi.wx.weiyu.Hncore.vip/api/v1/epay/paycallbacknoorderbill",
|
||||
"EPayRefundCallbackUrl": "https://psiptestapi.wx.weiyu.Hncore.vip/api/v1/epay/refundcallback",
|
||||
"WeiFuTongRefundCallbackUrl": "https://psiptestapi.wx.weiyu.Hncore.vip/api/v1/feecenter/refundcallback"
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"MySql": "Server=47.92.244.89;Database=property;User=root;Password=qaz123!@#;Convert Zero Datetime=True;TreatTinyAsBoolean=false;port=5000;",
|
||||
"Redis": "47.92.85.90:6379,password=etor0070x01,defaultDatabase=10,poolsize=1",
|
||||
"HWCNotifyUrl": "http://louxiaoyuapi.Hncore.vip/api/paymentcenter/v1/Payment/HWCNotify",
|
||||
"UnionpayAggregateRootNotifyUrl": "http://louxiaoyuapi.Hncore.vip/api/paymentcenter/v1/Payment/UANotify",
|
||||
"QuanFuTong": {
|
||||
"MchId": "23",
|
||||
"RSAPrivateKey": "242424cQu9cDT9LKpoU0JwDYKnu8yk8adt81xm4JifUNe/X7X242461GusKAQwYj3fs/mvgx8NHAgMBAAECggEAWLaC1jGLgvUvTPb9EhmGuLxy/WnjskI6NwSsL/ZzaC3KV+0Mz8Eo5n4CiIr/fMSrJgAPAqyqPKWjj8JgYqnDiZ+NdAjY/ai7oMQ+ABnSg9+u+tCAUBSQZ1zSD5NmmHbUiS/4OfLcEudZvn1ItCXuUlzqEMuxfLfShDyleN5VYtIF6+OKBGji7FePQJ+2eEdfsh+b1936vztKckVkERSMRaBHCs9RTLSKRfUH4OK7z0oaYV+GK/BBPWT1ylpd+DUoJOiTT13QMP2a3VfKuaYwonI3Whp/Wwc832GWRZMF0PLVJXFiPiPaB1AK+nYO5aD+8lresoYub530VMF6rdUj+QKBgQDWp73zQNtD1Cpcvz2kKjOlwqP2ROBTmZaJzn1AJEYW4bIyDCw1ECkZmo8JYaR8JFX0siyDvgMQsYUasmHZ8qbeKmX0AFfFa26c7A2VCJqWP++Mc1f83KH4dAGplLVirCs7xXGLxAxREPJsJB0BsmRzMwby7rS3J7978VNQD37vewKBgQDPO6jdEhhJZnvJcL2Uf/XCeEfc3Z5ascveqynSgZzkAkyeRKRBJ/rnQRmTklcq97jW+WkyQaYW5VxJ6Ep76gp7ip+UCL4dcjKRLgA/lWOxUh81NjZmsUDWZLSPVJIXdLuGbUU67XRBY5+1jG6SN2oYgBzxjeH/EM58Li7KE+1rpQKBgG1aeTdyZAGzFX24Y4O9iCA6/2KXhhyw0vMTgdVqZVaE1k4Oy3qg5/9GMPZbivs/W5bUKj9XTqJzfVazZ4wrxPzLZrMedZh6eZhx3R1+i5pdqgDIRhvono8/MhWdT6acBU5m8Mh+MLQfyRB0eV6C7g+OAE1EdEYWdsOClqnjqwv/AoGBAM45bofg8+5HYDRMTzyyQhzumYT/vhA/EsRZzNrBRSHZ7+koIdT9NkBfllY2sSv/LSAxPzBWCDSMttSNp2G8vAXJI5irQpYHCK9U+cZModLaCFhpxKVZC07PFKf13xQchGj34IcNuj1AKLUANSCoqJoNE8CGEzpCvVjylHlgb0pJAoGBANWAeTb3HYnPBJIUCkao13/gIP/6/0k7Z8vOnHZgoZVoZuOTVtCc+1Mg5Ati/CKDnvrRcpAyliaintO4kiRDMQTy4+PXpJ7dExG8smLWF428rOKe3D0pgSmrlMbBLZiDyj61/lsL8HNUNjaeuSLSEXlR7Br0PErVHOFvIHfFaH29",
|
||||
"RSAPublicKey": "MIIBIjANBgkqhkiG9w034343482fo2+AyHkq11yE27IgOjSrKofgg3GWJ6SSQonYuXZ0c09chXXiZPKYe0zRbvq83kAVsYDu1sMwi8mfiVff6CIALsehs1MOjmdLW40N1CicVmJaWuh2yee+sj1/0xMOlV1LyJq63hShBD7T93qpGbHoNkpdz+BFc2byrhv1idbB4DRbUiKynzj3FX2Nz8Dv9TFQv8p2Z8dIOst890atv3P8DO7a9FI8I1reLvFDdyPawIDAQAB"
|
||||
},
|
||||
"UnionpayAggregateRoot": {
|
||||
"MchId": "dfdf",
|
||||
"Md5Key": "43434"
|
||||
},
|
||||
"EPayNotifyUrl": "https://psipbmsapi.wx.weiyu.Hncore.vip/api/v1/epay/paycallback",
|
||||
"EPayNotifyUrl_Test": "http://ipistest.Hncore.top/api/paymentcenter/v1/epay/callback_test", //测试回调地址
|
||||
"EPayNoOrderBillCallbackUrl": "https://psipbmsapi.wx.weiyu.Hncore.vip/api/v1/epay/paycallbacknoorderbill",
|
||||
"EPayRefundCallbackUrl": "https://psipbmsapi.wx.weiyu.Hncore.vip/api/v1/epay/refundcallback",
|
||||
"WeiFuTongRefundCallbackUrl": "https://psipbmsapi.wx.weiyu.Hncore.vip/api/v1/feecenter/refundcallback"
|
||||
}
|
||||
8
Services/Hncore.Pass.PaymentCenter/appsettings.json
Normal file
8
Services/Hncore.Pass.PaymentCenter/appsettings.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
1
Services/Hncore.Pass.PaymentCenter/生成模型.txt
Normal file
1
Services/Hncore.Pass.PaymentCenter/生成模型.txt
Normal file
@@ -0,0 +1 @@
|
||||
dotnet ef dbcontext scaffold "Server=47.92.244.89;Database=course;User=root;Password=qaz123!@#;Convert Zero Datetime=True;TreatTinyAsBoolean=false;port=5000;" "Pomelo.EntityFrameworkCore.MySql" -o Domain -t tenant -t tenant_store -t manager -t payment_record -t payment_record_notify -t payment_refund_record
|
||||
Reference in New Issue
Block a user