微信和支付寶H5支付
最近開發(fā)任務遇到了一個要在手機瀏覽器里面調起微信和支付寶去支付的開發(fā)需求,以前都是做的掃碼支付或者JSAPI都是在軟件內部支付的,沒遇到過在自己瀏覽器內喚醒微信或者支付寶的支付這種開發(fā)在開發(fā)的過程中遇到了許多問題和坑點這里就一一寫進文章里面了
首先放上支付寶和微信支付的開發(fā)文檔
支付寶手機網頁支付 微信H5支付
由于是教程貼我們從一個新建的MVC項目開始

首先編寫一個H5支付的頁面  代碼如下所示:
@{
ViewBag.Title = "H5支付頁";
Layout = null;
}
<body>
<script src="~/Scripts/jquery-3.4.1.js"></script>
<script src="~/Scripts/bootstrap.min.js"></script>
<link href="~/Content/bootstrap.min.css" rel="stylesheet" />
<script src="~/Content/layer/layer.js"></script>
<link href="~/Content/layer/theme/default/layer.css" rel="stylesheet" />
<style>
ul li {
list-style: none;
font-size: 40px;
margin-bottom: 20px;
}
input {
height: 50px !important;
width: 50px !important;
margin-left: 5%;
}
.head {
background-color: darkblue;
height: 100px;
font-size: 40px;
color: white;
text-align: center;
}
.pay {
width: 50%;
margin: 5% 25%;
border: 3px dashed lightgray;
border-radius: 15px;
}
.btncolor {
border-radius: 25px;
background-color: #1E90FF;
color: white;
}
</style>
<form id="alitopay" action="支付寶后臺處理網址" onsubmit="return sure();" method="post">
<div style="text-align:center;margin:50px 0 0 0;font-size:40px">請選擇支付方式</div>
<img src="~/Images/weixin.png" class="pay" value="1" />
<img id="zhifubao" src="~/Images/zhifubao.png" class="pay" value="2" />
<div style="position:fixed;bottom:15%;width:100%;">
<button class="btncolor" style="height:130px;margin:250px 5% 5% 5%;font-size:40px;width:90%;">確認支付</button>
</div>
</form>
</body>
<script>
var paytype = "";
function bordernone() {
$(".pay").css("border", " 3px dashed lightgray");
}
$(".pay").click(function () {
bordernone();
$(this).css("border", "5px solid #1E90FF");
paytype = $(this).attr("value");
});
function sure() {
var Guid = $("#Guid").val();
//paytype = $('input:radio:checked').val();
if (paytype === "" || paytype === undefined) {
layer.msg('<div style="font-size: 40px;margin-top:20px;">' + '請選擇支付方式' + '</div>',
{
area: ["400px", "100px"]
});
return false;
} else if (paytype === 2) {//支付寶
$('#alitopay').submit();
return true;
} else if (paytype == 1) {//微信
$.ajax({
type: 'Post',
url: '微信后臺處理網址',
data: { Guid: Guid },
dataType: 'json',
success: function (result) {
if (result.id > 0) {
window.location.href = result.result;
}
}
});
return false;
}
}
$(document).ready(function () {
$('.pay').trigger("click");
})
</script>
這里我們首先講解微信支付 微信支付為了簡單快捷我們用了盛派的SDK引入步驟如下 1.首先右鍵項目選擇管理Nuget程序包 

需要值得注意的是我們要安裝2個dll一個Senparc.Weixin一個 Senparc.Weixin.TenPay 
安裝完成后我們在跟目錄的web.config添加以下內容
<!-- 微信支付V3 -->
<add key="TenPayV3_MchId" value="你的微信商戶ID" />
<add key="TenPayV3_Key" value="你的微信商戶密鑰" />
<add key="TenPayV3_AppId" value="你的微信平臺APPID" />
<add key="TenPayV3_AppSecret" value="你的微信平臺AppSecret" />
<add key="TenPayV3_TenpayNotify" value="支付完成后回調通知地址" />

