unit PayIntf_MisDll; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type { /* 交易類型: '00'消費'01'撤銷'02'退貨'03'查余 '04'重打印'05'簽到'06'結算'08'小費'09'預授權 '10'預授權追加'11'預授權完成'12'預授權撤銷 '13'預授權完成撤銷'14'商場分期'15'專用分期 '16'分期撤銷'18'分期額度查詢'19'汽車卡洗車 '20'快速支付'21'指定賬戶圈存'22'電子現金退貨 '23'電子現金查余'24'積分兌換消費'25'積分兌換撤銷 '26'積分查詢'28'權益積分查詢'29'權益積分消費 '30'權益積分撤銷'32'卡轉出轉賬'33'財務報銷'34'財務還款 ‘60’-聚合支付被掃 ‘61’-聚合支付主掃 ‘62’-聚合支付退貨 ‘63’-獲取 POS 掃碼槍二維碼數據 ‘64’-單筆聚合支付異常訂單查詢 '72'-聚合支付異常訂單查詢 ‘90’-惠兜圈優惠立減銀行卡 ‘94’-惠兜圈立減優惠銀聯二維碼 ‘95’-雲閃付優惠立減交易 ‘96’-商場分期優惠立減 */ } TInputData = record posid: array[0..7] of char; // /* 收銀機號(8 字節,左對齊,不足部分補空格)*/ operid: array[0..7] of char; // /* 操作員號(8 字節,左對齊,不足部分補空格)*/ trans: array[0..1] of char; //交易類型 見上面 amount: array[0..11] of char; // /* 金額(12 字節,無小數點,左補 0,單位:分)*/ old_date: array[0..7] of char; // /* 原交易日期(8 字節,yyyymmdd 格式,隔日退貨時用)*/ old_reference: array[0..11] of char; // /* 原交易參考號*/ old_trace: array[0..5] of char; // /* 流水號(6 字節,右對齊,左補 0,退貨或重打印等用)*/ old_batch: array[0..5] of char; // /*57~62 位 原批次號*/ old_auth: array[0..5] of char; // /*63~68 位 原授權碼*/ old_terno: array[0..7] of char; // /*69~76 位 原交易終端號*/ szFenqiNum: array[0..1] of char; // /*77~78 位 分期數 03,06,09,12,18,24,36,42,48,60*/ szServiceNum: array[0..1] of char; // /*79~80 位 享受服務人數*/ szGoodsNo: array[0..11] of char; // /*81~92 位 商品項目編碼*/ trk2: array[0..36] of char; // /* 二磁道數據(37 字節,左對齊,不足部分補空格)*/ trk3: array[0..103] of char; // /* 三磁道數據(104 字節, 左對齊,不足部分補空格)*/ lrc: array[0..2] of char; // /* 交易校驗數據(3 位從 0~9 的隨機字符)*/ szOrderTrace: array[0..19] of char; // 6/16/2015 新增 收銀流水(訂單)號 左對齊 不足補空格 szPrefer: array[0..49] of char; // 優惠券左對齊 不足補空格 szRsv: array[0..299] of char; // 保留字段 在此輸入二維碼的條碼數據 左對齊 不足補空格 end; TOutPutData = record resp_code: array[0..1] of char; // /*1~2 位 返回碼 (2 字節,"00"交易成功,其他失敗)*/ bank_code: array[0..3] of char; // /*3~6 位 銀行行號(4 字節)*/ card_no: array[0..29] of char; // /*7~36 位 卡號:622280*********4860 */ expr: array[0..3] of char; // /*37~40 位 有效期 (4 字節) */ amount: array[0..11] of char; // /*141~52 位 金額(12 字節,無小數點,左補 0,單位:分)*/ trace: array[0..5] of char; // /*53~58 位 流水號 (6 字節,左對齊)*/ refer: array[0..11] of char; // /*59~70 位 交易參考號*/ auth: array[0..5] of char; // /*71~76 位 授權號*/ batch: array[0..5] of char; // /*77~82 位 批次號*/ date: array[0..13] of char; // /*83~96 位 交易日期(8 字節,yyyymmddhhmmss 格式)*/ userno: array[0..14] of char; // /*97~111 位 商戶號*/ terno: array[0..7] of char; // /*112~119 位 終端號*/ old_terno: array[0..7] of char; // /*120~127 位 原終端號*/ resp_chin: array[0..49] of char; // /* 錯誤說明(左對齊,不足部分補空格)*/ OrderNo: array[0..49] of char; // /* 二維碼交易訂單訂單號 */ trans: array[0..1] of char; // /* 交易類型 */ ChannelType: array[0..1] of char; // /* '0'銀聯二維碼 '1'微信 '2' 支付寶 '3'龍支付 */ lrc: array[0..2] of char; // /* 交易數據校驗碼(3 字節)*/ end; function PayIntf_Trans(ParkCode, OptorCode, Trans, Money, OldTrandID: string; var OrderNo: string): Boolean; function GetInputStr(ParkCode, OptorCode, Trans, Money, OldTrandID: string; var InStr: string): Boolean; procedure GetOutputData(OutStr: string); procedure FillArrayChar(FillType, ParamVaule: string; var ArrVaule: array of char); function GetOldTradeInfo(OldTrandID: string; var old_date, old_reference, old_trace: string): boolean; function CheckDataValid(ParkCode, OptorCode, Trans, Money, OldTrandID: string): boolean; function Obj2Json_InputData(InputData: TInputData): string; function Obj2Json_OutputData(OutputData: TOutputData): string; procedure MyWriteLog(const mStr: string); function BankTrans(PInStr: Pchar; POutStr: Pchar): integer; stdcall; external 'C:\landiccbmispos\MisPos.dll'; //0- 成功 , 其他-失敗 implementation function CheckDataValid(ParkCode, OptorCode, Trans, Money, OldTrandID: string): boolean; var sMsg,sFileDLL: string; begin Result := False; sFileDLL := 'C:\landiccbmispos\MisPos.dll'; if not FileExists(sFileDLL) then begin sMsg := '調用支付接口文件不存在!'; MyWriteLog(sMsg + '[' + sFileDLL + ']'); Application.MessageBox(Pchar(sMsg), '提示', MB_OK); Exit; end; if (Trans <> '00') and (Trans <> '01') and (Trans <> '02') and (Trans <> '05') and (Trans <> '06') then //'00'消費'01'撤銷'02'退貨'03'查余 '05'簽到'06'結算 begin sMsg := '非法的交易類型!'; MyWriteLog(sMsg); Application.MessageBox(Pchar(sMsg), '提示', MB_OK); Exit; end; if (Trans = '01') or (Trans = '02') then begin if OldTrandID = '' then begin sMsg := '撤銷和退貨時,請傳入原交易單號!'; MyWriteLog(sMsg); Application.MessageBox(Pchar(sMsg), '提示', MB_OK); Exit; end; end; if StrToCurrDef(Money,0) <= 0 then begin sMsg := '金額不允許為負數或零!'; MyWriteLog(sMsg); Application.MessageBox(Pchar(sMsg), '提示', MB_OK); Exit; end; Result := True; end; function PayIntf_Trans(ParkCode, OptorCode, Trans, Money, OldTrandID: string; var OrderNo: string): Boolean; var lv_nRet: integer; InStr: string; OutStr: string; sMsg: string; resp_code: string; begin {* 1、檢查傳入參數:根據文檔規則 * 2、參數組合成定長字符串 * 3、調用DLL方法 * 4、解析返回定長字符串:判斷結果 * 5、記錄日志 *} //檢查傳入參數 if not CheckDataValid(ParkCode, OptorCode, Trans, Money, OldTrandID) then begin Exit; end; //組合傳入參數,形成定長字符串InStr if not GetInputStr(ParkCode, OptorCode, Trans, Money, OldTrandID, InStr) then begin Exit; end; MyWriteLog('發送定長字符串:[' + InStr + ']'); SetLength(OutStr, SizeOf(TOutPutData)); Application.ProcessMessages; lv_nRet := BankTrans(Pchar(InStr), Pchar(OutStr)); //調用DLL中的BankTrans方法 Application.ProcessMessages; MyWriteLog('返回定長字符串:[' + OutStr + ']'); GetOutputData(OutStr); if (lv_nRet <> 0) or (Copy(OutStr, 1, 2) <> '00') then begin sMsg := '交易失敗!'; MyWriteLog(sMsg); Application.MessageBox(Pchar(sMsg), '提示', MB_OK); Exit; end; OrderNo := Trim(Copy(OrderNo, 158, 50)); if Copy(OrderNo, 1, 8) <> 'EEEEEEEE' then // 特殊,當退貨出現異常情況, 如網絡異常時, 返回頭的輸出參數頭2個字節為00其余字符會以大寫字母EE填充, begin //同時DLL會彈出對話框。請商戶及時和銀行核實是否退貨成功!!! sMsg := '交易成功!'; MyWriteLog(sMsg + '[' + OrderNo + ']'); Application.MessageBox(Pchar(sMsg), '提示', MB_OK); end else begin sMsg := '交易成功,單需要手工核對!'; MyWriteLog(sMsg + '[' + OrderNo + ']'); Application.MessageBox(Pchar(sMsg), '提示', MB_OK); end; end; function GetInputStr(ParkCode, OptorCode, Trans, Money, OldTrandID: string; var InStr: string): Boolean; var old_date, old_reference, old_trace, sMsg: string; InputData: TInputData; begin {交易說明: 消費 :傳入必要信息為 (交易類型,金額) 退貨 :傳入必要信息為 (交易類型,金額,原交易參考號,原交易日期) 撤銷 : 傳入必要信息為 (交易類型,金額,原流水號) 查余 : 傳入必要信息為 (交易類型) 注: 查詢金額 操作 卡內余額顯示在 密碼鍵盤。並且不會返回對應的余額. 重打印 : 傳入必要信息為 (交易類型,原流水號) 注: 重打印上一筆交易 原流水號:”000000” 簽到 : 傳入必要信息為 (交易類型) 結算 : 傳入必要信息為 (交易類型) } Result := False; try FillChar(InputData,SizeOf(TInputData),' '); FillArrayChar('RN', ParkCode, InputData.posid); FillArrayChar('RN', OptorCode, InputData.operid); FillArrayChar('R0', Trans, InputData.trans); FillArrayChar('L0', IntToStr(Trunc(StrToCurrDef(Money, 0) * 100)), InputData.amount); if (Trans = '01') or (Trans = '02') then //'00'消費'01'撤銷'02'退貨'03'查余 begin //獲取原交易信息 if not GetOldTradeInfo(OldTrandID, old_date, old_reference, old_trace) then begin Exit; end; if Trans = '01' then begin FillArrayChar('L0', old_trace, InputData.old_trace); // /* 原流水號(6 字節,右對齊,左補 0,退貨或重打印等用)*/ FillArrayChar('RN', '', InputData.old_date); FillArrayChar('RN', '', InputData.old_reference); end; if Trans = '02' then begin FillArrayChar('L0', '', InputData.old_trace); FillArrayChar('RN', old_date, InputData.old_date); // /* 原交易日期(8 字節,yyyymmdd 格式,隔日退貨時用)*/ FillArrayChar('RN', old_reference, InputData.old_reference); // /* 原交易參考號*/ end; end else //非撤銷和退貨 begin FillArrayChar('L0', '', InputData.old_trace); FillArrayChar('RN', '', InputData.old_date); FillArrayChar('RN', '', InputData.old_reference); end; //以下為默認填充 FillArrayChar('RN', '', InputData.old_batch); // /*57~62 位 原批次號*/ FillArrayChar('RN', '', InputData.old_auth); // /*63~68 位 原授權碼*/ FillArrayChar('RN', '', InputData.old_terno); // /*69~76 位 原交易終端號*/ FillArrayChar('RN', '', InputData.szFenqiNum); // /*77~78 位 分期數 03,06,09,12,18,24,36,42,48,60*/ FillArrayChar('RN', '', InputData.szServiceNum); // /*79~80 位 享受服務人數*/ FillArrayChar('RN', '', InputData.szGoodsNo); // /*81~92 位 商品項目編碼*/ FillArrayChar('RN', '', InputData.trk2); // /* 二磁道數據(37 字節,左對齊,不足部分補空格)*/ FillArrayChar('RN', '', InputData.trk3); // /* 三磁道數據(104 字節, 左對齊,不足部分補空格)*/ FillArrayChar('RN', '', InputData.lrc); // /* 交易校驗數據(3 位從 0~9 的隨機字符)*/ FillArrayChar('RN', '', InputData.szOrderTrace); // 6/16/2015 新增 收銀流水(訂單)號 左對齊 不足補空格 FillArrayChar('RN', '', InputData.szPrefer); // 優惠券左對齊 不足補空格 FillArrayChar('RN', '', InputData.szRsv); // 保留字段 在此輸入二維碼的條碼數據 左對齊 不足補空格 //取傳參的累加的定長字符串 SetLength(InStr,SizeOf(TInputData)); StrLCopy(Pchar(InStr), @InputData, SizeOf(TInputData)); sMsg := '發送信息[轉換為JSON]:' + Obj2Json_InputData(InputData); MyWriteLog(sMsg); Result := True; except on e: exception do begin sMsg := '獲取傳入參數字符串異常:' + e.message; MyWriteLog(sMsg); Application.MessageBox(Pchar(sMsg), '提示', MB_OK); Exit; end; end; end; procedure GetOutputData(OutStr: string); var sMsg, sTemp: string; OutputData: TOutPutData; begin try if OutStr = '' then begin sMsg := '交易返回參數為空!'; MyWriteLog(sMsg); Application.MessageBox(Pchar(sMsg), '提示', MB_OK); Exit; end; //字符串賦值給記錄 FillChar(OutputData,SizeOf(TOutputData),' '); StrLCopy(@OutputData, Pchar(OutStr), SizeOf(TOutputData)); //記錄返回的信息 sTemp := '返回信息[轉換為JSON]:' + Obj2Json_OutputData(OutputData); MyWriteLog(sTemp); except on e: exception do begin sMsg := '轉換返回參數異常:' + e.message; MyWriteLog(sMsg); Application.MessageBox(Pchar(sMsg), '提示', MB_OK); Exit; end; end; end; function Obj2Json_InputData(InputData: TInputData): string; begin Result := '{'; Result := Result + '"posid":"' + Trim(InputData.posid) + '",'; Result := Result + '"operid":"' + Trim(InputData.operid) + '",'; Result := Result + '"trans":"' + Trim(InputData.trans) + '",'; Result := Result + '"amount":"' + Trim(InputData.amount) + '",'; Result := Result + '"old_date":"' + Trim(InputData.old_date) + '",'; Result := Result + '"old_reference":"' + Trim(InputData.old_reference) + '",'; Result := Result + '"old_trace":"' + Trim(InputData.old_trace) + '",'; Result := Result + '"old_batch":"' + Trim(InputData.old_batch) + '",'; Result := Result + '"old_auth":"' + Trim(InputData.old_auth) + '",'; Result := Result + '"old_terno":"' + Trim(InputData.old_terno) + '",'; Result := Result + '"szFenqiNum":"' + Trim(InputData.szFenqiNum) + '",'; Result := Result + '"szServiceNum":"' + Trim(InputData.szServiceNum) + '",'; Result := Result + '"szGoodsNo":"' + Trim(InputData.szGoodsNo) + '",'; Result := Result + '"trk2":"' + Trim(InputData.trk2) + '",'; Result := Result + '"trk3":"' + Trim(InputData.trk3) + '",'; Result := Result + '"lrc":"' + Trim(InputData.lrc) + '",'; Result := Result + '"szOrderTrace":"' + Trim(InputData.szOrderTrace) + '",'; Result := Result + '"szPrefer":"' + Trim(InputData.szPrefer) + '",'; Result := Result + '"szRsv":"' + Trim(InputData.szRsv) + '"'; Result := Result + '}'; end; function Obj2Json_OutputData(OutputData: TOutputData): string; begin Result := '{'; Result := Result + '"resp_code":"' + Trim(OutputData.resp_code) + '",'; Result := Result + '"bank_code":"' + Trim(OutputData.bank_code) + '",'; Result := Result + '"card_no":"' + Trim(OutputData.card_no) + '",'; Result := Result + '"expr":"' + Trim(OutputData.expr) + '",'; Result := Result + '"amount":"' + Trim(OutputData.amount) + '",'; Result := Result + '"trace":"' + Trim(OutputData.trace) + '",'; Result := Result + '"refer":"' + Trim(OutputData.refer) + '",'; Result := Result + '"auth":"' + Trim(OutputData.auth) + '",'; Result := Result + '"batch":"' + Trim(OutputData.batch) + '",'; Result := Result + '"date":"' + Trim(OutputData.date) + '",'; Result := Result + '"userno":"' + Trim(OutputData.userno) + '",'; Result := Result + '"terno":"' + Trim(OutputData.terno) + '",'; Result := Result + '"old_terno":"' + Trim(OutputData.old_terno) + '",'; Result := Result + '"resp_chin":"' + Trim(OutputData.resp_chin) + '",'; Result := Result + '"OrderNo":"' + Trim(OutputData.OrderNo) + '",'; Result := Result + '"trans":"' + Trim(OutputData.trans) + '",'; Result := Result + '"ChannelType":"' + Trim(OutputData.ChannelType) + '",'; Result := Result + '"lrc":"' + Trim(OutputData.lrc) + '"'; Result := Result + '}'; end; function GetOldTradeInfo(OldTrandID: string; var old_date, old_reference, old_trace: string): boolean; var sMsg: string; begin Result := False; try old_date := '20190805'; old_reference := '123456789987456'; old_trace := '123456'; Result := True; except on e: exception do begin sMsg := '獲取原交易支付信息異常[原交易號:' + OldTrandID + ']:' + e.message; MyWriteLog(sMsg); Application.MessageBox(Pchar(sMsg), '提示', MB_OK); Exit; end; end; end; procedure FillArrayChar(FillType, ParamVaule: string; var ArrVaule: array of char); var I, Len, Diff: integer; sTemp: string; begin Len := Length(ArrVaule); if Length(ParamVaule) < Len then begin Diff := Len - Length(ParamVaule); for I := 1 to Diff do begin if Copy(FillType, 2, 1) = '0' then //填充"0" begin sTemp := sTemp + '0'; end else begin sTemp := sTemp + ' '; //填充空格 end; end; if Copy(FillType, 1, 1) = 'L' then //左填充 begin sTemp := sTemp + ParamVaule; end else if Copy(FillType, 1, 1) = 'R' then //右填充 begin sTemp := ParamVaule + sTemp; end; end else begin sTemp := ParamVaule; end; for I := 1 to Len do begin ArrVaule[I - 1] := sTemp[I]; end; end; procedure MyWriteLog(const mStr: string); var f: textfile; myDir, myFileName: string; FileHandle: Integer; LogType, LogDate, ModuleID: string; begin LogType := 'INFO'; LogDate := FormatDateTime('YYYY-MM-DD hh:nn:ss zzz', Now); ModuleID := ''; // if not (CanLogFile in FLogFlags) then exit; //------寫入文件部分的實現--開始 myDir := ExtractFilePath(Paramstr(0)); //確定文件名稱 myFileName := FormatDateTime('"PayIntf"yyyymmdd".log"', Date); //如果可執行目錄下不存在log目錄創建之 if not DirectoryExists(myDir + '\log') then CreateDir(myDir + '\log'); //如果當日日志文件不存在,則創建文件並釋放句柄 if not FileExists(myDir + '\log\' + myFileName) then begin FileHandle := FileCreate(myDir + '\log\' + myFileName); //創建文件 FileClose(FileHandle); //釋放句柄 end; try//try...except...statements AssignFile(f, myDir + '\log\' + myFileName); Append(f); Writeln(f, '$$' + Format('%6s', [LogType]) + '$$ ' + Format('%12s', [ModuleID]) + '$$' + LogDate + chr(9) + mStr); Flush(f); CloseFile(f); //-----寫入文件部分的實現--結束 except//try...except...statements end; //try...except...statements end; end.