原文:微信支付[v3]

V2升级V3 顺便记录一下 ,文档: http://pay.weixin.qq.com/wiki/doc/api/index.html

!!!

  • 支付授权目录与测试人的微信帐号白名单(出现access_denied或access_not_allow错误,请检查是否设置正确)
  • 微信签名(用于jssdk调用支付,查询订单,异步参数签名等)
  • 数字证书,用于微信退款

参数签名

对所有传入参数按照字段名的ASCII 码从小到大排序(字典序)后,使用URL 键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1,注意:值为空的参数不参与签名 在string1 最后拼接上key=Key( 商户支付密钥) 得到stringSignTemp 字符串, 并对stringSignTemp 进行md5 运算,再将得到的字符串所有字符转换为大写,得到sign 值signValue

        /// <summary>
/// 创建微信Sign
/// </summary>
/// <param name="key">微信商户支付密钥</param>
/// <param name="dict">参数字典</param>
/// <returns></returns>
public string GenerateWxSign(string key, SortedDictionary<string, string> dict)
{
try
{
string str = string.Empty;
foreach (var item in dict)
{
if (item.Key != "sign" && item.Value.ToString() != "")
{
str += item.Key + "=" + item.Value + "&";
}
}
str = str.Trim('&');
//在string后加入API KEY
str += "&key=" + key;
//MD5加密
var md5 = System.Security.Cryptography.MD5.Create();
var bs = md5.ComputeHash(Encoding.UTF8.GetBytes(str));
var sb = new StringBuilder();
foreach (byte b in bs)
{
sb.Append(b.ToString("x2"));
}
//所有字符转为大写
return sb.ToString().ToUpper();
}
catch (Exception)
{
return string.Empty;
}
}

异步通知

基本按照文档来,返回的参数要使用流的方式来读取XML,另外验证参数的合法性可以验证签名或调用一次微信订单查询接口

    /// <summary>
