前言
自從微軟更換了CEO以後,微軟的戰略方向有了相當大的變化,不再是那麼封閉,開源了許多東西,擁抱開源社群,.NET實現跨平臺,收購xamarin並免費提供給開發者等等。我本人是很喜歡.net的,並希望.net core能夠崛起。我是從.net core 1.1的時候開始使用的,到現在的.net core 2.0。.net core 2.0比1.1有了一些改變,api也增加了很多,用著更順手了,最近在做asp.net core 對接支付寶,百度了一下沒找到關於core的支付寶支付相關資料,所以便自己研究了一下,把官方的服務端sdk修改成.net standard 2.0的(https://github.com/stulzq/Alipay.AopSdk.Core) ,並且根據官方的Demo成功做出了asp.net core 2.0的PC網站支付Demo,前面寫了篇文章介紹了一下:ASP.NET Core 2.0 使用支付寶PC網站支付,大家可以看一看。今天講的是支付寶當面付中的掃碼支付。
掃碼支付簡單介紹
掃碼支付是支付寶當面付中的一種支付方式,當面付包含條碼支付、掃碼支付、聲波支付。
掃碼支付,指使用者開啟支付寶錢包中的“掃一掃”功能,掃描商家展示在某收銀場景下的二維碼並進行支付的模式。該模式適用於線下實體店支付、面對面支付等場景。
使用示例:
1.某直播平臺充值
2.某視訊網站開通vip
掃碼支付比傳統的跳轉網頁支付方便快捷。
業務流程:
使用步驟:
- 收銀員在商家收銀系統操作生成支付寶訂單,並生成二維碼;
- 使用者登入支付寶錢包,點選首頁“付款-掃碼付”或直接點選“掃一掃”,進入掃一掃介面;
- 使用者掃收銀員提供的二維碼,核對金額,確認支付;
- 使用者付款後商家收銀系統會拿到支付成功或者失敗的結果。
詳細介紹請查閱官方文件:https://docs.open.alipay.com/194
配置
建立一個ASP.NET Core 2.0 MVC 專案
新建一個配置類Config
public class Config
{
// 應用ID,您的APPID
public static string AppId = "";
/// <summary>
/// 合作商戶uid
/// </summary>
public static string Uid = "";
// 支付寶閘道器
public static string Gatewayurl = "https://openapi.alipaydev.com/gateway.do";
// 商戶私鑰,您的原始格式RSA私鑰
public static string PrivateKey = "";
// 支付寶公鑰,檢視地址:https://openhome.alipay.com/platform/keyManage.htm 對應APPID下的支付寶公鑰。
public static string AlipayPublicKey = "";
// 簽名方式
public static string SignType = "RSA2";
// 編碼格式
public static string CharSet = "UTF-8";
}
以下演示均是使用的支付寶沙箱環境,支付寶沙箱環境指的協助開發者進行介面功能開發及主要功能聯調的輔助環境。沙箱環境模擬了開放平臺部分產品的主要功能和主要邏輯,在開發者應用上線稽核前,開發者可以根據自身需求,先在沙箱環境中瞭解、組合和除錯各種開放介面,進行開發調通工作,從而幫助開發者在應用上線稽核完成後,能更快速、更順利的進行線上除錯和驗收工作。
如果在簽約或建立應用前想要進行整合測試,可以使用沙箱環境
配置的詳細說明,請看我前一篇文章:ASP.NET Core 2.0 使用支付寶PC網站支付
ASP.NET Core 2.0 實現掃碼支付
使用的生成二維碼的元件名為
QRCoder
,該元件引用了一個第三方實現的System.Drawing類庫,當面付SDK為Alipay.AopSdk.F2FPay
,已經更新到github和nuget。Nuget: https://www.nuget.org/packages/Alipay.AopSdk.F2FPay/ ,github: https://github.com/stulzq/Alipay.AopSdk.Core
新建控制器FTFPayController
1.生成二維碼Action
/// <summary>
/// 生成支付二維碼
/// </summary>
/// <param name="orderName">訂單名稱</param>
/// <param name="orderAmount">訂單金額</param>
/// <param name="outTradeNo">訂單號</param>
/// <returns></returns>
[HttpGet]
public IActionResult ScanCodeGen(string orderName, string orderAmount, string outTradeNo)
{
AlipayTradePrecreateContentBuilder builder = BuildPrecreateContent(orderName,orderAmount,outTradeNo);
//如果需要接收掃碼支付非同步通知,那麼請把下面兩行註釋代替本行。
//推薦使用輪詢撤銷機制,不推薦使用非同步通知,避免單邊賬問題發生。
AlipayF2FPrecreateResult precreateResult = _serviceClient.tradePrecreate(builder);
//string notify_url = "http://10.5.21.14/Pay/Notify"; //商戶接收非同步通知的地址
//AlipayF2FPrecreateResult precreateResult = serviceClient.tradePrecreate(builder, notify_url);
//以下返回結果的處理供參考。
//payResponse.QrCode即二維碼對於的連結
//將連結用二維碼工具生成二維碼列印出來,顧客可以用支付寶錢包掃碼支付。
var bitmap = new Bitmap(Path.Combine(_hostingEnvironment.WebRootPath, "images/error.png"));
switch (precreateResult.Status)
{
case ResultEnum.SUCCESS:
bitmap.Dispose();
bitmap=RenderQrCode(precreateResult.response.QrCode);
//輪詢訂單結果
//根據業務需要,選擇是否新起執行緒進行輪詢
ParameterizedThreadStart parStart = new ParameterizedThreadStart(LoopQuery);
Thread myThread = new Thread(parStart);
object o = precreateResult.response.OutTradeNo;
myThread.Start(o);
break;
case ResultEnum.FAILED:
Console.WriteLine("生成二維碼失敗:"+ precreateResult.response.Body);
break;
case ResultEnum.UNKNOWN:
Console.WriteLine("生成二維碼失敗:" + (precreateResult.response == null ? "配置或網路異常,請檢查後重試" : "系統異常,請更新外部訂單後重新發起請求"));
break;
}
MemoryStream ms = new MemoryStream();
bitmap.Save(ms, ImageFormat.Png);
byte[] bytes = ms.GetBuffer();
return File(bytes, "image/png");
}
2.構造支付請求資料
/// <summary>
/// 構造支付請求資料
/// </summary>
/// <param name="orderName">訂單名稱</param>
/// <param name="orderAmount">訂單金額</param>
/// <param name="outTradeNo">訂單編號</param>
/// <returns>請求結果集</returns>
private AlipayTradePrecreateContentBuilder BuildPrecreateContent(string orderName,string orderAmount,string outTradeNo)
{
//線上聯調時,請輸入真實的外部訂單號。
if (string.IsNullOrEmpty(outTradeNo))
{
outTradeNo = System.DateTime.Now.ToString("yyyyMMddHHmmss") + "0000" + (new Random()).Next(1, 10000).ToString();
}
AlipayTradePrecreateContentBuilder builder = new AlipayTradePrecreateContentBuilder();
//收款賬號
builder.seller_id = Config.Uid;
//訂單編號
builder.out_trade_no = outTradeNo;
//訂單總金額
builder.total_amount = orderAmount;
//參與優惠計算的金額
//builder.discountable_amount = "";
//不參與優惠計算的金額
//builder.undiscountable_amount = "";
//訂單名稱
builder.subject = orderName;
//自定義超時時間
builder.timeout_express = "5m";
//訂單描述
builder.body = "";
//門店編號,很重要的引數,可以用作之後的營銷
builder.store_id = "test store id";
//操作員編號,很重要的引數,可以用作之後的營銷
builder.operator_id = "test";
//傳入商品資訊詳情
List<GoodsInfo> gList = new List<GoodsInfo>();
GoodsInfo goods = new GoodsInfo();
goods.goods_id = "goods id";
goods.goods_name = "goods name";
goods.price = "0.01";
goods.quantity = "1";
gList.Add(goods);
builder.goods_detail = gList;
//系統商接入可以填此引數用作返傭
//ExtendParams exParam = new ExtendParams();
//exParam.sysServiceProviderId = "20880000000000";
//builder.extendParams = exParam;
return builder;
}
3.渲染二維碼
/// <summary>
/// 渲染二維碼
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
private Bitmap RenderQrCode(string str)
{
QRCodeGenerator.ECCLevel eccLevel = QRCodeGenerator.ECCLevel.L;
using (QRCodeGenerator qrGenerator = new QRCodeGenerator())
{
using (QRCodeData qrCodeData = qrGenerator.CreateQrCode(str, eccLevel))
{
using (QRCode qrCode = new QRCode(qrCodeData))
{
Bitmap bp= qrCode.GetGraphic(20, Color.Black, Color.White,
new Bitmap(Path.Combine(_hostingEnvironment.WebRootPath, "images/alipay.png")), 15);
return bp;
}
}
}
}
4.輪詢支付結果
/// <summary>
/// 輪詢支付結果
/// </summary>
/// <param name="o">訂單號</param>
public void LoopQuery(object o)
{
AlipayF2FQueryResult queryResult = new AlipayF2FQueryResult();
int count = 100;
int interval = 10000;
string outTradeNo = o.ToString();
for (int i = 1; i <= count; i++)
{
Thread.Sleep(interval);
queryResult = _serviceClient.tradeQuery(outTradeNo);
if (queryResult?.Status == ResultEnum.SUCCESS)
{
DoSuccessProcess(queryResult);
return;
}
}
DoFailedProcess(queryResult);
}
/// <summary>
/// 請新增支付成功後的處理
/// </summary>
private void DoSuccessProcess(AlipayF2FQueryResult queryResult)
{
//支付成功,請更新相應單據
Console.WriteLine("掃碼支付成功:商戶訂單號 " + queryResult.response.OutTradeNo);
}
/// <summary>
/// 請新增支付失敗後的處理
/// </summary>
private void DoFailedProcess(AlipayF2FQueryResult queryResult)
{
//支付失敗,請更新相應單據
Console.WriteLine("掃碼支付失敗:商戶訂單號 " + queryResult.response.OutTradeNo);
}
關於支付結果,可以使用輪詢或者非同步通知來獲取,非同步通知我這裡沒條件就沒有演示,但是程式碼已經實現了的,自己可以下載Demo測試。
以上程式碼只是作為演示,實際中使用請根據自己的需求來修改,比如輪詢應該是用專門的一個或多個後臺任務執行緒來輪詢而不是每個訂單開一個執行緒,請注意。
Demo演示
1.生成二維碼
生成二維碼必須提供商戶訂單號、訂單金額、訂單名稱三個引數,不然會出現錯誤。
2.掃描支付
開啟手機支付寶,點開掃一掃
,掃描二維碼完成付款。
如果使用的是沙箱環境,那麼必須下載沙箱版支付寶,使用正式版支付寶掃描沙箱環境api生成的二維碼,會出現二維碼失效,沙箱環境後臺可以下載。沙箱環境必須使用沙箱環境賬號。
(1)掃碼之後:
(2)支付成功:
3.支付結果
通過輪詢獲得的結果:
地址集合
前一篇文章:ASP.NET Core 2.0 使用支付寶PC網站支付
當面付官方文件:https://docs.open.alipay.com/194
Nuget元件:
QRCoder(二維碼生成):https://www.nuget.org/packages/QRCoder/
Alipay.AopSdk.F2FPay(當面付SDK):https://www.nuget.org/packages/Alipay.AopSdk.F2FPay/
支付寶當面付與服務端SDK:https://github.com/stulzq/Alipay.AopSdk.Core
本文Demo:https://github.com/stulzq/Alipay.Demo.PCPayment
如果你覺得對你有用,歡迎來個star!