文章目录
- 前言
- 一、后台配置管理
- 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支付请求(下单、退款、签约等)以及支付渠道的交易结果回调处理流程。
处理思路就是根据接口上送的渠道码+交易码找到后台配置的支付渠道–>渠道交易类型–>渠道交易类型通讯–>资源信息(解析脚本),然后通过通讯配置的协议类型+报文格式+报文编码进行上送报文的读取,然后根据通讯关联的资源信息(解析脚本)对上送报文进行解析,解析为系统所需要的通用格式的参数,进入后一步处理。
下一步将进入参数校验阶段,由于我们没有按照一个交易类型或者每个支渠道各自定义接口然后并定义对应实体进行参数获取,然后依据一些框架进行参数校验,所以我们需要设计参数校验器进行上送参数的校验。