/// 微信支付异步回调控制器【V3版本】
/// </summary>
public class WeixinController : Controller
{
private static readonly Logger logger = LogManager.GetCurrentClassLogger(); /// <summary>
/// 订单服务
/// </summary>
private readonly IOrderService _orderService; /// <summary>
/// 微信服务
/// </summary>
private readonly IWeixinService _weixinService; /// <summary>
/// 构造函数
/// </summary>
/// <param name="orderService">订单服务</param>
/// <param name="weixinService">微信服务</param>
public WeixinController(IOrderService orderService, IWeixinService weixinService)
{
_orderService = orderService;
_weixinService = weixinService;
} /// <summary>
/// 微信Wap支付异步通知
/// </summary>
/// <returns></returns>
public async Task<ActionResult> Notify()
{
int intLen = Convert.ToInt32(Request.InputStream.Length);
if (intLen <= 0)
{
return Content(AjaxResult.Error("WeixinController NO Wap Notify PARAMS!").ToStringSafe());
}
try
{
var dict = new SortedDictionary<string, string>();
//dict = @"appid=wxqe353dd942e01&attach=F&bank_type=CFT&cash_fee=1&fee_type=CNY&is_subscribe=Y&mch_id=10036766&nonce_str=e9605038cccbffe5ed2b2eb095345d18&openid=oIO3gjsiGqv7eeuAKhbB_V83V528&out_trade_no=68047857&result_code=SUCCESS&return_code=SUCCESS&sign=85A469F874619EAB924FA5B7EB779444&time_end=20150526183632&total_fee=1&trade_type=JSAPI&transaction_id=1001640542201505260168707842".ConvertStringToSortedDictionary();
var xmlDoc = new XmlDocument();
xmlDoc.Load(Request.InputStream);
var root = xmlDoc.SelectSingleNode("xml");
XmlNodeList xnl = root.ChildNodes;
foreach (XmlNode xnf in xnl)
{
dict.Add(xnf.Name, xnf.InnerText);
}
if (dict.Count <= 0)
{
return Content(AjaxResult.Error("NO Notify PARAMS!").ToStringSafe());
}
logger.Info("【 WeixinController Wap Notify SDKUtil.ConvertDictionaryToString : 请求报文=[" + dict.ConvertDictionaryToString() + "]\n");
//验证参数签名
string signK = _weixinService.GenerateWxSign(WxPayConfig.KEY, dict);
if (signK != dict["sign"])
{
logger.Info("WeixinController Wap Notify Verify WxSign Error : 请求报文=[signK " + signK + " : dict['sign'] " + dict["sign"] + "]\n");
return Content(AjaxResult.Error("WeixinController Wap Notify Verify WxSign Error " + "]\n").ToStringSafe());
}
//验证通信标识
string return_code = dict["return_code"];
if (!return_code.Equals("SUCCESS", StringComparison.OrdinalIgnoreCase))
{
string return_msg = dict["return_msg"];
logger.Info("WeixinController Wap Notify return_code Error : 请求报文=[" + return_code + " : " + return_msg + "]\n");
return Content(AjaxResult.Error("WeixinController Wap Notify return_code Error " + return_code + "]\n").ToStringSafe());
}
//验证交易标识
string result_code = dict["result_code"];
if (!result_code.Equals("SUCCESS", StringComparison.OrdinalIgnoreCase))
{
string err_code = dict["err_code"];
string err_code_des = dict["err_code_des"];
logger.Info("WeixinController Wap Notify return_code Error : 请求报文=[" + err_code + " : " + err_code_des + "]\n");
return Content(AjaxResult.Error("WeixinController Wap Notify return_code Error " + result_code + "]\n").ToStringSafe());
}
//公众账号ID
string appid = dict["appid"];
//商户号
string mch_id = dict["mch_id"];
//随机字符串
string nonce_str = dict["nonce_str"];
//签名
string sign = dict["sign"];
//用户在商户appid 下的唯一标识
string openid = dict["openid"];
//用户是否关注公众账
string is_subscribe = dict["is_subscribe"];
//交易类型
string trade_type = dict["trade_type"];
//付款银行
string bank_type = dict["bank_type"];
//订单总金额,单位为分
string total_fee = dict["total_fee"];
//金额分转元
string total_fee_rmb = (float.Parse(total_fee) / 100).ToString();
//商户订单号
string out_trade_no = dict["out_trade_no"];
//微信支付订单号
string transaction_id = dict["transaction_id"];
//商家数据包
string attach = dict["attach"];
//支付完成时间
string time_end = dict["time_end"];
//支付方式
string paymethod = attach;
//微信订单查询【使用签名验证废弃】
//var wxorder = await _weixinService.Query(WxPayConfig.KEY, WxPayConfig.APPID, WxPayConfig.MCHID, transaction_id, out_trade_no);
//检查订单是否已经支付
var order = await _orderService.Query(out_trade_no);
if (order.IsNull() || order.Total_fee.IsNotNullOrEmpty()){
return Content(AjaxResult.Error("ORDERNO STATUS ERROR !!!").ToStringSafe());
}
//更新支付金额与订单状态
var orderFlag = await _orderService.Update(crsResv);
if (orderFlag.IsError)
{
logger.Fatal("WeixinController Wap Notify Update Order Error crsResv : " + crsResv.SerializeJson(System.Text.Encoding.UTF8));
return Json(new { IsError = true, Msg = "更新订单数据错误", Data = string.Empty }, JsonRequestBehavior.AllowGet);
}
//微信支付发货通知
var wxNotify = await _weixinService.WeixinNotify(openid, transaction_id, out_trade_no);
//记录DB
var save = await _weixinService.Save(wxAsync);
//构造返回成功XML
var wxdict = new SortedDictionary<string, string>();
wxdict.Add("return_code", "SUCCESS");
wxdict.Add("return_msg", "PAY_SUCCESS");
string wxRXml = wxdict.ConvertWxDictToString();
logger.Info("WeixinController Wap Notify Success wxRXml : " + wxRXml + " 】");
return Content(wxRXml);
}
catch (Exception ex)
{
logger.Fatal("WeixinController Wap Notify Exception : " + ex.Message + " 】", ex);
return Content(AjaxResult.Error("WeixinController Wap Notify Exception").ToStringSafe());
}
} /// <summary>
/// 字典转微信返回XML
/// </summary>
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="TValue"></typeparam>
/// <param name="wxdict"></param>
/// <returns></returns>
public static string ConvertWxDictToString<TKey, TValue>(this IDictionary<TKey, TValue> wxdict)
{
try
{
string xml = "<xml>";
wxdict.ForEach(wd =>
{
if (wd.Value.GetType() == typeof(int))
{
xml += "<" + wd.Key + ">" + wd.Value + "</" + wd.Key + ">";
}
else if (wd.Value.GetType() == typeof(string))
{
xml += "<" + wd.Key + ">" + "<![CDATA[" + wd.Value + "]]></" + wd.Key + ">";
}
});
xml += "</xml>";
return xml;
}
catch (Exception)
{
return string.Empty;
}
}

订单查询

