That series of tutorials use Kizs as the squeleton to build from scratch a basic payment application.
Use ACE to trace and monitor behavior (open ‘'Console Log’').
Tutorial #1: set communication and traces using ACE
The very step to avoid difficult integration times: unit test the external communication with ACE.
int main(int argc, char** argv) { // Communication int port, length; char address[50]=""; // Get communication parameters from ini file xgpiIniSetFilename("agnos.ini"); xgpiIniGetString("COM", "Address", 50, address, &length); xgpiIniGetNumeric("COM", "Port", &port); // Initialize ACE // Initialize generic communication interface assert(tcpInit(0) == TCP_NO_ERROR); // Device side is server fro ACE assert(comtcpOpen(&gACEServerCOMTCP, TCP_SERVER, 0, port) == COM_NO_ERROR); // Device side is also an ACE client assert(comtcpOpen(&gACEClientCOMTCP, TCP_CLIENT, address, 1979) == COM_NO_ERROR); aceInitializeCommunication(&gACEServerCOMTCP.com,&gACEClientCOMTCP.com); // Initialize running mode aceSetMode(pmSDK); // Set ACE option aceSetUIDisplay(bTRUE); // Tutorial aceOut("Training Session - BEGIN\n"); aceOut("Hello World!\n"); aceOut("Training Session - END\n"); return 0; }
Tutorial #2: initialize language and payment context
int main(int argc, char** argv) { // Trace for debugging char tmp[100]=""; // Communication int port, length; char address[50]=""; // System's language tByte availableLanguage=0; tByte currentLanguage = 0; tPaymentContext* pay = &gPaymentCtx; tOutComeParameter* out = &gOutcome; // Get Communication parameters from ini file xgpiIniSetFilename("agnos.ini"); xgpiIniGetString("COM", "Address", 50, address, &length); xgpiIniGetNumeric("COM", "Port", &port); // Initialize ACE // Initialize generic communication interface assert(tcpInit(0) == TCP_NO_ERROR); assert(comtcpOpen(&gACEServerCOMTCP, TCP_SERVER, 0, port) == COM_NO_ERROR); assert(comtcpOpen(&gACEClientCOMTCP, TCP_CLIENT, address, 1979) == COM_NO_ERROR); aceInitializeCommunication(&gACEServerCOMTCP.com,&gACEClientCOMTCP.com); // Initialize running mode aceSetMode(pmSDK); // Set up ACE's options aceSetUIDisplay(bTRUE); // Tutorial aceOut("Training Session - BEGIN\n"); aceOut("Hello World!\n");// set default language (merchant language indeed) // Initialize lines, columns and set string table from lang.ini gpiInitializeDisplay("lang.ini",&availableLanguage,NULL); // Assuming that lan.ini supports at least 2 languages sprintf(tmp,"Available lang.: %i\n",availableLanguage); aceOut(tmp); // Initialize payment context and outcome pmwInitializePaymentContext(pay); pmwSetLanguage(pay,currentLanguage); // Don't forget to propagate language in payment context for further processing currentLanguage = pmwGetLanguage(pay); pmwInitializeOutComeParameter(out); // Important to perform these initializations after pmwInitializePaymentContext pay->mAmount = &gAmount; pay->mCashBack = &gCashBack; pay->mTransactionType = (tTransactionType)0x00; gAmount = gCashBack = 0; gpiDisplayMessageByID(gpi_true,currentLanguage,WELCOME); gpiDisplayMessageByID(gpi_true,currentLanguage+1,THANKS); // Using the next language aceOut("Training Session - END\n"); return 0; }
Tutorial #3: manage ACE requests
void createFile(char* fileName, tByte* buffer, tWord len) { tFileHandler ifp = 0; if (fileName) { gpiFileDelete(fileName); if(gpiFileOpen(fileName,&ifp,CREATE|BINARY)==filNO_ERROR) { gpiFileWrite(ifp,buffer,len); gpiFileClose(ifp); } } } //--------------------------------------------------------- // Main //--------------------------------------------------------- // Main function: // - Initialize the platform // - Wait for payment trigger // - Initialize the data model // - Perform a payment // // Visibility: Public // Hypothesis: -- // Reference: -- // int main(int argc, char** argv) { // Trace for debugging char tmp[100]=""; // Communication int port, length; char address[50]=""; // System's language tByte availableLanguage=0, currentLanguage = 0; tPaymentContext* pay = &gPaymentCtx; tOutComeParameter* out = &gOutcome; // Get Communication parameters from ini file xgpiIniSetFilename("agnos.ini"); xgpiIniGetString("COM", "Address", 50, address, &length); xgpiIniGetNumeric("COM", "Port", &port); // Initialize ACE // Initialize generic communication interface assert(tcpInit(0) == TCP_NO_ERROR); assert(comtcpOpen(&gACEServerCOMTCP, TCP_SERVER, 0, port) == COM_NO_ERROR); assert(comtcpOpen(&gACEClientCOMTCP, TCP_CLIENT, address, 1979) == COM_NO_ERROR); aceInitializeCommunication(&gACEServerCOMTCP.com,&gACEClientCOMTCP.com); // Initialize running mode aceSetMode(pmSDK); // Set up ACE's options aceSetUIDisplay(bTRUE); // Tutorial aceOut("Training Session - BEGIN\n"); aceOut("Hello World!\n");// set default language (merchant language indeed) // Initialize lines, columns and set string table from lang.ini gpiInitializeDisplay("lang.ini",&availableLanguage,NULL); sprintf(tmp,"Available lang.: %i\n",availableLanguage); aceOut(tmp); // Initialize payment context and outcome pmwInitializePaymentContext(pay); pmwSetLanguage(pay,currentLanguage); // Don't forget to propagate language in payment context for further processing currentLanguage = pmwGetLanguage(pay); pmwInitializeOutComeParameter(out); // Important to perform these initializations after pmwInitializePaymentContext pay->mAmount = &gAmount; pay->mCashBack = &gCashBack; pay->mTransactionType = (tTransactionType)0x00; gAmount = gCashBack = 0; tByte *buffer = NULL; tDataFileSize size=0; tWord len=0; char c, workingString[1000]=""; gpiGetTotalRAM(&buffer,&size); if (!buffer) return 0; len=0; gpiMemSet(buffer,0x00,size); gpiMemSet(workingString,0x00,sizeof(workingString)); // Wait for inbound sale request do { gpiDisplayMessageByID(gpi_true,currentLanguage,WELCOME); c = aceGetRequest(buffer,&len); if (INIT == c) { sprintf(workingString,"%sTERMINAL","./AGNOS/"); createFile(workingString,buffer+1,len-1); gpiDisplayMessage(gpi_true,"TERMINAL Updated" ); } else if (CONFIG == c) { sprintf(workingString,"%sPROCESSING","./AGNOS/"); createFile(workingString,buffer+1,len-1); gpiDisplayMessage(gpi_true,"PROCESSING Updated" ); } else if ( CL_ENTRY_POINT == c) { sprintf(workingString,"%sENTRY_POINT","./AGNOS/"); createFile(workingString,buffer+1,len-1); gpiDisplayMessage(gpi_true,"ENTRY_POINT Updated" ); } else if ((CAPK == c) && (len >= 21)) { sprintf(workingString,"%sCAKeys","./AGNOS/"); createFile(workingString,buffer+1,len-1-20); gpiDisplayMessage(gpi_true,"CAPK Updated" ); } aceSendResponse((unsigned char*)"\x30\x30",2); } while (c != PAYMENT); gpiDisplayMessageByID(gpi_true,currentLanguage+1,THANKS); aceOut("Training Session - END\n"); return 0; }
Tutorial #4: manage a payment request
For this tutorial, code examples rely on previous tutorial. Let’s focus on payment. A comprehensive example is provided below.
void processRequest(tByte* request, tWord requestLen, tPaymentContext* ctx) { tByte date[4], time[3], datetime[6]; cursor_t cursorRequest; byte_t *value; static tByte TRD[500] = ""; static tWord TRDLength = 0; if(!requestLen || !request) return; gpiMemCpy(TRD,request,requestLen); TRDLength = requestLen; // Parse TLV stream if(tlvInitCursor(request,requestLen,requestLen,&cursorRequest)==TLV_NO_ERROR) { // Map tags to tPaymentContext structure tlvResetCursor(&cursorRequest); if(tlvFindTag(0x9C,1,&cursorRequest)==TLV_NO_ERROR) { value = tlvGetValue(&cursorRequest); if(value) { if(*value==0x00) ctx->mTransactionType = ttPURCHASE; else if(*value==0x01) ctx->mTransactionType = ttCASH; else if(*value==0x09) ctx->mTransactionType = ttWITH_CASHBACK; else ctx->mTransactionType = ttUNDEFINED; } else ctx->mTransactionType = ttUNDEFINED; } // as per new GTBI as of december 2014: ctx->mTransactionType is set at initialization time from agnos.ini //else //ctx->mTransactionType = ttPURCHASE; if(ctx->mTransactionType == ttWITH_CASHBACK) { tlvResetCursor(&cursorRequest); if(tlvFindTag(0x9F02,1,&cursorRequest)==TLV_ERR_TAG_NOT_FOUND) { ctx->mAmount = NULL; ctx->mCashBack = NULL; } else { if(tlvGetLength(&cursorRequest)) convertBCDToAmount(ctx->mAmount,tlvGetValue(&cursorRequest),6); else ctx->mAmount = NULL; if(tlvFindTag(0x9F03,1,&cursorRequest)==TLV_ERR_TAG_NOT_FOUND) ctx->mCashBack = NULL; else { if(tlvGetLength(&cursorRequest)) convertBCDToAmount(ctx->mCashBack,tlvGetValue(&cursorRequest),6); else ctx->mCashBack = NULL; } } } else { // ttPURCHASE or ttCASH or ttMANUALCASH or ttREFUND or ttDEPOSIT or dummy value tlvResetCursor(&cursorRequest); if(tlvFindTag(0x9F02,1,&cursorRequest)==TLV_ERR_TAG_NOT_FOUND) { ctx->mAmount = NULL; ctx->mCashBack = NULL; } else { if(tlvGetLength(&cursorRequest)) { convertBCDToAmount(ctx->mAmount,tlvGetValue(&cursorRequest),6); tlvResetCursor(&cursorRequest); if(tlvFindTag(0x9F03,1,&cursorRequest)==TLV_ERR_TAG_NOT_FOUND) ctx->mCashBack = NULL; else { if(tlvGetLength(&cursorRequest)) convertBCDToAmount(ctx->mCashBack,tlvGetValue(&cursorRequest),6); else ctx->mCashBack = NULL; } } else { ctx->mAmount = NULL; ctx->mCashBack = NULL; } } } tlvResetCursor(&cursorRequest); if(tlvFindTag(0x9A,1,&cursorRequest)==TLV_NO_ERROR) { if(tlvGetValue(&cursorRequest)) { gpiMemCpy(ctx->mTransactionDate,tlvGetValue(&cursorRequest),3); // Mantis 0000142: Set date and time from ACE // Synchronize system's date from ACE datetime[0] = bcdToDecimal(ctx->mTransactionDate[0]); datetime[1] = bcdToDecimal(ctx->mTransactionDate[1]); datetime[2] = bcdToDecimal(ctx->mTransactionDate[2]); gpiGetTime(time); gpiMemCpy(datetime+3,time,3); gpiSetDateTime(datetime); } else { gpiGetDate(date); ctx->mTransactionDate[0] = toNumeric(date[1]); ctx->mTransactionDate[1] = toNumeric(date[2]); ctx->mTransactionDate[2] = toNumeric(date[3]); } } else { gpiGetDate(date); ctx->mTransactionDate[0] = toNumeric(date[1]); ctx->mTransactionDate[1] = toNumeric(date[2]); ctx->mTransactionDate[2] = toNumeric(date[3]); } tlvResetCursor(&cursorRequest); if(tlvFindTag(0x9F21,1,&cursorRequest)==TLV_NO_ERROR) { if(tlvGetValue(&cursorRequest)) { gpiMemCpy(ctx->mTransactionTime,tlvGetValue(&cursorRequest),3); // Mantis 0000142: Set date and tiem from ACE // Synchronize system's time from ACE gpiGetDate(date); gpiMemCpy(datetime,date+1,3); datetime[3] = bcdToDecimal(ctx->mTransactionTime[0]); datetime[4] = bcdToDecimal(ctx->mTransactionTime[1]); datetime[5] = bcdToDecimal(ctx->mTransactionTime[2]); gpiSetDateTime(datetime); } else { gpiGetTime(time); ctx->mTransactionTime[0] = toNumeric(time[0]); ctx->mTransactionTime[1] = toNumeric(time[1]); ctx->mTransactionTime[2] = toNumeric(time[2]); } } else { gpiGetTime(time); ctx->mTransactionTime[0] = toNumeric(time[0]); ctx->mTransactionTime[1] = toNumeric(time[1]); ctx->mTransactionTime[2] = toNumeric(time[2]); } tlvResetCursor(&cursorRequest); if(tlvFindTag(0x5F2A,1,&cursorRequest)==TLV_NO_ERROR) { if(tlvGetValue(&cursorRequest)) gpiMemCpy(ctx->mTransactionCurrencyCode,tlvGetValue(&cursorRequest),2); } tlvResetCursor(&cursorRequest); if(tlvFindTag(0x5F36,1,&cursorRequest)==TLV_NO_ERROR) { if(tlvGetValue(&cursorRequest)) ctx->mTransactionCurrencyExponent = *(tlvGetValue(&cursorRequest)); } } } //--------------------------------------------------------- // Main //--------------------------------------------------------- // Main function: // - Initialize the platform // - Wait for payment trigger // - Initialize the data model // - Perform a payment // // Visibility: Public // Hypothesis: -- // Reference: -- // int main(int argc, char** argv) { // Do all initializations // Wait for inbound sale request // See previous tutorials // Contexts are empty outPaymentContext(pay); outOutcomeParameter(out); // Fill-up contextes from ACE request processRequest(buffer+1,len-1,pay); // Contexts are ready outPaymentContext(pay); outOutcomeParameter(out); // Acknomledge payment request aceSendResponse((unsigned char*)"\x30\x30",2); gpiDisplayMessageByID(gpi_true,currentLanguage+1,THANKS); aceOut("Training Session - END\n"); return 0; }