前言 如果我們希望為自己的網站增添微信掃碼收款功能,用于收取一些服務費用,為個人網站提供自動化有償服務的話,那我們有哪些方案呢?
?首先,我們先看下效果,以下是服務端的收款二維碼的發起示例演示:
其次,我們再看看手機端 微信掃碼支付的演示:
我們手機端會將收款的訊息推送到服務器API,其中介面資訊定義大概如下:
{"title":"微信支付","time":"2020-05-08 23:34:11","money":"0.80","deviceid":"mydevice","content":"[2條]微信支付: 微信支付收款0.80元(朋友到店)"}
以上視頻是讓大家有個效果感觀,下面我們將詳細講解具體實作原理與細節,
如果您對本專題有興趣,可以按照下面的思路實作,
同時,您也可以在 文章結尾處 查看獲取原始碼的方法 供用于學習研究用途的 完整原始碼ZIP,
原始碼ZIP包括:
一、主原始碼-服務端Api (基于.net core webapi,用于處理支付邏輯)
二、前端基于boostrap的發起二維碼掃碼界面UI
三、Android Apk 原始碼 (java,用于監控手機訊息)
四、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
}
}
下圖是支付回呼的發起與結果的接收示例:
細節原理請仔細往下看.....
作為一名程式員,我們或多或少都希望建立自己的個人技術網站、技術博客等等,用于記錄自己的汗水點滴,
同時,如果我們希望為自己的網站增添微信掃碼收款功能,用于收取一些服務費用,為個人網站提供自動化有償服務的話,那我們有哪些方案呢?
一、注冊公司,在微信公眾平臺申請支付權限
二、通過微信個人收款碼實作個人收款介面
本文我們分析第二種方法,通過微信個人收款碼實作個人收款介面,
這種方法的實作成本非常低,但也只是適用于一些個人網站,小并發量的收款服務,當然了,如果你的網站有大量用戶向你支付,你還不主動去申請注冊公司么,
言歸正傳,哪么怎么實作收款介面呢?
首先,我們看一個演示示例:
可復制鏈接打開體驗 http://letyouknow.net/serverfarm/serverfarm-tutorial3.html
此示例是技術文章內容付費示例,用戶試讀部分后,點擊 展開閱讀更多 并且掃碼支付成功后,展示全部內容,
首先,我們需要制作出一套專業的UI,用于展示收款碼
一、當我們點擊展開閱讀更多按鈕后,我們需要顯示一獲取二維碼的示意圖
二、根據預設的資費情況,從后臺拉取對應的個人收款二維碼,并設定收款碼有效期,此示例默認2分鐘,
三、設定超時失效機制,引導重新發起支付
四、預設個人收款二維碼
我們需要將同一個金額照不同的收款備注或不同的金額尾數設定多個,然后保存到服務端,由前端UI的產品拉取對應的金額的二維碼圖片,顯示給用戶
五、微信收款通知 回呼服務器API
我們可以用android apk 用于監控收款通知,并實時回呼我們的服務器,修改用戶訂單的支付狀態,
我們將apk安裝在手機上后,當有用戶掃碼付款后,我們的微信APP便收到收款通知,同時,我們回呼服務器,
此方案特性:
這種實作辦法適合小額,支付頻率不高的場景,比如針對 1元這個金額生成了100個有不同收款備注資訊的二維碼,那么也就是說5分鐘內最多只能有100個人同時支付,1分鐘內20個同時支付,對于一些小網站可以滿足需求,
此方案的核心是設計思想,另外就是我們如何實時獲取到收款通知,我們用android apk 用于監控收款通知,并實時回呼我們的服務器,修改用戶訂單的支付狀態,有關實時獲取收款通知的實作方法,我們后續將另起一個篇章重點介紹,
六、介面定義示例
{"title":"微信支付","time":"2020-05-08 23:34:11","money":"0.80","deviceid":"mydevice","content":"[2條]微信支付: 微信支付收款0.80元(朋友到店)"}
七、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
}
}
八、原始碼ZIP僅供用于學習與研究用途
一、主原始碼-服務端Api (基于.net core webapi,用于處理支付邏輯)
二、前端基于boostrap的發起二維碼掃碼界面UI
三、Android Apk 原始碼 (java,用于監控手機訊息)
四、apk (編譯完成可用的apk, 如果你不熟悉android,可以直接用這個已經編譯好的apk )
九、如何獲取原始碼?
掃碼關注的dotNet框架學苑公眾號,直接在公眾號文章中付費閱讀對應的文章,文章尾部有原始碼壓縮包,
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/33234.html
標籤:.NET Core
上一篇:docker部署netcore專案 nginx負載均衡
下一篇:Jenkins之Nunit的應用
