目录
注意
微信开发前期准备工作参照:【微信开发】微信支付前期准备工作(申请及配置)-CSDN博客
本文仅提供微信支付下单,付款,回调,退款等基础功能内容,更多微信支付功能请参照微信支付官网:微信支付开发者文档
正文
微信支付类
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Parameters;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Security.Cryptography;
namespace WaterCloud.Code.Web
{
/// <summary>
/// 微信支付类
/// 微信支付版本:V3
/// 所有方法只使用了接口提供的部分参数,可更具业务场景需要修改接口参数
/// </summary>
public class WXPayHelper
{
#region 公共参数
/// <summary>
/// 公众平台AppId
/// 这里填写你自己的公众平台AppId
/// </summary>
public static string appid = "自己的公众平台appid";
/// <summary>
/// 商户号
/// 这里填写你自己的商户号
/// </summary>
public static string mch_id = "自己的商户id";
/// <summary>
/// 特约商户微信支付API秘钥
/// 这里填写你自己的特约商户微信支付API秘钥
/// </summary>
public static string wxPayApiKey = "自己的api密钥";
/// <summary>
/// 商户私钥
/// (微信支付文档->下载并配置商户证书时生成)
/// NOTE: 私钥不包括私钥文件起始的-----BEGIN PRIVATE KEY-----
/// 亦不包括结尾的-----END PRIVATE KEY-----
/// 这里填写你自己的商户私钥
/// </summary>
public static string privateKey = @"自己的私钥";
/// <summary>
/// 商户证书序列号
/// (微信支付文档->下载并配置商户证书时生成)
/// 这里填写你自己的商户证书序列号
/// </summary>
public static string certificateKey = "自己的序列号";
/// <summary>
/// 回调地址
/// (微信支付后自动回调商户平台的外网地址,微信平台自动调用,用于处理支付后的工作)
/// 一般用于通过微信调用该接口地址传入的参数解析后获取支付结果,处理不同支付结果的业务逻辑
/// 需与微信商户号中支付目录地址配置一致
/// 回调地址随便外网能访问就行 /tPosition/WX_Callback
/// 注意!!!!!! 不以“/”结尾
/// </summary>
public static string notifyurl = "自己的回调地址";
/// <summary>
/// 统一退款接口
/// 微信支付平台提供的统一退款接口地址
/// </summary>
private readonly string OutUrl = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds";
#endregion
#region 公共参数类
#region 下单基类
/// <summary>
/// 下单请求参数基类
/// 详情参见链接:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_1.shtml 请求参数
/// </summary>
private class PayOrderbodyModelBase
{
/// <summary>
/// 直连商户号
/// </summary>
public string mchid { get; set; }
/// <summary>
/// 应用ID
/// </summary>
public string appid { get; set; }
/// <summary>
/// 商品描述
/// </summary>
public string description { get; set; }
/// <summary>
/// 商户订单号
/// </summary>
public string out_trade_no { get; set; }
/// <summary>
/// 通知地址
/// </summary>
public string notify_url { get; set; }
/// <summary>
/// 订单金额
/// </summary>
public object amount { get; set; }
}
/// <summary>
/// 下单返回结果基类
/// </summary>
private class ReturnParametersBase
{
/// <summary>
/// 返回结果【true/false】
/// </summary>
public bool result { get; set; }
/// <summary>
/// 错误描述
/// </summary>
public string errmsg { get; set; }
}
/// <summary>
/// 通用订单金额类
/// 详情参见链接:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_1.shtml
/// 请求参数中 的 订单金额(amount)参数
/// </summary>
private class amount
{
/// <summary>
/// 金钱
/// </summary>
public int total { get; set; }
/// <summary>
/// 货币类型
/// CNY:人民币,境内商户号仅支持人民币。
/// </summary>
public string currency { get; set; } = "CNY";
}
/// <summary>
/// 通用订单金额类
/// 详情参见链接:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_1.shtml
/// 请求参数中 的 场景信息(scene_info)参数
/// </summary>
private class scene_info
{
/// <summary>
/// 用户终端IP
/// 用户的客户端IP,支持IPv4和IPv6两种格式的IP地址。
/// 示例值:14.23.150.211
/// </summary>
public string payer_client_ip { get; set; }
/// <summary>
/// H5场景信息
/// </summary>
public object h5_info { get; set; }
}
/// <summary>
/// 通用H5场景信息类
/// 详情参见链接:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_1.shtml
/// 请求参数中 的 场景信息(scene_info)参数
/// </summary>
private class h5_info
{
/// <summary>
/// 场景类型
/// 示例值:iOS, Android, Wap
/// </summary>
public string type { get; set; }
}
#endregion
#region 支付通知结果
/// <summary>
/// 统一支付通知结果类
/// 详情参考:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_5.shtml 通知参数
/// </summary>
public class PayResult
{
/// <summary>
/// 通知ID 通知的唯一ID 示例值:EV-2018022511223320873
/// </summary>
public string id { get; set; }
/ <summary>
/ 通知ID 通知的唯一ID 示例值:EV-2018022511223320873
/ </summary>
//public string prepay_id { get; set; }
/// <summary>
/// 通知创建时间
/// 通知创建的时间,遵循rfc3339标准格式,格式为yyyy-MM-DDTHH:mm:ss+TIMEZONE,yyyy-MM-DD表示年月日,T出现在字符串中,表示time元素的开头,HH:mm:ss.表示时分秒,TIMEZONE表示时区(+08:00表示东八区时间,领先UTC 8小时,即北京时间)。例如:2015-05-20T13:29:35+08:00表示北京时间2015年05月20日13点29分35秒。
/// 示例值:2015-05-20T13:29:35+08:00
/// </summary>
public string create_time { get; set; }
/// <summary>
/// 通知数据类型
/// </summary>
public string resource_type { get; set; }
/// <summary>
/// 通知类型
/// </summary>
public string event_type { get; set; }
/// <summary>
/// 回调摘要
/// </summary>
public string summary { get; set; }
/// <summary>
/// 返回成功的链接
/// </summary>
public Resource resource { get; set; }
}
/// <summary>
/// 统一通知数据类
/// 详情参考:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_5.shtml
/// 通知参数 中 通知数据(resource)成员
/// </summary>
public class Resource
{
/// <summary>
/// 原始类型
/// </summary>
public string original_type { get; set; }
/// <summary>
/// 加密算法类型
/// </summary>
public string algorithm { get; set; }
/// <summary>
/// 数据密文
/// </summary>
public string ciphertext { get; set; }
/// <summary>
/// 附加数据
/// </summary>
public string associated_data { get; set; }
/// <summary>
/// 随机串
/// </summary>
public string nonce { get; set; }
}
#endregion
#region 订单/支付通知结果报文解析结果
/// <summary>
/// 统一通知数据解析结果类
/// 详情参见:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_2.shtml 返回参数
/// </summary>
public class ResourceReslt
{
/*微信支付查询订单接口返回参数*/
/// <summary>
/// 商户订单号
/// </summary>
public string out_trade_no { get; set; }
/// <summary>
/// 微信支付订单号
/// </summary>
public string transaction_id { get; set; }
/// <summary>
/// 交易类型
/// </summary>
public string trade_type { get; set; }
/// <summary>
/// 交易状态
/// 交易状态,枚举值:
///SUCCESS:支付成功
///REFUND:转入退款
///NOTPAY:未支付
///CLOSED:已关闭
///REVOKED:已撤销(付款码支付)
///USERPAYING:用户支付中(付款码支付)
///PAYERROR:支付失败(其他原因,如银行返回失败)
/// </summary>
public string trade_state { get; set; }
/// <summary>
/// 交易状态描述
/// </summary>
public string trade_state_desc { get; set; }
/// <summary>
/// 支付完成时间
/// </summary>
public string success_time { get; set; }
/// <summary>
/// 支付者
/// </summary>
public payPerson payer { get; set; }
/// <summary>
/// 订单金额
/// </summary>
public ResourceReslt_amount amount { get; set; }
/*自定义扩展参数*/
/// <summary>
/// 返回结果标识(0失败,1成功)
/// </summary>
public int code { get; set; }
}
/// <summary>
/// 统一通知数据解析结果金额类
/// 详情参考:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_5.shtml
/// 支付成功通知参数 中 订单金额(amount) 成员
/// </summary>
public class ResourceReslt_amount
{
/// <summary>
/// 总金额 订单总金额,单位为分。
/// </summary>
public int total { get; set; }
/// <summary>
/// 用户支付金额 用户支付金额,单位为分。
/// </summary>
public int payer_total { get; set; }
/// <summary>
/// 货币类型
/// </summary>
public string currency { get; set; } = "CNY";
/// <summary>
/// 用户支付币种
/// </summary>
public string payer_currency { get; set; } = "CNY";
}
/// <summary>
/// 通用支付者类
/// 详情参见链接:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_1.shtml
/// 请求参数中 的 支付者(payer)参数
/// </summary>
public class payPerson
{
/// <summary>
/// 用户标识
/// </summary>
public string openid { get; set; }
}
#endregion
#region 退款
/// <summary>
/// 统一退款请求参数类
/// 详情参见链接:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_9.shtml 请求参数
/// </summary>
private class OutBodyModel
{
/// <summary>
/// 商户订单号 必填
/// </summary>
public string out_trade_no { get; set; }
/// <summary>
/// 商户退款单号 必填
/// </summary>
public string out_refund_no { get; set; }
/// <summary>
/// 退款原因 非必填
/// </summary>
public string reason { get; set; } = "退款";
/ <summary>
/ 退款结果回调url 非必填
/ </summary>
//public string notify_url { get; set; } = "";
/// <summary>
/// 金额信息 必填
/// </summary>
public object amount { get; set; }
}
/// <summary>
/// 统一退款请求参数 金额信息类
/// 详情参见链接:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_9.shtml
/// 请求参数中 的 金额信息(amount)参数
/// </summary>
private class Out_amount
{
/// <summary>
/// 退款金额 必填
/// 退款金额,单位为分,只能为整数,不能超过原订单支付金额。
/// </summary>
public int refund { get; set; }
/// <summary>
/// 原订单金额 必填
/// 原支付交易的订单总金额,单位为分,只能为整数。
/// </summary>
public int total { get; set; }
/// <summary>
/// 退款币种 必填
/// 符合ISO 4217标准的三位字母代码,目前只支持人民币:CNY。
/// </summary>
public string currency { get; set; } = "CNY";
}
/// <summary>
/// 统一退款返回参数类
/// 请求统一申请退款接口返回参数类
/// 详情参见链接:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_9.shtml 返回参数
/// </summary>
public class Out_result
{
/*微信支付申请退款接口返回参数*/
/// <summary>
/// 微信支付退款单号 必填
/// </summary>
public string refund_id { get; set; }
/// <summary>
/// 商户退款单号 必填
/// </summary>
public string out_refund_no { get; set; }
/// <summary>
/// 微信支付订单号 必填
/// </summary>
public string transaction_id { get; set; }
/// <summary>
/// 商户订单号 必填
/// </summary>
public string out_trade_no { get; set; }
/// <summary>
/// 退款渠道 必填
/// </summary>
public string channel { get; set; }
/// <summary>
/// 退款入账账户 必填
/// </summary>
public string user_received_account { get; set; }
/// <summary>
/// 退款成功时间 非必填
/// </summary>
public string success_time { get; set; }
/// <summary>
/// 退款创建时间 必填
/// </summary>
public string create_time { get; set; }
/// <summary>
/// 退款状态 必填
/// </summary>
public string status { get; set; }
/// <summary>
/// 资金账户 必填
/// </summary>
public string funds_account { get; set; }
/// <summary>
/// 金额信息 必填
/// </summary>
public Out_result_amount amount { get; set; }
/*自定义扩展参数*/
/// <summary>
/// 状态码:200成功,其他失败
/// </summary>
public int code { get; set; } = 400;
}
/// <summary>
/// 统一退款返回参数 金额信息类
/// 详情参见链接:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_9.shtml
/// 返回参数中 的 金额信息(amount)参数
/// </summary>
public class Out_result_amount
{
/// <summary>
/// 退款金额 必填
/// 退款标价金额,单位为分,可以做部分退款
/// </summary>
public int refund { get; set; }
/// <summary>
/// 订单金额 必填
/// 订单总金额,单位为分
/// </summary>
public int total { get; set; }
/// <summary>
/// 用户支付金额 必填
/// 现金支付金额,单位为分,只能为整数
/// </summary>
public int payer_refund { get; set; }
/// <summary>
/// 用户退款金额 必填
/// 退款给用户的金额,不包含所有优惠券金额
/// </summary>
public int payer_total { get; set; }
/// <summary>
/// 应结退款金额 必填
/// 去掉非充值代金券退款金额后的退款金额,单位为分,退款金额=申请退款金额-非充值代金券退款金额,退款金额<=申请退款金额
/// </summary>
public int settlement_refund { get; set; }
/// <summary>
/// 应结订单金额 必填
/// 应结订单金额=订单金额-免充值代金券金额,应结订单金额<=订单金额,单位为分
/// </summary>
public int settlement_total { get; set; }
/// <summary>
/// 优惠退款金额 必填
/// 优惠退款金额<=退款金额,退款金额-代金券或立减优惠退款金额为现金,说明详见代金券或立减优惠,单位为分
/// </summary>
public int discount_refund { get; set; }
/// <summary>
/// 手续费退款金额 非必填
/// 手续费退款金额,单位为分。
/// </summary>
public int refund_fee { get; set; }
/// <summary>
/// 退款币种 必填
/// 符合ISO 4217标准的三位字母代码,目前只支持人民币:CNY。
/// </summary>
public string currency { get; set; } = "CNY";
}
#endregion
#endregion
#region 公共方法 (已测试上线)(适用于JSAPI、APP、H5、Native、小程序)
/// <summary>
/// 统一查询订单接口
/// 微信支付平台提供的JSAPI统一查询订单接口地址:https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/{out_trade_no}
/// 详情参见:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_2.shtml
/// </summary>
/// <param name="out_trade_no">商户平台订单编号 必填</param>
/// 其他参数更具业务需要自行添加
/// <returns></returns>
public async Task<string> SearchOrder(string out_trade_no)
{
LogHelper.WriteWithTime("JSAPISearchOrder进入");
LogHelper.WriteWithTime("mchid:" + mch_id + "out_trade_no" + out_trade_no);
//签名后发起请求
HttpClient client = new HttpClient(new HttpHandler(mch_id, certificateKey));
//请求头部添加Accept参数,值为application/json,固定参数,无需修改
client.DefaultRequestHeaders.Add("Accept", "application/json");
//请求头部添加User-Agent参数,值为Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36,固定参数,无需修改
client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36");
//发起GET请求并等待结果返回,传入参数为商户平台订单编号和商户号
var response = await client.GetAsync($@"https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/{out_trade_no}" + "?mchid=" + mch_id);
LogHelper.WriteWithTime("response : " + JsonConvert.SerializeObject(response));
//等待返回结果读取
var respStr = await response.Content.ReadAsStringAsync();
LogHelper.WriteWithTime("respStr : " + JsonConvert.SerializeObject(respStr));
//创建通知数据解析结果类实例
ResourceReslt jo = new ResourceReslt();
if (response.IsSuccessStatusCode)
{
//如果IsSuccessStatusCode为成功,则返回订单查询结果
jo = JsonConvert.DeserializeObject<ResourceReslt>(respStr);
jo.code = 1;
}
else
{
//如果IsSuccessStatusCode为失败,则返回失败状态码
jo.code = 0;
}
return JsonConvert.SerializeObject(jo);
}
/// <summary>
/// 统一退款接口
/// 详情参见:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_9.shtml
/// </summary>
/// <param name="out_trade_no">商户订单号</param>
/// <param name="out_refund_no">商户退款单号(商户系统内部的退款单号,商户系统内部唯一,只能是数字、大小写字母_-|*@ ,同一退款单号多次请求只退一笔。)</param>
/// <param name="refund">退款金额(元)(退款金额,单位为分,只能为整数,不能超过原订单支付金额。)</param>
/// <param name="total">订单金额(元)(原支付交易的订单总金额,单位为分,只能为整数。)</param>
/// 其他参数更具业务需要自行添加
/// <returns></returns>
public async Task<string> Refunds(string out_trade_no, string out_refund_no, double refund, double total)
{
//创建jsapi返回结果类的实例 returnParameters
Out_result returnParameters = new Out_result();
//构建即将发起的POST请求的body参数
var formData = new OutBodyModel
{
out_trade_no = out_trade_no,//商户订单号
out_refund_no = out_refund_no,//商户退款单号
amount = new Out_amount
{
total = Convert.ToInt32(total * 100),//订单金额
refund = Convert.ToInt32(refund * 100),//退款金额
}
};
LogHelper.WriteWithTime("OutBodyModel : " + JsonConvert.SerializeObject(formData));
//使用示例:HttpClient client = new HttpClient(new HttpHandler("{商户号}", "{商户证书序列号}"));
//HttpClient client = new HttpClient(new HttpHandler("1500536401", "340A5D32A5040892B41217F0730EDC5137A76405"));
HttpClient client = new HttpClient(new HttpHandler(mch_id, certificateKey));
//请求头部添加Accept参数,值为application/json,固定参数,无需修改
client.DefaultRequestHeaders.Add("Accept", "application/json");
//请求头部添加User-Agent参数,值为Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36,固定参数,无需修改
client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36");
var bodyJson = new StringContent(JsonConvert.SerializeObject(formData), Encoding.UTF8, "application/json");
//发起请求并等待请求结果返回
var response = await client.PostAsync(OutUrl, bodyJson);
LogHelper.WriteWithTime("response : " + JsonConvert.SerializeObject(response));
//等待返回结果
var respStr = await response.Content.ReadAsStringAsync();
LogHelper.WriteWithTime("respStr : " + JsonConvert.SerializeObject(respStr));
//JObject jo = (JObject)JsonConvert.DeserializeObject(respStr);
if (response.IsSuccessStatusCode)
{
//如果请求成功将返回的JSON字符串转换为对象
returnParameters = JsonConvert.DeserializeObject<Out_result>(respStr);
returnParameters.code = 200;
}//失败则返回初始化的returnParameters
//返回returnParameters
return JsonConvert.SerializeObject(returnParameters);
}
/// <summary>
/// 统一查询单笔退款接口
/// 微信支付平台提供的JSAPI统一查询订单接口地址:https://api.mch.weixin.qq.com/v3/refund/domestic/refunds/{out_refund_no}
/// 详情参见:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_10.shtml
/// </summary>
/// <param name="out_refund_no">商户平台退款单号 必填</param>
/// 其他参数更具业务需要自行添加
/// <returns></returns>
public async Task<string> SearchRefunds(string out_refund_no)
{
LogHelper.WriteWithTime("JSAPISearchOrder进入");
LogHelper.WriteWithTime("mchid:" + mch_id + "out_trade_no" + out_refund_no);
//签名后发起请求
HttpClient client = new HttpClient(new HttpHandler(mch_id, certificateKey));
//请求头部添加Accept参数,值为application/json,固定参数,无需修改
client.DefaultRequestHeaders.Add("Accept", "application/json");
//请求头部添加User-Agent参数,值为Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36,固定参数,无需修改
client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36");
//发起GET请求并等待结果返回,传入参数为商户平台订单编号和商户号
var response = await client.GetAsync($@"https://api.mch.weixin.qq.com/v3/refund/domestic/refunds/{out_refund_no}");
LogHelper.WriteWithTime("response : " + JsonConvert.SerializeObject(response));
//等待返回结果读取
var respStr = await response.Content.ReadAsStringAsync();
LogHelper.WriteWithTime("respStr : " + JsonConvert.SerializeObject(respStr));
//创建通知数据解析结果类实例
Out_result jo = new Out_result();
if (response.IsSuccessStatusCode)
{
//如果IsSuccessStatusCode为成功,则返回订单查询结果
jo = JsonConvert.DeserializeObject<Out_result>(respStr);
jo.code = 1;
}
else
{
//如果IsSuccessStatusCode为失败,则返回失败状态码
jo.code = 0;
}
return JsonConvert.SerializeObject(jo);
}
#endregion
#region JSAPI支付(已完成测试上线)
#region JSAPI参数
/// <summary>
/// 统一下单接口(JSAPI)
/// 微信支付平台提供的JSAPI统一下单接口地址
/// </summary>
private readonly string JSAPIurl = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";
#endregion
#region JSAPI参数类
/// <summary>
/// 调起支付参数类
/// 详情参见链接:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_4.shtml 接口定义
/// </summary>
private class JSAPICallUpPaymentParams
{
/*微信调起支付要求参数*/
/// <summary>
/// 公众号AppId
/// 商户申请的公众号对应的appid,由微信支付生成,可在公众号后台查看
/// </summary>
public string appid { get; set; }
/// <summary>
/// 时间戳
/// 时间戳,标准北京时间,时区为东八区,自1970年1月1日 0点0分0秒以来的秒数。注意:部分系统取到的值为毫秒级,需要转换成秒(10位数字)。
/// </summary>
public string timeStamp { get; set; }
/// <summary>
/// 随机字符串
/// 随机字符串,不长于32位。
/// </summary>
public string nonceStr { get; set; }
/// <summary>
/// 订单详情扩展字符串
/// JSAPI下单接口返回的prepay_id参数值,提交格式如:prepay_id=***
/// </summary>
public string package { get; set; }
/// <summary>
/// 签名方式
/// 签名类型,默认为RSA,仅支持RSA。
/// 示例值:RSA
/// </summary>
public string signType { get; set; }
/// <summary>
/// 签名
/// 使用字段appId、timeStamp、nonceStr、package计算得出的签名值
/// 签名生成调用Sign()方法
/// </summary>
public string paySign { get; set; }
/*自定义扩展参数*/
/// <summary>
/// 商户平台订单号
/// </summary>
public string orderCode { get; set; }
/// <summary>
/// 返回结果标识(0失败,1成功)
/// </summary>
public int code { get; set; }
}
/// <summary>
/// jsapi返回结果类
/// </summary>
private class JSAPIreturnParameters : ReturnParametersBase
{
/// <summary>
/// 预支付交易会话标识
/// 用于后续接口调用中使用,该值有效期为2小时
/// </summary>
public string prepay_id { get; set; }
}
#region 下单
/// <summary>
/// JSAPI下单请求参数类
/// 详情参见链接:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_1.shtml 请求参数
/// </summary>
private class JSAPIbodyModel : PayOrderbodyModelBase
{
/// <summary>
/// 支付者信息
/// </summary>
public object payer { get; set; }
}
#endregion
#endregion
#region JSAPI方法
/// <summary>
/// JSAPI调用的统一支付接口
/// 用户返回前端需要处理的数据内容
/// 详情参见:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_4.shtml
/// </summary>
/// <param name="description">商品描述 必填</param>
/// <param name="total">商品金额,以“元”为单位 必填</param>
/// <param name="openid">微信公众号用户openid 必填</param>
/// <param name="orderCode">商户平台订单编号 必填</param>
/// 其他参数更具业务需要自行添加
/// <returns></returns>
public async Task<object> JsApiPay(string description, decimal total, string openid, string orderCode)
{
LogHelper.WriteWithTime("JsApiPay 进入调起支付接口");
//获取prepay_id
string resultStr = await JSAPIGetprepay(description, total, orderCode, openid);
JSAPIreturnParameters result = JsonConvert.DeserializeObject<JSAPIreturnParameters>(resultStr);
LogHelper.WriteWithTime("resultStr" + resultStr);
//订单详情扩展字符串
string prepay_id = "";
//时间戳
string timeStamp = "";
//随机字符串 不长于32位
string nonceStr = "";
//微信签名
string paySign = "";
//接口返回结果标识(0失败,1成功)
int code = 0;
if (result.result)
{//如果获取prepay_id成功 则执行以下代码
prepay_id = "prepay_id=" + result.prepay_id;
//获取时间戳
timeStamp = GetTimestamp10(DateTime.Now);
//生成随机字符串
nonceStr = DateTime.Now.ToString("yyyyMMddHHmmssfff");
//生成签名字符串
paySign = Sign(appid + "\n" + timeStamp + "\n" + nonceStr + "\n" + prepay_id + "\n", privateKey);
//接口返回结果标识设置为成功
code = 1;
}
//返回的结果对象,结果对象内容按照微信支付提供的JSAPI调起支付参数进定义
JSAPICallUpPaymentParams returnObj = new JSAPICallUpPaymentParams()
{
appid = appid,
timeStamp = timeStamp,
nonceStr = nonceStr,
package = prepay_id,
signType = "RSA",
paySign = paySign,
orderCode = orderCode,
code = code
};
return returnObj;
}
/// <summary>
/// JSAPI下单接口
/// 成功返回prepay_id,失败返回https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi接口错误信息
/// 详情参见:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_1.shtml
/// </summary>
/// <param name="description">商品描述 必填</param>
/// <param name="total">金额,以“元”为单位 必填</param>
/// <param name="out_trade_no">商户平台订单号 必填</param>
/// <param name="openId">微信公众号openid 必填</param>
/// 其他参数更具业务需要自行添加
/// <returns></returns>
private async Task<string> JSAPIGetprepay(string description, decimal total, string out_trade_no, string openId)
{
//创建jsapi返回结果类的实例 returnParameters
JSAPIreturnParameters returnParameters = new JSAPIreturnParameters();
//构建即将发起的POST请求的body参数
var formData = new JSAPIbodyModel
{
appid = appid,//应用ID
mchid = mch_id,//商户号
description = description,//商品描述
out_trade_no = out_trade_no,//商户订单号
//attach = attach,//附加数据
notify_url = notifyurl,//通知地址
amount = new amount
{
total = Convert.ToInt32(total * 100),//金额
currency = "CNY"
},
payer = new payPerson()
{
openid = openId
}
};
//使用示例:HttpClient client = new HttpClient(new HttpHandler("{商户号}", "{商户证书序列号}"));
//HttpClient client = new HttpClient(new HttpHandler("1500536401", "340A5D32A5040892B41217F0730EDC5137A76405"));
HttpClient client = new HttpClient(new HttpHandler(mch_id, certificateKey));
//请求头部添加Accept参数,值为application/json,固定参数,无需修改
client.DefaultRequestHeaders.Add("Accept", "application/json");
//请求头部添加User-Agent参数,值为Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36,固定参数,无需修改
client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36");
var bodyJson = new StringContent(JsonConvert.SerializeObject(formData), Encoding.UTF8, "application/json");
//发起请求并等待请求结果返回
var response = await client.PostAsync(JSAPIurl, bodyJson);
LogHelper.WriteWithTime("response : " + JsonConvert.SerializeObject(response));
//等待返回结果
var respStr = await response.Content.ReadAsStringAsync();
LogHelper.WriteWithTime("respStr : " + JsonConvert.SerializeObject(respStr));
//将返回的JSON字符串转换为对象
JObject jo = (JObject)JsonConvert.DeserializeObject(respStr);
if (response.IsSuccessStatusCode)
{
//如果IsSuccessStatusCode为成功,获取请求结果中的prepay_id赋值到returnParameters.prepay_id
returnParameters.prepay_id = jo["prepay_id"].ToString();
returnParameters.result = true;
}
else
{
//如果IsSuccessStatusCode为失败,获取请求结果中的message赋值到returnParameters.errmsg
returnParameters.errmsg = jo["message"].ToString();
returnParameters.result = false;
}
//返回returnParameters
return JsonConvert.SerializeObject(returnParameters);
}
#endregion
#endregion
#region APP支付(完成未测试)
#region APP参数
/// <summary>
/// 统一下单接口(APP)
/// 微信支付平台提供的APP统一下单接口地址
/// </summary>
private readonly string APPUrl = "https://api.mch.weixin.qq.com/v3/pay/transactions/app";
#endregion
#region APP参数类
/// <summary>
/// 调起支付参数类
/// 详情参见链接:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_4.shtml 接口定义
/// </summary>
private class APPCallUpPaymentParams
{
/*微信调起支付要求参数*/
/// <summary>
/// 公众号AppId
/// 商户申请的公众号对应的appid,由微信支付生成,可在公众号后台查看
/// </summary>
public string appid { get; set; }
/// <summary>
/// 商户号
/// 请填写商户号mchid对应的值。
/// </summary>
public string partnerid { get; set; }
/// <summary>
/// 预支付交易会话ID
/// 微信返回的支付交易会话ID,该值有效期为2小时。
/// </summary>
public string prepayid { get; set; }
/// <summary>
/// 订单详情扩展字符串
/// 暂填写固定值Sign=WXPay
/// </summary>
public string package { get; set; }
/// <summary>
/// 随机字符串
/// 随机字符串,不长于32位。推荐随机数生成算法。
/// </summary>
public string nonceStr { get; set; }
/// <summary>
/// 时间戳
/// 时间戳,标准北京时间,时区为东八区,自1970年1月1日 0点0分0秒以来的秒数。注意:部分系统取到的值为毫秒级,需要转换成秒(10位数字)。
/// </summary>
public string timeStamp { get; set; }
/// <summary>
/// 签名
/// 使用字段appId、timeStamp、nonceStr、package计算得出的签名值
/// 签名生成调用Sign()方法
/// </summary>
public string sign { get; set; }
/*自定义扩展参数*/
/// <summary>
/// 商户平台订单号
/// </summary>
public string orderCode { get; set; }
/// <summary>
/// 返回结果标识(0失败,1成功)
/// </summary>
public int code { get; set; }
}
/// <summary>
/// APP返回结果类
/// </summary>
private class APPreturnParameters : ReturnParametersBase
{
/// <summary>
/// 预支付交易会话标识
/// 用于后续接口调用中使用,该值有效期为2小时
/// </summary>
public string prepay_id { get; set; }
}
#endregion
#region APP方法
/// <summary>
/// APP调用的统一支付接口
/// 用户返回前端需要处理的数据内容
/// 详情参见:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_2_4.shtml
/// </summary>
/// <param name="description">商品描述 必填</param>
/// <param name="total">商品金额,以“元”为单位 必填</param>
/// <param name="orderCode">商户平台订单编号 必填</param>
/// 其他参数更具业务需要自行添加
/// <returns></returns>
public async Task<object> APPPay(string description, decimal total, string orderCode)
{
LogHelper.WriteWithTime("APPPay 进入调起支付接口");
//获取prepay_id
string resultStr = await APPGetprepay(description, total, orderCode);
APPreturnParameters result = JsonConvert.DeserializeObject<APPreturnParameters>(resultStr);
LogHelper.WriteWithTime("resultStr" + resultStr);
//订单详情扩展字符串
string prepay_id = "";
//时间戳
string timeStamp = "";
//随机字符串 不长于32位
string nonceStr = "";
//微信签名
string paySign = "";
//接口返回结果标识(0失败,1成功)
int code = 0;
if (result.result)
{//如果获取prepay_id成功 则执行以下代码
prepay_id = result.prepay_id;
//获取时间戳
timeStamp = GetTimestamp10(DateTime.Now);
//生成随机字符串
nonceStr = DateTime.Now.ToString("yyyyMMddHHmmssfff");
//生成签名字符串
paySign = Sign(appid + "\n" + timeStamp + "\n" + nonceStr + "\n" + prepay_id + "\n", privateKey);
//接口返回结果标识设置为成功
code = 1;
}
//返回的结果对象,结果对象内容按照微信支付提供的JSAPI调起支付参数进定义
APPCallUpPaymentParams returnObj = new APPCallUpPaymentParams()
{
appid = appid,
partnerid = mch_id,
prepayid = prepay_id,
package = "Sign=WXPay",
nonceStr = nonceStr,
timeStamp = timeStamp,
sign = paySign,
orderCode = orderCode,
code = code
};
return returnObj;
}
/// <summary>
/// APP下单接口
/// 成功返回prepay_id,失败返回https://api.mch.weixin.qq.com/v3/pay/transactions/app接口错误信息
/// 详情参见:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_2_1.shtml
/// </summary>
/// <param name="description">商品描述 必填</param>
/// <param name="total">金额,以“元”为单位 必填</param>
/// <param name="out_trade_no">商户平台订单号 必填</param>
/// 其他参数更具业务需要自行添加
/// <returns></returns>
private async Task<string> APPGetprepay(string description, decimal total, string out_trade_no)
{
//创建APP返回结果类的实例 returnParameters
APPreturnParameters returnParameters = new APPreturnParameters();
//构建即将发起的POST请求的body参数
var formData = new PayOrderbodyModelBase
{
appid = appid,//应用ID
mchid = mch_id,//商户号
description = description,//商品描述
out_trade_no = out_trade_no,//商户订单号
//attach = attach,//附加数据
notify_url = notifyurl,//通知地址
amount = new amount
{
total = Convert.ToInt32(total * 100),//金额
currency = "CNY"
}
};
//使用示例:HttpClient client = new HttpClient(new HttpHandler("{商户号}", "{商户证书序列号}"));
//HttpClient client = new HttpClient(new HttpHandler("1500536401", "340A5D32A5040892B41217F0730EDC5137A76405"));
HttpClient client = new HttpClient(new HttpHandler(mch_id, certificateKey));
//请求头部添加Accept参数,值为application/json,固定参数,无需修改
client.DefaultRequestHeaders.Add("Accept", "application/json");
//请求头部添加User-Agent参数,值为Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36,固定参数,无需修改
client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36");
var bodyJson = new StringContent(JsonConvert.SerializeObject(formData), Encoding.UTF8, "application/json");
//发起请求并等待请求结果返回
var response = await client.PostAsync(APPUrl, bodyJson);
LogHelper.WriteWithTime("response : " + JsonConvert.SerializeObject(response));
//等待返回结果
var respStr = await response.Content.ReadAsStringAsync();
LogHelper.WriteWithTime("respStr : " + JsonConvert.SerializeObject(respStr));
//将返回的JSON字符串转换为对象
JObject jo = (JObject)JsonConvert.DeserializeObject(respStr);
if (response.IsSuccessStatusCode)
{
//如果IsSuccessStatusCode为成功,获取请求结果中的prepay_id赋值到returnParameters.prepay_id
returnParameters.prepay_id = jo["prepay_id"].ToString();
returnParameters.result = true;
}
else
{
//如果IsSuccessStatusCode为失败,获取请求结果中的message赋值到returnParameters.errmsg
returnParameters.errmsg = jo["message"].ToString();
returnParameters.result = false;
}
//返回returnParameters
return JsonConvert.SerializeObject(returnParameters);
}
#endregion
#endregion
#region H5支付 (完成未测试)
#region H5参数
/// <summary>
/// 统一下单接口(H5)
/// 微信支付平台提供的H5统一下单接口地址
/// </summary>
public static readonly string H5Url = "https://api.mch.weixin.qq.com/v3/pay/transactions/h5";
#endregion
#region H5参数类
/// <summary>
/// h5返回结果类
/// </summary>
private class H5returnParameters : ReturnParametersBase
{
/// <summary>
/// 支付跳转链接
/// h5_url为拉起微信支付收银台的中间页面,可通过访问该url来拉起微信客户端,完成支付,h5_url的有效期为5分钟。
/// </summary>
public string h5_url { get; set; }
}
#region 下单
/// <summary>
/// H5下单请求参数类
/// 详情参见链接:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_3_1.shtml 请求参数
/// </summary>
private class H5bodyModel : PayOrderbodyModelBase
{
/// <summary>
/// 场景信息
/// </summary>
public object scene_info { get; set; }
}
#endregion
#endregion
#region H5方法
/// <summary>
/// H5下单接口
/// 成功返回h5_url,失败返回https://api.mch.weixin.qq.com/v3/pay/transactions/h5接口错误信息
/// 详情参见:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_3_1.shtml
/// </summary>
/// <param name="description">商品描述 必填</param>
/// <param name="total">金额,以“元”为单位 必填</param>
/// <param name="out_trade_no">商户平台订单号 必填</param>
/// <param name="payer_client_ip">用户终端IP 用户的客户端IP,支持IPv4和IPv6两种格式的IP地址。 必填</param>
/// 其他参数更具业务需要自行添加
/// <returns></returns>
public async Task<string> H5Getprepay(string description, decimal total, string out_trade_no, string payer_client_ip)
{
//创建H5返回结果类的实例 returnParameters
H5returnParameters returnParameters = new H5returnParameters();
//构建即将发起的POST请求的body参数
var formData = new H5bodyModel
{
appid = appid,//应用ID
mchid = mch_id,//商户号
description = description,//商品描述
out_trade_no = out_trade_no,//商户订单号
notify_url = notifyurl,//通知地址
amount = new amount
{
total = Convert.ToInt32(total * 100),//金额
currency = "CNY"
},
scene_info = new scene_info()
{
payer_client_ip = payer_client_ip,
h5_info = new h5_info()
{
type = "Wap"
}
}
};
//使用示例:HttpClient client = new HttpClient(new HttpHandler("{商户号}", "{商户证书序列号}"));
//HttpClient client = new HttpClient(new HttpHandler("1500536401", "340A5D32A5040892B41217F0730EDC5137A76405"));
HttpClient client = new HttpClient(new HttpHandler(mch_id, certificateKey));
//请求头部添加Accept参数,值为application/json,固定参数,无需修改
client.DefaultRequestHeaders.Add("Accept", "application/json");
//请求头部添加User-Agent参数,值为Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36,固定参数,无需修改
client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36");
var bodyJson = new StringContent(JsonConvert.SerializeObject(formData), Encoding.UTF8, "application/json");
//发起请求并等待请求结果返回
var response = await client.PostAsync(H5Url, bodyJson);
LogHelper.WriteWithTime("response : " + JsonConvert.SerializeObject(response));
//等待返回结果
var respStr = await response.Content.ReadAsStringAsync();
LogHelper.WriteWithTime("respStr : " + JsonConvert.SerializeObject(respStr));
//将返回的JSON字符串转换为对象
JObject jo = (JObject)JsonConvert.DeserializeObject(respStr);
if (response.IsSuccessStatusCode)
{
//如果IsSuccessStatusCode为成功,获取请求结果中的h5_url赋值到returnParameters.h5_url
returnParameters.h5_url = jo["h5_url"].ToString();
returnParameters.result = true;
}
else
{
//如果IsSuccessStatusCode为失败,获取请求结果中的message赋值到returnParameters.errmsg
returnParameters.errmsg = jo["message"].ToString();
returnParameters.result = false;
}
//返回returnParameters
return JsonConvert.SerializeObject(returnParameters);
}
#endregion
#endregion
#region Native支付(完成未测试)
#region Native参数
/// <summary>
/// 统一下单接口(Native)
/// 微信支付平台提供的Native统一下单接口地址
/// </summary>
public static readonly string NativeUrl = "https://api.mch.weixin.qq.com/v3/pay/transactions/native";
#endregion
#region Native参数类
/// <summary>
/// Native返回结果类
/// </summary>
private class NativereturnParameters : ReturnParametersBase
{
/// <summary>
/// 二维码链接
/// 此URL用于生成支付二维码,然后提供给用户扫码支付。
/// </summary>
public string code_url { get; set; }
}
#region Native方法
/// <summary>
/// Native下单接口
/// 成功返回code_url,失败返回https://api.mch.weixin.qq.com/v3/pay/transactions/native接口错误信息
/// 详情参见:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_3_1.shtml
/// </summary>
/// <param name="description">商品描述 必填</param>
/// <param name="total">金额,以“元”为单位 必填</param>
/// <param name="out_trade_no">商户平台订单号 必填</param>
/// 其他参数更具业务需要自行添加
/// <returns></returns>
public async Task<string> NativeGetprepay(string description, decimal total, string out_trade_no)
{
//创建Nativere返回结果类的实例 returnParameters
NativereturnParameters returnParameters = new NativereturnParameters();
//构建即将发起的POST请求的body参数
var formData = new PayOrderbodyModelBase
{
appid = appid,//应用ID
mchid = mch_id,//商户号
description = description,//商品描述
out_trade_no = out_trade_no,//商户订单号
notify_url = notifyurl,//通知地址
amount = new amount
{
total = Convert.ToInt32(total * 100),//金额
currency = "CNY"
}
};
//使用示例:HttpClient client = new HttpClient(new HttpHandler("{商户号}", "{商户证书序列号}"));
//HttpClient client = new HttpClient(new HttpHandler("1500536401", "340A5D32A5040892B41217F0730EDC5137A76405"));
HttpClient client = new HttpClient(new HttpHandler(mch_id, certificateKey));
//请求头部添加Accept参数,值为application/json,固定参数,无需修改
client.DefaultRequestHeaders.Add("Accept", "application/json");
//请求头部添加User-Agent参数,值为Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36,固定参数,无需修改
client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36");
var bodyJson = new StringContent(JsonConvert.SerializeObject(formData), Encoding.UTF8, "application/json");
//发起请求并等待请求结果返回
var response = await client.PostAsync(NativeUrl, bodyJson);
LogHelper.WriteWithTime("response : " + JsonConvert.SerializeObject(response));
//等待返回结果
var respStr = await response.Content.ReadAsStringAsync();
LogHelper.WriteWithTime("respStr : " + JsonConvert.SerializeObject(respStr));
//将返回的JSON字符串转换为对象
JObject jo = (JObject)JsonConvert.DeserializeObject(respStr);
if (response.IsSuccessStatusCode)
{
//如果IsSuccessStatusCode为成功,获取请求结果中的code_url转换成二维码的base64编码赋值到returnParameters.code_url
try
{
var TheImage = QRCoderHelper.GetQRCode(jo["code_url"].ToString(), 4);
String base64 = String.Empty;
if (TheImage != null)
{
MemoryStream stream = new MemoryStream();
TheImage.Save(stream, System.Drawing.Imaging.ImageFormat.Bmp);
byte[] bytes = stream.ToArray();
stream.Close();
base64 = Convert.ToBase64String(bytes);
}
returnParameters.code_url = "data:image/png;base64," + base64;
returnParameters.result = true;
}
catch (Exception ex)
{
throw;
}
}
else
{
//如果IsSuccessStatusCode为失败,获取请求结果中的message赋值到returnParameters.errmsg
returnParameters.errmsg = jo["message"].ToString();
returnParameters.result = false;
}
//返回returnParameters
return JsonConvert.SerializeObject(returnParameters);
}
#endregion
#endregion
#endregion
#region 通用方法
/// <summary>
/// 获取时间戳10位(秒)
/// </summary>
/// <param name="dateTime"></param>
/// <returns></returns>
public string GetTimestamp10(DateTime dateTime)
{
return ((dateTime.ToUniversalTime().Ticks - 621355968000000000) / 10000000).ToString();
}
/// <summary>
/// 通用签名方法
/// </summary>
/// <param name="message">签名内容</param>
/// <param name="privateKey">商户私钥</param>
/// 调用示例:string paySign = Sign(appid + "\n" + timeStamp + "\n" + nonceStr + "\n" + prepay_id + "\n", privateKey);
/// <returns></returns>
public string Sign(string message, string privateKey)
{
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 解密类
/// <summary>
/// 解密类
/// 用于解密微信回调商户平台提供的回调接口时传输的加密报文
/// 详情参见:https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay4_2.shtml .NET示例代码
/// </summary>
public class AesGcm
{
private static string ALGORITHM = "AES/GCM/NoPadding";
private static int TAG_LENGTH_BIT = 128;
private static int NONCE_LENGTH_BYTE = 12;
private static string AES_KEY = wxPayApiKey;//APIv3密钥
/// <summary>
/// 报文解密方法
/// 参数内容参见:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_5.shtml 参数解密
/// </summary>
/// <param name="associatedData">附加数据</param>
/// <param name="nonce">随机串 加密使用的随机串</param>
/// <param name="ciphertext">数据密文 Base64编码后的开启/停用结果数据密文</param>
/// <returns></returns>
public static string AesGcmDecrypt(string associatedData, string nonce, string ciphertext)
{
GcmBlockCipher gcmBlockCipher = new GcmBlockCipher(new AesEngine());
AeadParameters aeadParameters = new AeadParameters(
new KeyParameter(Encoding.UTF8.GetBytes(AES_KEY)),
128,
Encoding.UTF8.GetBytes(nonce),
Encoding.UTF8.GetBytes(associatedData));
gcmBlockCipher.Init(false, aeadParameters);
byte[] data = Convert.FromBase64String(ciphertext);
byte[] plaintext = new byte[gcmBlockCipher.GetOutputSize(data.Length)];
int length = gcmBlockCipher.ProcessBytes(data, 0, data.Length, plaintext, 0);
gcmBlockCipher.DoFinal(plaintext, length);
return Encoding.UTF8.GetString(plaintext);
}
}
#endregion
}
/// <summary>
/// 微信支付 支付,退款接口参数
/// </summary>
public class WXApiParams
{
/// <summary>
/// 支付描述
/// </summary>
public string description { get; set; }
/// <summary>
/// 订单金额(元)
/// </summary>
public decimal total { get; set; }
/// <summary>
/// 平台订单号
/// </summary>
public string out_trade_no { get; set; }
/// <summary>
/// 微信公众平台OPENID
/// </summary>
public string openId { get; set; }
/// <summary>
/// 退款金额(元)
/// </summary>
public decimal refund { get; set; }
/// <summary>
/// 对应业务表主键
/// </summary>
public string F_BusinessId { get; set; }
/// <summary>
/// 创建人用户ID
/// </summary>
public string F_CreatorUserId { get; set; }
/// <summary>
/// 物业公司表主键ID
/// </summary>
public string F_PropertyId { get; set; }
/// <summary>
/// 订单类型 1为商超,2为餐饮,3物业费
/// </summary>
public int orderType { get; set; }
/// <summary>
/// 退款类型:1为商家退款,2为居民申请退款
/// </summary>
public int refundType { get; set; }
}
}
HTTP处理类
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace WaterCloud.Code.Web
{
/// <summary>
/// Http处理程序
/// 此类为官方提供内容,此处不做任何修改
/// 用于在请求微信接口时对请求构建签名信息,调用方式参照以下示例
/// 使用方法:
/// HttpClient client = new HttpClient(new HttpHandler("{商户号}", "{商户证书序列号}"));
/// ...
/// var response = client.GetAsync("https://api.mch.weixin.qq.com/v3/certificates");
/// 官方文档参考:https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay4_0.shtml .NET示例代码
/// </summary>
public class HttpHandler : DelegatingHandler
{
private readonly string merchantId;
private readonly string serialNo;
/// <summary>
/// HttpHandler构造函数
/// </summary>
/// <param name="merchantId">商户号</param>
/// <param name="merchantSerialNo">商户证书序列号</param>
public HttpHandler(string merchantId, string merchantSerialNo)
{
InnerHandler = new HttpClientHandler();
this.merchantId = merchantId;
this.serialNo = merchantSerialNo;
}
/// <summary>
/// 发送请求方法
/// </summary>
/// <param name="request"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
protected async override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
var auth = await BuildAuthAsync(request);
string value = $"WECHATPAY2-SHA256-RSA2048 {auth}";
request.Headers.Add("Authorization", value);
return await base.SendAsync(request, cancellationToken);
}
/// <summary>
/// 创建签名方法
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
protected async Task<string> BuildAuthAsync(HttpRequestMessage request)
{
string method = request.Method.ToString();
string body = "";
if (method == "POST" || method == "PUT" || method == "PATCH")
{
var content = request.Content;
body = await content.ReadAsStringAsync();
}
string uri = request.RequestUri.PathAndQuery;
var timestamp = DateTimeOffset.Now.ToUnixTimeSeconds();
string nonce = Path.GetRandomFileName();
string message = $"{method}\n{uri}\n{timestamp}\n{nonce}\n{body}\n";
string signature = Sign(message);
return $"mchid=\"{merchantId}\",nonce_str=\"{nonce}\",timestamp=\"{timestamp}\",serial_no=\"{serialNo}\",signature=\"{signature}\"";
}
/// <summary>
/// 请求签名方法
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
protected string Sign(string message)
{
//直接调用WXPayHelper中配置的商户私钥privateKey
string privateKey = WXPayHelper.privateKey;
byte[] keyData = Convert.FromBase64String(privateKey);
var rsa = RSA.Create();
//适用该方法的版本https://learn.microsoft.com/zh-cn/dotnet/api/system.security.cryptography.asymmetricalgorithm.importpkcs8privatekey?view=net-7.0
rsa.ImportPkcs8PrivateKey(keyData, out _);
byte[] data = System.Text.Encoding.UTF8.GetBytes(message);
return Convert.ToBase64String(rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1));
}
}
}
二维码生成类
using QRCoder;
using System.Drawing;
using System.Drawing.Imaging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WaterCloud.Code.Web
{
/// <summary>
/// 二维码生成类
/// </summary>
public static class QRCoderHelper
{
/// <summary>
/// 生成二维码
/// </summary>
/// <param name="url">存储内容</param>
/// <param name="pixel">像素大小</param>
/// <returns></returns>
public static Bitmap GetQRCode(string url, int pixel)
{
QRCodeGenerator generator = new QRCodeGenerator();
QRCodeData codeData = generator.CreateQrCode(url, QRCodeGenerator.ECCLevel.M, true);
QRCode qrcode = new QRCode(codeData);
Bitmap qrImage = qrcode.GetGraphic(pixel, Color.Black, Color.White, true);
return qrImage;
}
}
}
注:二维码生成类需要引用包QRCoder
使用方法
创建项目后复制代码创建类文件,根据提示安装缺失的包。微信支付类使用时根据业务场景修改接口参数。
微信支付回调示例
/// <summary>
/// 统一微信回调地址
/// 详情参见:
/// 支付结果通知:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_5.shtml
/// 用于微信平台调用商户平台的回调方法,在支付成功后微信会调用商户平台该方法,处理相关业务逻辑
/// </summary>
/// <returns></returns>
public async Task<string> WXCallback()
{
//创建回调方法返回对象
object reslut = new { code = "FAIL", message = "失败" };
LogHelper.WriteWithTime("WXCallback进入");
//创建支付结果通知类实例
WXPayHelper.PayResult payResult = new WXPayHelper.PayResult();
try
{
Stream stream = HttpContext.Request.Body;
byte[] buffer = new byte[HttpContext.Request.ContentLength.Value];
await stream.ReadAsync(buffer, 0, buffer.Length); //.net core 3.1需要用ReadAsync才不会出错
string str = System.Text.Encoding.UTF8.GetString(buffer);
JObject jsonObj = JObject.Parse(str);
//将参数内容json字符串结果转换为支付结果通知类对象
payResult = JsonConvert.DeserializeObject<WXPayHelper.PayResult>(str);
LogHelper.WriteWithTime("PayResult" + JsonConvert.SerializeObject(payResult));
if (payResult.event_type.Equals("TRANSACTION.SUCCESS"))
{
//如果支付通知结果中的通知类型event_type为"TRANSACTION.SUCCESS",处理成功后的业务逻辑
LogHelper.WriteWithTime("信息接收状态成功");
//调用Aes报文解密方法解密数据报文
var dataJson = WXPayHelper.AesGcm.AesGcmDecrypt(payResult.resource.associated_data, payResult.resource.nonce, payResult.resource.ciphertext);
LogHelper.WriteWithTime("dataJson" + dataJson);
//转换解密后的报文字符串为通知数据解析结果类对象
WXPayHelper.ResourceReslt data = JsonConvert.DeserializeObject<WXPayHelper.ResourceReslt>(dataJson);
if (data.trade_state.Equals("SUCCESS"))
{
/*
* 当解析结果内容中的交易状态trade_state为"SUCCESS"时
* 自己的业务逻辑 开始
*/
LogHelper.WriteWithTime("交易状态成功");
Response.StatusCode = 200;
reslut = new { code = "SUCCESS", message = "成功" };
/*
* 自己的业务逻辑 结束
*/
}
}
else
{
//打印日志
LogHelper.WriteWithTime("PayResult" + JsonConvert.SerializeObject(payResult));
Response.StatusCode = 500;
}
}
catch (Exception ex)
{
LogHelper.WriteWithTime("异常信息:" + ex.Message);
Response.StatusCode = 500;
return JsonConvert.SerializeObject(reslut);
}
return JsonConvert.SerializeObject(reslut);
}
下单调用示例
/// 生成订单号
/// </summary>
/// <returns></returns>
public string getRandomTime()
{
Random rd = new Random();//用于生成随机数
string DateStr = DateTime.Now.ToString("yyyyMMddHHmmssMM");//日期
string str = DateStr + rd.Next(10000).ToString().PadLeft(4, '0');//带日期的随机数
return str;
}
/// <summary>
/// 前端调用的统一支付接口
/// 返回前端需要处理的数据内容
/// 前端提取到数据后需要处理数据并调起支付,前端调起支付参考文件:项目更目录下 /Demo/微信调起支付前端代码.txt
/// </summary>
/// <param name="description">商品描述 必填</param>
/// <param name="total">商品金额,以“元”为单位 必填</param>
/// <param name="openid">微信公众号用户openid 必填</param>
/// 其他参数更具业务场景自行定义
/// <returns></returns>
public async Task<object> JsApiPay(string description, decimal total, string openid)
{
LogHelper.WriteWithTime("JsApiPay 进入调起支付接口");
/*
* 自定义生成未支付完成订单的业务逻辑 开始
*/
//生成订单编号
string orderCode = getRandomTime();
/*
* 自定义生成未支付完成订单的业务逻辑 结束
*/
//返回JSAPI调用的统一支付接口执行结果
return await new WXPayHelper().JsApiPay(description, total, openid, orderCode);
}
退款示例
/// <summary>
/// 前后端调用的统一申请退款接口
/// </summary>
/// <param name="orderCode">商户平台订单编号</param>
/// 其他参数更具业务场景自行定义
/// <returns></returns>
public async Task<object> PayRefunds(string orderCode)
{
//创建返回对象
object reslut = new { code = "FAIL", message = "失败" };
LogHelper.WriteWithTime("JsApiPayRefunds 进入");
/*
* 更具具体业务场景编写根据传入的商户平台订单编号orderCode查询订单信息业务逻辑
* 自己的业务逻辑 开始
*/
/*
* 自己的业务逻辑 结束
*/
/*
* 调用JSAPI申请退款接口接口
* 根据自己查询的订单信息按照接口参数要求传入参数即可
* 调用示例await new WXPayHelper().JSAPIRefunds("订单号","退款单号(手动生成,要求编号唯一)",退款金额(类型为double,单位为元),订单金额(类型为double,单位为元));
*/
string resultStr = await new WXPayHelper().Refunds(orderCode, "手动生成的退款单号", 0.01, 0.01);//使用时修改接口参数!!!!!!
//转换返回的JSON字符串为 JSAPI退款返回参数类 对象
WXPayHelper.Out_result data = JsonConvert.DeserializeObject<WXPayHelper.Out_result>(resultStr);
if (data != null)
{
//如果解析结果不为null,且退款状态status值为成功SUCCESS/退款处理中PROCESSING,
if (!string.IsNullOrEmpty(data.status) && (data.status.Equals("SUCCESS") || data.status.Equals("PROCESSING")))
{
/*
* 且退款状态status值为成功SUCCESS/退款处理中PROCESSING时
* 自己的业务逻辑 开始
*/
/*
* 自己的业务逻辑 结束
*/
reslut = new { code = "SUCCESS", message = "成功" };
}
else
{
/*
* 且退款状态status值为退款关闭CLOSED/退款异常ABNORMAL时
* 自己的业务逻辑 开始
*/
/*
* 自己的业务逻辑 结束
*/
}
}
return reslut;
}
订单查询
/// <summary>
/// 前端调用的统一查询订单接口
/// </summary>
/// <param name="orderCode">商户平台订单编号</param>
/// 其他参数更具业务场景自行定义
/// <returns></returns>
public async Task<object> PayDownSearch(string orderCode)
{
//创建返回对象
object reslut = new { code = "FAIL", message = "失败" };
LogHelper.WriteWithTime("JsApiPayDownSearch 进入");
//调用JSAPI查询订单接口
string resultStr = await new WXPayHelper().SearchOrder(orderCode);
//转换返回的JSON字符串为 通知数据解析结果类 对象
WXPayHelper.ResourceReslt data = JsonConvert.DeserializeObject<WXPayHelper.ResourceReslt>(resultStr);
if (data != null)
{
//如果解析结果不为null
if (!string.IsNullOrEmpty(data.trade_state) && data.trade_state.Equals("SUCCESS"))
{
/*
* 如果解析结果中的交易状态trade_state不为null或空字符串,且状态为"SUCCESS"时
* 自己的业务逻辑 开始
*/
/*
* 自己的业务逻辑 结束
*/
reslut = new { code = "SUCCESS", message = "成功" };
}
else
{
/*
* 如果解析结果中的交易状态trade_state状态不为成功时
* 自己的业务逻辑 开始
*/
/*
* 自己的业务逻辑 结束
*/
}
}
return reslut;
}
退款订单查询
/// <summary>
/// 前端调用的统一查询单笔退款接口
/// </summary>
/// <param name="out_refund_no">商户平台订单编号</param>
/// 其他参数更具业务场景自行定义
/// <returns></returns>
public async Task<object> PaySearchRefunds(string out_refund_no)
{
//创建返回对象
object reslut = new { code = "FAIL", message = "失败" };
LogHelper.WriteWithTime("JsApiPaySearchRefunds 进入");
//调用JSAPI查询单笔退款接口
string resultStr = await new WXPayHelper().SearchRefunds(out_refund_no);
//转换返回的JSON字符串为 JSAPI退款返回参数类 对象
WXPayHelper.Out_result data = JsonConvert.DeserializeObject<WXPayHelper.Out_result>(resultStr);
if (data != null)
{
//如果解析结果不为null
if (!string.IsNullOrEmpty(data.status) && data.status.Equals("SUCCESS"))
{
/*
* 如果解析结果中的退款状态status不为null或空字符串,且状态为"SUCCESS"时
* 自己的业务逻辑 开始
*/
/*
* 自己的业务逻辑 结束
*/
reslut = new { code = "SUCCESS", message = "成功" };
}
else
{
/*
* 如果解析结果中的交易状态status状态不为成功时
* 自己的业务逻辑 开始
*/
/*
* 自己的业务逻辑 结束
*/
}
}
return reslut;
}