Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Integrating Agnos - or Developing a payment application (L3) relies on:

  • A previous successful platform integration (DEVICE realization) validated from unit tests vectors execution

  • A first L2 qualification demonstrating that the system is mature enough to start L3 development (i.e. a full L2 TA is not required to start as long as significant use cases can be executed)

Agnos programmation paridigm is systematic:

  1. Set pre-conditions

  2. Trigger a transaction

  3. Get post-conditions

Agnos provides a rich API. The following gives some hints to built from scratch a first application integrating Agnos.

Table of Contents

Set pre-conditions

System Initialization

This level of initialization shall be performed only once usually at system’s start-up time.

  • gpiMain

  • gpiInitializeHSM

  • gpiInitializeDisplay

  • gpiInitPolling

Transaction Initialization

Trigger a transaction

Magstripe

Contact

Contactless

Get post-conditions

...

Table of Contents

  • 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’', Signal tab, Processing tab to see traces).

These examples have been built using a Visa configuration (paywave213_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.

Topics covered:

  • Trace in ACE

Code Block
breakoutModewide
languagec
// 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

Topics covered:

  • Init payment contexts

  • Languages management

Code Block
breakoutModewide
languagec
// 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

Topics covered:

  • Shared RAM example

  • Inbound requests from ACE:

    • TERMINAL

    • PROCESSING

    • ENTRY POINT

    • CAKeys

Code Block
breakoutModewide
// 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.

Topics covered:

  • Process and acknowledge a PAYMENT request from ACE

Code Block
breakoutModewide
languagec
// 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

Topics covered:

  • Configure Agnos Framework

  • Configure GPI for polling

  • Preparing Agnos Framework for contactless transaction

  • Execute a transaction with entry point

  • Complete a transaction by building up a receipt to ACE

Code Block
breakoutModewide
// 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

//---------------------------------------------------------
//            ackPayment
//---------------------------------------------------------
void ackPayment(tPaymentContext *ctx,tOutComeParameter* outcome,tBoolean isContactless)
{
	tByte buffer[300]="";
	tWord lenIn=0;
	tAmount amount=0, cashback=0;

	// CVM Results
	buffer[lenIn++] = 0x9F;
	buffer[lenIn++] = 0x34;
	buffer[lenIn++] = 0x03;
	gpiMemCpy(&buffer[lenIn],ctx->mCVMResult,sizeof(ctx->mCVMResult));
	lenIn+=3;

	// TVR
	buffer[lenIn++] = 0x95;
	buffer[lenIn++] = 0x05;
	gpiMemCpy(&buffer[lenIn],ctx->mTVR,sizeof(ctx->mTVR));
	lenIn+=5;

	// TSI
	buffer[lenIn++] = 0x9B;
	buffer[lenIn++] = 0x02;
	gpiMemCpy(&buffer[lenIn],ctx->mTSI,sizeof(ctx->mTSI));
	lenIn+=2;

	// AID
	buffer[lenIn++] = 0x84;
	buffer[lenIn++] = ctx->mAIDLen;
	gpiMemCpy(&buffer[lenIn],ctx->mAID,ctx->mAIDLen);
	lenIn+=ctx->mAIDLen;

	// POS entry mode
	buffer[lenIn++] = 0x9F;
	buffer[lenIn++] = 0x39;
	buffer[lenIn++] = 0x01;
	buffer[lenIn++] = ctx->mPOSEntryMode;

	// Cardholder name
	buffer[lenIn++] = 0x5F;
	buffer[lenIn++] = 0x20;
	buffer[lenIn++] = ctx->mCardholderNameLen;
	gpiMemCpy(&buffer[lenIn],ctx->mCardholderName,ctx->mCardholderNameLen);
	lenIn+=ctx->mCardholderNameLen;

	// AC
	buffer[lenIn++] = 0x9F;
	buffer[lenIn++] = 0x26;
	buffer[lenIn++] = 0x08;
	gpiMemCpy(&buffer[lenIn],ctx->mAC,8);
	lenIn+=8;

	// Date
	buffer[lenIn++] = 0x9A;
	buffer[lenIn++] = 0x03;
	gpiMemCpy(&buffer[lenIn],ctx->mTransactionDate,sizeof(ctx->mTransactionDate));
	lenIn+=3;

	// Time
	buffer[lenIn++] = 0x9F;
	buffer[lenIn++] = 0x21;
	buffer[lenIn++] = 0x03;
	gpiMemCpy(&buffer[lenIn],ctx->mTransactionTime,sizeof(ctx->mTransactionTime));
	lenIn+=3;

	// Transaction type
	buffer[lenIn++] = 0x9C;
	buffer[lenIn++] = 0x01;
	buffer[lenIn++] = ctx->mTransactionType;

	// Amount and Cashback
	buffer[lenIn++] = 0x9F;
	buffer[lenIn++] = 0x02;
	buffer[lenIn++] = 0x06;

	if(ctx->mAmount)
		amount = *ctx->mAmount;
	else // If amount not defined then 9F02 may be modified during CL processing
		convertBCDToAmount(&amount,ctx->mFinal9F02,sizeof(ctx->mFinal9F02));

	if(ctx->mCashBack)
		cashback = *ctx->mCashBack;

	convertAmount(amount + cashback,&buffer[lenIn]);
	lenIn+=6;
	buffer[lenIn++] = 0x9F;
	buffer[lenIn++] = 0x03;
	buffer[lenIn++] = 0x06;
	convertAmount(cashback,&buffer[lenIn]);
	lenIn+=6;

	// Transaction Currency Code
	buffer[lenIn++] = 0x5F;
	buffer[lenIn++] = 0x2A;
	buffer[lenIn++] = 0x02;
	buffer[lenIn++] = ctx->mTransactionCurrencyCode[0];
	buffer[lenIn++] = ctx->mTransactionCurrencyCode[1];

	// Transaction Currency Exponent
	buffer[lenIn++] = 0x5F;
	buffer[lenIn++] = 0x36;
	buffer[lenIn++] = 0x01;
	buffer[lenIn++] = ctx->mTransactionCurrencyExponent;

	// Label
	buffer[lenIn++] = 0x50;
	buffer[lenIn++] = ctx->mADF.mLabelLen;
	gpiMemCpy(&buffer[lenIn],ctx->mADF.mLabel,ctx->mADF.mLabelLen);
	lenIn+=ctx->mADF.mLabelLen;

	// Preferred Name
	buffer[lenIn++] = 0x9F;
	buffer[lenIn++] = 0x12;
	buffer[lenIn++] = ctx->mADF.mPreferredNameLen;
	gpiMemCpy(&buffer[lenIn],ctx->mADF.mPreferredName,ctx->mADF.mPreferredNameLen);
	lenIn+=ctx->mADF.mPreferredNameLen;

	// ISR
	buffer[lenIn++] = 0x9F;
	buffer[lenIn++] = 0x5B;
	buffer[lenIn++] = ctx->mIssuerScriptResultLen;
	gpiMemCpy(&buffer[lenIn],ctx->mIssuerScriptResult,ctx->mIssuerScriptResultLen);
	lenIn+=ctx->mIssuerScriptResultLen;

	// OASA
	buffer[lenIn++] = 0x9F;
	buffer[lenIn++] = 0x5D;
    if(ctx->mDataExchange.mUIRequestData.mValueQualifier == vqBALANCE)
    {
    	buffer[lenIn++] = sizeof(ctx->mDataExchange.mUIRequestData.mValue);
    	gpiMemCpy(&buffer[lenIn],ctx->mDataExchange.mUIRequestData.mValue,sizeof(ctx->mDataExchange.mUIRequestData.mValue));
    	lenIn+=sizeof(ctx->mDataExchange.mUIRequestData.mValue);
    }
    else
    	buffer[lenIn++] = 0x00;

    //  Agnos TVR
	buffer[lenIn++] = 0xDF;
	buffer[lenIn++] = 0x72;
	buffer[lenIn++] = sizeof(ctx->mAgnosTVR);
	gpiMemCpy(&buffer[lenIn],ctx->mAgnosTVR,sizeof(ctx->mAgnosTVR));
	lenIn+=sizeof(ctx->mAgnosTVR);

	// Proprietary blob (22 bytes)
	// 20 bytes: SHA PAN
	gpiMemCpy(&buffer[lenIn],ctx->mPANSHA,20);
	lenIn+=20;
	// 1 byte: Receipt and Sign flag
	if(pmwIsCVMSignature(ctx) || (outcome->mCVM == cvOBTAIN_SIGNATURE))
		buffer[lenIn++] = 0x31;
	else
		buffer[lenIn++] = 0x30;
	// Doesn't print receipt if CL + !gOutcome.mReceipt
	if(isContactless && !outcome->mReceipt)
			buffer[lenIn-1] |= 0x02;
	// 1 byte: outcome
	buffer[lenIn++] = 0x30 + outcome->mOutCome;

	aceSendResponse(buffer,lenIn);
}

//---------------------------------------------------------
//            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]="";


	// Initialize GPI
	// This is platform dependent. On Kizis AVT, this set PCSC readers' name from agnos.ini
	gpiMain(0,&parameters);

	// 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");
	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...
	ackPayment(pay,out,((pay->mPOSEntryMode == EMV_CL_MODE) || (pay->mPOSEntryMode == MAG_CL_MODE)));

	// UI stuff..
	gpiClearScreen();
	gpiDisplayMessageByID(gpi_true,currentLanguage,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

Topics covered:

  • Log signals to ACE

  • Log last APDU

Code Block
breakoutModewide
languagec
// 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]="";

	// Initialize GPI
	// This is platform dependent. On Kizis AVT, this set PCSC readers' name from agnos.ini
	gpiMain(0,&parameters);

	// 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");
	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...
	ackPayment(pay,out,((pay->mPOSEntryMode == EMV_CL_MODE) || (pay->mPOSEntryMode == MAG_CL_MODE)));

	// UI stuff..
	gpiClearScreen();
	gpiDisplayMessageByID(gpi_true,currentLanguage,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 #7: log APDU exchanges in batch mode

Topics covered:

  • Log APDU offline

  • Display offline APDU to ACE

  • Close session

Code Block
breakoutModewide
// NAME.......  Tutorial #7
// PURPOSE....	This code presents how to log APDU in batch mode
//
//	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)
{	
    // 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]="";

	// Initialize GPI
	// This is platform dependent. On Kizis AVT, this set PCSC readers' name from agnos.ini
	gpiMain(0,&parameters);

	// 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");
	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

	aceSetAPDULog(bFALSE); // Don't log APDU cmd/rsp in real-time
	aceSetTrace(MAX_TRACE_DEPTH+1); // Log offline

	enpExecutePaymentTransaction(sp,pay,out); // Call main entry point primitive to trigger a transaction

	// Log all batched APDU now
	aceLogAllBatchedAPDU();

	// 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...
	ackPayment(pay,out,((pay->mPOSEntryMode == EMV_CL_MODE) || (pay->mPOSEntryMode == MAG_CL_MODE)));

	// UI stuff..
	gpiClearScreen();
	gpiDisplayMessageByID(gpi_true,currentLanguage,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 #8: poll for card from payment application

Topics covered:

  • Polling from payment application

  • Manage gpiPolling error codes

  • Send an online authorization message

Code Block
breakoutModewide
languagec
// NAME.......  Tutorial #8
// PURPOSE....	This code presents how to poll for card from payment application
//
//	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)
{
	// 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]="";

	// Initialize GPI
	// This is platform dependent. On Kizis AVT, this set PCSC readers' name from agnos.ini
	gpiMain(0,&parameters);

	// Load available kernels
	enpConnectPaymentServices("./AGNOS/");
	
	// Set contactless configurations
	dtmInitializeFromFile("./AGNOS/",NULL,NULL,NULL,NULL,NULL,pay->mTransactionType,bFALSE);

	// Initialization entry point
	enpInitialize("","",bFALSE,bFALSE,bFALSE,pay); // No more signal managed now

	tTerminalContext *terminalCtx = NULL;
	tByte technoToDetect = TECHNO_CL_TYPE_EMV;
	tGPIError gpiError = 0;

	terminalCtx = (tTerminalContext*)dtmGetData(TERMINAL_CTX);

	if(terminalCtx->mMagstripe)
	{
		technoToDetect |= TECHNO_MAGSTRIPE;
	}
	if(terminalCtx->mEMVContact)
	{
		technoToDetect |= TECHNO_CONTACT;
	}

	gpiSetTechnoToDetect(technoToDetect);

	sprintf(line1,"PAY");
	if(pay->mAmount)
	{
		sprintf(line2,"AMOUNT: %.2f",(*pay->mAmount)/100.0);
	}
	else
	{
		sprintf(line2,"--");
	}

	gpiDisplayMessage(gpi_true,line1);
	gpiDisplayMessage(gpi_false,line2);

	gpiError = gpiPolling("",5); // timeout set to 5 seconds
	sprintf(tmp,"GPI ERROR: 0x%.4X\n",gpiError);
	aceOut(tmp);
	if(scrINTERRUPTED == gpiError)
	{
		sprintf(tmp,"TRANSACTION INTERRUPTED\n");
		aceOut(tmp);
	}
	else if(polTIMEOUT == gpiError)
	{
		sprintf(tmp,"No Tap\n");
		aceOut(tmp);
	}
	else
	{
		sprintf(tmp,"CARD DETECTED FROM L3\n");
		aceOut(tmp);

		enpSkipPollingOnNextStartB(gpiError);
		enpExecutePaymentTransaction(sp,pay,out); // Call main entry point primitive to trigger a transaction

		if (ocONLINE_REQUEST == out->mOutCome)
		{
			gpiDisplayMessage(gpi_true,"ONLINE");
			length = 0;
			pmwGetEMVData(	pay->mSession,(tByte*)"\x9F\x02\x95\x9F\x26\x9F\x27",7,
							pay->mDataExchange.mDataRecord.mStream,&pay->mDataExchange.mDataRecord.mLength);

			// For example, use ACE to emulate online auth.
			aceSendContactlessAuthorization(pay,bFALSE);

			if (TC == pay->mCID)
			{
				gpiDisplayMessage(gpi_true,"APPROVED");
			}
			else
			{
				gpiDisplayMessage(gpi_true,"DECLINED");
			}
		}
		else if (ocAPPROVED == out->mOutCome)
		{
			gpiDisplayMessage(gpi_true,"APPROVED");
		}
		else
		{
			gpiDisplayMessage(gpi_true,"DECLINED");
		}
	}

	// 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...
	ackPayment(pay,out,((pay->mPOSEntryMode == EMV_CL_MODE) || (pay->mPOSEntryMode == MAG_CL_MODE)));

	// UI stuff..
	gpiClearScreen();
	gpiDisplayMessageByID(gpi_true,currentLanguage,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 #9: integrate Data Exchange for contactless

Topics covered:

  • Create a DE/DS callback

  • Intercept contactless card processing flow

Code Block
breakoutModewide
languagec
// NAME.......  Tutorial #9
// PURPOSE....	This code presents how to integrated data exchange
//
//	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)
{	// 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]="";

	// Initialize GPI
	// This is platform dependent. On Kizis AVT, this set PCSC readers' name from agnos.ini
	gpiMain(0,&parameters);

	// Load available kernels
	enpConnectPaymentServices("./AGNOS/");
	
	// Set contactless configurations
	dtmInitializeFromFile("./AGNOS/",NULL,NULL,NULL,NULL,NULL,pay->mTransactionType,bFALSE);

	// Initialization entry point
	enpInitialize("","",bFALSE,bFALSE,bFALSE,pay); // No more signal managed now

	tTerminalContext *terminalCtx = NULL;
	tByte technoToDetect = TECHNO_CL_TYPE_EMV;
	tGPIError gpiError = 0;

	terminalCtx = (tTerminalContext*)dtmGetData(TERMINAL_CTX);

	if(terminalCtx->mMagstripe)
	{
		technoToDetect |= TECHNO_MAGSTRIPE;
	}
	if(terminalCtx->mEMVContact)
	{
		technoToDetect |= TECHNO_CONTACT;
	}

	gpiSetTechnoToDetect(technoToDetect);

	sprintf(line1,"PAY");
	if(pay->mAmount)
	{
		sprintf(line2,"AMOUNT: %.2f",(*pay->mAmount)/100.0);
	}
	else
	{
		sprintf(line2,"--");
	}

	gpiDisplayMessage(gpi_true,line1);
	gpiDisplayMessage(gpi_false,line2);

	gpiError = gpiPolling("",5); // timeout set to 5 seconds
	sprintf(tmp,"GPI ERROR: 0x%.4X\n",gpiError);
	aceOut(tmp);
	if(scrINTERRUPTED == gpiError)
	{
		sprintf(tmp,"TRANSACTION INTERRUPTED\n");
		aceOut(tmp);
	}
	else if(polTIMEOUT == gpiError)
	{
		sprintf(tmp,"No Tap\n");
		aceOut(tmp);
	}
	else
	{
		sprintf(tmp,"CARD DETECTED FROM L3\n");
		aceOut(tmp);

		// Use DF12 - TAGS to READ - inside contactless combination
		// DF12015A will trigger the data exchange mechanism when tag 5A will be found
		pay->mDataExchange.mCallback = &myDEProcess;
		pay->mDataExchange.mDEList = &gDEListCtx;


		enpSkipPollingOnNextStartB(gpiError);
		enpExecutePaymentTransaction(sp,pay,out); // Call main entry point primitive to trigger a transaction

		if (ocONLINE_REQUEST == out->mOutCome)
		{
			gpiDisplayMessage(gpi_true,"ONLINE");
			length = 0;
			pmwGetEMVData(	pay->mSession,(tByte*)"\x9F\x02\x95\x9F\x26\x9F\x27",7,
							pay->mDataExchange.mDataRecord.mStream,&pay->mDataExchange.mDataRecord.mLength);

			// For example, use ACE to emulate online auth.
			aceSendContactlessAuthorization(pay,bFALSE);

			if (TC == pay->mCID)
			{
				gpiDisplayMessage(gpi_true,"APPROVED");
			}
			else
			{
				gpiDisplayMessage(gpi_true,"DECLINED");
			}
		}
		else if (ocAPPROVED == out->mOutCome)
		{
			gpiDisplayMessage(gpi_true,"APPROVED");
		}
		else
		{
			gpiDisplayMessage(gpi_true,"DECLINED");
		}
	}

	// 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...
	ackPayment(pay,out,((pay->mPOSEntryMode == EMV_CL_MODE) || (pay->mPOSEntryMode == MAG_CL_MODE)));

	// UI stuff..
	gpiClearScreen();
	gpiDisplayMessageByID(gpi_true,currentLanguage,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;
}