重要通知:BrnShop企业版NOSQL设计(基于Redis)已经开源!源码内置于最新版的BrnShop中,感兴趣的园友可以去下载来看看。官网地址:www.brnshop.com

  好了现在进入今天的正题:自定义插件。上一讲中我们已经阐述了BrnShop插件的工作机制,现在我们详细介绍下如何自定义插件。首先BrnShop的插件从功能上分为三类,分别是:

  • 开放授权插件(OAuth)
  • 支付插件
  • 配送插件

  对应的接口文件(注:位于BrnShop.Core项目的Plugin/Base文件夹中)依次如下:

  • IOAuthPlugin
  • IPayPlugin
  • IShipPlugin

  现在我们依次介绍下各个接口,首先登场的是IOAuthPlugin接口。先看它的定义:

    /// <summary>
/// BrnShop开放授权插件接口
/// </summary>
public interface IOAuthPlugin : IPlugin
{
/// <summary>
/// 登陆控制器
/// </summary>
string LoginController { get; } /// <summary>
/// 登陆动作方法
/// </summary>
string LoginAction { get; } /// <summary>
/// 登陆路由数据
/// </summary>
RouteValueDictionary LoginRouteValues { get; }
}

  对于一个开放授权插件来说,它只需要向主应用程序提供自己的一个登陆地址就可以,至于怎么授权验证等那都是插件自己的事情了。所以IOAuthPlugin接口内容仅仅是登陆地址的mvc3要素(控制器名,动作方法名,路由数据)就可以了,以QQ授权登陆为例:

  

  接下来是IPayPlugin接口,代码如下:

/// <summary>
/// BrnShop支付插件接口
/// </summary>
public interface IPayPlugin : IPlugin
{
/// <summary>
/// 付款方式(0代表货到付款,1代表在线付款,2代表线下付款)
/// </summary>
int PayMode { get; }

/// <summary>
/// 是否允许账户充值(只对在线付款有效)
/// </summary>
bool AllowRecharge { get; }

/// <summary>
/// 支付返回控制器
/// </summary>
string ReturnController { get; }

/// <summary>
/// 支付返回动作方法
/// </summary>
string ReturnAction { get; }

/// <summary>
/// 支付返回路由数据
/// </summary>
RouteValueDictionary ReturnRouteValues { get; }

/// <summary>
/// 支付通知控制器
/// </summary>
string NotifyController { get; }

/// <summary>
/// 支付通知动作方法
/// </summary>
string NotifyAction { get; }

/// <summary>
/// 支付通知路由数据
/// </summary>
RouteValueDictionary NotifyRouteValues { get; }

/// <summary>
/// 获得支付手续费
/// </summary>
/// <param name="productAmount">商品合计</param>
/// <param name="buyTime">购买时间</param>
/// <param name="partUserInfo">购买用户</param>
/// <returns></returns>
decimal GetPayFee(decimal productAmount, DateTime buyTime, PartUserInfo partUserInfo);

/// <summary>
/// 如果付款方式为在线付款则返回付款请求的url,否则返回空字符串
/// </summary>
/// <param name="returnUrl">返回url</param>
/// <param name="notifyUrl">通知url</param>
/// <param name="pluginInfo">插件信息</param>
/// <param name="partUserInfo">购买用户</param>
/// <param name="orderInfo">订单信息</param>
/// <returns></returns>
string GetRequestUrl(string returnUrl, string notifyUrl, PluginInfo pluginInfo, PartUserInfo partUserInfo, OrderInfo orderInfo);
}

/// <summary>
/// BrnShop支付插件接口
/// </summary>
public interface IPayPlugin : IPlugin
{
/// <summary>
/// 付款方式(0代表货到付款,1代表在线付款,2代表线下付款)
/// </summary>
int PayMode { get; } /// <summary>
/// 是否允许账户充值(只对在线付款有效)
/// </summary>
bool AllowRecharge { get; } /// <summary>
/// 支付返回控制器
/// </summary>
string ReturnController { get; } /// <summary>
/// 支付返回动作方法
/// </summary>
string ReturnAction { get; } /// <summary>
/// 支付返回路由数据
/// </summary>
RouteValueDictionary ReturnRouteValues { get; } /// <summary>
/// 支付通知控制器
/// </summary>
string NotifyController { get; } /// <summary>
/// 支付通知动作方法
/// </summary>
string NotifyAction { get; } /// <summary>
/// 支付通知路由数据
/// </summary>
RouteValueDictionary NotifyRouteValues { get; } /// <summary>
/// 获得支付手续费
/// </summary>
/// <param name="productAmount">商品合计</param>
/// <param name="buyTime">购买时间</param>
/// <param name="partUserInfo">购买用户</param>
/// <returns></returns>
decimal GetPayFee(decimal productAmount, DateTime buyTime, PartUserInfo partUserInfo); /// <summary>
/// 如果付款方式为在线付款则返回付款请求的url,否则返回空字符串
/// </summary>
/// <param name="returnUrl">返回url</param>
/// <param name="notifyUrl">通知url</param>
/// <param name="pluginInfo">插件信息</param>
/// <param name="partUserInfo">购买用户</param>
/// <param name="orderInfo">订单信息</param>
/// <returns></returns>
string GetRequestUrl(string returnUrl, string notifyUrl, PluginInfo pluginInfo, PartUserInfo partUserInfo, OrderInfo orderInfo);
}

  成员比较多,我们分类来看就清晰了:

  • PayMode:代表支付的3种类型,这个属性非常重要,因为其它成员的实现跟他密切相关。
  • GetPayFee:计算订单的支付手续费。
  • GetRequestUrl:只在PayMode为在线付款时有效;返回支付地址,在BrnShop.Web项目的OrderController类的PayShow方法中调用。
  • ReturnController,ReturnAction,ReturnRouteValues:这3个成员为一组,并且只在PayMode为在线付款时有效;提供支付完成后的返回地址。
  • NotifyController,NotifyAction,NotifyRouteValues:这3个成员为一组,并且只在PayMode为在线付款时有效;提供支付完成后的回调地址。
  • AllowRecharge:系统预留成员,目前无用。

  最后我们来看下IShipPlugin接口,代码如下:

    /// <summary>