        /// <summary>
/// 微信订单查询
/// </summary>
/// <param name="appid">公众账号ID</param>
/// <param name="mch_id">商户号</param>
/// <param name="transaction_id">微信的订单号</param>
/// <param name="out_trade_no">商户订单号</param>
/// <param name="nonce_str">随机字符串</param>
/// <param name="sign">签名</param>
/// <returns></returns>
public async Task<WebAPIResponse> Query(string appid, string mch_id, string transaction_id, string out_trade_no, string nonce_str, string sign)
{
if (appid.IsEmpty() || mch_id.IsEmpty() || transaction_id.IsEmpty())
{
return new WebAPIResponse { IsError = true, Msg = "参数有误!!!" };
}
try
{
var dict = new Dictionary<string, string>();
dict.Add("appid", appid);
dict.Add("mch_id", mch_id);
dict.Add("transaction_id", transaction_id);
dict.Add("out_trade_no", out_trade_no);
dict.Add("nonce_str", nonce_str);
dict.Add("sign", sign);
string xml = "<xml>";
foreach (var pair in dict)
{
if (pair.Value.GetType() == typeof(int))
{
xml += "<" + pair.Key + ">" + pair.Value + "</" + pair.Key + ">";
}
else if (pair.Value.GetType() == typeof(string))
{
xml += "<" + pair.Key + ">" + "<![CDATA[" + pair.Value + "]]></" + pair.Key + ">";
}
}
xml += "</xml>";
logger.Info("WeixinRepository WeixinNotify Req xml: " + xml);
var data = await @"https://api.mch.weixin.qq.com/pay/orderquery".PostStringAsync(xml).ReceiveString();
logger.Info("WeixinRepository WeixinNotify Resp xml: " + data);
if (data.IsNotNullOrEmpty() && data.Contains("SUCCESS", StringComparison.OrdinalIgnoreCase))
{
return new WebAPIResponse { IsError = false, Data = "查询订单成功!!!" };
}
return new WebAPIResponse { IsError = true, Msg = "微信订单查询失败!!!" };
}
catch (Exception ex)
{
logger.Error("WeixinRepository WeixinNotify ConvertDictionaryToJson Exception :" + ex.Message, ex);
return new WebAPIResponse { IsError = true, Msg = ex.Message };
}
}

微信退款

!!!需注意签名与证书

