开发环境:.NET MVC+ ORM框架(EF)

一、参考文档:

  1、微信JSAPI支付官方文档:https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_3&index=1;

二、开发前准备:

   1、必须申请微信公众平台(企业用户开通);

  2、必须开通小程序平台,并与微信公众平台进行绑定;

  3、必须开通微信商户平台,并妥善保管号商户号和商户密钥;

(值得说明的是:微信商户密钥在拿到手之后,请首先重置三次以上,具体原因不清楚,但是不重置的话,后期开发的时候,微信统一下单接口会频繁报错,而且错误信息莫名其妙,主要报错信息为:“签名错误”,即使你的参数签名在微信的签名校验工具中校验通过,也会提示你“签名错误”,但是此时你无需对接口做任何改动,只需要重置商户密钥3次以上,此问题便可解决)

  4、网站升级https协议,因为调用微信支付成功后,微信服务器会对你传值的回调地址(notify_url字段,可以理解为具体的业务逻辑处理方法路径)进行回调,(虽然目前统一下单接口可以回调http协议接口,但是仍然建议网站升级为https协议);

三、支付流程

  1、统一下单接口,后台通过统一下单接口,向微信请求下单支付,微信后台接到参数后,会生成一个商户订单,并将预下单id(prepay_id 这个返回字段很重要)返回给后台;

  2、后台接收微信返回值,进行二次签名,并将签名的参数返回给小程序前台;

  3、小程序端接收到签名参数后,调用 wx.requestPayment 方法,传入参数,调起收银台;

  4、用户支付后,微信服务器处理本次支付情况,并回调后台业务处理接口。

