【轉】ASP.NET Core 2.0 支付寶當面付之掃碼支付

 

ASP.NET Core 2.0 支付寶當面付之掃碼支付

【轉載】原文地址:https://www.cnblogs.com/stulzq/p/7647948.html

前言

自從微軟更換了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

掃碼支付比傳統的跳轉網頁支付方便快捷。

業務流程:

使用步驟:

  1. 收銀員在商家收銀系統操作生成支付寶訂單,並生成二維碼;
  2. 用戶登錄支付寶錢包,點擊首頁“付款-掃碼付”或直接點擊“掃一掃”,進入掃一掃界面;
  3. 用戶掃收銀員提供的二維碼,覈對金額,確認支付;
  4. 用戶付款後商家收銀系統會拿到支付成功或者失敗的結果。

詳細介紹請查閱官方文檔: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.支付結果

通過輪詢獲得的結果:

地址集合

當面付官方文檔: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

本文Demohttps://github.com/stulzq/Alipay.Demo.PCPayment

 

原文作者:曉晨Master(李志強)

聲明:原創博客請在轉載時保留原文鏈接(https://www.cnblogs.com/stulzq/p/7647948.html)或者在文章開頭加上原文博客地址(https://www.cnblogs.com/stulzq/),凡是轉載文章,不能設置打賞功能,如有特殊需求請與原文作者聯繫!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章