智付云系统架构及配置

1.系统架构说明

本文档描述的智付云系统架构脱胎于现有的apoll系统,并大幅优化了系统的并发性,可维护性能,组件之间的通讯全部使用异步通讯,使得更有利于适应现在的互联网的运行环境和开发模式。

1.1系统架构图

1.2系统架构说明

智付云系统架构分为3块,第一块为python动态加载的C服务组件,第二块是redis-server服务端组件,第三块为C转发组件。动态组件之间的消息传递由redis服务实现。在一个交易流程中以xml消息总线维持之间的通讯。如需多次交互,由redis服务暂存xml消息总线的内容。

1.3Python动态加载框架

此框架由python语言编写,加载Ctype库,从而实现动态加载C或者其他语言库的能力。在动态库的编写上力求接口同化,都以int funcname(char resq,charresp)的方式命名,其中resq为接收的xml消息总线的内容,resp为返回的消息总线的内容及该服务处理后的结果。
Python的动态加载框架所需的配置文件存放在智付云mpos/json/和mpos/xml里,由*.json,init_op.xml,init_func.xml,user.xml,yl_trans.xml组成,及各用户文件下的trans_next.xml组成。

1.4Redis服务组件

Redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。这些数据类型都 支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排 序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文 件,并且在此基础上实现了master-slave(主从)同步 Redis服务组件主要的作用为:为各个组件的通讯提供了mem层的服务,为多次交互的实现提供缓存xml数据总线的服务。
Redis服务较之于传统的消息队列,管道通讯来说有巨大的优势。首先redis服务是基于key_value存储系统,这个特性在高并发的时候能够有效的提供支持。其次在最新的redis中提供了cluster的能力,为智付云系统提供了组件集群的能力。最后,redis提供了比消息队列,管道丰富多的数据类型及操作,基本上一个redis服务就可以完全满足现有或者将来的组件通讯及缓存、高并发的所有需求。

1.5服务转发组件

本组件由Python语言编写,通过加载json文件夹里的配置文件来驱动交易流程。
Json文件夹里的配置流程,按功能分可以划分为3块(“协议配置”,“交易处理配置”,“错误处理配置”)。由于json的流程组件都是标准的json格式,可以完全展现在web平台,并有web平台管理与生成。
Json配置文件采用多级模板体系,对于重复的交易流程,以及交易模块,可以分不同的粒度来生成对应的模板,以达到管理的方便与配置的重用。

2.组件配置

2.1组件加载

启动时由mpos_daemon程序加载mpos/xml/daemon.xml,由daemon.xml里的配置来启动对应的程序。
Daemon.xml实例:

<srvid>0004</srvid><srvnum>02</srvnum><paramdata>mpos_control.py</paramdata><paramindex><num>00</num></paramindex>
<srvid>0006</srvid><srvnum>02</srvnum><paramdata>mpos_service</paramdata><paramindex><num>01</num><01>mpos_proxy</01></paramindex>
<srvid>0007</srvid><srvnum>02</srvnum><paramdata>mpos_service</paramdata><paramindex><num>01</num><01>mpos_public</01></paramindex>
<srvid>0008</srvid><srvnum>02</srvnum><paramdata>mpos_service</paramdata><paramindex><num>01</num><01>mpos_trans</01></paramindex>
<srvid>0009</srvid><srvnum>02</srvnum><paramdata>mpos_service</paramdata><paramindex><num>01</num><01>mpos_socket</01></paramindex>
<srvid>0010</srvid><srvnum>02</srvnum><paramdata>mpos_service</paramdata><paramindex><num>01</num><01>mpos_dbfunc</01></paramindex>
<srvid>0011</srvid><srvnum>02</srvnum><paramdata>mpos_service</paramdata><paramindex><num>01</num><01>mpos_pyfunc</01></paramindex>
<srvid>0012</srvid><srvnum>02</srvnum><paramdata>mpos_service</paramdata><paramindex><num>01</num><01>mpos_pyabc</01></paramindex>
<srvid>0013</srvid><srvnum>02</srvnum><paramdata>mpos_service</paramdata><paramindex><num>01</num><01>mpos_pyhsm</01></paramindex>
<srvid>0014</srvid><srvnum>02</srvnum><paramdata>mpos_service</paramdata><paramindex><num>01</num><01>mpos_pypos</01></paramindex>
<srvid>0094</srvid><srvnum>01</srvnum><paramdata>mpos_epoll_server.py</paramdata><paramindex><num>01</num><01>term_server.ini</01></paramindex>
<srvid>0095</srvid><srvnum>01</srvnum><paramdata>mpos_epoll_server.py</paramdata><paramindex><num>01</num><01>pad_server.ini</01></paramindex>
Srvid:服务的id号,不重复就可以
Srvnum:启动服务的个数,此例子中启动2个mpos_public服务
Paramdata:需要加载的python动态框架,此处为mpos_service框架
Num:标示需要加载的参数个数
01:参数01的内容为mpos_public,就代表此流程需要加载mpos_public服务
整个流程为:python mpos_service mpos_public

