您的位置:首页 > 移动开发 > 微信开发

.Net C# 微信刷卡支付接口

2015-10-16 16:43 633 查看

微信刷卡支付接口

应用场景实例

收银员使用扫码设备读取微信用户刷卡授权码以后,二维码或条码信息传送至商户收银台,由商户收银台或者商户后台调用该接口发起支付。

SDK下载

微信提供5种开发语言的SDK,选择自己的开发语言下载,项目中会有很多示例。

微信代码

简略版 数据需自行获取 

public class ToWeixinPayBLL
{
/// <summary>
/// 微信扫码支付
/// </summary>
/// <param name="osaleh_osalehID"></param>
/// <param name="txtPaymentCode"></param>
/// <param name="orderType"></param>
/// <param name="user"></param>
/// <returns></returns>
public static ResponseDTO GetWeixinPayRequestExecute(string osaleh_osalehID, string str_osaled_osaledID, string txtPaymentCode, string orderType, UsersEntity user)
{
ResponseDTO resp = new ResponseDTO();
try
{
decimal osaled_amount = 0;
IList<OsaledEntity> OsaledList = new List<OsaledEntity>();

#region 提交支付信息,并接收返回结果
WxPayData data = new WxPayData();
data.SetValue("auth_code", txtPaymentCode);//授权码
data.SetValue("body", Description);//商品描述
try
{
//微信支付金额单位:分;所以要对支付总金额进行单位换算。
Int32 totalAmount = (Int32)(Convert.ToDecimal(osaled_amount) * 100);
data.SetValue("total_fee", totalAmount);//总金额(单位:分)

}
catch (Exception ex)
{
ExceptionLog.LogExp(ex.Message);
}
string out_trade_no = GenerateOutTradeNo();
data.SetValue("out_trade_no", out_trade_no);//产生随机的商户订单号

WxPayData result = Micropay(data, 10); //提交被扫支付,接收返回结果
#endregion

#region 如果提交被扫支付接口调用失败,则抛异常提示
if (!result.IsSet("return_code") || result.GetValue("return_code").ToString() == "FAIL")
{
string returnMsg = result.IsSet("return_msg") ? result.GetValue("return_msg").ToString() : "";

resp.Message = "微信支付接口调用失败,原因:" + returnMsg;
resp.Status = false;
return resp;
}
#endregion

//签名验证
result.CheckSign();

//刷卡支付直接成功
if (result.GetValue("return_code").ToString() == "SUCCESS" && result.GetValue("result_code").ToString() == "SUCCESS")
{
#region 支付成功,业务处理

resp.Message = "支付成功";
resp.Status = true;
return resp;

#endregion
}

/******************************************************************
* 剩下的都是接口调用成功,业务失败的情况
* ****************************************************************/
//1)业务结果明确失败
if (result.GetValue("err_code").ToString() != "USERPAYING" && result.GetValue("err_code").ToString() != "SYSTEMERROR")
{
resp.Message = "支付失败,错误代码:" + result.GetValue("err_code").ToString() + "。描述:" + result.GetValue("err_code_des").ToString();
resp.Status = false;
return resp;
}

//2)不能确定是否失败,需查单
//用商户订单号去查单
#region 确认支付是否成功,每隔一段时间(5s)查询一次订单,共查询6次

int queryTimes = 6;//查询次数计数器
while (queryTimes-- > 0)
{
int succResult = 0;//查询结果
WxPayData queryResult = Query(out_trade_no, out succResult);

//如果需要继续查询,则等待5s后继续
if (succResult == 2)
{
Thread.Sleep(5000);
continue;
}
//查询成功,返回订单查询接口返回的数据
else if (succResult == 1)
{
#region  支付成功,业务处理

resp.Message = "支付成功";
resp.Status = true;
return resp;

#endregion 支付成功,处理交易流程
}
//订单交易失败,直接返回刷卡支付接口返回的结果,失败原因会在err_code中描述
else
{
resp.Message = "支付失败,错误代码:" + result.GetValue("err_code").ToString() + "。描述:" + result.GetValue("err_code_des").ToString();
try
{
//交易失败,则撤销订单
Cancel(out_trade_no);
}
catch (Exception ex)
{
resp.Message += "\n撤销订单异常:" + ex.Message;
ExceptionLog.ToWeixinPayLog(resp.Message);
}
resp.Status = false;
return resp;
}
}
#endregion

try
{
//交易超时,则撤销订单
Cancel(out_trade_no);
}
catch (Exception ex)
{
resp.Message = "支付超时,请重试。撤销订单异常:" + ex.Message + "。";
ExceptionLog.ToWeixinPayLog(resp.Message);
}
resp.Status = false;
}
else
{
resp.Message = "操作失败,未查询到订单信息,请联系管理员!";
resp.Status = false;
}
}
catch (Exception ex)
{
ExceptionLog.ToWeixinPayLog(ex.Message);
resp.Message = "支付失败," + ex.Message;
resp.Status = false;
}
return resp;
}

