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}