File Coverage

blib/lib/WWW/PayPal/API/Orders.pm
Criterion Covered Total %
statement 62 74 83.7
branch 27 56 48.2
condition 8 25 32.0
subroutine 8 11 72.7
pod 5 5 100.0
total 110 171 64.3


line stmt bran cond sub pod time code
1             package WWW::PayPal::API::Orders;
2              
3             # ABSTRACT: PayPal Checkout / Orders v2 API
4              
5 2     2   14 use Moo;
  2         11  
  2         16  
6 2     2   624 use Carp qw(croak);
  2         14  
  2         79  
7 2     2   1044 use WWW::PayPal::Order;
  2         5  
  2         63  
8 2     2   12 use namespace::clean;
  2         3  
  2         14  
9              
10             our $VERSION = '0.002';
11              
12              
13             has client => (
14             is => 'ro',
15             required => 1,
16             weak_ref => 1,
17             );
18              
19              
20             has openapi_operations => (
21             is => 'lazy',
22             builder => sub {
23             # Pre-computed from paypal-rest-api-specifications
24             # (openapi/checkout_orders_v2.json). Regenerate manually when the
25             # spec changes.
26             return {
27 1     1   25 'orders.create' => { method => 'POST', path => '/v2/checkout/orders' },
28             'orders.get' => { method => 'GET', path => '/v2/checkout/orders/{id}' },
29             'orders.patch' => { method => 'PATCH', path => '/v2/checkout/orders/{id}' },
30             'orders.confirm' => { method => 'POST', path => '/v2/checkout/orders/{id}/confirm-payment-source' },
31             'orders.authorize' => { method => 'POST', path => '/v2/checkout/orders/{id}/authorize' },
32             'orders.capture' => { method => 'POST', path => '/v2/checkout/orders/{id}/capture' },
33             };
34             },
35             );
36              
37              
38             with 'WWW::PayPal::Role::OpenAPI';
39              
40             sub _wrap {
41 1     1   2 my ($self, $data) = @_;
42 1         30 return WWW::PayPal::Order->new(client => $self->client, data => $data);
43             }
44              
45             sub checkout {
46 2     2 1 901 my ($self, %args) = @_;
47              
48 2 100       262 croak 'amount required' unless defined $args{amount};
49 1 50       5 croak 'currency required' unless defined $args{currency};
50 1 50       5 croak 'return_url required' unless defined $args{return_url};
51 1 50       5 croak 'cancel_url required' unless defined $args{cancel_url};
52              
53 1         3 my $currency = $args{currency};
54 1         2 my $amount = $args{amount};
55              
56 1         5 my $pu = {
57             amount => { currency_code => $currency, value => $amount },
58             };
59 1 50       6 $pu->{description} = $args{description} if defined $args{description};
60 1 50       11 $pu->{invoice_id} = $args{invoice_id} if defined $args{invoice_id};
61 1 50       5 $pu->{custom_id} = $args{custom_id} if defined $args{custom_id};
62 1 50       4 $pu->{soft_descriptor} = $args{soft_descriptor} if defined $args{soft_descriptor};
63 1 50       4 $pu->{reference_id} = $args{reference_id} if defined $args{reference_id};
64              
65 1 50 33     6 if ($args{items} && @{ $args{items} }) {
  1         6  
66 1         2 my @items;
67 1         2 my $item_total = 0;
68 1         3 for my $i (@{ $args{items} }) {
  1         3  
69             my $unit = $i->{unit_amount} // $i->{price}
70 1 50 33     7 or croak 'item unit_amount required';
71 1   50     3 my $qty = $i->{quantity} // 1;
72             push @items, {
73             name => $i->{name} // croak('item name required'),
74             quantity => "$qty",
75             unit_amount => {
76             currency_code => $i->{currency_code} // $currency,
77             value => "$unit",
78             },
79             (defined $i->{sku} ? (sku => $i->{sku}) : ()),
80             (defined $i->{description} ? (description => $i->{description}) : ()),
81 1 50 33     18 (defined $i->{category} ? (category => $i->{category}) : ()),
    50 33        
    50          
82             };
83 1         9 $item_total += $unit * $qty;
84             }
85 1         3 $pu->{items} = \@items;
86             # PayPal requires a breakdown when items are present
87             $pu->{amount}{breakdown} = {
88 1         15 item_total => {
89             currency_code => $currency,
90             value => sprintf('%.2f', $item_total),
91             },
92             };
93             }
94              
95 1 50       5 $pu->{shipping} = $args{shipping} if $args{shipping};
96              
97             my %create = (
98             intent => $args{intent} // 'CAPTURE',
99             purchase_units => [$pu],
100             return_url => $args{return_url},
101             cancel_url => $args{cancel_url},
102 1   50     10 );
103 1         4 for my $k (qw(brand_name locale user_action shipping_preference payer)) {
104 5 100       16 $create{$k} = $args{$k} if defined $args{$k};
105             }
106              
107 1         33 return $self->create(%create);
108             }
109              
110              
111             sub create {
112 1     1 1 6 my ($self, %args) = @_;
113              
114 1 50       4 croak 'intent required' unless $args{intent};
115             croak 'purchase_units required'
116 1 50 33     4 unless ref $args{purchase_units} eq 'ARRAY' && @{$args{purchase_units}};
  1         3  
117              
118             my $body = {
119             intent => $args{intent},
120             purchase_units => $args{purchase_units},
121 1         3 };
122              
123             # Application context: return/cancel URLs and branding
124 1         1 my %ctx;
125 1 50       3 $ctx{return_url} = $args{return_url} if $args{return_url};
126 1 50       3 $ctx{cancel_url} = $args{cancel_url} if $args{cancel_url};
127 1 50       2 $ctx{brand_name} = $args{brand_name} if $args{brand_name};
128 1 50       3 $ctx{locale} = $args{locale} if $args{locale};
129 1   50     5 $ctx{user_action} = $args{user_action} // 'PAY_NOW';
130             $ctx{shipping_preference} = $args{shipping_preference}
131 1 50       2 if $args{shipping_preference};
132 1 50       2 if (%ctx) {
133             # PayPal supports both payment_source.paypal.experience_context (new)
134             # and application_context (legacy). We use application_context for
135             # broad compatibility.
136 1         3 $body->{application_context} = \%ctx;
137             }
138              
139 1 50       23 $body->{payer} = $args{payer} if $args{payer};
140              
141 1         6 my $data = $self->call_operation('orders.create', body => $body);
142 1         15 return $self->_wrap($data);
143             }
144              
145              
146             sub get {
147 0     0 1   my ($self, $id) = @_;
148 0 0         croak 'order id required' unless $id;
149 0           my $data = $self->call_operation('orders.get', path => { id => $id });
150 0           return $self->_wrap($data);
151             }
152              
153              
154             sub capture {
155 0     0 1   my ($self, $id, %args) = @_;
156 0 0         croak 'order id required' unless $id;
157              
158             my $data = $self->call_operation('orders.capture',
159             path => { id => $id },
160             body => $args{body} || {},
161 0   0       );
162 0           return $self->_wrap($data);
163             }
164              
165              
166             sub authorize {
167 0     0 1   my ($self, $id, %args) = @_;
168 0 0         croak 'order id required' unless $id;
169             my $data = $self->call_operation('orders.authorize',
170             path => { id => $id },
171             body => $args{body} || {},
172 0   0       );
173 0           return $self->_wrap($data);
174             }
175              
176              
177             1;
178              
179             __END__