配置web.config后我們還要修改Global.asax
//設置全局Debug 狀態(tài)
var isGLobalDebug = true;
//全局設置參數(shù),將被儲存到 Senparc.CO2NET.Config.SenparcSetting
var senparcSetting = SenparcSetting.BuildFromWebConfig(isGLobalDebug);
//也可以通過這種方法在程序任意位置設置全局 Debug 狀態(tài):
//Senparc.CO2NET.Config.IsDebug = isGLobalDebug;
//CO2NET 全局注冊,必須!!
IRegisterService register = RegisterService.Start(senparcSetting).UseSenparcGlobal();
//設置微信 Debug 狀態(tài)
var isWeixinDebug = true;
//全局設置參數(shù),將被儲存到 Senparc.Weixin.Config.SenparcWeixinSetting
var senparcWeixinSetting = SenparcWeixinSetting.BuildFromWebConfig(isWeixinDebug);
//也可以通過這種方法在程序任意位置設置微信的 Debug 狀態(tài):
//Senparc.Weixin.Config.IsDebug = isWeixinDebug;
//微信全局注冊,必須?。? register.UseSenparcWeixin(senparcWeixinSetting, senparcSetting)
.RegisterTenpayV3(senparcWeixinSetting, "XXX公眾號");
//記錄到同一個 SenparcWeixinSettingItem 對象中