/**
* 提交被扫支付API
* 收银员使用扫码设备读取微信用户刷卡授权码以后,二维码或条码信息传送至商户收银台,
* 由商户收银台或者商户后台调用该接口发起支付。
* @param WxPayData inputObj 提交给被扫支付API的参数
* @param int timeOut 超时时间
* @throws WxPayException
* @return 成功时返回调用结果,其他抛异常
*/
public static WxPayData Micropay(WxPayData inputObj, int timeOut = 10)
{
string url = "https://api.mch.weixin.qq.com/pay/micropay";
//检测必填参数
if (!inputObj.IsSet("body"))
{
ExceptionLog.ToWeixinPayLog("提交被扫支付API接口中,缺少必填参数body!");
}
else if (!inputObj.IsSet("out_trade_no"))
{
ExceptionLog.ToWeixinPayLog("提交被扫支付API接口中,缺少必填参数out_trade_no!");
}
else if (!inputObj.IsSet("total_fee"))
{
ExceptionLog.ToWeixinPayLog("提交被扫支付API接口中,缺少必填参数total_fee!");
}
else if (!inputObj.IsSet("auth_code"))
{
ExceptionLog.ToWeixinPayLog("提交被扫支付API接口中,缺少必填参数auth_code!");
}

inputObj.SetValue("spbill_create_ip", ConfigHelper.WinxinPay_AdressIP);//终端ip
inputObj.SetValue("appid", ConfigHelper.WinxinPay_AppID);//公众账号ID
inputObj.SetValue("mch_id", ConfigHelper.WWinxinPay_MCHID);//商户号
inputObj.SetValue("nonce_str", Guid.NewGuid().ToString().Replace("-", ""));//随机字符串
inputObj.SetValue("sign", inputObj.MakeSign());//签名
string xml = inputObj.ToXml();

var start = DateTime.Now;//请求开始时间

string response = HttpHelper.Post(xml, url, false, timeOut);//调用HTTP通信接口以提交数据到API
var end = DateTime.Now;
int timeCost = (int)((end - start).TotalMilliseconds);//获得接口耗时

//将xml格式的结果转换为对象以返回
WxPayData result = new WxPayData();
result.FromXml(response);

return result;
}

/// <summary>
/// 查询订单情况
/// @param string out_trade_no  商户订单号
/// @param int succCode         查询订单结果:0表示订单不成功,1表示订单成功,2表示继续查询
/// @return 订单查询接口返回的数据,参见协议接口
/// </summary>
/// <param name="out_trade_no"></param>
/// <param name="succCode"></param>
/// <returns></returns>
public static WxPayData Query(string out_trade_no, out int succCode)
{
WxPayData queryOrderInput = new WxPayData();
queryOrderInput.SetValue("out_trade_no", out_trade_no);
WxPayData result = OrderQuery(queryOrderInput);

if (result.GetValue("return_code").ToString() == "SUCCESS" && result.GetValue("result_code").ToString() == "SUCCESS")
{
//支付成功
if (result.GetValue("trade_state").ToString() == "SUCCESS")
{
succCode = 1;
return result;
}
//用户支付中,需要继续查询
else if (result.GetValue("trade_state").ToString() == "USERPAYING")
{
succCode = 2;
return result;
}
}

if (result.GetValue("return_code").ToString() == "SUCCESS" && result.GetValue("result_code").ToString() == "FAIL")
{
//如果返回错误码为“此交易订单号不存在”则直接认定失败
if (result.GetValue("err_code").ToString() == "ORDERNOTEXIST")
{
succCode = 0;
}
else
{
//如果是系统错误,则后续继续
succCode = 2;
}

return result;
}

succCode = 3;
return result;
}

