一. 案例介绍

这里模拟一个实际业务场景,进行介绍微信支付,业务功能包括:登录、注册、充值、查看充值记录。

  

页面图:

       

二. 概要设计

1.数据库设计

  这里数据库包括两张表:用户表和订单表。

  用户表: 主键id、用户名、密码、openid、注册时间

  订单表: 主键id、用户id,商品名称、订单状态(0代表下单了未支付,1代表支付成功)、商品价钱、下单时间

  

2.微信支付流程

  这里结合该案例,来说明微信支付流程。

  该流程中涉及到4种角色,分别是微信用户、微信客户端、商户系统(自己的系统)、微信支付系统。

  流程1:

  ①用户登录微信客户端系统→②进入主页→③去支付→④生成商户系统订单→⑤调用微信统一下单API,在微信支付系统里生成预支付订单,并返回预支付订单信息→

  ⑥商户系统拿到返回的预支付订单信息,进行签名,便按照一定的格式返给微信客户端(JSAPI页面)→⑦微信客户端JSAPI页面拿到参数,请求支付,输入密码,进行支付→

  ⑧这时会进行2个并行处理→异步通知商户支付结果,商户系统接到通知后,需要修改订单的业务逻辑(该案例修改订单状态0改为1),商户系统需要告知微信系统处理结果

  →给微信客户端发送支付结果,并发微信消息提示 

  ⑨微信客户端跳转到商户H5页面,查询商户后台支付结果

  ⑩ 这时候分两种情况

    A. 商户后台系统,已经接到通知,进行了业务修改,直接返回成功。

    B. 商户后台系统,没有接到通知,这时去查询微信支付系统,如果微信支付系统成功,说明确实付款成功,只是因为网络延迟造成商户后台暂时没有接到通知,如果查询后发现未付款成功,则返回付款失败。

微信支付业务流程图:
       

3.代码配置

(1).参数配置

(2).前端页面代码

    $(function () {
// 当微信内置浏览器完成内部初始化后会触发WeixinJSBridgeReady事件。
document.addEventListener('WeixinJSBridgeReady', function onBridgeReady() {
//公众号支付
document.getElementById("pay").onclick = function () {
//1.前端验证
var money = $('#num').val();
if (money == "") {
alert('请将信息输入完整');
return;
}
mui('#pay').button('loading');
//2.进行下单
$.ajax({
type: 'POST',
url: '/WeiXinGz/GetAPI',
data: { "money": money },
cache: false,
dataType: 'json',
success: function (jsonData) {
if (jsonData.status == "") {
//公众号支付
WeixinJSBridge.invoke('getBrandWCPayRequest', jsonData.payData, function (res) {
// 使用以下方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
//【因此微信团队建议:】当收到ok返回时,向商户后台询问是否收到交易成功的通知,
//若收到通知,前端展示交易成功的界面;
//若此时未收到通知,商户后台主动调用查询订单接口,查询订单的当前状态,并反馈给前端展示相应的界面。
if (res.err_msg == "get_brand_wcpay_request:ok") {
//JS API的返回结果get_brand_wcpay_request:ok仅在用户成功完成支付时返回
$.ajax({
type: 'POST',
url: '/WeiXinGz/QueryOrder',
data: {
orderId: jsonData.orderId
},
cache: false,
dataType: 'text',
success: function (jsonData) {
if (jsonData == "ok") {
alert("支付成功", "提示", function () {
alert("页面跳转等业务处理");
});
mui('#pay').button('reset');
} else {
alert("支付失败,请稍后重试!如果收到支付通知,切勿重复支付1!");
mui('#pay').button('reset');
}
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert("支付失败,请稍后重试!如果收到支付通知,切勿重复支付2!");
mui('#pay').button('reset');
}
});
} else if (res.err_msg == "get_brand_wcpay_request:cancel") {
//由于前端交互复杂,get_brand_wcpay_request:cancel或者get_brand_wcpay_request:fail可以统一处理为用户遇到错误或者主动放弃,不必细化区分。
alert("您放弃了支付");
mui('#pay').button('reset');
} else {
//由于前端交互复杂,get_brand_wcpay_request:cancel或者get_brand_wcpay_request:fail可以统一处理为用户遇到错误或者主动放弃,不必细化区分。
alert("支付失败,请稍后重试!如果收到支付通知,切勿重复支付3!");
mui('#pay').button('reset');
}
});
} else {
alert(jsonData.promptInfor);
mui('#pay').button('reset');
}
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert("微信订单提交失败,请稍后重试4!");
mui('#pay').button('reset');
}
}); }
}, false);
});