        /// <summary>
/// 微信申请退款V3
/// </summary>
/// <param name="orderNo">订单号</param>
/// <returns></returns>
public bool RefundWxV3(string orderNo)
{
try
{
//查询支付信息
var orderAsync =await _orderService.Query(orderNo);
var dict = new SortedDictionary<string, string>();
dict.Add("appid", WxPayConfig.APPID);
dict.Add("mch_id", WxPayConfig.MCHID);
dict.Add("nonce_str", ConstantKey.NonceStr);
dict.Add("transaction_id", orderAsync.transaction_id);
dict.Add("out_trade_no", orderAsync.out_trade_no);
dict.Add("out_refund_no", WxPayConfig.GenerateOutTradeNo);
dict.Add("total_fee", orderAsync.total_fee);
dict.Add("refund_fee", orderAsync.total_fee);
dict.Add("op_user_id", WxPayConfig.MCHID);
string sign = GenerateWxSign(WxPayConfig.KEY, dict);
dict.Add("sign", sign);
string xml = "<xml>";
foreach (var pair in dict)
{
if (pair.Value.GetType() == typeof(int))
{
xml += "<" + pair.Key + ">" + pair.Value + "</" + pair.Key + ">";
}
else if (pair.Value.GetType() == typeof(string))
{
xml += "<" + pair.Key + ">" + "<![CDATA[" + pair.Value + "]]></" + pair.Key + ">";
}
}
xml += "</xml>";
logger.Info("RefundWxV3 Req xml: " + xml);
var data = HttpWxCert(@"https://api.mch.weixin.qq.com/secapi/pay/refund", "POST", xml, "text/xml", isUseCert: true);
logger.Info("RefundWxV3 Resp xml: " + data);
if (data.IsNotEmpty() && data.Contains("SUCCESS", StringComparison.OrdinalIgnoreCase))
{
return true;
}
return false;
}
catch (Exception ex)
{
logger.Error("CancelResvForWeb RefundWxV3 Exception :" + ex.Message, ex);
return false;
}
}
/// <summary>
/// 创建HTTP请求
/// </summary>
/// <param name="url">请求URL</param>
/// <param name="method">请求方式</param>
/// <param name="data">请求参数</param>
/// <param name="contentType">设置HTTP contentType类型</param>
/// <param name="isUseCert">是否使用证书</param>
/// <param name="timeout">请求超时时间</param>
/// <returns></returns>
public static string HttpWxCert(string url, string method = "GET", string data = null, string contentType = "application/x-www-form-urlencoded", bool isUseCert = false, int timeout = 15000)
{
if (url.IsEmpty())
throw new ArgumentNullException("url");
HttpWebRequest req = null;
HttpWebResponse resp = null;
StreamReader sr = null;
Stream stream = null;
try
{
if (url.StartsWith("https", StringComparison.OrdinalIgnoreCase))
{
System.Net.ServicePointManager.ServerCertificateValidationCallback += (se, cert, chain, sslError) =>
{
return true;
};
}
//设置最大连接数
System.Net.ServicePointManager.DefaultConnectionLimit = 200;
req = (HttpWebRequest)WebRequest.Create(url);
req.Method = method.ToUpper();
req.Timeout = timeout;
req.ContentType = contentType;
//是否使用证书
if (isUseCert)
{
string pfxPath = WxPayConfig.SSLCERT_PATH;
logger.Info("wxV3.signCert.path pfxPath : " + pfxPath + " File Exists: " + File.Exists(pfxPath));
//string path = HttpContext.Current.Request.PhysicalApplicationPath;
//调用证书
var cert = new X509Certificate2(pfxPath, WxPayConfig.SSLCERT_PASSWORD, X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.MachineKeySet);
req.ClientCertificates.Add(cert);
} if (method.ToUpper() == "POST" && !data.IsEmpty())
{
byte[] postBytes = UTF8Encoding.UTF8.GetBytes(data);
req.ContentLength = postBytes.Length;
using (stream = req.GetRequestStream())
{
stream.Write(postBytes, 0, postBytes.Length);
}
}
resp = (HttpWebResponse)req.GetResponse();
using (sr = new StreamReader(resp.GetResponseStream(), Encoding.UTF8))
{
return sr.ReadToEnd().Trim();
}
}
catch (Exception ex)
{
logger.Error(string.Format("HttpWxCert Exception url: {0} \r\n data: {1}", url, data), ex);
return string.Empty;
}
finally
{
if (sr != null)
{
sr.Close();
sr = null;
}
if (stream != null)
{
stream.Close();
stream = null;
}
if (resp != null)
{
resp.Close();
resp = null;
}
if (req != null)
{
req.Abort();
req = null;
}
}
}

微信支付[v3]的更多相关文章

  1. 坑爹的微信支付v3,其实没有那么坑

    http://www.cnblogs.com/zskbll/p/wxpay.html 研究微信开发一年多了,每个新接口,都会第一时间进行研究.微信支付开放很久,一直没机会接触到支付接口,等了好久终于从 ...

  2. 到处都是坑的微信支付V3之 微信支付回调页面

    据上次 到处都是坑的微信支付V3 后很多园友在被虐了千百遍后终于跳转到了亲切的微信支付界面,但输入密码支付后却不知道怎么处理了,接下来补上支付后的处理流程. 1. html中根据前台支付后反馈信息成功 ...

  3. 到处都是坑的微信支付V3

