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