四、代码实现

  1、微信支付model类:

    public  class PayModel
{ /// <summary>
/// 统一下单API
/// </summary>
public static string orderUrl = ConfigurationManager.AppSettings["WXunifiedorder"].ToString(); /// <summary>
/// 支付结果通知API
/// </summary>
public static string notifyUrl = ConfigurationManager.AppSettings["WxPayNotifyurl"].ToString(); /// <summary>
/// 查询订单API
/// </summary>
public static string queryUrl = ConfigurationManager.AppSettings["WxPayQueryOrder"].ToString(); /// <summary>
/// 小程序唯一标识
/// </summary>
public static string AppID = ConfigurationManager.AppSettings["WxAppId"].ToString(); /// <summary>
/// 小程序的 app secret
/// </summary>
public static string secret = ConfigurationManager.AppSettings["WxSecret"].ToString(); /// <summary>
/// 商户号(微信支付分配的商户号)
/// </summary>
public static string mchid = ConfigurationManager.AppSettings["WxMerchantNo"].ToString(); /// <summary>
///商户平台设置的密钥key
/// </summary>
public static string WxMerchantKey = ConfigurationManager.AppSettings["WxMerchantKey"].ToString(); /// <summary>
/// 随机字符串不长于 32 位
/// </summary>
public static string nonceStr = RandomNum.CreateRandomNum().ToUpper(); /// <summary>
/// 时间戳 从1970年1月1日00:00:00至今的秒数,即当前的时间
/// </summary>
public static string timeStamp = Convert.ToInt64((DateTime.UtcNow - new DateTime(, , , , , , )).TotalSeconds).ToString(); /// <summary>
/// 交易类型 小程序取值如下:JSAPI
/// </summary>
public static string tradeType = "JSAPI"; /// <summary>
/// 签名类型 默认为MD5,支持HMAC-SHA256和MD5。
/// </summary>
public static string signType = "MD5"; /// <summary>
/// 商品描述 商品简单描述,该字段请按照规范传递
/// </summary>
public static string body = "挪威躺椅"; /// <summary>
/// 附加数据 在查询API和支付通知中原样返回
/// </summary>
public static string attach = "商城支付"; /// <summary>
/// 签名,参与签名参数:appid,mch_id,transaction_id,out_trade_no,nonce_str,key
/// </summary>
public string sign = ""; /// <summary>
/// 微信订单号,优先使用
/// </summary>
public string transactionid = ""; /// <summary>
/// 商户系统内部订单号
/// </summary>
public string out_trade_no = "";
/// <summary>
/// 订单金额
/// </summary>
public decimal totalfee=; }
public class OrderReloadModel
{
public string return_msg { get; set; }
public string return_code { get; set; }
public string orderNo { get; set; }
public string AppID { get; set; }
public string package { get; set; }
public string timeStamp { get; set; }
public string nonecStr { get; set; }
public string signType { get; set; }
public string paysign { get; set; } }

 

   2、微信支付帮助类

    public class PayHelper
{
#region 生成签名
/// <summary>
/// 获取签名数据
///</summary>
/// <param name="strParam"></param>
/// <param name="key"></param>
/// <returns></returns>
public static string GetSignInfo(Dictionary<string, string> strParam, string key)
{
int i = ;
string sign = string.Empty;
StringBuilder sb = new StringBuilder();
try
{
foreach (KeyValuePair<string, string> temp in strParam)
{
if (temp.Value == "" || temp.Value == null || temp.Key.ToLower() == "sign")
{
continue;
}
i++;
sb.Append(temp.Key.Trim() + "=" + temp.Value.Trim() + "&");
}
sb.Append("key=" + key.Trim() + "");
sign = CryptoService.Md5EncryptStr(sb.ToString()).ToUpper();
LogHelper.Writer("服务端参数:" + sb.ToString() + "签名:" + sign, "", );
}
catch (Exception ex)
{
throw ex;
}
return sign;
}
#endregion #region XML 处理
/// <summary>
/// 获取XML值
/// </summary>
/// <param name="strXml">XML字符串</param>
/// <param name="strData">字段值</param>
/// <returns></returns>
public static string GetXmlValue(string strXml, string strData)
{
string xmlValue = string.Empty;
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.LoadXml(strXml);
var selectSingleNode = xmlDocument.DocumentElement.SelectSingleNode(strData);
if (selectSingleNode != null)
{
xmlValue = selectSingleNode.InnerText;
}
return xmlValue;
} /// <summary>
/// 集合转换XML数据 (拼接成XML请求数据)
/// </summary>
/// <param name="strParam">参数</param>
/// <returns></returns>
public static string CreateXmlParam(Dictionary<string, string> strParam)
{
StringBuilder sb = new StringBuilder();
try
{
sb.Append("<xml>");
foreach (KeyValuePair<string, string> k in strParam)
{
if (k.Key == "attach" || k.Key == "body" || k.Key == "sign")
{
sb.Append("<" + k.Key + "><![CDATA[" + k.Value + "]]></" + k.Key + ">");
}
else
{
sb.Append("<" + k.Key + ">" + k.Value + "</" + k.Key + ">");
}
}
sb.Append("</xml>");
}
catch (Exception ex)
{
throw ex;
// AddLog("PayHelper", "CreateXmlParam", ex.Message, ex);
}
var a = sb.ToString();
return sb.ToString();
} /// <summary>
/// XML数据转换集合(XML数据拼接成字符串)
/// </summary>
/// <param name="xmlString"></param>
/// <returns></returns>
public static Dictionary<string, string> GetFromXml(string xmlString)
{
Dictionary<string, string> sParams = new Dictionary<string, string>();
try
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(xmlString);
XmlElement root = doc.DocumentElement;
int len = root.ChildNodes.Count;
for (int i = ; i < len; i++)
{
string name = root.ChildNodes[i].Name;
if (!sParams.ContainsKey(name))
{
sParams.Add(name.Trim(), root.ChildNodes[i].InnerText.Trim());
}
}
}
catch (Exception ex)
{
throw ex;
}
return sParams;
} /// <summary>
/// 返回通知 XML
/// </summary>
/// <param name="returnCode"></param>
/// <param name="returnMsg"></param>
/// <returns></returns>
public static string GetReturnXml(string returnCode, string returnMsg)
{
StringBuilder sb = new StringBuilder();
sb.Append("<xml>");
sb.Append("<return_code><![CDATA[" + returnCode + "]]></return_code>");
sb.Append("<return_msg><![CDATA[" + returnMsg + "]]></return_msg>");
sb.Append("</xml>");
return sb.ToString();
}
#endregion /// <summary>
/// 获得Post过来的数据
/// </summary>
/// <returns></returns>
public static string GetPostStr()
{
Int32 intLen = Convert.ToInt32(HttpContext.Current.Request.InputStream.Length);
byte[] b = new byte[intLen];
HttpContext.Current.Request.InputStream.Read(b, , intLen);
return Encoding.UTF8.GetString(b);
} /// <summary>
/// 模拟POST提交 (不需要微信API证书)
/// </summary>
/// <param name="url">请求地址</param>
/// <param name="xmlParam">xml参数</param>
/// <returns>返回结果</returns>
public static string PostHttpResponse(string url, string xmlParam)
{
HttpWebRequest myHttpWebRequest = (HttpWebRequest)HttpWebRequest.Create(url);
myHttpWebRequest.Method = "POST";
myHttpWebRequest.ContentType = "application/x-www-form-urlencoded;charset=utf-8"; // Encode the data
byte[] encodedBytes = Encoding.UTF8.GetBytes(xmlParam);
myHttpWebRequest.ContentLength = encodedBytes.Length; // Write encoded data into request stream
Stream requestStream = myHttpWebRequest.GetRequestStream();
requestStream.Write(encodedBytes, , encodedBytes.Length);
requestStream.Close(); HttpWebResponse result; try
{
result = (HttpWebResponse)myHttpWebRequest.GetResponse();
}
catch
{
return string.Empty;
} if (result.StatusCode == HttpStatusCode.OK)
{
using (Stream mystream = result.GetResponseStream())
{
using (StreamReader reader = new StreamReader(mystream))
{
return reader.ReadToEnd();
}
}
}
return null;
}/// <summary>
/// 获取客户端IP
/// </summary>
/// <returns></returns>
public static string GetUserIP()
{
string ipv4 = String.Empty;
foreach (IPAddress ip in Dns.GetHostAddresses(GetClientIP()))
{
if (ip.AddressFamily.ToString() == "InterNetwork")
{
ipv4 = ip.ToString();
break;
}
} if (ipv4 != String.Empty)
{
return ipv4;
}
// 利用 Dns.GetHostEntry 方法,由获取的 IPv6 位址反查 DNS 纪录,
// 再逐一判断何者为 IPv4 协议,即可转为 IPv4 位址。
foreach (IPAddress ip in Dns.GetHostEntry(GetClientIP()).AddressList)
//foreach (IPAddress ip in Dns.GetHostAddresses(Dns.GetHostName()))
{
if (ip.AddressFamily.ToString() == "InterNetwork")
{
ipv4 = ip.ToString();
break;
}
} return ipv4;
} }

  

  3、微信支付加密类(MD5加密):  

        public static string Md5EncryptStr(string input)
{
var result = string.Empty;
if (string.IsNullOrEmpty(input)) return result;
using (var md5 = MD5.Create())
{
result = GetMd5Hash(md5, input);
}
return result;
}

  4、统一下单接口:

public JsonResult GetPrayPayId(string openId, string orderNo)
{
Result result = new Result();
List<OrderReloadModel> listMsg = new List<OrderReloadModel>();
try
{

#region 统一下单接口API接口调用参数准备
//获取请求数据
Dictionary<string, string> strParam = new Dictionary<string, string>();
//小程序ID
strParam.Add("appid", PayModel.AppID);//商品描述 对商品的简单描述,次字段值会在 订单详情--商品 字段展示出来
strParam.Add("body", PayModel.body);
//商户号 微信商户平台商户号,10位数字
strParam.Add("mch_id", PayModel.mchid);
//随机字符串
strParam.Add("nonce_str", PayModel.nonceStr);
//通知地址 (异步接收微信支付结果通知的回调地址,可以看作一个后台方法的完整路径,通知url必须为外网可访问的url,不能携带参数。)
strParam.Add("notify_url", PayModel.notifyUrl);
//用户标识
strParam.Add("openid", openId);
//商户订单号 必须保持唯一,如果针对同一个订单重复提交,在订单信息(如订单金额)发生改变时,会导致prepay_id为空
strParam.Add("out_trade_no", orderNo);
//终端IP
strParam.Add("spbill_create_ip", PayHelper.GetUserIP());
//标价金额 已分为单位
strParam.Add("total_fee", order_cent.ToString());
//交易类型
strParam.Add("trade_type", PayModel.tradeType);
strParam.Add("sign", PayHelper.GetSignInfo(strParam, PayModel.WxMerchantKey));
#endregion

#region 统一下单接口返回结果
//获取预支付ID
string preInfo = PayHelper.PostHttpResponse(PayModel.orderUrl, PayHelper.CreateXmlParam(strParam));
string return_code = PayHelper.GetXmlValue(preInfo, "return_code");
string return_msg = PayHelper.GetXmlValue(preInfo, "return_msg");

LogHelper.Writer("统一下单请求发送至微信,微信接口统一下单方法返回结果:/WXWecahtPay/GetPrayPayId 参数【" + strParam + "】,微信平台返回结果【" + preInfo + "】", "", 5);

#endregion

#region 将统一下单的返回参数返回给小程序前台

OrderReloadModel info = new OrderReloadModel();
if (return_code == "SUCCESS")
{
//再次签名
string nonecStr = PayModel.nonceStr;
string timeStamp = PayModel.timeStamp;
string package = "prepay_id=" + PayHelper.GetXmlValue(preInfo, "prepay_id");
Dictionary<string, string> singInfo = new Dictionary<string, string>();
singInfo.Add("appId", PayModel.AppID);
singInfo.Add("nonceStr", nonecStr);
singInfo.Add("package", package);
singInfo.Add("signType", PayModel.signType);
singInfo.Add("timeStamp", timeStamp);
//将二次签名后的参数返回给小程序
info.return_msg = return_msg;
info.return_code = return_code;
info.orderNo = orderNo;
info.AppID = PayModel.AppID;
info.package = package;
info.timeStamp = timeStamp;
info.nonecStr = nonecStr;
info.signType = PayModel.signType;
info.paysign = PayHelper.GetSignInfo(singInfo, PayModel.WxMerchantKey);
listMsg.Add(info);
if (listMsg.Count > 0)
{
result.ResultCode = Infrastructure.Enum.ReusltCode.OK;
result.ResultObj = listMsg;
}
}
else
{
info = new OrderReloadModel();
info.return_msg = return_msg;
info.return_code = return_code;
info.orderNo = orderNo;
listMsg.Add(info);
result.ResultCode = Infrastructure.Enum.ReusltCode.Fail;
result.ResultObj = listMsg;
}
#endregion
LogHelper.Writer("返回小程序支付参数接口 方法:/WXWecahtPay/GetPrayPayId 参数【string openId=" + openId + ", string orderNo=" + orderNo + "】,返回结果【" + JsonConvert.SerializeObject(result) + "】", "", 5);
}
catch (Exception ex)
{
result.ResultCode = Infrastructure.Enum.ReusltCode.Exception;
result.ResultMsg = ex.Message;
LogHelper.Writer("返回小程序支付参数接口:/WXWecahtPay/GetPrayPayId 接口异常 异常信息【" + ex.Message + "】", "", 5);
}
return new IMG_JsonResult(result);
}

  5、支付成功回调方法:

        /// <summary>