/// BrnShop配送插件接口
/// </summary>
public interface IShipPlugin : IPlugin
{
/// <summary>
/// 获得配送费用
/// </summary>
/// <param name="totalWeight">订单总重量</param>
/// <param name="productAmount">商品合计</param>
/// <param name="buyTime">购买时间</param>
/// <param name="shipRegionId">收货区域</param>
/// <param name="partUserInfo">购买用户</param>
/// <returns></returns>
decimal GetShipFee(int totalWeight, decimal productAmount, DateTime buyTime, int shipRegionId, PartUserInfo partUserInfo);
}

  这个接口比较简单,只是提供一个计算配送费用的成员。

  补充说明一下:以上3个接口都继承自IPlugin,这个接口提供后台配置地址mvc3要素,只在BrnShop.Web.Admin项目中的插件配置视图中使用,具体如下:

    @if (Model.ConfigRouteValues == null)
{
@Html.Action(Model.ConfigAction, Model.ConfigController)
}
else
{
@Html.Action(Model.ConfigAction, Model.ConfigController, Model.ConfigRouteValues)
}

  下面我们以支付宝插件为例来讲解下如何自定义一个插件。

   首先新建一个ASP.NET MVC3应用程序并引用需要的程序集,根据上一篇讲解我们知道需要修改非.net自带程序集的复制到本地属性和项目生成属性。具体如下图:

  至此我们的项目基本框架已经搭好了。现在我们需要添加一个插件说明文件(注:此文件为必须文件且文件名称必须为PluginInfo.config,还有不要忘记修改它的复制属性)。内容如下:

<?xml version="1.0" encoding="utf-8"?>
<PluginInfo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<SystemName>alipay</SystemName>
<FriendlyName>支付宝</FriendlyName>
<ClassFullName>BrnShop.PayPlugin.Alipay.PluginService,BrnShop.PayPlugin.Alipay</ClassFullName>
<Folder>BrnShop.PayPlugin.Alipay</Folder>
<Description>阿里巴巴旗下支付工具</Description>
<Type>1</Type>
<Author>brnshop</Author>
<Version>1.0</Version>
<SupVersion>1.0.0</SupVersion>
<DisplayOrder>3</DisplayOrder>
<IsDefault>0</IsDefault>
</PluginInfo>

  这个文件是BrnShop.Core项目中的PluginInfo序列化文件,所以节点说明请参考下面代码:

/// <summary>
/// 插件信息类
/// </summary>
public class PluginInfo : IComparable
{
private string _systemname = "";//插件系统名称(必须具有唯一性)
private string _friendlyname = "";//插件友好名称
private string _classfullname = "";//插件控制器
private string _folder = "";//插件目录
private string _description = "";//插件描述
private int _type = 0;//插件类型(0代表开放授权插件,1代表支付插件,2代表配送插件)
private string _author = "";//插件作者
private string _version = "";//插件版本
private string _supversion = "";//插件支持的BrnShop版本
private int _displayOrder = 0;//插件顺序
private int _isdefault = 0;//是否是默认插件

/// <summary>
/// 插件系统名称
/// </summary>
public string SystemName
{
get { return _systemname; }
set { _systemname = value; }
}

/// <summary>
/// 插件友好名称
/// </summary>
public string FriendlyName
{
get { return _friendlyname; }
set { _friendlyname = value; }
}

/// <summary>
/// 插件类型名称
/// </summary>
public string ClassFullName
{
get { return _classfullname; }
set { _classfullname = value; }
}

/// <summary>
/// 插件目录
/// </summary>
public string Folder
{
get { return _folder; }
set { _folder = value; }
}

/// <summary>
/// 插件描述
/// </summary>
public string Description
{
get { return _description; }
set { _description = value; }
}

/// <summary>
/// 插件类型(0代表开放授权插件,1代表支付插件,2代表配送插件)
/// </summary>
public int Type
{
get { return _type; }
set { _type = value; }
}

/// <summary>
/// 插件作者
/// </summary>
public string Author
{
get { return _author; }
set { _author = value; }
}

/// <summary>
/// 插件版本
/// </summary>
public string Version
{
get { return _version; }
set { _version = value; }
}

/// <summary>
/// 插件支持的BrnShop版本
/// </summary>
public string SupVersion
{
get { return _supversion; }
set { _supversion = value; }
}

/// <summary>
/// 插件顺序
/// </summary>
public int DisplayOrder
{
get { return _displayOrder; }
set { _displayOrder = value; }
}

/// <summary>
/// 是否是默认插件
/// </summary>
public int IsDefault
{
get { return _isdefault; }
set { _isdefault = value; }
}

/// <summary>
/// 插件实例
/// </summary>
private IPlugin _instance = null;

/// <summary>
/// 插件实例
/// </summary>
[XmlIgnoreAttribute]
public IPlugin Instance
{
get
{
if (_instance == null)
{
try
{
_instance = (IPlugin)Activator.CreateInstance(System.Type.GetType(ClassFullName, false, true));
}
catch (Exception ex)
{
throw new BSPException("创建插件:" + _classfullname + "的实例失败", ex);
}
}
return _instance;
}
}

public int CompareTo(object obj)
{
PluginInfo info = (PluginInfo)obj;

if (this.DisplayOrder > info.DisplayOrder)
return 1;
else if (this.DisplayOrder < info.DisplayOrder)
return -1;
else
return 0;
}

}

