File Coverage

blib/lib/Business/CPI/Gateway/PagSeguro.pm
Criterion Covered Total %
statement 4 6 66.6
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 6 8 75.0


line stmt bran cond sub pod time code
1             package Business::CPI::Gateway::PagSeguro;
2             # ABSTRACT: Business::CPI's PagSeguro driver
3              
4 2     2   34920 use Moo;
  2         44694  
  2         14  
5 2     2   6428 use XML::LibXML;
  0            
  0            
6             use Carp;
7             use LWP::Simple ();
8             use URI;
9             use URI::QueryParam;
10             use DateTime;
11             use Locale::Country ();
12             use Data::Dumper;
13              
14             extends 'Business::CPI::Gateway::Base';
15             with 'Business::CPI::Role::Gateway::FormCheckout';
16              
17             our $VERSION = '0.903'; # VERSION
18              
19             has '+checkout_url' => (
20             default => sub { 'https://pagseguro.uol.com.br/v2/checkout/payment.html' },
21             );
22              
23             has '+currency' => (
24             default => sub { 'BRL' },
25             );
26              
27             has base_url => (
28             is => 'ro',
29             default => sub { 'https://ws.pagseguro.uol.com.br/v2' },
30             );
31              
32             has token => (
33             is => 'ro',
34             );
35              
36             sub get_notifications_url {
37             my ($self, $code) = @_;
38              
39             return $self->_build_uri("/transactions/notifications/$code");
40             }
41              
42             sub get_transaction_details_url {
43             my ($self, $code) = @_;
44              
45             return $self->_build_uri("/transactions/$code");
46             }
47              
48             sub get_transaction_query_url {
49             my ($self, $info) = @_;
50              
51             $info ||= {};
52              
53             my $final_date = $info->{final_date} || DateTime->now(time_zone => 'local'); # XXX: really local?
54             my $initial_date = $info->{initial_date} || $final_date->clone->subtract(days => 30);
55              
56             my $new_info = {
57             initialDate => $initial_date->strftime('%Y-%m-%dT%H:%M'),
58             finalDate => $final_date->strftime('%Y-%m-%dT%H:%M'),
59             page => $info->{page} || 1,
60             maxPageResults => $info->{rows} || 1000,
61             };
62              
63             return $self->_build_uri('/transactions', $new_info);
64             }
65              
66             sub query_transactions { goto \&get_and_parse_transactions }
67              
68             sub get_and_parse_notification {
69             my ($self, $code) = @_;
70              
71             my $xml = $self->_load_xml_from_url(
72             $self->get_notifications_url($code)
73             );
74              
75             if ($self->log->is_debug) {
76             $self->log->debug("The notification we received was:\n" . Dumper($xml));
77             }
78              
79             return $self->_parse_transaction($xml);
80             }
81              
82             sub notify {
83             my ($self, $req) = @_;
84              
85             if ($req->params->{notificationType} eq 'transaction') {
86             my $code = $req->params->{notificationCode};
87              
88             $self->log->info("Received notification for $code");
89              
90             my $result = $self->get_and_parse_notification( $code );
91              
92             if ($self->log->is_debug) {
93             $self->log->debug("The notification we're returning is " . Dumper($result));
94             }
95              
96             return $result;
97             }
98             }
99              
100             sub get_and_parse_transactions {
101             my ($self, $info) = @_;
102              
103             my $xml = $self->_load_xml_from_url(
104             $self->get_transaction_query_url( $info )
105             );
106              
107             my @transactions = $xml->getChildrenByTagName('transactions')->get_node(1)->getChildrenByTagName('transaction');
108              
109             return {
110             current_page => $xml->getChildrenByTagName('currentPage')->string_value,
111             results_in_this_page => $xml->getChildrenByTagName('resultsInThisPage')->string_value,
112             total_pages => $xml->getChildrenByTagName('totalPages')->string_value,
113             transactions => [
114             map { $self->get_transaction_details( $_ ) }
115             map { $_->getChildrenByTagName('code')->string_value } @transactions
116             ],
117             };
118             }
119              
120             sub get_transaction_details {
121             my ($self, $code) = @_;
122              
123             my $xml = $self->_load_xml_from_url(
124             $self->get_transaction_details_url( $code )
125             );
126              
127             my $result = $self->_parse_transaction($xml);
128             $result->{buyer_email} = $xml->getChildrenByTagName('sender')->get_node(1)->getChildrenByTagName('email')->string_value;
129              
130             return $result;
131             }
132              
133             sub _parse_transaction {
134             my ($self, $xml) = @_;
135              
136             my $date = $xml->getChildrenByTagName('date')->string_value;
137             my $ref = $xml->getChildrenByTagName('reference')->string_value;
138             my $status = $xml->getChildrenByTagName('status')->string_value;
139             my $amount = $xml->getChildrenByTagName('grossAmount')->string_value;
140             my $net = $xml->getChildrenByTagName('netAmount')->string_value;
141             my $fee = $xml->getChildrenByTagName('feeAmount')->string_value;
142             my $code = $xml->getChildrenByTagName('code')->string_value;
143             my $payer = $xml->getChildrenByTagName('sender')->get_node(1)->getChildrenByTagName('name')->string_value;
144              
145             return {
146             payment_id => $ref,
147             gateway_transaction_id => $code,
148             status => $self->_interpret_status($status),
149             amount => $amount,
150             date => $date,
151             net_amount => $net,
152             fee => $fee,
153             exchange_rate => 0,
154             payer => {
155             name => $payer,
156             },
157             };
158             }
159              
160             sub _load_xml_from_url {
161             my ($self, $url) = @_;
162              
163             return XML::LibXML->load_xml(
164             string => LWP::Simple::get( $url )
165             )->firstChild();
166             }
167              
168             sub _build_uri {
169             my ($self, $path, $info) = @_;
170              
171             $info ||= {};
172              
173             $info->{email} = $self->receiver_id;
174             $info->{token} = $self->token;
175              
176             my $uri = URI->new($self->base_url . $path);
177              
178             while (my ($k, $v) = each %$info) {
179             $uri->query_param($k, $v);
180             }
181              
182             return $uri->as_string;
183             }
184              
185             sub _interpret_status {
186             my ($self, $status) = @_;
187              
188             $status = int($status || 0);
189              
190             # 1: aguardando pagamento
191             # 2: em análise
192             # 3: paga
193             # 4: disponível
194             # 5: em disputa
195             # 6: devolvida
196             # 7: cancelada
197              
198             my @status_codes = ('unknown');
199             @status_codes[1,2,5] = ('processing') x 3;
200             @status_codes[3,4] = ('completed') x 2;
201             $status_codes[6] = 'refunded';
202             $status_codes[7] = 'failed';
203              
204             if ($status > 7) {
205             return 'unknown';
206             }
207              
208             return $status_codes[$status];
209             }
210              
211             sub _checkout_form_main_map {
212             return {
213             receiver_id => 'receiverEmail',
214             currency => 'currency',
215             form_encoding => 'encoding',
216             };
217             }
218              
219             sub _checkout_form_item_map {
220             my ($self, $number) = @_;
221              
222             return {
223             id => "itemId$number",
224             description => "itemDescription$number",
225             price => "itemAmount$number",
226             quantity => "itemQuantity$number",
227             weight => {
228             name => "itemWeight$number",
229             coerce => sub { $_[0] * 1000 },
230             },
231             shipping => "itemShippingCost$number"
232             };
233             }
234              
235             sub _checkout_form_buyer_map {
236             return {
237             name => 'senderName',
238             email => 'senderEmail',
239             address_complement => 'shippingAddressComplement',
240             address_district => 'shippingAddressDistrict',
241             address_street => 'shippingAddressStreet',
242             address_number => 'shippingAddressNumber',
243             address_city => 'shippingAddressCity',
244             address_state => 'shippingAddressState',
245             address_zip_code => 'shippingAddressPostalCode',
246             address_country => {
247             name => 'shippingAddressCountry',
248             coerce => sub {
249             uc(
250             Locale::Country::country_code2code(
251             $_[0], 'alpha-2', 'alpha-3'
252             )
253             )
254             },
255             },
256             };
257             }
258              
259             sub _get_hidden_inputs_for_cart {
260             my ($self, $cart) = @_;
261              
262             my $handling = $cart->handling || 0;
263             my $discount = $cart->discount || 0;
264             my $tax = $cart->tax || 0;
265              
266             my $extra_amount = $tax + $handling - $discount;
267              
268             if ($extra_amount) {
269             return ( extraAmount => sprintf( "%.2f", $extra_amount ) );
270             }
271             return ();
272             }
273              
274             sub get_hidden_inputs {
275             my ($self, $info) = @_;
276              
277             return (
278             reference => $info->{payment_id},
279              
280             $self->_get_hidden_inputs_main(),
281             $self->_get_hidden_inputs_for_buyer($info->{buyer}),
282             $self->_get_hidden_inputs_for_items($info->{items}),
283             $self->_get_hidden_inputs_for_cart($info->{cart}),
284             );
285             }
286              
287             1;
288              
289             __END__