#include "pos_vtk/vtk_pos_protocol.h" #include "pos_vtk/vtk_tlv.h" #include "publicdata/public_define.h" #include #if (POS_VTK_EN) /****************************************************************************** * 模块说明:VTK 支付消息构造与解析 * * 负责将业务参数映射为 VTK TLV 参数,并打包/解析串口帧: * - vtk_build_preauth_request() :构造预扣款(CDP)请求。 * - vtk_build_settle_request() :构造结算(CRD)请求。 * - vtk_parse_payment_response(): 解析 POS 返回的 CDP/CRD 响应结果。 * * 内部约定: * - Tag 0x01: Message name (ASCII,例如 "CDP"/"CRD") * - Tag 0x03: Operation no. (ASCII 十进制) * - Tag 0x04: Amount (ASCII 十进制,单位:分) * - Tag 0x0D: Simple data block 的首字节为结果码: * 0 = Approved, 1 = Declined, 2 = Timeout, 其它 = Unknown ******************************************************************************/ /* 内部约定:结果码放在 Simple data block(Tag=0x0D) 的首字节 * 0 = Approved, 1 = Declined, 2 = Timeout, 其它 = Unknown */ #define VTK_TAG_MESSAGE_NAME ((U8_T)0x01) #define VTK_TAG_OPERATION_NO ((U8_T)0x03) #define VTK_TAG_AMOUNT ((U8_T)0x04) #define VTK_TAG_RESULT ((U8_T)0x0D) static VTK_POS_RESULT_E vtk_result_from_code(U8_T code) { switch (code) { case 0: return VTK_POS_RESULT_APPROVED; case 1: return VTK_POS_RESULT_DECLINED; case 2: return VTK_POS_RESULT_TIMEOUT; default: return VTK_POS_RESULT_UNKNOWN; } } static U32_T vtk_ascii_to_u32(const U8_T *p, U16_T len) { U32_T v = 0; U16_T i; if (!p || len == 0) return 0; for (i = 0; i < len; i++) { if (p[i] < '0' || p[i] > '9') break; v = v * 10U + (U32_T)(p[i] - '0'); } return v; } static U16_T vtk_build_common(const char *msgName, U32_T opNumber, U32_T amountMinor, U8_T *pOutFrame, U16_T outMax) { U8_T tlvBuf[VTK_MAX_APP_LEN]; U16_T tlvLen = 0U; if (!msgName || !pOutFrame) return 0; /* 消息名,例如 "CDP" / "CRD" */ if (!vtk_tlv_append_ascii(VTK_TAG_MESSAGE_NAME, msgName, tlvBuf, sizeof(tlvBuf), &tlvLen)) return 0; /* 操作号:十进制 ASCII */ if (!vtk_tlv_append_ascii_uint(VTK_TAG_OPERATION_NO, opNumber, 1U, tlvBuf, sizeof(tlvBuf), &tlvLen)) return 0; /* 金额(分):十进制 ASCII */ if (!vtk_tlv_append_ascii_uint(VTK_TAG_AMOUNT, amountMinor, 1U, tlvBuf, sizeof(tlvBuf), &tlvLen)) return 0; /* 组装 VTK 串口帧(VMC→POS) */ return vtk_build_frame(tlvBuf, tlvLen, VTK_PD_VMC_TO_POS, pOutFrame, outMax); } U16_T vtk_build_preauth_request(U32_T opNumber, U32_T amountMinor, U8_T *pOutFrame, U16_T outMax) { return vtk_build_common("CDP", opNumber, amountMinor, pOutFrame, outMax); } U16_T vtk_build_settle_request(U32_T opNumber, U32_T amountMinor, U8_T *pOutFrame, U16_T outMax) { return vtk_build_common("CRD", opNumber, amountMinor, pOutFrame, outMax); } U8_T vtk_parse_payment_response(const U8_T *pFrame, U16_T frameLen, VTK_POS_PAY_RESP_T *pResp) { VTK_FRAME_INFO_T info; const U8_T *pVal; U16_T valLen; if (!pFrame || !pResp) return 0; if (!vtk_parse_frame(pFrame, frameLen, &info)) return 0; /* 只接受来自 POS 的响应(0x97FB) */ if (info.protoDisc != VTK_PD_POS_TO_VMC) return 0; (void)memset(pResp, 0, sizeof(*pResp)); pResp->eResult = VTK_POS_RESULT_UNKNOWN; /* 消息名 */ if (vtk_tlv_find_first(info.pApp, info.appLen, VTK_TAG_MESSAGE_NAME, &pVal, &valLen)) { if (valLen > 3U) valLen = 3U; (void)memcpy(pResp->messageName, pVal, valLen); pResp->messageName[valLen] = '\0'; } /* 操作号 */ if (vtk_tlv_find_first(info.pApp, info.appLen, VTK_TAG_OPERATION_NO, &pVal, &valLen)) { pResp->operationNumber = vtk_ascii_to_u32(pVal, valLen); } /* 金额 */ if (vtk_tlv_find_first(info.pApp, info.appLen, VTK_TAG_AMOUNT, &pVal, &valLen)) { pResp->amountMinor = vtk_ascii_to_u32(pVal, valLen); } /* 结果码(Simple data block 首字节) */ if (vtk_tlv_find_first(info.pApp, info.appLen, VTK_TAG_RESULT, &pVal, &valLen)) { if (valLen > 0U) { pResp->eResult = vtk_result_from_code(pVal[0]); } } return 1; } /* * 外部使用示例: * * 1)构造并发送预扣款请求(CDP) * * U8_T frameBuf[VTK_MAX_FRAME_LEN]; * U16_T frameLen; * * // 操作号 12345,预扣 50 元(5000 分) * frameLen = vtk_build_preauth_request(12345U, 5000U, * frameBuf, sizeof(frameBuf)); * if (frameLen > 0U) * { * POS_VTK_UART_SEND(frameBuf, frameLen); * } * * 2)解析 POS 返回的响应(在串口接收处理里获得完整 VTK 帧后调用) * * VTK_POS_PAY_RESP_T resp; * if (vtk_parse_payment_response(frameBuf, frameLen, &resp)) * { * // resp.messageName 例如 "CDP"/"CRD" * // resp.operationNumber 例如 12345 * // resp.amountMinor 例如 5000 * // resp.eResult = VTK_POS_RESULT_APPROVED / DECLINED / TIMEOUT / UNKNOWN * } */ #endif /* POS_VTK_EN */