Skip to content

Commit

Permalink
make it easier to see EMV tags (#328)
Browse files Browse the repository at this point in the history
* make it easier to see EMV tags

* use field.Hex to avoid ASCIIHexToBytes packing issue (#326)
  • Loading branch information
alovak authored Oct 4, 2024
1 parent b2065f8 commit b29f3de
Show file tree
Hide file tree
Showing 3 changed files with 839 additions and 667 deletions.
131 changes: 131 additions & 0 deletions exp/emv/data.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,3 +131,134 @@ type Data struct {
UnpredictableNumber *field.String `index:"9F37"`
UpperConsecutiveOfflineLimit *field.String `index:"9F23"`
}

// genearte struct with native go types
type NativeData struct {
AcquirerIdentifier string `index:"9F01"`
AdditionalTerminalCapabilities string `index:"9F40"`
AmountAuthorisedBinary string `index:"81"`
AmountAuthorisedNumeric int64 `index:"9F02"`
AmountOtherBinary string `index:"9F04"`
AmountOtherNumeric int64 `index:"9F03"`
AmountReferenceCurrency string `index:"9F3A"`
ApplicationCryptogram string `index:"9F26"`
ApplicationCurrencyCode string `index:"9F42"`
ApplicationCurrencyExponent string `index:"9F44"`
ApplicationDiscretionaryData string `index:"9F05"`
ApplicationEffectiveDate string `index:"5F25"`
ApplicationExpirationDate string `index:"5F24"`
ApplicationFileLocatorAFL string `index:"94"`
ApplicationIdentifierAIDcard string `index:"4F"`
ApplicationIdentifierAIDterminal string `index:"9F06"`
ApplicationInterchangeProfile string `index:"82"`
ApplicationLabel string `index:"50"`
ApplicationPreferredName string `index:"9F12"`
ApplicationPrimaryAccountNumberPAN string `index:"5A"`
ApplicationPrimaryAccountNumberPANSequenceNumber string `index:"5F34"`
ApplicationPriorityIndicator string `index:"87"`
ApplicationReferenceCurrency string `index:"9F3B"`
ApplicationReferenceCurrencyExponent string `index:"9F43"`
ApplicationSelectionRegisteredProprietaryData string `index:"9F0A"`
ApplicationTemplate string `index:"61"`
ApplicationTransactionCounter int64 `index:"9F36"`
ApplicationUsageControl string `index:"9F07"`
ApplicationVersionNumber string `index:"9F08"`
ApplicationVersionNumberTerminal string `index:"9F09"`
AuthorisationCode string `index:"89"`
AuthorisationResponseCode string `index:"8A"`
BankIdentifierCodeBIC string `index:"5F54"`
CardBITGroupTemplate string `index:"9F31"`
CardRiskManagementDataObjectList1CDOL1 string `index:"8C"`
CardRiskManagementDataObjectList2CDOL2 string `index:"8D"`
CardholderName string `index:"5F20"`
CardholderNameExtended string `index:"9F0B"`
CardholderVerificationMethodCVMList string `index:"8E"`
CardholderVerificationMethodCVMResults string `index:"9F34"`
CertificationAuthorityPublicKeyIndex string `index:"8F"`
CertificationAuthorityPublicKeyIndexTerminal string `index:"9F22"`
CommandTemplate string `index:"83"`
CryptogramInformationData string `index:"9F27"`
DataAuthenticationCode string `index:"9F45"`
DedicatedFileDFName string `index:"84"`
DirectoryDefinitionFileDDFName string `index:"9D"`
DirectoryDiscretionaryTemplate string `index:"73"`
DynamicDataAuthenticationDataObjectListDDOL string `index:"9F49"`
EMVProprietaryTemplate string `index:"70"`
FacialTryCounter string `index:"DF50"`
FileControlInformationFCIIssuerDiscretionaryData string `index:"BF0C"`
FileControlInformationFCIProprietaryTemplate string `index:"A5"`
FileControlInformationFCITemplate string `index:"6F"`
FingerTryCounter string `index:"DF51"`
ICCDynamicNumber string `index:"9F4C"`
IntegratedCircuitCardICCPINEnciphermentPublicKeyCertificate string `index:"9F2D"`
IntegratedCircuitCardICCPINEnciphermentPublicKeyExponent string `index:"9F2E"`
IntegratedCircuitCardICCPINEnciphermentPublicKeyRemainder string `index:"9F2F"`
IntegratedCircuitCardICCPublicKeyCertificate string `index:"9F46"`
IntegratedCircuitCardICCPublicKeyExponent string `index:"9F47"`
IntegratedCircuitCardICCPublicKeyRemainder string `index:"9F48"`
InterfaceDeviceIFDSerialNumber string `index:"9F1E"`
InternationalBankAccountNumberIBAN string `index:"5F53"`
IssuerActionCodeDefault string `index:"9F0D"`
IssuerActionCodeDenial string `index:"9F0E"`
IssuerActionCodeOnline string `index:"9F0F"`
IssuerApplicationData string `index:"9F10"`
IssuerAuthenticationData string `index:"91"`
IssuerCodeTableIndex string `index:"9F11"`
IssuerCountryCode string `index:"5F28"`
IssuerCountryCodealpha2format string `index:"5F55"`
IssuerCountryCodealpha3format string `index:"5F56"`
IssuerIdentificationNumberIIN string `index:"42"`
IssuerIdentificationNumberExtended string `index:"9F0C"`
IssuerPublicKeyCertificate string `index:"90"`
IssuerPublicKeyExponent string `index:"9F32"`
IssuerPublicKeyRemainder string `index:"92"`
IssuerScriptCommand string `index:"86"`
IssuerScriptIdentifier string `index:"9F18"`
IssuerScriptTemplate1 string `index:"71"`
IssuerScriptTemplate2 string `index:"72"`
IssuerURL string `index:"5F50"`
LanguagePreference string `index:"5F2D"`
LastOnlineApplicationTransactionCounterATCRegister string `index:"9F13"`
LogEntry string `index:"9F4D"`
LogFormat string `index:"9F4F"`
LowerConsecutiveOfflineLimit string `index:"9F14"`
MerchantCategoryCode string `index:"9F15"`
MerchantIdentifier string `index:"9F16"`
MerchantNameandLocation string `index:"9F4E"`
PaymentAccountReferencePAR string `index:"9F24"`
PersonalIdentificationNumberPINTryCounter string `index:"9F17"`
PointofServicePOSEntryMode string `index:"9F39"`
ProcessingOptionsDataObjectListPDOL string `index:"9F38"`
ResponseMessageTemplateFormat1 string `index:"80"`
ResponseMessageTemplateFormat2 string `index:"77"`
ServiceCode string `index:"5F30"`
ShortFileIdentifierSFI string `index:"88"`
SignedDynamicApplicationData string `index:"9F4B"`
SignedStaticApplicationData string `index:"93"`
StaticDataAuthenticationTagList string `index:"9F4A"`
TerminalCapabilities string `index:"9F33"`
TerminalCountryCode string `index:"9F1A"`
TerminalFloorLimit string `index:"9F1B"`
TerminalIdentification string `index:"9F1C"`
TerminalRiskManagementData string `index:"9F1D"`
TerminalType string `index:"9F35"`
TerminalVerificationResults string `index:"95"`
TokenRequestorID string `index:"9F19"`
Track1DiscretionaryData string `index:"9F1F"`
Track2DiscretionaryData string `index:"9F20"`
Track2EquivalentData string `index:"57"`
TransactionCertificateTCHashValue string `index:"98"`
TransactionCertificateDataObjectListTDOL string `index:"97"`
TransactionCurrencyCode string `index:"5F2A"`
TransactionCurrencyExponent string `index:"5F36"`
TransactionDate string `index:"9A"`
TransactionPersonalIdentificationNumberPINData string `index:"99"`
TransactionReferenceCurrencyCode string `index:"9F3C"`
TransactionReferenceCurrencyExponent string `index:"9F3D"`
TransactionSequenceCounter string `index:"9F41"`
TransactionStatusInformation string `index:"9B"`
TransactionTime string `index:"9F21"`
TransactionType string `index:"9C"`
UnpredictableNumber string `index:"9F37"`
UpperConsecutiveOfflineLimit string `index:"9F23"`
}
57 changes: 36 additions & 21 deletions exp/emv/emv_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,50 @@ package emv

import (
"encoding/hex"
"fmt"
"os"
"testing"

"github.com/moov-io/iso8583/field"
"github.com/moov-io/iso8583"
"github.com/stretchr/testify/require"
)

func TestEmv(t *testing.T) {
exampleICCData := `9F0206000000006300820258009F360200029F2608B9B2B58202D37033840FA000000152301010000100000000009F100801050000000000009F3303E0F0C09F1A020840950500000000009A031711209C01005F2A0208409F370459F58EB1`
rawData, err := hex.DecodeString(exampleICCData)
iccData := `4f07a00000000410105f2a0208405f2d047275656e82021980950500000000009a032410029c01009f02060000000001009f03060000000000009f0607a00000000410109f090200029f1a0208409f1e0834543734353038359f21030909329f33030000e89f34030000009f3501229f360200539f370409bc21069f3901919f4104000000069f530100df81290830f0f00030f0ff00dfee2601d1dfef4c06002700000000dfef4d28fd4b4f392e1278361252d85649e405b430b9cb5e57d5211ba81d00242dbbd987ffe099fbf92b422eff810581ac500a4d6173746572436172648407a00000000410109f6d02000156a1292a353438392a2a2a2a2a2a2a2a313433375e202f5e323330333230312a2a2a2a2a2a2a2a2a2a2a2a2a56c1307004c1af307c59842b4edd3e745d27e783f4fefefb36698568ee921be4b2d6ae066b1a8cac8aba5c29453da922bc7ed89f6ba1135489cccccccc1437d2303201cccccccccccccc9f6bc118c5e921e707dabbfc762d47986509668fd22f9b3dbd10cbd6ff81063cdf812ac11870b80a15602160f54503ce3f5d9116997adb85fcfcd5388edf812bc110e912f0f4c0d82d1075ccc984f9864127df8115060000000000ffffee012cdf300100df31c110ab6c422bbfb95378504cbc6683306641df32c11081592240b4e352d17af39b923ad115caffee120acdcdcd0701453de0000d`
rawData, err := hex.DecodeString(iccData)
require.NoError(t, err)

// we have to add LLL length before rawData to unpack it
lenPrefix := fmt.Sprintf("%03d", len(rawData))
rawData = append([]byte(lenPrefix), rawData...)

emvField := field.NewComposite(Spec)
_, err = emvField.Unpack(rawData)

require.NoError(t, err)

data := &Data{}

err = emvField.Unmarshal(data)
msg := iso8583.NewMessage(MessageSpec)
msg.MTI("0100")
msg.BinaryField(55, rawData)

// this will print the all EMV tags in readable format
iso8583.Describe(msg, os.Stdout)

// like this (note, that first F is not part of the tag, it's just a Filed prefix):
// F55 ICC Data SUBFIELDS:
// -------------------------------------------
// F4F Application Identifier (AID) – card............: A0000000041010
// F5F2A Transaction Currency Code.....................: 0840
// F5F2D Language Preference...........................: 7275656E
// F82 Application Interchange Profile................: 1980
// F95 Terminal Verification Results..................: 0000000000
// F9A Transaction Date...............................: 241002
// F9C Transaction Type...............................: 00
// F9F02 Amount, Authorised (Numeric)..................: 100

// now we can extract values we can use
iccField := msg.GetField(55)
data := &NativeData{}
iccField.Unmarshal(data)

// test our spec and that we can pack and unpack the same
// without errors
packed, _ := msg.Pack()
msg2 := iso8583.NewMessage(MessageSpec)

err = msg2.Unpack(packed)
require.NoError(t, err)

require.Equal(t, int64(6300), data.AmountAuthorisedNumeric.Value())
require.Equal(t, "5800", data.ApplicationInterchangeProfile.Value())
require.Equal(t, int64(2), data.ApplicationTransactionCounter.Value())
require.Equal(t, "B9B2B58202D37033", data.ApplicationCryptogram.Value())

packed2, err := msg2.Pack()
require.Equal(t, packed, packed2)
}
Loading

0 comments on commit b29f3de

Please sign in to comment.