--- /dev/null
+package Business::BatchPayment;
+
+use 5.006;
+use strict;
+use vars '$DEBUG';
+use warnings;
+
+use Class::MOP;
+use Business::BatchPayment::Processor;
+use Business::BatchPayment::Item;
+use Business::BatchPayment::Transport;
+
+$DEBUG = 0;
+
+=head1 NAME
+
+Business::BatchPayment - Batch-oriented payment processing
+
+=head1 VERSION
+
+Version 0.01
+
+=cut
+
+our $VERSION = '0.01';
+
+=head1 SYNOPSIS
+
+ use Business::BatchPayment;
+
+ my %options = ( merchant_id => '00451', password => 'opensesame' );
+ my $processor = Business::BatchPayment->create(MyGateway => %options);
+
+ my @request;
+ push @request, Business::BatchPayment->create(Item =>
+ action => 'payment', # as opposed to 'credit'
+ payment_type => 'CC', #credit card, or 'ECHECK' for check/ACH
+ amount => '49.95',
+ tid => '0001234', # transaction id, like a customer/order number
+ card_number => '1234123412341238',
+ expiration => '0100', # MM/YY
+ # these fields are optional
+ first_name => 'John',
+ last_name => 'Doe',
+ address => '123 Main Street',
+ address2 => 'Suite H',
+ city => 'Anytown',
+ state => 'CA',
+ country => 'US',
+ zip => '99015',
+ ); # returns a Business::OnlinePayment::Item;
+
+ $processor->submit(@request);
+
+ # at some point in the future
+
+ my @reply = $processor->receive();
+ foreach my $item (@reply) {
+ ... process items and record successful/failed payments
+ }
+
+=head1 CLASS METHODS
+
+=over 4
+
+=item create MODULE[, OPTIONS ]
+
+Loads Business::BatchPayment::MODULE, then attempts to call
+Business::BatchPayment::MODULE->new(OPTIONS).
+
+=cut
+
+# not a Moose method
+
+sub create {
+ my $class = shift;
+ my $subclass = shift;
+ $subclass = "Business::BatchPayment::$subclass";
+ Class::MOP::load_class($subclass);
+ $subclass->new(@_);
+}
+
+=back
+
+=head1 AUTHOR
+
+Mark Wells, C<< <mark at freeside.biz> >>
+
+=head1 BUGS
+
+=head1 SUPPORT
+
+You can find documentation for this module with the perldoc command.
+
+ perldoc Business::BatchPayment
+
+Commercial support is available from Freeside Internet Services,
+L<http://www.freeside.biz>.
+
+=head1 LICENSE AND COPYRIGHT
+
+Copyright 2012 Mark Wells.
+
+This program is free software; you can redistribute it and/or modify it
+under the terms of either: the GNU General Public License as published
+by the Free Software Foundation; or the Artistic License.
+
+See http://dev.perl.org/licenses/ for more information.
+
+
+=cut
+
+1; # End of Business::BatchPayment
--- /dev/null
+=head1 NAME
+
+Business::BatchPayment::Batch
+
+=head1 DESCRIPTION
+
+A Business::BatchPayment::Batch object represents a group of payment
+requests or confirmations (L<Business::BatchPayment::Item>) submitted
+to a bank (or returned from a bank) as a batch.
+
+=head1 ATTRIBUTES
+
+=over 4
+
+=item batch_id - Batch identifier. The format is processor-specific
+but usually must be a positive integer, if it's used at all.
+
+Processor modules may include C<batch_id> in a reply batch ONLY if
+it is guaranteed to match the batch_id of a request batch AND all
+the items in the reply batch come from that request batch. Otherwise,
+C<batch_id> must be null. It must always be null when using one-way
+(receive-only) workflow, since there are no request batches.
+
+=item items - An arrayref of L<Business::BatchPayment::Item> objects
+included in the batch.
+
+=back
+
+=cut
+
+package Business::BatchPayment::Batch;
+
+use Moose;
+
+has batch_id => (
+ is => 'rw',
+ isa => 'Str',
+ default => '',
+);
+
+has items => (
+ traits => ['Array'],
+ is => 'rw',
+ isa => 'ArrayRef[Business::BatchPayment::Item]',
+ handles => {
+ count => 'count',
+ elements => 'elements',
+ push => 'push',
+ },
+);
+
+1;
--- /dev/null
+package Business::BatchPayment::Debug;
+
+=head1 DESCRIPTION
+
+The 'debug' attribute. All Business::BatchPayment classes should support
+this. $Business::BatchPayment::DEBUG can be set to turn on debugging
+globally.
+
+=cut
+
+use Moose::Role;
+
+has 'debug' => (
+ is => 'rw',
+ isa => 'Int',
+ default => sub { $Business::BatchPayment::DEBUG || 0 },
+ lazy => 1,
+);
+
+1;
--- /dev/null
+package Business::BatchPayment::Item;
+
+use Moose;
+use Moose::Util::TypeConstraints;
+use MooseX::UndefTolerant;
+use DateTime;
+
+=head1 NAME
+
+Business::BatchPayment::Item
+
+=head1 DESCRIPTION
+
+A Business::BatchPayment::Item represents a single payment request or
+reply (approval or rejection). When submitting a batch, the merchant
+system constructs B::BP::Item objects for each attempted payment in
+the batch. Results downloaded from the gateway are returned as a
+list of Items with the 'approved' field set to a true or false value.
+
+=head1 REQUIRED ATTRIBUTES
+
+=over 4
+
+=item action
+
+"payment" or "credit". Most processors support only "payment".
+"payment" is defined as "money transfer FROM the account identified in the
+Item TO the account identified by the Processor object's login settings."
+"credit" is the other direction.
+
+=cut
+
+enum 'Action' => qw(payment credit);
+coerce 'Action', from 'Str', via { lc $_ };
+has action => (
+ is => 'rw',
+ isa => 'Action',
+ default => 'payment',
+ required => 1,
+ coerce => 1,
+);
+
+=item payment_type
+
+"CC" or "ECHECK". Most processors will only support
+one or the other, and if set on the BBP::Processor object, this is not
+required.
+
+=cut
+
+# are we okay with these names?
+enum 'PaymentType' => qw( CC ECHECK );
+has payment_type => ( is => 'rw', isa => 'PaymentType' );
+
+=item amount
+
+the amount, as a decimal number. Required only in request
+items.
+
+=cut
+
+# perhaps we should apply roles that distinguish request and reply items?
+# they have different required fields.
+has amount => (
+ is => 'rw',
+ isa => 'Num',
+);
+
+=item tid
+
+transaction identifier. Requests must provide this. It's
+a token of some kind to be passed to the gateway and used to identify the
+reply. For now it's required to be an integer. An invoice number would
+be a good choice.
+
+=cut
+
+has tid => ( is => 'rw', isa => 'Int' );
+
+=back
+
+=head1 OPTIONAL ATTRIBUTES
+
+=head2 Customer Information
+
+=over 4
+
+=item customer_id
+
+A customer number or other identifier, for the merchant's
+use.
+
+=item first_name
+
+First name.
+
+=item last_name
+
+Last name.
+
+=item company
+
+Company name.
+
+=item address, address2, city, state, country, zip
+
+Billing address fields. Credit card processors may use these (especially
+zip) for authentication.
+
+=cut
+
+has [ qw(
+ customer_id
+ first_name
+ last_name
+ company
+ address
+ address2
+ city
+ state
+ country
+ zip
+) ] => ( is => 'rw', isa => 'Str', default => '' );
+
+=back
+
+=head2 Transaction Information
+
+=over 4
+
+=item process_date
+
+The date requested for processing.
+
+=item invoice_number
+
+An invoice number, for your use.
+
+=cut
+
+class_type 'DateTime';
+coerce 'DateTime', from 'Int', via { DateTime->from_epoch($_) };
+has process_date => ( is => 'rw', isa => 'DateTime', coerce => 1 );
+
+has invoice_number => ( is => 'rw', isa => 'Str' );
+
+=back
+
+=head2 Bank Transfer / ACH / EFT
+
+=over 4
+
+=item account_number
+
+Bank account number.
+
+=item routing_code
+
+Bank's routing code.
+
+=item account_type
+
+Can be 'personal checking', 'personal savings'
+'business checking', or 'business savings'.
+
+=cut
+
+enum 'Account_Type' => [
+ 'personal checking',
+ 'personal savings',
+ 'business checking',
+ 'business savings',
+];
+coerce 'Account_Type', from 'Str', via { lc $_ };
+
+has account_number => ( is => 'rw', isa => 'Str' );
+has routing_code => ( is => 'rw', isa => 'Str' );
+has account_type => ( is => 'rw', isa => 'Account_Type', coerce => 1 );
+
+=back
+
+=head2 Credit Card
+
+=over 4
+
+=item card_number
+
+Credit card number.
+
+=item expiration
+
+Credit card expiration, MMYY format.
+
+=cut
+
+has card_number => ( is => 'rw', isa => 'Str' );
+has expiration => ( is => 'rw', isa => 'Str' );
+
+=back
+
+=head2 Tokenized Payment
+
+=over 4
+
+=item pay_by_token
+
+If your gateway supports it, this may be
+provided instead of card_number/account_number. See also
+C<assigned_token> below.
+
+=cut
+
+has pay_by_token => ( is => 'rw', isa => 'Str' );
+
+=back
+
+=head1 REPLY ATTRIBUTES
+
+=over 4
+
+=item approved
+
+Boolean field for whether the item was approved. This
+will always be set on replies.
+
+=item payment_date
+
+The date the payment was processed, as a DateTime
+object.
+
+=item order_number
+
+The transaction identifier returned by the gateway
+(not to be confused with 'tid', which is a transaction identifier assigned
+by the merchant system). This is usually the identifier for performing
+other operations on the transaction, like voiding or refunding it.
+
+=item authorization
+
+The authorization code, probably only meaningful for credit cards.
+Should be undef (or not present) if the transaction wasn't approved.
+
+=item assigned_token
+
+In tokenized systems which store the customer's account number or
+credit card for future transactions, this is the token assigned to
+identify that account. Pass it as 'pay_by_token' to use that payment
+account again.
+
+=item error_message
+
+The message returned by the gateway. This may
+contain a value even if the payment was successful (use C<approved>
+to determine that.)
+
+=back
+
+=cut
+
+has approved => ( is => 'rw', isa => 'Maybe[Bool]' );
+
+has payment_date => ( is => 'rw', isa => 'DateTime' );
+
+has [qw(
+ authorization
+ error_message
+ order_number
+ assigned_token
+)] => ( is => 'rw', isa => 'Str');
+
+__PACKAGE__->meta->make_immutable;
+
+1;
--- /dev/null
+=head1 NAME
+
+Business::BatchPayment::Processor - Common interface for batch payment gateways
+
+=head1 DESCRIPTION
+
+Business::BatchPayment::Processor is a Moose role. Modules implementing
+the protocol to talk to specific payment processing services should compose
+it.
+
+There are two general schemes for interacting with a batch payment gateway:
+
+=over 4
+
+=item Request/Reply
+
+In this mode, you (the merchant) assemble a batch of payment requests,
+including account numbers, and send it to the gateway. At some point in
+the future, the gateway sends back one or more reply batches indicating the
+results of processing the payments.
+
+When submitting a request batch, the merchant software marks each payment
+request with a unique transaction ID (the "tid" field). This should be
+stored somewhere. When the reply batch is processed, each item will have
+a tid matching its request.
+
+Note that some gateways will provide results only for approved payments,
+or even only for declined payments. It is then up to the merchant software
+to follow a sensible policy for approving or declining payments whose
+ultimate status is unconfirmed.
+
+=item One-Way
+
+In this mode, the gateway transmits a batch file containing approved
+payments, without those payments being requested. For example, most
+commercial banks provide check lockbox services and periodically
+send the merchant a statement of payments received to the lockbox.
+This statement would be processed as a one-way batch.
+
+=back
+
+=head1 ATTRIBUTES
+
+Most attributes for B::BP::Processor objects are defined by the module.
+
+=over 4
+
+=item transport - See L<Business::BatchPayment::Transport>. This must
+be set before calling submit() or receive(). Some modules will set it
+themselves; others require a transport to be supplied. Check for the
+existence of a 'default_transport' method.
+
+=item debug - Debug level. This may be interpreted in various ways by
+the module.
+
+=item test_mode - Communicate with a test server instead of the production
+gateway. Not all processors support this.
+C<$processor->does('Business::BatchPayment::TestMode')> should tell whether
+it's supported.
+
+=back
+
+=head1 OTHER PARAMETERS
+
+=over 4
+
+=item input FILE
+
+=item output FILE
+
+If either of these is passed when constructing a Processor object, the
+transport will be replaced with a File transport with those parameters.
+Specifying only 'input' will direct 'output' to /dev/null, and vice versa.
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item submit BATCH
+
+Send a batch of requests to the gateway. BATCH must be a
+L<Business::BatchPayment::Batch>.
+
+=item receive
+
+Download/otherwise acquire the available confirmed transactions from the
+gateway, parse them, and return a list of L<Business::BatchPayment::Batch>
+objects. The items in these batches will have, at minimum, the 'approved'
+field and either the 'tid' or 'amount' field set.
+
+=back
+
+=cut
+
+package Business::BatchPayment::Processor;
+
+use Moose::Role;
+with 'Business::BatchPayment::Debug';
+
+requires 'format_request', 'parse_response';
+
+has 'transport' => (
+ is => 'rw',
+ does => 'Business::BatchPayment::Transport',
+ # possibly this part should be a separate role
+ lazy => 1,
+ builder => 'default_transport',
+);
+
+around BUILDARGS => sub {
+ my ($orig, $class, %args) = @_;
+ %args = %{ $class->$orig(%args) }; #process as usual
+ # then:
+ if ( $args{input} or $args{output} ) {
+ $args{transport} = Business::BatchPayment->create( 'Transport::File',
+ input => $args{input},
+ output => $args{output},
+ );
+ }
+ \%args;
+};
+
+sub submit {
+ my $self = shift;
+ my $batch = shift;
+ my @items = @_;
+ my $request = $self->format_request($batch);
+ warn $request if $self->debug >= 2;
+ $self->transport->upload($request);
+}
+
+sub receive {
+ my $self = shift;
+ my @responses = $self->transport->download;
+ warn join("\n\n",@responses) if $self->debug >= 2 and scalar(@responses);
+ map { $self->parse_response($_) } @responses;
+}
+
+1;
--- /dev/null
+package Business::BatchPayment::TestMode;
+
+=head1 DESCRIPTION
+
+The 'test_mode' attribute. A module that does this role is promising that
+requests will not be submitted to a live account if test_mode is true.
+
+=cut
+
+use Moose::Role;
+
+has 'test_mode' => (
+ is => 'rw',
+ isa => 'Bool',
+ default => 0,
+);
+
+1;
--- /dev/null
+package Business::BatchPayment::Transport;
+
+use Moose::Role;
+with 'Business::BatchPayment::Debug';
+
+requires 'upload';
+requires 'download';
+
+=head1 NAME
+
+Business::BatchPayment::Transport
+
+=head1 USAGE
+
+Business::BatchPayment::Processor classes must have a 'transport' attribute
+which does the Business::BatchPayment::Transport role (implements 'upload'
+and 'download'). The B::BP::Processor class can implement 'default_transport'
+to construct a B::BP::Transport object.
+
+'upload' must take a scalar argument containing the (correctly formatted)
+batch for upload. On success, it may return nothing, or a scalar identifier
+for the batch (such as a serial number) if the gateway requires one.
+
+'download' takes the return value from 'upload' (if any) as its argument,
+and returns any number of scalars containing batch files. If there are no
+batches available, it should return an empty list.
+
+Both of these methods should die on error.
+
+=cut
+
+1;
--- /dev/null
+package Business::BatchPayment::Transport::File;
+
+=head2 File transport
+
+The simplest case. Takes two arguments, 'input' and 'output'. These can
+be open filehandles or strings naming files. If unspecified, they default
+to /dev/null.
+
+=cut
+
+use IO::File;
+use Moose;
+with 'Business::BatchPayment::Transport';
+
+has 'input' => (
+ is => 'rw',
+ isa => 'Maybe[FileHandle|Str]',
+ default => sub {
+ warn "no input passed to file transport; using /dev/null";
+ '/dev/null'
+ },
+ #lazy => 1,
+);
+
+has 'output' => (
+ is => 'rw',
+ isa => 'Maybe[FileHandle|Str]',
+ default => sub {
+ warn "no output passed to file transport; using /dev/null";
+ '/dev/null'
+ },
+ #lazy => 1,
+);
+
+sub upload {
+ my $self = shift;
+ my $text = shift;
+ my $fh;
+ if ( ref $self->output ) {
+ $fh = $self->output;
+ } else {
+ $fh = IO::File->new();
+ $fh->open($self->output,'>')
+ or die "couldn't write to ".$self->output;
+ }
+ print $fh $text;
+}
+
+sub download {
+ my $self = shift;
+ my $fh;
+ if ( ref $self->input ) {
+ $fh = $self->input;
+ } else {
+ $fh = IO::File->new();
+ $fh->open($self->input,'<')
+ or die "couldn't read from ".$self->input;
+ }
+ local $/;
+ my $text = <$fh>;
+}
+
+1;
--- /dev/null
+package Business::BatchPayment::Transport::HTTPS;
+
+=head2 HTTPS transport
+
+Sends a request by HTTPS POST, and downloads the response the same way.
+Options are 'server', 'port', 'get_path', 'put_path', optionally
+'content_type'.
+
+=cut
+
+use Net::HTTPS::Any 0.10;
+use Moose;
+with 'Business::BatchPayment::Transport';
+
+has [ qw( host port get_path put_path ) ] => (
+ is => 'ro',
+ isa => 'Str'
+);
+
+has 'content_type' => (
+ is => 'rw',
+ isa => 'Str',
+ default => 'text/plain'
+);
+
+sub https_post {
+ my $self = shift;
+ my $path = shift;
+ my $content = shift;
+
+ warn "starting https_post...\n" if $self->debug;
+ my ( $page, $response, %reply_headers ) = Net::HTTPS::Any::https_post(
+ host => $self->host,
+ port => $self->port,
+ path => $path,
+ content => $content,
+ debug => ($self->debug >= 3),
+ );
+ warn "PAGE:\n$page\n\nRESPONSE:\n$response\n\n" if $self->debug >= 2;
+ return ($page, $response, %reply_headers);
+}
+
+sub upload {
+ my $self = shift;
+ my $content = shift;
+ $self->https_post($self->put_path, $content);
+}
+
+sub download {
+ # will probably need to be overridden in most cases
+ my $self = shift;
+ my ($page, $response, %reply_headers) = $self->https_post($self->get_path);
+ $page;
+}
+
+__PACKAGE__->meta->make_immutable;
+
+1;
--- /dev/null
+package Business::BatchPayment::Transport::SFTP;
+
+=head2 Business::BatchPayment::Transport::SFTP
+
+Import this role to use SFTP. Requires "host", "port', "user", and
+"password" to be set. The 'sftp' method returns a Net::SFTP::Foreign
+object providing 'get' and 'put' methods (among others). All of these
+methods die on error.
+
+Also provides "get_path" and "put_path" attributes. The default 'download'
+and 'upload' methods get and put to those paths on the SFTP host. You
+may find it useful to modify or override that behavior.
+
+=cut
+
+use Net::SFTP::Foreign;
+use File::Slurp qw(read_file);
+use Moose;
+with 'Business::BatchPayment::Transport';
+
+has [ qw( host login password ) ] => (
+ is => 'rw',
+ isa => 'Str',
+);
+
+has 'port' => (
+ is => 'rw',
+ isa => 'Int',
+ default => 22,
+);
+
+has [ 'get_path', 'put_path' ] => (
+ is => 'rw',
+ isa => 'Str',
+);
+
+has 'sftp' => (
+ is => 'ro',
+ isa => 'Net::SFTP::Foreign',
+ handles => [ 'get', 'put', 'ls', 'setcwd' ],
+ builder => '_sftp',
+ lazy => 1,
+);
+
+has 'timeout' => (
+ is => 'rw',
+ isa => 'Int',
+ default => 60,
+);
+
+sub _sftp {
+ my $self = shift;
+ my %args = (
+ host => $self->host,
+ port => $self->port,
+ user => $self->login,
+ password => $self->password,
+ timeout => $self->timeout,
+ autodie => 1,
+ );
+ $args{'more'} = '-v' if $self->debug >= 2;
+ Net::SFTP::Foreign->new(%args);
+}
+
+sub upload {
+ my $self = shift;
+ die "default SFTP upload requires 'put_path'\n" unless $self->put_path;
+ my $content = shift;
+ my ($out, $tmpfile) = tempfile();
+ die "failed to open temporary file $tmpfile\n" unless $out;
+ print $out $content;
+ close $out;
+ $self->sftp->put($tmpfile, $self->put_path);
+ unlink $tmpfile;
+}
+
+sub download { # mostly illustrative rather than directly useful
+ my $self = shift;
+ die "default SFTP download requires 'get_path'\n" unless $self->get_path;
+ my ($out, $tmpfile) = tempfile();
+ $self->sftp->get($self->get_path, $out);
+ close $out;
+ return read_file($tmpfile);
+}
+
+__PACKAGE__->meta->make_immutable;
+
+1;
--- /dev/null
+Revision history for Business-BatchPayment
+
+0.01 unreleased
+
--- /dev/null
+Changes
+ignore.txt
+Makefile.PL
+BatchPayment/Item.pm
+BatchPayment/Transport/HTTPS.pm
+BatchPayment/Transport/SFTP.pm
+BatchPayment/Transport/File.pm
+BatchPayment/Debug.pm
+BatchPayment/TestMode.pm
+BatchPayment/Processor.pm
+BatchPayment/Transport.pm
+BatchPayment/Batch.pm
+BatchPayment.pm
+t/manifest.t
+t/pod.t
+t/00-load.t
+README
+MANIFEST
+META.yml Module meta-data (added by MakeMaker)
--- /dev/null
+--- #YAML:1.0
+name: Business-BatchPayment
+version: 0.01
+abstract: Batch-oriented payment processing
+author:
+ - Mark Wells <mark@freeside.biz>
+license: perl
+distribution_type: module
+configure_requires:
+ ExtUtils::MakeMaker: 0
+build_requires:
+ ExtUtils::MakeMaker: 0
+requires:
+ DateTime: 0
+ File::Slurp: 0
+ Moose: 2
+ MooseX::UndefTolerant: 0
+ Net::HTTPS::Any: 0.1
+ Net::SFTP::Foreign: 0
+ Test::More: 0
+no_index:
+ directory:
+ - t
+ - inc
+generated_by: ExtUtils::MakeMaker version 6.57_05
+meta-spec:
+ url: http://module-build.sourceforge.net/META-spec-v1.4.html
+ version: 1.4
--- /dev/null
+use 5.006;
+use strict;
+use warnings;
+use ExtUtils::MakeMaker;
+
+WriteMakefile(
+ NAME => 'Business::BatchPayment',
+ AUTHOR => q{Mark Wells <mark@freeside.biz>},
+ VERSION_FROM => 'BatchPayment.pm',
+ ABSTRACT_FROM => 'BatchPayment.pm',
+ ($ExtUtils::MakeMaker::VERSION >= 6.3002
+ ? ('LICENSE'=> 'perl')
+ : ()),
+ PL_FILES => {},
+ PREREQ_PM => {
+ 'Moose' => 2.000,
+ 'MooseX::UndefTolerant' => 0,
+ 'DateTime' => 0,
+ 'File::Slurp' => 0,
+ 'Net::SFTP::Foreign' => 0,
+ 'Net::HTTPS::Any' => 0.10,
+ 'Test::More' => 0,
+ },
+ dist => { COMPRESS => 'gzip -9f', SUFFIX => 'gz', },
+ clean => { FILES => 'Business-BatchPayment-*' },
+);
--- /dev/null
+Business::BatchPayment is a generic interface for processing credit card
+or electronic checking payments in an asynchronous manner. It fills a
+similar role to Business::OnlinePayment, but works with processors that
+expect periodic delivery of payment requests and return results in a
+later step.
+
+INSTALLATION
+
+To install this module, run the following commands:
+
+ perl Makefile.PL
+ make
+ make test
+ make install
+
+SUPPORT AND DOCUMENTATION
+
+After installing, you can find documentation for this module with the
+perldoc command.
+
+ perldoc Business::BatchPayment
+
+Contact Freeside Internet Services <sales@freeside.biz> for commercial
+support and custom development services.
+
+LICENSE AND COPYRIGHT
+
+Copyright (C) 2012 Mark Wells
+
+This program is free software; you can redistribute it and/or modify it
+under the terms of either: the GNU General Public License as published
+by the Free Software Foundation; or the Artistic License.
+
+See http://dev.perl.org/licenses/ for more information.
+
--- /dev/null
+.git*
+blib*
+Makefile
+Makefile.old
+Build
+Build.bat
+_build*
+pm_to_blib*
+*.tar.gz
+.lwpcookies
+cover_db
+pod2htm*.tmp
+Business-BatchPayment-*
--- /dev/null
+#!perl -T
+
+use Test::More tests => 1;
+
+BEGIN {
+ use_ok( 'Business::BatchPayment' ) || print "Bail out!\n";
+}
+
+diag( "Testing Business::BatchPayment $Business::BatchPayment::VERSION, Perl $], $^X" );
--- /dev/null
+#!perl -T
+
+use strict;
+use warnings;
+use Test::More;
+
+unless ( $ENV{RELEASE_TESTING} ) {
+ plan( skip_all => "Author tests not required for installation" );
+}
+
+eval "use Test::CheckManifest 0.9";
+plan skip_all => "Test::CheckManifest 0.9 required" if $@;
+ok_manifest({ exclude => ['/.git', '/blib'] });
--- /dev/null
+#!perl -T
+
+use strict;
+use warnings;
+use Test::More;
+
+# Ensure a recent version of Test::Pod
+my $min_tp = 1.22;
+eval "use Test::Pod $min_tp";
+plan skip_all => "Test::Pod $min_tp required for testing POD" if $@;
+
+all_pod_files_ok();