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   39895 use Moo;
  2         43694  
  2         13  
5 2     2   5891 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.904'; # 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 $results_in_this_page = $xml->getChildrenByTagName('resultsInThisPage')->string_value;
108              
109             my @transactions = $results_in_this_page
110             ? $xml->getChildrenByTagName('transactions')->get_node(1)->getChildrenByTagName('transaction')
111             : ()
112             ;
113              
114             return {
115             current_page => $xml->getChildrenByTagName('currentPage')->string_value,
116             results_in_this_page => $results_in_this_page,
117             total_pages => $xml->getChildrenByTagName('totalPages')->string_value,
118             transactions => [
119             map { $self->get_transaction_details( $_ ) }
120             map { $_->getChildrenByTagName('code')->string_value } @transactions
121             ],
122             };
123             }
124              
125             sub get_transaction_details {
126             my ($self, $code) = @_;
127              
128             my $xml = $self->_load_xml_from_url(
129             $self->get_transaction_details_url( $code )
130             );
131              
132             my $result = $self->_parse_transaction($xml);
133             $result->{buyer_email} = $xml->getChildrenByTagName('sender')->get_node(1)->getChildrenByTagName('email')->string_value;
134              
135             return $result;
136             }
137              
138             sub _parse_transaction {
139             my ($self, $xml) = @_;
140              
141             my $date = $xml->getChildrenByTagName('date')->string_value;
142             my $ref = $xml->getChildrenByTagName('reference')->string_value;
143             my $status = $xml->getChildrenByTagName('status')->string_value;
144             my $amount = $xml->getChildrenByTagName('grossAmount')->string_value;
145             my $net = $xml->getChildrenByTagName('netAmount')->string_value;
146             my $fee = $xml->getChildrenByTagName('feeAmount')->string_value;
147             my $code = $xml->getChildrenByTagName('code')->string_value;
148             my $payer = $xml->getChildrenByTagName('sender')->get_node(1)->getChildrenByTagName('name')->string_value;
149              
150             return {
151             payment_id => $ref,
152             gateway_transaction_id => $code,
153             status => $self->_interpret_status($status),
154             amount => $amount,
155             date => $date,
156             net_amount => $net,
157             fee => $fee,
158             exchange_rate => 0,
159             payer => {
160             name => $payer,
161             },
162             };
163             }
164              
165             sub _load_xml_from_url {
166             my ($self, $url) = @_;
167              
168             return XML::LibXML->load_xml(
169             string => LWP::Simple::get( $url )
170             )->firstChild();
171             }
172              
173             sub _build_uri {
174             my ($self, $path, $info) = @_;
175              
176             $info ||= {};
177              
178             $info->{email} = $self->receiver_id;
179             $info->{token} = $self->token;
180              
181             my $uri = URI->new($self->base_url . $path);
182              
183             while (my ($k, $v) = each %$info) {
184             $uri->query_param($k, $v);
185             }
186              
187             return $uri->as_string;
188             }
189              
190             sub _interpret_status {
191             my ($self, $status) = @_;
192              
193             $status = int($status || 0);
194              
195             # 1: aguardando pagamento
196             # 2: em análise
197             # 3: paga
198             # 4: disponível
199             # 5: em disputa
200             # 6: devolvida
201             # 7: cancelada
202              
203             my @status_codes = ('unknown');
204             @status_codes[1,2,5] = ('processing') x 3;
205             @status_codes[3,4] = ('completed') x 2;
206             $status_codes[6] = 'refunded';
207             $status_codes[7] = 'failed';
208              
209             if ($status > 7) {
210             return 'unknown';
211             }
212              
213             return $status_codes[$status];
214             }
215              
216             sub _checkout_form_main_map {
217             return {
218             receiver_id => 'receiverEmail',
219             currency => 'currency',
220             form_encoding => 'encoding',
221             };
222             }
223              
224             sub _checkout_form_item_map {
225             my ($self, $number) = @_;
226              
227             return {
228             id => "itemId$number",
229             description => "itemDescription$number",
230             price => "itemAmount$number",
231             quantity => "itemQuantity$number",
232             weight => {
233             name => "itemWeight$number",
234             coerce => sub { $_[0] * 1000 },
235             },
236             shipping => "itemShippingCost$number"
237             };
238             }
239              
240             sub _checkout_form_buyer_map {
241             return {
242             name => 'senderName',
243             email => 'senderEmail',
244             address_complement => 'shippingAddressComplement',
245             address_district => 'shippingAddressDistrict',
246             address_street => 'shippingAddressStreet',
247             address_number => 'shippingAddressNumber',
248             address_city => 'shippingAddressCity',
249             address_state => 'shippingAddressState',
250             address_zip_code => 'shippingAddressPostalCode',
251             address_country => {
252             name => 'shippingAddressCountry',
253             coerce => sub {
254             uc(
255             Locale::Country::country_code2code(
256             $_[0], 'alpha-2', 'alpha-3'
257             )
258             )
259             },
260             },
261             };
262             }
263              
264             sub _get_hidden_inputs_for_cart {
265             my ($self, $cart) = @_;
266              
267             my $handling = $cart->handling || 0;
268             my $discount = $cart->discount || 0;
269             my $tax = $cart->tax || 0;
270              
271             my $extra_amount = $tax + $handling - $discount;
272              
273             if ($extra_amount) {
274             return ( extraAmount => sprintf( "%.2f", $extra_amount ) );
275             }
276             return ();
277             }
278              
279             sub get_hidden_inputs {
280             my ($self, $info) = @_;
281              
282             return (
283             reference => $info->{payment_id},
284              
285             $self->_get_hidden_inputs_main(),
286             $self->_get_hidden_inputs_for_buyer($info->{buyer}),
287             $self->_get_hidden_inputs_for_items($info->{items}),
288             $self->_get_hidden_inputs_for_cart($info->{cart}),
289             );
290             }
291              
292             1;
293              
294             __END__