支付系统设计三:渠道网关设计02-客户端报文解析

news2024/11/25 2:40:59

文章目录

  • 前言
  • 一、后台配置管理
    • 1.1 渠道配置
      • 1.1.1 渠道基本信息新增
      • 1.1.2 渠道交易类型配置
      • 1.1.3 渠道商户信息配置
      • 1.1.4 账户配置
      • 1.1.5 交易类型机构配置
    • 1.2 渠道通讯配置
      • 1.2.1 内部渠道通讯
      • 1.2.1 外部渠道通讯
    • 1.3 资源配置
    • 1.4 证书管理
    • 1.5 路由配置
  • 二、运行时逻辑处理
    • 1. 控制接口定义
      • 1.1 内部交易网关控制接口
      • 1.2 外部交易网关控制接口
    • 2. 消息信息实体定义
    • 3. 初始化客户端消息描述信息
    • 4. 协议处理服务
      • 4.1 协议处理服务接口
      • 4.2 服务端协议处理抽象基类
      • 4.3 Http协议支付网关服务端实现
    • 5. 消息解析引擎
      • 5.1 消息解析引擎实现
      • 5.2 支付网关内部报文解析器
      • 5.3 支付网关外部报文解析器
    • 6. MessageDescription
  • 总结


前言

在这里插入图片描述
本篇将讲解paygw对渠道请求报文接收以及解析为系统所需格式参数的部分。
:此处的渠道包含paycore/支付渠道,也将paycore抽象为渠道,可以理解为内部渠道,对于paygw来说,调用方都属于渠道。

对应于如下的1、3部分:

在这里插入图片描述
在支付下单时:paycore调用paygw,对于paygw来说paycore是属于客户端;
在结果回调时:支付渠道调用paygw,对于paygw来说支付渠道是属于客户端;

所以对于如上两种情况,在后台管理系统进行通讯配置时候都将是作为客户端通信进行配置。


一、后台配置管理

在这里插入图片描述

1.1 渠道配置

1.1.1 渠道基本信息新增

在对接新支付渠道时候,需要在后台管理系统新增渠道基本信息,支付渠道类型包含:内部系统、银行、第三方支付、银联等,支付核心系统paycore也将作为内部支付渠道进行配置。

内部渠道基本信息新增

paycore系统也将被作为支付渠道进行配置:

在这里插入图片描述

外部渠道基本信息新增

配置外部支付渠道:

在这里插入图片描述

1.1.2 渠道交易类型配置

交易类型配置,很容易理解,就是我们接入了支付渠道的什么交易类型,是代扣、代发还是快捷,签约等交易类型。

内部渠道交易类型配置
对于交易类型配置,内部渠道只需要配置一次就行了。
在这里插入图片描述

外部渠道交易类型配置
对于交易类型配置,外部渠道对于新接入的支付渠道都需要进行配置。
在这里插入图片描述

1.1.3 渠道商户信息配置

渠道商户信息新增:
对应不同的支付渠道,所需要的参数信息不同,这里可以具体配置商户配置项,以完成不同渠道对参数的需求。

在这里插入图片描述
渠道商户信息配置项模板管理:
在这里插入图片描述

渠道商户信息配置项模板配置项新增:

在这里插入图片描述
渠道交易需要什么参数即可以在此配置,然后添加到对应商户对应的配置项模板中,即可完成商户参数的配置功能,在请求支付渠道的模板中可以获取到,组装为支付渠道报文信息。

1.1.4 账户配置

账户作为支付路由的输出项进行配置:
在这里插入图片描述
如上配置父商户也就是我们上面配置的渠道商户信息,子商户为支付路由系统根据上送参数路由匹配的输出项目,可以根据子商户找到父商户信息,进行交易参数的获取。

1.1.5 交易类型机构配置

不同交易类型所支持的交易机构(银行)不同,所以,此处为交易类型挂其所支持的交易机构信息,主要是用于支付路由的筛选。
在这里插入图片描述

1.2 渠道通讯配置

通讯配置需要配置paycore–>paygw以及paygw–>支付渠道两部分的通讯信息。

1.2.1 内部渠道通讯

在这里插入图片描述
在这里插入图片描述
如上是配置支付核心paycore—>paygw的通讯配置,此交互paycore是paygw的客户端,所以配置时候,客户/服务端选择客户端。

1.2.1 外部渠道通讯

外部渠道通讯配置包含paygw–>支付渠道的通讯配置(交易)和支付渠道的通讯配置–>(通知)配置。
此处不再展开,具体配置后文展开。

1.3 资源配置

