From Code to Community: Sponsoring The Perl and Raku Conference 2025 Learn more

use strict;
use JSON;
our $VERSION = '0.04';
# build 4.1
sub new {
my ($class, %args) = (@_);
my $self = bless {}, $class;
$args{config} ||= {};
$self->{config} = {
api_version => '1', # the '1' in the API endpoint host names, e.g. https://test-api.pin.net.au/1/charges
api_key => undef, # Secret API Key
api => 'charges', # 'customers', 'refunds'
amount => '100', # 100 cents. Must be greater or equal to $1.00
currency => 'AUD', # 'USD', 'NZD', or 'SGD'
description => 'charges',
email => 'tester@work.com.au',
ip_address => undef,
charge_token => undef, # for refunds API
card_token => undef,
customer_token => undef,
card => {
number => '5520000000000000',
expiry_month => '05',
expiry_year => '2014',
cvc => '123',
name => 'John Smith',
address_line1 => '1 Test St',
address_line2 => undef,
address_city => 'Sydney',
address_postcode => '2000',
address_state => 'NSW',
address_country => 'Australia'
}
};
foreach my $key (qw(api_key api amount currency description email ip_address charge_token card_token customer_token)) {
next unless defined $args{config}->{$key};
$self->{config}->{$key} = $args{config}->{$key};
}
if ($self->{config}->{card_token} || $self->{config}->{customer_token}) {
delete $self->{config}->{card};
}
else {
foreach my $key (qw(number expiry_month expiry_year cvc name address_line1 address_line2 address_city address_postcode address_state address_country)) {
next unless defined $args{config}->{card}->{$key};
$self->{config}->{card}->{$key} = $args{config}->{card}->{$key};
}
}
my $url;
my $live = $args{live};
my $api = delete ($self->{config}->{api});
my $api_version = delete ($self->{config}->{api_version});
my $api_key = delete ($self->{config}->{api_key});
unless ($api_key) {
$self->{error} = 'Missing Secret API Key';
return $self;
}
if ($live) {
$url = 'https://api.pin.net.au/' . $api_version;
}
else {
$url = 'https://test-api.pin.net.au/' . $api_version
}
if ($api eq 'refunds' && $self->{config}->{charge_token}) {
$url .= '/charges/' . $self->{config}->{charge_token} .'/' .$api;
}
else {
$url .= '/' . $api;
}
my $ua = LWP::UserAgent->new();
my $p = HTTP::Request->new(POST => $url);
$p->content_type('application/json');
$p->authorization_basic($api_key);
my $json_request = to_json( $self->{config}, {utf8 => 1} );
$p->content($json_request) unless $api eq 'refunds';
$self->{response} = $ua->request($p);
my $json_response;
if ($self->{response}->content) {
$json_response = from_json( $self->{response}->content, {utf8 => 1} );
$self->{json_response} = $json_response;
}
if ($json_response) {
if ($json_response->{response}->{success}) {
$self->{successful} = 1;
$self->{id} = $json_response->{response}->{token};
$self->{status} = $json_response->{response}->{status_message};
}
elsif ($json_response->{response}->{token}) {
$self->{successful} = 1;
$self->{id} = $json_response->{response}->{token};
$self->{status} = $json_response->{response}->{status_message} || ''; # customers has non status message
}
elsif (exists $json_response->{error}) {
$self->{status} = $json_response->{error};
my @errors = ($json_response->{error_description} . '.');
if (exists $json_response->{messages}) {
foreach my $message (@{$json_response->{messages}}) {
push (@errors, $message->{message} . '.');
}
}
$self->{error} = join (' ', @errors);
}
elsif (exists $json_response->{messages}) {
$self->{error} = $json_response->{messages}->[0]->{message};
$self->{status} = $json_response->{messages}->[0]->{code};
}
}
else {
$self->{error} = $self->{response}->status_line;
}
return $self;
}
sub card_token {
my $self = shift;
return $self->{json_response}->{response}->{card}->{token} || '';
}
sub json_response {
my $self = shift;
return $self->{json_response} || {};
}
sub response {
my $self = shift;
return $self->{response} || '';
}
sub successful {
my $self = shift;
return $self->{successful} || undef;
}
sub error {
my $self = shift;
return $self->{error} || '';
}
sub id {
my $self = shift;
return $self->{id} || ''; # charge or customer token depending on the API
}
sub status {
my $self = shift;
return $self->{status} || '';
}
1;
__END__
=head1 NAME
Business::PinPayment - Interface for Pin Payment API
=head1 SYNOPSIS
use Business::PinPayment;
# Run a test charge of $1.00
my $test = Business::PinPayment->new(config => {api_key => 'T3sTS3cret-ap1key'});
if ($test->successful()) {
print 'Test Successful';
}
else {
print $test->error();
}
# Run a live one
my $live = Business::PinPayment->new(
live => 1,
config => {
api_key => 'L1veS3cret-ap1key',
amount => '100',
currency => 'AUD',
description => 'charges',
email => 'tester@work.com.au',
card => {
number => '5520000000000000',
expiry_month => '05',
expiry_year => '2014',
cvc => '123',
name => 'John Smith',
address_line1 => '1 Test St',
address_city => 'Sydney',
address_postcode => '2000',
address_state => 'NSW',
address_country => 'Australia'
}
}
);
=head1 DESCRIPTION
An interface to the Pin Payment API.
=head1 METHODS
=head2 C<new>
Instantiates a new PinPayment object. By default, it runs a test transaction with the given API key.
=over
=item C<config>
A hashref of parameters accepted by the PinPayment API . See L<https://pin.net.au/docs/api>. Default values:
api_version => '1', # the '1' in the API endpoint host names, e.g. https://test-api.pin.net.au/1/charges
api_key => undef, # Secret API Key
api => 'charges', # 'customers', 'refunds'
amount => '100', # 100 cents. Must be greater or equal to $1.00
currency => 'AUD', # 'USD', 'NZD', or 'SGD'
description => 'charges',
email => 'tester@work.com.au',
ip_address => undef,
charge_token => undef, # for refunds API
card_token => undef,
customer_token => undef,
card => {
number => '5520000000000000',
expiry_month => '05',
expiry_year => '2014',
cvc => '123',
name => 'John Smith',
address_line1 => '1 Test St',
address_line2 => undef,
address_city => 'Sydney',
address_postcode => '2000',
address_state => 'NSW',
address_country => 'Australia',
}
The following script performs a test charge and then refunds it. It then creates a test customer, charges the created customer via the customer token (ID) and card token.
# You may need to disable SSL host name verification for testing
$ENV{PERL_LWP_SSL_VERIFY_HOSTNAME} = 0;
my $test_api_key = 'L1veS3cret-ap1key';
# Test charge for $1.00
my $charge = Business::PinPayment->new(
config => {
api_key => $test_api_key,
card => {
number => '5520000000000000',
expiry_month => '05',
expiry_year => '2014',
cvc => '123',
name => 'John Smith',
address_line1 => '1 Test St',
address_city => 'Sydney',
address_postcode => '2000',
address_state => 'NSW',
address_country => 'Australia'
}
}
);
if ($charge->successful()) {
print 'Charge Token: ' . $charge->id() . "\n";
# Refund the charge
my $refund = Business::PinPayment->new(
config => {
api => 'refunds',
api_key => $test_api_key,
charge_token => $charge->id()
}
);
if ($refund->successful()) {
print 'Refund Token: ' . $refund->id() . "\n";
}
else {
print 'Refund Error: ' . $refund->error() . "\n";
}
}
else {
print 'Charge Error: ' . $charge->error() . "\n";
}
# Create a customer
my $customer = Business::PinPayment->new(
config => {
api => 'customers',
api_key => $test_api_key,
card => {
number => '5520000000000000',
expiry_month => '05',
expiry_year => '2014',
cvc => '123',
name => 'John Smith',
address_line1 => '1 Test St',
address_city => 'Sydney',
address_postcode => '2000',
address_state => 'NSW',
address_country => 'Australia'
}
}
);
if ($customer->successful()) {
print 'Customer Token: ' . $customer->id() . "\n";
# Charge the customer $1.00
my $charge_customer = Business::PinPayment->new(
config => {
api => 'charges',
api_key => $test_api_key,
customer_token => $customer->id()
},
);
if ($charge_customer->successful()) {
print 'Charge Customer Token: ' . $charge_customer->id() . "\n";
}
else {
print 'Charge Customer Error: ' . $charge_customer->error() . "\n";
}
# Charge the customer's card token $1.00
my $charge_card = Business::PinPayment->new(
config => {
api => 'charges',
api_key => $test_api_key,
card_token => $customer->card_token(),
},
);
if ($charge_card->successful()) {
print 'Charge Card Token: ' . $charge_card->id() . "\n";
}
else {
print 'Charge Card Error: ' . $charge_card->error() . "\n";
}
}
else {
print 'Customer Error: ' . $charge->error() . "\n";
}
=item C<live>
Use the live URL when set to 1.
=back
=head2 C<successful>
Returns true if the transaction is successful.
=head2 C<error>
Returns the error message.
=head2 C<response>
Returns the response of the L<HTTP::Request> object.
=head2 C<json_response>
Returns a hashref of the JSON response content.
=head2 C<id>
Returns the transaction 'token'.
=head2 C<status>
Returns the successful 'status_message', error message or code.
=head2 C<card_token>
Returns the 'card_token'.
=head1 SEE ALSO
L<HTTP::Request>, L<LWP::UserAgent>, L<https://pin.net.au>
=head1 AUTHOR
Xufeng (Danny) Liang (danny.glue@gmail.com)
=head1 COPYRIGHT & LICENSE
Copyright 2013 Xufeng (Danny) Liang, All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
=cut