更新支付、转账、用户控制、新增证书

development
温天培 10 months ago
parent 32a96c59dc
commit bcdac0394c

@ -0,0 +1,23 @@
using System;
using SqlSugar;
using System.ComponentModel;
using GDZZ.Core.Entity;
namespace GDZZ.Application
{
/// <summary>
/// 业务用户关联表
/// </summary>
[SugarTable("baseuser_sysuser_scope")]
[Description("业务用户关联表")]
public class UserScope
{
/// <summary>
/// 业务用户ID
/// </summary>
public long BaseUserID { get; set; }
/// <summary>
/// 系统用户ID
/// </summary>
public long SysUserID { get; set; }
}
}

@ -0,0 +1,55 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GDZZ.Application.Enum
{
public enum MerchantErrorEnum
{
/// <summary>
/// 系统错误
/// </summary>
[Description("系统错误")] SYSTEM_ERROR = 0,
/// <summary>
/// 商户号和appid没有绑定关系
/// </summary>
[Description("商户号和appid没有绑定关系")] APPID_MCHID_NOT_MATCH = 1,
/// <summary>
/// 系统错误
/// </summary>
[Description("参数错误")] PARAM_ERROR = 2,
/// <summary>
/// 系统错误
/// </summary>
[Description("请求参数符合参数格式,但不符合业务规则")] INVALID_REQUEST = 3,
/// <summary>
/// 系统错误
/// </summary>
[Description("商户信息不合法")] NO_AUTH = 4,
/// <summary>
/// 系统错误
/// </summary>
[Description("资金不足")] NOT_ENOUGH = 5,
/// <summary>
/// 系统错误
/// </summary>
[Description("商户账户付款受限")] ACCOUNTERROR = 6,
/// <summary>
/// 系统错误
/// </summary>
[Description("超出商户单日转账额度")] QUOTA_EXCEED =7,
/// <summary>
/// 系统错误
/// </summary>
[Description("频率超限")] FREQUENCY_LIMITED = 0,
}
}

@ -7,6 +7,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Flurl" Version="4.0.0" />
<PackageReference Include="Flurl.Http" Version="4.0.2" />
<PackageReference Include="Senparc.CO2NET.WebApi" Version="1.3.3.7" />
<PackageReference Include="Senparc.NeuChar.App" Version="1.1.4.5" />
<PackageReference Include="Senparc.NeuChar.AspNet" Version="1.1.3.5" />

@ -804,6 +804,21 @@
求职联系费用
</summary>
</member>
<member name="T:GDZZ.Application.UserScope">
<summary>
业务用户关联表
</summary>
</member>
<member name="P:GDZZ.Application.UserScope.BaseUserID">
<summary>
业务用户ID
</summary>
</member>
<member name="P:GDZZ.Application.UserScope.SysUserID">
<summary>
系统用户ID
</summary>
</member>
<member name="T:GDZZ.Application.Enum.BannerEnum">
<summary>
活动类型
@ -834,6 +849,51 @@
解除合作
</summary>
</member>
<member name="F:GDZZ.Application.Enum.MerchantErrorEnum.SYSTEM_ERROR">
<summary>
系统错误
</summary>
</member>
<member name="F:GDZZ.Application.Enum.MerchantErrorEnum.APPID_MCHID_NOT_MATCH">
<summary>
商户号和appid没有绑定关系
</summary>
</member>
<member name="F:GDZZ.Application.Enum.MerchantErrorEnum.PARAM_ERROR">
<summary>
系统错误
</summary>
</member>
<member name="F:GDZZ.Application.Enum.MerchantErrorEnum.INVALID_REQUEST">
<summary>
系统错误
</summary>
</member>
<member name="F:GDZZ.Application.Enum.MerchantErrorEnum.NO_AUTH">
<summary>
系统错误
</summary>
</member>
<member name="F:GDZZ.Application.Enum.MerchantErrorEnum.NOT_ENOUGH">
<summary>
系统错误
</summary>
</member>
<member name="F:GDZZ.Application.Enum.MerchantErrorEnum.ACCOUNTERROR">
<summary>
系统错误
</summary>
</member>
<member name="F:GDZZ.Application.Enum.MerchantErrorEnum.QUOTA_EXCEED">
<summary>
系统错误
</summary>
</member>
<member name="F:GDZZ.Application.Enum.MerchantErrorEnum.FREQUENCY_LIMITED">
<summary>
系统错误
</summary>
</member>
<member name="F:GDZZ.Application.Enum.ResumeTypeEnum.Hot">
<summary>
热招
@ -1667,7 +1727,7 @@
退款消息
</summary>
</member>
<member name="P:GDZZ.Application.TransferInput.MerchantID">
<member name="P:GDZZ.Application.TransferInput.OpenID">
<summary>
商户ID
</summary>
@ -4260,16 +4320,16 @@
JsApiPay 的摘要说明
</summary>
</member>
<member name="M:JsApiPay.WxV3PostJson(System.String,System.String,System.String,System.String)">
<member name="M:JsApiPay.WxV3PostJson(System.String,System.String,System.String,System.String,System.String)">
<summary>
V3版本请求接口
</summary>
<param name="url">微信的接口地址</param>
<param name="postData">post请求的数据json格式 </param>
<param name="privateKey">apiclient_key.pem中的内容不要-----BEGIN PRIVATE KEY----- -----END PRIVATE KEY-----</param>
<param name="merchantId">发起请求的商户(包括直连商户、服务商或渠道商)的商户号 mchid</param>
<param name="mchId">发起请求的商户(包括直连商户、服务商或渠道商)的商户号 mchid</param>
<param name="serialNo">商户证书号</param>
<param name="method"></param>
<param name="hostting"></param>
<returns></returns>
</member>
</members>