/// <summary>
/// 查询订单
/// @param WxPayData inputObj 提交给查询订单API的参数
/// @param int timeOut 超时时间
/// @throws WxPayException
/// @return 成功时返回订单查询结果,其他抛异常
/// </summary>
/// <param name="inputObj"></param>
/// <param name="timeOut"></param>
/// <returns></returns>
public static WxPayData OrderQuery(WxPayData inputObj, int timeOut = 6)
{
string url = "https://api.mch.weixin.qq.com/pay/orderquery";

//检测必填参数
if (!inputObj.IsSet("out_trade_no") && !inputObj.IsSet("transaction_id"))
{
ExceptionLog.ToWeixinPayLog("订单查询接口中,out_trade_no、transaction_id至少填一个!");
}

inputObj.SetValue("appid", ConfigHelper.WinxinPay_AppID);//公众账号ID
inputObj.SetValue("mch_id", ConfigHelper.WWinxinPay_MCHID);//商户号
inputObj.SetValue("nonce_str", Guid.NewGuid().ToString().Replace("-", ""));//随机字符串
inputObj.SetValue("sign", inputObj.MakeSign());//签名

string xml = inputObj.ToXml();
string response = HttpHelper.Post(xml, url, false, timeOut);//调用HTTP通信接口提交数据

//将xml格式的数据转化为对象以返回
WxPayData result = new WxPayData();
result.FromXml(response);

return result;
}

/// <summary>
/// 撤销订单API接口
/// @param WxPayData inputObj 提交给撤销订单API接口的参数,out_trade_no和transaction_id必填一个
/// @param int timeOut 接口超时时间
/// @throws WxPayException
/// @return 成功时返回API调用结果,其他抛异常
/// </summary>
/// <param name="inputObj"></param>
/// <param name="timeOut"></param>
/// <returns></returns>
public static WxPayData Reverse(WxPayData inputObj, int timeOut = 6)
{
string url = "https://api.mch.weixin.qq.com/secapi/pay/reverse";

//检测必填参数
if (!inputObj.IsSet("out_trade_no") && !inputObj.IsSet("transaction_id"))
{
ExceptionLog.ToWeixinPayLog("撤销订单API接口中,参数out_trade_no和transaction_id必须填写一个!");
}

inputObj.SetValue("appid", ConfigHelper.WinxinPay_AppID);//公众账号ID
inputObj.SetValue("mch_id", ConfigHelper.WWinxinPay_MCHID);//商户号
inputObj.SetValue("nonce_str", Guid.NewGuid().ToString().Replace("-", ""));//随机字符串
inputObj.SetValue("sign", inputObj.MakeSign());//签名
string xml = inputObj.ToXml();

string response = HttpHelper.Post(xml, url, true, timeOut);

WxPayData result = new WxPayData();
result.FromXml(response);

return result;
}

/// <summary>
/// 撤销订单,如果失败会重复调用10次
/// @param string out_trade_no 商户订单号
/// @param depth 调用次数,这里用递归深度表示
/// @return false表示撤销失败,true表示撤销成功
/// </summary>
/// <param name="out_trade_no"></param>
/// <param name="depth"></param>
/// <returns></returns>
public static bool Cancel(string out_trade_no, int depth = 0)
{
if (depth > 10) { return false; }

WxPayData reverseInput = new WxPayData();
reverseInput.SetValue("out_trade_no", out_trade_no);
WxPayData result = Reverse(reverseInput);

//接口调用失败
if (result.GetValue("return_code").ToString() != "SUCCESS")
{
return false;
}

//如果结果为success且不需要重新调用撤销,则表示撤销成功
if (result.GetValue("result_code").ToString() != "SUCCESS" && result.GetValue("recall").ToString() == "N")
{
return true;
}
else if (result.GetValue("recall").ToString() == "Y")
{
return Cancel(out_trade_no, ++depth);
}

return false;
}

/**
* 根据当前系统时间加随机序列来生成订单号
* @return 订单号
*/
public static string GenerateOutTradeNo()
{
var ran = new Random();
return string.Format("{0}{1}{2}", ConfigHelper.WWinxinPay_MCHID, DateTime.Now.ToString("yyyyMMddHHmmss"), ran.Next(999));
}

}
public class WxPayData
{
public WxPayData()
{

}

//采用排序的Dictionary的好处是方便对数据包进行签名,不用再签名之前再做一次排序
private SortedDictionary<string, object> m_values = new SortedDictionary<string, object>();

/**
* 设置某个字段的值
* @param key 字段名
* @param value 字段值
*/
public void SetValue(string key, object value)
{
m_values[key] = value;
}

/**
* 根据字段名获取某个字段的值
* @param key 字段名
* @return key对应的字段值
*/
public object GetValue(string key)
{
object o = null;
m_values.TryGetValue(key, out o);
return o;
}

/**
* 判断某个字段是否已设置
* @param key 字段名
* @return 若字段key已被设置,则返回true,否则返回false
*/
public bool IsSet(string key)
{
object o = null;
m_values.TryGetValue(key, out o);
if (null != o)
return true;
else
return false;
}

/**
* @将Dictionary转成xml
* @return 经转换得到的xml串
* @throws WxPayException
**/
public string ToXml()
{
//数据为空时不能转化为xml格式
if (0 == m_values.Count)
{
ExceptionLog.LogExp("WxPayData数据为空!");
//throw new WxPayException("WxPayData数据为空!");
}

string xml = "<xml>";
foreach (KeyValuePair<string, object> pair in m_values)
{
//字段值不能为null,会影响后续流程
if (pair.Value == null)
{
ExceptionLog.LogExp("WxPayData内部含有值为null的字段!");
//throw new WxPayException("WxPayData内部含有值为null的字段!");
}

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 + ">";
}
else//除了string和int类型不能含有其他数据类型
{
ExceptionLog.LogExp("WxPayData字段数据类型错误!");
//throw new WxPayException("WxPayData字段数据类型错误!");
}
}
xml += "</xml>";
return xml;
}