paycore–>paygw之间的解析脚本配置:
在这里插入图片描述
paygw在收到paycore或者paygw在收到外部支付渠道的结果回调时候需要将请求参数进行转化为系统所需要格式的参数。对于不同支付渠道(内部/外部)的通讯每个交易类型都需要配置一次。对于客户端的通讯解析脚本是将请求paygw的参数转为为paygw所需要的统一格式参数,对于服务端通讯的解析脚本是将paygw发送出去的响应报文进行解析为paygw所需要的统一格式参数;

在这里插入图片描述
资源类型如下:
在这里插入图片描述
因为本篇只讲解客户端报文解析,所以系需要配置解析脚本就行了。

平台响应码新增:
在这里插入图片描述
支付渠道响应码与平台响应码转换:
在这里插入图片描述

1.4 证书管理

在和外部支付渠道交互时候涉及到加解密和验签工作,系统也将证书进行可配置化,预设多种加解密方法,具体设计见《支付系统设计之证书管理设计一:整体实现概览》、《支付系统设计之证书管理设计二:加/解密器适配实现》、《支付系统设计之证书管理设计三:自定义Classloader装载sdk》设计实现。

1.5 路由配置

此阶段还不涉及到路由系统,暂不展开。

二、运行时逻辑处理

1. 控制接口定义

1.1 内部交易网关控制接口

/**
 * @author Kkk
 * @Describe: 内部交易网关(主要处理内部系统请求)
 */
