recurring_billing parameter
[Business-BatchPayment.git] / BatchPayment / Item.pm
1 package Business::BatchPayment::Item;
2
3 use strict;
4 use Moose;
5 use Moose::Util::TypeConstraints;
6 use MooseX::UndefTolerant;
7 use DateTime;
8
9 =head1 NAME
10
11 Business::BatchPayment::Item
12
13 =head1 DESCRIPTION
14
15 A Business::BatchPayment::Item represents a single payment request or 
16 reply (approval or rejection).  When submitting a batch, the merchant 
17 system constructs B::BP::Item objects for each attempted payment in 
18 the batch.  Results downloaded from the gateway are returned as a 
19 list of Items with the 'approved' field set to a true or false value. 
20
21 =head1 REQUIRED ATTRIBUTES
22
23 =over 4
24
25 =item action
26
27 "payment" or "credit".  Most processors support only "payment".
28 "payment" is defined as "money transfer FROM the account identified in the 
29 Item TO the account identified by the Processor object's login settings."
30 "credit" is the other direction.
31
32 =cut
33
34 enum 'Action' => [qw(payment credit)];
35 coerce 'Action', from 'Str', via { lc $_ };
36 has action => (
37   is  => 'rw',
38   isa => 'Action',
39   default => 'payment',
40   required => 1,
41   coerce => 1,
42 );
43
44 =item payment_type
45
46 "CC" or "ECHECK".  Most processors will only support one or the other, 
47 and if set on the Processor object, this is not required.
48
49 =cut
50
51 # are we okay with these names?
52 enum 'PaymentType' => [qw( CC ECHECK )];
53 has payment_type => ( is  => 'rw', isa => 'PaymentType' );
54
55 =item amount
56
57 the amount, as a decimal number.  Required only in request
58 items.
59
60 =cut
61
62 # perhaps we should apply roles that distinguish request and reply items?
63 # they have different required fields.
64 has amount => (
65   is  => 'rw',
66   isa => 'Num',
67 );
68
69 =item tid
70
71 transaction identifier.  Requests must provide this.  It's a token of 
72 some kind to be passed to the gateway and used to identify the reply.  
73 For now it's required to be an integer.  An invoice number would be 
74 a good choice.
75
76 =cut
77
78 has tid => ( is  => 'rw', isa => 'Int' );
79
80 =back
81
82 =head1 OPTIONAL ATTRIBUTES
83
84 =head2 Customer Information
85
86 =over 4
87
88 =item customer_id
89
90 A customer number or other identifier, for the merchant's use.
91
92 =item first_name
93
94 First name.
95
96 =item last_name
97
98 Last name.
99
100 =item company
101
102 Company name.
103
104 =item address, address2, city, state, country, zip
105
106 Billing address fields.  Credit card processors may use these (especially
107 zip) for authentication.
108
109 =item phone
110
111 Customer phone number.
112
113 =cut
114
115 has [ qw(
116   customer_id
117   first_name
118   last_name
119   company
120   address
121   address2
122   city
123   state
124   country
125   zip
126   phone
127 ) ] => ( is => 'rw', isa => 'Str', default => '' );
128
129 =back
130
131 =head2 Transaction Information
132
133 =over 4
134
135 =item process_date
136
137 The date requested for processing.  This is meaningful only if the 
138 processor allows different processing dates for items in the same 
139 batch.
140
141 =item invoice_number
142
143 An invoice number, for your use.
144
145 =item recurring_billing
146
147 A flag indicating whether this is a "recurring" transaction. Different
148 processors interpret this differently, but the interface defines three kinds
149 of payments:
150
151 N: non-recurring; the customer is not expected to make another payment using
152 the same card or bank account.
153
154 F: first use; the customer has never used this card/account before but is 
155 expected to use it again.
156
157 S: subsequent; the customer has used the card/account in the past.
158
159 =cut
160
161 class_type 'DateTime';
162 coerce 'DateTime', from 'Int', via { DateTime->from_epoch($_) };
163 has process_date    => ( is => 'rw', isa => 'DateTime', coerce => 1 );
164
165 has invoice_number  => ( is => 'rw', isa => 'Str' );
166
167 enum 'Recurring_Flag' => [ 'N', 'F', 'S' ];
168 has recurring_billing => ( is => 'rw', isa => 'Recurring_Flag' );
169
170 =back
171
172 =head2 Bank Transfer / ACH / EFT
173
174 =over 4
175
176 =item account_number
177
178 Bank account number.
179
180 =item routing_code
181
182 Bank's routing code.
183
184 =item account_type
185
186 Can be 'personal checking', 'personal savings', 'business checking', 
187 or 'business savings'.
188
189 =cut
190
191 enum 'Account_Type' => [
192   'personal checking',
193   'personal savings',
194   'business checking',
195   'business savings',
196 ];
197 coerce 'Account_Type', from 'Str', via { lc $_ };
198
199 has account_number  => ( is => 'rw', isa => 'Str' );
200 has routing_code    => ( is => 'rw', isa => 'Str' );
201 has account_type    => ( is => 'rw', isa => 'Account_Type', coerce => 1 );
202
203 =back
204
205 =head2 Credit Card
206
207 =over 4
208
209 =item card_number
210
211 Credit card number.
212
213 =item expiration
214
215 Credit card expiration, MMYY format.
216
217 =cut
218
219 has card_number     => ( is => 'rw', isa => 'Str' );
220 has ['expiration_month', 'expiration_year'] => ( is => 'rw', isa => 'Int' );
221
222 sub expiration {
223   # gets/sets expiration_month and _year in MMYY format
224   my $self = shift;
225   my $arg = shift;
226   if ( $arg ) {
227     # well, we said it's in MMYY format
228     my ($m, $y) = _parse_expiration($arg);
229     $self->expiration_month($m);
230     $self->expiration_year($y);
231   }
232   return sprintf('%02d/%02d',
233     $self->expiration_month,
234     $self->expiration_year % 2000);
235 }
236
237 sub _parse_expiration {
238   my $arg = shift;
239   if ( $arg =~ /^(\d\d)(\d\d)$/ ) {
240     return ($1, 2000 + $2);
241   } elsif ( $arg =~ /^(\d\d?)\W(\d\d)$/ ) {
242     return ($1, 2000 + $2);
243   } elsif ( $arg =~ /^(\d\d?)\W(\d\d\d\d)$/ ) {
244     return ($1, $2);
245   } elsif ( $arg =~ /^(\d\d?)\W\d\d?\W(\d\d\d\d)$/) {
246     return ($1, $3);
247   } else {
248     die "can't parse expiration date '$arg'";
249   }
250 }
251
252 sub payinfo {
253   # gets/sets either the card number, or the account number + routing code
254   # depending on the payment type
255   my $self = shift;
256   if ( $self->payment_type eq 'CC' ) {
257     $self->card_number(@_);
258   } elsif ( $self->payment_type eq 'ECHECK' ) {
259     my $arg = shift;
260     if ( $arg ) {
261       $arg =~ /^(\d+)@(\d+)$/ or die "Validation failed for payinfo";
262       $self->account_number($1);
263       $self->routing_code($2);
264     }
265     return ($self->account_number . '@' . $self->routing_code);
266   }
267 }
268
269 =back
270
271 =head2 Tokenized Payment
272
273 =over 4
274
275 =item pay_by_token
276
277 If your gateway supports it, this may be 
278 provided instead of card_number/account_number.  See also 
279 C<assigned_token> below.
280
281 =cut
282
283 has pay_by_token    => ( is => 'rw', isa => 'Str' );
284
285 =back
286
287 =head1 REPLY ATTRIBUTES
288
289 =over 4
290
291 =item approved 
292
293 Boolean field for whether the item was approved.  This 
294 will always be set on replies.
295
296 =item payment_date 
297
298 The date the payment was processed, as a DateTime
299 object.
300
301 =item order_number 
302
303 The transaction identifier returned by the gateway
304 (not to be confused with 'tid', which is a transaction identifier assigned
305 by the merchant system).  This is usually the identifier for performing 
306 other operations on the transaction, like voiding or refunding it.
307
308 =item authorization
309
310 The authorization code, probably only meaningful for credit cards.  
311 Should be undef (or not present) if the transaction wasn't approved.
312
313 =item check_number
314
315 The check number, probably only meaningful if this transaction was
316 processed from a paper check.
317
318 =item assigned_token
319
320 In tokenized systems which store the customer's account number or 
321 credit card for future transactions, this is the token assigned to 
322 identify that account.  Pass it as 'pay_by_token' to use that payment 
323 account again.
324
325 =item error_message
326
327 The message returned by the gateway.  This may contain a value even 
328 if the payment was successful (use C<approved> to determine that.)
329
330 =item failure_status
331
332 A normalized failure status, from the following list:
333
334 =over 4
335
336 =item expired
337
338 =item nsf (non-sufficient funds / credit limit)
339
340 =item stolen
341
342 =item pickup
343
344 =item blacklisted
345
346 =item inactive
347
348 =item decline (other card/transaction declines)
349
350 =back
351
352 =back
353
354 =cut
355
356 has approved        => ( is => 'rw', isa => 'Maybe[Bool]' );
357
358 has payment_date    => ( is => 'rw', isa => 'DateTime' );
359
360 has [qw( 
361   authorization
362   error_message
363   order_number
364   assigned_token
365 )] => ( is => 'rw', isa => 'Str');
366
367 enum FailureStatus => [ qw(
368   expired
369   nsf
370   stolen
371   pickup
372   blacklisted
373   inactive
374   decline
375 ) ];
376 has failure_status  => ( is => 'rw', isa => 'Maybe[FailureStatus]' );
377
378 has check_number => ( is => 'rw', isa => 'Int' );
379
380 around 'BUILDARGS' => sub {
381   my ($orig, $self, %args) = @_;
382   if ( $args{expiration} ) {
383     @args{'expiration_month', 'expiration_year'} =
384       _parse_expiration($args{expiration}); 
385   }
386   $self->$orig(%args);
387 };
388
389 __PACKAGE__->meta->make_immutable;
390
391 1;