card_token
expiry_month
expiry_year
+ failure_status
invoice_number
message_id
payment_method
my $method = $action_dispatch_table{$action};
- $self->submit_action_unsupported()
- unless $method
- && $self->can($method);
+ unless ( $method && $self->can($method) ) {
+ warn $self->error_message( "Action is unsupported ($action)" );
+ return $self->is_success(0);
+ }
$self->$method(@_);
}
my %post = (
order_number => $self->truncate( $content->{invoice_number}, 30 ),
amount => $content->{amount},
- billing => $self->jhref_billing_address,
);
- # Credit Card
- if ( $content->{card_number} ) {
+ if (
+ $content->{card_token}
+ || ( $content->{card_number} && $content->{card_number} =~ /^99\d{14}$/ )
+ ) {
+ # Process payment against a stored Payment Profile, whose
+ # customer_code is used as the card_token
+
+ my $card_token = $content->{card_token} || $content->{card_number};
+
+ unless ( $card_token =~ /^99\d{14}$/ ) {
+ $self->error_message(
+ "Invalid card_token($card_token): Expected 16-digit "
+ . " beginning with 99"
+ );
+ return $self->is_success(0);
+ }
+
+ $post{payment_method} = 'payment_profile';
+
+ $post{payment_profile} = {
+ customer_code => $card_token,
+ card_id => 1,
+ };
+
+ } elsif ( $content->{card_number} ) {
+
$post{payment_method} = 'card';
# Add card payment details to %post
$post{card} = $self->jhref_card;
return if $self->error_message;
+ # Add billing address to card
+ $post{billing} = $self->jhref_billing_address;
+
# Designate recurring payment label
$post{card}->{recurring_payment} = $content->{recurring_payment} ? 1 : 0;
# Direct API to issue a complete auth, instead of pre-auth
$post{card}->{complete} = 1;
- # $post{card} = {
- # number => $self->truncate( $content->{card_number}, 20 ),
- # name => $self->truncate( $content->{owner}, 64 ),
- # expiry_month => sprintf( '%02d', $content->{expiry_month} ),
- # expiry_year => sprintf( '%02d', $content->{expiry_year} ),
- # cvd => $content->{cvv2},
- # recurring_payment => $content->{recurring_payment} ? 1 : 0,
- # complete => 1,
- # };
-
} else {
croak 'unknown/unsupported payment method!';
}
} elsif ( $action eq 'authorization only' ) {
# Perform pre-authorization
$self->path('/v1/payments');
- $post{card}->{complete} = 0;
+
+ # Set the 'complete' flag to false, directing API to perform pre-auth
+ if ( ref $post{payment_profile} ) {
+ $post{payment_profile}->{complete} = 0;
+ } elsif ( ref $post{card} ) {
+ $post{card}->{complete} = 0;
+ }
} elsif ( $action eq 'post authorization' ) {
# Complete a pre-authorization
);
my $post_body = encode_json( \%post );
+ $self->path( sprintf '/v1/payments/%s/returns', $content->{order_number} );
if ( $DEBUG ) {
warn Dumper({
+ path => $self->path,
post => \%post,
post_body => $post_body,
});
}
- $self->path( sprintf '/v1/payments/%s/returns', $content->{order_number} );
my $response = $self->submit_api_request( $post_body );
+ return if $self->error_message;
+
+ $self->is_success(1);
+
+ $response;
}
=head2 submit_tokenize
Bambora tokenization is based on the Payment Profile feature of their API.
-The token created by this method represnets the Bambora customer_code for the
+The token created by this method represents the Bambora customer_code for the
Payment Profile. The token resembles a credit card number. It is 16 digits
long, beginning with 99. No valid card number can begin with the digits 99.
-This method creates the payment profile, then replaces the customer_code
-generated by Bambora with the card number resembling token.
+This method creates the payment profile and reports the customer_code
+as the card_token
=cut
}
$self->response_decoded( $response );
- # Response returned an error
if ( $response->{code} && $response->{code} != 1 ) {
+ # Response returned an error
+
$self->is_success( 0 );
$self->result_code( $response->{code} );
+ if ( $response->{message} =~ /decline/i ) {
+ $self->failure_status('declined');
+ }
+
return $self->error_message(
sprintf '%s %s',
$response->{code},
return $response;
}
-=head2 submit_action_unsupported
-
-Croak with the error message Action $action unsupported
-
-=cut
-
-sub submit_action_unsupported {
- croak sprintf 'Action %s unsupported', shift->{_content}{action}
-}
-
=head2 authorization_header
Bambora REST requests authenticate via a HTTP header of the format:
sub jhref_billing_address {
my $self = shift;
- $self->set_province;
+ $self->parse_province;
$self->set_country;
- $self->set_phone_number;
+ $self->parse_phone_number;
my $content = $self->{_content};
$self->set_expiration;
+ $content->{owner} ||= $content->{name};
+
# Check required input
for my $f (qw/
card_number
owner
expiry_month
expiry_year
- cvv2
/) {
next if $content->{$f};
"Cannot parse card payment - missing required content $f"
);
- warn $self->error_message if $DEBUG;
- $self->is_success( 0 );
+ if ( $DEBUG ) {
+ warn Dumper({
+ error_message => $self->error_message,
+ content => $content,
+ });
+ }
+ $self->is_success( 0 );
return {};
}
name => $self->truncate( $content->{owner}, 64 ),
expiry_month => sprintf( '%02d', $content->{expiry_month} ),
expiry_year => sprintf( '%02d', $content->{expiry_year} ),
- cvd => $content->{cvv2},
+
+ $content->{cvv2} ? ( cvd => $content->{cvv2} ) : (),
}
}
);
}
-=head2 set_payment_method
-
-Set payment_method value to one of the following strings
-
- card
- token
- payment_profile
- cash
- cheque
- interac
- apple_pay
- android_pay
-
-=cut
-
-sub set_payment_method {
- # todo - determine correct payment method
- warn "set_payment_method() STUB FUNCTION ALWAYS RETURNS card!\n";
- shift->{_content}->{payment_method} = 'card';
-}
-
-=head2 set_phone_number
+=head2 parse_phone_number
Set value for field phone_number, from value in field phone
=cut
-sub set_phone_number {
+sub parse_phone_number {
my $self = shift;
my $content = $self->{_content};
$content->{phone_number} = $phone;
}
-=head2 set_province
+=head2 parse_province
Set value for field province, from value in field state
=cut
-sub set_province {
+sub parse_province {
my $self = shift;
my $content = $self->{_content};
my $country = uc $content->{country};