use strict;
+=head1 NAME
+
+Business::BatchPayment::BillBuddy - BillBuddy batch payment format and transport
+
+=head1 USAGE
+
+See L<Business::BatchPayment> for general usage notes.
+
+=head2 SYNOPSIS
+
+ use Business::BatchPayment;
+
+ # Upload batch
+ my @items = Business::BatchPayment::Item->new( ... );
+ my $batch = Business::BatchPayment->create(Batch =>
+ batch_id => $self->batchnum,
+ items => \@items
+ );
+
+ my $processor = Business::BatchPayment->processor('BillBuddy',
+ login => 'USER_ID',
+ password => 'API_KEY',
+ host => 'xmlrpc.billbuddy.com',
+ path => 'v1_sandbox',
+ #optional...
+ port => 443,
+ debug => 1,
+ );
+
+ my $result = $processor->submit($batch);
+
+ # this gets set by submit, and is needed for receive
+ my $processor_id = $batch->processor_id;
+
+ # Download results
+ my @reply = $processor->receive(@process_ids);
+
+=head2 PROCESSOR ATTRIBUTES
+
+=over 4
+
+=item username - the user_id provided to you by BillBuddy
+
+=item password - the api_key (NOT the web portal password) provided to you by BillBuddy
+
+=item host - the domain name for BillBuddy XMLRPC requests
+
+=item path - the path for BillBuddy XMLRPC requests
+
+=item port - the port for BillBuddy XMLRPC requests (optional, default 443)
+
+=item debug - print debug warnings if true, including XML requests and responses
+
+=back
+
+=head1 AUTHOR
+
+Jonathan Prykop, jonathan@freeside.biz
+
+=head1 SUPPORT
+
+You can find documentation for this module with the perldoc command.
+
+ perldoc Business::BatchPayment::BillBuddy
+
+Commercial support is available from Freeside Internet Services,
+L<http://www.freeside.biz>
+
+=head1 LICENSE AND COPYRIGHT
+
+Copyright 2015 Freeside Internet Services
+
+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
+
use Business::BatchPayment;
+use DateTime;
use Moose;
with 'Business::BatchPayment::Processor';
-our $VERSION = '0.01';
+our $VERSION = '0.03';
-has [ qw(user_id api_key host) ] => (
+has [ qw(username password) ] => (
is => 'ro',
isa => 'Str',
- required => 1,
+);
+
+has 'host' => (
+ is => 'ro',
+ isa => 'Str',
+ default => 'xmlrpc.billbuddy.com',
);
has 'path' => (
sub default_transport {
my $self = shift;
Business::BatchPayment->create('BillBuddy::Transport',
- user_id => $self->user_id,
- api_key => $self->api_key,
+ username => $self->username,
+ password => $self->password,
host => $self->host,
port => $self->port,
path => $self->path,
my $self = shift;
my $batch = shift;
my $request = $self->format_request($batch);
- warn $request if $self->debug >= 2;
$self->transport->upload($request,$batch);
}
use Moose;
extends 'Business::BatchPayment::Transport::HTTPS';
-has [ qw(user_id api_key) ] => (
+has [ qw(username password) ] => (
is => 'ro',
isa => 'Str',
required => 1,
my $xmlcontent = $self->xml_format($sid,@param);
warn $self->host . ' ' . $self->port . ' ' . $path . "\n" . $xmlcontent if $self->debug;
my ($response, $rcode, %rheaders) = $self->https_post($path,$xmlcontent);
- die "Bad response from gateway: $rcode" unless $rcode eq '200 OK';
+ die "Bad response from gateway: $rcode\n" unless $rcode eq '200 OK';
warn $response . "\n" if $self->debug;
my $rref = XMLin($response, KeyAttr => ['ResponseData'], ForceArray => []);
- die "Error from gateway: " . $rref->{'ResponseStatusDescription'} if $rref->{'ResponseStatus'};
+ die "Error from gateway: " . $rref->{'ResponseStatusDescription'}. "\n"
+ if $rref->{'ResponseStatus'};
return $rref;
}
sub upload {
my ($self,$request,$batch) = @_;
my @tokens = ();
- # get date from batch
- my ($date) = $batch->process_date =~ /^(....-..-..)/;
# login
- my $resp = $self->xmlrpc_post('xmlrpc_tp_Login.asp','',$self->user_id,$self->api_key);
+ my $resp = $self->xmlrpc_post('xmlrpc_tp_Login.asp','',$self->username,$self->password);
my $sid = $resp->{'ResponseData'}->{'sessionID'};
die "Could not parse sessionid from gateway response" unless $sid;
+ # get date from login, to ensure we're using upstream date
+ my ($year,$mon,$mday,$hour,$min,$sec) = $resp->{'ResponseTimestamp'} =~ /^(....)-(..)-(..)\s+(..):(..):(..)/;
+ # then add a day and a bit, because "processs date need to be a date in the future"
+ my $date = DateTime->new(
+ year => $year,
+ month => $mon,
+ day => $mday,
+ hour => $hour,
+ minute => $min,
+ second => $sec,
+ # timezone on object mostly doesn't matter,
+ # but this does appear to be the tz being passed by BillBuddy,
+ # and this should avoid DST troubles (Queensland does not do DST)
+ time_zone => 'Australia/Queensland',
+ )->add_duration(
+ # extra hour is buffer for upload to run, hopefully that's plenty
+ DateTime::Duration->new( hours => 25 )
+ )->ymd;
# start a payment batch
- $resp = $self->xmlrpc_post('xmlrpc_tp_DDRBatch_Open.asp',$sid,$self->user_id,$date);
+ $resp = $self->xmlrpc_post('xmlrpc_tp_DDRBatch_Open.asp',$sid,$self->username,$date);
my $batchno = $resp->{'ResponseData'}->{'batchno'};
die "Could not parse batchno from gateway response" unless $batchno;
$batch->processor_id($batchno);
# post a payment transaction
foreach my $line (split(/\n/,$request)) {
- $self->xmlrpc_post('xmlrpc_tp_DDRTransaction_Add.asp',$sid,$self->user_id,$batchno,['cdataElement',$line]);
+ $self->xmlrpc_post('xmlrpc_tp_DDRTransaction_Add.asp',$sid,$self->username,$batchno,['cdataElement',$line]);
}
# close payment batch
- $self->xmlrpc_post('xmlrpc_tp_DDRBatch_Close.asp',$sid,$self->user_id,$batchno);
+ $self->xmlrpc_post('xmlrpc_tp_DDRBatch_Close.asp',$sid,$self->username,$batchno);
# submit payment batch
- $self->xmlrpc_post('xmlrpc_tp_DDRBatch_Submit.asp',$sid,$self->user_id,$batchno);
+ $self->xmlrpc_post('xmlrpc_tp_DDRBatch_Submit.asp',$sid,$self->username,$batchno);
# logout
- $self->xmlrpc_post('xmlrpc_tp_Logout.asp',$sid,$self->user_id);
+ $self->xmlrpc_post('xmlrpc_tp_Logout.asp',$sid,$self->username);
return '';
}
my @processor_ids = @_;
return () unless @processor_ids;
# login
- my $resp = $self->xmlrpc_post('xmlrpc_tp_Login.asp','',$self->user_id,$self->api_key);
+ my $resp = $self->xmlrpc_post('xmlrpc_tp_Login.asp','',$self->username,$self->password);
my $sid = $resp->{'ResponseData'}->{'sessionID'};
die "Could not parse sessionid from gateway response" unless $sid;
my @batches = ();
foreach my $batchno (@processor_ids) {
#get BillBuddy transaction ids for batch
- $resp = $self->xmlrpc_post('xmlrpc_tp_DDRBatch_getTranList.asp',$sid,$self->user_id,$batchno);
+ $resp = $self->xmlrpc_post('xmlrpc_tp_DDRBatch_getTranList.asp',$sid,$self->username,$batchno);
my $tids = $resp->{'ResponseData'}->{'id'};
next unless $tids; #error/die instead?
my @batchitems = ();
$tids = ref($tids) ? $tids : [ $tids ];
#get status by individual transaction
foreach my $tid (@$tids) {
- $resp = $self->xmlrpc_post('xmlrpc_tp_DDRBatch_getTranStatus.asp',$sid,$self->user_id,$tid);
+ $resp = $self->xmlrpc_post('xmlrpc_tp_DDRBatch_getTranStatus.asp',$sid,$self->username,$tid);
my $status = lc($resp->{'ResponseData'}->{'bankprocessstatus'});
my $error = '';
next if grep(/^$status$/,('submitted','processing','scheduled'));
error_message => $error,
authorization => '',
);
- #not sure what format date gets returned in, item creation will fail on bad format,
- #so I'm taking a guess, and not recording the date if my guess is wrong
if ($resp->{'ResponseData'}->{'actualprocessdate'} =~ /^(\d\d\d\d).(\d\d).(\d\d)/) {
$item->payment_date($1.'-'.$2.'-'.$3);
+ } else {
+ warn "Could not parse actualprocessdate ".$resp->{'ResponseData'}->{'actualprocessdate'};
}
push(@batchitems,$item);
}
}
}
# logout
- $self->xmlrpc_post('xmlrpc_tp_Logout.asp',$sid,$self->user_id);
+ $self->xmlrpc_post('xmlrpc_tp_Logout.asp',$sid,$self->username);
return @batches;
}