001/*
002 * jPOS Project [http://jpos.org]
003 * Copyright (C) 2000-2023 jPOS Software SRL
004 *
005 * This program is free software: you can redistribute it and/or modify
006 * it under the terms of the GNU Affero General Public License as
007 * published by the Free Software Foundation, either version 3 of the
008 * License, or (at your option) any later version.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
013 * GNU Affero General Public License for more details.
014 *
015 * You should have received a copy of the GNU Affero General Public License
016 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
017 */
018
019package org.jpos.core;
020
021import org.jpos.iso.ISOUtil;
022
023import java.util.Objects;
024import java.util.regex.Matcher;
025import java.util.regex.Pattern;
026
027/**
028 * @author [email protected]
029 * @since jPOS 2.0.5
030 *
031 * This class is based on the old 'CardHolder' class and adds support for multiple
032 * PAN and Expiration dates taken from manual entry, track1, track1. It also corrects the name.
033 */
034
035@SuppressWarnings("unused")
036public class Track1 {
037    private String pan;
038    private String nameOnCard;
039    private String exp;
040    private String serviceCode;
041    private String cvv;
042    private String discretionaryData;
043    private String track;
044
045    private Track1 () { }
046
047    public Track1 (Builder builder) {
048        pan                = builder.pan;
049        nameOnCard         = builder.nameOnCard;
050        exp                = builder.exp;
051        cvv                = builder.cvv;
052        discretionaryData  = builder.discretionaryData;
053        serviceCode        = builder.serviceCode;
054        track              = builder.track;
055    }
056
057    public String getPan() {
058        return pan;
059    }
060
061    public String getNameOnCard() {
062        return nameOnCard;
063    }
064
065    public String getExp() {
066        return exp;
067    }
068
069    public String getCvv() {
070        return cvv;
071    }
072
073    public String getServiceCode() {
074        return serviceCode;
075    }
076
077    public String getDiscretionaryData() {
078        return discretionaryData;
079    }
080
081    public String getTrack() {
082
083        return track;
084    }
085
086    @Override
087    public String toString() {
088        return pan != null ? ISOUtil.protect(pan) : "nil";
089    }
090
091    @Override
092    public boolean equals(Object o) {
093        if (this == o) return true;
094        if (o == null || getClass() != o.getClass()) return false;
095        Track1 track11 = (Track1) o;
096        return Objects.equals(pan, track11.pan) &&
097          Objects.equals(nameOnCard, track11.nameOnCard) &&
098          Objects.equals(exp, track11.exp) &&
099          Objects.equals(serviceCode, track11.serviceCode) &&
100          Objects.equals(cvv, track11.cvv) &&
101          Objects.equals(discretionaryData, track11.discretionaryData) &&
102          Objects.equals(track, track11.track);
103    }
104
105    @Override
106    public int hashCode() {
107        return Objects.hash(pan, nameOnCard, exp, serviceCode, cvv, discretionaryData, track);
108    }
109
110    public static Builder builder() {
111        return new Builder();
112    }
113
114    public static class Builder {
115        private static String TRACK1_EXPR = "^[%]?[A-Z]+([0-9]{1,19})\\^([^\\^]{2,26})\\^([0-9]{4})([0-9]{3})([0-9]{4})?([0-9]{1,10})?";
116        private static Pattern TRACK1_PATTERN = Pattern.compile(TRACK1_EXPR);
117        private String pan;
118        private String nameOnCard;
119        private String exp;
120        private String cvv;
121        private String serviceCode;
122        private String discretionaryData;
123        private String track;
124        private Pattern pattern = TRACK1_PATTERN;
125        private Builder () { }
126
127        public Builder pan (String pan) {
128            this.pan = pan; return this;
129        }
130
131        public Builder nameOnCard (String nameOnCard) {
132            this.nameOnCard = nameOnCard;
133            return this;
134        }
135
136        public Builder exp (String exp) {
137            this.exp = exp; return this;
138        }
139
140        public Builder cvv (String cvv) {
141            this.cvv = cvv; return this;
142        }
143
144        public Builder serviceCode (String serviceCode) {
145            this.serviceCode = serviceCode; return this;
146        }
147
148        public Builder discretionaryData (String discretionaryData) {
149            this.discretionaryData = discretionaryData;
150            return this;
151        }
152
153        /**
154         * Optional method, can be used to override default pattern
155         * @param pattern overrides default pattern
156         * @return this builder
157         */
158        public Builder pattern (Pattern pattern) {
159            this.pattern = pattern;
160            return this;
161        }
162
163        public Builder track (String s)
164          throws InvalidCardException
165        {
166            if (s == null)
167                throw new InvalidCardException ("null track1 data");
168
169            track = s;
170            Matcher matcher = pattern.matcher(s);
171            int cnt = matcher.groupCount();
172            if (matcher.find() && cnt >= 2) {
173                pan = matcher.group(1);
174                nameOnCard = matcher.group(2);
175                if (cnt > 2)
176                    exp = matcher.group(3);
177                if (cnt > 3)
178                    serviceCode = matcher.group(4);
179                if (cnt > 4)
180                    cvv = matcher.group(5);
181                if (cnt > 5)
182                    discretionaryData = matcher.group(6);
183            } else {
184                throw new InvalidCardException ("invalid track1");
185            }
186            return this;
187        }
188
189        public Track1 build() {
190            return new Track1(this);
191        }
192    }
193}