实现使用支付宝交易接口实现机构端代理系统的总结
Posted wuyb_2004
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了实现使用支付宝交易接口实现机构端代理系统的总结相关的知识,希望对你有一定的参考价值。
支付宝接口系统设计总结
1 前言
经过1个月的紧张开发,支付宝接口系统项目顺利完工,现在对整个项目的设计方案进行总结,目的是提升一下表达能力,同时为以后的系统设计提供一个参考。
该项目为燃气公司提供使用支付宝代收燃气费的接入服务。数据交换协议由支付宝方规定。系统实现的主要功能包括:用户欠费查询、用户缴费、清算、对账等功能。
由于系统跑在公网上,所以数据传输要求加密传输,支付宝方规定数据通讯采用SSL Socket双向验证通道,对账文件采用sftp数据传输。这也是我首次接触在互联网上采用加密通讯方式实现缴费业务的系统。
2 系统架构设计
整个系统功能和业务并不复杂,这样的系统要求稳定性和性能(能够快速响应),目前经过测试和时间运行,系统响应能力在1秒以内。
虽然需求相对稳定(因为支付宝的协议一旦确定,基本上不会发生更改),但是为了实现可维护和代码可读、以最少的代码实现整个项目,因此,我还是采用了分层结构(虽然理由并不充分),好处是,整体结构清晰,各层模块的功能和职责单一,便于维护和阅读。
整体结构由业务层、通讯层、数据访问层、数据实体层、日志5个层次实现,各层关系如图1:
图1
下面简要介绍各层的职责:
先说说“实体层”,这层将支付宝协议中交换数据包用对象的思想进行封装,由于是请求/应答模式实现交易,因此,请求包和应答包都是相对独立和稳定的,可以用两个自定义类型来表示(可以是类或结构),具体实现思路后面再细说。
再说说“通讯层”,这层负责和支付宝前置服务进行数据通信,包括:建立SSL Socket连接,发送数据、接收数据,并将数据提交给上层(业务层)处理,该层具体设计后面再细说。
再看看“业务层”,这层负责处理和支付宝之间的交易。过程为:通讯层接收到支付宝前端的数据后,检查是否符合交易数据帧格式,如果是正确的数据帧,则将数据帧报告给业务层。业务层根据数据帧中交易类型参数,自动选择业务处理类,进行业务处理,并将处理结果通过通讯层发送给支付宝。
最后是“数据访问层”和“日志层”,这两层比较简单。从名称上我们就可以知道,“数据访问层”负责从数据库读取或写入数据;“日志层”负责记录系统日志,包括:接收和发送的数据,系统错误信息等。
3 系统设计
3.1 总体设计
根据分层的原则,系统采用组件化设计方案,数据通信和业务处理抽象为独立的组件,分别提供创建SSL Socket连接和数据收发功能和处理具体业务功能。组件间通过接口来工作,降低组件间的耦合,系统的核心模型,参看图2的设计模型。
图2
业务服务组件通过订阅通讯服务组件的连接事件,获得和支付宝通讯的数据通道(使用SSL Socket收发数据)的能力。通讯服务组件在接收到请求并创建了SSL Socket连接后,通过封装的数据通道,通知业务服务组件,有连接就绪,准备接收业务数据。
业务服务组件在接收到通讯服务组件的连接事件后,使用数据通道,创建交易处理控制器并从数据通道订阅数据事件(从数据通道接收数据)。
交易处理控制器接收到数据通道的数据(通过事件通知),根据数据报文head区的交易类型,向业务服务请求具体业务处理对象,并调用业务对象相关处理方法,并然后将处理结果,通过数据通道发送给请求方(支付宝),完成本次业务处理。过程参见图3
图3
3.2 通讯层设计
通讯层的设计原则是小接口、职责单一、尽量降低类间耦合。通讯层设计模型如图4所示
图4
通讯服务其实是个控制类,负责创建TCP侦听服务,并订阅其连接事件。在连接事件发生时,创建数据通道,并触发对业务层提供的连接事件。
数据通道的职责很简单,就是创建SSL加密数据通道,在接收到数据后,调用数据帧检查类对数据进行检查,符合支付宝定义数据要求后,触发对业务层提供的数据事件。
参考类图如:图5
图5
3.3 业务层设计
先看看业务处理结构设计,设计原则:依赖倒置。
实现的业务是,根据支付宝端请求的交易类型,进行相应的处理。正常的设计方法如下面的代码所示:
class 业务处理
{
public string 处理(head head,string inBody)
{
switch (head.servCode)
{
case "200001":
//在此编写欠费查询的业务代码
break;
//其他业务类似
default:
}
}
}
这样的实现没有对不同的业务进行分离,导致全部业务实现下来,代码很长,且大部分都是类似的代码结构。为了将不同的业务进行分离,可以将不同的业务用单独的类来封装。不同的业务类如下:
//欠费查询类
class 欠费查询
{
Publicstring 查询(string inBody)
{
//在这里实现业务逻辑
}
}
//缴费类
class 缴费业务
{
Publicstring 缴费(string inBody)
{
//在这里实现业务逻辑
}
}
//。。。。
这样就可以用这些类替换原来业务代码,实现将变成如下代码:
class 业务处理
{
public string 处理(head head,string inBody)
{
switch (head.servCode)
{
case "200001":
new 欠费查询().查询(inBody)
break;
//其他业务类似
default:
}
}
}
这样的设计依然强依赖,并不满足依赖倒置的原则。根据观察,每个业务处理结构相似,这样就可以抽象业务处理接口,各类业务实现这个接口,在创建一个工厂类,由工厂类创建所需业务的处理对象,这样就实现了解耦,并符合依赖倒置原则。实现代码如下:
//业务接口
public interfaceItrading
{
body Dowith(head head,string inBody);
}
//业务对象创建工厂
class TradingFactory
{
Public static ItradinggetTradingFace(string serviceCode)
{
switch (head.servCode)
{
case "200001":
//在此创建业务处理对象
return new 欠费查询();
break;
//其他业务类似
default:
}
return null;
}
}
业务处理代码如下:
class 业务处理
{
public string 处理(head head,stringinBody)
{
ItradingiTrading = TradingFactory. getTradingFace(head.servCode);
If(iTrading != null)
iTrading.
}
}
到这里,基本实现了设计目标,但是有点缺陷,在工厂类中依然使用了switch多分支语句,那有办法解决吗?答案是肯定的。实现技术有多种,如:.Net的反射,在这里我选择了.Net 的MEF(Managed Extensibility Framework)技术,可以实现无配置的动态加载。实现方法如下:
首先,在实现的业务类的头部,添加 [Export(typeof(Itrading))] 标签,含义是该类是可以导出为Itrading接口。
然后改造工厂类。方法是,在项目中引入System.ServiceModel组件,实现代码如下:
class TradingFactory
{
privateConnectorHandle _connector;
privateCompositionContainer _container;
[ImportMany(AllowRecomposition = true)]
privateItrading[] ItradingQueueServices { get; set; }
privateCompositionContainer GetContainerFromDirectory()
{
var catalog= new AggregateCatalog();
varthisAssembly =
newAssemblyCatalog(
System.Reflection.Assembly.GetExecutingAssembly());
catalog.Catalogs.Add(thisAssembly);
varcontainer = new CompositionContainer(catalog);
returncontainer;
}
private boolCompose()
{
_container= GetContainerFromDirectory();
try
{
_container.ComposeParts(this);
}
catch(CompositionException compException)
{
//Log.getInstance().Write(compException, MsgType.Error);
returnfalse;
}
catch(Exception e)
{
//Log.getInstance().Write(e, MsgType.Error);
returnfalse;
}
returntrue;
}
publicTradingFactory()
{
this.Compose();
}
public ItradinggetTradingFace(string serviceCode)
{
foreach(Itrading trading in this.ItradingQueueServices)
{
if(trading.getServiceCode == serviceCode)
return trading;
}
returnnull;
}
}
这样实现后,我们就摆脱了swith带来的烦恼了。
到这里,业务处理的基础结构就设计完成了,在配合通信层,就可以完成业务要求了,下面是项目实际的设计类图,供参考:
图6
部分实现代码,如图7
图7
3.4 实体层设计
这层比较简单,为了使整体结构简单,将每个业务请求和应答数据用数据类进行封装。说明:
支付宝协议定义数据交换采用json数据格式。
先看实体层结构吧,为了减少代码,使代码尽量可以重用,采用继承结构。由于支付宝的数据结构由数据头和数据体两部分组成,所以首先定义数据头和数据体,数据头在所有业务中都相同,但数据体在不同业务中不同,支付宝数据格式要求如图8:
图8
数据实体基础结构如图9:
图9
类body作为数据体的父类,其本身并没有定义任何数据字段,仅实现了一个ToSting()方法的重载。这里的设计借用了面向对象设计原则的Liskov替换原则(李氏替换原则)。
4 采用的技术
该项目采用.NetFramework4.5作为开发平台,在项目中使用的TCP加密通讯(SSL Socket)、数据访问、MEF等.Net技术。采用面向对象的思想进行系统分析、设计和实现。
SSL Socket,在socket通信的基础上添加了一层安全性保护,提供了更高的安全性,包括身份验证、数据加密以及完整性验证。如何建立SSL Socket通讯,可以参看我的一篇文章:http://blog.csdn.net/wuyb_2004/article/details/51393290
MEF(ManagedExtensibility Framework)是一个用于创建可扩展的轻型应用程序的库。 应用程序开发人员可利用该库发现并使用扩展,而无需进行配置。 扩展开发人员还可以利用该库轻松地封装代码,避免生成脆弱的硬依赖项。 通过 MEF,不仅可以在应用程序内重用扩展,还可以在应用程序之间重用扩展。有关技术可以参看博客:http://www.360doc.com/content/11/0830/16/7582031_144521508.shtml
在系统设计过程中,充分考虑面向对象的设计原则。
以上是关于实现使用支付宝交易接口实现机构端代理系统的总结的主要内容,如果未能解决你的问题,请参考以下文章