(3).统一下单接口

    /// <summary>
/// 统一下单接口
/// </summary>
/// <param name="money">钱数</param>
/// <returns></returns>
public ActionResult GetAPI(string money)
{
try
{
//一.系统本身自有的业务处理
//1.必要信息的初始化
string userId = Session["userId"].ToString(); //用户主键
UserInfor userInfor = db.Set<UserInfor>().Where(a => a.id == userId).FirstOrDefault();
string orderId = GenerateOrderNum(); //生成订单号
string totalFee = money;//设置默认商品费用为【1分】
string nonceStr = TenPayV3Util.GetNoncestr(); //获取 随机字符串
string openid = userInfor.openId;
//2.自己商户系统下单
OrderInfor orderInfor = new OrderInfor();
orderInfor.id = orderId;
orderInfor.uid = userInfor.id;
orderInfor.goodName = "测试商品";
orderInfor.goodPrice = totalFee;
orderInfor.addTime = DateTime.Now;
orderInfor.status = ""; //已经下单,但未付款
db.Set<OrderInfor>().Add(orderInfor);
db.SaveChanges(); //二.微信系统下单
//1.创建支付应答对象并初始化
RequestHandler packageReqHandler = new RequestHandler(null);
packageReqHandler.Init();
//1.1设置统一下单的参数
packageReqHandler.SetParameter("appid", ConfigHelp.AppSettings("AppId")); //公众账号ID
packageReqHandler.SetParameter("mch_id", ConfigHelp.AppSettings("MchId")); //商户号
packageReqHandler.SetParameter("nonce_str", nonceStr); //随机字符串
packageReqHandler.SetParameter("body", "科技-服务"); //商品描述
packageReqHandler.SetParameter("out_trade_no", orderId); //商户订单号
packageReqHandler.SetParameter("total_fee", totalFee); //商品金额,以分为单位
packageReqHandler.SetParameter("spbill_create_ip", Request.UserHostAddress); //终端IP
packageReqHandler.SetParameter("notify_url", ConfigHelp.AppSettings("notify_url")); //微信支付异步通知回调地址
packageReqHandler.SetParameter("trade_type", "JSAPI"); //交易类型 代表公众号支付
packageReqHandler.SetParameter("openid", openid); //用户标识
string sign = packageReqHandler.CreateMd5Sign("key", ConfigHelp.AppSettings("key")); //预支付签名
packageReqHandler.SetParameter("sign", sign);
//1.2 下单数据格式转换
string data = packageReqHandler.ParseXML();
//1.3 进行下单
string result = TenPayV3.Unifiedorder(data);
//2.对下单返回结果进行分析
XDocument res = XDocument.Parse(result);
//2.1 对返回结果进行判断 //2.2 成功的情况下获取必要的参数
string prepayId = res.Element("xml").Element("prepay_id").Value; //获取预支付订单编号prepayId
//3. 获取支付参数并签名
string timeStamp = TenPayV3Util.GetTimestamp(); //获取时间戳
//设置支付参数
RequestHandler paySignReqHandler = new RequestHandler(null);
paySignReqHandler.SetParameter("appId", ConfigHelp.AppSettings("AppId"));
paySignReqHandler.SetParameter("timeStamp", TenPayV3Util.GetTimestamp());
paySignReqHandler.SetParameter("nonceStr", nonceStr);
paySignReqHandler.SetParameter("package", string.Format("prepay_id={0}", prepayId));
paySignReqHandler.SetParameter("signType", "MD5"); //签名【MD5】
string paySign = paySignReqHandler.CreateMd5Sign("key", ConfigHelp.AppSettings("key")); //JSAPI支付签名
var payData = new
{
appId = ConfigHelp.AppSettings("AppId"),
timeStamp = timeStamp,
nonceStr = nonceStr,
package = string.Format("prepay_id={0}", prepayId),
signType = "MD5",
paySign = paySign,
};
return Json(new
{
status = "",
promptInfor = "微信下单成功",
payData = payData,
orderId = orderId
});
}
catch (Exception ex)
{
string a = ex.Message; return Json(new
{
status = "",
promptInfor = a
});
}
}

