199 lines
7.7 KiB
C#
199 lines
7.7 KiB
C#
|
||
using Microsoft.Extensions.Options;
|
||
using NPOI.SS.Formula.Functions;
|
||
using Senparc.Weixin.TenPayV3;
|
||
using Senparc.Weixin.TenPayV3.Apis;
|
||
using Senparc.Weixin.TenPayV3.Apis.BasePay;
|
||
using Senparc.Weixin.TenPayV3.Entities;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.IO;
|
||
using System.Linq;
|
||
using System.Net.Http;
|
||
using System.Text;
|
||
using System.Threading;
|
||
using System.Threading.Tasks;
|
||
|
||
|
||
namespace New_College.Common.Helper
|
||
{
|
||
public static class WeChatPayV3
|
||
{
|
||
|
||
|
||
|
||
/// <summary>
|
||
/// 扫码支付-Native下单API
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
public static async Task<MemoryStream> QrCodePay(int Price, string title)
|
||
{
|
||
///native
|
||
string url = "https://api.mch.weixin.qq.com/v3/pay/transactions/native";
|
||
|
||
//使用 Native 支付,输出二维码并展示
|
||
MemoryStream fileStream = null;//输出图片的URL
|
||
var price = (int)(Price * 100);
|
||
var name = title + " - Native 支付";
|
||
var sp_billno = string.Format("{0}{1}{2}", WeixinConfig.MCHID, SystemTime.Now.ToString("yyyyMMddHHmmss"),
|
||
TenPayV3Util.BuildRandomStr(6));
|
||
var notifyUrl = WeixinConfig.NotifyUrl;
|
||
TransactionsRequestData requestData = new(WeixinConfig.Appid, WeixinConfig.MCHID, name, sp_billno, new TenpayDateTime(DateTime.Now.AddHours(1)), null, notifyUrl, null, new() { currency = "CNY", total = price }, null, null, null, null);
|
||
BasePayApis basePayApis = new BasePayApis();
|
||
var result = await basePayApis.NativeAsync(requestData);
|
||
//进行安全签名验证
|
||
if (result.VerifySignSuccess == true)
|
||
{
|
||
fileStream = QrCodeHelper.GerQrCodeStream(result.code_url);
|
||
}
|
||
else
|
||
{
|
||
fileStream = QrCodeHelper.GetTextImageStream("Native Pay 未能通过签名验证,无法显示二维码");
|
||
}
|
||
return fileStream;
|
||
}
|
||
|
||
|
||
/**
|
||
*
|
||
* 统一下单
|
||
* @param WxPaydata inputObj 提交给统一下单API的参数
|
||
* @param int timeOut 超时时间
|
||
* @throws WePayException
|
||
* @return 成功时返回,其他抛异常
|
||
*/
|
||
public static WxPayData UnifiedOrder(WxPayData inputObj, int timeOut = 60)
|
||
{
|
||
string url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
|
||
//检测必填参数
|
||
if (!inputObj.IsSet("out_trade_no"))
|
||
{
|
||
throw new Exception("缺少统一支付接口必填参数out_trade_no!");
|
||
}
|
||
else if (!inputObj.IsSet("body"))
|
||
{
|
||
throw new Exception("缺少统一支付接口必填参数body!");
|
||
}
|
||
else if (!inputObj.IsSet("total_fee"))
|
||
{
|
||
throw new Exception("缺少统一支付接口必填参数total_fee!");
|
||
}
|
||
else if (!inputObj.IsSet("trade_type"))
|
||
{
|
||
throw new Exception("缺少统一支付接口必填参数trade_type!");
|
||
}
|
||
|
||
//关联参数
|
||
if (inputObj.GetValue("trade_type").ToString() == "JSAPI" && !inputObj.IsSet("openid"))
|
||
{
|
||
throw new Exception("统一支付接口中,缺少必填参数openid!trade_type为JSAPI时,openid为必填参数!");
|
||
}
|
||
if (inputObj.GetValue("trade_type").ToString() == "NATIVE" && !inputObj.IsSet("product_id"))
|
||
{
|
||
throw new Exception("统一支付接口中,缺少必填参数product_id!trade_type为JSAPI时,product_id为必填参数!");
|
||
}
|
||
|
||
//异步通知url未设置,则使用配置文件中的url
|
||
if (!inputObj.IsSet("notify_url"))
|
||
{
|
||
inputObj.SetValue("notify_url", WeixinConfig.NotifyUrl);//异步通知url
|
||
}
|
||
|
||
inputObj.SetValue("appid", WeixinConfig.Appid);//公众账号ID
|
||
inputObj.SetValue("mch_id", WeixinConfig.MCHID);//商户号
|
||
inputObj.SetValue("spbill_create_ip", WePayConfig.IP);//终端ip
|
||
inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串
|
||
|
||
//签名
|
||
inputObj.SetValue("sign", inputObj.MakeSign());
|
||
string xml = inputObj.ToXml();
|
||
|
||
var start = DateTime.Now;
|
||
|
||
// Log.Info("XcxPayApi", "UnfiedOrder request : " + xml);
|
||
string response = HttpPost(xml, url, "application/xml", timeOut);
|
||
//Log.Info("XcxPayApi", "UnfiedOrder response : " + response);
|
||
// WebHookHelper.WebHookmarkdownSend(response);
|
||
|
||
var end = DateTime.Now;
|
||
int timeCost = (int)((end - start).TotalMilliseconds);
|
||
|
||
WxPayData result = new WxPayData();
|
||
result.FromXml(response);
|
||
// ReportCostTime(url, timeCost, result);//测速上报网络不好时使用
|
||
return result;
|
||
}
|
||
/// <summary>
|
||
/// 生成随机数
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
public static string GenerateNonceStr()
|
||
{
|
||
return Guid.NewGuid().ToString().Replace("-", "");
|
||
}
|
||
/// <summary>
|
||
/// POST请求
|
||
/// </summary>
|
||
/// <param name="postData"></param>
|
||
/// <param name="url"></param>
|
||
/// <param name="contentType">application/xml、application/json、application/text、application/x-www-form-urlencoded</param>
|
||
/// <param name="timeOut"></param>
|
||
/// <param name="headers"></param>
|
||
/// <returns></returns>
|
||
public static string HttpPost(string postData, string url, string contentType = null, int timeOut = 30, Dictionary<string, string> headers = null)
|
||
{
|
||
postData = postData ?? "";
|
||
|
||
var httpClientHandler = new HttpClientHandler
|
||
{
|
||
ServerCertificateCustomValidationCallback = (message, certificate2, arg3, arg4) => true
|
||
};
|
||
using (HttpClient httpClient = new HttpClient(httpClientHandler))
|
||
{
|
||
if (headers != null)
|
||
{
|
||
foreach (var header in headers)
|
||
httpClient.DefaultRequestHeaders.Add(header.Key, header.Value);
|
||
}
|
||
using (HttpContent client = new StringContent(postData, Encoding.UTF8))
|
||
{
|
||
if (contentType != null)
|
||
client.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(contentType);
|
||
|
||
HttpResponseMessage response = httpClient.PostAsync(url, client).Result;
|
||
return response.Content.ReadAsStringAsync().Result;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
|
||
/// <summary>
|
||
///
|
||
/// </summary>
|
||
public class WePayConfig
|
||
{
|
||
//=======【商户系统后台机器IP】=====================================
|
||
/* 此参数可手动配置也可在程序中自动获取
|
||
*/
|
||
public const string IP = "8.8.8.8";
|
||
|
||
|
||
//=======【代理服务器设置】===================================
|
||
/* 默认IP和端口号分别为0.0.0.0和0,此时不开启代理(如有需要才设置)
|
||
*/
|
||
public const string PROXY_URL = "";
|
||
|
||
//=======【上报信息配置】===================================
|
||
/* 测速上报等级,0.关闭上报; 1.仅错误时上报; 2.全量上报
|
||
*/
|
||
public const int REPORT_LEVENL = 1;
|
||
|
||
//=======【日志级别】===================================
|
||
/* 日志等级,0.不输出日志;1.只输出错误信息; 2.输出错误和正常信息; 3.输出错误信息、正常信息和调试信息
|
||
*/
|
||
public const int LOG_LEVENL = 3;
|
||
}
|
||
}
|