That series of tutorials use Kizis as the squeleton to build from scratch a basic payment application.
Use ACE to trace and monitor behaviors (open ‘'Console Log’' to see traces).
These examples have been built using a Visa configuration (Ipaywave213_ICC/CONF_01)
Tutorial #1: set communication and traces using ACE
The very step to avoid difficult integration times: unit test the external communication with ACE.
// NAME....... Tutorial #1 // PURPOSE.... This code presents how to connect onto ACE and to use aceOut // // Hypothesis and optimizations: // Communication between ACE and acceptance device is IP // // PROJECT.... Wiki // REFERENCES. -- // // Copyright ©2005-2020 - 9164-4187 QUEBEC INC (“AMADIS”), All Rights Reserved // //--------------------------------------------------------- // Main //--------------------------------------------------------- int main(int argc, char** argv) { // Communication int port, length; char address[50]=""; // Get Communication parameters from ini file // These paramerters maybe wired inside the code for training purposes 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); // Create server and client assert(comtcpOpen(&gACEServerCOMTCP, TCP_SERVER, 0, port) == COM_NO_ERROR); assert(comtcpOpen(&gACEClientCOMTCP, TCP_CLIENT, address, 1979) == COM_NO_ERROR); // Set ACE with server and client aceInitializeCommunication(&gACEServerCOMTCP.com,&gACEClientCOMTCP.com); // Initialize running mode aceSetMode(pmSDK); // Set ACE option aceSetUIDisplay(bTRUE); // Trace into ACE aceOut("Training Session - BEGIN\n"); aceOut("Hello World!\n"); aceOut("Training Session - END\n"); return 0; }
Tutorial #2: initialize language and payment context
// NAME....... Tutorial #2 // PURPOSE.... This code presents how to intialize language and payment contexts // // Hypothesis and optimizations: // Communication between ACE and acceptance device is IP // // PROJECT.... Wiki // REFERENCES. -- // // Copyright ©2005-2020 - 9164-4187 QUEBEC INC (“AMADIS”), All Rights Reserved // //--------------------------------------------------------- // Main //--------------------------------------------------------- int main(int argc, char** argv) { // Communication int port, length; char address[50]=""; // Trace for debugging char tmp[100]=""; // Payment Contexts tPaymentContext* pay = &gPaymentCtx; tOutComeParameter* out = &gOutcome; // System's language tByte availableLanguage=0; tByte currentLanguage = 0; // Get Communication parameters from ini file // These paramerters maybe wired inside the code for training purposes 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); // Create server and client assert(comtcpOpen(&gACEServerCOMTCP, TCP_SERVER, 0, port) == COM_NO_ERROR); assert(comtcpOpen(&gACEClientCOMTCP, TCP_CLIENT, address, 1979) == COM_NO_ERROR); // Set ACE with server and client aceInitializeCommunication(&gACEServerCOMTCP.com,&gACEClientCOMTCP.com); // Initialize running mode aceSetMode(pmSDK); // Set up ACE's options aceSetUIDisplay(bTRUE); 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; gpiDisplayMessageByID(gpi_true,currentLanguage,WELCOME); gpiDisplayMessageByID(gpi_true,currentLanguage+1,THANKS); // Assuming that there are at least 2 languages aceOut("Training Session - END\n"); return 0; }
Tutorial #3: manage ACE requests
// NAME....... Tutorial #3 // PURPOSE.... This code presents how to intialize language and payment contexts // // Hypothesis and optimizations: // Communication between ACE and acceptance device is IP // // PROJECT.... Wiki // REFERENCES. -- // // Copyright ©2005-2020 - 9164-4187 QUEBEC INC (“AMADIS”), All Rights Reserved // //--------------------------------------------------------- // CreateFile //--------------------------------------------------------- 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 //--------------------------------------------------------- int main(int argc, char** argv) { // Communication int port, length; char address[50]=""; // Trace for debugging char tmp[100]=""; // Payment contexts tPaymentContext* pay = &gPaymentCtx; tOutComeParameter* out = &gOutcome; // System's language tByte availableLanguage=0, currentLanguage = 0; // 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); // Create server and client assert(comtcpOpen(&gACEServerCOMTCP, TCP_SERVER, 0, port) == COM_NO_ERROR); assert(comtcpOpen(&gACEClientCOMTCP, TCP_CLIENT, address, 1979) == COM_NO_ERROR); // Set ACE with server and client aceInitializeCommunication(&gACEServerCOMTCP.com,&gACEClientCOMTCP.com); // Initialize running mode aceSetMode(pmSDK); // Set up ACE's options aceSetUIDisplay(bTRUE); 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,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 how to manage a transaction payment transaction ans set tPaymentContext accordingly. A comprehensive example is provided below.
// NAME....... Tutorial #4 // PURPOSE.... This code presents how to process a payment request // // Hypothesis and optimizations: // Communication between ACE and acceptance device is IP // // PROJECT.... Wiki // REFERENCES. -- // // Copyright ©2005-2020 - 9164-4187 QUEBEC INC (“AMADIS”), All Rights Reserved // // See comprehensive example to get all functions used by this tutorial //--------------------------------------------------------- // Main //--------------------------------------------------------- int main(int argc, char** argv) { // See previous examples to get a PAYMENT request from ACE // Display contexts BEFORE processing ACE request outPaymentContext(pay); outOutcomeParameter(out); // Process ACE request for a payment. Set pay as per TRD contents processRequest(buffer+1,len-1,pay); // Display contexts AFTER processing ACE request outPaymentContext(pay); outOutcomeParameter(out); // Acknowledge ACE aceSendResponse((unsigned char*)"\x30\x30",2); gpiDisplayMessageByID(gpi_true,currentLanguage,THANKS); aceOut("Training Session - END\n"); return 0; }
Tutorial #5: perform a contactless transaction
// NAME....... Tutorial #5 // PURPOSE.... This code presents how to excute a contactless payment transaction // // Hypothesis and optimizations: // Communication between ACE and acceptance device is IP // // PROJECT.... Wiki // REFERENCES. -- // // Copyright ©2005-2020 - 9164-4187 QUEBEC INC (“AMADIS”), All Rights Reserved // // See comprehensive example to get all functions used by this tutorial //--------------------------------------------------------- // Main //--------------------------------------------------------- int main(int argc, char** argv) { // See previous examples to get a PAYMENT request from ACE processRequest(buffer+1,len-1,pay); char* parameters; tStartingPoint sp = spSTART_A; // To initiate contactless transaction at preprocessing step char line1[100]="",line2[100]=""; char *string=NULL; // Initialize GPI // This is platform dependent. On Kizis AVT, this set PCSC readers' name from agnos.ini gpiMain(0,¶meters); // Load available kernels enpConnectPaymentServices("./AGNOS/"); // Set contactless configurations dtmInitializeFromFile("./AGNOS/",NULL,NULL,NULL,NULL,NULL,pay->mTransactionType,bFALSE); // Initialization entry point enpInitialize("","",bTRUE,bFALSE,bFALSE,pay); gpiSetTechnoToDetect(TECHNO_CL_TYPE_EMV); // Right before polling, need to tell which techno to poll on sprintf(line1,"PAY"); gpiGetString(pmwGetLanguage(pay),PRESENT_CARD,&string); if(pay->mAmount) { sprintf(line2,"AMOUNT: %.2f",(*pay->mAmount)/100.0); } else { sprintf(line2,"--"); } gpiSetPollingMessage(line1,line2,NULL); outPaymentContext(pay); // Trace payment request enpExecutePaymentTransaction(sp,pay,out); // Call main entry point primitive to trigger a transaction outOutcomeParameter(out); // Trace outcome // Acknowledge ACE and sends several tags for receipt printing... completePayment(pay,out,((pay->mPOSEntryMode == EMV_CL_MODE) || (pay->mPOSEntryMode == MAG_CL_MODE))); // UI stuff.. gpiClearScreen(); gpiDisplayMessageByID(gpi_true,currentLanguage+1,THANKS); gpiSwitchLED(LED_1,gpi_false,0); gpiSwitchLED(LED_2,gpi_false,0); gpiSwitchLED(LED_3,gpi_false,0); gpiSwitchLED(LED_4,gpi_false,0); aceOut("Training Session - END\n"); return 0; }
Tutorial #6: add some logs to analyze the transaction
// NAME....... Tutorial #6 // PURPOSE.... This code presents how to log a transaction for analysis // // Hypothesis and optimizations: // Communication between ACE and acceptance device is IP // // PROJECT.... Wiki // REFERENCES. -- // // Copyright ©2005-2020 - 9164-4187 QUEBEC INC (“AMADIS”), All Rights Reserved // // See comprehensive example to get all functions used by this tutorial //--------------------------------------------------------- // Main //--------------------------------------------------------- int main(int argc, char** argv) { // See previous examples to get a PAYMENT request from ACE processRequest(buffer+1,len-1,pay); char* parameters; tStartingPoint sp = spSTART_A; // To initiate contactless transaction at preprocessing step char line1[100]="",line2[100]=""; char *string=NULL; // Initialize GPI // This is platform dependent. On Kizis AVT, this set PCSC readers' name from agnos.ini gpiMain(0,¶meters); // Load available kernels enpConnectPaymentServices("./AGNOS/"); // Set contactless configurations dtmInitializeFromFile("./AGNOS/",NULL,NULL,NULL,NULL,NULL,pay->mTransactionType,bFALSE); // Initialization entry point enpInitialize("","",bTRUE,bFALSE,bFALSE,pay); gpiSetTechnoToDetect(TECHNO_CL_TYPE_EMV); // Right before polling, need to tell which techno to poll on sprintf(line1,"PAY"); gpiGetString(pmwGetLanguage(pay),PRESENT_CARD,&string); if(pay->mAmount) { sprintf(line2,"AMOUNT: %.2f",(*pay->mAmount)/100.0); } else { sprintf(line2,"--"); } gpiSetPollingMessage(line1,line2,NULL); // aceOut are to slow // Let's use some testing signals the same way a TA would do // Even if enpInitialize set signal = bTRUE, need to do more stuff tSignal signal; aceSetSignal(bTRUE); // Activate signal in ACE aceSetDigitalSignal(bTRUE); // Use digital signal instead of legacy strings aceGetSignal(&signal); // Get ACE signal services pmwSetSignal(&signal); // Set AgnosMW accordingly enpExecutePaymentTransaction(sp,pay,out); // Call main entry point primitive to trigger a transaction // Use ACE to trace last APDU jsut for fun tByte apduReq[MAX_APDU_LENGTH]="", apduResp[MAX_APDU_LENGTH]=""; tWord lenReq, lenResp; aceSetAPDULog(bTRUE); gpiGetLastExchangedAPDU((const char*)"",apduReq,&lenReq,apduResp,&lenResp); aceLogAPDU((const char*)"OUT",apduReq,lenReq); // Must be labelled OUT aceLogAPDU((const char*)"IN",apduResp,lenResp); // Must be labelled IN // For the same price, send all tags to ACE aceSendTag(pay); // Clean session closing (will switch antenna off) pay->mDataExchange.mError.mL1 = erL1OK; // Avoid display message within entry point out->mUIReqOnRestartPresent = bFALSE; // Avoid display message within entry point enpExecutePaymentTransaction(spNO_START,pay,out); // Acknowledge ACE and sends several tags for receipt printing... completePayment(pay,out,((pay->mPOSEntryMode == EMV_CL_MODE) || (pay->mPOSEntryMode == MAG_CL_MODE))); // UI stuff.. gpiClearScreen(); gpiDisplayMessageByID(gpi_true,currentLanguage+1,THANKS); gpiSwitchLED(LED_1,gpi_false,0); gpiSwitchLED(LED_2,gpi_false,0); gpiSwitchLED(LED_3,gpi_false,0); gpiSwitchLED(LED_4,gpi_false,0); aceOut("Training Session - END\n"); return 0; }