(4).微信异步通知接口

   public ActionResult PayNotifyUrl()
{
//获取当前http请求
HttpContext httpContext = System.Web.HttpContext.Current;
ResponseHandler notifyDataHandler = new ResponseHandler(httpContext);
//返回状态码【SUCCESS/FAIL】此字段是通信标识
string return_code = notifyDataHandler.GetParameter("return_code");
//返回信息【如非空,为错误原因】
string return_msg = notifyDataHandler.GetParameter("return_msg");
//表示通信成功
if (return_code == "SUCCESS")
{
//获取业务结果【交易是否成功(SUCCESS/FAIL)】
string result_code = notifyDataHandler.GetParameter("result_code");
//表示业务结果成功
if (result_code == "SUCCESS")
{
//设置签名密钥
notifyDataHandler.SetKey(ConfigHelp.AppSettings("key"));
//验证请求是否从微信发过来(安全)【验证签名】
if (notifyDataHandler.IsTenpaySign())
{
//获取订单编号
string out_trade_no = notifyDataHandler.GetParameter("out_trade_no");
//检查是否返回商户订单号
if (!string.IsNullOrEmpty(out_trade_no))
{
try
{
OrderInfor orderInfor = db.Set<OrderInfor>().Where(a => a.id == out_trade_no).FirstOrDefault();
if (orderInfor != null)
{
//这里需要根据订单号更改系统的业务
//这里模拟测试记录订单号
orderInfor.status = ""; //表示付款成功,成功回调
db.Entry(orderInfor).State = EntityState.Modified;
db.SaveChanges(); return_code = "SUCCESS";
return_msg = "OK";
}
else
{
return_code = "FAIL";
return_msg = "商户系统中不存在该订单";
}
}
catch (Exception ex)
{
//系统异常
return_code = "FAIL";
return_msg = ex.Message;
}
}
else
{
//支付结果中商户订单号不存在
return_code = "FAIL";
return_msg = "支付结果中商户订单号不存在";
}
}
else
{
//签名失败
return_code = "FAIL";
return_msg = "签名失败";
}
}
else
{
//交易失败
return_code = "FAIL";
return_msg = "交易失败";
}
}
//商户处理后同步返回给微信参数
string xml = string.Format(@"<xml><return_code><![CDATA[{0}]]></return_code><return_msg><![CDATA[{1}]]></return_msg></xml>", return_code, return_msg);
//返回处理结果
return Content(xml, "text/xml");
}

(5).JSAPI接口请求

  //公众号支付
WeixinJSBridge.invoke('getBrandWCPayRequest', jsonData.payData, function (res) {
// 使用以下方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
//【因此微信团队建议:】当收到ok返回时,向商户后台询问是否收到交易成功的通知,
//若收到通知,前端展示交易成功的界面;
//若此时未收到通知,商户后台主动调用查询订单接口,查询订单的当前状态,并反馈给前端展示相应的界面。
if (res.err_msg == "get_brand_wcpay_request:ok") {
//JS API的返回结果get_brand_wcpay_request:ok仅在用户成功完成支付时返回
$.ajax({
type: 'POST',
url: '/WeiXinGz/QueryOrder',
data: {
orderId: jsonData.orderId
},
cache: false,
dataType: 'text',
success: function (jsonData) {
if (jsonData == "ok") {
alert("支付成功", "提示", function () {
alert("页面跳转等业务处理");
});
mui('#pay').button('reset');
} else {
alert("支付失败,请稍后重试!如果收到支付通知,切勿重复支付1!");
mui('#pay').button('reset');
}
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert("支付失败,请稍后重试!如果收到支付通知,切勿重复支付2!");
mui('#pay').button('reset');
}
});
} else if (res.err_msg == "get_brand_wcpay_request:cancel") {
//由于前端交互复杂,get_brand_wcpay_request:cancel或者get_brand_wcpay_request:fail可以统一处理为用户遇到错误或者主动放弃,不必细化区分。
alert("您放弃了支付");
mui('#pay').button('reset');
} else {
//由于前端交互复杂,get_brand_wcpay_request:cancel或者get_brand_wcpay_request:fail可以统一处理为用户遇到错误或者主动放弃,不必细化区分。
alert("支付失败,请稍后重试!如果收到支付通知,切勿重复支付3!");
mui('#pay').button('reset');
}
});
 