Python动态加载框架启动时,从Mpos/xml/init_op.xml加载对应的模块,从Mpos/xml/init_func.xml加载对应的服务。
Init_op.xml实例:

<svrname>mpos_public</svrname><libname>mpos_public</libname><lib>libmpos_public.so</lib><type>c</type>
<svrname>mpos_public</svrname><libname>watch_door</libname><lib>libwatch_door.so</lib><type>c</type>
<svrname>mpos_socket</svrname><libname>mpos_socket</libname><lib>libmpos_socket.so</lib><type>c</type>
<svrname>mpos_socket</svrname><libname>watch_door</libname><lib>libwatch_door.so</lib><type>c</type>
<svrname>mpos_trans</svrname><libname>mpos_trans</libname><lib>libmpos_trans.so</lib><type>c</type>
<svrname>mpos_trans</svrname><libname>watch_door</libname><lib>libwatch_door.so</lib><type>c</type>
<svrname>mpos_dbfunc</svrname><libname>mpos_dbfunc</libname><lib>mpos_dbfunc</lib><type>py</type>
<svrname>mpos_dbfunc</svrname><libname>watch_door</libname><lib>libwatch_door.so</lib><type>c</type>
<svrname>mpos_pyfunc</svrname><libname>mpos_pyfunc</libname><lib>mpos_pyfunc</lib><type>py</type>
<svrname>mpos_pyfunc</svrname><libname>watch_door</libname><lib>libwatch_door.so</lib><type>c</type>
<svrname>mpos_proxy</svrname><libname>mpos_proxy</libname><lib>libmpos_proxy.so</lib><type>c</type>
<svrname>mpos_proxy</svrname><libname>watch_door</libname><lib>libwatch_door.so</lib><type>c</type>
<svrname>mpos_pyabc</svrname><libname>mpos_pyabc</libname><lib>mpos_pyabc</lib><type>py</type>
<svrname>mpos_pyhsm</svrname><libname>mpos_pyhsm</libname><lib>mpos_pyhsm</lib><type>py</type>
<svrname>mpos_pypos</svrname><libname>mpos_pypos</libname><lib>mpos_pypos</lib><type>py</type>
这里的实例为智付云组件服务需要加载的组件
svrname:服务的名称
Libname:需要加载到框架里服务的名称,此处为mpos_public服务
Lib:需要加载的服务的文件名,此处为libmpos_public.so
Type:加载库的类型,此处为c库

init_func.xml实例:

<libname>mpos_public</libname><func>get_term_to_local</func>
Libname:为需要加载的服务名,此处为mpos_public
Func:需要加载的函数名称。此处为get_term_to_local

3系统架构

appoll框架图_ver5.png

4网络架构

4.1统一接入配置

[global]
local_trans_id=00000002
recv_mode=http
sock=pad_server
local_trans_id:接入服务号
recv_mode:接入模式
sock:接入unix socket句柄

4.2统一接入处理流程配置

{
    "gateway": [
       {
            "trans_id": "00000001",
            "menu":
                {
                    "next_step": "term_start.json",
                    "next_step_err": "term_err_info.json",
                    "readme":"term_gateway"
                }

        },
        {
            "trans_id": "00000002",
            "menu":
                {
                    "next_step": "pad_start.json",
                    "next_step_err": "pad_err_info.json",
                    "readme":"pad_gateway"
                }

        }
    ]
}

5编程示例

下面以查余额为一个示例流程

5.1交易流程

交易中需要加载的的配置文件有:Mpos/xml/yl_trans.xml, Mpos/xml/userid/trans_next.xml,Mpos/json/

(1) Mpos/xml/yl_trans.xml为交易终端送上来的交易码转换为智付云系统内部的交易码:

<uniontrans>005</uniontrans><localtrans>10000005</localtrans><tradename>查余额</tradename><readme>查余额</readme>
Uniontrans:为交易终端上送的交易码
Localtrans:为智付云系统内部交易码

(2) Mpos/xml/userid/trans_next.xml为内部交易时的流程转换:

<begintrans>100000005</begintrans><nextflag>00</nextflag><F2>00000</F2><nexttrans>10000005</nexttrans><tag></tag><readme>余额查询</readme>
Begintrans:为当前交易码
Nextflag:为下步流程选择项,如begintrans为10000005时,nextflag为00时,nexttrans的值为10000005。
Nexttrans:为下批处理流程的交易码