此處修改完成后我們就可以動手開始寫controller了 
支付代碼如下:
using Senparc.Weixin.Helpers;
using Senparc.Weixin.TenPay;
using Senparc.Weixin.TenPay.V3;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Web;
using System.Web.Mvc;
namespace TestPay.Controllers
{
public class WeiXinController : Controller
{
private static TenPayV3Info _tenPayV3Info;
public static TenPayV3Info TenPayV3Info
{
get
{
if (_tenPayV3Info == null)
{
var key = TenPayHelper.GetRegisterKey(Senparc.Weixin.Config.SenparcWeixinSetting);
_tenPayV3Info =
TenPayV3InfoCollection.Data[key];
}
return _tenPayV3Info;
}
}
// GET: WeiXin
public ActionResult H5Pay()
{
string total_amout = null;//支付價格
string Guid = GetSerialNumber();//系統(tǒng)內訂單號我這里是生成的時間+隨機數(shù)字//注意不要超過32位否則微信會報錯
// 商品描述
string body = "測試支付";
string openId = "";
var timeStamp = TenPayV3Util.GetTimestamp();//時間戳
var nonceStr = TenPayV3Util.GetNoncestr();
var price = total_amout == null ? 1 : int.Parse(total_amout) * 100;//注意微信支付單位為分所以要100 如果傳1就是1分錢 傳100就是1塊錢
//var ip = Request.Params["REMOTE_ADDR"];
var xmlDataInfo = new TenPayV3UnifiedorderRequestData(TenPayV3Info.AppId, TenPayV3Info.MchId, body, Guid, price, Request.UserHostAddress, TenPayV3Info.TenPayV3Notify, TenPayV3Type.MWEB/*此處無論傳什么,方法內部都會強制變?yōu)镸WEB*/, openId, TenPayV3Info.Key, nonceStr);
var result = TenPayV3.Html5Order(xmlDataInfo);//調用統(tǒng)一訂單接口
//JsSdkUiPackage jsPackage = new JsSdkUiPackage(TenPayV3Info.AppId, timeStamp, nonceStr,);
var package = string.Format("prepay_id={0}", result.prepay_id);//預支付訂單id
string paysign = TenPayV3.GetJsPaySign(TenPayV3Info.AppId, timeStamp, nonceStr, package, TenPayV3Info.Key);//簽名sign
//設置成功頁面(也可以不設置,支付成功后默認返回來源地址)
string returnUrl = "";
string hosturl = Request.Url.Host;
returnUrl =hosturl+ "/WeiXin/PayOK";//支付成功后跳轉網址
var mwebUrl = result.mweb_url;
if (!string.IsNullOrEmpty(returnUrl))
{
mwebUrl += string.Format("&redirect_url={0}", AsUrlData(returnUrl));
}
return Json(new { id = 1, result = mwebUrl });
}
#region url處理
public static string AsUrlData(string data)
{
if (data == null)
{
return null;
}
return Uri.EscapeDataString(data);
}
#endregion
#region 生成隨機流水或
private static long np1 = 0, np2 = 0, np3 = 1; //臨時計算用。
private static object orderFormNumberLock = new object();//線程并行鎖,以保證同一時間點只有一個用戶能夠操作流水號。如果分多個流水號段,放多個鎖,并行壓力可以更好的解決,大家自己想法子擴充吧
private string strOrderNumber = null;//訂單號。
/// <summary>
/// 生成充值流水號格式:8位日期加8位順序號,如2010030200000056。
/// </summary>
public string GetSerialNumber()
{
DateTime now = DateTime.Now;
TimeSpan span = now - DateTime.MinValue;
long tmpDays = span.Days;
long seconds = span.Hours * 3600 + span.Seconds;
StringBuilder sb = new StringBuilder();
Monitor.Enter(orderFormNumberLock); //鎖定資源
if (tmpDays != np1)
{
np1 = tmpDays;
np2 = 0;
np3 = 1;
}
if (np2 != seconds)
{
np2 = seconds;
np3 = 1;
}
sb.Append(Convert.ToString(np1, 16).PadLeft(5, '0') + Convert.ToString(np2, 16).PadLeft(5, '0') + Convert.ToString(np3++, 16).PadLeft(6, '0'));
Monitor.Exit(orderFormNumberLock); //釋放資源
strOrderNumber = sb.ToString();
return strOrderNumber;
}
#endregion
}
}
完成后我們選擇微信支付如下圖所示 
完成微信支付后我們開始支付寶的開發(fā)
同樣支付寶支付我們需要一個AliPay.AopSDK的dll 
我們再次將配置需要的參數(shù)寫入web.config中
<!--應用ID,您的APPID-->
<add key="app_id" value="xxxxxxxxx" />
<add key="gatewayUrl" value="https://openapi.alipay.com/gateway.do" />
<!--商戶私鑰,您的原始格式RSA私鑰-->
<add key="private_key" value="xxxxxxx" />
<!--支付寶公鑰,查看地址:https://openhome.alipay.com/platform/keyManage.htm 對應APPID下的支付寶公鑰。-->
<add key="alipay_public_key" value="xxxxxxx" />
<add key="sign_type" value="RSA2" />
<add key="charset" value="UTF-8" />
controller 如下所示
using Aop.Api;
using Aop.Api.Domain;
using Aop.Api.Request;
using Aop.Api.Response;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace TestPay.Controllers
{
public class AliPayController : Controller
{
// GET: AliPay
[HttpPost]
public void ALiPay()
{
string gatewayUrl = ConfigurationManager.AppSettings["gatewayUrl"].ToString();
string app_id = ConfigurationManager.AppSettings["app_id"].ToString();
string private_key = ConfigurationManager.AppSettings["private_key"].ToString();
string sign_type = ConfigurationManager.AppSettings["sign_type"].ToString();
string alipay_public_key = ConfigurationManager.AppSettings["alipay_public_key"].ToString();
string charset = ConfigurationManager.AppSettings["charset"].ToString();
DefaultAopClient client = new DefaultAopClient(gatewayUrl, app_id,private_key, "json", "1.0", sign_type, alipay_public_key, charset, false);
string hosturl = Request.Url.Host;
// 外部訂單號,商戶網站訂單系統(tǒng)中唯一的訂單號
string out_trade_no = Helper.GetSerialNumber();//系統(tǒng)內訂單號我這里是生成的時間+隨機數(shù)字//注意不要超過64位否則微信會報錯
// 訂單名稱
string subject = "測試支付";
// 付款金額
string total_amout = "0.01";
//string total_amout = "0.01";
// 商品描述
string body = "測試支付";
string quit_url = "";
// 支付中途退出返回商戶網站地址
quit_url = hosturl + "H5pay/Index";
// 組裝業(yè)務參數(shù)model
AlipayTradeWapPayModel model = new AlipayTradeWapPayModel();
model.Body = body;
model.Subject = subject;
model.TotalAmount = total_amout;
model.OutTradeNo = out_trade_no;
model.ProductCode = "QUICK_WAP_WAY";
model.QuitUrl = quit_url;
AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest();
// 設置支付完成同步回調地址
request.SetReturnUrl(hosturl + "H5pay/PayOK");
// 設置支付完成異步通知接收地址
request.SetNotifyUrl(hosturl + "H5pay/Notify_url");
// 將業(yè)務model載入到request
request.SetBizModel(model);
AlipayTradeWapPayResponse response = null;
try
{
response = client.pageExecute(request, null, "post");
Response.Write(response.Body);
}
catch (Exception exp)
{
throw exp;
}
}
}
}
完成后代碼效果如下  
寫完支付后其實還有一步重要的步驟就是支付回調
微信支付回調
#region 微信回調方法
[HttpPost]
public ActionResult PayNotifyUrl()
{
try
{
ResponseHandler resHandler = new ResponseHandler(null);
string return_code = resHandler.GetParameter("return_code");//支付結果
string return_msg = resHandler.GetParameter("return_msg");//支付消息
bool paySuccess = false;
resHandler.SetKey(TenPayV3Info.Key);
//驗證請求是否從微信發(fā)過來(安全)
if (resHandler.IsTenpaySign() && return_code.ToUpper() == "SUCCESS")
{
paySuccess = true;//正確的訂單處理
//直到這里,才能認為交易真正成功了,可以進行數(shù)據(jù)庫操作,但是別忘了返回規(guī)定格式的消息!
}
else
{
paySuccess = false;//錯誤的訂單處理
}
if (paySuccess)
{
/* 這里可以進行訂單處理的邏輯 */
//發(fā)送支付成功的模板消息
try
{
string appId = Senparc.Weixin.Config.SenparcWeixinSetting.WeixinAppId;//與微信公眾賬號后臺的AppId設置保持一致,區(qū)分大小寫。
int total_fee = int.Parse(resHandler.GetParameter("total_fee"));//訂單金額
string out_trade_no = resHandler.GetParameter("out_trade_no");//商戶訂單號
string transaction_id = resHandler.GetParameter("transaction_id");//商戶訂單號
//在此處編寫支付成功的邏輯
}
catch (Exception ex)
{
}
}
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");
}
catch (Exception ex)
{
throw;
}
}
#endregion
支付寶支付回調
#region 支付寶回調方法
[HttpPost]
public ActionResult Notify_url()
{
/* 實際驗證過程建議商戶添加以下校驗。
1、商戶需要驗證該通知數(shù)據(jù)中的out_trade_no是否為商戶系統(tǒng)中創(chuàng)建的訂單號,
2、判斷total_amount是否確實為該訂單的實際金額(即商戶訂單創(chuàng)建時的金額),
3、校驗通知中的seller_id(或者seller_email) 是否為out_trade_no這筆單據(jù)的對應的操作方(有的時候,一個商戶可能有多個seller_id/seller_email)
4、驗證app_id是否為該商戶本身。
*/
Dictionary<string, string> sArray = GetRequestPost();
string gatewayUrl = ConfigurationManager.AppSettings["gatewayUrl"].ToString();
string app_id = ConfigurationManager.AppSettings["app_id"].ToString();
string private_key = ConfigurationManager.AppSettings["private_key"].ToString();
string sign_type = ConfigurationManager.AppSettings["sign_type"].ToString();
string alipay_public_key = ConfigurationManager.AppSettings["alipay_public_key"].ToString();
string charset = ConfigurationManager.AppSettings["charset"].ToString();
if (sArray.Count != 0)
{
bool flag = AlipaySignature.RSACheckV1(sArray, alipay_public_key,charset, sign_type, false);
if (flag)
{
//交易狀態(tài)
//判斷該筆訂單是否在商戶網站中已經做過處理
//如果沒有做過處理,根據(jù)訂單號(out_trade_no)在商戶網站的訂單系統(tǒng)中查到該筆訂單的詳細,并執(zhí)行商戶的業(yè)務程序
//請務必判斷請求時的total_amount與通知時獲取的total_fee為一致的
//如果有做過處理,不執(zhí)行商戶的業(yè)務程序
//注意:
//退款日期超過可退款期限后(如三個月可退款),支付寶系統(tǒng)發(fā)送該交易狀態(tài)通知
string trade_status = Request.Form["trade_status"];
//商戶訂單號
string out_trade_no = Request.Form["out_trade_no"];
//支付寶交易號
string trade_no = Request.Form["trade_no"];
string total_amount = Request.Form["total_amount"];
if (trade_status == "TRADE_FINISHED" || trade_status == "TRADE_SUCCESS")
{
try
{
//在此處編寫你的支付成功的邏輯
return Json("success");
}
catch (Exception e)
{
return Json("fail");
}
}
else if (trade_status == "TRADE_CLOSED")
{
return Json("fail");
}
return Json("success");
}
else
{
return Json("fail");
}
}
return Json("fail");
}
public Dictionary<string, string> GetRequestPost()
{
int i = 0;
Dictionary<string, string> sArray = new Dictionary<string, string>();
NameValueCollection coll;
//coll = Request.Form;
coll = Request.Form;
String[] requestItem = coll.AllKeys;
for (i = 0; i < requestItem.Length; i++)
{
sArray.Add(requestItem[i], Request.Form[requestItem[i]]);
}
return sArray;
}
#endregion
因為很多人私信我弄不明白支付寶的參數(shù)腫么弄,我這里重新在最后講下支付寶支付參數(shù)的設置
1.下載支付寶密鑰生成工具:下載地址  2.運行程序,按下圖所示生成密鑰  配置里面的私鑰就是生成的私鑰 而下面的支付寶公鑰需要在支付寶后臺  將你生成的應用公鑰復制到對應的位置確認后網頁會返回給你一個支付寶公鑰這個支付寶公鑰就是填入配置所需的參數(shù)
 
|