首先,咱們先看下效果,如下是服務端的收款二維碼的發起示例演示:html
其次,咱們再看看手機端 微信掃碼支付的演示:前端
咱們手機端會將收款的消息推送到服務器API。其中接口信息定義大概以下:java
{"title":"微信支付","time":"2020-05-08 23:34:11","money":"0.80","deviceid":"mydevice","content":"[2條]微信支付: 微信支付收款0.80元(朋友到店)"}
android
以上視頻是讓你們有個效果感觀,下面咱們將詳細講解具體實現原理與細節。git
若是您對本專題有興趣,能夠按照下面的思路實現。程序員
同時,您也能夠在 文章結尾處 查看獲取源碼的方法 供用於學習研究用途的 完整源碼ZIP。github
源碼ZIP包括:web
1、主源碼-服務端Api (基於.net core webapi,用於處理支付邏輯)sql
2、前端基於boostrap的發起二維碼掃碼界面UIapi
3、Android Apk 源碼 (java,用於監控手機消息)
4、apk (編譯完成可用的apk, 若是你不熟悉android,能夠直接用這個已經編譯好的apk )
API處理源碼示例以下,
using System; using System.Linq; using System.Linq.Expressions; using System.Collections.Generic; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Authorization; using Lyn.Pay.Api.Domain; using Lyn.Pay.Api.Utils; using Lyn.Pay.Api.DAL; namespace Lyn.Pay.Api.Controllers { /// <summary> /// 控制器 /// </summary> [Route("v1/[controller]/[action]")] public class PayController : Controller { //https://github.com/stulzq/snowflake-net private static Snowflake.Core.IdWorker worker = new Snowflake.Core.IdWorker(1, 1); public PayController() { } #region 業務應用 [HttpPost] [AllowAnonymous] public JsonResult QueryAlreadyBuy([FromBody]AddOrderVo vo) { var remoteUserIp = HttpContext.Connection.RemoteIpAddress.MapToIPv4().ToString(); var userIp = vo.IP.HasValue() ? vo.IP : remoteUserIp; //檢查這個IP是否已經購買過此文章了 var alreadyBuy = Service.QueryAny("SELECT 1 FROM [ORDER] d WHERE d.TradeProduct = @TradeProduct AND d.IP = @IP AND d.TradeStatus=1 ", new { TradeProduct = vo.ProductName, IP = userIp }); if (alreadyBuy) { return Json(Result.Fail(ResultCode.AlreadyBuy)); } else { return Json(Result.Fail(ResultCode.Fail)); } } /// <summary> /// 產生新支付訂單 /// </summary> /// <param name="vo">訂單</param> /// <returns>列表數據</returns> [HttpPost] [AllowAnonymous] public JsonResult AddOrder([FromBody]AddOrderVo vo) { var remoteUserIp = HttpContext.Connection.RemoteIpAddress.MapToIPv4().ToString(); var userIp = vo.IP.HasValue() ? vo.IP : remoteUserIp; //檢查這個IP是否已經購買過此文章了 var alreadyBuy = Service.QueryAny("SELECT 1 FROM [ORDER] d WHERE d.TradeProduct = @TradeProduct AND d.IP = @IP AND d.TradeStatus=1 ", new { TradeProduct = vo.ProductName, IP = userIp }); if (alreadyBuy) { return Json(Result.Fail(ResultCode.AlreadyBuy)); } var money = 0; var sqlGetValidMoney = @" SELECT TOP 1 * FROM Product p WHERE (p.[NAME]=@ProductName OR p.[NAME]='Gobal') AND NOT EXISTS( SELECT 1 FROM [ORDER] d WHERE (d.TradeProduct = p.[NAME] OR p.[NAME]='Gobal') AND d.TradeMoney = p.Money AND d.TradeStatus=0 ) ORDER BY p.IsGobal ASC , p.Money ASC "; var r = new Order(); //先將過時的更新爲過時狀態 Service.Execute("UPDATE [ORDER] SET TradeStatus=3,ModifyTime=GETDATE() WHERE TradeStatus=0 AND datediff(ss,CreateTime,GETDATE())>120", null); var p = Service.QuerySingle<Product>(sqlGetValidMoney, new { ProductName = vo.ProductName }); if (p != null) { r.Id = worker.NextId(); long.TryParse(r.Id.ToString().Substring(1), out long shortid); r.Id = shortid; r.TradeNo = r.Id.ToString();// vo.TradeNo; r.TradeProduct = vo.ProductName; r.TradeMoney = p.Money; r.TradeStatus = 0; r.Remark = remoteUserIp; r.IP = userIp; r.City = vo.City; r.CreateTime = DateTime.Now; Service.Execute("INSERT INTO [ORDER](Id,TradeNo,TradeProduct,TradeMoney,Remark,IP,City,TradeStatus,CreateTime)VALUES(@Id,@TradeNo,@TradeProduct,@TradeMoney,@Remark,@IP,@City,@TradeStatus,@CreateTime)" , new { Id = r.Id, TradeNo = r.TradeNo, TradeProduct = r.TradeProduct, TradeMoney = r.TradeMoney, Remark = r.Remark, IP = r.IP, City = r.City, TradeStatus = r.TradeStatus, CreateTime = r.CreateTime }); money = p.Money; } if (money > 0) { return Json(Result.Success(new { TradeNo = r.Id, Money = money , MoneyYuan = money/100.0, PayQRCode = $"/PayQRCode/{money}.jpg" })); } else { return Json(Result.Fail(ResultCode.Fail)); } } /// <summary> /// 產生新支付訂單 /// </summary> /// <param name="vo">訂單</param> /// <returns>列表數據</returns> [HttpPost] [AllowAnonymous] public JsonResult DiscardOrder([FromBody]DiscardOrderVo vo) { //先將過時的更新爲過時狀態 Service.Execute("UPDATE [ORDER] SET TradeStatus=3,ModifyTime=GETDATE() WHERE TradeStatus=0 AND datediff(ss,CreateTime,GETDATE())>120", null); var d = Service.QuerySingle<Order>("SELECT * FROM [ORDER] WHERE TradeNo = @TradeNo", new { TradeNo = vo.TradeNo }); if (d != null && d.TradeStatus == 0) { Service.Execute("UPDATE [ORDER] SET TradeStatus=2,ModifyTime=GETDATE() WHERE TradeStatus=0 AND TradeNo = @TradeNo", new { TradeNo = vo.TradeNo }); return Json(Result.Success()); } return Json(Result.Fail()); } /// <summary> /// 查詢訂單 /// </summary> /// <param name="vo">訂單</param> /// <returns>列表數據</returns> [HttpPost] [AllowAnonymous] public JsonResult QueryOrder([FromBody]DiscardOrderVo vo) { var d = Service.QuerySingle<Order>("SELECT * FROM [ORDER] WHERE TradeNo = @TradeNo", new { TradeNo = vo.TradeNo }); if (d != null && d.TradeStatus == 1) { return Json(Result.Success()); } return Json(Result.Fail()); } [HttpPost] [AllowAnonymous] public JsonResult PayNotify([FromBody]PayNotifyVo vo) { if (vo.title.IndexOf("微信支付")>=0 && vo.money.HasValue()) { //{"title":"微信支付","time":"2019-06-19 21:45:23","money":"0.10","encrypt":"0","deviceid":"ffffffff-c818-83fb-ffff-ffffbbd87511","content":"微信支付收款0.10元(朋友到店)"} var moneyFen = Convert.ToInt32(decimal.Parse(vo.money) * 100); Service.Execute("UPDATE [ORDER] SET TradeStatus=1,ModifyTime=GETDATE() WHERE TradeStatus=0 AND TradeMoney = @TradeMoney", new { TradeMoney = moneyFen }); } if (vo.title.IndexOf("微信收款助手")>=0) { //{ "title":"微信收款助手","time":"2019-06-20 22:24:54","money":"null","deviceid":"ffffffff-c818-83fb-ffff-ffffbbd87511","content":"[店員消息]收款到帳0.01元"} var content = vo.content; var money = content.Substring(content.IndexOf("收款到帳"), content.IndexOf("元") - content.IndexOf("收款到帳")).Replace("收款到帳", ""); var moneyFen = Convert.ToInt32(decimal.Parse(money) * 100); Service.Execute("UPDATE [ORDER] SET TradeStatus=1,ModifyTime=GETDATE() WHERE TradeStatus=0 AND TradeMoney = @TradeMoney", new { TradeMoney = moneyFen }); } return Json(Result.Success()); } #endregion } }
下圖是支付回調的發起與結果的接收示例:
細節原理請仔細往下看.....
做爲一名程序員,咱們或多或少都但願創建本身的我的技術網站、技術博客等等,用於記錄本身的汗水點滴。
同時,若是咱們但願爲本身的網站增添微信掃碼收款功能,用於收取一些服務費用,爲我的網站提供自動化有償服務的話,那咱們有哪些方案呢?
1、註冊公司,在微信公衆平臺申請支付權限
2、經過微信我的收款碼實現我的收款接口
本文咱們分析第二種方法,經過微信我的收款碼實現我的收款接口。
這種方法的實現成本很是低,但也只是適用於一些我的網站,小併發量的收款服務,固然了,若是你的網站有大量用戶向你支付,你還不主動去申請註冊公司麼。
言歸正傳,哪麼怎麼實現收款接口呢?
首先,咱們看一個演示示例:
可複製連接打開體驗 http://letyouknow.net/serverfarm/serverfarm-tutorial3.html
此示例是技術文章內容付費示例,用戶試讀部分後,點擊 展開閱讀更多 而且掃碼支付成功後,展現所有內容。
首先,咱們須要製做出一套專業的UI,用於展現收款碼
1、當咱們點擊展開閱讀更多按鈕後,咱們須要顯示一獲取二維碼的示意圖
2、根據預設的資費狀況,從後臺拉取對應的我的收款二維碼,並設置收款碼有效期,此示例默認2分鐘。
3、設置超時失效機制,引導從新發起支付
4、預設我的收款二維碼
咱們須要將同一個金額照不一樣的收款備註或不一樣的金額尾數設置多個,而後保存到服務端,由前端UI的產品拉取對應的金額的二維碼圖片,顯示給用戶
5、微信收款通知 回調服務器API
咱們能夠用android apk 用於監控收款通知,並實時回調咱們的服務器,修改用戶訂單的支付狀態。
咱們將apk安裝在手機上後,當有用戶掃碼付款後,咱們的微信APP便收到收款通知,同時,咱們回調服務器。
此方案特性:
這種實現辦法適合小額,支付頻率不高的場景。好比針對 1元這個金額生成了100個有不一樣收款備註信息的二維碼,那麼也就是說5分鐘內最多隻能有100我的同時支付,1分鐘內20個同時支付。對於一些小網站能夠知足需求。
此方案的核心是設計思想,另外就是咱們如何實時獲取到收款通知。咱們用android apk 用於監控收款通知,並實時回調咱們的服務器,修改用戶訂單的支付狀態。有關實時獲取收款通知的實現方法,咱們後續將另起一個篇章重點介紹。
6、接口定義示例
{"title":"微信支付","time":"2020-05-08 23:34:11","money":"0.80","deviceid":"mydevice","content":"[2條]微信支付: 微信支付收款0.80元(朋友到店)"}
7、api源碼的說明
下載源碼後,用vs2017or2019打開項目,F5運行便可,
http://localhost:54914/Demo.html
using System; using System.Linq; using System.Linq.Expressions; using System.Collections.Generic; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Authorization; using Lyn.Pay.Api.Domain; using Lyn.Pay.Api.Utils; using Lyn.Pay.Api.DAL; namespace Lyn.Pay.Api.Controllers { /// <summary> /// 控制器 /// </summary> [Route("v1/[controller]/[action]")] public class PayController : Controller { //https://github.com/stulzq/snowflake-net private static Snowflake.Core.IdWorker worker = new Snowflake.Core.IdWorker(1, 1); public PayController() { } #region 業務應用 [HttpPost] [AllowAnonymous] public JsonResult QueryAlreadyBuy([FromBody]AddOrderVo vo) { var remoteUserIp = HttpContext.Connection.RemoteIpAddress.MapToIPv4().ToString(); var userIp = vo.IP.HasValue() ? vo.IP : remoteUserIp; //檢查這個IP是否已經購買過此文章了 var alreadyBuy = Service.QueryAny("SELECT 1 FROM [ORDER] d WHERE d.TradeProduct = @TradeProduct AND d.IP = @IP AND d.TradeStatus=1 ", new { TradeProduct = vo.ProductName, IP = userIp }); if (alreadyBuy) { return Json(Result.Fail(ResultCode.AlreadyBuy)); } else { return Json(Result.Fail(ResultCode.Fail)); } } /// <summary> /// 產生新支付訂單 /// </summary> /// <param name="vo">訂單</param> /// <returns>列表數據</returns> [HttpPost] [AllowAnonymous] public JsonResult AddOrder([FromBody]AddOrderVo vo) { var remoteUserIp = HttpContext.Connection.RemoteIpAddress.MapToIPv4().ToString(); var userIp = vo.IP.HasValue() ? vo.IP : remoteUserIp; //檢查這個IP是否已經購買過此文章了 var alreadyBuy = Service.QueryAny("SELECT 1 FROM [ORDER] d WHERE d.TradeProduct = @TradeProduct AND d.IP = @IP AND d.TradeStatus=1 ", new { TradeProduct = vo.ProductName, IP = userIp }); if (alreadyBuy) { return Json(Result.Fail(ResultCode.AlreadyBuy)); } var money = 0; var sqlGetValidMoney = @" SELECT TOP 1 * FROM Product p WHERE (p.[NAME]=@ProductName OR p.[NAME]='Gobal') AND NOT EXISTS( SELECT 1 FROM [ORDER] d WHERE (d.TradeProduct = p.[NAME] OR p.[NAME]='Gobal') AND d.TradeMoney = p.Money AND d.TradeStatus=0 ) ORDER BY p.IsGobal ASC , p.Money ASC "; var r = new Order(); //先將過時的更新爲過時狀態 Service.Execute("UPDATE [ORDER] SET TradeStatus=3,ModifyTime=GETDATE() WHERE TradeStatus=0 AND datediff(ss,CreateTime,GETDATE())>120", null); var p = Service.QuerySingle<Product>(sqlGetValidMoney, new { ProductName = vo.ProductName }); if (p != null) { r.Id = worker.NextId(); long.TryParse(r.Id.ToString().Substring(1), out long shortid); r.Id = shortid; r.TradeNo = r.Id.ToString();// vo.TradeNo; r.TradeProduct = vo.ProductName; r.TradeMoney = p.Money; r.TradeStatus = 0; r.Remark = remoteUserIp; r.IP = userIp; r.City = vo.City; r.CreateTime = DateTime.Now; Service.Execute("INSERT INTO [ORDER](Id,TradeNo,TradeProduct,TradeMoney,Remark,IP,City,TradeStatus,CreateTime)VALUES(@Id,@TradeNo,@TradeProduct,@TradeMoney,@Remark,@IP,@City,@TradeStatus,@CreateTime)" , new { Id = r.Id, TradeNo = r.TradeNo, TradeProduct = r.TradeProduct, TradeMoney = r.TradeMoney, Remark = r.Remark, IP = r.IP, City = r.City, TradeStatus = r.TradeStatus, CreateTime = r.CreateTime }); money = p.Money; } if (money > 0) { return Json(Result.Success(new { TradeNo = r.Id, Money = money , MoneyYuan = money/100.0, PayQRCode = $"/PayQRCode/{money}.jpg" })); } else { return Json(Result.Fail(ResultCode.Fail)); } } /// <summary> /// 產生新支付訂單 /// </summary> /// <param name="vo">訂單</param> /// <returns>列表數據</returns> [HttpPost] [AllowAnonymous] public JsonResult DiscardOrder([FromBody]DiscardOrderVo vo) { //先將過時的更新爲過時狀態 Service.Execute("UPDATE [ORDER] SET TradeStatus=3,ModifyTime=GETDATE() WHERE TradeStatus=0 AND datediff(ss,CreateTime,GETDATE())>120", null); var d = Service.QuerySingle<Order>("SELECT * FROM [ORDER] WHERE TradeNo = @TradeNo", new { TradeNo = vo.TradeNo }); if (d != null && d.TradeStatus == 0) { Service.Execute("UPDATE [ORDER] SET TradeStatus=2,ModifyTime=GETDATE() WHERE TradeStatus=0 AND TradeNo = @TradeNo", new { TradeNo = vo.TradeNo }); return Json(Result.Success()); } return Json(Result.Fail()); } /// <summary> /// 查詢訂單 /// </summary> /// <param name="vo">訂單</param> /// <returns>列表數據</returns> [HttpPost] [AllowAnonymous] public JsonResult QueryOrder([FromBody]DiscardOrderVo vo) { var d = Service.QuerySingle<Order>("SELECT * FROM [ORDER] WHERE TradeNo = @TradeNo", new { TradeNo = vo.TradeNo }); if (d != null && d.TradeStatus == 1) { return Json(Result.Success()); } return Json(Result.Fail()); } [HttpPost] [AllowAnonymous] public JsonResult PayNotify([FromBody]PayNotifyVo vo) { if (vo.title.IndexOf("微信支付")>=0 && vo.money.HasValue()) { //{"title":"微信支付","time":"2019-06-19 21:45:23","money":"0.10","encrypt":"0","deviceid":"ffffffff-c818-83fb-ffff-ffffbbd87511","content":"微信支付收款0.10元(朋友到店)"} var moneyFen = Convert.ToInt32(decimal.Parse(vo.money) * 100); Service.Execute("UPDATE [ORDER] SET TradeStatus=1,ModifyTime=GETDATE() WHERE TradeStatus=0 AND TradeMoney = @TradeMoney", new { TradeMoney = moneyFen }); } if (vo.title.IndexOf("微信收款助手")>=0) { //{ "title":"微信收款助手","time":"2019-06-20 22:24:54","money":"null","deviceid":"ffffffff-c818-83fb-ffff-ffffbbd87511","content":"[店員消息]收款到帳0.01元"} var content = vo.content; var money = content.Substring(content.IndexOf("收款到帳"), content.IndexOf("元") - content.IndexOf("收款到帳")).Replace("收款到帳", ""); var moneyFen = Convert.ToInt32(decimal.Parse(money) * 100); Service.Execute("UPDATE [ORDER] SET TradeStatus=1,ModifyTime=GETDATE() WHERE TradeStatus=0 AND TradeMoney = @TradeMoney", new { TradeMoney = moneyFen }); } return Json(Result.Success()); } #endregion } }
8、源碼ZIP僅供用於學習與研究用途
1、主源碼-服務端Api (基於.net core webapi,用於處理支付邏輯)
2、前端基於boostrap的發起二維碼掃碼界面UI
3、Android Apk 源碼 (java,用於監控手機消息)
4、apk (編譯完成可用的apk, 若是你不熟悉android,能夠直接用這個已經編譯好的apk )
9、如何獲取源碼?
掃碼關注的dotNet框架學苑公衆號,直接在公衆號文章中付費閱讀對應的文章,文章尾部有源碼壓縮包。