基于个⼈收款码的⽀付接⼝的实现与源码
前⾔
如果我们希望为⾃⼰的⽹站增添扫码收款功能,⽤于收取⼀些服务费⽤,为个⼈⽹站提供⾃动化有偿服务的话,那我们有哪些⽅案呢?
⾸先,我们先看下效果,以下是服务端的收款⼆维码的发起⽰例演⽰:
其次,我们再看看⼿机端扫码⽀付的演⽰:
我们⼿机端会将收款的消息推送到服务器API。其中接⼝信息定义⼤概如下:
{"title":"⽀付","time":"2020-05-08 23:34:11","money":"0.80","deviceid":"mydevice","content":"[2条]⽀付: ⽀付收款0.80元(朋友到店)"} 以上视频是让⼤家有个效果感观,下⾯我们将详细讲解具体实现原理与细节。
如果您对本专题有兴趣,可以按照下⾯的思路实现。
同时,您也可以在⽂章结尾处查看获取源码的⽅法供⽤于学习研究⽤途的完整源码ZIP。
源码ZIP包括:
⼀、主源码-服务端Api (基于 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
{
//github/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 TradeSt
atus=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.) * 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
}
}
下图是⽀付回调的发起与结果的接收⽰例:
细节原理请仔细往下看.....
作为⼀名程序员,我们或多或少都希望建⽴⾃⼰的个⼈技术⽹站、技术博客等等,⽤于记录⾃⼰的汗⽔点滴。
同时,如果我们希望为⾃⼰的⽹站增添扫码收款功能,⽤于收取⼀些服务费⽤,为个⼈⽹站提供⾃动化有偿服务的话,那我们有哪些⽅案呢?
⼀、注册公司,在公众平台申请⽀付权限
⼆、通过个⼈收款码实现个⼈收款接⼝
本⽂我们分析第⼆种⽅法,通过个⼈收款码实现个⼈收款接⼝。
这种⽅法的实现成本⾮常低,但也只是适⽤于⼀些个⼈⽹站,⼩并发量的收款服务,当然了,如果你的⽹站有⼤量⽤户向你⽀付,你还不主动去申请注册公司么。
⾔归正传,哪么怎么实现收款接⼝呢?
⾸先,我们看⼀个演⽰⽰例:
此⽰例是技术⽂章内容付费⽰例,⽤户试读部分后,点击展开阅读更多并且扫码⽀付成功后,展⽰全部内容。
⾸先,我们需要制作出⼀套专业的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源码的说明
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
{
//github/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)

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。