一、相关描述
文档地址:微信支付-开发者文档
二、下单和商户订单号查询代码:
using Newtonsoft.Json;
using System;
using System.Configuration;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace 微信的Native支付_用于网页端扫描二维码支付_
{
class Program
{
#region 公用参数
private readonly static string url = "https://api.mch.weixin.qq.com/v3/pay/transactions/native"; //请求地址
private readonly static string appId = ConfigurationManager.AppSettings["appId"]; //应用ID
private readonly static string mchId = ConfigurationManager.AppSettings["mchId"]; //直连商户号
private readonly static QRCodeDBHelper _codeDb = new QRCodeDBHelper();
#endregion
static void Main(string[] args)
{
try
{
//string qrCodePath = PlaceGenerateQRCode("测试商品", "http://w.hrflag.cn/", "9.90").Result; //获取下单支付二维码
SelectOrderInfo("O202307063473971331157"); //根据商户订单查找订单信息
Console.ReadKey();
}
catch (Exception e)
{
Console.WriteLine("错误信息:" + e.Message);
}
}
/// <summary>
/// Native下单生成二维码
/// </summary>
/// <param name="descriptionText">商品描述</param>
/// <param name="notify_url">通知地址</param>
/// <param name="price">总金额</param>
/// <returns></returns>
private static async Task<string> PlaceGenerateQRCode(string descriptionText, string notify_urlText, string price)
{
string qrCodePath = string.Empty; //二维码路径
decimal amountDecimal = decimal.Parse(price); // 将字符串金钱解析为 decimal 类型
int amountInCents = (int)(amountDecimal * 100); // 将金额转换为分,使用整数类型存储
string orderNo = GetOrderNo(); //订单号
//接口请求参数
var requestData = new
{
appid = appId,
mchid = mchId,
description = descriptionText,
out_trade_no = orderNo, //商户订单号
notify_url = notify_urlText,
amount = new
{
total = amountInCents,//订单总金额,单位为分。
currency = "CNY" // 货币类型
}
};
string jsonStr = JsonConvert.SerializeObject(requestData); //json字符串参数
string jsonResponse = await PostUrl(url, jsonStr); //获取微信支付链接
qrCodePath = _codeDb.GenerateQRCode(jsonResponse, 320, 320);//生成二维码
return qrCodePath;
}
/// <summary>
/// 查询订单信息
/// </summary>
/// <param name="orderId">商户订单号</param>
/// <returns></returns>
public static async Task<string> SelectOrderInfo(string orderId)
{
string responesJsonStr = string.Empty; //订单返回信息
string url = $"https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/{orderId}?mchid={mchId}"; //微信商户订单号查询订单接口
try
{
string body = "";
string timestamp = DateTimeOffset.Now.ToUnixTimeSeconds().ToString(); //获取本地时间戳
string trade_no = GenerateSign(appId, timestamp, GenerateNonceStr()); //随机串
string message = $"GET\n/v3/pay/transactions/out-trade-no/{orderId}?mchid={mchId}\n{timestamp}\n{trade_no}\n{body}\n"; //构造签名串 注意每个支付类型需要修改一下路径:/v3/pay/transactions/native
string signature = Sign(message);//获取签名值
//请求接口
using (HttpClient client = new HttpClient())
{
// 创建HttpRequestMessage对象,并设置请求头
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.Accept.Clear();
request.Headers.Accept.ParseAdd("application/json");
// 设置User-Agent头
request.Headers.UserAgent.ParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36");
string authorization = $"WECHATPAY2-SHA256-RSA2048 mchid=\"{mchId}\",nonce_str=\"{trade_no}\",signature=\"{signature}\",timestamp=\"{timestamp}\",serial_no=\"{ConfigurationManager.AppSettings["serialno"]}\"";
request.Headers.Add("Authorization", authorization);
//request.Content = new StringContent(jsonStr, Encoding.UTF8, "application/json");
// 发送请求并获取响应 在ASP.NET WebAPI项目中,存在线程同步上下文(SynchronizationContext),这可能会导致异步操作被阻塞或死锁。尝试在WebAPI项目中使用ConfigureAwait(false)来避免上下文切换,例如:await client.SendAsync(request).ConfigureAwait(false);。这可以确保异步操作不会受到同步上下文的影响。
HttpResponseMessage response = await client.SendAsync(request);
// 读取响应内容
responesJsonStr = await response.Content.ReadAsStringAsync();
dynamic prepayId = JsonConvert.DeserializeObject<dynamic>(responesJsonStr);
}
return responesJsonStr;
}
catch (Exception e)
{
throw new Exception(e.Message);
}
}
#region 调用微信支付接口
/// <summary>
/// Post请求微信支付接口并返回接口响应内容
/// </summary>
/// <param name="url">微信接口</param>
/// <param name="parment">参数 JSON字符串</param>
/// <returns>返回支付二维码链接</returns>
private static async Task<string> PostUrl(string url, string parment)
{
string jsonResponse = string.Empty; //读取接口响应内容
string timestamp = DateTimeOffset.Now.ToUnixTimeSeconds().ToString(); //获取本地时间戳
string trade_no = GenerateSign(appId, timestamp, GenerateNonceStr()); //随机串
string message = $"POST\n/v3/pay/transactions/native\n{timestamp}\n{trade_no}\n{parment}\n"; //构造签名串 注意每个支付类型需要修改一下路径:/v3/pay/transactions/native
string signature = Sign(message);//获取签名值
try
{
using (HttpClient client = new HttpClient())
{
// 创建HttpRequestMessage对象,并设置请求头
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, url);
request.Headers.Accept.Clear();
request.Headers.Accept.ParseAdd("application/json");
// 设置User-Agent头
request.Headers.UserAgent.ParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36");
string authorization = $"WECHATPAY2-SHA256-RSA2048 mchid=\"{mchId}\",nonce_str=\"{trade_no}\",signature=\"{signature}\",timestamp=\"{timestamp}\",serial_no=\"{ConfigurationManager.AppSettings["serialno"]}\"";
request.Headers.Add("Authorization", authorization);
request.Content = new StringContent(parment, Encoding.UTF8, "application/json");
// 发送请求并获取响应 在ASP.NET WebAPI项目中,存在线程同步上下文(SynchronizationContext),这可能会导致异步操作被阻塞或死锁。尝试在WebAPI项目中使用ConfigureAwait(false)来避免上下文切换,例如:await client.SendAsync(request).ConfigureAwait(false);。这可以确保异步操作不会受到同步上下文的影响。
HttpResponseMessage response = await client.SendAsync(request);
// 读取响应内容
jsonResponse = await response.Content.ReadAsStringAsync();
dynamic prepayId = JsonConvert.DeserializeObject<dynamic>(jsonResponse);
jsonResponse = prepayId.code_url; //返回二维码链接
}
return jsonResponse;
}
catch (Exception e)
{
throw new Exception(e.Message);
}
}
#endregion
#region 微信签名
/// <summary>
/// 签名
/// </summary>
/// <param name="message">参数</param>
/// <returns></returns>
protected static string Sign(string message)
{
// 私钥
string privateKey = ConfigurationManager.AppSettings["wxapiKey"];
byte[] keyData = Convert.FromBase64String(privateKey);
using (CngKey cngKey = CngKey.Import(keyData, CngKeyBlobFormat.Pkcs8PrivateBlob))
using (RSACng rsa = new RSACng(cngKey))
{
byte[] data = System.Text.Encoding.UTF8.GetBytes(message);
return Convert.ToBase64String(rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1));
}
}
#endregion
#region 微信生成随机数
private static string GenerateSign(string appId, string timeStamp, string nonceStr)
{
string stringToSign = $"appId={appId}&nonceStr={nonceStr}&timeStamp={timeStamp}";
stringToSign += "&key=" + ConfigurationManager.AppSettings["APIV3"]; // 替换为APIV3秘钥
using (var md5 = MD5.Create())
{
byte[] hashBytes = md5.ComputeHash(Encoding.UTF8.GetBytes(stringToSign));
StringBuilder sb = new StringBuilder();
foreach (byte b in hashBytes)
{
sb.Append(b.ToString("x2"));
}
return sb.ToString().ToUpper();
}
}
#endregion
#region 生成订单号
/// <summary>
/// 生成订单号
/// </summary>
/// <returns></returns>
private static string GetOrderNo()
{
string strOrderNo = "O" + DateTime.Now.ToString("yyyyMMddfff") + GenerateNonceStr();
return strOrderNo;
}
private static string GenerateNonceStr()
{
return GetRandomUInt().ToString();
}
private static uint GetRandomUInt()
{
var randomBytes = GenerateRandomBytes(sizeof(uint));
return BitConverter.ToUInt32(randomBytes, 0);
}
private static byte[] GenerateRandomBytes(int bytesNumber)
{
RNGCryptoServiceProvider csp = new RNGCryptoServiceProvider();
byte[] buffer = new byte[bytesNumber];
csp.GetBytes(buffer);
return buffer;
}
#endregion
}
}
三、生成二维码封装代码
/// <summary>
/// 生成二维码(访问链接并设置二维码宽高)
/// </summary>
/// <param name="url">链接</param>
/// <param name="width">宽</param>
/// <param name="height">高</param>
/// <returns>返回二维码路径</returns>
public string GenerateQRCode(string url, int width, int height)
{
try
{
//生成二维码
using (QRCodeGenerator qrCodeGenerator = new QRCodeGenerator())
{
QRCodeData qrCodeData = qrCodeGenerator.CreateQrCode(url, QRCodeGenerator.ECCLevel.Q);
QRCode qrCode = new QRCode(qrCodeData);
Bitmap qrBitmap = qrCode.GetGraphic(100); //设置像素100
Bitmap resizeImage = new Bitmap(qrBitmap, new Size(width, height)); //设置宽度
string fileName = DateTimeOffset.Now.ToUnixTimeSeconds() + ".png"; //获取本地时间戳
string mlPath = Directory.GetCurrentDirectory() + "\\WxPayImage\\"; //目录地址
if (!Directory.Exists(mlPath)) Directory.CreateDirectory(mlPath); //没有当前目录则添加目录
string filePath = Path.Combine(mlPath + fileName); //拼接文件路径
resizeImage.Save(filePath, ImageFormat.Png); //保存文件到指定路径
return filePath;//返回二维码路径
}
}
catch (Exception e)
{
throw new Exception(e.Message);
}
}