@RequestMapping(path="/inner",produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
@Controller
public class InnerGateway {
    private static final Logger logger = LoggerFactory.getLogger(InnerGateway.class);

    @Autowired
    private PayGwBizService payGwBizService;

    /**
     * 交易请求统一入口(支付核心请求调用)
     * @param request
     * @param response
     * @param instCode
     * @param transCode
     */
    @RequestMapping(value = "/{instCode}/{transCode}")
    public void accessHtml(HttpServletRequest request, HttpServletResponse response,
                           @PathVariable("instCode") String instCode,
                           @PathVariable("transCode") String transCode) {
        LoggerUtil.info(logger, "支付网关内部交易请求-客户端渠道编码({})-客户端交易码({})", instCode, transCode);
        
        //1、支付渠道合法性校验
        verifyChannel(instCode);
        
        //2、构建 context
        PayGwContext context = new PayGwContext();
        context.addParam(PayGwContext.ParamType.HTTP_SERVER_REQUEST, request);
        context.addParam(PayGwContext.ParamType.HTTP_SERVER_RESPONSE, response);
        //客户端渠道编码
        context.setClientInstCode(instCode);
        //客户端交易码
        context.setClientTransCode(transCode);
        //内部系统调用默认http协议
        context.addParam(PayGwContext.ParamType.PROTOCOL, ProtocolTypeEnum.HTTP);

        //3、执行交易
        payGwBizService.bizProcess(context);

        //4、渲染视图
        render(transCode, response, context.getMessageDescription().getClientResponseMessageEnvelope());

        LoggerUtil.info(logger, "支付网关内部交易请求-结束-客户端渠道编码({})-客户端交易码({})", instCode, transCode);
    }
}

内部交易网关控制接口,用于接收paycore系统的调用,核心处理逻辑渠道合法性校验、构建系统上线文,默认采用HTTP协议通讯。

1.2 外部交易网关控制接口

/**
 * @author Kkk
 * @Describe: 外部交易网关(统一外部通知入口)
 */
@RequestMapping(path="/outter",produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
@Controller
public class InnerGateway {
    private static final Logger logger = LoggerFactory.getLogger(InnerGateway.class);

    @Autowired
    private PayGwBizService payGwBizService;

    /**
     * 交易请求统一入口(支付核心请求调用)
     * @param request
     * @param response
     * @param instCode
     * @param transCode
     */
    @RequestMapping(value = "/{instCode}/{transCode}")
    public void accessHtml(HttpServletRequest request, HttpServletResponse response,
                           @PathVariable("instCode") String instCode,
                           @PathVariable("transCode") String transCode) {
        LoggerUtil.info(logger, "支付网关外部交易请求-客户端渠道编码({})-客户端交易码({})", instCode, transCode);
        
        //1、支付渠道合法性校验
        verifyChannel(instCode);
        
        //2、构建 context
        PayGwContext context = new PayGwContext();
        context.addParam(PayGwContext.ParamType.HTTP_SERVER_REQUEST, request);
        context.addParam(PayGwContext.ParamType.HTTP_SERVER_RESPONSE, response);
        //客户端渠道编码
        context.setClientInstCode(instCode);
        //客户端交易码
        context.setClientTransCode(transCode);
        //内部系统调用默认http协议
        context.addParam(PayGwContext.ParamType.PROTOCOL, ProtocolTypeEnum.HTTP);

        //3、执行交易
        payGwBizService.bizProcess(context);

        //4、渲染视图
        render(transCode, response, context.getMessageDescription().getClientResponseMessageEnvelope());

        LoggerUtil.info(logger, "支付网关外部交易请求-结束-客户端渠道编码({})-客户端交易码({})", instCode, transCode);
    }
}

外部交易网关控制接口,用于接收外部支付渠道系统的交易结果回调,核心处理逻辑渠道合法性校验、构建系统上线文,默认采用HTTP协议通讯。

具体业务处理:

 /**
     * 具体业务处理
     * @param context
     */
    @Override
    public void bizProcess(PayGwContext context) {
        //1、初始化客户端信息
        MessageDescription messageDescription = initMessageDescription(context);

        //2、执行交易
        try {
            //处理协议分发(目前支持http和MQ两种请求方式)
            ServiceManagerFactory.getServerManager((ProtocolTypeEnum) context.getParam(PayGwContext.ParamType.PROTOCOL)).accept(context);
            messageDescription.putData(PayGwConstant.IS_SUCCESS, true);
        } catch (PayGwException payGwException) {
            //解析为交易处理失败
        } catch (Exception e) {
            //解析为交易处理失败
        } finally {
            //3、生成响应报文
            LoggerUtil.info(logger,"交易({})-响应结果处理",context.getClientTransCode());
            MessageEnvelope messageEnvelope = resultProcesser.process(context);
            messageDescription.setClientResponseMessageEnvelope(messageEnvelope);
        }
    }

具体业务处理之前首先初始化客户端信息,即通过接口上送的渠道编码+交易类型编码找到支付渠道信息、交易类型信息、通讯信息、资源信息等。

2. 消息信息实体定义

在介绍具体业务处理之前,先介绍下包含一次请求所有基本信息MessageDescription,这个实体贯穿整个业务处理流程,包含此次请求的渠道信息、通讯信息、报文信息和运行时KV数据等。

/**
 * @author Kkk
 * @Describe: 消息信息,包含一次请求所有基本信息
 */
public class MessageDescription {
    /**
     * 支付渠道编码
     */
    private String instCode;
    /**
     * 交易id
     */
    private String transId;

    /**
     * 交易码
     */
    private String transCode;

    /**
     * 客户端支付机构
     */
    private PayInstitution clientPayInstitution;

    /**
     * 服务端支付机构
     */
    private PayInstitution serverPayInstitution;

    /**
     * 接收到客户端的请求报文
     */
    private MessageEnvelope clientRequestMessageEnvelope;

    /**
     * 返回到客户端请求报文
     */
    private MessageEnvelope clientResponseMessageEnvelope;

    /**
     * 发送到服务端的请求报文
     */
    private MessageEnvelope serverRequestMessageEnvelope;

    /**
     * 接收到服务端返回的请求报文
     */
    private MessageEnvelope serverResponseMessageEnvelope;

    /**
     * 所有运行时KV数据,包括接收到的数据和返回的数据;
     */
    private Map<String, Object> datas = new HashMap<>();

    /**
     * 支付网关通用结果
     */
    private PayGwResult payGwResult;

    /**
     * 当前处理阶段
     */
    private ProcessPhaseEnum processPhase;

    /**
     * 当前处理的通讯
     */
    private CommunicationEntity communicationEntity;
    
    /**
     * 增加属性到datas中
     * @param datas
     */
    public void putDatas(Map<String, Object> datas) {
        for (Map.Entry<String, Object> entry : datas.entrySet()) {
            if (entry.getValue() instanceof java.util.Map) {
                this.datas.putAll((Map) entry.getValue());
            } else {
                Object value = entry.getValue();
                Object oldValue = this.datas.get(entry.getKey());
                if (StringUtils.isNotBlank(value) || StringUtils.isBlank(oldValue)) {
                    this.datas.put(entry.getKey(), value);
                }
            }
        }
    }
    //... ...
}

3. 初始化客户端消息描述信息

根据接口上送的参数 渠道码instCode 获取到配置的渠道信息+交易码transCode 获取到渠道交易类型信息,然后即可以获取到配置的通讯信息了。由于客户端通讯只会有一个(服务端有些交易类型会有多个),所以直接获取,获取到通讯信息就能获取到对应的资源信息(脚本、模板信息)了,并构建MessageDescription 进行赋值后填充到上下文中。

 /**
     * 初始化客户端消息描述信息
     * @param context
     * @return
     */
    private MessageDescription initMessageDescription(PayGwContext context) {
        MessageDescription messageDescription = new MessageDescription();

        //客户端渠道
        String instCode = context.getClientInstCode();
        AssertUtils.isNotBlank(instCode, SystemErrorCode.INST_CODE_UNDEFINED,  new Object[]{instCode});
        PayInstitution payInstitution = payInstitutionService.getByInstCode(context.getClientInstCode());
        AssertUtils.isNotNull(payInstitution, SystemErrorCode.INST_CODE_UNSUPPORT, new Object[]{instCode});
        messageDescription.setClientPayInstitution(payInstitution);
        
        //客户端交易码
        String transCode = context.getClientTransCode();
        AssertUtils.isNotNull(TransactionEnum.getByCode(transCode), SystemErrorCode.TRANS_CODE_UNDEFINED, new Object[]{transCode});
        messageDescription.setTransCode(transCode);

        //一个客户端渠道,针对一个交易码,只会有一个交易配置
        List<InstTransTypeEntity> instTransTypeEntityList = payInstitution.getInstTransTypeEntityByTransCode(transCode);
        AssertUtils.isNotEmpty(instTransTypeEntityList, SystemErrorCode.UNFOUND_CONFIG, new Object[]{"客户端支付渠道交易"});
        InstTransTypeEntity instTransTypeEntity = instTransTypeEntityList.get(0);
        String transId = instTransTypeEntity.getTransId();
        AssertUtils.isNotNull(transId,SystemErrorCode.UNFOUND_CONFIG,new Object[]{"客户端支付渠道交易id"});
        context.setClientTransId(transId);
        messageDescription.setTransId(transId);

        //获取客户端通讯,因为支付核心也抽象成的支付机构,入参也需要用通讯进行解析;
        List<CommunicationEntity> communicationEntityList = conmmunicationCacheManager.getByType(instTransTypeEntity.getId(), CsFlagEnum.CLIENT.getCode());
        AssertUtils.isNotEmpty(communicationEntityList, SystemErrorCode.UNFOUND_CONFIG, new Object[]{"客户端通讯"});

        //支付网关的调用方只会有一个通讯,也就是只会通讯一次;
        CommunicationEntity communicationEntity = communicationEntityList.get(0);
        messageDescription.setCommunicationEntity(communicationEntity);

        //当前处理阶段
        messageDescription.setProcessPhase(ProcessPhaseEnum.CLIENT_REQUEST_RECEIVE);
        context.addParam(PayGwContext.ParamType.MESSAGE_DESCRIPTION, messageDescription);

        return messageDescription;
    }

4. 协议处理服务

4.1 协议处理服务接口

/**
 * @author Kkk
 * @Describe: 协议处理服务(做统一封装,客户端与服务端只是调用的顺序不一样,但都包含一进一出的转换)
 */
public interface ProtocolService {

    /**
     * 转换进来消息
     * @param context
     */
    void convertInMessage(PayGwContext context);
    
    /**
     * 转换出去消息
     * @param context
     */
    void convertOutMessage(PayGwContext context);
}

类图实现如下:
在这里插入图片描述
对于处理客户端消息,此时paygw属于服务端,所以此处我们看服务端协议处理逻辑。

4.2 服务端协议处理抽象基类

/**
 * @author Kkk
 * @Describe: 服务端协议处理抽象基类,定义服务端协议处理框架
 */
@Service("protocolServerService")
public abstract class ProtocolServerService implements ProtocolService {
    /**
     * 交易业务模板管理
     */
    @Autowired
    private TemplateManager templateManager;

    /**
     *消息处理,抽象为三个阶段:
     *  1、消息读取,读取成object,供后面脚本进行具体内容解析;
     *  2、消息接收到逻辑处理,包括解析,发送,更多的交由效果引擎处理;
     *  3、需要返回的消息处理,转换返回的消息格式;
     * @Params
     * @Return void
     * @Exceptions
     */
    public void hand(PayGwContext context) {

        //1、转换接收到(进来)的消息;
        convertInMessage(context);

        //2、消息发送;
        messageReceive(context);

        //3、转换需要返回消息;
        convertOutMessage(context);
    }

    /**
     * @Description
     * @Params
     * @Return void
     * @Exceptions
     */
    protected void messageReceive(PayGwContext context) {
        templateManager.getTemplate(context.getClientTransCode()).execute(context);
    }
}

4.3 Http协议支付网关服务端实现

/**
 * @author Kkk
 * @Describe: 支付网关服务端,用于接收请求
 */
@Service("httpServerService")
public class HttpServerService extends ProtocolServerService {
    private static final Logger logger = LoggerFactory.getLogger(HttpServerService.class);

    /**
     * 消息解析
     */
    @Autowired
    protected MessageReceiver messageReceiver;
    
    @Override
    public void convertInMessage(PayGwContext context) {
        LoggerUtil.info(logger, "交易({})-读取报文-开始", context.getClientTransCode());
        MessageDescription messageDescription = context.getMessageDescription();

        //1、获取支付机构配置的接收消息的类型(map,text)
        CommunicationEntity communicationEntity = messageDescription.getCommunicationEntity();
        MessageFormatEnum messageFormatEnum = communicationEntity.getReceiveMessageFormat();//报文格式
        EncodeEnum encodeEnum = communicationEntity.getReceiveMessageEncode();//报文编码

        //2、根据不同的消息类型进行不同的读取(paycore传过来的参数内容)
        Object message = messageRead(context.getHttpServletRequest(), messageFormatEnum, encodeEnum.getMessage());
        LoggerUtil.info(logger, "交易({})-读取报文-报文内容:\n" + message, context.getClientTransCode());
        
        MessageEnvelope clientRequestMessageEnvelope = new MessageEnvelope();
        clientRequestMessageEnvelope.setMessageFormat(messageFormatEnum);
        clientRequestMessageEnvelope.setEncode(encodeEnum);
        clientRequestMessageEnvelope.setContent(message);
        messageDescription.setClientRequestMessageEnvelope(clientRequestMessageEnvelope);
        LoggerUtil.info(logger, "交易({})-读取报文-结束", context.getClientTransCode());

        //3、报文解析(从object->map的转换,采用脚本实现)
        messageReceiver.receive(context);
    }


    @Override
    public void convertOutMessage(PayGwContext context) {

    }
    
    /**
     * 读取消息
     * @param request
     * @param messageType
     * @param charSet
     * @return
     */
    private Object messageRead(HttpServletRequest request, MessageFormatEnum messageType, String charSet) {
        switch (messageType) {
            case FORM:
                return readMap(request);
            case BYTE:
                return readByte(request);
            default:
                return readText(request, charSet);
        }
    }
    // ... ...
}

5. 消息解析引擎

5.1 消息解析引擎实现

/**
 * @author Kkk
 * @Describe: 消息解析引擎实现
 */
@Component
public class MessageParseEngineImpl implements MessageParseEngine {
    private static final Logger logger = LoggerFactory.getLogger(MessageParseEngineImpl.class);

    @Autowired
    private GroovyScriptCache groovyScriptCache;

    /**
     * 消息解析
     * @param context
     */
    @Override
    public void messageParse(PayGwContext context) {
        MessageDescription messageDescription = context.getMessageDescription();
        //1.获取待解析报文
        Object parseMessage = getParseMessage(messageDescription);
        LoggerUtil.info(logger, "交易-开始报文解析");

        //2.获取报文解析器(加载groovy脚本,有可能是默认实现的java类,脚本统一实现MessageParser接口)
        MessageParser messageParser = findParser(context);

        //3.进行报文解析 TODO 报文解析
        Object obj = messageParser.parse(context, parseMessage);
        messageDescription.putDatas(BeanUtils.beanToMap(obj));
    }

    /**
     * 获取待解析报文
     * @param messageDescription
     * @return
     */
    public Object getParseMessage(MessageDescription messageDescription) {
        Object message = null;
        ProcessPhaseEnum processPhase = messageDescription.getProcessPhase();
        switch (processPhase) {
            case CLIENT_REQUEST_RECEIVE://客户端请求(支付核心请求或支付渠道异步通知)
                message = messageDescription.getClientRequestMessageEnvelope().getContent();
                break;
            case SERVER_RESPONSE_RECEIVE://服务端响应
                message = messageDescription.getServerResponseMessageEnvelope().getContent();
                break;
            default:
                LoggerUtil.warn(logger, "processPhase = {} 没有加 switch.", processPhase);
                throw new PayGwException(SystemErrorCode.SYSTEM_ERROR);
        }
        return message;
    }

    /**
     * 获取报文解析器
     * @param context
     * @return
     */
    private MessageParser findParser(PayGwContext context) {
        String parserName = getParserName(context);
        MessageParser messageParser = groovyScriptCache.getMessageParser(parserName);
        if (messageParser == null) {
            LoggerUtil.error(logger, "未找到交易的报文解析器({})", parserName);
            throw new PayGwException(SystemErrorCode.SYSTEM_ERROR);
        }
        return messageParser;
    }

    /**
     * 获取报文解析器名称
     * @param context
     * @return
     */
    private String getParserName(PayGwContext context) {
        MessageDescription messageDescription = context.getMessageDescription();
        return messageDescription.getCommunicationEntity().getMessageParserId();
    }
}

5.2 支付网关内部报文解析器

/**
 * @author Kkk
 * @Describe: 支付网关内部报文解析器
 */
public class PayCoreParser implements MessageParser {

    @Override
    public Object parse(PayGwContext context, Object message) {
        Map<String, Object> map = JSON.parseObject(StringUtils.valueOf(message), new TypeReference<HashMap<String, Object>>() {
        });
        // 获取extend1并解析其中的json,并将其放入map中
        def body = (HashMap<String, Object>)map.get("body")
        def extend1 = body.get("extend1")
        if(StringUtils.isNotBlank(extend1)){
            def propertyMap = JSON.parseObject(StringUtils.valueOf(extend1))
            map.get("body").putAll(propertyMap)
        }
        return map;
    }
}

对于内部支付渠道上送报文的解析工作比较简单,主要是将上送参数中的扩展字段放入body中,以供业务处理时候直接获取。

即将如下请求路径:http://localhost:8081/inner/paycore/deduct 的请求报文进行转为为如下:

{
    "header": {
        "channelCode": "paycore",
        "transCode": "deduct",
        "channelDateTime": "20230510221050",
        "channelTransNo": "2023051000001"
    },
    "body": {
        "deptCode": "0308",
        "transAmount": 100,
        "acctNo": "622002324354354645665",
        "acctName": "张三",
        "mediaType": "01",
        "certType": "00",
        "certNo": "320390199909091234",
        "purpose": "代扣还款",
        "bizOrderNo": "20230510274235345001",
        "systemId": "1010",
        "productCode": "001",
        "extend1": "{"dcFlag":"D","orgId":"123"}"
    }
}

转化为如下:

{
    "header": {
        "channelCode": "paycore",
        "transCode": "deduct",
        "channelDateTime": "20230510221050",
        "channelTransNo": "2023051000001"
    },
    "body": {
        "deptCode": "0308",
        "transAmount": 100,
        "acctNo": "622002324354354645665",
        "acctName": "张三",
        "mediaType": "01",
        "certType": "00",
        "certNo": "320390199909091234",
        "purpose": "代扣还款",
        "bizOrderNo": "20230510274235345001",
        "systemId": "1010",
        "productCode": "001",
		"dcFlag":"D",
		"orgId":"123"
    }
}

5.3 支付网关外部报文解析器

/**
 * @author kkk
 * @description 兴业银行代发交易结果通知解析脚本
 */
class CIBDeputeNotifyAsyParser implements MessageParser {
    def logger = LoggerFactory.getLogger(CIBDeputeNotificationAsyParser.class)
    def resp_code_fail = ["E0100","E0101"]
    def resp_code_success = ["E0000"]
    /** 证书服务 */
    @Autowired
    CertService certService

    @Override
    Object parse(PayContext context, Object message) {
        def messageParserResult = new MessageParserResult()
        messageParserResult.setOrgProcessStatus(ProcessStatusEnum.PROCESSING.getCode())//处理中
        try {
            Object result = JSON.parse(message)
            JSONObject jobj = (JSONObject) result

            logger.info("[兴业银行-单笔代付-异步通知] 同步返回: ({})", message)

            //验证签名
            def flag = verifySign(context, message)
            if (!flag) {
                throw new Exception("[兴业银行-单笔代付-异步通知] 返回参数,验签失败!")
            }

            def respCode= jobj.get("respCode")
            def respMsg= jobj.get("respMsg")
            messageParserResult.setInstRespCode(respCode)
            messageParserResult.setInstRespMsg(respMsg)

            if(resp_code_success.contains(respCode)){
                messageParserResult.setTransStatus(TransStatusEnum.SUCCESS.getCode())
                messageParserResult.setProcessStatus(ProcessStatusEnum.FINISH.getCode())
            }else if(resp_code_fail.contains(respCode)){
                messageParserResult.setTransStatus(TransStatusEnum.FAIL.getCode())
                messageParserResult.setProcessStatus(ProcessStatusEnum.FINISH.getCode())
            }else {
                messageParserResult.setTransStatus(TransStatusEnum.PROCESS.getCode())
                messageParserResult.setProcessStatus(ProcessStatusEnum.PROCESSING.getCode())
            }
            return messageParserResult

        } catch (Exception e) {
            LoggerUtil.error(logger, "[兴业银行-单笔代付-异步通知] 报文解析异常异常", e)
            messageParserResult.setTransStatus(TransStatusEnum.PROCESS.getCode())
            messageParserResult.setProcessStatus(ProcessStatusEnum.PROCESSING.getCode())
            messageParserResult.setOrgProcessStatus(ProcessStatusEnum.PROCESSING.getCode())
            messageParserResult.setInstTransDate(DateUtil.getCurrentDate())
            return messageParserResult
        }
    }

    /**
     * 验签
     */
    boolean verifySign(PayContext context, String resData) {
        def certCodePublic = context.getMessageDescription().getData("merExtends").get("certCodePublic")
        Map<String,String> resMap=MapUtils.covertToJSON(resData)
        String mac=resMap.get("mac")
        resMap.remove("mac")
        String oriSign=MapUtils.generateParamStr(resMap);
        boolean  vflag=  certService.checkSign(certCodePublic,mac,oriSign)
        logger.info("[兴业银行-单笔代付-异步通知],请求签名值({}),验签结果({})",mac,vflag)
        return vflag
    }
}

对于外部请求报文的解析,需要具体渠道具体分析了,因为每个支付渠道回调的报文格式以及参数字段不同,所以要根据外部支付渠道的具体报文进行脚本的编写,将不同支付渠道的参数解析为paygw系统所需要的统一格式参数。

6. MessageDescription

客户端报文解析完成后MessageDescription对应的值信息如下:
在这里插入图片描述


总结

本篇主要讲解了paygw渠道网关系统接收渠道客户端系统请求后的报文解析处理流程,包括paycore支付请求(下单、退款、签约等)以及支付渠道的交易结果回调处理流程。

处理思路就是根据接口上送的渠道码+交易码找到后台配置的支付渠道–>渠道交易类型–>渠道交易类型通讯–>资源信息(解析脚本),然后通过通讯配置的协议类型+报文格式+报文编码进行上送报文的读取,然后根据通讯关联的资源信息(解析脚本)对上送报文进行解析,解析为系统所需要的通用格式的参数,进入后一步处理。

下一步将进入参数校验阶段,由于我们没有按照一个交易类型或者每个支渠道各自定义接口然后并定义对应实体进行参数获取,然后依据一些框架进行参数校验,所以我们需要设计参数校验器进行上送参数的校验。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/519957.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Fluent的视角设置

1 背景 针对不同设计方案在同一工况下的差异点进行细节分析&#xff0c;其中一个很重要的要求就是需要在同一视角下比较不同设计方案的差异性。设置视角并进行快速重用是很重要的提高仿真效率的方法。 2 视角设置 视角设置有2类方法&#xff1a;手动拖拉和定量指定。 手动拖拉即…

一图看懂 tomli 模块:一个 TOML解析器、使用 mformat -toc 生成的目录,资料整理+笔记(大全)

本文由 大侠(AhcaoZhu)原创&#xff0c;转载请声明。 链接: https://blog.csdn.net/Ahcao2008 一图看懂 tomli 模块&#xff1a;一个 TOML解析器、使用 mformat -toc 生成的目录&#xff0c;资料整理笔记&#xff08;大全&#xff09; &#x1f9ca;摘要&#x1f9ca;模块图&am…

前端开发中,定位bug的几种常用方法

目录 第一章 前言 第二章 解决bug的方法 2.1 百度 2.2 有道翻译 2.3 debugger 2.4 console.log 日志打印 2.5 请求体是否携带参数 2.6 注释页面渲染代码 2.7 其他 第三章 尾声 备注&#xff1a;该文章只是本人在工作/学习中常用的几种方法&#xff0c;如果有不对大家…

智慧园区移动应用发展面临瓶颈,如何解决?

智慧园区移动应用将在多元化服务、生态建设、智能化管理和跨界融合等方面发展&#xff0c;成为园区管理和服务的重要手段之一&#xff0c;为员工和企业提供更加智能化和便捷化的管理和服务。伴随着智慧城市的建设和智慧园区的崛起&#xff0c;智慧园区数字一体化建设成为园区发…

docker搭建nginx负载均衡

一点小背景 docker起了几个服务&#xff0c;没有配置端口映射&#xff0c;导致不能通过网络访问。当然&#xff0c;更简单的方式是加端口映射&#xff0c;笔者的情况更复杂一些&#xff0c;就想到了用nginx映射一下。 Nginx&#xff08;发音同“engine X”&#xff09;是异步框…

避免“文献综抄”,5种写作结构助你完成文献综述→

很多作者可能有过这样的体验&#xff1a;读了很多文献&#xff0c;但在写综述的时候总感觉不像是在写文献综述&#xff0c;更像在写文献总结 如果引用方面不注意&#xff0c;甚至会成为文献综抄。 那么&#xff0c;你可以参考下我们整理的以下资料哦~ 01 文献总结和文献综述的…

DCDC反馈电阻的阻值如何取值?

DCDC芯片的反馈电阻 下图为我们公司现在常用的两款DCDC芯片&#xff0c;TPS54335ADDA/TI和LMR14050SDDA/TI。 其中RFBT和RFBB都是反馈电阻&#xff0c;可以通过调节这两个电阻的比值来输出 预期电压。 FBT&#xff1a;feedback top&a…

保姆级丨XAMPP安装使用

0x00 前言 XAMPP 是一个完全免费&#xff0c;易于安装的 Apache 发行版&#xff0c;包含 MariaDB &#xff0c; PHP 和 Perl 。 XAMPP 开源软件包已经设置为非常容易安装和使用。 0x01 环境说明 Windows 11xampp-windows-x64-8.2.4-0-VS16-installer 0x02 准备工作 首先要访问…

数据库分区;pgAdmin操作pgsql分区;修改pgsql数据库名字

目录 分区 什么是分区 分区的优势 pgAdmin操作pgsql分区 创建父表 创建分区 数据入库分区 扩展&#xff08;按天创建分区脚本&#xff09; 修改数据库名字 链接 分区 什么是分区 指将一个大的表或索引分成多个小的、独立的部分&#xff0c;每个部分称为一个分区&#x…

带你了解家居智能的心脏:物联网关

本文将介绍家庭物联网关的相关内容&#xff0c;将明白物联网关在家庭这个场景当中的应用。现在市面上各种各样的智能家居的家电或者其他设备非常多&#xff0c;那么这就需要一个智能的设备去对所有的家电进行管控。这样一个设备就是家庭智能网关&#xff0c;家庭物联网关是家居…

Py之onnx:onnx/onnxruntime库的简介、安装、使用方法之详细攻略

Py之onnx&#xff1a;onnx/onnxruntime库的简介、安装、使用方法之详细攻略 目录 onnx/onnxruntime库的简介 onnx/onnxruntime库的安装 onnx/onnxruntime库的使用方法 1、基础用法 onnx/onnxruntime库的简介 Open Neural Network Exchange&#xff08;ONNX&#xff09;是一…

Python每日一练(20230513) 粉刷房子 I\II\III Paint House

目录 1. 粉刷房子 Paint House 2. 粉刷房子 II Paint House-ii 3. 粉刷房子 III Paint House-iii &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练 专栏 Java每日一练 专栏 leetcode题号分别为&#xff1a; 256、2…

32 位 ARM® Cortex®-M0+ 单片机,PY32F002B 系列微控制器

PY32F002B 系列微控制器采用高性能的 32 位 ARM Cortex-M0内核&#xff0c;宽电压工作范围的 MCU。嵌入24Kbytes Flash 和 3Kbytes SRAM 存储器&#xff0c;最高工作频率 24MHz。包含多种不同封装类型多款产品。 芯片集成I2C、SPI、USART 等通讯外设&#xff0c;1 路 12bit ADC…

UNIAPP实战项目笔记70 购物车删除商品的前后端交互

UNIAPP实战项目笔记70 购物车删除商品的前后端交互 思路 需要用到vuex 传id值到后端,删除指定id购物车数据 案例截图 购物车商品编辑页面 代码 后端代码 index.js var express require(express); var router express.Router(); var connection require(../db/sql.js); va…

6.Go语言学习笔记-结合chatGPT辅助学习Go语言底层原理

1、Go版本 go1.14.15 2、汇编基础 推荐阅读:GO汇编语言简介 推荐阅读:A Quick Guide to Gos Assembler - The Go Programming Language 精简指令集 数据传输: MOV/LEA 跳转指令: CMP/TEST/JMP/JCC 栈指令: PUSH/POP 函数调用指令: CALL/RET 算术指令: ADD/SUB/MUL/DIV …

PFCdocumentation_FISH Rules and Usage

目录 FISH Scripting FISH Rules and Usage Lines Data Types Reserved Names for Functions and Variables Scope of Variables Functions: Structure, Evaluation, and Calling Scheme Arithmetic: Expressions and Type Conversions Redefining FISH Functions Ex…

hadoop03

MapReduce是Hadoop系统核心组件之一&#xff0c;它是一种可用于大数据并行处理的计算模型、框架和平台&#xff0c;主要解决海量数据的计算&#xff0c;是目前分布式计算模型中应用较为广泛的一种。 练习&#xff1a;计算a.txt文件中每个单词出现的次数 hello world hello ha…

Android View 事件分发机制,看这一篇就够了

在 Android 开发当中&#xff0c;View 的事件分发机制是一块很重要的知识。不仅在开发当中经常需要用到&#xff0c;面试的时候也经常被问到。 如果你在面试的时候&#xff0c;能把这块讲清楚&#xff0c;对于校招生或者实习生来说&#xff0c;算是一块不错的加分项。对于工作…

三种方式在HTML使用阿里字体图标--iconfont阿里巴巴矢量图标库

好久没用到阿里巴巴的图标&#xff0c;突然要用到就发现不会用了&#xff0c;只会导出png格式的图标png了 目录 1、字体图标 方法一、本地使用通过类名使用阿里矢量图标 1、把图标添加入库 2、把图标添加到项目 3、下载字体图标 4、使用文件 5、在对应的HTML页面上引入…

瑞吉外卖项目笔记01——环境搭建、后台登录功能

1.1 数据库 数据库&#xff1a; 创建一个空白数据库reggie&#xff0c;然后导入执行SQL文件创建的表如下&#xff1a; 1.2 项目依赖 项目依赖&#xff1a; pom.xml文件内的依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"…