moov-io / iso8583

A golang implementation to marshal and unmarshal iso8583 message.
https://moov.io
Apache License 2.0
353 stars 105 forks source link

example bit55 emv tag #200

Closed Juniornewxt closed 1 year ago

Juniornewxt commented 1 year ago

Hello friends, I was reading the code, but I didn't find a practical way to generate the bit55 ISO, can you help me?

How would it look in the example below?

"9A" = 220911 "9F02" = 000000000001

...... func NewSpec() *iso8583.MessageSpec { return &iso8583.MessageSpec{ Fields: map[int]field.Field{ 0: field.NewString(&field.Spec{ Length: 4, Description: "MTI", Enc: encoding.Binary, Pref: prefix.ASCII.Fixed, }), 1: field.NewBitmap(&field.Spec{ Description: "Bitmap", Enc: encoding.BytesToASCIIHex, Pref: prefix.Hex.Fixed, }), 2: field.NewString(&field.Spec{ Length: 16, Description: "PAN ", Enc: encoding.ASCII, Pref: prefix.ASCII.LL, }), 55: field.NewComposite(&field.Spec{ Length: 999, Description: "ICC Data – EMV Having Multiple Tags", Pref: prefix.ASCII.LLL, Tag: &field.TagSpec{ Enc: encoding.BerTLVTag, Sort: sort.StringsByHex, }, Subfields: map[string]field.Field{ "9A": field.NewString(&field.Spec{ Description: "Transaction Date", Enc: encoding.Binary, Pref: prefix.BerTLV, }), "9F02": field.NewString(&field.Spec{ Description: "Amount, Authorized (Numeric)", Enc: encoding.Binary, Pref: prefix.BerTLV, }), }, }), .............. message.Field(2, "4116090000000000") message.Field(55, "220911000000000001") ............. Tks !

alovak commented 1 year ago

Hey @Juniornewxt , here is how it can work:

spec := &MessageSpec{
    Fields: map[int]field.Field{
        0: field.NewString(&field.Spec{
            Length:      4,
            Description: "Message Type Indicator",
            Enc:         encoding.ASCII,
            Pref:        prefix.ASCII.Fixed,
        }),
        1: field.NewBitmap(&field.Spec{
            Description: "Bitmap",
            Enc:         encoding.Binary,
            Pref:        prefix.ASCII.Fixed,
        }),
        2: field.NewString(&field.Spec{
            Length:      19,
            Description: "Primary Account Number",
            Enc:         encoding.ASCII,
            Pref:        prefix.ASCII.LL,
        }),
        // TLV
        55: field.NewComposite(&field.Spec{
            Length:      999,
            Description: "ICC Data – EMV Having Multiple Tags",
            Pref:        prefix.ASCII.LLL,
            Tag: &field.TagSpec{
                Enc:  encoding.BerTLVTag,
                Sort: sort.StringsByHex,
            },
            Subfields: map[string]field.Field{
                "9A": field.NewString(&field.Spec{
                    Description: "Transaction Date",
                    Enc:         encoding.Binary,
                    Pref:        prefix.BerTLV,
                }),
                "9F02": field.NewString(&field.Spec{
                    Description: "Amount, Authorized (Numeric)",
                    Enc:         encoding.Binary,
                    Pref:        prefix.BerTLV,
                }),
            },
        }),
    },
}

type TestISOF55Data struct {
    F9A   *field.String
    F9F02 *field.String
}

type TestISOData struct {
    F2  *field.String
    F55 *TestISOF55Data
}

message := NewMessage(spec)

err := message.Marshal(&TestISOData{
    F2: field.NewStringValue("4276555555555555"),
    F55: &TestISOF55Data{
        F9A:   field.NewStringValue("210720"),
        F9F02: field.NewStringValue("000000000501"),
    },
})
// handle err

feel free to re-open the ticket if it doesn't work for you or if you have other questions!

Juniornewxt commented 1 year ago

Ok friend, yes, I think I understand, I thought there was another way to do it so I don't have to change a lot of my already done code :) Tks my friend.

alovak commented 1 year ago

You don't have to change a lot of your code, because:

  1. when you message.Marshal - it can partially set fields. So, you don't have to define a type for the whole message. You can only define a struct for field55 and its subfields like this:
type TestISOF55Data struct {
    F9A   *field.String
    F9F02 *field.String
}

// you can define struct with only one field and populate message with its data
type PartialData struct {
    F55 *TestISOF55Data
}

// will update/set only F55 and F9A and F9F02 in the message, the rest of the fields will stay untouched
err := message.Marshal(&PartialData{
    F55: &TestISOF55Data{
        F9A:   field.NewStringValue("210720"),
        F9F02: field.NewStringValue("000000000501"),
    },
})
  1. define spec for EMV, create separate field, pack it and then use packed value as value for the message.BinaryField(55, val)

Here is the code:

    f55Spec  := field.NewComposite(&field.Spec{
        Length:      999,
        Description: "ICC Data – EMV Having Multiple Tags",
        Pref:        prefix.ASCII.LLL,
        Tag: &field.TagSpec{
            Enc:  encoding.BerTLVTag,
            Sort: sort.StringsByHex,
        },
        Subfields: map[string]field.Field{
            "9A": field.NewString(&field.Spec{
                Description: "Transaction Date",
                Enc:         encoding.Binary,
                Pref:        prefix.BerTLV,
            }),
            "9F02": field.NewString(&field.Spec{
                Description: "Amount, Authorized (Numeric)",
                Enc:         encoding.Binary,
                Pref:        prefix.BerTLV,
            }),
        },
    })

        // we can create composite filed like this
    f55 := field.NewComposite(f55Spec)

    type TestISOF55Data struct {
        F9A   *field.String
        F9F02 *field.String
    }

        // and set value only of this field
    err := f55.Marshal(&TestISOF55Data{
        F9A:   field.NewStringValue("210720"),
        F9F02: field.NewStringValue("000000000501"),
    })

    // handle err

        // get binary value of the field
    rawValue, err := f55.Pack()
    // handle err

        // use message method to set field value
    err = message.BinaryField(55, rawValue)
    // handle er
Juniornewxt commented 1 year ago

Now I got it, but I'm testing it for a local flag here that is very similar to Visa, it doesn't seem to me that the bit55 formatted correctly, because I can't find the size of the field in the message, see that it says Visa:

This field 55 VSDC chip data usage contains three subfields after the length subfield. Positions: 1 2–3 4–255 Subfield 1: length Subfield 2: dataset ID Subfield 3: dataset length Subfield 4: Chip Card TLV data elements Tag Length Value Tag Length Value

TLV1    TLVN

Byte 1 Byte 2 Byte 3–4 Byte 5–256 Length Subfield: This is a one-byte binary subfield that contains the number of bytes in this field after the length subfield. Position 1, Dataset ID: This is a one-byte binary identifier given to each dataset. The identifier is hexadecimal 01. Positions 2–3, Dataset Length: This is a 2-byte binary subfield that contains the total length of all TLV elements that follow.

alovak commented 1 year ago

That will take more time for me to check and come up with a code example :(

mfdeveloper508 commented 1 year ago

Created sample code with above spec