(6).微信订单查询接口

  /// <summary>
/// 微信订单查询接口
/// </summary>
/// <param name="orderId">订单编号id</param>
/// <returns></returns>
//[WeixinInternalRequest("无法访问!")]
public ActionResult QueryOrder(string orderId)
{
try
{
//一.先查商户后台的订单状态,判断微信端是否异步通知商户后台了!!!
OrderInfor orderInfor = db.Set<OrderInfor>().Where(a => a.id == orderId).FirstOrDefault();
//判断订单状态
if (orderInfor.status == "")
{
//表示查询成功
return Content("ok");
}
else if (orderInfor == null || orderInfor.status != "")
{
//二.进行调用下面的微信查询api进行查询
//生成随机字符串
string nonceStr = TenPayV3Util.GetNoncestr();
RequestHandler packageReqHandler = new RequestHandler(null);
//设置package订单参数
packageReqHandler.SetParameter("appid", ConfigHelp.AppSettings("AppId")); //公众账号ID
packageReqHandler.SetParameter("mch_id", ConfigHelp.AppSettings("MchId")); //商户号
packageReqHandler.SetParameter("out_trade_no", orderId); //填入商家订单号
packageReqHandler.SetParameter("nonce_str", nonceStr); //随机字符串
string sign = packageReqHandler.CreateMd5Sign("key", ConfigHelp.AppSettings("key"));//参数进行签名
packageReqHandler.SetParameter("sign", sign); //参数中添加签名字符串
string data = packageReqHandler.ParseXML(); //将传的参数转化为XML格式字符串
var result = TenPayV3.OrderQuery(data); //调用订单查询接口
var res = XDocument.Parse(result);
//返回状态码【SUCCESS/FAIL】此字段是通信标识
string return_code = res.Element("xml").Element("return_code").Value;
if (return_code == "SUCCESS")
{
//获取业务结果【交易是否成功(SUCCESS/FAIL)】
string result_code = res.Element("xml").Element("result_code").Value;
if (result_code == "SUCCESS")
{
//交易状态
/**SUCCESS—支付成功
*REFUND—转入退款
*NOTPAY—未支付
*CLOSED—已关闭
*REVOKED—已撤销(刷卡支付)
*USERPAYING--用户支付中
*PAYERROR--支付失败(其他原因,如银行返回失败)
*/
string trade_state = res.Element("xml").Element("trade_state").Value;
if (return_code == "SUCCESS")
{
return Content("ok");
}
}
} }
//未查询到该订单或者该订单交易状态不相符
return Content("error");
}
catch (Exception ex)
{
//抛异常信息,返回异常消息
return Content(ex.Message);
}
}

上述代码中,用到的openid,需要事先获取

 亲测好用,如有问题,可联系QQ 604649488
 
 
 
 
 
 
 
 