    业务需要一个在微信上能付款的功能,于是乎想到了最普遍的支付宝,坑爹的是T与A是水火不容啊,默默的还是接微信支付吧,没想到从此掉进了连环坑…… 网上写微信支付接口的还是很多,PHP官方有(鄙视源码作者, ...

  4. 微信支付v3发布到iis时的证书问题(转)

    本文纯粹转载(原地址:微信支付v3发布到iis时的证书问题 ) 一开始报“出现了内部错误” 解决方法是 方法一 var cer = new X509Certificate(certpath, pass ...

  5. 微信支付v3开发(5) 扫码并输入金额支付

    关键字:微信支付 微信支付v3 动态native支付 统一支付 Native支付 prepay_id 作者:方倍工作室 本文介绍微信支付下的扫描二维码并输入自定义金额的支付的开发过程. 注意 微信支付 ...

  6. 为了Java微信支付V3开发包,我找出了微信支付文档至少六个错误

    1. 前言 最近忙的一批,难得今天有喘气的机会就赶紧把最近在开发中的一些成果分享出来.前几日分享了自己写的一个微信支付V3的开发包payment-spring-boot-starter,就忙里偷闲完善 ...

  7. 如何在Spring Boot项目中集成微信支付V3

    Payment Spring Boot 是微信支付V3的Java实现,仅仅依赖Spring内置的一些类库.配置简单方便,可以让开发者快速为Spring Boot应用接入微信支付. 演示例子: paym ...

  8. Payment Spring Boot 1.0.4.RELEASE 发布,最易用的微信支付 V3 实现

    Payment Spring Boot 是微信支付V3的Java实现,仅仅依赖Spring内置的一些类库.配置简单方便,可以让开发者快速为Spring Boot应用接入微信支付. 欢迎ISSUE,欢迎 ...

  9. 微信支付V3 SDK Payment Spring Boot 1.0.6 发布,实现留守红包,助力抗疫

    春节将至,为防控疫情,多地政府提倡员工.外来务工者留守本地过年,并鼓励企业向员工发放"留守红包".为此,微信支付推出"春节留守红包"能力,希望可以协助有发放需求 ...

随机推荐

  1. __get __set 实例

    <?php class Person { //下面是人的成员属性,都是封装的私有成员 private $name; //人的名子 private $sex; //人的性别 private $ag ...

  2. pycharm+QT4的helloworld

    # -*- coding: utf-8 -*- from PyQt4 import QtCore, QtGui try: _fromUtf8 = QtCore.QString.fromUtf8 exc ...

  3. 关于PsCreateSystemThread函数

    研究了1天这个...MSDN说的不是很清楚NTSTATUS PsCreateSystemThread(  _Out_      PHANDLE ThreadHandle,  _In_       UL ...

  4. Main方法的执行过程(转)

    要运行一个 main 方法 , 首先要知道 main 方法所在的 Class, 在命令行中指定这个 Class 名 Class Lava{ Private int speed = 4; Void fl ...

  5. CMAKE的使用

    CMAKE的使用 Version 1.0 2009-3-18 一.      基本使用 安装:下载二进制包后可直接解压使用 从源码安装则执行命令:./bootstrap; make; make ins ...

  6. 跨平台运行ASP.NET Core 1.0(转载)

    前言 首先提一下微软更名后的叫法: ASP.NET 5 更名为 ASP.NET Core 1.0 .NET Core 更名为 .NET Core 1.0 Entity Framework 7 更名为  ...

  7. Security:蠕虫的行为特征描述和工作原理分析

    ________________________ 参考: 百度文库---蠕虫的行为特征描述和工作原理分析 http://wenku.baidu.com/link?url=ygP1SaVE4t4-5fi ...

  8. iOS开发之使用Ad Hoc进行测试

    由于最近某个项目需要给别人测试,使用的是Ad Hoc方法 首先登录开发者官网配置证书 1.添加Certificates,从电脑获取certSigningRequest然后添加进去 2.在Identif ...

  9. 简要解析XMPP框架及iOS-Objective-C的使用

    前言:这两天看了XMPP框架,查阅了一些资料,写下这篇文章记录一下学习笔记 一.简要解析XMPP核心部分 XMPP框架分为两个部分 1.核心部分 2.扩展部分 扩展部分主要讲好友列表(roster). ...

  10. <脱机手写汉字识别若干关键技术研究>

    脱机手写汉字识别若干关键技术研究 对于大字符集识别问题,一般采用模板匹配的算法,主要是因为该算法比较简单,识别速度快.但直接的模板匹配算法往往无法满足实际应用中对识别精度的需求.为此任俊玲编著的< ...