/// 支付结果通知API
/// </summary>
/// <returns></returns>
[HttpPost]
public JsonResult OrderNotify()
{
string strResult = string.Empty;
try
{
//1.将微信小程序请求支付的参数传入body中,提交到微信服务器,并获取微信通知的参数
string strXML = PayHelper.GetPostStr();
if (string.IsNullOrEmpty(strXML))
{
strResult = "未检测到支付业务!";
return new JsonResult(strResult);
} LogHelper.Writer("支付请求发送至微信,微信接口回调方法:/WXWecahtPay/OrderNotify 参数【" + strXML + "】", "", ); //判断是否请求成功
if (PayHelper.GetXmlValue(strXML, "return_code") == "SUCCESS")
{
//判断是否支付成功
if (PayHelper.GetXmlValue(strXML, "result_code") == "SUCCESS")
{
//获得本次支付的签名
string getSign = PayHelper.GetXmlValue(strXML, "sign");
//验证签名是否有效
string sign = PayHelper.GetSignInfo(PayHelper.GetFromXml(strXML), PayModel.WxMerchantKey);
if (sign == getSign)
{
//校验订单信息
string wxOrderNum = PayHelper.GetXmlValue(strXML, "transaction_id"); //微信订单号
string orderNum = PayHelper.GetXmlValue(strXML, "out_trade_no"); //商户订单号
string orderTotal = PayHelper.GetXmlValue(strXML, "total_fee");
string openid = PayHelper.GetXmlValue(strXML, "openid");
string payMark = PayHelper.GetXmlValue(strXML, "transaction_id"); //微信支付订单号
//2.更新订单的相关状态
你的业务处理代码...//3.返回一个xml格式的结果给微信服务器,完成支付流程,避免微信重复回调我们的服务器,造成服务器不必要的开支
strResult = PayHelper.GetReturnXml("SUCCESS", "OK");
}
else
{
strResult = PayHelper.GetReturnXml("FAIL", "签名不一致!");
}
}
else
{
strResult = PayHelper.GetReturnXml("FAIL", "支付通知失败!");
}
}
else
{
strResult = PayHelper.GetReturnXml("FAIL", "支付通知失败!");
}
LogHelper.Writer("支付请求发送至微信,微信接口回调方法:/WXWecahtPay/OrderNotify 参数【" + strXML + "】 接口处理结果【" + strResult + "】", "", );
}
catch (Exception ex)
{
strResult = ex.Message;
LogHelper.Writer("支付请求发送至微信,微信接口回调方法:/WXWecahtPay/OrderNotify 接口异常 异常信息【" + ex.Message + "】", "", );
}
return new JsonResult(strResult);
}

