开发环境:.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. LeetCode 819. Most Common Word (最常见的单词)

    Given a paragraph and a list of banned words, return the most frequent word that is not in the list ...

  2. unity3D游戏开发实战原创视频讲座系列11之相扑游戏开发并公布到Win\WP8

     解说文件夹 第一讲 游戏的演示和资源介绍 第二讲 场景的建设 第三讲 玩家的移动 第四讲 对手的AI(让对手动起来) 第五讲 游戏的管理(上) 第六讲 游戏的管理(下) 第七讲 公布到Win8系 ...

  3. 通常每个套接字地址(协议/网络地址/端口)只允许使用一次。 数据库连接不释放测试 连接池 释放连接 关闭连接 有关 redis-py 连接池会导致服务器产生大量 CLOSE_WAIT 的再讨论以及一个解决方案

    import pymysqlfrom redis import Redisimport time h, pt, u, p, db = '192.168.2.210', 3306, 'root', 'n ...

  4. Android-通过SlidingPaneLayout高仿微信6.2最新版手势滑动返回(一)

    近期更新了微信版本号到6.2.发现里面有个很好的体验,就是在第二个页面Activity能手势向右滑动返回,在手势滑动的过程中能看到第一个页面,这样的体验很赞,这里高仿了一下. 这里使用的是v4包里面的 ...

  5. IOS系统控件高度

    以下是常见的几种控件的高度.Statusbar,Navigationbar和Tabbar的宽度极其图标大小. 下表是更为详细的参数,包括了Statusbar,Navigationbar.Tabbar. ...

  6. .NET连接数据库实例

    .NET连接数据库实例 keleyi.com 柯乐义 本实例实现了从MSSQL 2005数据库读取数据并显示在页面上的功能.在Visual Studio 2010上测试成功.源代码下载:http:// ...

  7. hdoj--1205--吃糖果(规律)

     吃糖果 Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others) Total Sub ...

  8. bzoj2744 [HEOI2012]朋友圈——二分图匹配

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2744 首先,求一个图的最大团等价于求它的补图的最大独立集,而二分图的最大独立集 = 总点数 ...

  9. 特征变化--->特征向量中部分特征到类别索引的转换(VectorIndexer)

    VectorIndexer: 倘若所有特征都已经被组织在一个向量中,又想对其中某些单个分量进行处理时,Spark ML提供了VectorIndexer类来解决向量数据集中的类别性特征转换. 通过为其提 ...

  10. 40. combo的displayField和valueField属性

    转自:https://xsl2007.iteye.com/blog/773464 下拉框combo可以设置displayField和valueField属性,这两个值值相当于Java中的map,一个键 ...