支付宝插件源码下载地址:点击下载
上篇介绍了使用支付宝插件进行支付,全额退款,部分退款还有插件的多店铺配置,本文介绍下如何实现的。
插件主要有3个功能:
数据库支持:
其他准备:
2. 支付流程
3. 退款流程
1. 新建类库项目,名称为 DaBoLang.Nop.Plugin.Payments.AliPay,位置放在Plugins下
2. 点击项目快捷键Alt+Enter进入项目属性,
设置输出路径为..\..\Presentation\Nop.Web\Plugins\DaBoLang.Payments.AliPay\
3. 安装相关包:项目根目录新建packages.config文件,内容如下
1 1.0" encoding="utf-8"?> 23 Autofac" version="4.4.0" targetFramework="net451" /> 4 Microsoft.AspNet.Mvc" version="5.2.3" targetFramework="net451" /> 5 Microsoft.AspNet.Razor" version="3.2.3" targetFramework="net451" /> 6 Microsoft.AspNet.WebPages" version="3.2.3" targetFramework="net451" /> 7 Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net451" /> 8
打开【工具】【NuGet 包管理器】【程序包管理器控制台】,
输入下边命令更新包: Update-Package -ProjectName 'DaBoLang.Nop.Plugin.Payments.AliPay' -Reinstall
最后添加 Nop.Core Nop.Data Nop.Services Nop.Web.FrameWork 项目引用
4. 创建插件描述文件:根目录新建Description.txt文件 用于插件描述,写入内容如下:
1 Group: Payment methods 2 FriendlyName: 支付宝 3 SystemName: DaBoLang.Payments.AliPay 4 Version: 1.00 5 SupportedVersions: 3.90 6 Author: 大波浪 7 DisplayOrder: 1 8 FileName: DaBoLang.Nop.Plugin.Payments.AliPay.dll 9 Description: 支付宝 即时到账 插件,支持支付,退款
5. 创建项目目录,如果你已经下载了插件项目参考项目目录如下:
项目我们已经创建好了。记下来我们创建付款记录表和退款记录表用于保存相关记录。nop使用的是ORM框架为Entity Framework框架,我们使用Code First模式来创建。
1. 在Domain文件夹创建PaymentInfo类用于保存付款信息,RefundInfo类用于保存退款信息。
nop中的实体类需要继承 Nop.Core.BaseEntity 抽象方法。
2. Data文件夹中创建实体与数据库的映射关系配置
nop中映射关系配置需要继承 Nop.Data.Mapping.NopEntityTypeConfiguration
接口,T泛型为映射的实体类 该接口也是继承EntityTypeConfiguration
接口的
PaymentInfoMap类关联PaymentInfo实体类,
构造函数中通过this.ToTable("dbl_PaymentInfo")指定数据库表明,this.HasKey(x => x.Id);指定主键
1 using DaBoLang.Nop.Plugin.Payments.AliPay.Domain; 2 using Nop.Data.Mapping; 3 4 namespace DaBoLang.Nop.Plugin.Payments.AliPay.Data 5 { 6 ///7 /// 命名空间:DaBoLang.Nop.Plugin.Payments.AliPay.Data 8 /// 名 称:PaymentInfoMap 9 /// 功 能:实体映射 10 /// 详 细:支付表映射 11 /// 作 者:大波浪 12 /// 13 public partial class PaymentInfoMap : NopEntityTypeConfiguration 14 { 15 public PaymentInfoMap() 16 { 17 this.ToTable("dbl_PaymentInfo"); 18 this.HasKey(x => x.Id); 19 } 20 } 21 }
RefundInfoMap则将RefundInfo实体类与dbl_RefundInfo表进行关联。
this.Ignore(x=>x.RefundStatus); 表示忽略RefundStatus属性,忽略的属性在创建数据时不会创建该属性字段。
1 using DaBoLang.Nop.Plugin.Payments.AliPay.Domain; 2 using Nop.Data.Mapping; 3 4 namespace DaBoLang.Nop.Plugin.Payments.AliPay.Data 5 { 6 ///7 /// 命名空间:DaBoLang.Nop.Plugin.Payments.AliPay.Data 8 /// 名 称:RefundInfoMap 9 /// 功 能:实体映射 10 /// 详 细:退款记录映射 11 /// 12 public partial class RefundInfoMap : NopEntityTypeConfiguration 13 { 14 public RefundInfoMap() 15 { 16 this.ToTable("dbl_RefundInfo"); 17 this.HasKey(x => x.Id); 18 this.Ignore(x=>x.RefundStatus); 19 } 20 } 21 }
3. 创建数据库上下文
我们已经创建好2个表的实体类和映射关系,接下来我们需要创建DbContext数据库上下文。
在Data目录下创建 AliPayObjectContext类,继承DbContext类和Nop.Data.IDbContext接口
重写OnModelCreating方法,将Map配置添加到EF中,
提示:Nop.Data.NopObjectContext是Nop默认上下文可供,这里也可以使用反射机制自动添加配置,当然需要再提取一次Map接口,有兴趣的朋友自行扩展。
1 protected override void OnModelCreating(DbModelBuilder modelBuilder) 2 { 3 modelBuilder.Configurations.Add(new PaymentInfoMap()); 4 modelBuilder.Configurations.Add(new RefundInfoMap()); 5 6 //disable EdmMetadata generation 7 //modelBuilder.Conventions.Remove(); 8 base.OnModelCreating(modelBuilder); 9 }
添加Install()方法创建表,Uninstall()方法删除表。
1 ///2 /// 安装 3 /// 4 public void Install() 5 { 6 //创建表 7 var dbScript = CreateDatabaseScript(); 8 Database.ExecuteSqlCommand(dbScript); 9 SaveChanges(); 10 } 11 12 /// 13 /// 卸载 14 /// 15 public void Uninstall() 16 { 17 //删除表 18 var tableName = this.GetTableName (); 19 this.DropPluginTable(tableName); 20 tableName = this.GetTableName (); 21 this.DropPluginTable(tableName); 22 }
完整的代码如下
4. 添加EfStartUpTask类用于启动时初数据库进行初始化
1 using System.Data.Entity; 2 using Nop.Core.Infrastructure; 3 4 namespace DaBoLang.Nop.Plugin.Payments.AliPay.Data 5 { 6 ///7 /// 命名空间:DaBoLang.Nop.Plugin.Payments.AliPay.Data 8 /// 名 称:EfStartUpTask 9 /// 功 能:启动任务 10 /// 详 细:启动时数据库初始化从不创建数据库 11 /// 版 本:1.0.0.0 12 /// 作 者:大波浪 13 /// 联系方式:http://www.cnblogs.com/yaoshangjin 14 /// 说 明: 15 /// 16 public class EfStartUpTask : IStartupTask 17 { 18 public void Execute() 19 { 20 //It's required to set initializer to null (for SQL Server Compact). 21 //otherwise, you'll get something like "The model backing the 'your context name' context has changed since the database was created. Consider using Code First Migrations to update the database" 22 Database.SetInitializer (null); 23 } 24 25 public int Order 26 { 27 //ensure that this task is run first 28 get { return 0; } 29 } 30 } 31 } 32
到这里数据库相关类已经创建完毕,但是还不能使用,我们需要将这些类注册到依赖注入框架中。
5. 添加DependencyRegistrar类用于依赖关系注册
1 using Autofac; 2 using Autofac.Core; 3 using DaBoLang.Nop.Plugin.Payments.AliPay.Data; 4 using DaBoLang.Nop.Plugin.Payments.AliPay.Domain; 5 using DaBoLang.Nop.Plugin.Payments.AliPay.Services; 6 using Nop.Core.Configuration; 7 using Nop.Core.Data; 8 using Nop.Core.Infrastructure; 9 using Nop.Core.Infrastructure.DependencyManagement; 10 using Nop.Data; 11 using Nop.Web.Framework.Mvc; 12 13 namespace DaBoLang.Nop.Plugin.Payments.AliPay 14 { 15 ///16 /// 命名空间:DaBoLang.Nop.Plugin.Payments.AliPay 17 /// 名 称:DependencyRegistrar 18 /// 功 能:框架 19 /// 详 细:注册 20 /// 版 本:1.0.0.0 21 /// 作 者:大波浪 22 /// 联系方式:http://www.cnblogs.com/yaoshangjin 23 /// 说 明: 24 /// 25 public class DependencyRegistrar : IDependencyRegistrar 26 { 27 /// 28 /// Register services and interfaces 29 /// 30 /// Container builder 31 /// Type finder 32 /// Config 33 public virtual void Register(ContainerBuilder builder, ITypeFinder typeFinder, NopConfig config) 34 { 35 //数据库上下文 36 this.RegisterPluginDataContext (builder, "nop_object_context_alipay"); 37 38 //为Repository注册数据库上下文 39 builder.RegisterType >() 40 .As >() 41 .WithParameter(ResolvedParameter.ForNamed ("nop_object_context_alipay")) 42 .InstancePerLifetimeScope(); 43 44 builder.RegisterType >() 45 .As >() 46 .WithParameter(ResolvedParameter.ForNamed ("nop_object_context_alipay")) 47 .InstancePerLifetimeScope(); 48 49 50 //注册支付记录服务 51 builder.RegisterType ().As ().InstancePerLifetimeScope(); 52 //注册退款记录服务 53 builder.RegisterType ().As ().InstancePerLifetimeScope(); 54 } 55 56 /// 57 /// Order of this dependency registrar implementation 58 /// 59 public int Order 60 { 61 get { return 1; } 62 } 63 } 64 } 65
好了样数据库相关类就完成了。
现在我们就可以编写支付插件类了,首先创建AliPayPaymentProcessor类,该类继承BasePlugin抽象类,同时继承IPaymentMethod接口。
nop支付插件需要继承Nop.Services.Payments.IpaymentMethod接口,该接口提供了用于支付相关的方法。接口说明请看下边代码注释(理解不正确的地方请留言指正)
1 namespace Nop.Services.Payments 2 { 3 ///4 /// 提供了一个接口用于创建支付网关和方法 5 /// 6 public partial interface IPaymentMethod : IPlugin 7 { 8 #region Methods 9 10 /// 11 /// 付款处理 12 /// 13 /// Payment info required for an order processing 14 /// Process payment result 15 ProcessPaymentResult ProcessPayment(ProcessPaymentRequest processPaymentRequest); 16 17 ///18 /// 请求付款流程(需要重定向到一个第三方的支付网关使用的URL) 19 /// 20 /// Payment info required for an order processing 21 void PostProcessPayment(PostProcessPaymentRequest postProcessPaymentRequest); 22 23 /// 24 /// 是否应该隐藏支付方式,例如购物车内商品不需要配送 25 /// Shoping cart 26 /// true - hide; false - display. 27 bool HidePaymentMethod(IListcart); 28 29 /// 30 /// 额外费用 31 /// 32 /// Shoping cart 33 /// Additional handling fee 34 decimal GetAdditionalHandlingFee(IListcart); 35 36 /// 37 /// 跟踪付款 38 /// 39 /// Capture payment request 40 /// Capture payment result 41 CapturePaymentResult Capture(CapturePaymentRequest capturePaymentRequest); 42 43 ///44 /// 退款 45 /// 46 /// Request 47 /// Result 48 RefundPaymentResult Refund(RefundPaymentRequest refundPaymentRequest); 49 50 ///51 /// Voids a payment 52 /// 53 /// Request 54 /// Result 55 VoidPaymentResult Void(VoidPaymentRequest voidPaymentRequest); 56 57 ///58 /// 定期支付 59 /// 60 /// Payment info required for an order processing 61 /// Process payment result 62 ProcessPaymentResult ProcessRecurringPayment(ProcessPaymentRequest processPaymentRequest); 63 64 ///65 /// 取消定期支付 66 /// 67 /// Request 68 /// Result 69 CancelRecurringPaymentResult CancelRecurringPayment(CancelRecurringPaymentRequest cancelPaymentRequest); 70 71 ///72 /// 订单创建后但支付未成功,是否支持重复支付 73 /// 74 /// Order 75 /// Result 76 bool CanRePostProcessPayment(Order order); 77 78 ///79 /// 配置路由 80 /// 81 /// Action name 82 /// Controller name 83 /// Route values 84 void GetConfigurationRoute(out string actionName, out string controllerName, out RouteValueDictionary routeValues); 85 86 /// 87 /// 支付信息路由 88 /// 89 /// Action name 90 /// Controller name 91 /// Route values 92 void GetPaymentInfoRoute(out string actionName, out string controllerName, out RouteValueDictionary routeValues); 93 94 Type GetControllerType(); 95 96 #endregion 97 98 #region Properties 99 100 /// 101 /// 支持跟踪 102 /// 103 bool SupportCapture { get; } 104 105 /// 106 /// 支持部分退款 107 /// 108 bool SupportPartiallyRefund { get; } 109 110 /// 111 /// 支持退款 112 /// 113 bool SupportRefund { get; } 114 115 /// 116 /// 无效 117 /// 118 bool SupportVoid { get; } 119 120 /// 121 /// 定期付款方式 122 /// 123 RecurringPaymentType RecurringPaymentType { get; } 124 125 /// 126 /// 支付方式 127 /// 128 PaymentMethodType PaymentMethodType { get; } 129 130 /// 131 /// 是否显示插件的付款信息页面 132 /// 133 bool SkipPaymentInfo { get; } 134 135 /// 136 /// 在支付方式页面将显示的支付方式描述 137 /// 138 string PaymentMethodDescription { get; } 139 140 #endregion 141 } 142 }
重写安装、卸载处理方法,AliPayPaymentSettings类用于用于保存插件配置
最后看下安装、卸载处理流程
接下来我们开发插件配置并支持多店铺设置,插件类中GetConfigurationRoute方法用于返回路由信息,我们定义一个路由到
AliPayController控制器的Configure方法下的路由,Configure用于处理配置。
Models文件夹中ConfigurationModel.cs类为模型类,用于沟通控制器与视图之间的数据。这里不再具体讨论。
付款的时候我们需要跳转到支付宝页面进行操作,因此AliPayPaymentProcessor类中
PaymentMethodType返回PaymentMethodType.Redirection告诉nop我们需要到第三方进行支付。
1 public PaymentMethodType PaymentMethodType 2 { 3 get 4 { 5 return PaymentMethodType.Redirection; 6 } 7 }
然后实现PostProcessPayment方法,用于处理支付宝付款。Alipay文件夹里是支付宝辅助类。
调用支付宝接口时需要设置notify_url地址用于接收付款通知,return_url地址用支付成功后支付宝跳转到该地址。
1 notify_url = _webHelper.GetStoreLocation(false) + "Plugins/AliPay/Notify", 2 return_url = _webHelper.GetStoreLocation(false) + "Plugins/AliPay/Return",
RouteProvider.cs类用于注册路由,在这里定义上边两个地址的路由。
Plugins/AliPay/Notify 会调用AliPayController控制器类Notify方法进行处理
Plugins/AliPay/Return 会调用AliPayController控制器类Return方法进行处理
AliPayController->Notify方法用于接收支付宝通知,当支付成功后会调用该方法,该方法中会处理订单支付状态,并在付款信息表中添加付款记录。
AliPayController->Return方法定义返回位置。
支持全额退款,需要插件类SupportRefund属性返回true,支持部分退款需要SupportPartiallyRefund属性返回true。
退款需要实现Refund方法,当退款操作时nop会调用Refund方法,本插件中会新建退款信息保存在退款表中,然后重定向到支付宝进行有密退款。
支付宝接口也需要notify_url地址,用于退款成功后接受通知,我们在前边RouteProvider路由表中定义了退款通知路由,AliPayController控制器RefundNotify方法来处理。
RefundNotify方法会改变订单付款状态,修改订单退款金额,退款记录表中修改已退款状态等等业务逻辑。具体实现请看源代码。
IPaymentInfoService接口:用于对付款记录处理,如对付款表的增删改查等。
IRefundInfoService接口:用于对退款记录的处理,如对退款表的增删改查等操作。
nop使用IRepository
来进行数据处理的封装(Dao层),T为实体类
1 #region 属性 2 private readonly IRepository_refundInfoRepository; 3 #endregion 4 #region 构造 5 public RefundInfoService(IRepository refundInfoRepository) 6 { 7 this._refundInfoRepository = refundInfoRepository; 8 } 9 #endregion
接下来创建接口实现类,PaymentInfoService和RefundInfoService用于实现业务逻辑。
最后我们需要把接口和实现类注册到依赖注入容器中,在DependencyRegistrar文件中进行注册。
1 //注册支付记录服务 2 builder.RegisterType().As ().InstancePerLifetimeScope(); 3 //注册退款记录服务 4 builder.RegisterType ().As ().InstancePerLifetimeScope();
注意:调试时项目一定要部署在公网中,这样支付宝才能正确的调用通知地址,否则插件就会失效。
本文地址:http://www.cnblogs.com/yaoshangjin/p/7290003.html
本文为大波浪原创、转载请注明出处。