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

文章目录

  • 前言
  • 一、后台配置管理
    • 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);@Autowiredprivate 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、构建 contextPayGwContext 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);@Autowiredprivate 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、构建 contextPayGwContext 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*/@Overridepublic 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 {/*** 交易业务模板管理*/@Autowiredprivate 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);/*** 消息解析*/@Autowiredprotected MessageReceiver messageReceiver;@Overridepublic 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);}@Overridepublic 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);@Autowiredprivate GroovyScriptCache groovyScriptCache;/*** 消息解析* @param context*/@Overridepublic 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 {@Overridepublic 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"]/** 证书服务 */@AutowiredCertService certService@OverrideObject parse(PayContext context, Object message) {def messageParserResult = new MessageParserResult()messageParserResult.setOrgProcessStatus(ProcessStatusEnum.PROCESSING.getCode())//处理中try {Object result = JSON.parse(message)JSONObject jobj = (JSONObject) resultlogger.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.rhkb.cn/news/26690.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

完美解决小爱同学蓝牙音箱(包括触屏版)连接电脑后找不到音频设备问题

前言&#xff1a; 最近入手了一台Redmi小爱同学触屏版8Pro,发现正常连接手机蓝牙是可以播放音乐的&#xff0c;但是连接电脑后虽然提示连接成功&#xff0c;但是不能播放音乐&#xff0c;在选择播放设备处没有显示该设备&#xff0c;如下图是正常的状态 &#xff08;正常的样子…

小米互联网音箱首次促销 你买还是不买?

小米互联网音箱自去年底发布以来&#xff0c;凭借着时尚外观&#xff0c;海量内容&#xff0c;还有不俗音质创造了屡次售罄的佳绩。而近日&#xff0c;这款高性价比互联网音箱再度升级&#xff0c;可正式支持接入米家&#xff0c;从而实现更强大的远程控制功能。为了感谢广大消…

智能音箱的差评|为什么我要买一堆垃圾回来吃灰!

我终于没有躲过智能音箱的坑&#xff0c;还是在618的时候买了叮咚2代回来玩。 作为第一批亚马逊echo智能音箱的体验用户&#xff0c;这次中文版的坑是补踩了。 用了一个月&#xff0c;叮咚同学目前吃灰中&#xff0c;每天唤醒次数不超过一次。 在痛心疾首的检讨中&#xff0c;认…

多用户分销商城系统开发及多商家入驻的功能介绍

多用户商城是一种电子商务平台&#xff0c;允许多个商家在同一个平台上出售自己的产品或服务。这种商城模式可以吸引更多的消费者&#xff0c;因为他们可以在同一个网站上找到多个商家的产品&#xff0c;而不需要分别访问不同的网站。同时&#xff0c;多用户商城也可以为商家提…

在智能家居音箱领域上的音频功放芯片IC

目前&#xff0c;音频功放芯片主要应用于手机、音响、车载、可穿戴设备、计算机设备、智能家居等领域。随着人机交互逐步落地&#xff0c;从应用广度上对音频功放芯片需求完全放开&#xff0c;截止2021年以“智能音箱”、“智能家居”为代表的音频智能终端也持续放量&#xff1…

支付渠道网关设计2.0版本

文章目录 支付渠道网关设计2.0版本前言概述业务架构设计技术分层设计友情链接 支付渠道网关设计2.0版本 前言 之前写过一篇支付渠道网关设计的文章https://shanglishuai.blog.csdn.net/article/details/103545708 随着业务的复杂性&#xff0c;对支付业务的理解也越来越深刻…

全志成智能音箱最大赢家,索尼、腾讯、小米、百度、京东都在用,累计出货超千万台

全球范围内智能音箱销量都在持续增长&#xff0c;报告显示&#xff0c;在2018年第一季度的出货量已经超过900万台。中国已经成为仅次于美国的全球第二大智能音箱消费市场&#xff0c;其中国产的智能音箱产品更是占据了大头。我们都知道智能音箱的“智能”主要依靠它的心脏&…

小米荣获首张智能家居系统用户体验水平泰尔测评证书

近日&#xff0c;“小米全屋智能家居系统V1.0”荣获中国泰尔实验室颁发的首张智能家居系统用户体验水平泰尔测评证书。中国泰尔实验室依据中国通信标准化协会团体标准T/CCSA 357-2022 《移动互联网智能家居系统 用户体验评测方法》对其进行评估测试&#xff0c;测试总分达到用户…

小米商城官网(登录页,首页,详情页,我的购物车页,我的订单页,确认订单页)HTML+CSS+JS

文章目录 前言一、登录页二、首页三、我的购物车页四、我的订单页五、确认订单页六、详情页七、整体结构和效果图总结 前言 仿小米商城官网项目是本人实训内容&#xff0c;实训老师带着做的首页和登录页&#xff0c;本人在此基础上加入了我的购物车页&#xff0c;我的订单页&am…

小米网关+HomeAssistant获取智能硬件数据

因为想获取小米智能网关的温湿度传感器数据的可视化&#xff0c;希望可以和智能网关交互。 最后的技术方案采用&#xff1a;dockerHomeAssistant 1、安装docker 我是在mac上做的&#xff0c;所以去官网下载&#xff0c;一键安装 2、下载HomeAssistant镜像 docker pull hom…

小米智能家居接入智能家居平台homeassistant的方法

[原文] 在安装和设置完homeassistant之后,我们终于来到激动人心的一步——把智能家居产品接入homeassistant了。把智能家居产品接入homeassistant智能家居平台之后,就可以实现用亚马逊echo智能音箱或叮咚智能音箱语音控制智能家居产品;也可以实现打破品牌壁垒,让不同的品牌…

【笔记】可能是唯一能让天猫精灵方糖播放本地音乐的智能方案

微信关注公众号 “DLGG创客DIY” 设为“星标”&#xff0c;重磅干货&#xff0c;第一时间送达。 前言 如上图所示&#xff0c;我家也有个同款的天猫精灵方糖&#xff0c;“买了”&#xff08;好像是个奖品&#xff09;好几年了&#xff0c;利用率不高&#xff0c;主要因为资源不…

小度音箱音响酒店版使用说明

小度酒店版无屏音响配网、管理流程 1、下载“小度在酒店”APP 下载路径&#xff1a;https://www.hnzhzn.cn/xiaodujd.apk (用浏览器打开&#xff0c;目前只支持安卓手机安卓) 2、登录“小度在酒店”APP 使用酒店方提供的、并已经授权酒店使用的百度账号登录&#xff0c;登录后…

【小米商城-1 注册功能】

1.搭建环境&#xff08;略&#xff09; 2.用户名验证 1、通过创建一个QueryRunner对象创建数据库连接池 2、编写Sql查询语句 3、执行sql&#xff0c;返回user&#xff0c;如果数据库查到数据则返回数据&#xff0c;否则返回为空&#xff0c;然后将数据传到业务层也就是我们的s…

开源版GPT-3来了?Meta发布LLaMa,多数任务效果超越GPT-3,已开源

源 | 量子位 编 | 泽南 ChatGPT的热度稍有平息&#xff0c;蛰伏已久的Meta就迅速放出“大招”&#xff1a; 一次性发布四种尺寸的大语言模型LLaMA&#xff1a;7B、13B、33B和65B&#xff0c;用小杯、中杯、大杯和超大杯来解释很形象了有木有&#xff08;Doge&#xff09;。 还声…

单卡就能跑的大模型等效GPT-3!Meta发布大语言模型LLaMA,大小从7B到65B不等

Pine 发自 凹非寺量子位 | 公众号 QbitAI ChatGPT的热度稍有平息&#xff0c;蛰伏已久的Meta就迅速放出“大招”&#xff1a; 一次性发布四种尺寸的大语言模型LLaMA&#xff1a;7B、13B、33B和65B&#xff0c;用小杯、中杯、大杯和超大杯来解释很形象了有木有&#xff08;Doge&…

js 解密

Ai - Chat (sbaliyun.com) 在分析接口的时候&#xff0c;我们看到请求做了加密 查看事件我们发信啊了方法 callCHATGPT() 得到 callCHATGPT()的代码 async function callCHATGPT() {var responseText1 document.getElementById("chatgpt-response");responseText1.i…

基数树简介

文章目录 1.简介2.为什么要设计基数树&#xff1f;3.应用4.操作插入删除查找 5.小结参考文献 1.简介 基数树&#xff08;Radix Trie&#xff09;也叫基数特里树或压缩前缀树&#xff0c;是一种多叉树&#xff0c;一种更节省空间的 Trie&#xff08;前缀树&#xff09;。 基数…

0101代理模式详解-设计模式-spring

1 概述 代理模式是一种结构型设计模式&#xff0c;它通过提供一个代理对象来控制对另一个对象的访问。在代理模式中&#xff0c;代理对象充当原始对象的接口&#xff0c;客户端可以通过代理对象来访问原始对象&#xff0c;代理对象则可以控制对原始对象的访问&#xff0c;并在…

chatgpt赋能python:Python中的按位取反

Python中的按位取反 Python中的按位取反是一种常见的操作&#xff0c;它可以让我们快速地对二进制的数字进行取反操作。在本文中&#xff0c;我们将介绍Python中的按位取反操作&#xff0c;并探讨它的用途和示例。 什么是按位取反 按位取反是一种将二进制数中的每一位进行反…