@ -57,10 +57,10 @@ namespace GDZZ.Application.Help
/// </summary>
/// <returns></returns>
[NonAction]
public async Task DelLiveHistoryService(long UserID)
public async Task<bool> DelLiveHistoryService(long UserID)
{
string cacheKey = SystemConst.LIVE_HISTORYLIST + $"{UserID}";
await _redisCache.DelAsync(cacheKey);
return await _redisCache.DelAsync(cacheKey)>0;
}
#endregion

@ -13,7 +13,7 @@ namespace GDZZ.Application.Help
public interface ICacheService
{
#region 聊天接口
Task DelLiveHistoryService(long UserID);
Task<bool> DelLiveHistoryService(long UserID);
Task<List<LiveHistoryLists>> GetLiveHistoryService(long UserID);
Task SetLiveHistoryService(long UserID, List<LiveHistoryLists> liveMessageLists);
#endregion

@ -33,6 +33,8 @@ namespace GDZZ.Application.Service.Auth
#region 仓储
private readonly SqlSugarRepository<BaseUser> Baseuser; // wx用户仓储
private readonly SqlSugarRepository<SysUser> _sysUserRep; // 用户表仓储
private readonly SqlSugarRepository<UserScope> UserScope; //用户业务关联仓储 _
private readonly SqlSugarRepository<SysTenant> _sysTenantRep; //租户仓储
private readonly SqlSugarRepository<SeIF> Self; //职业仓储
private readonly SqlSugarRepository<Company> CompanyRep;
@ -59,6 +61,7 @@ namespace GDZZ.Application.Service.Auth
public AuthService(
IOptions<OAuthOptions> options,
SqlSugarRepository<UserScope> UserScope,
SqlSugarRepository<BaseUser> Baseuser,
SqlSugarRepository<SysTenant> sysTenantRep,
SqlSugarRepository<SysUser> sysUserRep,
@ -75,6 +78,7 @@ namespace GDZZ.Application.Service.Auth
IEventPublisher eventPublisher,
IHttpContextAccessor httpContextAccessor)
{
this.UserScope = UserScope;
this.CompanyRep= CompanyRep;
this._eventPublisher= eventPublisher;
this._sysUserRep = sysUserRep;
@ -102,133 +106,144 @@ namespace GDZZ.Application.Service.Auth
public async Task<AuthUserOut> SignInAsync(PhoneModel phoneModel)
{
AuthUserOut authUserOut = new AuthUserOut();
Company company = new Company();
//读取凭证
var tokenModel = await this._wechatOAuth.GetCode2SessionAsync(phoneModel.Code);
//解析电话
var phoneInfo = MiniProgramUtil.AESDecrypt(phoneModel.EncryptedDataStr, tokenModel.SessionKey, phoneModel.Iv);
//查询系统用户
var sysUser = this._sysUserRep.AsQueryable()
.Filter("TenantId", true)
.First(x => x.Phone == phoneInfo.PhoneNumber);
var wxUser = await this.Baseuser.AsQueryable()
.Filter("TenantId", true)
.Where(x => x.OpenID == tokenModel.OpenId).SingleAsync();
//账号不存在 生成系统账号
if (sysUser.IsEmpty())
try
{
sysUser = await this._sysUserRep.InsertReturnEntityAsync(new SysUser()
this.UserScope.BeginTran(); //开启事务
//读取凭证
var tokenModel = await this._wechatOAuth.GetCode2SessionAsync(phoneModel.Code);
//解析电话
var phoneInfo = MiniProgramUtil.AESDecrypt(phoneModel.EncryptedDataStr, tokenModel.SessionKey, phoneModel.Iv);
//系统用户
var sysUser = this._sysUserRep.AsQueryable()
.Filter("TenantId", true)
.First(x => x.Phone == phoneInfo.PhoneNumber);
//业务用户
var wxUser = await this.Baseuser.AsQueryable()
.Filter("TenantId", true)
.Where(x => x.OpenID == tokenModel.OpenId).SingleAsync();
//账号不存在 生成系统账号
if (sysUser.IsEmpty())
{
Account = phoneInfo.PurePhoneNumber,
AdminType = AdminType.None,
Avatar = "https://gdzongzhi.com/assets/img/logo.png",
Birthday = DateTime.Now,
CreatedTime = DateTime.Now,
CreatedUserId = null,
CreatedUserName = null,
Sex = Gender.UNKNOWN,
Status = CommonStatus.ENABLE,
Email = null,
IsDeleted = false,
Name = phoneInfo.PhoneNumber,
Password = MD5Encryption.Encrypt("123456"),
TenantId = 392820661919813,
Phone = phoneInfo.PhoneNumber,
NickName = "",
Tel = null,
});
}
//wxUser = await this.UserScope.InsertAsync(new UserScope)
sysUser = await this._sysUserRep.InsertReturnEntityAsync(new SysUser()
{
Account = phoneInfo.PurePhoneNumber,
AdminType = AdminType.None,
Avatar = "https://gdzongzhi.com/assets/img/logo.png",
Birthday = DateTime.Now,
CreatedTime = DateTime.Now,
CreatedUserId = null,
CreatedUserName = null,
Sex = Gender.UNKNOWN,
Status = CommonStatus.ENABLE,
Email = null,
IsDeleted = false,
Name = phoneInfo.PhoneNumber,
Password = MD5Encryption.Encrypt("123456"),
TenantId = 392820661919813,
Phone = phoneInfo.PhoneNumber,
NickName = "",
Tel = null,
});
}
if (wxUser.IsEmpty())
{
wxUser = await this.Baseuser.InsertReturnEntityAsync(new BaseUser()
if (wxUser.IsEmpty())
{
UnionId = tokenModel.Unionid,
CreatedUserId = sysUser.Id,
CreatedTime = DateTime.Now,
CreatedUserName = sysUser.Name,
AvatarUrl = "https://gdzongzhi.com/assets/img/logo.png",
Status = (int)CommonStatus.ENABLE,
OpenID = tokenModel.OpenId,
UserName = phoneModel.Phone.ToString(),
});
}
//区分账号类型
switch (phoneModel.LogInType)
{
case (int)UserEnum.JOB:
wxUser.Type = (int)UserEnum.JOB;
await this.Baseuser.UpdateAsync(wxUser);
break;
case (int)UserEnum.HEADHUNTERS:
wxUser.Type = (int)UserEnum.HEADHUNTERS;
await this.Baseuser.UpdateAsync(wxUser);
break;
case (int)UserEnum.ADVERTISE:
wxUser.Type = (int)UserEnum.ADVERTISE;
await this.Baseuser.UpdateAsync(wxUser);
//获取公司信息
company = await this.CompanyRep.FirstOrDefaultAsync(x => x.Id == wxUser.CompanyID);
authUserOut.companyDto = company.Adapt<CompanyDto>();
break;
default:
break;
}
wxUser = await this.Baseuser.InsertReturnEntityAsync(new BaseUser()
{
UnionId = tokenModel.Unionid,
CreatedUserId = sysUser.Id,
CreatedTime = DateTime.Now,
CreatedUserName = sysUser.Name,
AvatarUrl = "https://gdzongzhi.com/assets/img/logo.png",
Status = (int)CommonStatus.ENABLE,
OpenID = tokenModel.OpenId,
UserName = phoneModel.Phone.ToString(),
});
}
//区分账号类型
switch (phoneModel.LogInType)
{
case (int)UserEnum.JOB:
wxUser.Type = (int)UserEnum.JOB;
await this.Baseuser.UpdateAsync(wxUser);
break;
case (int)UserEnum.HEADHUNTERS:
wxUser.Type = (int)UserEnum.HEADHUNTERS;
await this.Baseuser.UpdateAsync(wxUser);
break;
case (int)UserEnum.ADVERTISE:
wxUser.Type = (int)UserEnum.ADVERTISE;
await this.Baseuser.UpdateAsync(wxUser);
//获取公司信息
company = await this.CompanyRep.FirstOrDefaultAsync(x => x.Id == wxUser.CompanyID);
authUserOut.companyDto = company.Adapt<CompanyDto>();
break;
default:
break;
}
if (wxUser.IsEmpty() || sysUser.IsEmpty())
throw Oops.Oh(ErrorCode.xg1002);
//判断是否存在邀请
if(phoneModel.Scene != null)
{
//判断当前用户是否被邀请过
var invi = await this.invitaitionRey.FirstOrDefaultAsync(x => x.UserID == UserManager.UserId);
if (invi.IsNullOrZero())
if (wxUser.IsEmpty() || sysUser.IsEmpty())
throw Oops.Oh(ErrorCode.xg1002);
var userc = await this.UserScope.FirstOrDefaultAsync(x => x.BaseUserID == wxUser.Id && x.SysUserID == sysUser.Id);
if (userc.IsNullOrZero())
{
var invres = await this.invitaitionRey.FirstOrDefaultAsync(x => x.InviteID == phoneModel.Scene);
//未被邀请
var invrey = await this.invitaitionRey.InsertAsync(new InviteUserPos()
await this.UserScope.InsertAsync(new UserScope()
{
UserID = UserManager.UserId,
InviteUserID = (long)phoneModel.Scene,
InviteID = invres.InviteID
SysUserID = sysUser.Id,
BaseUserID = wxUser.Id,
});
if (invrey > 0)
}
//判断是否存在邀请
if (phoneModel.Scene != null)
{
//判断当前用户是否被邀请过
var invi = await this.invitaitionRey.FirstOrDefaultAsync(x => x.UserID == UserManager.UserId);
if (invi.IsNullOrZero())
{
//附加奖励给邀请人
UtilService utilService = new UtilService(this.rechargeRep);
utilService.Reward((long)phoneModel.Scene, 1);
var invres = await this.invitaitionRey.FirstOrDefaultAsync(x => x.InviteID == phoneModel.Scene);
//未被邀请
var invrey = await this.invitaitionRey.InsertAsync(new InviteUserPos()
{
UserID = UserManager.UserId,
InviteUserID = (long)phoneModel.Scene,
InviteID = invres.InviteID
});
if (invrey > 0)
{
//附加奖励给邀请人
UtilService utilService = new UtilService(this.rechargeRep);
utilService.Reward((long)phoneModel.Scene, 1);
}
}
}
}
}
var Self = await this.Self.FirstOrDefaultAsync(x => x.CreatedUserId == sysUser.Id);
var Self = await this.Self.FirstOrDefaultAsync(x => x.CreatedUserId == sysUser.Id);
// 获取加密后的密码
var encryptPassword = MD5Encryption.Encrypt(sysUser.Password);
// 获取加密后的密码
var encryptPassword = MD5Encryption.Encrypt(sysUser.Password);
// 验证账号是否被冻结
if (sysUser.Status == CommonStatus.DISABLE)
throw Oops.Oh(ErrorCode.D1017);
//获取对应租户
var tenant = this._sysTenantRep.Single(sysUser.TenantId);
// 验证账号是否被冻结
if (sysUser.Status == CommonStatus.DISABLE)
throw Oops.Oh(ErrorCode.D1017);
//获取对应租户
var tenant = this._sysTenantRep.Single(sysUser.TenantId);
if (tenant.IsNullOrZero())
throw Oops.Oh(ErrorCode.F1001);
if (tenant.IsNullOrZero())
throw Oops.Oh(ErrorCode.F1001);
// 生成Token令牌
authUserOut.Token = JWTEncryption.Encrypt(new Dictionary<string, object>
// 生成Token令牌
authUserOut.Token = JWTEncryption.Encrypt(new Dictionary<string, object>
{
{ClaimConst.CLAINM_USERID, sysUser.Id},
{ClaimConst.TENANT_ID, sysUser.TenantId},
@ -239,31 +254,38 @@ namespace GDZZ.Application.Service.Auth
{ ClaimConst.CLAINM_TENANT_NAME, tenant.Name },
});
// 设置Swagger自动登录
_httpContextAccessor.HttpContext.SigninToSwagger(authUserOut.Token);
// 生成刷新Token令牌
var refreshToken = JWTEncryption.GenerateRefreshToken(authUserOut.Token, 30);
// 设置刷新Token令牌
_httpContextAccessor.HttpContext.Response.Headers["x-access-token"] = refreshToken;
var httpContext = App.HttpContext;
await _eventPublisher.PublishAsync(new ChannelEventSource("Update:UserLoginInfo",
new SysUser { Id = sysUser.Id, LastLoginIp = httpContext.GetLocalIpAddressToIPv4(), LastLoginTime = DateTime.Now }));
// 设置Swagger自动登录
_httpContextAccessor.HttpContext.SigninToSwagger(authUserOut.Token);
// 生成刷新Token令牌
var refreshToken = JWTEncryption.GenerateRefreshToken(authUserOut.Token, 30);
// 设置刷新Token令牌
_httpContextAccessor.HttpContext.Response.Headers["x-access-token"] = refreshToken;
var httpContext = App.HttpContext;
await _eventPublisher.PublishAsync(new ChannelEventSource("Update:UserLoginInfo",
new SysUser { Id = sysUser.Id, LastLoginIp = httpContext.GetLocalIpAddressToIPv4(), LastLoginTime = DateTime.Now }));
authUserOut.Avatar = sysUser.Avatar;
authUserOut.Phone = sysUser.Phone;
authUserOut.Sex = sysUser.Sex;
authUserOut.UserId = sysUser.Id;
authUserOut.UserName = sysUser.Name;
authUserOut.Self = Self.IsEmpty() ? null : Self.Name;
authUserOut.Type = (UserEnum)wxUser.Type;
authUserOut.OpenID = tokenModel.OpenId;
authUserOut.Tenant = tenant.Adapt<TenantOutput>();
authUserOut.Describe = wxUser.Describe;
await this.cacheService.SetUserInfoAsync(authUserOut, authUserOut.UserId);
this.UserScope.CurrentCommitTran();
}
catch (Exception)
{
this.UserScope.CurrentRollbackTran();
throw;
}
authUserOut.Avatar = sysUser.Avatar;
authUserOut.Phone = sysUser.Phone;
authUserOut.Sex = sysUser.Sex;
authUserOut.UserId = sysUser.Id;
authUserOut.UserName = sysUser.Name;
authUserOut.Self = Self.IsEmpty() ? null : Self.Name;
authUserOut.Type = (UserEnum)wxUser.Type;
authUserOut.OpenID = tokenModel.OpenId;
authUserOut.Tenant = tenant.Adapt<TenantOutput>();
authUserOut.Describe = wxUser.Describe;
await this.cacheService.SetUserInfoAsync(authUserOut, authUserOut.UserId);
return authUserOut;
}