微信小程序支付(JSAPI支付)的更多相关文章

  1. 让你的微信小程序具有在线支付功能

    前言 最近需要在微信小程序中用到在线支付功能,于是看了一下官方的文档,发现要在小程序里实现微信支付还是很方便的,如果你以前开发过服务号下的微信支付,那么你会发现其实小程序里的微信支付和服务号里的开发过 ...

  2. 微信小程序书简易支付

    这里结合了上一篇的手机号登录接下来的实现功能 https://www.cnblogs.com/xiaoyantongxue/p/15472915.html 登录后进入课程选择页面 1:数据库填入数据 ...

  3. 微信小程序-登陆、支付、模板消息

    wx.login(OBJECT) 调用接口获取登录凭证(code)进而换取用户登录态信息,包括用户的唯一标识(openid) 及本次登录的 会话密钥(session_key).用户数据的加解密通讯需要 ...

  4. 微信小程序发布与支付

    一.小程序的发布流程 小程序协同工作和发布官网链接 1.背景 小程序的平台里,开发者完成开发之后,需要在开发者工具提交小程序的代码包,然后在小程序后台发布小程序. 2.流程 上传代码 代码管理服务器上 ...

  5. [转]微信小程序 c#后台支付结果回调

    本文转自:http://www.cnblogs.com/weizhiing/p/7700723.html 又为大家带来简单的c#后台支付结果回调方法,首先还是要去微信官网下载模板(WxPayAPI), ...

  6. 微信小程序 功能函数 支付接口

    // 订单生成返回数据,弹出是否支付模态 wx.showModal({ title: '微信支付', content: '确定支付吗?', success: function (res) { if ( ...

  7. 微信小程序 c#后台支付结果回调

    又为大家带来简单的c#后台支付结果回调方法,首先还是要去微信官网下载模板(WxPayAPI),将模板(WxPayAPI)添加到服务器上,然后在打开WxPayAPI项目中的example文件下的 Nat ...

  8. 微信小程序调起支付API

    官方文档: https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_7 https://developers.weixin.q ...

  9. 微信小程序之微信支付C#后台(统一下单)

    一.微信小程序支付 1.微信小程序端请求支付接口 商户在小程序中先调用该接口在微信支付服务后台生成预支付交易单,返回正确的预支付交易后调起支付.具体可以查看接口示例. 接口传入参数示例: <xm ...

  10. 微信小程序支付接口之Django后台

    本文链接:https://blog.csdn.net/qq_41860162/article/details/89098694Python3-django-微信小程序支付接口调用工具类生成一系列微信官 ...

