支付寶插件源碼下載地址:點擊下載 html
上篇介紹了使用支付寶插件進行支付,全額退款,部分退款還有插件的多店鋪配置,本文介紹下如何實現的。git
插件主要有3個功能:web
數據庫支持:sql
其餘準備:數據庫
2. 支付流程數組
3. 退款流程微信
1. 新建類庫項目,名稱爲 DaBoLang.Nop.Plugin.Payments.AliPay,位置放在Plugins下app
2. 點擊項目快捷鍵Alt+Enter進入項目屬性,框架
設置輸出路徑爲..\..\Presentation\Nop.Web\Plugins\DaBoLang.Payments.AliPay\ide
3. 安裝相關包:項目根目錄新建packages.config文件,內容以下
1 <?xml version="1.0" encoding="utf-8"?> 2 <packages> 3 <package id="Autofac" version="4.4.0" targetFramework="net451" /> 4 <package id="Microsoft.AspNet.Mvc" version="5.2.3" targetFramework="net451" /> 5 <package id="Microsoft.AspNet.Razor" version="3.2.3" targetFramework="net451" /> 6 <package id="Microsoft.AspNet.WebPages" version="3.2.3" targetFramework="net451" /> 7 <package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net451" /> 8 </packages>
打開【工具】【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 抽象方法。
1 using Nop.Core; 2 using Nop.Core.Domain.Customers; 3 using Nop.Core.Domain.Orders; 4 using System; 5 using System.Collections.Generic; 6 using System.Diagnostics; 7 using System.Globalization; 8 using System.Linq; 9 10 namespace DaBoLang.Nop.Plugin.Payments.AliPay.Domain 11 { 12 /// <summary> 13 /// 命名空間:DaBoLang.Nop.Plugin.Payments.AliPay.Domain 14 /// 名 稱:PaymentInfo 15 /// 功 能:實體類 16 /// 詳 細:付款記錄 17 /// 版 本:1.0.0.0 18 /// 做 者:大波浪 19 /// 聯繫方式:http://www.cnblogs.com/yaoshangjin 20 /// 說 明: 21 /// </summary> 22 public partial class PaymentInfo : BaseEntity 23 { 24 #region Properties 25 public Guid PaymentGuid { get; set; } 26 /// <summary> 27 /// 訂單編號 28 /// </summary> 29 public int OrderId { get; set; } 30 /// <summary> 31 /// 插件SystemName 32 /// </summary> 33 public string Name { get; set; } 34 /// <summary> 35 /// 交易金額 36 /// </summary> 37 public decimal Total { get; set; } 38 /// <summary> 39 /// 訂單編號外部交易號 40 /// </summary> 41 public string Out_Trade_No { get; set; } 42 /// <summary> 43 /// 說明 44 /// </summary> 45 public string Note { get; set; } 46 /// <summary> 47 /// 交易號,內部交易號,支付寶交易號或者微信交易號 48 /// </summary> 49 public string Trade_no { get; set; } 50 /// <summary> 51 /// 第三方交易狀態 52 /// </summary> 53 public string Trade_status { get; set; } 54 /// <summary> 55 /// 收款單位email 56 /// </summary> 57 public string Seller_email { get; set; } 58 /// <summary> 59 /// 收款單位id 60 /// </summary> 61 public string Seller_id { get; set; } 62 /// <summary> 63 /// 付款帳戶id 64 /// </summary> 65 public string Buyer_id { get; set; } 66 /// <summary> 67 /// 付款帳戶email 68 /// </summary> 69 public string Buyer_email { get; set; } 70 /// <summary> 71 /// 內部訂單建立時間 72 /// </summary> 73 public DateTime CreateDateUtc { get; set; } 74 75 #endregion 76 } 77 } 78
1 using Nop.Core; 2 using Nop.Core.Domain.Customers; 3 using Nop.Core.Domain.Orders; 4 using System; 5 using System.Collections.Generic; 6 using System.Diagnostics; 7 using System.Globalization; 8 using System.Linq; 9 10 namespace DaBoLang.Nop.Plugin.Payments.AliPay.Domain 11 { 12 /// <summary> 13 /// 命名空間:DaBoLang.Nop.Plugin.Payments.AliPay.Domain 14 /// 名 稱:RefundInfo 15 /// 功 能:實體類 16 /// 詳 細:退款記錄 17 /// 版 本:1.0.0.0 18 /// 做 者:大波浪 19 /// 聯繫方式:http://www.cnblogs.com/yaoshangjin 20 /// 說 明: 21 /// </summary> 22 public partial class RefundInfo : BaseEntity 23 { 24 25 #region Properties 26 public int OrderId { get; set; } 27 /// <summary> 28 /// 退款狀態 29 /// </summary> 30 public int RefundStatusId { get; set; } 31 /// <summary> 32 /// 退款金額 33 /// </summary> 34 public decimal AmountToRefund { get; set; } 35 public string Seller_Email { get; set; } 36 public string Seller_Id { get; set; } 37 /// <summary> 38 /// 交易號,內部交易號,支付寶交易號或者微信交易號 39 /// </summary> 40 public string Batch_no { get; set; } 41 /// <summary> 42 /// 訂單標號外部交易號 43 /// </summary> 44 public string Out_Trade_No { get; set; } 45 /// <summary> 46 /// 建立時間 47 /// </summary> 48 public DateTime CreateOnUtc { get; set; } 49 /// <summary> 50 /// 退款成功時間 51 /// </summary> 52 public DateTime? RefundOnUtc { get; set; } 53 54 /// <summary> 55 /// 回調ID 56 /// </summary> 57 public string Notify_Id { get; set; } 58 /// <summary> 59 /// 回調類型 60 /// </summary> 61 public string Notify_Type { get; set; } 62 63 public string Result_Details { get; set; } 64 #endregion 65 66 /// <summary> 67 /// 訂單狀態 68 /// </summary> 69 public RefundStatus RefundStatus 70 { 71 get 72 { 73 return (RefundStatus)this.RefundStatusId; 74 } 75 set 76 { 77 this.RefundStatusId = (int)value; 78 } 79 } 80 81 } 82 public enum RefundStatus 83 { 84 /// <summary> 85 /// 申請退款 86 /// </summary> 87 refunding = 10, 88 /// <summary> 89 /// 退款成功 90 /// </summary> 91 refund = 20, 92 /// <summary> 93 /// 取消退款 94 /// </summary> 95 cancel = 30, 96 /// <summary> 97 /// 退款過時 98 /// </summary> 99 overtime = 40, 100 /// <summary> 101 /// 退款失敗 102 /// </summary> 103 error = 50, 104 } 105 } 106
2. Data文件夾中建立實體與數據庫的映射關係配置
nop中映射關係配置須要繼承 Nop.Data.Mapping.NopEntityTypeConfiguration<T> 接口,T泛型爲映射的實體類
該接口也是繼承EntityTypeConfiguration<T>接口的
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 /// <summary> 7 /// 命名空間:DaBoLang.Nop.Plugin.Payments.AliPay.Data 8 /// 名 稱:PaymentInfoMap 9 /// 功 能:實體映射 10 /// 詳 細:支付表映射 11 /// 做 者:大波浪 12 /// </summary> 13 public partial class PaymentInfoMap : NopEntityTypeConfiguration<PaymentInfo> 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 /// <summary> 7 /// 命名空間:DaBoLang.Nop.Plugin.Payments.AliPay.Data 8 /// 名 稱:RefundInfoMap 9 /// 功 能:實體映射 10 /// 詳 細:退款記錄映射 11 /// </summary> 12 public partial class RefundInfoMap : NopEntityTypeConfiguration<RefundInfo> 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<IncludeMetadataConvention>(); 8 base.OnModelCreating(modelBuilder); 9 }
添加Install()方法建立表,Uninstall()方法刪除表。
1 /// <summary> 2 /// 安裝 3 /// </summary> 4 public void Install() 5 { 6 //建立表 7 var dbScript = CreateDatabaseScript(); 8 Database.ExecuteSqlCommand(dbScript); 9 SaveChanges(); 10 } 11 12 /// <summary> 13 /// 卸載 14 /// </summary> 15 public void Uninstall() 16 { 17 //刪除表 18 var tableName = this.GetTableName<PaymentInfo>(); 19 this.DropPluginTable(tableName); 20 tableName = this.GetTableName<RefundInfo>(); 21 this.DropPluginTable(tableName); 22 }
完整的代碼以下
1 using System; 2 using System.Collections.Generic; 3 using System.Data.Entity; 4 using System.Data.Entity.Infrastructure; 5 using Nop.Core; 6 using Nop.Data; 7 using DaBoLang.Nop.Plugin.Payments.AliPay.Domain; 8 9 namespace DaBoLang.Nop.Plugin.Payments.AliPay.Data 10 { 11 /// <summary> 12 /// 命名空間:DaBoLang.Nop.Plugin.Payments.AliPay.Data 13 /// 名 稱:AliPayObjectContext 14 /// 功 能:數據庫 15 /// 詳 細:數據庫上下文 16 /// 版 本:1.0.0.0 17 /// 文件名稱:AliPayObjectContext.cs 18 /// 做 者:大波浪 19 /// 聯繫方式:http://www.cnblogs.com/yaoshangjin 20 /// 說 明: 21 /// </summary> 22 public class AliPayObjectContext : DbContext, IDbContext 23 { 24 #region Ctor 25 26 public AliPayObjectContext(string nameOrConnectionString) 27 : base(nameOrConnectionString) 28 { 29 //((IObjectContextAdapter) this).ObjectContext.ContextOptions.LazyLoadingEnabled = true; 30 } 31 32 #endregion 33 34 #region Utilities 35 36 protected override void OnModelCreating(DbModelBuilder modelBuilder) 37 { 38 modelBuilder.Configurations.Add(new PaymentInfoMap()); 39 modelBuilder.Configurations.Add(new RefundInfoMap()); 40 41 //disable EdmMetadata generation 42 //modelBuilder.Conventions.Remove<IncludeMetadataConvention>(); 43 base.OnModelCreating(modelBuilder); 44 } 45 46 #endregion 47 48 #region Methods 49 50 public string CreateDatabaseScript() 51 { 52 return ((IObjectContextAdapter)this).ObjectContext.CreateDatabaseScript(); 53 } 54 55 public new IDbSet<TEntity> Set<TEntity>() where TEntity : BaseEntity 56 { 57 return base.Set<TEntity>(); 58 } 59 60 /// <summary> 61 /// 安裝 62 /// </summary> 63 public void Install() 64 { 65 //create the table 66 var dbScript = CreateDatabaseScript(); 67 Database.ExecuteSqlCommand(dbScript); 68 SaveChanges(); 69 } 70 71 /// <summary> 72 /// 卸載 73 /// </summary> 74 public void Uninstall() 75 { 76 //刪除表 77 var tableName = this.GetTableName<PaymentInfo>(); 78 this.DropPluginTable(tableName); 79 tableName = this.GetTableName<RefundInfo>(); 80 this.DropPluginTable(tableName); 81 } 82 83 /// <summary> 84 /// Execute stores procedure and load a list of entities at the end 85 /// </summary> 86 /// <typeparam name="TEntity">Entity type</typeparam> 87 /// <param name="commandText">Command text</param> 88 /// <param name="parameters">Parameters</param> 89 /// <returns>Entities</returns> 90 public IList<TEntity> ExecuteStoredProcedureList<TEntity>(string commandText, params object[] parameters) where TEntity : BaseEntity, new() 91 { 92 throw new NotImplementedException(); 93 } 94 95 /// <summary> 96 /// Creates a raw SQL query that will return elements of the given generic type. The type can be any type that has properties that match the names of the columns returned from the query, or can be a simple primitive type. The type does not have to be an entity type. The results of this query are never tracked by the context even if the type of object returned is an entity type. 97 /// </summary> 98 /// <typeparam name="TElement">The type of object returned by the query.</typeparam> 99 /// <param name="sql">The SQL query string.</param> 100 /// <param name="parameters">The parameters to apply to the SQL query string.</param> 101 /// <returns>Result</returns> 102 public IEnumerable<TElement> SqlQuery<TElement>(string sql, params object[] parameters) 103 { 104 throw new NotImplementedException(); 105 } 106 107 /// <summary> 108 /// Executes the given DDL/DML command against the database. 109 /// </summary> 110 /// <param name="sql">The command string</param> 111 /// <param name="doNotEnsureTransaction">false - the transaction creation is not ensured; true - the transaction creation is ensured.</param> 112 /// <param name="timeout">Timeout value, in seconds. A null value indicates that the default value of the underlying provider will be used</param> 113 /// <param name="parameters">The parameters to apply to the command string.</param> 114 /// <returns>The result returned by the database after executing the command.</returns> 115 public int ExecuteSqlCommand(string sql, bool doNotEnsureTransaction = false, int? timeout = null, params object[] parameters) 116 { 117 throw new NotImplementedException(); 118 } 119 120 /// <summary> 121 /// Detach an entity 122 /// </summary> 123 /// <param name="entity">Entity</param> 124 public void Detach(object entity) 125 { 126 if (entity == null) 127 throw new ArgumentNullException("entity"); 128 129 ((IObjectContextAdapter)this).ObjectContext.Detach(entity); 130 } 131 132 #endregion 133 134 #region Properties 135 136 /// <summary> 137 /// Gets or sets a value indicating whether proxy creation setting is enabled (used in EF) 138 /// </summary> 139 public virtual bool ProxyCreationEnabled 140 { 141 get 142 { 143 return this.Configuration.ProxyCreationEnabled; 144 } 145 set 146 { 147 this.Configuration.ProxyCreationEnabled = value; 148 } 149 } 150 151 /// <summary> 152 /// Gets or sets a value indicating whether auto detect changes setting is enabled (used in EF) 153 /// </summary> 154 public virtual bool AutoDetectChangesEnabled 155 { 156 get 157 { 158 return this.Configuration.AutoDetectChangesEnabled; 159 } 160 set 161 { 162 this.Configuration.AutoDetectChangesEnabled = value; 163 } 164 } 165 166 #endregion 167 } 168 }
4. 添加EfStartUpTask類用於啓動時初數據庫進行初始化
1 using System.Data.Entity; 2 using Nop.Core.Infrastructure; 3 4 namespace DaBoLang.Nop.Plugin.Payments.AliPay.Data 5 { 6 /// <summary> 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 /// </summary> 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<AliPayObjectContext>(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 /// <summary> 16 /// 命名空間:DaBoLang.Nop.Plugin.Payments.AliPay 17 /// 名 稱:DependencyRegistrar 18 /// 功 能:框架 19 /// 詳 細:註冊 20 /// 版 本:1.0.0.0 21 /// 做 者:大波浪 22 /// 聯繫方式:http://www.cnblogs.com/yaoshangjin 23 /// 說 明: 24 /// </summary> 25 public class DependencyRegistrar : IDependencyRegistrar 26 { 27 /// <summary> 28 /// Register services and interfaces 29 /// </summary> 30 /// <param name="builder">Container builder</param> 31 /// <param name="typeFinder">Type finder</param> 32 /// <param name="config">Config</param> 33 public virtual void Register(ContainerBuilder builder, ITypeFinder typeFinder, NopConfig config) 34 { 35 //數據庫上下文 36 this.RegisterPluginDataContext<AliPayObjectContext>(builder, "nop_object_context_alipay"); 37 38 //爲Repository註冊數據庫上下文 39 builder.RegisterType<EfRepository<PaymentInfo>>() 40 .As<IRepository<PaymentInfo>>() 41 .WithParameter(ResolvedParameter.ForNamed<IDbContext>("nop_object_context_alipay")) 42 .InstancePerLifetimeScope(); 43 44 builder.RegisterType<EfRepository<RefundInfo>>() 45 .As<IRepository<RefundInfo>>() 46 .WithParameter(ResolvedParameter.ForNamed<IDbContext>("nop_object_context_alipay")) 47 .InstancePerLifetimeScope(); 48 49 50 //註冊支付記錄服務 51 builder.RegisterType<PaymentInfoService>().As<IPaymentInfoService>().InstancePerLifetimeScope(); 52 //註冊退款記錄服務 53 builder.RegisterType<RefundInfoService>().As<IRefundInfoService>().InstancePerLifetimeScope(); 54 } 55 56 /// <summary> 57 /// Order of this dependency registrar implementation 58 /// </summary> 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 /// <summary> 4 /// 提供了一個接口用於建立支付網關和方法 5 /// </summary> 6 public partial interface IPaymentMethod : IPlugin 7 { 8 #region Methods 9 10 /// <summary> 11 /// 付款處理 12 /// </summary> 13 /// <param name="processPaymentRequest">Payment info required for an order processing</param> 14 /// <returns>Process payment result</returns> 15 ProcessPaymentResult ProcessPayment(ProcessPaymentRequest processPaymentRequest); 16 17 /// <summary> 18 /// 請求付款流程(須要重定向到一個第三方的支付網關使用的URL) 19 /// </summary> 20 /// <param name="postProcessPaymentRequest">Payment info required for an order processing</param> 21 void PostProcessPayment(PostProcessPaymentRequest postProcessPaymentRequest); 22 23 /// <summary> 24 /// 是否應該隱藏支付方式,例如購物車內商品不須要配送 25 /// <param name="cart">Shoping cart</param> 26 /// <returns>true - hide; false - display.</returns> 27 bool HidePaymentMethod(IList<ShoppingCartItem> cart); 28 29 /// <summary> 30 /// 額外費用 31 /// </summary> 32 /// <param name="cart">Shoping cart</param> 33 /// <returns>Additional handling fee</returns> 34 decimal GetAdditionalHandlingFee(IList<ShoppingCartItem> cart); 35 36 /// <summary> 37 /// 跟蹤付款 38 /// </summary> 39 /// <param name="capturePaymentRequest">Capture payment request</param> 40 /// <returns>Capture payment result</returns> 41 CapturePaymentResult Capture(CapturePaymentRequest capturePaymentRequest); 42 43 /// <summary> 44 /// 退款 45 /// </summary> 46 /// <param name="refundPaymentRequest">Request</param> 47 /// <returns>Result</returns> 48 RefundPaymentResult Refund(RefundPaymentRequest refundPaymentRequest); 49 50 /// <summary> 51 /// Voids a payment 52 /// </summary> 53 /// <param name="voidPaymentRequest">Request</param> 54 /// <returns>Result</returns> 55 VoidPaymentResult Void(VoidPaymentRequest voidPaymentRequest); 56 57 /// <summary> 58 /// 按期支付 59 /// </summary> 60 /// <param name="processPaymentRequest">Payment info required for an order processing</param> 61 /// <returns>Process payment result</returns> 62 ProcessPaymentResult ProcessRecurringPayment(ProcessPaymentRequest processPaymentRequest); 63 64 /// <summary> 65 /// 取消按期支付 66 /// </summary> 67 /// <param name="cancelPaymentRequest">Request</param> 68 /// <returns>Result</returns> 69 CancelRecurringPaymentResult CancelRecurringPayment(CancelRecurringPaymentRequest cancelPaymentRequest); 70 71 /// <summary> 72 /// 訂單建立後但支付未成功,是否支持重複支付 73 /// </summary> 74 /// <param name="order">Order</param> 75 /// <returns>Result</returns> 76 bool CanRePostProcessPayment(Order order); 77 78 /// <summary> 79 /// 配置路由 80 /// </summary> 81 /// <param name="actionName">Action name</param> 82 /// <param name="controllerName">Controller name</param> 83 /// <param name="routeValues">Route values</param> 84 void GetConfigurationRoute(out string actionName, out string controllerName, out RouteValueDictionary routeValues); 85 86 /// <summary> 87 /// 支付信息路由 88 /// </summary> 89 /// <param name="actionName">Action name</param> 90 /// <param name="controllerName">Controller name</param> 91 /// <param name="routeValues">Route values</param> 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 /// <summary> 101 /// 支持跟蹤 102 /// </summary> 103 bool SupportCapture { get; } 104 105 /// <summary> 106 /// 支持部分退款 107 /// </summary> 108 bool SupportPartiallyRefund { get; } 109 110 /// <summary> 111 /// 支持退款 112 /// </summary> 113 bool SupportRefund { get; } 114 115 /// <summary> 116 /// 無效 117 /// </summary> 118 bool SupportVoid { get; } 119 120 /// <summary> 121 /// 按期付款方式 122 /// </summary> 123 RecurringPaymentType RecurringPaymentType { get; } 124 125 /// <summary> 126 /// 支付方式 127 /// </summary> 128 PaymentMethodType PaymentMethodType { get; } 129 130 /// <summary> 131 /// 是否顯示插件的付款信息頁面 132 /// </summary> 133 bool SkipPaymentInfo { get; } 134 135 /// <summary> 136 /// 在支付方式頁面將顯示的支付方式描述 137 /// </summary> 138 string PaymentMethodDescription { get; } 139 140 #endregion 141 } 142 }
重寫安裝、卸載處理方法,AliPayPaymentSettings類用於用於保存插件配置
1 #region 插件安裝/卸載 2 3 public override void Install() 4 { 5 //配置 6 var settings = new AliPayPaymentSettings 7 { 8 SellerEmail = "", 9 Key = "", 10 Partner = "", 11 AdditionalFee = 0, 12 }; 13 14 _settingService.SaveSetting(settings); 15 16 //安裝數據表 17 _objectContext.Install(); 18 19 //本地化資源 20 this.AddOrUpdatePluginLocaleResource("DaBoLang.Plugins.Payments.AliPay.RedirectionTip", "您將被重定向到支付寶網站完成訂單."); 21 this.AddOrUpdatePluginLocaleResource("DaBoLang.Plugins.Payments.AliPay.SellerEmail", "賣方郵箱"); 22 this.AddOrUpdatePluginLocaleResource("DaBoLang.Plugins.Payments.AliPay.SellerEmail.Hint", "支付寶賣方電子郵箱."); 23 this.AddOrUpdatePluginLocaleResource("DaBoLang.Plugins.Payments.AliPay.Key", "Key"); 24 this.AddOrUpdatePluginLocaleResource("DaBoLang.Plugins.Payments.AliPay.Key.Hint", "輸入 key."); 25 this.AddOrUpdatePluginLocaleResource("DaBoLang.Plugins.Payments.AliPay.Partner", "Partner"); 26 this.AddOrUpdatePluginLocaleResource("DaBoLang.Plugins.Payments.AliPay.Partner.Hint", "輸入 partner."); 27 this.AddOrUpdatePluginLocaleResource("DaBoLang.Plugins.Payments.AliPay.AdditionalFee", "額外費用"); 28 this.AddOrUpdatePluginLocaleResource("DaBoLang.Plugins.Payments.AliPay.AdditionalFee.Hint", "客戶選擇此支付方式將付額外的費用."); 29 this.AddOrUpdatePluginLocaleResource("DaBoLang.Plugins.Payments.AliPay.PaymentMethodDescription", "使用支付寶進行支付"); 30 31 base.Install(); 32 } 33 34 public override void Uninstall() 35 { 36 //配置 37 _settingService.DeleteSetting<AliPayPaymentSettings>(); 38 39 //本地化資源 40 this.DeletePluginLocaleResource("DaBoLang.Plugins.Payments.AliPay.SellerEmail.RedirectionTip"); 41 this.DeletePluginLocaleResource("DaBoLang.Plugins.Payments.AliPay.SellerEmail"); 42 this.DeletePluginLocaleResource("DaBoLang.Plugins.Payments.AliPay.SellerEmail.Hint"); 43 this.DeletePluginLocaleResource("DaBoLang.Plugins.Payments.AliPay.Key"); 44 this.DeletePluginLocaleResource("DaBoLang.Plugins.Payments.AliPay.Key.Hint"); 45 this.DeletePluginLocaleResource("DaBoLang.Plugins.Payments.AliPay.Partner"); 46 this.DeletePluginLocaleResource("DaBoLang.Plugins.Payments.AliPay.Partner.Hint"); 47 this.DeletePluginLocaleResource("DaBoLang.Plugins.Payments.AliPay.AdditionalFee"); 48 this.DeletePluginLocaleResource("DaBoLang.Plugins.Payments.AliPay.AdditionalFee.Hint"); 49 this.DeletePluginLocaleResource("DaBoLang.Plugins.Payments.AliPay.PaymentMethodDescription"); 50 51 //卸載數據表 52 _objectContext.Uninstall(); 53 54 base.Uninstall(); 55 } 56 57 #endregion
1 using Nop.Core.Configuration; 2 3 namespace DaBoLang.Nop.Plugin.Payments.AliPay 4 { 5 /// <summary> 6 /// 命名空間:DaBoLang.Nop.Plugin.Payments.AliPay 7 /// 名 稱:AliPayPaymentSettings 8 /// 功 能:配置類 9 /// 詳 細:支付寶配置 10 /// 版 本:1.0.0.0 11 /// 文件名稱:AliPayPaymentSettings.cs 12 /// 做 者:大波浪 13 /// 聯繫方式:http://www.cnblogs.com/yaoshangjin 14 /// 說 明: 15 /// </summary> 16 public class AliPayPaymentSettings : ISettings 17 { 18 /// <summary> 19 /// 賣家Email 20 /// </summary> 21 public string SellerEmail { get; set; } 22 /// <summary> 23 /// Key 24 /// </summary> 25 public string Key { get; set; } 26 /// <summary> 27 /// PID 28 /// </summary> 29 public string Partner { get; set; } 30 /// <summary> 31 /// 額外費用 32 /// </summary> 33 public decimal AdditionalFee { get; set; } 34 } 35 } 36
最後看下安裝、卸載處理流程
接下來咱們開發插件配置並支持多店鋪設置,插件類中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文件夾裏是支付寶輔助類。
1 public void PostProcessPayment(PostProcessPaymentRequest postProcessPaymentRequest) 2 { 3 var partner = _aliPayPaymentSettings.Partner; 4 5 if (string.IsNullOrEmpty(partner)) 6 throw new Exception("合做身份者ID 不能爲空"); 7 8 var key = _aliPayPaymentSettings.Key; 9 10 if (string.IsNullOrEmpty(key)) 11 throw new Exception("MD5密鑰不能爲空"); 12 13 var sellerEmail = _aliPayPaymentSettings.SellerEmail; 14 15 if (string.IsNullOrEmpty(sellerEmail)) 16 throw new Exception("賣家Email 不能爲空"); 17 18 var customer = _workContext.CurrentCustomer;//當前用戶 19 string username = customer.Username; 20 21 22 //商戶訂單號,商戶網站訂單系統中惟一訂單號,必填 23 string out_trade_no = postProcessPaymentRequest.Order.Id.ToString().Trim();//訂單編號 24 25 //訂單名稱,必填 26 string subject = _storeContext.CurrentStore.Name + ":訂單" + out_trade_no; 27 28 //付款金額,必填 29 string total_fee = postProcessPaymentRequest.Order.OrderTotal.ToString("0.00", CultureInfo.InvariantCulture); 30 31 //商品描述,可空 32 string body = _storeContext.CurrentStore.Name + ":用戶_" + username; 33 34 //支付配置信息 35 var aliPayDirectConfig = new AlipayDirectConfig() 36 { 37 key = _aliPayPaymentSettings.Key, 38 partner = _aliPayPaymentSettings.Partner, 39 seller_email = _aliPayPaymentSettings.SellerEmail, 40 notify_url = _webHelper.GetStoreLocation(false) + "Plugins/AliPay/Notify", 41 return_url = _webHelper.GetStoreLocation(false) + "Plugins/AliPay/Return", 42 sign_type = "MD5", 43 input_charset= "utf-8", 44 }; 45 //把請求參數打包成數組 46 SortedDictionary<string, string> sParaTemp = new SortedDictionary<string, string>(); 47 sParaTemp.Add("service", aliPayDirectConfig.service); 48 sParaTemp.Add("partner", aliPayDirectConfig.partner); 49 sParaTemp.Add("seller_email", aliPayDirectConfig.seller_email); 50 sParaTemp.Add("payment_type", aliPayDirectConfig.payment_type); 51 sParaTemp.Add("notify_url", aliPayDirectConfig.notify_url); 52 sParaTemp.Add("return_url", aliPayDirectConfig.return_url); 53 sParaTemp.Add("_input_charset", aliPayDirectConfig.input_charset); 54 sParaTemp.Add("out_trade_no", out_trade_no); 55 sParaTemp.Add("subject", subject); 56 sParaTemp.Add("body", body); 57 sParaTemp.Add("total_fee", total_fee); 58 //建立支付寶請求 59 var post = AlipaySubmit.BuildRequest(sParaTemp, aliPayDirectConfig, "POST"); 60 post.Post(); 61 62 }
調用支付寶接口時須要設置notify_url地址用於接收付款通知,return_url地址用支付成功後支付寶跳轉到該地址。
1 notify_url = _webHelper.GetStoreLocation(false) + "Plugins/AliPay/Notify", 2 return_url = _webHelper.GetStoreLocation(false) + "Plugins/AliPay/Return",
RouteProvider.cs類用於註冊路由,在這裏定義上邊兩個地址的路由。
1 using System.Web.Mvc; 2 using System.Web.Routing; 3 using Nop.Web.Framework.Mvc.Routes; 4 5 namespace DaBoLang.Nop.Plugin.Payments.AliPay 6 { 7 /// <summary> 8 /// 命名空間:DaBoLang.Nop.Plugin.Payments.AliPay 9 /// 名 稱:RouteProvider 10 /// 功 能:路由 11 /// 詳 細:定義路由 12 /// 版 本:1.0.0.0 13 /// 文件名稱:RouteProvider.cs 14 /// 做 者:大波浪 15 /// 聯繫方式:http://www.cnblogs.com/yaoshangjin 16 /// 說 明: 17 /// </summary> 18 public partial class RouteProvider : IRouteProvider 19 { 20 #region Methods 21 22 public void RegisterRoutes(RouteCollection routes) 23 { 24 //支付通知路由 25 routes.MapRoute("DaBoLang.Plugin.Payments.AliPay.Notify", 26 "Plugins/AliPay/Notify", 27 new { controller = "AliPay", action = "Notify" }, 28 new[] { "DaBoLang.Nop.Plugin.Payments.AliPay.Controllers" } 29 ); 30 31 //支付頁面跳轉同步通知頁面 32 routes.MapRoute("DaBoLang.Plugin.Payments.AliPay.Return", 33 "Plugins/AliPay/Return", 34 new { controller = "AliPay", action = "Return" }, 35 new[] { "DaBoLang.Nop.Plugin.Payments.AliPay.Controllers" } 36 ); 37 38 //退款通知路由 39 routes.MapRoute("Plugin.Payments.AliPay.RefundNotify", 40 "Plugins/AliPay/RefundNotify", 41 new { controller = "AliPay", action = "RefundNotify" }, 42 new[] { "DaBoLang.Nop.Plugin.Payments.AliPay.Controllers" } 43 ); 44 } 45 46 #endregion 47 48 #region Properties 49 50 public int Priority 51 { 52 get 53 { 54 return 0; 55 } 56 } 57 58 #endregion 59 } 60 } 61
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<T>來進行數據處理的封裝(Dao層),T爲實體類
1 #region 屬性 2 private readonly IRepository<RefundInfo> _refundInfoRepository; 3 #endregion 4 #region 構造 5 public RefundInfoService(IRepository<RefundInfo> refundInfoRepository) 6 { 7 this._refundInfoRepository = refundInfoRepository; 8 } 9 #endregion
接下來建立接口實現類,PaymentInfoService和RefundInfoService用於實現業務邏輯。
最後咱們須要把接口和實現類註冊到依賴注入容器中,在DependencyRegistrar文件中進行註冊。
1 //註冊支付記錄服務 2 builder.RegisterType<PaymentInfoService>().As<IPaymentInfoService>().InstancePerLifetimeScope(); 3 //註冊退款記錄服務 4 builder.RegisterType<RefundInfoService>().As<IRefundInfoService>().InstancePerLifetimeScope();
注意:調試時項目必定要部署在公網中,這樣支付寶才能正確的調用通知地址,不然插件就會失效。
本文地址:http://www.cnblogs.com/yaoshangjin/p/7290003.html
本文爲大波浪原創、轉載請註明出處。