@ -39,11 +39,11 @@ namespace GDZZ.Application
/// <summary>
/// 商户ID
/// </summary>
public string MerchantID { get; set; }
public string OpenID { get; set; }
/// <summary>
/// 转账金额
/// </summary>
public dynamic TransferAmount { get; set; }
public decimal TransferAmount { get; set; }
}
}

@ -133,7 +133,7 @@ namespace GDZZ.Application
/// <summary>
/// 公司ID
/// </summary>
public long CompanyID { get; set; }
public long? CompanyID { get; set; }
}
}

@ -80,12 +80,20 @@ namespace GDZZ.Application
public async Task<dynamic> AddLive(AddLiveFriendInput input)
{
var baseUser = await this.Baseuser.AsQueryable().Filter("TenantId", true).Where(x => x.CompanyID == input.CompanyID).SingleAsync();
if (baseUser.IsNullOrZero())
throw Oops.Oh(ErrorCode.B1002);
var user = await this._sysUserRep.Where(x => x.Id == baseUser.CreatedUserId).SingleAsync();
if (user.IsNullOrZero())
throw Oops.Oh(ErrorCode.xg1002);
SysUser user = new SysUser();
if (input.CompanyID.IsNullOrZero())
{
user = await this._sysUserRep.FirstOrDefaultAsync(x => x.Id == input.UserId);
if (user.IsNullOrZero())
throw Oops.Oh(ErrorCode.xg1002);
}
else
{
var buser = await this.Baseuser.FirstOrDefaultAsync(x => x.CompanyID == input.CompanyID);
user = await this._sysUserRep.Where(x => x.Id == buser.CreatedUserId).SingleAsync();
if (user.IsNullOrZero())
throw Oops.Oh(ErrorCode.xg1002);
}
//判断是是好友
var fends =await this.liveUserFriend.Where(x => x.FriendID == UserManager.UserId && x.CreatedUserId == user.Id).FirstAsync();
@ -111,7 +119,9 @@ namespace GDZZ.Application
await this.liveUserFriend.InsertAsync(item);
}
}
await this.cacheService.DelLiveHistoryService(UserManager.UserId);
if(!await this.cacheService.DelLiveHistoryService(UserManager.UserId))
throw Oops.Oh(ErrorCode.B1004);
return user;
}
@ -186,7 +196,6 @@ namespace GDZZ.Application
Id = item.FriendID,
Index = sysUser.Name.Substring(0, 1),
LastSendTime = (DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000000,
};
//查询聊天记录