/// <summary>
/// 插件信息类
/// </summary>
public class PluginInfo : IComparable
{
private string _systemname = "";//插件系统名称(必须具有唯一性)
private string _friendlyname = "";//插件友好名称
private string _classfullname = "";//插件控制器
private string _folder = "";//插件目录
private string _description = "";//插件描述
private int _type = 0;//插件类型(0代表开放授权插件,1代表支付插件,2代表配送插件)
private string _author = "";//插件作者
private string _version = "";//插件版本
private string _supversion = "";//插件支持的BrnShop版本
private int _displayOrder = 0;//插件顺序
private int _isdefault = 0;//是否是默认插件 /// <summary>
/// 插件系统名称
/// </summary>
public string SystemName
{
get { return _systemname; }
set { _systemname = value; }
} /// <summary>
/// 插件友好名称
/// </summary>
public string FriendlyName
{
get { return _friendlyname; }
set { _friendlyname = value; }
} /// <summary>
/// 插件类型名称
/// </summary>
public string ClassFullName
{
get { return _classfullname; }
set { _classfullname = value; }
} /// <summary>
/// 插件目录
/// </summary>
public string Folder
{
get { return _folder; }
set { _folder = value; }
} /// <summary>
/// 插件描述
/// </summary>
public string Description
{
get { return _description; }
set { _description = value; }
} /// <summary>
/// 插件类型(0代表开放授权插件,1代表支付插件,2代表配送插件)
/// </summary>
public int Type
{
get { return _type; }
set { _type = value; }
} /// <summary>
/// 插件作者
/// </summary>
public string Author
{
get { return _author; }
set { _author = value; }
} /// <summary>
/// 插件版本
/// </summary>
public string Version
{
get { return _version; }
set { _version = value; }
} /// <summary>
/// 插件支持的BrnShop版本
/// </summary>
public string SupVersion
{
get { return _supversion; }
set { _supversion = value; }
} /// <summary>
/// 插件顺序
/// </summary>
public int DisplayOrder
{
get { return _displayOrder; }
set { _displayOrder = value; }
} /// <summary>
/// 是否是默认插件
/// </summary>
public int IsDefault
{
get { return _isdefault; }
set { _isdefault = value; }
} /// <summary>
/// 插件实例
/// </summary>
private IPlugin _instance = null; /// <summary>
/// 插件实例
/// </summary>
[XmlIgnoreAttribute]
public IPlugin Instance
{
get
{
if (_instance == null)
{
try
{
_instance = (IPlugin)Activator.CreateInstance(System.Type.GetType(ClassFullName, false, true));
}
catch (Exception ex)
{
throw new BSPException("创建插件:" + _classfullname + "的实例失败", ex);
}
}
return _instance;
}
} public int CompareTo(object obj)
{
PluginInfo info = (PluginInfo)obj; if (this.DisplayOrder > info.DisplayOrder)
return 1;
else if (this.DisplayOrder < info.DisplayOrder)
return -1;
else
return 0;
} }

  接下来我们定义一个类PluginService,并实现接口IPayPlugin,代码如下:

/// <summary>
/// 插件服务类
/// </summary>
public class PluginService : IPayPlugin
{
/// <summary>
/// 插件配置控制器
/// </summary>
/// <value></value>
public string ConfigController
{
get { return "AdminAlipay"; }
}
/// <summary>
/// 插件配置动作方法
/// </summary>
/// <value></value>
public string ConfigAction
{
get { return "Config"; }
}
/// <summary>
/// 插件配置路由数据
/// </summary>
/// <value></value>
public RouteValueDictionary ConfigRouteValues
{
get { return new RouteValueDictionary() { { "area", "Admin" } }; }
}
/// <summary>
/// 付款方式(0代表货到付款,1代表在线付款,2代表线下付款)
/// </summary>
/// <value></value>
public int PayMode
{
get { return 1; }
}
/// <summary>
/// 是否允许账户充值(只对在线付款有效)
/// </summary>
/// <value></value>
public bool AllowRecharge
{
get { return PluginUtils.GetPluginSet().AllowRecharge == 1; }
}
/// <summary>
/// 支付返回控制器
/// </summary>
/// <value></value>
public string ReturnController
{
get { return "Alipay"; }
}
/// <summary>
/// 支付返回动作方法
/// </summary>
/// <value></value>
public string ReturnAction
{
get { return "Return"; }
}
/// <summary>
/// 支付返回路由数据
/// </summary>
/// <value></value>
public RouteValueDictionary ReturnRouteValues
{
get { return null; }
}
/// <summary>
/// 支付通知控制器
/// </summary>
/// <value></value>
public string NotifyController
{
get { return "Alipay"; }
}
/// <summary>
/// 支付通知动作方法
/// </summary>
/// <value></value>
public string NotifyAction
{
get { return "Notify"; }
}
/// <summary>
/// 支付通知路由数据
/// </summary>
/// <value></value>
public RouteValueDictionary NotifyRouteValues
{
get { return null; }
}
/// <summary>
/// 获得支付手续费
/// </summary>
/// <param name="productAmount">商品合计</param>
/// <param name="buyTime">购买时间</param>
/// <param name="partUserInfo">购买用户</param>
/// <returns></returns>
public decimal GetPayFee(decimal productAmount, DateTime buyTime, PartUserInfo partUserInfo)
{
return 0M;
}
/// <summary>
/// 如果付款方式为在线付款则返回付款请求的url,否则返回空字符串
/// </summary>
/// <param name="notifyUrl">通知url</param>
/// <param name="returnUrl">返回url</param>
/// <param name="pluginInfo">插件信息</param>
/// <param name="partUserInfo">购买用户</param>
/// <param name="orderInfo">订单信息</param>
/// <returns></returns>
public string GetRequestUrl(string notifyUrl, string returnUrl, PluginInfo pluginInfo, PartUserInfo partUserInfo, OrderInfo orderInfo)
{
//支付类型,必填,不能修改
string paymentType = "1";

//服务器异步通知页面路径,需http://格式的完整路径,不能加?id=123这类自定义参数
notifyUrl = string.Format("http://{0}{1}", BSPConfig.ShopConfig.SiteUrl, notifyUrl);

//页面跳转同步通知页面路径,需http://格式的完整路径,不能加?id=123这类自定义参数,不能写成http://localhost/
returnUrl = string.Format("http://{0}{1}", BSPConfig.ShopConfig.SiteUrl, returnUrl);

//收款支付宝帐户
string sellerEmail = AlipayConfig.Seller;

//合作者身份ID
string partner = AlipayConfig.Partner;

//交易安全检验码
string key = AlipayConfig.Key;

//商户订单号
string outTradeNo = orderInfo.Oid.ToString();

//订单名称
string subject = "";

//付款金额
string totalFee = orderInfo.SurplusMoney.ToString();

//订单描述
string body = "";

//防钓鱼时间戳,若要使用请调用类文件submit中的query_timestamp函数
string anti_phishing_key = "";

//客户端的IP地址,非局域网的外网IP地址,如:221.0.0.1
string exter_invoke_ip = "";

//把请求参数打包成数组
SortedDictionary<string, string> sParaTemp = new SortedDictionary<string, string>();
sParaTemp.Add("partner", partner);
sParaTemp.Add("_input_charset", key);
sParaTemp.Add("service", "create_direct_pay_by_user");
sParaTemp.Add("payment_type", paymentType);
sParaTemp.Add("notify_url", notifyUrl);
sParaTemp.Add("return_url", returnUrl);
sParaTemp.Add("seller_email", sellerEmail);
sParaTemp.Add("out_trade_no", outTradeNo);
sParaTemp.Add("subject", subject);
sParaTemp.Add("total_fee", totalFee);
sParaTemp.Add("body", body);
sParaTemp.Add("anti_phishing_key", anti_phishing_key);
sParaTemp.Add("exter_invoke_ip", exter_invoke_ip);

return AlipaySubmit.BuildRequestUrl(sParaTemp, AlipayConfig.Gateway, AlipayConfig.InputCharset, AlipayConfig.SignType, AlipayConfig.Key, AlipayConfig.Code);
}
}

