nopCommerce 3.9 大波浪系列 之 可退款的支付寶插件(下)

一.回顧

支付寶插件源碼下載地址:點擊下載      html

    上篇介紹了使用支付寶插件進行支付,全額退款,部分退款還有插件的多店鋪配置,本文介紹下如何實現的。git

二.前期準備

    插件主要有3個功能:web

  1. 多店鋪插件配置 
  2. 支付功能
  3. 退款功能

   數據庫支持:sql

  1. 新增dbl_PaymentInfo表保存支付記錄。
  2. 新增dbl_RefundInfo表保存退款記錄。

   其餘準備:數據庫

  1. 支付寶即時到帳PID和MD5祕鑰Key,可經過支付寶開放平臺獲取。

三.流程規劃 

  1. 插件安裝卸載流程

FX]3{CKOJX5`(@G}_MUO6(D

   2.  支付流程數組

98UDRBDZ2Q_[`8O@%ZIZU_M

   3.  退款流程微信

{EM~~HSKQWNTTT$438(CJJ5

四.建立項目

    1.  新建類庫項目,名稱爲 DaBoLang.Nop.Plugin.Payments.AliPay,位置放在Plugins下app

image_thumb2

    2.  點擊項目快捷鍵Alt+Enter進入項目屬性,框架

         設置輸出路徑爲..\..\Presentation\Nop.Web\Plugins\DaBoLang.Payments.AliPay\ide

image_thumb4

    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

image_thumb6

      最後添加 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. 建立項目目錄,若是你已經下載了插件項目參考項目目錄以下:

image_thumb[1]

  • Alipay  支付寶即時到帳相關
  • Controllers 控制器文件夾
  • Data    數據庫相關
  • Domain 實體類
  • Models  模型
  • Services  服務接口
  • Views  視圖
  • AliPayPaymentProcessor.cs   支付插件實現類
  • AliPayPaymentSettings.cs     支付寶即時到帳配置類
  • DependencyRegistrar.cs     依賴注入擴展類
  • Description.txt   插件描述文件
  • RouteProvider.cs    路由註冊類

五.建立數據庫

     項目咱們已經建立好了。記下來咱們建立付款記錄表和退款記錄表用於保存相關記錄。nop使用的是ORM框架爲Entity Framework框架,咱們使用Code First模式來建立。

image

    1.  在Domain文件夾建立imagePaymentInfo類用於保存付款信息,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 
PaymentInfo
  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 
RefundInfo

    2.  Data文件夾中建立實體與數據庫的映射關係配置

image

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 }
AliPayObjectContext

    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 
AliPayPaymentSettings

最後看下安裝、卸載處理流程

FX]3{CKOJX5`(@G}_MUO6(D

七.插件配置

     接下來咱們開發插件配置並支持多店鋪設置,插件類中GetConfigurationRoute方法用於返回路由信息,咱們定義一個路由到

AliPayController控制器的Configure方法下的路由,Configure用於處理配置。

     Models文件夾中ConfigurationModel.cs類爲模型類,用於溝通控制器與視圖之間的數據。這裏再也不具體討論。

八.支付功能

     98UDRBDZ2Q_[`8O@%ZIZU_M

     付款的時候咱們須要跳轉到支付寶頁面進行操做,所以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         }
PostProcessPayment

     調用支付寶接口時須要設置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 
RouteProvider

     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方法會改變訂單付款狀態,修改訂單退款金額,退款記錄表中修改已退款狀態等等業務邏輯。具體實現請看源代碼。

十.服務類

     Services文件夾用於保存服務類image

     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();

十一.總結

  • 本文經過完整的實例介紹了支付插件接口的擴展。
  • 實現IPaymentMethod接口用於支付插件接口擴展。
  • 經過AliPayObjectContext數據庫上下文擴展來對數據庫表操做。
  • 理解支付寶即時到帳支付與退款流程。
  • 路由註冊、依賴注入註冊。

注意:調試時項目必定要部署在公網中,這樣支付寶才能正確的調用通知地址,不然插件就會失效。

文中有不正確的觀點請指正,若是您以爲本文對您有幫助,請轉載支持

本文地址:http://www.cnblogs.com/yaoshangjin/p/7290003.html

本文爲大波浪原創、轉載請註明出處。

相關文章
相關標籤/搜索