@ -0,0 +1,20 @@
using GDZZ.Application.Enum;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GDZZ.Application
{
public class Merchant
{
public string out_batch_no { get; set; }
public string batch_id { get; set; }
public DateTime create_time { get; set; }
public string code { get; set; }
public string message { get; set; }
}
}

@ -6,19 +6,31 @@ using System.Runtime.Serialization;
using System.IO;
using System.Text;
using System.Net;
using Flurl;
using Flurl.Http;
using Furion.Logging;
using SqlSugar;
using Enyim.Caching;
using System.Security.Cryptography;
using static System.Net.Mime.MediaTypeNames;
using Microsoft.Extensions.Hosting.Internal;
using Enyim.Caching.Configuration;
using Furion.RemoteRequest;
using Furion.RemoteRequest.Extensions;
using System.Net.Http;
using System.Threading.Tasks;
using System.Security.Policy;
/// <summary>
///JsApiPay 的摘要说明
/// </summary>
public static class JsApiPay
public class JsApiPay : IHttpDispatchProxy
{
public static string WithDrawsToWx(string appid, string mchid, string serialNo, string openID, string partnerTradeNo, decimal totalFee)
const string PrivateKey = "App_Data/cert/apiclient_key.pem";
public async Task<string> WithDrawsToWx(string url, string appid, string mchid, string serialNo, string openID, string partnerTradeNo, decimal totalFee, string hostting)
{
SortedDictionary<string, object> dic = new SortedDictionary<string, object>();
@ -39,10 +51,7 @@ public static class JsApiPay
list.Add(dic1);
dic.Add("transfer_detail_list", list);
var url = "https://api.mch.weixin.qq.com/v3/transfer/batches";
string transactionsResponse = WxV3PostJson(url, Newtonsoft.Json.JsonConvert.SerializeObject(dic), mchid, serialNo);
//Log.Info("商户转账到零钱返回:" , transactionsResponse);
return transactionsResponse; ;
return await WxV3PostJson(url, Newtonsoft.Json.JsonConvert.SerializeObject(dic), mchid, serialNo, hostting); ; ;
}
/// <summary>
/// V3版本请求接口
@ -50,52 +59,49 @@ public static class JsApiPay
/// <param name="url">微信的接口地址</param>
/// <param name="postData">post请求的数据json格式 </param>
/// <param name="privateKey">apiclient_key.pem中的内容不要-----BEGIN PRIVATE KEY----- -----END PRIVATE KEY-----</param>
/// <param name="merchantId">发起请求的商户(包括直连商户、服务商或渠道商)的商户号 mchid</param>
/// <param name="mchId">发起请求的商户(包括直连商户、服务商或渠道商)的商户号 mchid</param>
/// <param name="serialNo">商户证书号</param>
/// <param name="method"></param>
/// <param name="hostting"></param>
/// <returns></returns>
public static string WxV3PostJson(string url, string postData, string mchId, string serialNo)
public async Task<string> WxV3PostJson(string url, string postData, string mchId, string serialNo, string hostting)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
string Authorization = GetAuthorization(url, "POST", postData, mchId, serialNo, hostting);
var request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "POST";
request.ContentType = "application/json;charset=UTF-8";
request.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3100.0 Safari/537.36";
request.Accept = "application/json";
//Log.Info("发起开始申请商户转账到零钱请求", "发起请求1");
string Authorization = GetAuthorization(url, "POST", postData, mchId, serialNo);
request.Headers.Add("Authorization", Authorization);
//Log.Info("返回申请商户转账到零钱结果", Authorization);
byte[] paramJsonBytes;
paramJsonBytes = System.Text.Encoding.UTF8.GetBytes(postData);
request.ContentLength = paramJsonBytes.Length;
byte[] byteData = System.Text.Encoding.UTF8.GetBytes(postData);
request.ContentLength = byteData.Length;
Stream writer;
try
{
writer = request.GetRequestStream();
}
catch (Exception)
catch (Exception e)
{
writer = null;
Console.Write("连接服务器失败!");
throw;
}
writer.Write(paramJsonBytes, 0, paramJsonBytes.Length);
writer.Write(byteData, 0, byteData.Length);
writer.Close();
string responseString = "";
HttpWebResponse response;
try
{
response = (HttpWebResponse)request.GetResponse();
}
catch (WebException ex)
catch (WebException e)
{
response = ex.Response as HttpWebResponse;
response = e.Response as HttpWebResponse;
}
Stream resStream = response.GetResponseStream();
StreamReader reader = new StreamReader(resStream);
string text = reader.ReadToEnd();
return text;
responseString = reader.ReadToEnd();
return responseString;
}
private static string GetAuthorization(string url, string method, string jsonParame, string mchId, string serialNo)
private string GetAuthorization(string url, string method, string jsonParame, string mchId, string serialNo, string hostting)
{
var uri = new Uri(url);
string urlPath = uri.PathAndQuery;
@ -107,7 +113,7 @@ public static class JsApiPay
//Log.Info("请求message", message);
//string signTxt = Sign(message, privateKey);
string signTxt = Sign(message);
string signTxt = Sign(message, hostting);
//Authorization和格式
@ -122,17 +128,31 @@ public static class JsApiPay
}
private static string Sign(string message)
private string Sign(string message, string hostting)
{
string base64X509Cert = "";
string pemPublicCert = Path.Combine(hostting, PrivateKey);
using (FileStream fs = new FileStream(pemPublicCert, FileMode.Open, FileAccess.Read))
{
using (StreamReader sr = new StreamReader(fs))
{
base64X509Cert = sr.ReadToEnd().Trim();
}
}
base64X509Cert = base64X509Cert.Replace("-----BEGIN PRIVATE KEY-----", "").Replace("-----END PRIVATE KEY-----", "").Replace("\r", "").Replace("\n", "");
// NOTE 私钥不包括私钥文件起始的-----BEGIN PRIVATE KEY-----
// 亦不包括结尾的-----END PRIVATE KEY-----
string privateKey = "{你的私钥}";
byte[] keyData = Convert.FromBase64String(privateKey);
byte[] keyData = Convert.FromBase64String(base64X509Cert);
var rsa = RSA.Create();
rsa.ImportPkcs8PrivateKey(keyData, out _);
byte[] data = System.Text.Encoding.UTF8.GetBytes(message);
return Convert.ToBase64String(rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1));
var sigdata = rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
var res = Convert.ToBase64String(sigdata);
return res;
}
}

