支付 (Finance.Payment)
支付插件提供了通过各种收款接口(支付宝, 财付通等)收取钱款的功能.
支付插件实现了交易(订单交易, 充值交易等)和收款接口的分离, 收款接口只需要实现统一的接口就能支持各种交易.
支付接口和交易的数据结构
在后台管理支付接口
后台可以添加删除支付接口, 并且可以设置每个支付接口的参数和可以使用的交易.
在后台管理支付交易
后台可以查看发起的交易记录, 每笔交易都有对应的详细日志.
添加新的收款接口
添加收款接口需要继承IPaymentApiHandler
.
以下是收款接口的示例代码, 注意每种收款接口的处理流程都有不同, 需要根据实际情况编写.
以支付宝为例
- 接口数据应该保存商家邮箱和密钥
- 获取支付Html时应该返回跳转到支付宝页面的html
- 调用发货接口时应该调用支付宝提供的发货api
- 同时还需要提供接收支付宝同步和异步通知的控制器
- 收到交易开始, 付款成功等通知后, 应该调用交易管理器通知指定的交易处理
/// <summary> /// 测试接口的处理器 /// </summary> [ExportMany] public class TestApiHandler : IPaymentApiHandler { /// <summary> /// 接口类型 /// </summary> public string Type { get { return "TestApi"; } } /// <summary> /// 编辑中的接口数据 /// </summary> protected ApiData ApiDataEditing = new ApiData(); /// <summary> /// 后台编辑表单创建后的处理 /// </summary> public void OnFormCreated(PaymentApiEditForm form) { form.AddFieldsFrom(ApiDataEditing); } /// <summary> /// 后台编辑表单绑定时的处理 /// </summary> public void OnFormBind(PaymentApiEditForm form, PaymentApi bindFrom) { var apiData = bindFrom.ExtraData.GetOrDefault<ApiData>("ApiData") ?? new ApiData(); ApiDataEditing.PaymentPassword = apiData.PaymentPassword; } /// <summary> /// 后台编辑表单保存时的处理 /// </summary> public void OnFormSubmit(PaymentApiEditForm form, PaymentApi saveTo) { saveTo.ExtraData["ApiData"] = ApiDataEditing; } /// <summary> /// 计算支付手续费 /// </summary> public void CalculatePaymentFee(PaymentApi api, decimal amount, ref decimal paymentFee) { paymentFee = 0; } /// <summary> /// 获取支付Html /// </summary> public void GetPaymentHtml(PaymentTransaction transaction, ref HtmlString html) { var templateManager = Application.Ioc.Resolve<TemplateManager>(); var form = new TestApiPayForm(transaction); form.Bind(); html = new HtmlString(templateManager.RenderTemplate("finance.payment/test_api_pay.html", new { form })); } /// <summary> /// 调用发货接口 /// 发货后自动确认收货 /// </summary> public void SendGoods( PaymentTransaction transaction, string logisticsName, string invoiceNo) { var logManager = Application.Ioc.Resolve<LogManager>(); logManager.LogTransaction(string.Format( "PaymentApi send goods: transaction {0} logisticsName {1} invoiceNo {2}", transaction.Serial, logisticsName, invoiceNo)); var transactionManager = Application.Ioc.Resolve<PaymentTransactionManager>(); transactionManager.Process(transaction.Id, null, PaymentTransactionState.Success); } /// <summary> /// 接口数据 /// </summary> public class ApiData { /// <summary> /// 支付密码 /// </summary> [Required] [StringLength(100, MinimumLength = 5)] [PasswordField("PaymentPassword", "Password required to pay transactions")] public string PaymentPassword { get; set; } /// <summary> /// 检查支付密码是否和设置的密码一致 /// </summary> public void CheckPaymentPassword(string paymentPassword) { if (string.IsNullOrEmpty(PaymentPassword) || paymentPassword != PaymentPassword) { throw new ForbiddenException(new T("Incorrect payment password")); } } } }
添加新的交易类型
添加交易类型需要继承IPaymentTransactionHandler
.
以下是测试交易的示例代码, 注意需要根据每种交易的实际流程进行编写.
以订单交易为例
- 交易创建后: 不需要做特殊的处理, 只需要记录到日志
- 等待付款时: 同上(在这个状态后订单金额应该不允许修改, 否则有可能导致支付金额对不上)
- 担保交易付款后: 修改订单状态为等待发货, 并在发货时调用支付接口的发货接口通知
- 交易成功时:
- 如果之前的交易状态是担保交易付款后, 代表已确认收货, 这时应该设置订单状态为交易成功
- 如果之前的交易状态时交易创建后, 代表已付款, 这时应该设置订单状态为等待发货
- 交易终止时: 这时应该在订单上显示交易已终止提醒后台管理员和用户
- 获取显示交易结果的Html: 显示当前的交易状态, 并在一定时间后跳转到订单详情页
/// <summary> /// 测试交易的处理器 /// </summary> [ExportMany] public class TestTransactionHandler : IPaymentTransactionHandler { /// <summary> /// 交易类型 /// </summary> public string Type { get { return "TestTransaction"; } } /// <summary> /// 交易创建后 /// </summary> public void OnCreated(PaymentTransaction transaction) { var logManager = Application.Ioc.Resolve<LogManager>(); logManager.LogTransaction(string.Format("TestTransaction Created: {0}", transaction.Serial)); } /// <summary> /// 等待付款时 /// </summary> public void OnWaitingPaying( PaymentTransaction transaction, PaymentTransactionState previousState) { var logManager = Application.Ioc.Resolve<LogManager>(); logManager.LogTransaction(string.Format("TestTransaction Waiting Paying: {0}", transaction.Serial)); } /// <summary> /// 担保交易付款后 /// 付款后自动发货 /// </summary> public void OnSecuredPaid(PaymentTransaction transaction, PaymentTransactionState previousState, IList<AutoSendGoodsParameters> parameters) { var logManager = Application.Ioc.Resolve<LogManager>(); logManager.LogTransaction(string.Format("TestTransaction Secured Paid: {0}", transaction.Serial)); parameters.Add(new AutoSendGoodsParameters() { LogisticsName = "TestLogistics", InvoiceNo = "00000000" }); } /// <summary> /// 交易成功时 /// </summary> public void OnSuccess( PaymentTransaction transaction, PaymentTransactionState previousState) { var logManager = Application.Ioc.Resolve<LogManager>(); logManager.LogTransaction(string.Format("TestTransaction Success: {0}", transaction.Serial)); } /// <summary> /// 交易终止时 /// </summary> public void OnAbort( PaymentTransaction transaction, PaymentTransactionState previousState) { var logManager = Application.Ioc.Resolve<LogManager>(); logManager.LogTransaction(string.Format("TestTransaction Aborted: {0}", transaction.Serial)); } /// <summary> /// 获取显示交易结果的Html /// </summary> public void GetResultHtml(PaymentTransaction transaction, IList<HtmlString> html) { var templateManager = Application.Ioc.Resolve<TemplateManager>(); var args = new { serial = transaction.Serial, state = transaction.State.GetDescription() }; html.Add(new HtmlString( templateManager.RenderTemplate("finance.payment/test_transaction_result.html", args))); } }
创建交易
使用交易管理器可以创建交易.
创建交易前应该调用支付接口管理器计算好交易手续费(但一般都不会从付款人收取手续费).
var transactionManager = Application.Ioc.Resolve<PaymentTransactionManager>(); var apiManager = Application.Ioc.Resolve<PaymentApiManager>(); var paymentFee = apiManager.CalculatePaymentFee(api.Id, Amount); var transaction = transactionManager.CreateTransaction( "TestTransaction", api.Id, Amount, paymentFee, Currency, payerId, payeeId, payerId, Description);
完整交易流程(即时到账)
- 创建交易
- 调用
PaymentTransactionManager.CreateTransaction
- 调用
- 跳转到支付页面
- 调用
PaymentTransactionManager.GetPaymentUrl
- 支付接口可以跳转到支付平台上, 也可以提供一个表单在网站内部处理支付
- 支付接口应该设置一个返回页面
- 调用
- 用户支付完成
- 首先回到支付接口的返回页面
- 支付接口通知支付交易处理支付完成
- 处理成功时, 跳转到显示支付结果的页面
- 调用
PaymentTransactionManager.GetResultUrl
- 调用
完整交易流程(担保交易)
- 创建交易
- 调用
PaymentTransactionManager.CreateTransaction
- 调用
- 跳转到支付页面
- 调用
PaymentTransactionManager.GetPaymentUrl
- 支付接口可以跳转到支付平台上, 也可以提供一个表单在网站内部处理支付
- 支付接口应该设置一个返回页面
- 调用
- 用户使用担保交易支付完成
- 首先回到支付接口的返回页面
- 支付接口通知支付交易处理担保交易已付款
- 处理成功时, 跳转到显示支付结果的页面
- 调用
PaymentTransactionManager.GetResultUrl
- 调用
- 等待后台发货
- 后台发货后, 应该调用支付接口的发货api
- 调用
PaymentTransactionRepository.SendGoods
- 调用
- 通知用户确认收货后, 到支付平台上确认收货
- 后台发货后, 应该调用支付接口的发货api
- 用户到支付平台上确认收货
- 支付平台给支付接口发出通知
- 支付接口把交易状态设为交易成功
- 调用
PaymentTransactionRepository.Process
- 调用