C# .NET MVC微信JSAPI支付
经过本人不断翻找资料和百度终于结合一些大佬的经验和思路弄出来一个MVC的微信支付了。
再弄微信支付之前我们需要先有一个商户号,并且开通了微信支付的JSAPI支付功能,也需要申请微信公众号并绑定指定的商户号。
首先我们根据微信官方给出的文档得知我们要在H5调起微信支付的流程如下:
文档的链接 https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_4
在H5中调起微信支付,官方已经明确的告诉了我们我们需要拿到哪些数据才可以调起微信支付的功能,如下:
只有拿到了这些数据并使用微信指定的方法‘WeixinJSBridge.invoke()’通过’getBrandWCPayRequest’来拉起微信支付,当然这些都是最后的步骤。
首先我们需要检测页面是否在微信内置环境中打开,先使用微信官方给的一个插件:
然后在使用如下方法判断:
var ua = navigator.userAgent.toLowerCase();
if (ua.match(/MicroMessenger/i) == “micromessenger”) {
//ios的ua中无miniProgram,但都有MicroMessenger(表示是微信浏览器)//https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
wx.miniProgram.getEnv((res) => {
if (openid == “”) {
getCode();
}
})
} else {
//alert(‘不在微信里’);
}
因为微信的JSAPI支付只能在微信环境下才可以调起支付,在其他的环境中我们无法拉起微信支付的功能。
然后我们要去微信官方下载一个C#的JSAPI支付的示例,然后把其中的business和lib这两个文件夹拷贝到自己的项目里面去
在这之前我们需要在控制器里面自定义一个实体
public class ModelForOrder
{
public string appId { get; set; }
public string timeStamp { get; set; }
public string nonceStr { get; set; }
public string packageValue { get; set; }
public string paySign { get; set; }
public string msg { get; set; }
}
public static string GenerateOutTradeNo1()
{
var ran = new Random();
return string.Format("{0}{1}", DateTime.Now.ToString(“yyyyMMddHHmmss”), ran.Next(999));
}
然后我们再去将js传过来支付的金额传到我们的控制器,再通过微信官方给的方法去获取出我们需要的数据出来
JsApiPay jsApiPay = new JsApiPay();
///
/// 充值
///
///
[HttpPost]
public ActionResult MeterRecharge()
{
var dingdanhao = “”;
object objResult = “”;
string strTotal_fee = Request.Form[“totalfee”];
string strFee = (double.Parse(strTotal_fee) * 100).ToString();
//var openid = “o1FBe1tSXimasQAlsxppBd8zgPuA”;
var openid = Session[“OpenID”].ToString();
//若传递了相关参数,则调统一下单接口,获得后续相关接口的入口参数
jsApiPay.openid = openid;//
jsApiPay.total_fee = int.Parse(strFee);
//JSAPI支付预处理
try
{
var UserID = "123";
string strBody = "WXZF";//商品描述
var weixindanhao = GenerateOutTradeNo1().ToString();
weixindanhao = weixindanhao + UserID.ToString();
WxPayData unifiedOrderResult = jsApiPay.GetUnifiedOrderResult(strBody, weixindanhao);
WxPayData wxJsApiParam = jsApiPay.GetJsApiParameters();//获取H5调起JS API参数,注意,这里引用了官方的demo的方法,由于原方法是返回string的,所以,要对原方法改为下面的代码,代码在下一段
//dingdanhao = WxPayApi.DingDanHao;
dingdanhao = weixindanhao;
Session["DingDanHao"] = dingdanhao;
Session["czjine"] = strTotal_fee;
ModelForOrder aOrder = new ModelForOrder()
{
appId = wxJsApiParam.GetValue("appId").ToString(),
nonceStr = wxJsApiParam.GetValue("nonceStr").ToString(),
packageValue = wxJsApiParam.GetValue("package").ToString(),
paySign = wxJsApiParam.GetValue("paySign").ToString(),
timeStamp = wxJsApiParam.GetValue("timeStamp").ToString(),
msg = "成功下单,正在接入微信支付."
};
objResult = aOrder;
}
catch (Exception ex)
{
ModelForOrder aOrder = new ModelForOrder()
{
appId = "",
nonceStr = "",
packageValue = "",
paySign = "",
timeStamp = "",
msg = "下单失败"
};
objResult = aOrder;
}
return Json(new { objResult, dingdanhao });
}
在这里我们需要将官方的这个JsApiPay实体里面稍微做一些改动,在这我也是参照网络上的模板来弄的,如下:
public JsApiPay(Page page)
{
this.page = page;
}
需要改为:
public JsApiPay()
{
}
由于官方的这个GetJsApiParameters()实例是string类型的,这里我们改成了WxPayData类型的实例
public string GetJsApiParameters()
{
Log.Debug(this.GetType().ToString(), “JsApiPay::GetJsApiParam is processing…”);
WxPayData jsApiParam = new WxPayData();
jsApiParam.SetValue("appId", unifiedOrderResult.GetValue("appid"));
jsApiParam.SetValue("timeStamp", WxPayApi.GenerateTimeStamp());
jsApiParam.SetValue("nonceStr", WxPayApi.GenerateNonceStr());
jsApiParam.SetValue("package", "prepay_id=" + unifiedOrderResult.GetValue("prepay_id"));
jsApiParam.SetValue("signType", "MD5");
jsApiParam.SetValue("paySign", jsApiParam.MakeSign());
string parameters = jsApiParam.ToJson();
Log.Debug(this.GetType().ToString(), "Get jsApiParam : " + parameters);
return parameters;
}
需要改为:
public WxPayData GetJsApiParameters()
{
Log.Debug(this.GetType().ToString(), “JsApiPay::GetJsApiParam is processing…”);
WxPayData jsApiParam = new WxPayData();
jsApiParam.SetValue("appId", unifiedOrderResult.GetValue("appid"));
jsApiParam.SetValue("timeStamp", WxPayApi.GenerateTimeStamp());
jsApiParam.SetValue("nonceStr", WxPayApi.GenerateNonceStr());
jsApiParam.SetValue("package", "prepay_id=" + unifiedOrderResult.GetValue("prepay_id"));
jsApiParam.SetValue("signType", "MD5");
jsApiParam.SetValue("paySign", jsApiParam.MakeSign());
string parameters = jsApiParam.ToJson();
Log.Debug(this.GetType().ToString(), "Get jsApiParam : " + parameters);
return jsApiParam;
}
最后还要改一个地方,JsApiPay.cs 将其中的GetUnifiedOrderResult这个方法改成下面这样的格式,因为微信官方给的订单号是根据时间戳来拼接的,所以在同一时间发起微信支付生成的订单号是重复的,所以我们需要自己弄一个不会重复的订单号,最好是拼接上用户的id之类的,另外一个传的是金额我们不动他,如下:
public WxPayData GetUnifiedOrderResult(string strBody,string weixindanhao)
{
WxPayData data = new WxPayData();
data.SetValue(“body”, strBody);
data.SetValue(“attach”, “test”);
data.SetValue(“out_trade_no”, weixindanhao);
data.SetValue(“total_fee”, total_fee);
data.SetValue(“time_start”, DateTime.Now.ToString(“yyyyMMddHHmmss”));
data.SetValue(“time_expire”, DateTime.Now.AddMinutes(10).ToString(“yyyyMMddHHmmss”));
data.SetValue(“goods_tag”, “test”);
data.SetValue(“trade_type”, “JSAPI”);
data.SetValue(“openid”, openid);
WxPayData result = WxPayApi.UnifiedOrder(data);
if (!result.IsSet(“appid”) || !result.IsSet(“prepay_id”) || result.GetValue(“prepay_id”).ToString() == “”)
{
Log.Error(this.GetType().ToString(), “UnifiedOrder response error!”);
throw new WxPayException(“UnifiedOrder response error!”);
}
unifiedOrderResult = result;
return result;
}
弄完这些我们还需要再lib文件夹的 DemoConfig这个示例里面的一些私人数据,比如商户号,商户密匙,公众号APPID 和公众号密匙AppSecret等等,
弄完这些差不多就可以实现调起微信支付了,这里需要特别声明这里支付的结果回调官方给了特别说明,不能保证绝对的正确,所以我们需要再去根据商户订单号或者微信支付号再去查询回调结果保证数据不会出现错误。
在这里我们需要用到两个引用,这个需要自己去网上找一下,
支付成功微信会将我们的支付结果给到我们那个回调的路径里面,所以我们需要根据那个回调路径拿到支付完成的结果,并将我们支付成功的结果返回给微信官方,否在微信会一直回调这个路径10次才停止回调,如下 huidiao() 是我这里支付成功的回调路径:
#region 微信回调方法
[HttpPost]
[ValidateInput(false)]
public ActionResult huidiao()
{
var xml = "";
try
{
ResponseHandler resHandler = new ResponseHandler(null);
string return_msg = resHandler.GetParameter("return_msg");//返回结果
string result_code = resHandler.GetParameter("result_code");//业务结果
string return_code = resHandler.GetParameter("return_code");//返回状态吗
string jine = resHandler.GetParameter("total_fee");
string out_trade_no = resHandler.GetParameter("out_trade_no");//商户订单号
string WEXIN_OPENID = resHandler.GetParameter("openid");//微信openid
string transaction_id = resHandler.GetParameter("transaction_id");//微信支付单号
ZhiFuHuiDiao1 zfhd = ZhiFuHuiDiao1.GetZhiFuHuiDiao1();
string bit = zfhd.UpdateHuiDiao1(jine, out_trade_no, transaction_id, return_code);
if (bit == "支付成功")
{
xml = string.Format(@"<xml><return_code><![CDATA[SUCCESS]]></return_code> <return_msg><![CDATA[OK]]></return_msg></xml>", return_code, return_msg);
}
return Content(xml, "text/xml");
}
catch (Exception ex)
{
dqwj(ex.ToString());
//throw;
}
return Json("11s", JsonRequestBehavior.AllowGet);
}
#endregion
在这里我还遇到了一个问题,就是当用户长时间未支付,等了将近7分钟才支付的,这样微信可能拿不到回调结果,所以我们要给一个时间端,如果超过了3分钟我们就撤销订单,让用户查询下单:
///
/// 订单超时,撤销订单
///
///
public ActionResult cxdingdan()
{
try
{
var dingdanhao = Session[“DingDanHao”].ToString();
string out_trade_no = dingdanhao.Trim();
WxPayData data = new WxPayData();
data.SetValue(“out_trade_no”, out_trade_no);//授权码
WxPayData result = WxPayApi.CloseOrder(data, 10); //提交被扫支付,接收返回结果
//如果提交被扫支付接口调用失败,则抛异常
if (!result.IsSet(“return_code”) || result.GetValue(“return_code”).ToString() == “FAIL”)
{
string returnMsg = result.IsSet(“return_msg”) ? result.GetValue(“return_msg”).ToString() : “”;
Log.Error(“MicroPay”, "Micropay API interface call failure, result : " + result.ToXml());
throw new WxPayException("Micropay API interface call failure, return_msg : " + returnMsg);
}
var zt = result;
return Json(zt, JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
var dingdanhao = Session[“DingDanHao”].ToString();
return Json(dingdanhao, JsonRequestBehavior.AllowGet);
//throw;
}
}