@ -27,6 +27,8 @@ using Senparc.CO2NET.Cache.Redis;
using GDZZ.Core.Service;
using System.Linq.Dynamic.Core.Tokenizer;
using System.Diagnostics;
using Microsoft.AspNetCore.Hosting;
using Furion.JsonSerialization;
namespace GDZZ.Application.Service.WXPay
{
@ -35,7 +37,7 @@ namespace GDZZ.Application.Service.WXPay
{
private readonly IHostingEnvironment hostingEnvironment;
private readonly SqlSugarRepository<BaseUser> Baseuser; // wx用户仓储
private readonly SqlSugarRepository<SysUser> _sysUserRep; // 用户表仓储
private readonly SqlSugarRepository<SysTenant> _sysTenantRep; //租户仓储
@ -71,6 +73,7 @@ namespace GDZZ.Application.Service.WXPay
SqlSugarRepository<MiniPayTake> payTakeRep,
SqlSugarRepository<ReFund> refundRep,
WechatOAuth wechatOAuth,
IHostingEnvironment hostingEnvironment,
IHttpContextAccessor _httpContextAccessor,
IEventPublisher eventPublisher)
{
@ -85,6 +88,7 @@ namespace GDZZ.Application.Service.WXPay
this.payTakeRep = payTakeRep;
this._wechatOAuth = wechatOAuth;
this._oauthConfig = options.Value.SenparcWeixin;
this.hostingEnvironment = hostingEnvironment;
this._httpContextAccessor = _httpContextAccessor;
}
@ -128,6 +132,7 @@ namespace GDZZ.Application.Service.WXPay
public async Task<dynamic> UpBalance(decimal Consume,long? ResumeID)
{
this.balance.CurrentBeginTran();
var comrep = await this.ComsumeRep.Where(x=>x.ResumeID== ResumeID).FirstAsync();
if(!comrep.IsNullOrZero()) //已经消费过了
return "";
@ -424,19 +429,21 @@ namespace GDZZ.Application.Service.WXPay
/// </summary>
/// <returns></returns>
[HttpPost]
[Route("Mini/v1/GetBalance")]
[Route("Mini/v1/MerchantTransfer")]
public async Task<dynamic> MerchantTransfer(TransferInput wxRefundInput)
{
var hostting = this.hostingEnvironment.ContentRootPath;
//读取商户信息
var appid = Config.SenparcWeixinSetting.TenPayV3_AppId;
var mchid = Config.SenparcWeixinSetting.TenPayV3_MchId;
var serialno = Config.SenparcWeixinSetting.TenPayV3_SerialNumber;
string partnerTradeNo = "xcx" + DateTime.Now.ToString("yyyyMMddHHmmfff");
string result = JsApiPay.WithDrawsToWx(appid, mchid, serialno, wxRefundInput.MerchantID, partnerTradeNo, Convert.ToInt32(wxRefundInput.TransferAmount * 100));
string url = string.Format("https://api.mch.weixin.qq.com/v3/transfer/batches");
string partnerTradeNo = "tk" + DateTime.Now.ToString("yyyyMMddHHmmfff");
JsApiPay jsApiPay = new JsApiPay();
string result =await jsApiPay.WithDrawsToWx(url, appid, mchid, serialno, wxRefundInput.OpenID, partnerTradeNo,
Convert.ToInt32(wxRefundInput.TransferAmount * 100), hostting);
return result;
}

@ -244,6 +244,8 @@ public class SenparcWeixinSetting
public string TenPayV3_PrivateKey { get; set; }
public string TenPayV3_SerialNumber { get; set; }
public string TenPayV3_ApiV3Key { get; set; }
public string TenPayv3_WxTransfer { get; set; }
}

@ -431,4 +431,10 @@ public enum ErrorCode
/// </summary>
[ErrorCodeItemMetadata("手机已经存在")]
B1003,
/// <summary>
/// 数据删除失败
/// </summary>
[ErrorCodeItemMetadata("数据删除失败")]
B1004,
}

