/* * Module_Payment.c * * Created on: 2021/03/24 * Author: Henry Yeh */ #include "Module_Payment.h" unsigned char version[] = {'D', '0', '.', '0', '1'}; unsigned char CMD_C8[30] = {0xc8,0x01, // Activate the reader 0x9f,0x02,0x06,0x00,0x00,0x00,0x01,0x00,0x00, // Amount, Authorized, sample: 100.00 dollar 0x5f,0x2a,0x02,0x08,0x40, // Transaction Currency Code follow ISO-4217, sample: 0840(USD) 0x9c,0x01,0x00, // Transaction Type, sample: 00 0x9a,0x03,0x21,0x03,0x24, // Transaction Date, sample: 2021/03/24 0x9f,0x21,0x03,0x13,0x36,0x10 }; // Transaction Time, sample: 13:36:10 unsigned char CMD_CARD_DETECT[11] = {0x09, 0, 0x07, 'M','F','1','4','1','2','1', 0x32}; // Enable payment, MIFARE, 15693 card; Detect Payment Card First; Enable ApplePay VAS unsigned char CMD_USI2[7] = {0x09, 0, 0x03, 'P', 'C', '0', 0x29}; // Configure protocol to USI2 unsigned char CMD_SET_BAUD[7] = {0x09, 0, 0x03, 'B', 'R', '7', 0x2d}; // Configure module baud rate to 115200 unsigned char CMD_RESTORE_DEFAULT[7] = {0x09, 0, 0x03, 'D', 'F', 0, 0x08 }; // Restore module configuration to default setting int system_command(int uart, unsigned char* cmd, int cmd_len, unsigned char* rx); int USI2_Parse(unsigned char* rx, unsigned char* rx_data); struct C9_RESULT { unsigned char result_data[512]; unsigned char status; unsigned char pos_entry; unsigned char u_id[20]; unsigned char tkData[4][128]; } C9_Result; //========================================== // Common routine //========================================== int StoreLogMsg(const char *fmt, ...) { char Buf[65536+256]; char buffer[65536]; //char Buf[4096+256]; //char buffer[4096]; time_t CurrentTime; struct tm *tm; struct timeval tv; va_list args; va_start(args, fmt); int rc = vsnprintf(buffer, sizeof(buffer), fmt, args); va_end(args); memset(Buf,0,sizeof(Buf)); CurrentTime = time(NULL); tm=localtime(&CurrentTime); gettimeofday(&tv, NULL); // get microseconds, 10^-6 sprintf(Buf,"echo -n \"[%04d.%02d.%02d %02d:%02d:%02d.%06ld]%s\" >> /Storage/SystemLog/[%04d.%02d]PaymentLog", tm->tm_year+1900,tm->tm_mon+1,tm->tm_mday,tm->tm_hour,tm->tm_min,tm->tm_sec,tv.tv_usec, buffer, tm->tm_year+1900,tm->tm_mon+1); system((const char*)Buf); #ifdef ConsloePrintLog printf("[%04d.%02d.%02d %02d:%02d:%02d.%06ld]%s", tm->tm_year+1900,tm->tm_mon+1,tm->tm_mday,tm->tm_hour,tm->tm_min,tm->tm_sec,tv.tv_usec, buffer); #endif return rc; } /** * Execute shell command * @param cmd: shell command string * @return shell command execution result */ int runShellCmd(const char*cmd) { int result = FAIL; char buf[256]; FILE *fp; fp = popen(cmd, "r"); if(fp != NULL) { while(fgets(buf, sizeof(buf), fp) != NULL) { DEBUG_INFO("%s\n", buf); } result = PASS; } pclose(fp); return result; } /** * Calculate time differential * @param ST: start time * @param ET: end time * @return time differential in million seconds */ int DiffTimeb(struct timeb ST, struct timeb ET) { //return milli-second unsigned int StartTime,StopTime; StartTime=(unsigned int)ST.time; StopTime=(unsigned int)ET.time; return (StopTime-StartTime)*1000+ET.millitm-ST.millitm; } /** * Show communication raw data to debug info * @param data: raw data * @param len: data length * @param isRX: is receive data */ void show_raw(uint8_t *data, uint16_t len, uint8_t isRX) { uint8_t output[8192]; memset(output, 0x00, ARRAY_SIZE(output)); sprintf((char*)output, "%s", (isRX?"RX: ":"TX: ")); for(uint16_t idx = 0;idx 31 && dat[idx] < 128 ) sprintf((char*)output, "%s%c", output, dat[idx]); else sprintf((char*)output, "%s<%x>", output, dat[idx]); } DEBUG_INFO("%s\n", output); } /** * Get sentinel quantity in data array * @param data: message array address * @param dataLen: array seek size * @return how many sentinel flag found */ int getSentinelQuantity(unsigned char *data, unsigned int dataLen) { int result = 0; for(uint16_t idx=0;idx 0) { /* * TODO: Improve sleep time. */ usleep(1000000); rx_len = read(uart, rx, 512); show_raw(rx, rx_len, YES); } else { DEBUG_ERROR("system command write fail.\n"); } return rx_len; } /** * Parsing raw data to USI data * @param rx: raw data * @param rx_data: parsing result data * @return parsing result data length */ int USI2_Parse(unsigned char* rx, unsigned char* rx_data) { int result = -1; unsigned int data_len =0; unsigned int chksum = 0; if(rx[0] == SOH) // SOH = 0x01 { data_len = (unsigned int)rx[2] <<8; data_len |= rx[3]; for(int idx=0;idx<(data_len+4);idx++) { chksum ^= rx[idx]; } if((chksum&0xff) == rx[(data_len+4)]) { memcpy(rx_data, &rx[4], data_len); result = data_len; } } else { DEBUG_WARN("USI2 message header is not <01>.\n"); } return result; } //========================================== // Main loop //========================================== int main(void) { int UartFd; uint16_t failCount = 0; unsigned char rx_Array[512]={0}, rx_Data[512]={0}; char C8_Polling = true; char Wait_C9 = false; int rx_len = 0; int data_len = 0; DEBUG_INFO("Task version: %s\n", version); //=============================================== // Initialization //=============================================== if(InitShareMemory() == FAIL) { DEBUG_ERROR("InitShareMemory NG\n"); if(ShmStatusCodeData!=NULL) { ShmStatusCodeData->AlarmCode.AlarmEvents.bits.FailToCreateShareMemory=1; } sleep(5); return FAIL; } UartFd=InitComPort(); if(UartFd<0) { DEBUG_ERROR("InitComPort NG\n"); if(ShmStatusCodeData!=NULL) { ShmStatusCodeData->AlarmCode.AlarmEvents.bits.CsuInitFailed=1; } sleep(5); return FAIL; } else { DEBUG_INFO("ttyS3 port open success.\n"); } //=============================================== // Payment module configuration set to default. //=============================================== do { rx_len = system_command(UartFd, CMD_RESTORE_DEFAULT, ARRAY_SIZE(CMD_RESTORE_DEFAULT), rx_Array); if((rx_Array[0] == ACK) && (rx_len ==1)) { DEBUG_INFO("Set to the default success.\n"); failCount = 0; } else { DEBUG_WARN("Set to the default fail (<%02x>).\n", rx_Array[0]); failCount++; } if(failCount > RETRY_LIMIT) { DEBUG_ERROR("Set to the default fail over retry limit.\n"); return FAIL; } }while((rx_Array[0] != ACK) || (rx_len !=1)); //=============================================== // set payment card detect type. //=============================================== do { rx_len = system_command(UartFd, CMD_CARD_DETECT, ARRAY_SIZE(CMD_CARD_DETECT), rx_Array); if((rx_Array[0] == ACK) && (rx_len ==1)) { DEBUG_INFO("Set payment card type success.\n"); failCount = 0; } else { DEBUG_WARN("Set payment card type fail (<%02x>).\n", rx_Array[0]); failCount++; } if(failCount > RETRY_LIMIT) { DEBUG_ERROR("Set payment card type fail over retry limit.\n"); return FAIL; } }while((rx_Array[0] != ACK) || (rx_len !=1)); //=============================================== // set to protocol_2 //=============================================== do { rx_len =system_command(UartFd, CMD_USI2, ARRAY_SIZE(CMD_USI2), rx_Array); if((rx_Array[0] == ACK) && (rx_len ==1)) { DEBUG_INFO("Set protocol to USI2 success.\n"); failCount = 0; } else { DEBUG_WARN("Set protocol to USI2 fail (<%02x>).\n", rx_Array[0]); failCount++; } if(failCount > RETRY_LIMIT) { DEBUG_ERROR("Set protocol to USI2 fail over retry limit.\n"); return FAIL; } }while((rx_Array[0] != ACK) || (rx_len !=1)); //=============================================== // Main loop //=============================================== for(;;) { if(C8_Polling == true) { Wait_C9 = false; /* * TODO: * 1. C8 parameter configure */ rx_len =system_command(UartFd, CMD_C8, sizeof(CMD_C8), rx_Array); if((rx_Array[0] == ACK) && (rx_len ==1)) { DEBUG_INFO("Polling C8 command get response.\n"); Wait_C9 = true; failCount = 0; } if(Wait_C9 == true) { //============================================= // wait card to attach the reader and wait C9 //============================================= tcflush(UartFd,TCIOFLUSH); memset(rx_Array, 0x00, ARRAY_SIZE(rx_Array)); rx_len = 0; do { sleep(1); rx_len = read(UartFd, rx_Array, ARRAY_SIZE(rx_Array)); // read response if data count match 512 or timeout. failCount++; } while ((rx_len == 0) && (failCount < RETRY_LIMIT)); //============================================= // Parse rx_Array to rx_Data //============================================= if((rx_len > 3) && (failCount < RETRY_LIMIT)) { // print this raw data before parse it. show_data(rx_Array, rx_len); rx_len = USI2_Parse( rx_Array, rx_Data); if(rx_len > 0) { // debug the input data message show_data(rx_Data, rx_len); // Copy RAW data to structure memcpy(&C9_Result.result_data, rx_Data, rx_len); C9_Result.status = C9_Result.result_data[1]; C9_Result.pos_entry = C9_Result.result_data[2]; switch(C9_Result.pos_entry) { case VISA_qVSDC: case VISA_MSD: case MASTER_MChip: case Master_MagStripe: case AMEX_EMV: case AMEX_MSD: memset(C9_Result.u_id, 0x00, ARRAY_SIZE(C9_Result.u_id)); memcpy(C9_Result.u_id, &C9_Result.result_data[4], 16); DEBUG_INFO("Credit card SN:\n"); show_data(C9_Result.u_id, 16); DEBUG_INFO("Payment card\n"); for(uint8_t idx=0;idx No any data.\n"); break; default: DEBUG_INFO("--> Unknown pos entry.\n"); break; } failCount = 0; } else { DEBUG_INFO("C9 Parsing result fail.\n"); } } else { DEBUG_WARN("C9 Response timeout: %d \n", failCount); } } } usleep(500000); } return FAIL; }