void CISO8583BackEndServerSession::OnSendMsgFailed(CMultiXAppMsg &FailedMsg,bool bTimeout) { /* If we forwarded a message to another process, we need to check the failure and decide what to do. */ switch(CISO8583Msg::VersionIndependentMTI(FailedMsg.MsgCode())) // this will give us version independent MTI { case CISO8583Msg::MTIAuthorizationMessageRequest : case CISO8583Msg::MTIReversalMessageAdvice : case CISO8583Msg::MTIReversalMessageAdviceRepeat : { /* These are all messages we received from acquirer gateways or acquirers in general, and failed to forward them to remote gateways/issuers, so we respond to the acquirer with an error indicating that we were unable to forward the request. */ // We extract the acquirer we saved when we forwarded the message. CMultiXAppMsg *AcquirerMsg = (CMultiXAppMsg *)FailedMsg.SavedContext(); AcquirerMsg->Reply(CISO8583Msg::ForwardToIssuerFailed); delete AcquirerMsg; // WE MUST DELETE THE MESSAGE BECAUSE WE CALLED "Keep()" BEFORE WE FORWARDED IT. } break; } }
void CISO8583AuthorizerServerSession::OnNewMsg(CMultiXAppMsg &Msg) { DebugPrint(1,"New Message Received\n"); switch(CISO8583Msg::VersionIndependentMTI(Msg.MsgCode())) // we check the ISO8583 Version independent MTI value { case CISO8583Msg::MTIAuthorizationMessageRequest : case CISO8583Msg::MTIFinancialMessageRequest : { // If we have an ISO 8583 Msg Code, we try to parse the msg to make sure that // we have a valid ISO 8583 CISO8583Msg ISOMsg; CISO8583Msg::TValidationError Error = ISOMsg.FromISO((const byte_t *)Msg.AppData(),Msg.AppDataSize()); if(Error != CISO8583Msg::NoError) { Msg.Reply(Error); } else if(ISOMsg.Version() != CISO8583Msg::ISO8583_1_1987) // we Support only 1987 version { Msg.Reply(CISO8583Msg::VersionNotSupported); } else { OnISO8583Msg(Msg,ISOMsg); } } break; default : // We do not know this message, reply with error Msg.Reply(CISO8583Msg::MTINotSupported); break; } }
void CMultiXProcess::OnNewMsgFromTpm(CMultiXAppMsg &AppMsg) { if(Owner()->TpmProcID() == 0) Owner()->m_TpmProcID = this->ProcessID(); if(Owner()->TpmProcID() != this->ProcessID()) { Reject(); } else { switch(AppMsg.MsgCode()) { case CMultiXTpmCtrlMsg::ConfigDataMsgCode : { OnTpmConfigData(AppMsg); break; } case CMultiXTpmCtrlMsg::ProcessShutdownMsgCode : OnPrepareForShutdown(AppMsg); break; case CMultiXTpmCtrlMsg::ProcessRestartMsgCode : OnProcessRestart(AppMsg); break; case CMultiXTpmCtrlMsg::ProcessSuspendMsgCode : OnProcessSuspend(AppMsg); break; case CMultiXTpmCtrlMsg::ProcessResumeMsgCode : OnProcessResume(AppMsg); break; } } AppMsg.Reply(MultiXNoError); }
void CMultiXProcess::OnSendMsgToTpmFailed(CMultiXAppMsg &OriginalMsg) { switch(OriginalMsg.MsgCode()) { case CMultiXTpmCtrlMsg::GetListeningAddressMsgCode : OnGetListeningAddressFailed(OriginalMsg); break; } }
void CMultiXProcess::OnDataReplyFromTpmReceived(CMultiXAppMsg &ReplyMsg, CMultiXAppMsg &OriginalMsg) { switch(OriginalMsg.MsgCode()) { case CMultiXTpmCtrlMsg::GetListeningAddressMsgCode : OnListeningAddressReceived(ReplyMsg); break; } }
//! see CMultiXSession::OnDataReplyReceived void CISO8583BackEndServerSession::OnDataReplyReceived(CMultiXAppMsg &ReplyMsg,CMultiXAppMsg &ForwardedMsg) { DebugPrint(3,"Data Reply Received\n"); /* We get here when we receive a response for a message we forwarded before. In this case we will forward the response back to the originator, we do not care for the content, we forward it back almost AS IS except for few fields that we need to restore because changed them before we forwarded the message, the fields are: MTI - Convert it to 1993 version. BMP 7 - Set Transmission time BMP 11 - restore sender STAN BMP 12 - Restore sender Date and Time Local BMP 32 - Restore senders Acquiring Institution Identification Code */ /* In order to restore old values, we need to restore the message we received originaly from the acquirer or from the pos terminal. this message is saved in the SaveContext() of the ForwardedMsg. */ CMultiXAppMsg *AcquirerMsg = (CMultiXAppMsg *)ForwardedMsg.SavedContext(); CISO8583Msg AcquirerISO; CISO8583Msg ReplyISO; AcquirerISO.FromISO((const byte_t *)AcquirerMsg->AppData(),AcquirerMsg->AppDataSize()); ReplyISO.FromISO((const byte_t *)ReplyMsg.AppData(),ReplyMsg.AppDataSize()); ReplyISO.SetTimes(time(NULL),true); ReplyISO.SetDateTimeLocal(AcquirerISO.DateTimeLocal()); ReplyISO.SetSTAN(AcquirerISO.STAN()); ReplyISO.SetAcquiringInstitutionIdentificationCode(AcquirerISO.AcquiringInstitutionIdentificationCode()); ReplyISO.ToISO(); AcquirerMsg->Reply(CISO8583Msg::VersionDependentMTI(CISO8583Msg::ISO8583_2_1993,ReplyMsg.MsgCode()), ReplyISO.ISOBuffer(), ReplyISO.ISOBufferSize(),0,0,0,0,0,ReplyMsg.Error()); delete AcquirerMsg; // WE MUST DELETE THE MESSAGE BECAUSE WE CALLED "Keep()" BEFORE WE FORWARDED IT. /* we reply the ReplyMsg for the case that the process that replied to us expects to receive a notification that we received the reply, if it does not wait for the reply, no reply is sent. */ ReplyMsg.Reply(); }
void CISO8583AuthorizerServerSession::ForwardToIssuer(int CreditAccountAtIssuer,CMultiXAppMsg &Msg,CISO8583Msg &ISOMsg) { /* we get here if we need someone else to authorize the request. In that case we need to forward the request to our Acquirer Gateway Front End. To do that we need to forward it with a different message code, so MultiXTpm will not forward it back to us. The way we do it, the message code change is by setting the MTI version field to CISO8583Msg::ISO8583_Private. When the message is received by our Gateway process, it will revert back to CISO8583Msg::ISO8583_2_1993 and forward it on. When we forward a request, we do not block for a response, when a response arrives, we get it thru the event "OnDataReplyReceived". in order to be able to associate the response with the original msg, we pass a pointer to the original message in the "Context" parameter of the "Send" function, and when "OnDataReplyReceived" is called, we retrieve this pointer by using "SavedContext()" in the Original msg we sent. Since MultiX automatically destroys messages it forwards to the application, we must call "Keep()" on the received message, to prevent it from being destroyed, in that case, when the reply is received, we need to destroy the original message ourselves. When we play the role of an acquirer gateway, usualy get the request from the POS terminal, do validation required and maybe modify some data, and then forward the message to the issuer gateway, in our case, the forward will be to a process in our system, that will decide to which issuer to forward to, based on that, will use a specific connection to the spcific issuer gateway. */ /* Before we forward the request, we need to set 3 fields to identify the message as a one sent from this process : BMP 11 - STAN, BMP 12 - Times, BMP 32 - Our Acquirer ID BMP 42 - our account within the issuer When we receive the response from the remote gateway/issuer, we must replace these fields again to the orignal values that the sender set before sending the request to us */ ISOMsg.SetTimes(time(NULL)); ISOMsg.SetSTAN(Owner()->GetNextSTAN()); ISOMsg.SetAcquiringInstitutionIdentificationCode(Owner()->MyAcquirerID()); CISO8583ElementValue Value(CISO8583Utilities::ToString(CreditAccountAtIssuer),CISO8583ElementValue::ISO8583ElementTypeNumeric); ISOMsg.SetCardAcceptorIdentificationCode(Value); ISOMsg.ToISO(); if(Send(CISO8583Msg::VersionDependentMTI(CISO8583Msg::ISO8583_Private,Msg.MsgCode()),ISOMsg.ISOBuffer(),ISOMsg.ISOBufferSize(),CMultiXAppMsg::FlagNotifyAll,0,0,&Msg)) Msg.Keep(); else Msg.Reply(ErrUnableToForwardMsg); }
void CMultiplexerServerFEServerSession::OnNewMsg(CMultiXAppMsg &Msg) { DebugPrint(1,"New Message Received\n"); int ReplyError = ErrUnableToForwardMsg; CMultiplexerServerFELink *Link = Owner()->FindReadyLink(Msg.MsgCode()); if(Link) { ReplyError = Link->Forward(Msg); } if(ReplyError == 0) { Msg.Keep(); } else { Msg.Reply(ReplyError); } }
void CISO8583AcquirerGatewayFEServerSession::OnNewMsg(CMultiXAppMsg &Msg) { DebugPrint(1,"New Message Received\n"); switch(CISO8583Msg::VersionIndependentMTI(Msg.MsgCode())) { case CISO8583Msg::MTIAuthorizationMessageRequest : case CISO8583Msg::MTIFinancialMessageRequest : case CISO8583Msg::MTIReversalMessageAdviceRepeat : case CISO8583Msg::MTIReversalMessageAdvice : OnTransactionRequest(Msg); break; case CISO8583Msg::MTINetworkManagementMessageRequestOther : case CISO8583Msg::MTINetworkManagementMessageRequestAcquirer : case CISO8583Msg::MTINetworkManagementMessageRequestAcquirerRepeat : OnNetworkManagementRequest(Msg); break; default : Msg.Reply(ErrInvalidMsgCode); break; } }
void CISO8583BackEndServerSession::OnReversalAdvice(CMultiXAppMsg &Msg,CISO8583Msg &ISOMsg) { std::string Temp; byte_t ElementIDs[] = {2,3,4,6,7,10,11,24,25,30,32,37,38,43,49,51,53,54,56,58,64}; // We make sure that no extra elements are found in the request CISO8583Msg::TValidationError Error = ISOMsg.EnsureOnlyElementIDs(ElementIDs,sizeof(ElementIDs)); if(Error != CISO8583Msg::NoError) { Msg.Reply(Error); return; } // Check that we have PAN Temp = ISOMsg.PAN(); if(Temp == "") { Msg.Reply(CISO8583Msg::PANIsMissing); return; } // Check that Processing Code exists and is valid. Temp = ISOMsg.ProcessingCode(); if(Temp == "") { Msg.Reply(CISO8583Msg::ProcessingCodeIsMissing); return; } if(!(Temp == "000000" || Temp == "010000" || Temp == "090000")) { Msg.Reply(CISO8583Msg::InvalidProcessingCode); return; } // Check the transaction amount for existance and that it does not exceede some Maximum Temp = ISOMsg.TransactionAmount(); if(Temp == "") { Msg.Reply(CISO8583Msg::TransactionAmountIsMissing); return; } if(CISO8583Utilities::ToInt64(Temp) > 99999999) { Msg.Reply(CISO8583Msg::TransactionAmountAboveMaximum); return; } // check Transaction Currency. Temp = ISOMsg.TransactionCurrencyCode(); if(Temp == "") { Msg.Reply(CISO8583Msg::TransactionCurrencyIsMissing); return; } if(Temp != "978") // Transaction amount is not in Euro { if(ISOMsg.CardholderBillingAmount() == "") { Msg.Reply(CISO8583Msg::CardholderBillingAmountIsMissing); return; } if(ISOMsg.CardholderBillingCurrencyCode() != "978") { Msg.Reply(CISO8583Msg::CardholderBillingCurrencyIsNotEuro); return; } if(ISOMsg.CardholderBillingConversionRate() == "") { Msg.Reply(CISO8583Msg::CardholderBillingConversionRateIsMissing); return; } // Lets validate that the conversion to Euro is done correctly. int64_t TRAmount = CISO8583Utilities::ToInt64(ISOMsg.TransactionAmount()); int64_t BLAmount = CISO8583Utilities::ToInt64(ISOMsg.CardholderBillingAmount()); int64_t CRate = CISO8583Utilities::ToInt64(ISOMsg.CardholderBillingConversionRate()); int64_t Divisor = CISO8583Utilities::PowerOf10((unsigned int)(CRate / 10000000)); TRAmount *= (CRate % 10000000); TRAmount /= Divisor; if(TRAmount != BLAmount) { Msg.Reply(CISO8583Msg::CardholderBillingAmountWrong); return; } TRAmount *= CRate; } else // we must make sure that IDs 6,10,51 are not there { if(!(ISOMsg.CardholderBillingAmount() == "" || ISOMsg.CardholderBillingCurrencyCode() == "" || ISOMsg.CardholderBillingConversionRate() == "")) { Msg.Reply(CISO8583Msg::ExtraElementsFound); return; } } // Check that we got STAN if(ISOMsg.STAN() == "") { Msg.Reply(CISO8583Msg::STANIsMissing); return; } // Check that we got Function Code if(ISOMsg.FunctionCode() == "") { Msg.Reply(CISO8583Msg::FunctionCodeIsMissing); return; } // Check that we have Message Reason Code if(ISOMsg.MessageReasonCode() == "") { Msg.Reply(CISO8583Msg::MessageReasonCodeIsMissing); return; } // Check that we Original Amounts on Partail Reversal if(ISOMsg.OriginalAmounts() == "" && ISOMsg.FunctionCode() == "401") { Msg.Reply(CISO8583Msg::OriginalAmountsIsMissing); return; } else if(ISOMsg.OriginalAmounts() != "" && ISOMsg.FunctionCode() != "401") { Msg.Reply(CISO8583Msg::ExtraElementsFound); return; } // Check that we got Acquiring Institution Identification Code if(ISOMsg.AcquiringInstitutionIdentificationCode() == "") { Msg.Reply(CISO8583Msg::AcquiringInstitutionIdentificationCodeIsMissing); return; } // Check that we have Retrieval Reference Number if(ISOMsg.RRN() == "") { Msg.Reply(CISO8583Msg::RRNIsMissing); return; } // Check that we have Approval Code if(ISOMsg.ApprovalCode() == "") { Msg.Reply(CISO8583Msg::ApprovalCodeIsMissing); return; } // check that we have Card Acceptor Name/Location if(ISOMsg.CardAcceptorNameLocation().Size() == 0) { Msg.Reply(CISO8583Msg::CardAcceptorNameLocationIsMissing); return; } // check that we have Security Related Control Information, we do not check the values because we do not have the Keys // in this implementation. if(ISOMsg.SecurityRelatedControlInformation() == "") { Msg.Reply(CISO8583Msg::SecurityRelatedControlInformationIsMissing); return; } // check that we have Additional amounts when processing code is "090000" if(ISOMsg.ProcessingCode() == "090000") { if(ISOMsg.AdditionalAmounts().Size() == 0) { Msg.Reply(CISO8583Msg::AdditionalAmountsIsMissing); return; } } else if(ISOMsg.AdditionalAmounts().Size() != 0) { Msg.Reply(CISO8583Msg::ExtraElementsFound); return; } // check that we have Original Data Elements if(ISOMsg.OriginalDataElements().Size() == 0) { Msg.Reply(CISO8583Msg::OriginalDataElementsIsMissing); return; } // check that we have Authorizing Agent Institution Identification Code if(ISOMsg.AuthorizingAgentInstitutionIdentificationCode().Size() == 0) { Msg.Reply(CISO8583Msg::AuthorizingAgentInstitutionIdentificationCodeIsMissing); return; } // check that we have MAC, we do not check the values because we do not have the Keys // in this implementation. if(ISOMsg.MAC1() == "") { Msg.Reply(CISO8583Msg::MAC1IsMissing); return; } /* After we finished validating the request, we need to decide if we authorize locally or we forward it to the issuer for authorization */ if(AuthorizeLocally(ISOMsg)) // in this example it is always true { // We create a new ISO Message // we will decline this message CISO8583Msg Rsp; // We will Dup field from the original message and add/ change the ones that are relevant for the response Rsp.SetMTI(CISO8583Msg::VersionDependentMTI(CISO8583Msg::ISO8583_2_1993,CISO8583Msg::MTIReversalMessageAdviceResponse)); // this is a response Rsp.SetPAN(ISOMsg.PAN()); Rsp.SetProcessingCode(ISOMsg.ProcessingCode()); Rsp.SetTransactionAmount(ISOMsg.TransactionAmount()); Rsp.SetCardholderBillingAmount(ISOMsg.CardholderBillingAmount()); Rsp.SetTimes(time(NULL),true); // This will set transmission time Rsp.SetDateTimeLocal(ISOMsg.DateTimeLocal()); Rsp.SetCardholderBillingConversionRate(ISOMsg.CardholderBillingConversionRate()); Rsp.SetSTAN(ISOMsg.STAN()); Rsp.SetAcquiringInstitutionIdentificationCode(ISOMsg.AcquiringInstitutionIdentificationCode()); Rsp.SetRRN(ISOMsg.RRN()); Rsp.SetActionCode(std::string("909")); // System Mulfunction Rsp.SetTransactionCurrencyCode(ISOMsg.TransactionCurrencyCode()); Rsp.SetCardholderBillingCurrencyCode(ISOMsg.CardholderBillingCurrencyCode()); Rsp.SetSecurityRelatedControlInformation(std::string("00000007000001FFFF")); // this is a dummy value, real value should be set based on Keys. Rsp.SetOriginalDataElements(ISOMsg.OriginalDataElements()); Rsp.SetMAC1(std::string("0123456789ABCDEF")); // dummy value, should be based on keys CISO8583Msg::TValidationError Error = Rsp.ToISO(); if(Error != CISO8583Msg::NoError) Throw(); // we are done preparing the response, we reply with an MTI = CISO8583Msg::MTIAuthorizationMessageResponse // and the handling is complete. Msg.Reply(Rsp.MTI(),Rsp.ISOBuffer(),Rsp.ISOBufferSize()); } else { /* we get here if we need some external processing on behalf of this request, so we forward the request with a different message code (so MultiXTpm will not forward it back to us). In this case we just set the msg code to the same MTI but with CISO8583Msg::ISO8583_Private version (in MultiXTpm we must configure this message code !!!). When we forward a request, we do not block for a response, when a response arrives, we get it thru the event "OnDataReplyReceived". in order to be able to associate the response with the original msg, we pass a pointer to the original message in the "Context" parameter of the "Send" function, and when "OnDataReplyReceived" is called, we retrieve this pointer by using "SavedContext()" in the Original msg. Since MultiX automatically destroys messages it forwards to the application, we must call "Keep()" on the received message, to prevent it from being destroyed, in that case, when the reply is received, we need to destroy the original message ourselves. When we play the role of an acquirer gateway, we usualy get the request from the POS terminal, do validation required and maybe modify some data, and then forward the message to the issuer gateway, in our case, the forward will be to a process in our system, that will decide to which issuer to forward to, based on that, will use a specific connection to the spcific issuer gateway. */ if(Owner()->MyAcquirerID().length() == 0) Msg.Reply(ErrUnableToForwardMsg); /* Before we forward the request, we need to set 3 fields to identify the message as a one sent from this process : BMP 11 - STAN,BMP 12 - Times, BMP 32 - Our Acquirer ID When we receive the response from the remote gateway/issuer, we must replace these fields again to the orignal values that the sender set before sending the request to us */ ISOMsg.SetTimes(time(NULL)); ISOMsg.SetSTAN(Owner()->GetNextSTAN()); ISOMsg.SetAcquiringInstitutionIdentificationCode(Owner()->MyAcquirerID()); ISOMsg.ToISO(); if(Send(CISO8583Msg::VersionDependentMTI(CISO8583Msg::ISO8583_Private,Msg.MsgCode()),ISOMsg.ISOBuffer(),ISOMsg.ISOBufferSize(),CMultiXAppMsg::FlagNotifyAll,0,0,&Msg)) Msg.Keep(); else Msg.Reply(ErrUnableToForwardMsg); } }
/*! This function is called when an authorization request is received from an acquirer gateway or from POS terminal. When we receive such a message, we do all syntax checking, and if everything is OK, we might process the request locally or we might forward it to remote Issuer Gateway for authorization. In this implementation we assume local processing, so we reply we with a fixed response, but the logic to forward the request and handling the response is there. */ void CISO8583BackEndServerSession::OnAuthorizationRequest(CMultiXAppMsg &Msg,CISO8583Msg &ISOMsg) { std::string Temp; byte_t ElementIDs[] = {2,3,4,6,7,10,11,12,14,22,23,24,26,32,35,37,41,42,43,49,51,52,53,54,55,59,64}; // We make sure that no extra elements are found in the request CISO8583Msg::TValidationError Error = ISOMsg.EnsureOnlyElementIDs(ElementIDs,sizeof(ElementIDs)); if(Error != CISO8583Msg::NoError) { Msg.Reply(Error); return; } // Check that we have PAN Temp = ISOMsg.PAN(); if(Temp == "") { Msg.Reply(CISO8583Msg::PANIsMissing); return; } // Check that Processing Code exists and is valid. Temp = ISOMsg.ProcessingCode(); if(Temp == "") { Msg.Reply(CISO8583Msg::ProcessingCodeIsMissing); return; } if(!(Temp == "000000" || Temp == "010000" || Temp == "090000")) { Msg.Reply(CISO8583Msg::InvalidProcessingCode); return; } // Check the transaction amount for existance and that it does not exceede some Maximum Temp = ISOMsg.TransactionAmount(); if(Temp == "") { Msg.Reply(CISO8583Msg::TransactionAmountIsMissing); return; } if(CISO8583Utilities::ToInt64(Temp) > 99999999) { Msg.Reply(CISO8583Msg::TransactionAmountAboveMaximum); return; } // check Transaction Currency. Temp = ISOMsg.TransactionCurrencyCode(); if(Temp == "") { Msg.Reply(CISO8583Msg::TransactionCurrencyIsMissing); return; } if(Temp != "978") // Transaction amount is not in Euro { if(ISOMsg.CardholderBillingAmount() == "") { Msg.Reply(CISO8583Msg::CardholderBillingAmountIsMissing); return; } if(ISOMsg.CardholderBillingCurrencyCode() != "978") { Msg.Reply(CISO8583Msg::CardholderBillingCurrencyIsNotEuro); return; } if(ISOMsg.CardholderBillingConversionRate() == "") { Msg.Reply(CISO8583Msg::CardholderBillingConversionRateIsMissing); return; } // Lets validate that the conversion to Euro is done correctly. int64_t TRAmount = CISO8583Utilities::ToInt64(ISOMsg.TransactionAmount()); int64_t BLAmount = CISO8583Utilities::ToInt64(ISOMsg.CardholderBillingAmount()); int64_t CRate = CISO8583Utilities::ToInt64(ISOMsg.CardholderBillingConversionRate()); int64_t Divisor = CISO8583Utilities::PowerOf10((unsigned int)(CRate / 10000000)); TRAmount *= (CRate % 10000000); TRAmount /= Divisor; if(TRAmount != BLAmount) { Msg.Reply(CISO8583Msg::CardholderBillingAmountWrong); return; } TRAmount *= CRate; } else // we must make sure that IDs 6,10,51 are not there { if(!(ISOMsg.CardholderBillingAmount() == "" || ISOMsg.CardholderBillingCurrencyCode() == "" || ISOMsg.CardholderBillingConversionRate() == "")) { Msg.Reply(CISO8583Msg::ExtraElementsFound); return; } } // Check that we got STAN if(ISOMsg.STAN() == "") { Msg.Reply(CISO8583Msg::STANIsMissing); return; } // Check that we got Local Date and Time if(ISOMsg.DateTimeLocal() == "") { Msg.Reply(CISO8583Msg::DateTimeLocalIsMissing); return; } // Check that we got Expiration Date when track 2 is missing if(ISOMsg.ExpirationDate() == "" && ISOMsg.Track2Data() == "") { Msg.Reply(CISO8583Msg::ExpirationDateIsMissing); return; } // Check that we got POS Data Code if(ISOMsg.POSDataCode() == "") { Msg.Reply(CISO8583Msg::POSDataCodeIsMissing); return; } CISO8583ElementValue ICCData = ISOMsg.ICCData(); if(ISOMsg.CardSequenceNumber() == "") { // lets make sure that it is not an ICC transaction and if it is, lets make sure we do not have tag 0x5f34 if(ISOMsg.POSDataCode()[6] == '5') // ICC Transaction { if(ICCData.Size() > 0 && CISO8583Utilities::TLVDataHasTag((const byte_t *)ICCData.RawData(),ICCData.Size(),0x5f34)) { Msg.Reply(CISO8583Msg::CardSequenceNumberIsMissing); return; } } } else { // lets make sure that IT IS an ICC transaction and we have tag 0x5f34 if(!(ISOMsg.POSDataCode()[6] == '5' && ICCData.Size() > 0 && CISO8583Utilities::TLVDataHasTag((const byte_t *)ICCData.RawData(),ICCData.Size(),0x5f34))) { Msg.Reply(CISO8583Msg::ExtraElementsFound); return; } } // Check that we got Function Code if(ISOMsg.FunctionCode() == "") { Msg.Reply(CISO8583Msg::FunctionCodeIsMissing); return; } // Check that we got Card Acceptor Business Code if(ISOMsg.CardAcceptorBusinessCode() == "") { Msg.Reply(CISO8583Msg::CardAcceptorBusinessCodeIsMissing); return; } // Check that we got Acquiring Institution Identification Code if(ISOMsg.AcquiringInstitutionIdentificationCode() == "") { Msg.Reply(CISO8583Msg::AcquiringInstitutionIdentificationCodeIsMissing); return; } // Check that we have Track2 if not E Commerce if(ISOMsg.Track2Data() == "" && ISOMsg.POSDataCode()[7] != 'U') { Msg.Reply(CISO8583Msg::Track2DataIsMissing); return; } // Check that we have Retrieval Reference Number if(ISOMsg.RRN() == "") { Msg.Reply(CISO8583Msg::RRNIsMissing); return; } // check that we have Card Acceptor Terminal Identification if(ISOMsg.CardAcceptorTerminalIdentification().Size() == 0) { Msg.Reply(CISO8583Msg::TerminalIDIsMissing); return; } // check that we have Card Acceptor Identification Code if(ISOMsg.CardAcceptorIdentificationCode().Size() == 0) { Msg.Reply(CISO8583Msg::CardAcceptorIdentificationCodeIsMissing); return; } // check that we have Card Acceptor Name/Location if(ISOMsg.CardAcceptorNameLocation().Size() == 0) { Msg.Reply(CISO8583Msg::CardAcceptorNameLocationIsMissing); return; } // check that we have PIN , if required if((ISOMsg.POSDataCode()[6] == '2' && ISOMsg.POSDataCode()[7] != '5') || // magnetic stripe + PIN (ISOMsg.POSDataCode()[6] == '5' && ISOMsg.POSDataCode()[7] == '1')) // chip + PIN { if(ISOMsg.PIN() == "") { Msg.Reply(CISO8583Msg::PINIsMissing); return; } } else if(ISOMsg.PIN() != "") { Msg.Reply(CISO8583Msg::ExtraElementsFound); return; } // check that we have Security Related Control Information, we do not check the values because we do not have the Keys // in this implementation. if(ISOMsg.SecurityRelatedControlInformation() == "") { Msg.Reply(CISO8583Msg::SecurityRelatedControlInformationIsMissing); return; } // check that we have Additional amounts when processing code is "090000" if(ISOMsg.ProcessingCode() == "090000") { if(ISOMsg.AdditionalAmounts().Size() == 0) { Msg.Reply(CISO8583Msg::AdditionalAmountsIsMissing); return; } } else if(ISOMsg.AdditionalAmounts().Size() != 0) { Msg.Reply(CISO8583Msg::ExtraElementsFound); return; } // check that we have ICC Data for chip transactions if(ISOMsg.POSDataCode()[6] == '5') { if(ICCData.Size() == 0) { Msg.Reply(CISO8583Msg::ICCDataIsMissing); return; } } else if(ICCData.Size() != 0) { Msg.Reply(CISO8583Msg::ExtraElementsFound); return; } // check that we have Additional Private Data when E-Commerce transaction if(ISOMsg.POSDataCode()[7] == 'U') { if(ISOMsg.AdditionalPrivateData().Size() == 0) { Msg.Reply(CISO8583Msg::AdditionalPrivateDataIsMissing); return; } } else if(ISOMsg.AdditionalPrivateData().Size() != 0) { Msg.Reply(CISO8583Msg::ExtraElementsFound); return; } // check that we have MAC, we do not check the values because we do not have the Keys // in this implementation. if(ISOMsg.MAC1() == "") { Msg.Reply(CISO8583Msg::MAC1IsMissing); return; } /* After we finished validating the request, we need to decide if we authorize locally or we forward it to the issuer for authorization */ if(AuthorizeLocally(ISOMsg)) // in this example it is always true { // We create a new ISO Message // we will decline this message CISO8583Msg Rsp; // We will Dup field from the original message and add/ change the ones that are relevant for the response Rsp.SetMTI(ISOMsg.MTI() + 10); // this is a response Rsp.SetPAN(ISOMsg.PAN()); Rsp.SetProcessingCode(ISOMsg.ProcessingCode()); Rsp.SetTransactionAmount(ISOMsg.TransactionAmount()); Rsp.SetCardholderBillingAmount(ISOMsg.CardholderBillingAmount()); Rsp.SetTimes(time(NULL),true); // This will set, transmission times Rsp.SetDateTimeLocal(ISOMsg.DateTimeLocal()); Rsp.SetCardholderBillingConversionRate(ISOMsg.CardholderBillingConversionRate()); Rsp.SetSTAN(ISOMsg.STAN()); Rsp.SetOriginalAmounts(ISOMsg.TransactionAmount()+"000000000000"); // this is adecline response, we include the original amounts Rsp.SetAcquiringInstitutionIdentificationCode(ISOMsg.AcquiringInstitutionIdentificationCode()); Rsp.SetRRN(ISOMsg.RRN()); Rsp.SetActionCode(std::string("909")); // System Mulfunction Rsp.SetCardAcceptorTerminalIdentification(ISOMsg.CardAcceptorTerminalIdentification()); Rsp.SetCardAcceptorIdentificationCode(ISOMsg.CardAcceptorIdentificationCode()); Rsp.SetTransactionCurrencyCode(ISOMsg.TransactionCurrencyCode()); Rsp.SetCardholderBillingCurrencyCode(ISOMsg.CardholderBillingCurrencyCode()); Rsp.SetSecurityRelatedControlInformation(std::string("00000007000001FFFF")); // this is a dummy value, real value should be set based on Keys. Rsp.SetICCData(ISOMsg.ICCData()); // not mandatory Rsp.SetAuthorizingAgentInstitutionIdentificationCode(CISO8583ElementValue("12345678901",CISO8583ElementValue::ISO8583ElementTypeNumeric)); // dummy value Rsp.SetMAC1(std::string("0123456789ABCDEF")); // dummy value, should be based on keys CISO8583Msg::TValidationError Error = Rsp.ToISO(); if(Error != CISO8583Msg::NoError) Throw(); /* we are done preparing the response, we reply with an MTI = CISO8583Msg::MTIAuthorizationMessageResponse and the handling is complete. */ Msg.Reply(Rsp.MTI(),Rsp.ISOBuffer(),Rsp.ISOBufferSize()); } else { /* we get here if we need someone else to authorize the request. In that case we need to forward the request to our Acquirer Gateway Front End. To do that we need to forward it with a different message code, so MultiXTpm will not forward it back to us. The way we do the message code change is by setting the MTI version field to CISO8583Msg::ISO8583_Private. When the message is received by our Gateway process, it will revert back to CISO8583Msg::ISO8583_2_1993 and forward it on. When we forward a request, we do not block for a response, when a response arrives, we get it thru the event "OnDataReplyReceived". in order to be able to associate the response with the original msg, we pass a pointer to the original message in the "Context" parameter of the "Send" function, and when "OnDataReplyReceived" is called, we retrieve this pointer by using "SavedContext()" in the Original msg we sent. Since MultiX automatically destroys messages it forwards to the application, we must call "Keep()" on the received message, to prevent it from being destroyed, in that case, when the reply is received, we need to destroy the original message ourselves. When we play the role of an acquirer gateway, usualy we get the request from the POS terminal, do validation required and maybe modify some data, and then forward the message to the issuer gateway, in our case, the forward will be to a process in our system, that will decide to which issuer to forward to, based on that, will use a specific connection to the spcific issuer gateway. */ /* Before we forward the request, we need to set 3 fields to identify the message as a one sent from this process : BMP 11 - STAN, BMP 12 - Times, BMP 32 - Our Acquirer ID When we receive the response from the remote gateway/issuer, we must replace these fields again to the orignal values that the sender set before sending the request to us */ ISOMsg.SetTimes(time(NULL)); ISOMsg.SetSTAN(Owner()->GetNextSTAN()); ISOMsg.SetAcquiringInstitutionIdentificationCode(Owner()->MyAcquirerID()); ISOMsg.ToISO(); if(Send(CISO8583Msg::VersionDependentMTI(CISO8583Msg::ISO8583_Private,Msg.MsgCode()),ISOMsg.ISOBuffer(),ISOMsg.ISOBufferSize(),CMultiXAppMsg::FlagNotifyAll,0,0,&Msg)) Msg.Keep(); else Msg.Reply(ErrUnableToForwardMsg); } }
//! see CMultiXSession::OnDataReplyReceived void CISO8583AuthorizerServerSession::OnDataReplyReceived(CMultiXAppMsg &ReplyMsg,CMultiXAppMsg &ForwardedMsg) { DebugPrint(3,"Data Reply Received\n"); switch(CISO8583Msg::VersionIndependentMTI(ForwardedMsg.MsgCode())) // this will give us version independent MTI { case CISO8583Msg::MTIAuthorizationMessageRequest : case CISO8583Msg::MTIFinancialMessageRequest : { CMultiXAppMsg *AcquirerMsg = (CMultiXAppMsg *)ForwardedMsg.SavedContext(); if(ReplyMsg.AppDataSize() == 0) { AcquirerMsg->Reply(ReplyMsg.Error()); delete AcquirerMsg; break; } CISO8583Msg AcquirerISO; CISO8583Msg ReplyISO; AcquirerISO.FromISO((const byte_t *)AcquirerMsg->AppData(),AcquirerMsg->AppDataSize()); ReplyISO.FromISO((const byte_t *)ReplyMsg.AppData(),ReplyMsg.AppDataSize()); /* before we send the reply, we need to check if it is MTI 200 and if the response is positive, if so : 1. we debit the issuer account for the sales amount + commission. in this sample we derive the issuer account from the PAN. 2. we credit our own account for these amounts 3. we write transactions log. */ if(CISO8583Msg::VersionIndependentMTI(AcquirerMsg->MsgCode()) == CISO8583Msg::MTIFinancialMessageRequest) { if(CISO8583Utilities::ToInt64(ReplyISO.ActionCode()) == 0) { double Amount = (double)CISO8583Utilities::ToInt64(ReplyISO.TransactionAmount())/100; int IssuerAccount = PANToIssuerAccount(AcquirerISO.PAN()); int MerchantAccount = (int)CISO8583Utilities::ToInt64(AcquirerISO.CardAcceptorIdentificationCode().StringData()); if(!UpdateAuthorizedSale(IssuerAccount,MerchantAccount,AcquirerISO.PAN(),Amount)) { AcquirerMsg->Reply(CISO8583Msg::RequestRejected); // SendReversalToIssuer(ReplyISO); not implemented delete AcquirerMsg; return; } } } else if(CISO8583Msg::VersionIndependentMTI(AcquirerMsg->MsgCode()) == CISO8583Msg::MTIAuthorizationMessageRequest) { int IssuerAccount = PANToIssuerAccount(AcquirerISO.PAN()); double Amount = (double)CISO8583Utilities::ToInt64(AcquirerISO.TransactionAmount())/100; mysqlpp::Transaction Tran(Owner()->DBConn()); mysqlpp::Query Query = Owner()->DBConn().query(); transactions_log Log; Log.Time = TomysqlppDateTime(time(NULL)); Log.AccountNumber = IssuerAccount; Log.CardNumber = AcquirerISO.PAN(); Log.Action = 11; // Query Log.Amount = Amount; Log.NewBalance = 0; Query.insert(Log); Query.execute(); Tran.commit(); } /* We get here when we receive a response for a message we forwarded before. In this case we will forward the response back to the originator, we do not care for the content, we forward it back almost AS IS except for few fields that we need to restore because changed them before we forwarded the message, the fields are: BMP 7 - Set Transmission time BMP 11 - restore sender STAN BMP 12 - Restore sender Date and Time Local BMP 32 - Restore senders Acquiring Institution Identification Code */ /* In order to restore old values, we need to restore the message we received originaly from the acquirer or from the pos terminal. this message is saved in the SaveContext() of the ForwardedMsg. */ ReplyISO.SetTimes(time(NULL),true); ReplyISO.SetSTAN(AcquirerISO.STAN()); ReplyISO.SetDateTimeLocal(AcquirerISO.DateTimeLocal()); ReplyISO.SetAcquiringInstitutionIdentificationCode(AcquirerISO.AcquiringInstitutionIdentificationCode()); ReplyISO.ToISO(); AcquirerMsg->Reply(AcquirerMsg->MsgCode(), ReplyISO.ISOBuffer(), ReplyISO.ISOBufferSize(),0,0,0,0,0,ReplyMsg.Error()); delete AcquirerMsg; // WE MUST DELETE THE MESSAGE BECAUSE WE CALLED "Keep()" BEFORE WE FORWARDED IT. } break; } /* we reply the ReplyMsg for the case that the process that replied to us expects to receive a notification that we received the reply, if it does not wait for the reply, no reply is sent. */ ReplyMsg.Reply(); }
CMultiXLayer::EventHandlerReturn CMultiXWSStream::OnNewRequestMessage(CMultiXEvent *Event) { CMultiXWSStreamEvent *Ev = (CMultiXWSStreamEvent *)Event; // if we have a valid message saved, we reply it with error - this will happen only if we have not replied to it before // we will also delete the message, since we do not need it any more. CMultiXAppMsg *pMsg = m_RequestMsgID.GetObject(); if(pMsg != NULL) { pMsg->Reply(TpmErrMsgCanceled); delete pMsg; } m_RequestMsgID = Ev->m_MsgID; pMsg = m_RequestMsgID.GetObject(); if(pMsg == NULL) return CMultiXLayer::DeleteEvent; bool bExit = false; while(!bExit) // we do a "while" here just so we can fall thru the end of function and delete the message object { bExit = true; if(!pMsg->IsWebServiceCall()) { pMsg->Reply(TpmErrMsgNotSupported); break; } std::string FuncName; std::string DLLName; EXPORTABLE_STL::map<int32_t,std::string>::iterator It = m_MsgCodeToFunctionMap.find(pMsg->MsgCode()); if(It != m_MsgCodeToFunctionMap.end()) { DLLName = It->second.substr(0,It->second.find("|||")); FuncName = It->second.substr(It->second.find("|||")+3); } else { DLLName = pMsg->WSDllFile(); FuncName = pMsg->WSDllFunction(); } try { InitgSoap(DLLName); } catch(...) { pMsg->Reply(WSErrgSoapDllNotFound); break; } TgSoapServiceFunction Func = (TgSoapServiceFunction)GetProcAddress(m_pFunctions->m_pDLLHandle,FuncName.c_str()); if(Func == NULL) { pMsg->Reply(WSErrServiceFunctionNotFound); break; } else if(It == m_MsgCodeToFunctionMap.end()) { m_MsgCodeToFunctionMap[pMsg->MsgCode()] = DLLName + "|||" + pMsg->WSDllFunction(); } ResetData(); if(m_pInBuf) m_pInBuf->ReturnBuffer(); m_pInBuf = pMsg->Owner()->Owner()->AllocateBuffer(pMsg->AppData(),pMsg->AppDataSize()); int Error = Func(gSoap()); if(OutBuf().Length() > 0) { pMsg->Reply(pMsg->MsgCode(),OutBuf()); } else { pMsg->Reply(Error); } } if(pMsg) delete pMsg; m_RequestMsgID.Init(); return CMultiXLayer::DeleteEvent; }