.Net版微信支付的更多相关文章

  1. 非微信内如何调起wap版微信支付

    微信支付一直没有出wap版,wap版想用微信支付,只有在微信内调用才可以.今天偶然发现,中国电信一个wap站点, 不在微信内也能调起微信支付,而且官方还提供了一个demo. WAP怎么调起客户端? 在 ...

  2. java版微信支付/查询/撤销

    最近公司接入微信刷卡支付,网上根本没见到很直接的教程(可能眼拙),一直摸滚打爬,加班加点才走通,忍不了必须写一写 微信 刷卡支付/查询/撤销... 必须要有公众号然后去申请,申请自己去看文档,这里主要 ...

  3. 微信支付(.NET版)

    前段时间做了网页版微信支付,遇到很多问题,不过最终还是解决了,现在在这里记录下开发流程以及说明,给其他人一些参考. 一.准备工作     首先肯定得先要开通微信支付功能,之前开通微信支付需要三万的押金 ...

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

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

  5. ecshop微信支付(0923更新)商户支付密钥key的生成与设置

    ECSHOP 微信支付(0923更新)商户支付密钥key的生成与设置 说明:新版微信支付,用户必须授权登录才能支付.需要商家自己设置商户号支付密钥. 申请微信支付手机版部分时需要填写的配置接口地址: ...

  6. JS-SDK微信支付开发攻略

    一.吐槽篇 一个字——坑!两个字——很坑!三个字——非常坑!首先,微信支付接口作为微信开发接口的一部分,竟然有一本书那么厚的官方文档,共36页,更重要的是,这36页还不能把开发的流程说清楚,描述过于分 ...

  7. 在Web应用中接入微信支付的流程之极简清晰版

    在Web应用中接入微信支付的流程之极简清晰版 背景: 在Web应用中接入微信支付,我以为只是调用几个API稍作调试即可. 没想到微信的API和官方文档里隐坑无数,致我抱着怀疑人生的心情悲愤踩遍了丫们布 ...

  8. 微信支付开发(1) JS API支付V3版(转)

    http://www.cnblogs.com/txw1958/p/wxpayv3-jsapi.html 本文介绍微信支付下的jsapi实现流程 前言 微信支付现在分为v2版和v3版,2014年9月10 ...

  9. 在Web应用中接入微信支付的流程之极简清晰版 (转)

    在Web应用中接入微信支付的流程之极简清晰版 背景: 在Web应用中接入微信支付,我以为只是调用几个API稍作调试即可. 没想到微信的API和官方文档里隐坑无数,致我抱着怀疑人生的心情悲愤踩遍了丫们布 ...

随机推荐

  1. 洛谷AT2046 Namori(思维,基环树,树形DP)

    洛谷题目传送门 神仙思维题还是要写点东西才好. 树 每次操作把相邻且同色的点反色,直接这样思考会发现状态有很强的后效性,没办法考虑转移. 因为树是二分图,所以我们转化模型:在树的奇数层的所有点上都有一 ...

  2. [复习]动态dp

    [复习]动态dp 你还是可以认为我原来写的动态dp就是在扯蛋. [Luogu4719][模板]动态dp 首先作为一个\(dp\)题,我们显然可以每次修改之后都进行暴力\(dp\),设\(f[i][0/ ...

  3. linux统计使用最多的10个命令

    # cat /root/.bash_history  | awk '{print $1}' | sort | uniq -c | sort -nr | head history      查看命令历史 ...

  4. 配置日志中显示IP

    package com.demo.conf; import ch.qos.logback.classic.pattern.ClassicConverter; import ch.qos.logback ...

  5. DNA Evolution CodeForces - 828E(树状数组)

    题中有两种操作,第一种把某个位置的字母修改,第二种操作查询与[L, R]内与给出字符串循环起来以后对应位置的字母相同的个数.给出的字符串最大长度是10. 用一个四维树状数组表示 cnt[ATCG的编号 ...

  6. WebService学习总结(一)——WebService的相关概念

    一.序言 大家或多或少都听过 WebService(Web服务),有一段时间很多计算机期刊.书籍和网站都大肆的提及和宣传WebService技术,其中不乏很多吹嘘和做广告的成 分.但是不得不承认的是W ...

  7. 洛谷P1880 石子合并

    经典水题....... 断环为链长度乘二,求前缀和区间DP. #include <cstdio> #include <cstring> #include <algorit ...

  8. A1072. Gas Station

    A gas station has to be built at such a location that the minimum distance between the station and a ...

  9. 基于Senparc.CO2NET 缓存策略扩展的缓存使用方法

    没啥说的,直接上代码 1.缓存  CacheFactory 实现: //---------------------------------------------------------------- ...

  10. Installation failed with message Failed to finalize session: INSTALL_FAILED_TEST_ONLY:installPackageLI.

    这样还不行的话,加 -t吧.