随机推荐

  1. [Vue @Component] Define Props on a Vue Class with vue-property-decorator

    While traditional Vue components require a data function which returns an object and a method object ...

  2. SuperSwipeRefreshLayout 一个功能强大的自己定义下拉刷新组件

    SuperSwipeRefreshLayout 一个功能强大的自己定义下拉刷新组件. Why? 下拉刷新这样的控件.想必大家用的太多了,比方使用非常多的XListView等. 近期.项目中非常多列表都 ...

  3. 国际维修联合会IMA年度大会在瑞士圆满结束

    瑞士卢加诺 ☆2016年4月5日至7日 中国设备管理协会国际交流合作中心主任.学府咨询(国际)集团董事长.IMA中国分会主席李葆文教授,应邀出席了4月5日至7日在瑞士卢加诺召开的国际维修联合会年度大会 ...

  4. 【剑指offer】合并两有序单链表

    转载请注明出处:http://blog.csdn.net/ns_code/article/details/25739727 九度OJ上AC,採用归并的思想递归实现. 题目描写叙述: 输入两个单调递增的 ...

  5. 利用 gnuplot_i 在你的 c 程序中调用 GNUPLOT

    这是一篇非常早曾经写的小文章,最初发表于我的搜狐博客(2008-09-23 22:55).由于自从转移到这里后,sohu 博客就不再维护了,所以把这篇文章也一起挪了过来. GNUPLOT 是一款功能强 ...

  6. iconfont 不居中的问题

    引用 阿里的 iconfont 发现跟我的文字不居中 页面中实际展示的时候,发现 iconfont 字体飘起来了 原因是:iconfont 的基线跟 文字 的基线不同导致的. 解决办法:给 iconf ...

  7. js 里面的 function 与 Function

    function 是 js 的标识符 Function 是 js 里面的一个 构造函数 1.new function 与 new Function 的区别 new 运算符在 js 里面是 创建一个自定 ...

  8. Error:全局变量不明白(using namespace std 与全局变量的冲突)

    在用递归写八皇后时,定义了一个全局变量count,结果出现故障例如以下:提示全局变量不明白. 最后发如今实现文件.cpp中.我使用了using  namespace std; 解决方法: 1.使用co ...

  9. Linux对外连接port数限制

    左右时,開始大量抛例如以下异常: java.net.BindException:Cannot assign requested address atsun.nio.ch.Net.connect0(Na ...

  10. FOBiz组合模糊查询

    List list= delegator.findList("Entity",condition , null, null, null, false);其中condition为:组 ...