@ -2620,6 +2620,11 @@
手机已经存在
</summary>
</member>
<member name="F:GDZZ.Core.ErrorCode.B1004">
<summary>
数据删除失败
</summary>
</member>
<member name="T:GDZZ.Core.FileExtensionEnum">
<summary>
文件扩展枚举

@ -18,8 +18,6 @@ public static class UserManager
/// </summary>
public static string Account => App.User.FindFirst(ClaimConst.CLAINM_ACCOUNT)?.Value;
/// <summary>
/// 租户ID
/// </summary>

@ -158,9 +158,9 @@
* 1
* 2~/App_Data/cert/apiclient_key.pem App_Data
*/
"TenPayV3_PrivateKey": "#{TenPayV3_PrivateKey}#", //
"TenPayV3_SerialNumber": "56FA7394CDD74ACB0329480EAB7185BE7A2CB081", //
"TenPayV3_ApiV3Key": "#{TenPayV3_APIv3Key}#", //APIv3
"TenPayV3_PrivateKey": "/App_Data/cert/apiclient_key.pem", //
"TenPayV3_SerialNumber": "133833F7E1F4083BF191A81938EF25AC659F23D6", //
"TenPayV3_ApiV3Key": "ZZRZJKvAstv4SJapStuyHhCOzqrrjSUD", //APIv3
//TenPayV3_WxOpenTenpayNotify TenPayV3_TenpayNotify "WxOpen"
"TenPayV3_WxOpenTenpayNotify": "https://admin.gdzongzhi.com/api/Mini/NotifyUrlWxOpen", //http://YourDomainName/TenpayV3/PayNotifyUrlWxOpen

