File Coverage

blib/lib/Data/FormValidator/Constraints/CreditCard.pm
Criterion Covered Total %
statement 66 66 100.0
branch 28 28 100.0
condition 4 6 66.6
subroutine 19 19 100.0
pod 5 5 100.0
total 122 124 98.3


line stmt bran cond sub pod time code
1             package Data::FormValidator::Constraints::CreditCard;
2              
3             ###############################################################################
4             # Required inclusions.
5             ###############################################################################
6 1     1   116547 use strict;
  1         15  
  1         38  
7 1     1   7 use warnings;
  1         2  
  1         32  
8 1     1   521 use Business::CreditCard qw();
  1         2153  
  1         47  
9              
10             ###############################################################################
11             # Version number.
12             ###############################################################################
13             our $VERSION = '0.03';
14              
15             ###############################################################################
16             # Allow our methods to be exported.
17             ###############################################################################
18 1     1   8 use Exporter;
  1         2  
  1         54  
19 1     1   7 use base qw( Exporter );
  1         3  
  1         138  
20 1     1   7 use vars qw( @EXPORT_OK %EXPORT_TAGS );
  1         3  
  1         697  
21             @EXPORT_OK = qw(
22             FV_cc_number
23             FV_cc_type
24             FV_cc_expiry
25             FV_cc_expiry_month
26             FV_cc_expiry_year
27             );
28             %EXPORT_TAGS = (
29             'all' => [@EXPORT_OK],
30             );
31              
32             ###############################################################################
33             # Subroutine: FV_cc_number()
34             ###############################################################################
35             # Creates a constraint closure that returns true if the constrained value
36             # appears to be a valid credit card number.
37             #
38             # NOTE: "appears to be a valid credit card number" ONLY means that the number
39             # appears to be valid and has passed the checksum test; -NO- tests have been
40             # performed to verify that this is actually a real/valid credit card number.
41             ###############################################################################
42             sub FV_cc_number {
43             return sub {
44 4     4   1167 my $dfv = shift;
45 4         12 my $val = $dfv->get_current_constraint_value();
46 4         27 return Business::CreditCard::validate($val);
47 4     4 1 133 };
48             }
49              
50             ###############################################################################
51             # Subroutine: FV_cc_type(@set)
52             ###############################################################################
53             # Creates a constraint closure that returns true if the constrained value
54             # appears to be a credit card of one of the types listed in the given '@set'.
55             # The '@set' can be provided as either a list of scalars (which are compared
56             # using the 'eq' operator), or as a list of regular expressions.
57             #
58             # For more information on the actual card types that can be checked for, please
59             # refer to the information for the 'cardtype()' method in
60             # 'Business::CreditCard'.
61             ###############################################################################
62             sub FV_cc_type {
63 5     5 1 1566 my (@set) = @_;
64             return sub {
65 5     5   950 my $dfv = shift;
66 5         15 my $val = $dfv->get_current_constraint_value();
67 5         30 my $type = Business::CreditCard::cardtype($val);
68 5         128 foreach my $elem (@set) {
69 7 100       20 if (ref($elem) eq 'Regexp') {
70 3 100       20 return 1 if ($type =~ $elem);
71             }
72             else {
73 4 100       14 return 1 if ($type eq $elem);
74             }
75             }
76 1         4 return;
77             }
78 5         48 }
79              
80             ###############################################################################
81             # Subroutine: FV_cc_expiry()
82             ###############################################################################
83             # Creates a constraint closure that returns true if the constrained value
84             # appears to be a valid credit card expiry date; correct integer values for
85             # year/month, with the date not being in the past.
86             #
87             # Accepted formats include "MM/YY" and "MM/YYYY".
88             #
89             # NOTE: use of this method requires that the full credit card expiry date be
90             # present in a single field; no facilities are provided for gathering the
91             # month/year data from two separate fields.
92             ###############################################################################
93             sub FV_cc_expiry {
94             return sub {
95 14     14   2704 my $dfv = shift;
96 14         42 my $val = $dfv->get_current_constraint_value();
97 14         90 my ($month, $year) = split('/', $val);
98 14 100 66     71 return if ((!defined $month) or (!defined $year));
99             # verify each field individually
100 13 100       31 return if (!_match_cc_expiry_month($month));
101 7 100       19 return if (!_match_cc_expiry_year($year));
102             # verify that date is not in the past
103 3         48 my @now = localtime();
104 3         11 $year = _windowize_year($year);
105 3 100 66     19 return if ($year == ($now[5]+1900) and $month <= ($now[4]+1));
106             # looks good!
107 2         11 return "$month/$year";
108             }
109 15     15 1 1768 }
110              
111             sub _windowize_year {
112 12     12   21 my $year = shift;
113 12 100       35 if ($year < 1900) {
114 5 100       17 $year += ($year < 70) ? 2000 : 1900;
115             }
116 12         28 return $year;
117             }
118              
119             sub _match_cc_expiry_month {
120 24     24   48 my $val = shift;
121 24 100       78 return if ($val =~ /\D/); # only contain numerics
122 21 100       62 return if ($val < 1); # can't be <1
123 17 100       42 return if ($val > 12); # can't be >12
124 13         37 return $val;
125             }
126              
127             sub _match_cc_expiry_year {
128 13     13   22 my $val = shift;
129 13         313 my $now = (localtime)[5] + 1900;
130 13 100       85 return if ($val =~ /\D/); # only contain numerics
131 9         31 $val = _windowize_year($val);
132 9 100       53 return if ($val < $now); # can't be before this year
133 5         14 return $val;
134             }
135              
136             ###############################################################################
137             # Subroutine: FV_cc_expiry_month()
138             ###############################################################################
139             # Creates a constraint closure that returns true if the constrained value
140             # appears to be a valid credit card expiry month; an integer in the range of
141             # "1-12".
142             ###############################################################################
143             sub FV_cc_expiry_month {
144             return sub {
145 11     11   2022 my $dfv = shift;
146 11         29 my $val = $dfv->get_current_constraint_value();
147 11         60 return _match_cc_expiry_month($val);
148             }
149 12     12 1 4744 }
150              
151             ###############################################################################
152             # Subroutine: FV_cc_expiry_year()
153             ###############################################################################
154             # Creates a constraint closure that returns true if the constrained value
155             # appears to be a valid credit card expiry year; an integer value for a year,
156             # not in the past.
157             #
158             # Expiry years can be provided as either "YY" or "YYYY". When using the
159             # two-digit "YY" format, the year is considered to be part of the sliding
160             # window 1970-2069.
161             ###############################################################################
162             sub FV_cc_expiry_year {
163             return sub {
164 6     6   1127 my $dfv = shift;
165 6         17 my $val = $dfv->get_current_constraint_value();
166 6         37 return _match_cc_expiry_year($val);
167             }
168 6     6 1 3693 }
169              
170             1;
171              
172             =for stopwords MM YY YYYY checksum
173              
174             =head1 NAME
175              
176             Data::FormValidator::Constraints::CreditCard - Data constraints, using Business::CreditCard
177              
178             =head1 SYNOPSIS
179              
180             use Data::FormValidator::Constraints::CreditCard qw(:all);
181              
182             constraint_methods => {
183             cc_number => [
184             # number is syntactically valid
185             FV_cc_number(),
186              
187             # verify type, by value
188             FV_cc_type(qw(Visa MasterCard)),
189              
190             # verify type, by regex
191             FV_cc_type(qr/visa|mastercard/i),
192             ],
193              
194             # expiry month is within valid range
195             cc_exp_mon => FV_cc_expiry_month(),
196              
197             # expiry year is not in the past
198             cc_exp_year => FV_cc_expiry_year(),
199              
200             # full expiry date is not in the past
201             cc_expiry => FV_cc_expiry(),
202             },
203              
204             =head1 DESCRIPTION
205              
206             C provides several methods that
207             can be used to generate constraint closures for use with C
208             for the purpose of validating credit card numbers and expiry dates, using
209             C.
210              
211             =head1 METHODS
212              
213             =over
214              
215             =item FV_cc_number()
216              
217             Creates a constraint closure that returns true if the constrained value
218             appears to be a valid credit card number.
219              
220             NOTE: "appears to be a valid credit card number" ONLY means that the number
221             appears to be valid and has passed the checksum test; -NO- tests have been
222             performed to verify that this is actually a real/valid credit card number.
223              
224             =item FV_cc_type(@set)
225              
226             Creates a constraint closure that returns true if the constrained value
227             appears to be a credit card of one of the types listed in the given
228             C<@set>. The C<@set> can be provided as either a list of scalars (which are
229             compared using the C operator), or as a list of regular expressions.
230              
231             For more information on the actual card types that can be checked for,
232             please refer to the information for the C method in
233             C.
234              
235             =item FV_cc_expiry()
236              
237             Creates a constraint closure that returns true if the constrained value
238             appears to be a valid credit card expiry date; correct integer values for
239             year/month, with the date not being in the past.
240              
241             Accepted formats include "MM/YY" and "MM/YYYY".
242              
243             NOTE: use of this method requires that the full credit card expiry date be
244             present in a single field; no facilities are provided for gathering the
245             month/year data from two separate fields.
246              
247             =item FV_cc_expiry_month()
248              
249             Creates a constraint closure that returns true if the constrained value
250             appears to be a valid credit card expiry month; an integer in the range of
251             "1-12".
252              
253             =item FV_cc_expiry_year()
254              
255             Creates a constraint closure that returns true if the constrained value
256             appears to be a valid credit card expiry year; an integer value for a year,
257             not in the past.
258              
259             Expiry years can be provided as either "YY" or "YYYY". When using the
260             two-digit "YY" format, the year is considered to be part of the sliding
261             window 1970-2069.
262              
263             =back
264              
265             =head1 AUTHOR
266              
267             Graham TerMarsch (cpan@howlingfrog.com)
268              
269             =head1 COPYRIGHT
270              
271             Copyright (C) 2008, Graham TerMarsch. All Rights Reserved.
272              
273             This is free software; you can redistribute it and/or modify it under the same
274             license as Perl itself.
275              
276             =head1 SEE ALSO
277              
278             L,
279             L.
280              
281             =cut