1 package Business::BatchPayment::Item;
5 use Moose::Util::TypeConstraints;
6 use MooseX::UndefTolerant;
11 Business::BatchPayment::Item
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.
21 =head1 REQUIRED ATTRIBUTES
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.
34 enum 'Action' => [qw(payment credit)];
35 coerce 'Action', from 'Str', via { lc $_ };
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.
51 # are we okay with these names?
52 enum 'PaymentType' => [qw( CC ECHECK )];
53 has payment_type => ( is => 'rw', isa => 'PaymentType' );
57 the amount, as a decimal number. Required only in request
62 # perhaps we should apply roles that distinguish request and reply items?
63 # they have different required fields.
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
78 has tid => ( is => 'rw', isa => 'Int' );
82 =head1 OPTIONAL ATTRIBUTES
84 =head2 Customer Information
90 A customer number or other identifier, for the merchant's use.
104 =item address, address2, city, state, country, zip
106 Billing address fields. Credit card processors may use these (especially
107 zip) for authentication.
111 Customer phone number.
127 ) ] => ( is => 'rw', isa => 'Str', default => '' );
131 =head2 Transaction Information
137 The date requested for processing. This is meaningful only if the
138 processor allows different processing dates for items in the same
143 An invoice number, for your use.
147 class_type 'DateTime';
148 coerce 'DateTime', from 'Int', via { DateTime->from_epoch($_) };
149 has process_date => ( is => 'rw', isa => 'DateTime', coerce => 1 );
151 has invoice_number => ( is => 'rw', isa => 'Str' );
155 =head2 Bank Transfer / ACH / EFT
169 Can be 'personal checking', 'personal savings', 'business checking',
170 or 'business savings'.
174 enum 'Account_Type' => [
180 coerce 'Account_Type', from 'Str', via { lc $_ };
182 has account_number => ( is => 'rw', isa => 'Str' );
183 has routing_code => ( is => 'rw', isa => 'Str' );
184 has account_type => ( is => 'rw', isa => 'Account_Type', coerce => 1 );
198 Credit card expiration, MMYY format.
202 has card_number => ( is => 'rw', isa => 'Str' );
203 has ['expiration_month', 'expiration_year'] => ( is => 'rw', isa => 'Int' );
206 # gets/sets expiration_month and _year in MMYY format
210 # well, we said it's in MMYY format
211 my ($m, $y) = _parse_expiration($arg);
212 $self->expiration_month($m);
213 $self->expiration_year($y);
215 return sprintf('%02d/%02d',
216 $self->expiration_month,
217 $self->expiration_year % 2000);
220 sub _parse_expiration {
222 if ( $arg =~ /^(\d\d)(\d\d)$/ ) {
223 return ($1, 2000 + $2);
224 } elsif ( $arg =~ /^(\d\d?)\W(\d\d)$/ ) {
225 return ($1, 2000 + $2);
226 } elsif ( $arg =~ /^(\d\d?)\W(\d\d\d\d)$/ ) {
228 } elsif ( $arg =~ /^(\d\d?)\W\d\d?\W(\d\d\d\d)$/) {
231 die "can't parse expiration date '$arg'";
236 # gets/sets either the card number, or the account number + routing code
237 # depending on the payment type
239 if ( $self->payment_type eq 'CC' ) {
240 $self->card_number(@_);
241 } elsif ( $self->payment_type eq 'ECHECK' ) {
244 $arg =~ /^(\d+)@(\d+)$/ or die "Validation failed for payinfo";
245 $self->account_number($1);
246 $self->routing_code($2);
248 return ($self->account_number . '@' . $self->routing_code);
254 =head2 Tokenized Payment
260 If your gateway supports it, this may be
261 provided instead of card_number/account_number. See also
262 C<assigned_token> below.
266 has pay_by_token => ( is => 'rw', isa => 'Str' );
270 =head1 REPLY ATTRIBUTES
276 Boolean field for whether the item was approved. This
277 will always be set on replies.
281 The date the payment was processed, as a DateTime
286 The transaction identifier returned by the gateway
287 (not to be confused with 'tid', which is a transaction identifier assigned
288 by the merchant system). This is usually the identifier for performing
289 other operations on the transaction, like voiding or refunding it.
293 The authorization code, probably only meaningful for credit cards.
294 Should be undef (or not present) if the transaction wasn't approved.
298 The check number, probably only meaningful if this transaction was
299 processed from a paper check.
303 In tokenized systems which store the customer's account number or
304 credit card for future transactions, this is the token assigned to
305 identify that account. Pass it as 'pay_by_token' to use that payment
310 The message returned by the gateway. This may contain a value even
311 if the payment was successful (use C<approved> to determine that.)
315 A normalized failure status, from the following list:
321 =item nsf (non-sufficient funds / credit limit)
331 =item decline (other card/transaction declines)
339 has approved => ( is => 'rw', isa => 'Maybe[Bool]' );
341 has payment_date => ( is => 'rw', isa => 'DateTime' );
348 )] => ( is => 'rw', isa => 'Str');
350 enum FailureStatus => qw(
359 has failure_status => ( is => 'rw', isa => 'Maybe[FailureStatus]' );
361 has check_number => ( is => 'rw', isa => 'Int' );
363 around 'BUILDARGS' => sub {
364 my ($orig, $self, %args) = @_;
365 if ( $args{expiration} ) {
366 @args{'expiration_month', 'expiration_year'} =
367 _parse_expiration($args{expiration});
372 __PACKAGE__->meta->make_immutable;