(3) Mpos/json/*.json为交易流程处理驱动配置:

首先在bankuser.json中增加用户类型

{
    "bankuser": [
          {
            "usertype": "01",
            "describe": "term",
            "next_step": "term_menu.json"
        }
        
    ]
}
Usertype:为用户类型,此处为01
Describe:为描述字段,此处为term,标示为支付终端
Next_step:为“01”的用户菜单模板

(4) 下面为term_menu.json的菜单内容:

{
    "term_menu": [
        {
            "trans_id": "10000001",
            "menu":
                {
                    "next_step": "term_sign.json",
                    "next_step_err": "term_err_info.json",
                    "readme":"sign"
                }

       },
       {
            "trans_id": "10000005",
            "menu":
                {
                    "next_step": "term_query.json",
                    "next_step_err": "term_err_info.json",
                    "readme":"query"
                }

       },
       {
            "trans_id": "10000011",
            "menu":
                {
                    "next_step": "term_own_card_in.json",
                    "next_step_err": "term_own_card_in_err.json",
                    "readme":"card to card"
                }

       }
    ]
}
Trans_id:为本地交易码
Menu:为交易处理流程
Next_step:为本交易流程的模板json
Next_step_err:为本次交易流程错误流程处理模板json
Readme:为交易内容说明
    

(5) 查余额流程:

{
    "term_query": [
        {
            "trans_id":"10000005",
            "next_step":"temp_term_mac_pin.json"
        },{
            "trans_id":"10000005",
            "next_step":"temp_abc_trans.json",
            "trans_tag":"QryBal"
        },{
            "trans_id":"10000005",
            "next_step":"temp_term_pack.json"
        },{
            "trans_id":"10000005",
            "next_step":"term_end.json"
        }
    ]
}
Trans_id:为智付云系统内部交易码
next_step:为交易模板
trans_tag:动态模板标签,根据此标签定义不同的模板功能

(6) 流程处理解释:

当从yl_trans.xml中获得智付云本地交易码后执行term_query处理流程,此处需要智付云服务执行"next_step":"temp_term_mac_pin.json",这个模板,模板的内容为:

{
"temp_term_mac_pin": [
       {
            "service": "mpos_proxy",
            "command": [
                 {"op": "check_pos_info"},
                 {"op": "Gen_mac_87"},
                 {"op": "CheckPosNo"}
            ]
        },{
            "service": "mpos_pyhsm",
            "command": [
                {"op": "gen_mac"},
                {"op": "check_mac"},
                {"op": "dec_track"},
                {"op": "dec_pin"}
            ]
        }
    ]
}    
分别为通过调用mpos_proxy获取macbuf与检查流水号,然后调用mpos_pyhsm到DS系统去解密磁道,校验mac,解密pin 。
service:为当前流程需要的哪个服务模块处理
Command:为处理流程
op:服务模块的名称
    "op": "check_pos_info":校验终端合法性
    "op": "Gen_mac_87":校验终端mac
    "op": "CheckPosNo":校验流水号是否合法

做完这个模板交易后,执行模板:
"next_step":"temp_abc_trans.json":

{
    "trans_id":"10000005",
    "service": "mpos_pyabc",
    "command": [
        {"op": "get_url"},
        {"op": "pack_json"},
        {"op": "trans_abc"},
        {"op": "unpack_json"}
    ]
}
调用mpos_pyabc,调用打包程序发给农行后台,并从农行后台接收报文,并转成本地xml格式。
"op": "get_url":取得本次交易的url地址
"op": "pack_json":把本地xml标签转换为农行交易的json报文
"op": "trans_abc":把交易报文发往农行系统
"op": "unpack_json":从农行系统接收到返回报文后,把json报文转换成本地的xml标签

转换结束后,顺序执行模板 "next_step":"temp_term_pack.json":

{
    "trans_id":"10000005",
    "next_step":"temp_term_pack.json"
}
{
    "temp_term_pack": [
       {
            "service": "mpos_public",
            "command": [
                {
                    "op": "get_local_to_term"
                }
            ]
        },{
            "service": "mpos_proxy",
            "command": [
                {
                    "op": "GenMessage"
                },
                {
                    "op": "tel_pack"
                }                ,
                {
                    "op": "Gen_mac_84"
                }
            ]
        }
    ]
}
此处的功能为调用二型打包模板
"op": "get_local_to_term"把本地标签转换为二型交易码
"op": "GenMessage"打包余额查询显示内容
"op": "tel_pack" 打包支付终端报文
"op": "Gen_mac_84"生成返回报文的MAC域

把本地标签打包成二型终端报文,并找到当时的接入ID,发送给统一接入服务:

{
    "trans_id":"10000005",
    "next_step":"term_end.json"
}
{
    "term_end": [
        {
            "service": "mpos_public",
            "command": [
                {
                    "op": "log_list"
                },{
                    "op": "exchange_term_tpdu"
                }
            ]
        },
        {
            "service": "mpos_trans",
            "command": [
                {
                    "op": "pack8487enc"
                }
            ]
        },
        {
            "service": "mpos_public",
            "command": [
                {
                    "op": "EnableGatewayIndex"
                }
            ]
        },
         {
            "service": "mpos_epoll_server",
            "command": [
                {
                    "op": "None"
                }
            ]
        }
    ]
}
最后整个流程走完,重新打包发给交易终端
"op": "log_list":记录本次交易的明细日志
"op": "exchange_term_tpdu":翻转终端上送的TPDU
"op": "pack8487enc":组包返回报文给NAC
"op": "EnableGatewayIndex":取得统一接入网关的程序id

5.2代码示例

智付云系统中python动态加载框架所加载的服务必须是以int funcname(char resq,charresp)的方式,否则Python框架无法加载函数。
例如:

int get_term_to_local(unsigned char * resq, unsigned char *resp)
{
    unsigned char tmp_code[100];
    memset(tmp_code, 0, sizeof(tmp_code));

    ABC_XmlGetValue(resq, "FTRANS", tmp_code);
    int i;

    if(strlen(tmp_code)==0)
    {
       set_err(resq,resp, "100067");
   return -1;
    }
 
    for ( i = 0; i < MAX_TRANSTYPE; i++ )
    {
        printf("the tmpcode:[%s],[%s]\n", tmp_code, Trans_Code[i].uniontranscode);
        if( memcmp( tmp_code, Trans_Code[i].uniontranscode, 10 ) == 0 )
        {
            ABC_XmlChgValue(resp, "F3", Trans_Code[i].localtranscode);
            break;
        }
    }
    if (MAX_TRANSTYPE == i)
    {
        set_err(resq,resp, "100067");
        return -1;
    }
    return 0;
}
get_term_to_local为函数名称
char *resq:传入的xml数据总线
char *resp:返回的xml数据总线
从resq中取出FTRANS从配置系统缓存中外围交易码到本地加以码的对应,如果找不到返回“100067”的错误,如果找到则把本地交易码放入到F3的xml标签中。
请注意,每次调用函数返回的resp,Python动态加载框架都会加入xml数据总线中
在mpos_public中写好此函数后,需要加入init_func.xml中:
<libname>mpos_public</libname><func>get_term_to_local</func>

3.3交易组包流程

交易组包的模块在mpos_pyabc服务中,其功能完成了xml到json的互转,http报文的打包与解包,utf-8与GBK的互转。mpos_pyabc服务启动时,从Mpos/cfg_json/abc_trans_mapping.json中加载。

"10000005_pack": 
[
    {"out_code":"InputType",        "sys_code":"901",       "flag":"S"},
    {"out_code":"CardSeqNo",        "sys_code":"",          "flag":"S"},
    {"out_code":"ChannelId",        "sys_code":"04",        "flag":"S"},
    {"out_code":"ChannelName",      "sys_code":"CEPT",      "flag":"S"},
    {"out_code":"Term",             "sys_code":"CEPA",      "flag":"S"},
    {"out_code":"Teller",           "sys_code":"CEPA",      "flag":"S"},
    {"out_code":"AccNo",            "sys_code":"F20",       "flag":"T"},
    {"out_code":"Track2",           "sys_code":"F24",       "flag":"T"},
    {"out_code":"Track3",           "sys_code":"F25",       "flag":"S"},
    {"out_code":"EmvData",          "sys_code":"",          "flag":"S"},
    {"out_code":"AccPwd",           "sys_code":"F66",       "flag":"T"},
    {"out_code":"CurrCode",         "sys_code":"01",        "flag":"S"}
],
"10000005_unpack": 
[
    {"out_code":"RetCode",          "sys_code":"F61",       "sys_func":"deal_F61",      "flag":"F"},
    {"out_code":"RetMsg",           "sys_code":"F60",                                   "flag":"T"},
    {"out_code":"AccName",          "sys_code":"F18",                                   "flag":"T"},
    {"out_code":"Balance",          "sys_code":"F15",                                   "flag":"T"},
    {"out_code":"UseBal",           "sys_code":"F16",                                   "flag":"T"}
]
Out_code:农行标签
Sys_code:本地标签
Sys_func:变迁处理函数
Flag:标签S为固定值,T为动态标签值,F为动态标签值调用变迁函数后的值
1000005_pack为打包配置,1000005_uppack为解包配置