/// <summary>
/// 插件服务类
/// </summary>
public class PluginService : IPayPlugin
{
/// <summary>
/// 插件配置控制器
/// </summary>
/// <value></value>
public string ConfigController
{
get { return "AdminAlipay"; }
}
/// <summary>
/// 插件配置动作方法
/// </summary>
/// <value></value>
public string ConfigAction
{
get { return "Config"; }
}
/// <summary>
/// 插件配置路由数据
/// </summary>
/// <value></value>
public RouteValueDictionary ConfigRouteValues
{
get { return new RouteValueDictionary() { { "area", "Admin" } }; }
}
/// <summary>
/// 付款方式(0代表货到付款,1代表在线付款,2代表线下付款)
/// </summary>
/// <value></value>
public int PayMode
{
get { return 1; }
}
/// <summary>
/// 是否允许账户充值(只对在线付款有效)
/// </summary>
/// <value></value>
public bool AllowRecharge
{
get { return PluginUtils.GetPluginSet().AllowRecharge == 1; }
}
/// <summary>
/// 支付返回控制器
/// </summary>
/// <value></value>
public string ReturnController
{
get { return "Alipay"; }
}
/// <summary>
/// 支付返回动作方法
/// </summary>
/// <value></value>
public string ReturnAction
{
get { return "Return"; }
}
/// <summary>
/// 支付返回路由数据
/// </summary>
/// <value></value>
public RouteValueDictionary ReturnRouteValues
{
get { return null; }
}
/// <summary>
/// 支付通知控制器
/// </summary>
/// <value></value>
public string NotifyController
{
get { return "Alipay"; }
}
/// <summary>
/// 支付通知动作方法
/// </summary>
/// <value></value>
public string NotifyAction
{
get { return "Notify"; }
}
/// <summary>
/// 支付通知路由数据
/// </summary>
/// <value></value>
public RouteValueDictionary NotifyRouteValues
{
get { return null; }
}
/// <summary>
/// 获得支付手续费
/// </summary>
/// <param name="productAmount">商品合计</param>
/// <param name="buyTime">购买时间</param>
/// <param name="partUserInfo">购买用户</param>
/// <returns></returns>
public decimal GetPayFee(decimal productAmount, DateTime buyTime, PartUserInfo partUserInfo)
{
return 0M;
}
/// <summary>
/// 如果付款方式为在线付款则返回付款请求的url,否则返回空字符串
/// </summary>
/// <param name="notifyUrl">通知url</param>
/// <param name="returnUrl">返回url</param>
/// <param name="pluginInfo">插件信息</param>
/// <param name="partUserInfo">购买用户</param>
/// <param name="orderInfo">订单信息</param>
/// <returns></returns>
public string GetRequestUrl(string notifyUrl, string returnUrl, PluginInfo pluginInfo, PartUserInfo partUserInfo, OrderInfo orderInfo)
{
//支付类型,必填,不能修改
string paymentType = "1"; //服务器异步通知页面路径,需http://格式的完整路径,不能加?id=123这类自定义参数
notifyUrl = string.Format("http://{0}{1}", BSPConfig.ShopConfig.SiteUrl, notifyUrl); //页面跳转同步通知页面路径,需http://格式的完整路径,不能加?id=123这类自定义参数,不能写成http://localhost/
returnUrl = string.Format("http://{0}{1}", BSPConfig.ShopConfig.SiteUrl, returnUrl); //收款支付宝帐户
string sellerEmail = AlipayConfig.Seller; //合作者身份ID
string partner = AlipayConfig.Partner; //交易安全检验码
string key = AlipayConfig.Key; //商户订单号
string outTradeNo = orderInfo.Oid.ToString(); //订单名称
string subject = ""; //付款金额
string totalFee = orderInfo.SurplusMoney.ToString(); //订单描述
string body = ""; //防钓鱼时间戳,若要使用请调用类文件submit中的query_timestamp函数
string anti_phishing_key = ""; //客户端的IP地址,非局域网的外网IP地址,如:221.0.0.1
string exter_invoke_ip = ""; //把请求参数打包成数组
SortedDictionary<string, string> sParaTemp = new SortedDictionary<string, string>();
sParaTemp.Add("partner", partner);
sParaTemp.Add("_input_charset", key);
sParaTemp.Add("service", "create_direct_pay_by_user");
sParaTemp.Add("payment_type", paymentType);
sParaTemp.Add("notify_url", notifyUrl);
sParaTemp.Add("return_url", returnUrl);
sParaTemp.Add("seller_email", sellerEmail);
sParaTemp.Add("out_trade_no", outTradeNo);
sParaTemp.Add("subject", subject);
sParaTemp.Add("total_fee", totalFee);
sParaTemp.Add("body", body);
sParaTemp.Add("anti_phishing_key", anti_phishing_key);
sParaTemp.Add("exter_invoke_ip", exter_invoke_ip); return AlipaySubmit.BuildRequestUrl(sParaTemp, AlipayConfig.Gateway, AlipayConfig.InputCharset, AlipayConfig.SignType, AlipayConfig.Key, AlipayConfig.Code);
}
}

  因为支付宝有一些需要配置的属性要保存,例如收款支付宝帐户等。在此我们采用对象序列化和文件的方式来保存这些信息。首先定义一个设置信息类,代码如下:

/// <summary>
/// 插件设置信息类
/// </summary>
public class PluginSetInfo
{
private string _partner;//合作者身份ID
private string _key;//交易安全检验码
private string _seller;//收款支付宝帐户
private int _allowrecharge;//是否允许账户充值

/// <summary>
/// 合作者身份ID
/// </summary>
public string Partner
{
get { return _partner; }
set { _partner = value; }
}

/// <summary>
/// 交易安全检验码
/// </summary>
public string Key
{
get { return _key; }
set { _key = value; }
}

/// <summary>
/// 收款支付宝帐户
/// </summary>
public string Seller
{
get { return _seller; }
set { _seller = value; }
}

/// <summary>
/// 是否允许账户充值
/// </summary>
public int AllowRecharge
{
get { return _allowrecharge; }
set { _allowrecharge = value; }
}
}

/// <summary>
/// 插件设置信息类
/// </summary>
public class PluginSetInfo
{
private string _partner;//合作者身份ID
private string _key;//交易安全检验码
private string _seller;//收款支付宝帐户
private int _allowrecharge;//是否允许账户充值 /// <summary>
/// 合作者身份ID
/// </summary>
public string Partner
{
get { return _partner; }
set { _partner = value; }
} /// <summary>
/// 交易安全检验码
/// </summary>
public string Key
{
get { return _key; }
set { _key = value; }
} /// <summary>
/// 收款支付宝帐户
/// </summary>
public string Seller
{
get { return _seller; }
set { _seller = value; }
} /// <summary>
/// 是否允许账户充值
/// </summary>
public int AllowRecharge
{
get { return _allowrecharge; }
set { _allowrecharge = value; }
}
}

  然后添加一个工具类PluginUtils来序列化和反序列化设置信息,代码如下:

/// <summary>
/// 插件工具类
/// </summary>
public class PluginUtils
{
private static object _locker = new object();//锁对象
private static PluginSetInfo _pluginsetinfo = null;//插件设置信息
private static string _plugindatafilepath = "/Plugins/BrnShop.PayPlugin.Alipay/PluginData.config";//数据文件路径

/// <summary>
///获得插件设置
/// </summary>
/// <returns></returns>
public static PluginSetInfo GetPluginSet()
{
if (_pluginsetinfo == null)
{
lock (_locker)
{
if (_pluginsetinfo == null)
{
_pluginsetinfo = (PluginSetInfo)IOHelper.DeserializeFromXML(typeof(PluginSetInfo), IOHelper.GetMapPath(_plugindatafilepath));
}
}
}
return _pluginsetinfo;
}

/// <summary>
/// 保存插件设置到数据数据文件中
/// </summary>
public static void SavePluginSet(PluginSetInfo pluginSetInfo)
{
lock (_locker)
{
IOHelper.SerializeToXml(pluginSetInfo, IOHelper.GetMapPath(_plugindatafilepath));
_pluginsetinfo = null;
AlipayConfig.ReSet();
}
}

}

/// <summary>
/// 插件工具类
/// </summary>
public class PluginUtils
{
private static object _locker = new object();//锁对象
private static PluginSetInfo _pluginsetinfo = null;//插件设置信息
private static string _plugindatafilepath = "/Plugins/BrnShop.PayPlugin.Alipay/PluginData.config";//数据文件路径 /// <summary>
///获得插件设置
/// </summary>
/// <returns></returns>
public static PluginSetInfo GetPluginSet()
{
if (_pluginsetinfo == null)
{
lock (_locker)
{
if (_pluginsetinfo == null)
{
_pluginsetinfo = (PluginSetInfo)IOHelper.DeserializeFromXML(typeof(PluginSetInfo), IOHelper.GetMapPath(_plugindatafilepath));
}
}
}
return _pluginsetinfo;
} /// <summary>
/// 保存插件设置到数据数据文件中
/// </summary>
public static void SavePluginSet(PluginSetInfo pluginSetInfo)
{
lock (_locker)
{
IOHelper.SerializeToXml(pluginSetInfo, IOHelper.GetMapPath(_plugindatafilepath));
_pluginsetinfo = null;
AlipayConfig.ReSet();
}
} }

  就下来就是前台和后台实现了。首先是后台实现,对于后台我们只需要提供一个可供修改支付宝配置的子方法就行了,此子方法在上面讲解"IPlugin"接口时已经给出了它的调用方式。代码如下:

/// <summary>
/// 通知调用
/// </summary>
public ActionResult Notify()
{
SortedDictionary<string, string> sPara = AlipayCore.GetRequestPost(); if (sPara.Count > 0)//判断是否有带返回参数
{
bool verifyResult = AlipayNotify.Verify(sPara, Request.QueryString["notify_id"], Request.QueryString["sign"], AlipayConfig.SignType, AlipayConfig.Key, AlipayConfig.Code, AlipayConfig.VeryfyUrl, AlipayConfig.Partner); if (verifyResult && (Request.QueryString["trade_status"] == "TRADE_FINISHED" || Request.QueryString["trade_status"] == "TRADE_SUCCESS"))//验证成功
{
int oid = TypeHelper.StringToInt(Request.QueryString["out_trade_no"]);//商户订单号
string tradeSN = Request.QueryString["trade_no"];//支付宝交易号
decimal tradeMoney = TypeHelper.StringToDecimal(Request.QueryString["total_fee"]);//交易金额
DateTime tradeTime = TypeHelper.StringToDateTime(Request.QueryString["gmt_payment"]);//交易时间 OrderInfo orderInfo = Orders.GetOrderByOid(oid);
if (orderInfo.PayMode == 1 && orderInfo.SurplusMoney > 0 && orderInfo.SurplusMoney <= tradeMoney)
{
Orders.PayOrder(oid, OrderState.Confirming, tradeSN);
OrderActions.CreateOrderAction(new OrderActionInfo()
{
Oid = oid,
Uid = orderInfo.Uid,
RealName = "本人",
AdminGid = 1,
AdminGTitle = "非管理员",
ActionType = (int)OrderActionType.Pay,
ActionTime = tradeTime,
ActionDes = "你使用支付宝支付订单成功,支付宝交易号为:" + tradeSN
});
} return new EmptyResult();
}
else//验证失败
{
return new EmptyResult();
}
}
else
{
return new EmptyResult();
}
}
    /// <summary>
/// 后台支付宝插件控制器类
/// </summary>
public class AdminAlipayController : BaseAdminController
{
/// <summary>
/// 配置
/// </summary>
[HttpGet]
[ChildActionOnly]
public ActionResult Config()
{
ConfigModel model = new ConfigModel(); model.Partner = PluginUtils.GetPluginSet().Partner;
model.Key = PluginUtils.GetPluginSet().Key;
model.Seller = PluginUtils.GetPluginSet().Seller;
model.AllowRecharge = PluginUtils.GetPluginSet().AllowRecharge; return View("~/Plugins/BrnShop.PayPlugin.Alipay/Views/AdminAlipay/Config.cshtml", model);
} /// <summary>
/// 配置
/// </summary>
[HttpPost]
public ActionResult Config(ConfigModel model)
{
if (ModelState.IsValid)
{
PluginSetInfo setting = new PluginSetInfo(); setting.Partner = model.Partner.Trim();
setting.Key = model.Key.Trim();
setting.Seller = model.Seller.Trim();
setting.AllowRecharge = model.AllowRecharge; PluginUtils.SavePluginSet(setting);
}
return RedirectToAction("List", "Plugin", new RouteValueDictionary() { { "area", "Admin" }, { "Type", "1" } });
}
}

  至于前台我们有3个方面需要去实现,第一个方面是返回调用动作方法,代码如下:

/// <summary>
/// 通知调用
/// </summary>
public ActionResult Notify()
{
SortedDictionary<string, string> sPara = AlipayCore.GetRequestPost(); if (sPara.Count > 0)//判断是否有带返回参数
{
bool verifyResult = AlipayNotify.Verify(sPara, Request.QueryString["notify_id"], Request.QueryString["sign"], AlipayConfig.SignType, AlipayConfig.Key, AlipayConfig.Code, AlipayConfig.VeryfyUrl, AlipayConfig.Partner); if (verifyResult && (Request.QueryString["trade_status"] == "TRADE_FINISHED" || Request.QueryString["trade_status"] == "TRADE_SUCCESS"))//验证成功
{
int oid = TypeHelper.StringToInt(Request.QueryString["out_trade_no"]);//商户订单号
string tradeSN = Request.QueryString["trade_no"];//支付宝交易号
decimal tradeMoney = TypeHelper.StringToDecimal(Request.QueryString["total_fee"]);//交易金额
DateTime tradeTime = TypeHelper.StringToDateTime(Request.QueryString["gmt_payment"]);//交易时间 OrderInfo orderInfo = Orders.GetOrderByOid(oid);
if (orderInfo.PayMode == 1 && orderInfo.SurplusMoney > 0 && orderInfo.SurplusMoney <= tradeMoney)
{
Orders.PayOrder(oid, OrderState.Confirming, tradeSN);
OrderActions.CreateOrderAction(new OrderActionInfo()
{
Oid = oid,
Uid = orderInfo.Uid,
RealName = "本人",
AdminGid = 1,
AdminGTitle = "非管理员",
ActionType = (int)OrderActionType.Pay,
ActionTime = tradeTime,
ActionDes = "你使用支付宝支付订单成功,支付宝交易号为:" + tradeSN
});
} return new EmptyResult();
}
else//验证失败
{
return new EmptyResult();
}
}
else
{
return new EmptyResult();
}
}
/// <summary>
/// 返回调用
/// </summary>
public ActionResult Return()
{
SortedDictionary<string, string> sPara = AlipayCore.GetRequestGet(); if (sPara.Count > 0)//判断是否有带返回参数
{
bool verifyResult = AlipayNotify.Verify(sPara, Request.QueryString["notify_id"], Request.QueryString["sign"], AlipayConfig.SignType, AlipayConfig.Key, AlipayConfig.Code, AlipayConfig.VeryfyUrl, AlipayConfig.Partner); if (verifyResult && (Request.QueryString["trade_status"] == "TRADE_FINISHED" || Request.QueryString["trade_status"] == "TRADE_SUCCESS"))//验证成功
{
int oid = TypeHelper.StringToInt(Request.QueryString["out_trade_no"]);//商户订单号
string tradeSN = Request.QueryString["trade_no"];//支付宝交易号
decimal tradeMoney = TypeHelper.StringToDecimal(Request.QueryString["total_fee"]);//交易金额
DateTime tradeTime = TypeHelper.StringToDateTime(Request.QueryString["notify_time"]);//交易时间 OrderInfo orderInfo = Orders.GetOrderByOid(oid);
if (orderInfo.PayMode == 1 && orderInfo.SurplusMoney > 0 && orderInfo.SurplusMoney <= tradeMoney)
{
Orders.PayOrder(oid, OrderState.Confirming, tradeSN);
OrderActions.CreateOrderAction(new OrderActionInfo()
{
Oid = oid,
Uid = orderInfo.Uid,
RealName = "本人",
AdminGid = 1,
AdminGTitle = "非管理员",
ActionType = (int)OrderActionType.Pay,
ActionTime = tradeTime,
ActionDes = "你使用支付宝支付订单成功,支付宝交易号为:" + tradeSN
});
} return RedirectToAction("PaySuccess", "Order", new RouteValueDictionary { { "oid", orderInfo.Oid } });
}
else//验证失败
{
return new EmptyResult();
}
}
else
{
return new EmptyResult();
}
}

  第二个方面是通知调用动作方法,代码如下:

/// <summary>
/// 通知调用
/// </summary>
public ActionResult Notify()
{
SortedDictionary<string, string> sPara = AlipayCore.GetRequestPost(); if (sPara.Count > 0)//判断是否有带返回参数
{
bool verifyResult = AlipayNotify.Verify(sPara, Request.QueryString["notify_id"], Request.QueryString["sign"], AlipayConfig.SignType, AlipayConfig.Key, AlipayConfig.Code, AlipayConfig.VeryfyUrl, AlipayConfig.Partner); if (verifyResult && (Request.QueryString["trade_status"] == "TRADE_FINISHED" || Request.QueryString["trade_status"] == "TRADE_SUCCESS"))//验证成功
{
int oid = TypeHelper.StringToInt(Request.QueryString["out_trade_no"]);//商户订单号
string tradeSN = Request.QueryString["trade_no"];//支付宝交易号
decimal tradeMoney = TypeHelper.StringToDecimal(Request.QueryString["total_fee"]);//交易金额
DateTime tradeTime = TypeHelper.StringToDateTime(Request.QueryString["gmt_payment"]);//交易时间 OrderInfo orderInfo = Orders.GetOrderByOid(oid);
if (orderInfo.PayMode == 1 && orderInfo.SurplusMoney > 0 && orderInfo.SurplusMoney <= tradeMoney)
{
Orders.PayOrder(oid, OrderState.Confirming, tradeSN);
OrderActions.CreateOrderAction(new OrderActionInfo()
{
Oid = oid,
Uid = orderInfo.Uid,
RealName = "本人",
AdminGid = 1,
AdminGTitle = "非管理员",
ActionType = (int)OrderActionType.Pay,
ActionTime = tradeTime,
ActionDes = "你使用支付宝支付订单成功,支付宝交易号为:" + tradeSN
});
} return new EmptyResult();
}
else//验证失败
{
return new EmptyResult();
}
}
else
{
return new EmptyResult();
}
}
/// <summary>
/// 通知调用
/// </summary>
public ActionResult Notify()
{
SortedDictionary<string, string> sPara = AlipayCore.GetRequestPost(); if (sPara.Count > 0)//判断是否有带返回参数
{
bool verifyResult = AlipayNotify.Verify(sPara, Request.QueryString["notify_id"], Request.QueryString["sign"], AlipayConfig.SignType, AlipayConfig.Key, AlipayConfig.Code, AlipayConfig.VeryfyUrl, AlipayConfig.Partner); if (verifyResult && (Request.QueryString["trade_status"] == "TRADE_FINISHED" || Request.QueryString["trade_status"] == "TRADE_SUCCESS"))//验证成功
{
int oid = TypeHelper.StringToInt(Request.QueryString["out_trade_no"]);//商户订单号
string tradeSN = Request.QueryString["trade_no"];//支付宝交易号
decimal tradeMoney = TypeHelper.StringToDecimal(Request.QueryString["total_fee"]);//交易金额
DateTime tradeTime = TypeHelper.StringToDateTime(Request.QueryString["gmt_payment"]);//交易时间 OrderInfo orderInfo = Orders.GetOrderByOid(oid);
if (orderInfo.PayMode == 1 && orderInfo.SurplusMoney > 0 && orderInfo.SurplusMoney <= tradeMoney)
{
Orders.PayOrder(oid, OrderState.Confirming, tradeSN);
OrderActions.CreateOrderAction(new OrderActionInfo()
{
Oid = oid,
Uid = orderInfo.Uid,
RealName = "本人",
AdminGid = 1,
AdminGTitle = "非管理员",
ActionType = (int)OrderActionType.Pay,
ActionTime = tradeTime,
ActionDes = "你使用支付宝支付订单成功,支付宝交易号为:" + tradeSN
});
} return new EmptyResult();
}
else//验证失败
{
return new EmptyResult();
}
}
else
{
return new EmptyResult();
}
}

  第三个方面是我们需要提供一个支付宝简介的视图文件,这个视图文件在顾客支付订单时给其展示一些支付宝的说明信息,此文件有以下几点要求:

  • 文件名必须为"Show.cshtml"。
  • 此文件必须位于插件项目的Views文件夹的顶层中。
  • 此视图文件接收类型为OrderInfo的视图模型对象。

  这个视图文件在BrnShop.Web项目的支付展示视图文件PayShowModel.cshtml中调用,调用方式如下:

@Html.Partial(Model.ShowView, Model.OrderInfo)

  至此支付宝插件开发完成,至于其它类型的插件开发也都是大同小异。

  PS:最后附上一张BrnShop开启NOSQL前后的性能对比图,以商品详细页面为例:

   开启NOSQL前:

  开启NOSQL后:

  可见性能提升还是很明显的。

BrnShop开源网上商城第四讲:自定义插件的更多相关文章

  1. BrnShop开源网上商城第五讲:自定义视图引擎

    今天这篇博文主要讲解自定义视图引擎,大家都知道在asp.net mvc框架中默认自带一个Razor视图引擎,除此之外我们也可以自定义自己的视图引擎,只需要实现IViewEngine接口,接口定义如下: ...

  2. BrnShop开源网上商城第六讲:扩展视图功能

    在正式讲解扩展视图功能以前,我们有必要把视图的工作原理简单说明下.任何一个视图都会被翻译成一个c#类,并保存到指定的位置,然后被编译.这也就是为什么能在视图中包含c#代码片段的原因.下面我们通过一个项 ...

  3. BrnShop开源网上商城第一讲:架构设计

    首先在此感谢大家对BrnShop项目的支持和鼓励!我们在发布BrnShop以前曾推测项目会受到不少园友的支持,但没想到园友们的支持大大超过我们的预测.4天6000次浏览,140个推荐,170个评论,8 ...

  4. BrnShop开源网上商城第二讲:ASP.NET MVC框架

    在团队设计BrnShop的web项目之初,我们碰到了两个问题,第一个是数据的复用和传递,第二个是大mvc框架和小mvc框架的选择.下面我依次来说明下. 首先是数据的复用和传递:对于BrnShop的每一 ...

  5. BrnShop开源网上商城第三讲:插件的工作机制

    这几天BrnShop的开发工作比较多,所以这一篇文章来的晚了一些,还请大家见谅呀!还有通知大家一下BrnShop1.0.312版本已经发布,此版本添加了报表统计等新功能,需要源码的园友可以点此下载.好 ...

  6. 第四讲 自定义Realm来实现身份认证

    1.jdbcReam已经实现了从数据库中获取用户的验证信息,但是jdbcRealm灵活性太差.如果要实现自己的一些特殊应用时,将不能支持.这时,可以通过自定义Realm来实现身份的认证功能. 2.Re ...

  7. Python 开源网上商城项目

    django-oscar  https://github.com/django-oscar/django-oscar#screenshots django-shop  https://github.c ...

  8. BrnShop:自定义插件

    BrnShop开源网上商城第四讲:自定义插件 重要通知:BrnShop企业版NOSQL设计(基于Redis)已经开源!源码内置于最新版的BrnShop中,感兴趣的园友可以去下载来看看.官网地址:www ...

  9. 开源Asp.Net MVC网上商城BrnShop

    开源Asp.Net MVC网上商城BrnShop正式发布,提供源码下载 BrnShop网上商城是以Asp.Net mvc3为基础开发的网上商城,源代码完全开源(企业版的源代码目前还没有完全整理完成,一 ...

随机推荐

  1. java nio使用方法(转)

    最近由于工作关系要做一些Java方面的开发,其中最重要的一块就是Java NIO(New I/O),尽管很早以前了解过一些,但并没有认真去看过它的实现原理,也没有机会在工作中使用,这次也好重新研究一下 ...

  2. 此博客不更新文章,请到www.xiaoxiangyucuo.com看最新文章

    请到www.xiaoxiangyucuo.com看更多资料,包括Linux,JavaScript,Python,MySQL,PHP,HTML,Photoshop,以及各类软件下载. 希望大家支持,提出 ...

  3. Application.StartupPath同System.Environment.CurrentDirectory区别

    System.Windows.Forms.Application.StartupPath:获取启动了应用程序的可执行文件的路径,不包括可执行文件的名称. System.Environment.Curr ...

  4. jquery-easyui使用

    1,闲来无事用了一下jquery-easyui,首先下载下来easyui,文件夹如图 2,将themes里面的东西整体拷贝,我是整体的,省的麻烦了 3,在在自己的vs里面新建一个mvc的项目(项目自己 ...

  5. 如何使用ERStudio 生成comment

    在ER使用中,在生成sql过程中,如何批量生成字段描述,如何批量添加Owner,请看下文: 1.ER生成字段描述 2.ER生成描述添加Owner 使用的ER版本是8.0,英文版本,在操作过程中,有些配 ...

  6. 盗链网易163、腾讯QQ、新浪sina、百度Baidu的图片之PHP独立版

    网易相册频道,网易相册确实是一个高质量图片的地方,而且免费,唯一缺点是很多加了水印,但这个不重要了,无意间发现网易163相册频道的图片是防止盗链的,于是便自己写了一个小程序来突破这个. 本盗链图片最新 ...

  7. web2py--------------用web2py写 django的例子 --------建立一个投票应用(3)

    我们建立了数据模型,然后这次来进行页面的展示 1.这里是列表页面的 control 这里是dal的语法 只有两行 第一行 是查询出所有问题,也就是问题的id大于0 第二行是返回问题的列表 这里是vie ...

  8. Sqlmap下载安装与基础命令使用

    本文介绍一下Sqlmap的安装跟配置环境变量. 顺便附上一些常用的命令 SQLMAP-64位.Python 下载链接:http://pan.baidu.com/s/1c0D82fm 密码:d7ec P ...

  9. 【springmvc Request】 springmvc请求接收参数的几种方法

    通过@PathVariabl注解获取路径中传递参数 转载请注明出处:springmvc请求接收参数的几种方法 代码下载地址:http://www.zuida@ima@com/share/1751862 ...

  10. 实现n皇后问题(回溯法)

    /*======================================== 功能:实现n皇后问题,这里实现4皇后问题 算法:回溯法 ============================= ...