@ -0,0 +1,25 @@
-----BEGIN CERTIFICATE-----
MIIEMTCCAxmgAwIBAgIUEzgz9+H0CDvxkagZOO8lrGWfI9YwDQYJKoZIhvcNAQEL
BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT
FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg
Q0EwHhcNMjQwMTIyMDgyMjIyWhcNMjkwMTIwMDgyMjIyWjCBijETMBEGA1UEAwwK
MTY0MDM2MzE2NzEbMBkGA1UECgwS5b6u5L+h5ZWG5oi357O757ufMTYwNAYDVQQL
DC3lub/kuJznnIHnurXmmbrkvIHkuJrnrqHnkIblkqjor6LmnInpmZDlhazlj7gx
CzAJBgNVBAYTAkNOMREwDwYDVQQHDAhTaGVuWmhlbjCCASIwDQYJKoZIhvcNAQEB
BQADggEPADCCAQoCggEBAMGnNPWyzoN6ldrf0B8coEu0naNSCJZvXRYKKOi8VH17
uNwbuxdYP4G0rWQC1hSbqc7Qz3iFEW5PqKA2ZqoDBdhFNqZTSVMOcw8Dagz0ya1m
a3EdjXSGp7prHNXRSWQtVGYVPqPmg1qrt+Uptfkd75Z1EmIxDMoN8ERLToOtmfi5
vTWWS0TZ9xaE99qGVirb4yjDr9qrutSCx/C1xNHssAnT3m2x5bYHiOEcDGtVp6iG
DqvUBpLaqSa3IxJlQZSZP3mdrV7i2o8tlw8heSQJ+o7VWWrP0V/Y1CgPhsT0HBK4
PRQRPfpHGv98W5tf+XBPorrjYqBFAc6TpoZHKLTY6BcCAwEAAaOBuTCBtjAJBgNV
HRMEAjAAMAsGA1UdDwQEAwID+DCBmwYDVR0fBIGTMIGQMIGNoIGKoIGHhoGEaHR0
cDovL2V2Y2EuaXRydXMuY29tLmNuL3B1YmxpYy9pdHJ1c2NybD9DQT0xQkQ0MjIw
RTUwREJDMDRCMDZBRDM5NzU0OTg0NkMwMUMzRThFQkQyJnNnPUhBQ0M0NzFCNjU0
MjJFMTJCMjdBOUQzM0E4N0FEMUNERjU5MjZFMTQwMzcxMA0GCSqGSIb3DQEBCwUA
A4IBAQCTh90ch63bv8M0xPwCk31nuM5YZL3TsOxN8ZB+3odVEjY/zWuypSgK2aM7
VuvroBsivAlLKefqw/U0JXw2oHNGveWW9Bq71K2KJmb8E+8yLzF9jae1mwCQmBo0
+r0D2OWDdXKrJ0eFAu4Ug6LYIu7x//JoUW3e0jY+BgiOZgkOl7LdI0bmGHiQ8LMr
g0BgUSWc7TDd9r9UH86Rq9E0leA+5ftIN3QDfpIsxcoKXFG1hlFEj6hHQP25aKS2
ViGl35FrUyOC+/2MAyJqjEpfTGOrX3s5HyXDyM19Qqd+oCiLjc0vblWY0Zk8Y22a
DIzWjsG1cpBTaRqxkLaquCkyR26G
-----END CERTIFICATE-----

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDBpzT1ss6DepXa
39AfHKBLtJ2jUgiWb10WCijovFR9e7jcG7sXWD+BtK1kAtYUm6nO0M94hRFuT6ig
NmaqAwXYRTamU0lTDnMPA2oM9MmtZmtxHY10hqe6axzV0UlkLVRmFT6j5oNaq7fl
KbX5He+WdRJiMQzKDfBES06DrZn4ub01lktE2fcWhPfahlYq2+Mow6/aq7rUgsfw
tcTR7LAJ095tseW2B4jhHAxrVaeohg6r1AaS2qkmtyMSZUGUmT95na1e4tqPLZcP
IXkkCfqO1Vlqz9Ff2NQoD4bE9BwSuD0UET36Rxr/fFubX/lwT6K642KgRQHOk6aG
Ryi02OgXAgMBAAECggEACBBXYzfL46uyG8ggGXuOrThbLBbZZrJCdQ19QJu/BO6m
9vtsof85vcPxSG3ZzkfhHUySpxkbbbWBdxJs2f2AO84+BVUIg53haqmgu+Nhlofi
R3aMkmKdD2UwcTLi1HoSvqF510ddBuSJptBC1JnLhT9gwZf7SVqlO6LLJS6Qb8vx
hMqNlvG3EqBVGSTuE8mTjcga3lmI9eAxz8rL9sZ59lj17k2JTxW6/Q3QjeTRqIpA
UTm+SOjVYn++2kHB3Ix8RYD5vClsZ7BpMS0f3fko6qt/SuRHS0wZF2RoELia+ohv
AyON+jKGSGRVivCiMPISDE7UGaA/cxKrolTok63QWQKBgQD8v14vWxZVp15VPosb
1quBUDajQLBjfNIifoU1Aqc7VD2zLJ79LN7Pngv84p55TAS3+MTcu/elYGeUeZuS
X/JexTJmSE30FtmGhpCuf/9VXyj7rN6sYbJkj+1ENFFv/qM0NBxagk/aVewM+PHs
tIetibw+qZ0iiabnSUrqBMMVkwKBgQDEJSm4hkzGdL3I1oOyVbju/gMXcMM3IMxZ
RpsmUQ8qhjid9KvBvC/AmWJFyDAkAkVownAcvSwoJAMSNUuApghEPDqRvvNnoc72
lmtbwF5s6veyadZMVnrTc/ipFZdZg9ya+Qs0Kocbnceo2fJ+R5+HBtt+Wd8GE0oc
24//+C+17QKBgQCFdsxWd2QI4POYUgmFLsur4l6nwG4kavJP0r2mq3sBgk9+gO6H
xJz3x36PEGAcrz7CozPZV8zC4HBx3/F9zvqefsVJa572aOZ++ioGa4K6YyCyHawM
HR7lqXbiEDp8yFsIIwhh5vQh2ENo6kBd/Uq0Icps0IYwib2/3l0XzGHzzwKBgEsa
h70V/3PF451xNgAk/qjULk7daIJFVrmgZWvogcwglLE2rEWETyyKDqz1mClRjU4t
lUwLy0qbb2mbaouaB3RJM4v297BorpyQwA7ju8QsvCdeiyWzv1gUAdSMZeVqrh/Q
2E9jMVSLt5WZzlY4CodjQsxAkTr8S9Z848h5OZuJAoGBAKaaRH/9FY7igN5okg6s
4GkoxvhOtUZ2quMCK79xKnq7SaMXz07qEhBK/a9ajYvzlbOCw9ZcX3pe641IbE+K
03Fo7Uy/dJEtKE9rqA0lefGR9GpMbb6PdeHh9RvOKftMhrk8p8rL/GwUc2eFzgse
BfWBXzr2mpCy3arBimmjeyx/
-----END PRIVATE KEY-----
Loading…
Cancel
Save