/**
* @将xml转为WxPayData对象并返回对象内部的数据
* @param string 待转换的xml串
* @return 经转换得到的Dictionary
* @throws WxPayException
*/
public SortedDictionary<string, object> FromXml(string xml)
{
if (string.IsNullOrEmpty(xml))
{
ExceptionLog.LogExp("将空的xml串转换为WxPayData不合法!");
//throw new WxPayException("将空的xml串转换为WxPayData不合法!");
}

XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(xml);
XmlNode xmlNode = xmlDoc.FirstChild;//获取到根节点<xml>
XmlNodeList nodes = xmlNode.ChildNodes;
foreach (XmlNode xn in nodes)
{
XmlElement xe = (XmlElement)xn;
m_values[xe.Name] = xe.InnerText;//获取xml的键值对到WxPayData内部的数据中
}

try
{
//2015-06-29 错误是没有签名
if (m_values["return_code"].ToString() != "SUCCESS")
{
return m_values;
}
CheckSign();//验证签名,不通过会抛异常
}
catch (Exception ex)
{
ExceptionLog.LogExp(ex.Message);
}

return m_values;
}

/**
* @Dictionary格式转化成url参数格式
* @ return url格式串, 该串不包含sign字段值
*/
public string ToUrl()
{
string buff = "";
foreach (KeyValuePair<string, object> pair in m_values)
{
if (pair.Value == null)
{
ExceptionLog.LogExp("WxPayData内部含有值为null的字段!");
//throw new WxPayException("WxPayData内部含有值为null的字段!");
}

if (pair.Key != "sign" && pair.Value.ToString() != "")
{
buff += pair.Key + "=" + pair.Value + "&";
}
}
buff = buff.Trim('&');
return buff;
}

/**
* @values格式化成能在Web页面上显示的结果(因为web页面上不能直接输出xml格式的字符串)
*/
public string ToPrintStr()
{
string str = "";
foreach (KeyValuePair<string, object> pair in m_values)
{
if (pair.Value == null)
{
ExceptionLog.LogExp("WxPayData内部含有值为null的字段!");
//throw new WxPayException("WxPayData内部含有值为null的字段!");
}

str += string.Format("{0}={1}<br>", pair.Key, pair.Value.ToString());
}
return str;
}

/**
* @生成签名,详见签名生成算法
* @return 签名, sign字段不参加签名
*/
public string MakeSign()
{
//转url格式
string str = ToUrl();
//在string后加入API KEY
str += "&key=" + ConfigHelper.WinxinPay_Key;
//MD5加密
var md5 = 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();
}

/**
*
* 检测签名是否正确
* 正确返回true,错误抛异常
*/
public bool CheckSign()
{
//如果没有设置签名,则跳过检测
if (!IsSet("sign"))
{
ExceptionLog.LogExp("WxPayData签名存在但不合法!");
//throw new WxPayException("WxPayData签名存在但不合法!");
}
//如果设置了签名但是签名为空,则抛异常
else if (GetValue("sign") == null || GetValue("sign").ToString() == "")
{
ExceptionLog.LogExp("WxPayData签名存在但不合法!");
//throw new WxPayException("WxPayData签名存在但不合法!");
}

//获取接收到的签名
string return_sign = GetValue("sign").ToString();

//在本地计算新的签名
string cal_sign = MakeSign();

if (cal_sign == return_sign)
{
return true;
}

ExceptionLog.LogExp("WxPayData签名验证错误!");
//throw new WxPayException("WxPayData签名验证错误!");
return false;
}

/**
* @获取Dictionary
*/
public SortedDictionary<string, object> GetValues()
{
return m_values;
}
}


前端效果图



扫描枪自动提交,input输入框内“onkeyup=fun()”方法即可
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  .net sdk 微信 支付