| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Business::Stripe; |
|
2
|
|
|
|
|
|
|
|
|
3
|
4
|
|
|
4
|
|
239182
|
use strict; |
|
|
4
|
|
|
|
|
31
|
|
|
|
4
|
|
|
|
|
108
|
|
|
4
|
4
|
|
|
4
|
|
27
|
use warnings; |
|
|
4
|
|
|
|
|
6
|
|
|
|
4
|
|
|
|
|
166
|
|
|
5
|
|
|
|
|
|
|
|
|
6
|
4
|
|
|
4
|
|
2205
|
use JSON; |
|
|
4
|
|
|
|
|
44211
|
|
|
|
4
|
|
|
|
|
21
|
|
|
7
|
4
|
|
|
4
|
|
2830
|
use LWP::UserAgent; |
|
|
4
|
|
|
|
|
166394
|
|
|
|
4
|
|
|
|
|
172
|
|
|
8
|
4
|
|
|
4
|
|
2015
|
use HTTP::Request::Common qw/DELETE GET POST/; |
|
|
4
|
|
|
|
|
7967
|
|
|
|
4
|
|
|
|
|
306
|
|
|
9
|
4
|
|
|
4
|
|
1781
|
use MIME::Base64 qw(encode_base64); |
|
|
4
|
|
|
|
|
2513
|
|
|
|
4
|
|
|
|
|
287
|
|
|
10
|
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
our $VERSION = '0.07'; |
|
12
|
|
|
|
|
|
|
|
|
13
|
4
|
|
|
4
|
|
43
|
use constant URL => 'https://api.stripe.com/v1/'; |
|
|
4
|
|
|
|
|
10
|
|
|
|
4
|
|
|
|
|
5180
|
|
|
14
|
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
=encoding utf8 |
|
16
|
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
=head1 NAME |
|
18
|
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
Business::Stripe - Interface for Stripe payment system. |
|
20
|
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
22
|
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
my $stripe = Business::Stripe->new( |
|
24
|
|
|
|
|
|
|
-api_key => 'your-api-key-here', |
|
25
|
|
|
|
|
|
|
); |
|
26
|
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
## get the payment token from Stripe.js, then: |
|
28
|
|
|
|
|
|
|
$stripe->charges_create( |
|
29
|
|
|
|
|
|
|
amount => 400, |
|
30
|
|
|
|
|
|
|
source => 'tok_5EuIyKyCTc0f2V', |
|
31
|
|
|
|
|
|
|
description => 'Ice cream' |
|
32
|
|
|
|
|
|
|
) and return $stripe->success; |
|
33
|
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
say $stripe->error->{message}; |
|
35
|
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
37
|
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
This module provides common bindings for the Stripe payment system. |
|
39
|
|
|
|
|
|
|
Any API calls that do not have bindings can be accessed through the |
|
40
|
|
|
|
|
|
|
generic C method. |
|
41
|
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
=head2 General Methods |
|
43
|
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
=head3 new (I<%options>) |
|
45
|
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
Creates a new Business::Stripe object for you to use. The only |
|
47
|
|
|
|
|
|
|
B<< required argument >> is C<-api_key>, which was given to you |
|
48
|
|
|
|
|
|
|
as part of your Stripe account to access the API. |
|
49
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
Other (optional) arguments are: |
|
51
|
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
=over 4 |
|
53
|
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
=item C<-version> Sets a Stripe API version to use, overriding your |
|
55
|
|
|
|
|
|
|
account's default. You can use this to test if new versions of |
|
56
|
|
|
|
|
|
|
the API work with your code. To support marketplaces, for instance, you |
|
57
|
|
|
|
|
|
|
should use at least C<'2014-11-05'>. |
|
58
|
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
=item C<-ua_args> Hashref of options that will be passed directly as |
|
60
|
|
|
|
|
|
|
arguments to LWP::UserAgent. Example: |
|
61
|
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
my $stripe = Business::Stripe->new( |
|
63
|
|
|
|
|
|
|
-api_key => 'xxxxxxxxx', |
|
64
|
|
|
|
|
|
|
-ua_args => { |
|
65
|
|
|
|
|
|
|
timeout => 10, |
|
66
|
|
|
|
|
|
|
env_proxy => 1, |
|
67
|
|
|
|
|
|
|
agent => 'myApp', |
|
68
|
|
|
|
|
|
|
ssl_opts => { verify_hostname => 0 }, |
|
69
|
|
|
|
|
|
|
}, |
|
70
|
|
|
|
|
|
|
); |
|
71
|
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
=item C<-ua> Completely overrides the default user agent object (L). |
|
73
|
|
|
|
|
|
|
Note that your object I accept HTTPS, and provide a C method |
|
74
|
|
|
|
|
|
|
accepting L objects and returning L-compatible |
|
75
|
|
|
|
|
|
|
objects. You can use this to have a common user agent make all requests in |
|
76
|
|
|
|
|
|
|
your code. The example above works exactly like: |
|
77
|
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
my $ua = LWP::UserAgent->new( |
|
79
|
|
|
|
|
|
|
timeout => 10, |
|
80
|
|
|
|
|
|
|
env_proxy => 1, |
|
81
|
|
|
|
|
|
|
agent => 'myApp', |
|
82
|
|
|
|
|
|
|
ssl_opts => { verify_hostname => 0 }, |
|
83
|
|
|
|
|
|
|
); |
|
84
|
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
my $stripe = Business::Stripe->new( |
|
86
|
|
|
|
|
|
|
-api_key => 'xxxxxxxx', |
|
87
|
|
|
|
|
|
|
-ua => $ua, |
|
88
|
|
|
|
|
|
|
); |
|
89
|
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
=item C<-url> Overrides the default API endpoint (C) |
|
91
|
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
=item C<-stripe_account> If you use the |
|
93
|
|
|
|
|
|
|
L<< OAauth authentication flow for managed accounts|https://stripe.com/docs/connect/authentication >> |
|
94
|
|
|
|
|
|
|
You can use this argument to make operations on behalf of a managed account. |
|
95
|
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
=back |
|
97
|
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
=cut |
|
99
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
sub new { |
|
101
|
24
|
|
|
24
|
1
|
3396
|
my $class = shift; |
|
102
|
24
|
|
|
|
|
70
|
my $self = { @_ }; |
|
103
|
|
|
|
|
|
|
|
|
104
|
24
|
|
|
|
|
47
|
bless $self, $class; |
|
105
|
24
|
|
|
|
|
81
|
$self->_init; |
|
106
|
24
|
|
|
|
|
195
|
return $self; |
|
107
|
|
|
|
|
|
|
} |
|
108
|
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
=head3 api (I<$method>, I<$path>, I<%params>) |
|
110
|
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
Generic function that sends requests to Stripe. |
|
112
|
|
|
|
|
|
|
Check the L<< Stripe API Reference|https://stripe.com/docs/api >> |
|
113
|
|
|
|
|
|
|
for specific calls. |
|
114
|
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
The first argument is the HTTP method: C<"post">, C<"get"> or C<"delete">. |
|
116
|
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
The second is the target path, like "tokens", "plans", "customers" |
|
118
|
|
|
|
|
|
|
or even complex paths like "customers/$id/subscriptions". Check the |
|
119
|
|
|
|
|
|
|
Stripe API Reference for a list of all available paths. |
|
120
|
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
Use the optional third argument to send a hash of data with your API call. |
|
122
|
|
|
|
|
|
|
This is usually required on all C<"post"> calls to the API. |
|
123
|
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
On success, it returns a true value. If the returned data structure contains |
|
125
|
|
|
|
|
|
|
an C field, this is the value returned. Otherwise, "1" is returned and |
|
126
|
|
|
|
|
|
|
you should check L<< $stripe->success() | /success >> for the actual data |
|
127
|
|
|
|
|
|
|
structure. |
|
128
|
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
In case of failures, it returns false (0) and you should then check |
|
130
|
|
|
|
|
|
|
L<< $stripe->error() | /error >> for the appropriate data structure. |
|
131
|
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
Examples: |
|
133
|
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
=over 4 |
|
135
|
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
=item get a credit card source token on the server side (without using Stripe.js) |
|
137
|
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
my $token_id = $stripe->api('post', 'tokens', |
|
139
|
|
|
|
|
|
|
'card[number]' => '4242424242424242', |
|
140
|
|
|
|
|
|
|
'card[exp_month]' => 12, |
|
141
|
|
|
|
|
|
|
'card[exp_year]' => 2022, |
|
142
|
|
|
|
|
|
|
'card[cvc]' => 123 |
|
143
|
|
|
|
|
|
|
) or die $stripe->error->{message}; |
|
144
|
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
=item create a new customer (with the $token_id from above) |
|
146
|
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
my $customer = $stripe->api('post', 'customers', |
|
148
|
|
|
|
|
|
|
email => 'myuser@example.com', |
|
149
|
|
|
|
|
|
|
name => 'Jane S. Customer', |
|
150
|
|
|
|
|
|
|
description => 'Displayed alongside the customer on your dashboard', |
|
151
|
|
|
|
|
|
|
source => $token_id, |
|
152
|
|
|
|
|
|
|
) and $stripe->success; |
|
153
|
|
|
|
|
|
|
die $stripe->error unless $customer; |
|
154
|
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
=item create a new plan to subscribe your customers |
|
156
|
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
my $plan_id = $stripe->api('post', 'plans', |
|
158
|
|
|
|
|
|
|
'amount' => 999, # *IN CENTS* (999 = 9.99). Use 0 for a free plan! |
|
159
|
|
|
|
|
|
|
'id' => 'my-plan', # Optional. Must be unique in your account |
|
160
|
|
|
|
|
|
|
'currency' => 'usd', # See https://stripe.com/docs/currencies |
|
161
|
|
|
|
|
|
|
'interval' => 'month', # Also: 'day', 'week', 'year' |
|
162
|
|
|
|
|
|
|
'product[name]' => 'My Plan', |
|
163
|
|
|
|
|
|
|
) or die $stripe->error->{message}; |
|
164
|
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
=item subscribe the customer to a plan (using examples above) |
|
166
|
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
my $subscription = $stripe->api('post', 'subscriptions', |
|
168
|
|
|
|
|
|
|
'customer' => $customer->{id}, |
|
169
|
|
|
|
|
|
|
'items[0][plan]' => $plan_id, |
|
170
|
|
|
|
|
|
|
) ? $stripe->success : $stripe_error; |
|
171
|
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
=item cancel a subscription immediately |
|
173
|
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
$stripe->api('delete', "subscriptions/" . $subscription->{id}) |
|
175
|
|
|
|
|
|
|
or die "error canceling subscription: " . $stripe->error->{message}; |
|
176
|
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
=back |
|
178
|
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
As you can see, all actions can be performed by using only this method. |
|
180
|
|
|
|
|
|
|
The other methods provided by this class are just helper wrappers around this, |
|
181
|
|
|
|
|
|
|
for frequently made calls. |
|
182
|
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
=cut |
|
184
|
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
sub api { |
|
186
|
10
|
|
|
10
|
1
|
52
|
my $self = shift; |
|
187
|
10
|
|
|
|
|
13
|
my $method = shift; |
|
188
|
10
|
|
|
|
|
13
|
my $path = shift; |
|
189
|
|
|
|
|
|
|
|
|
190
|
10
|
100
|
|
|
|
33
|
if ($method eq 'post') { |
|
191
|
1
|
|
|
|
|
4
|
return $self->_compose($path, @_); |
|
192
|
|
|
|
|
|
|
} |
|
193
|
|
|
|
|
|
|
|
|
194
|
9
|
100
|
|
|
|
32
|
$method eq 'delete' or undef $method; |
|
195
|
|
|
|
|
|
|
|
|
196
|
9
|
100
|
|
|
|
31
|
if (scalar @_ >= 2) { |
|
|
|
100
|
|
|
|
|
|
|
197
|
1
|
|
|
|
|
4
|
my %params = (@_); |
|
198
|
|
|
|
|
|
|
my $qs = join '&', map { |
|
199
|
1
|
|
100
|
|
|
6
|
$_ . '=' . ($params{$_}||'') |
|
|
4
|
|
|
|
|
19
|
|
|
200
|
|
|
|
|
|
|
} sort keys %params; |
|
201
|
|
|
|
|
|
|
|
|
202
|
1
|
|
|
|
|
5
|
return $self->_compose($path.'?'.$qs, $method); |
|
203
|
|
|
|
|
|
|
} elsif (scalar @_) { |
|
204
|
|
|
|
|
|
|
### allowing api('delete','plans','gold') |
|
205
|
|
|
|
|
|
|
### for readability api('delete','plans/gold'); |
|
206
|
2
|
|
|
|
|
7
|
return $self->_compose($path.'/'.$_[0], $method); |
|
207
|
|
|
|
|
|
|
} |
|
208
|
|
|
|
|
|
|
|
|
209
|
6
|
|
|
|
|
19
|
$self->_compose($path, $method); |
|
210
|
|
|
|
|
|
|
} |
|
211
|
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
=head3 error |
|
213
|
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
All API and helper methods return C<0> when they encounter error conditions. |
|
215
|
|
|
|
|
|
|
The JSON object returned by Stripe can be retrieved via this method. |
|
216
|
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
say $stripe->error->{message}; |
|
218
|
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
Most error messages include C, C, C and C. |
|
220
|
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
=cut |
|
222
|
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
sub error { |
|
224
|
1
|
|
|
1
|
1
|
4
|
return shift->{-error}->{error}; |
|
225
|
|
|
|
|
|
|
} |
|
226
|
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
=head3 success |
|
228
|
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
All API and helper methods return either C<1> or the object's ID on success. |
|
230
|
|
|
|
|
|
|
Use this method to get access to the complete JSON object returned on the |
|
231
|
|
|
|
|
|
|
last call made by your object. See Stripe's API Documentation for details |
|
232
|
|
|
|
|
|
|
on what is returned on each call. |
|
233
|
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
say $stripe->success->{data}->[0]->{description}; |
|
235
|
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
=cut |
|
237
|
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
sub success { |
|
239
|
17
|
|
|
17
|
1
|
99
|
return shift->{-success}; |
|
240
|
|
|
|
|
|
|
} |
|
241
|
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
=head2 Charges |
|
243
|
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
Set of methods that handle credit/debit card such as charging a card, |
|
245
|
|
|
|
|
|
|
refund, retrieve specific charge and list charges. |
|
246
|
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
=head3 charges_create (I<%params>) |
|
248
|
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
my $success = $stripe->charges_create( |
|
250
|
|
|
|
|
|
|
amount => 100, # <-- amount in cents |
|
251
|
|
|
|
|
|
|
source => 'tok_Wzm6ewTBrkVvC3', |
|
252
|
|
|
|
|
|
|
description => 'customer@example.com' |
|
253
|
|
|
|
|
|
|
); |
|
254
|
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
Charges a credit card or other payment sources. This is exactly |
|
256
|
|
|
|
|
|
|
the same as C<< $stripe->api('post', 'charges', %params) >>, |
|
257
|
|
|
|
|
|
|
except that it defaults to 'usd' if you don't provide a currency. |
|
258
|
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
It returns the C of the charge on success, or 0 on error. |
|
260
|
|
|
|
|
|
|
You may also check L<< $stripe->success | /success >> or |
|
261
|
|
|
|
|
|
|
L<< $stripe->error | /error >> for the complete JSON. |
|
262
|
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
Please see Stripe's API Documentation for which parameters are |
|
264
|
|
|
|
|
|
|
accepted by your current API version. |
|
265
|
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
B The C field is in the I<< currency's smallest unit >>. |
|
267
|
|
|
|
|
|
|
For currencies that allow cents (like USD), an amount of 100 means $1.00, |
|
268
|
|
|
|
|
|
|
1000 mean $10.00 and so on. For zero-decimal currencies (like JPY) you don't |
|
269
|
|
|
|
|
|
|
have to multiply, as an amount of 100 mean ¥100. |
|
270
|
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
B Older (2015-ish) versions of Stripe's API support the C |
|
272
|
|
|
|
|
|
|
parameter containing the source token from Stripe.js. This has since |
|
273
|
|
|
|
|
|
|
been deprecated in favour of the C parameter, shown in the |
|
274
|
|
|
|
|
|
|
example above. |
|
275
|
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
=cut |
|
277
|
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
sub charges_create { |
|
279
|
2
|
|
|
2
|
1
|
15
|
my $self = shift; |
|
280
|
2
|
|
|
|
|
13
|
my %param = (@_); |
|
281
|
2
|
|
100
|
|
|
11
|
$param{currency} ||= 'usd'; |
|
282
|
|
|
|
|
|
|
|
|
283
|
2
|
|
|
|
|
21
|
return $self->_compose('charges', %param); |
|
284
|
|
|
|
|
|
|
} |
|
285
|
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
=head3 charges_retrieve (I<$id>) |
|
287
|
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
my $charge_data = $stripe->charges_retrieve('ch_uxLBSIZB8azrSr') |
|
289
|
|
|
|
|
|
|
and $stripe->success; |
|
290
|
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
Takes the charge C value and yields data about the charge, available |
|
292
|
|
|
|
|
|
|
on L<< $stripe->success | /success >>. |
|
293
|
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
This is exactly the same as C<< $stripe->api('get', "charges/$charge_id") >>. |
|
295
|
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
=cut |
|
297
|
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
sub charges_retrieve { |
|
299
|
1
|
|
|
1
|
1
|
7
|
my $self = shift; |
|
300
|
1
|
|
|
|
|
2
|
my $id = shift; |
|
301
|
1
|
|
|
|
|
5
|
return $self->_compose('charges/'.$id); |
|
302
|
|
|
|
|
|
|
} |
|
303
|
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
=head3 charges_refund (I<$id>, [I<$amount>]) |
|
305
|
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
Refunds a specific C (or if omitted, issues a full refund) |
|
307
|
|
|
|
|
|
|
to the charge C. Remember: the C parameter is I |
|
308
|
|
|
|
|
|
|
whenever the currency supports cents. |
|
309
|
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
### refunds full amount |
|
311
|
|
|
|
|
|
|
$stripe->charges_refund('ch_uxLBSIZB8azrSr') |
|
312
|
|
|
|
|
|
|
or die $stripe->error->{message}; |
|
313
|
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
### refunds $5 over a bigger charge |
|
315
|
|
|
|
|
|
|
$stripe->charges_refund('ch_uxLBSIZB8azrSr', 500) |
|
316
|
|
|
|
|
|
|
or die $stripe->error->{message}; |
|
317
|
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
=cut |
|
319
|
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
sub charges_refund { |
|
321
|
2
|
|
|
2
|
1
|
15
|
my ($self,$id,$amount) = (@_); |
|
322
|
|
|
|
|
|
|
|
|
323
|
2
|
100
|
|
|
|
16
|
return $self->_compose( |
|
324
|
|
|
|
|
|
|
'charges/'.$id.'/refunds', |
|
325
|
|
|
|
|
|
|
$amount ? (amount => $amount) : [] |
|
326
|
|
|
|
|
|
|
); |
|
327
|
|
|
|
|
|
|
} |
|
328
|
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
=head3 charges_list (I<%params>) |
|
330
|
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
List all the charges, with pagination. |
|
332
|
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
### lists next 5 charges |
|
334
|
|
|
|
|
|
|
my $charges = $stripe->charges_list(limit => 5) |
|
335
|
|
|
|
|
|
|
? $stripe->success : die $stripe->error->{message}; |
|
336
|
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
foreach my $charge (@{$charges->{data}}) { |
|
338
|
|
|
|
|
|
|
say $charge->{amount} . $charge->{currency}; |
|
339
|
|
|
|
|
|
|
} |
|
340
|
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
if ($charges->{has_more}) { |
|
342
|
|
|
|
|
|
|
say "there are more charges to show if you raise the limit" |
|
343
|
|
|
|
|
|
|
. " or change the 'starting_after' argument."; |
|
344
|
|
|
|
|
|
|
} |
|
345
|
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
Pass on the customer's ID to only get charges made to that customer: |
|
347
|
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
$stripe->charges_list(customer => 'cus_gpj0mzwbQKBI7c') |
|
349
|
|
|
|
|
|
|
or die "error fetching customer charges: " . $stripe->error; |
|
350
|
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
my $charges = $stripe->success; |
|
352
|
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
=cut |
|
354
|
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
sub charges_list { |
|
356
|
2
|
|
|
2
|
1
|
13
|
my $self = shift; |
|
357
|
2
|
|
|
|
|
9
|
my %params = (@_); |
|
358
|
|
|
|
|
|
|
my $qs = join '&', map { |
|
359
|
2
|
|
100
|
|
|
15
|
$_ . '=' . ($params{$_}||'') |
|
|
3
|
|
|
|
|
19
|
|
|
360
|
|
|
|
|
|
|
} sort keys %params; |
|
361
|
|
|
|
|
|
|
|
|
362
|
2
|
100
|
|
|
|
15
|
return $self->_compose('charges' . ($qs ? "?$qs" : '')); |
|
363
|
|
|
|
|
|
|
} |
|
364
|
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
=head2 Customers |
|
367
|
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
Some operations require you create a customer. Also, by creating a customer, |
|
369
|
|
|
|
|
|
|
you don't have to ask for credit card information on every charge. |
|
370
|
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
=head3 customers_create (I<%params>) |
|
372
|
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
Creates a new customer according to the credit card information or token given. |
|
374
|
|
|
|
|
|
|
Use this method to create a customer-ID for the given C |
|
375
|
|
|
|
|
|
|
(token when used in conjunction with Stripe.js). |
|
376
|
|
|
|
|
|
|
The customer-ID can be passed to C's C parameter |
|
377
|
|
|
|
|
|
|
instead of C so that you don't have to ask for credit card info again. |
|
378
|
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
my $customer_id = $stripe->customers_create( |
|
380
|
|
|
|
|
|
|
source => 'tok_Wzm6ewTBrkVvC3', |
|
381
|
|
|
|
|
|
|
email => 'customer@example.com', |
|
382
|
|
|
|
|
|
|
description => 'userid-123456' |
|
383
|
|
|
|
|
|
|
) or die $stripe->error; |
|
384
|
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
### charges the customer $5 |
|
386
|
|
|
|
|
|
|
$stripe->charges_create( |
|
387
|
|
|
|
|
|
|
customer => $customer_id, |
|
388
|
|
|
|
|
|
|
amount => 500, |
|
389
|
|
|
|
|
|
|
description => 'userid-123456 paid $5' |
|
390
|
|
|
|
|
|
|
); |
|
391
|
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
Returns the customer's ID if successful. As usual, you may check the |
|
393
|
|
|
|
|
|
|
full JSON object returned on L<< $stripe->success | /success >>. |
|
394
|
|
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
=cut |
|
396
|
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
sub customers_create { |
|
398
|
1
|
|
|
1
|
1
|
5
|
my $self = shift; |
|
399
|
1
|
|
|
|
|
3
|
return $self->_compose('customers', @_); |
|
400
|
|
|
|
|
|
|
} |
|
401
|
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
=head3 customers_retrieve (I<$id>) |
|
403
|
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
Gets the customer's object. Returns the id (which you already have) so |
|
405
|
|
|
|
|
|
|
make sure to fetch the actual object using L<< $stripe->success | /success >>. |
|
406
|
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
my $customer = $stripe->customers_retrieve('cus_gpj0mzwbQKBI7c') |
|
408
|
|
|
|
|
|
|
and $stripe->success; |
|
409
|
|
|
|
|
|
|
die $stripe->error unless $customer; |
|
410
|
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
=cut |
|
412
|
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
sub customers_retrieve { |
|
414
|
1
|
|
|
1
|
1
|
5
|
my $self = shift; |
|
415
|
1
|
|
|
|
|
2
|
my $id = shift; |
|
416
|
1
|
|
|
|
|
4
|
return $self->_compose('customers/'.$id); |
|
417
|
|
|
|
|
|
|
} |
|
418
|
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
=head3 customers_update (I<$id>, [I<%params>]) |
|
420
|
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
Updates customer's information. |
|
422
|
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
$stripe->customers_update('cus_gpj0mzwbQKBI7c', |
|
424
|
|
|
|
|
|
|
email => 'newemail@example.com', |
|
425
|
|
|
|
|
|
|
); |
|
426
|
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
B If you update the C of a customer, Stripe will create |
|
428
|
|
|
|
|
|
|
a source object with the new value, make it the default source, and |
|
429
|
|
|
|
|
|
|
I if it exists. If you just want to |
|
430
|
|
|
|
|
|
|
add extra sources for that customer, refer to Stripe's |
|
431
|
|
|
|
|
|
|
L<< card creation API | https://stripe.com/docs/api#create_card >>. |
|
432
|
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
=cut |
|
434
|
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
sub customers_update { |
|
436
|
1
|
|
|
1
|
1
|
4
|
my $self = shift; |
|
437
|
1
|
|
|
|
|
3
|
return $self->_compose('customers/'.(shift), @_); |
|
438
|
|
|
|
|
|
|
} |
|
439
|
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
=head3 customers_delete (I<$id>) |
|
441
|
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
Deletes the customer. |
|
443
|
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
$stripe->customers_delete('cus_gpj0mzwbQKBI7c') |
|
445
|
|
|
|
|
|
|
or die $stripe->error; |
|
446
|
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
=cut |
|
448
|
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
sub customers_delete { |
|
450
|
1
|
|
|
1
|
1
|
5
|
my $self = shift; |
|
451
|
1
|
|
|
|
|
4
|
return $self->_compose('customers/'.(shift), 'delete'); |
|
452
|
|
|
|
|
|
|
} |
|
453
|
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
=head3 customers_list (I<%params>) |
|
455
|
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
List all customers. |
|
457
|
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
$stripe->customers_list(limit => 20); |
|
459
|
|
|
|
|
|
|
|
|
460
|
|
|
|
|
|
|
=cut |
|
461
|
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
sub customers_list { |
|
463
|
2
|
|
|
2
|
1
|
8
|
my $self = shift; |
|
464
|
2
|
|
|
|
|
6
|
my %params = (@_); |
|
465
|
|
|
|
|
|
|
my $qs = join '&', map { |
|
466
|
2
|
|
100
|
|
|
8
|
$_ . '=' . ($params{$_}||'') |
|
|
3
|
|
|
|
|
14
|
|
|
467
|
|
|
|
|
|
|
} sort keys %params; |
|
468
|
|
|
|
|
|
|
|
|
469
|
2
|
100
|
|
|
|
13
|
return $self->_compose('customers' . ($qs ? "?$qs" : '')); |
|
470
|
|
|
|
|
|
|
} |
|
471
|
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
=head3 customers_subscribe (I<$id>, I<%params>) |
|
474
|
|
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
Subscribes a customer to a specified plan: |
|
476
|
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
$stripe->customers_subscribe('cus_YrUZejr9oojQjs', |
|
478
|
|
|
|
|
|
|
'items[0][plan]' => $some_plan_id, |
|
479
|
|
|
|
|
|
|
'prorate' => 'false' |
|
480
|
|
|
|
|
|
|
); |
|
481
|
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
Assuming C<$some_plan_id> is the id of a plan already created in your |
|
483
|
|
|
|
|
|
|
Stripe account. |
|
484
|
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
B pass C<'items[0][quantity]'> with a value of 2 or more to subscribe |
|
486
|
|
|
|
|
|
|
the same user to 2 or more of the same plan. It defaults to 1. |
|
487
|
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
B This method will I<< replace all your user's subscriptions >> with |
|
489
|
|
|
|
|
|
|
the new data provided. To subscribe the user to more than one plan, write: |
|
490
|
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
$stripe->api('post', 'subscriptions', |
|
492
|
|
|
|
|
|
|
'customer' => $customer_id, |
|
493
|
|
|
|
|
|
|
'items[0][plan]' => $plan_id_to_add, |
|
494
|
|
|
|
|
|
|
); |
|
495
|
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
Note that this will keep all previous billing cycles (and associated fees) |
|
497
|
|
|
|
|
|
|
for any other subscription already present and add a new billing cycle (and fee) |
|
498
|
|
|
|
|
|
|
for this new one. If you want to subscribe the customer to more than one plan |
|
499
|
|
|
|
|
|
|
I<< with a single billing cycle >>, pass each plan as a separate item: |
|
500
|
|
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
$stripe->customers_subscribe('cus_YrUZejr9oojQjs', |
|
502
|
|
|
|
|
|
|
'items[0][plan]' => $some_plan_id, |
|
503
|
|
|
|
|
|
|
'items[1][plan]' => $other_plan_id, |
|
504
|
|
|
|
|
|
|
) or die "error subscribing customer: " . $stripe->error->{message}; |
|
505
|
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
=cut |
|
507
|
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
sub customers_subscribe { |
|
509
|
1
|
|
|
1
|
1
|
5
|
my $self = shift; |
|
510
|
1
|
|
|
|
|
2
|
my $id = shift; |
|
511
|
1
|
|
|
|
|
5
|
return $self->_compose("customers/$id/subscriptions", @_); |
|
512
|
|
|
|
|
|
|
} |
|
513
|
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
=head3 customers_unsubscribe (I<$id>) |
|
516
|
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
Immediately unsubscribe the customer from all currently subscribed plans. |
|
518
|
|
|
|
|
|
|
Useful for terminating accounts (or paid subscriptions). |
|
519
|
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
NOTE: As per Stripe's documentation, any pending invoice items that you’ve |
|
521
|
|
|
|
|
|
|
created will still be charged for at the end of the period, unless manually |
|
522
|
|
|
|
|
|
|
deleted. If you’ve set the subscription to cancel at the end of the period, |
|
523
|
|
|
|
|
|
|
any pending prorations will also be left in place and collected at the end |
|
524
|
|
|
|
|
|
|
of the period. But if the subscription is set to cancel immediately, pending |
|
525
|
|
|
|
|
|
|
prorations will be removed. |
|
526
|
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
$stripe->customers_unsubscribe('cus_YrUZejr9oojQjs') |
|
528
|
|
|
|
|
|
|
or die "error unsubscribing customer: " . $stripe->error->{message}; |
|
529
|
|
|
|
|
|
|
|
|
530
|
|
|
|
|
|
|
=cut |
|
531
|
|
|
|
|
|
|
|
|
532
|
|
|
|
|
|
|
sub customers_unsubscribe { |
|
533
|
1
|
|
|
1
|
1
|
4
|
my $self = shift; |
|
534
|
1
|
|
|
|
|
2
|
my $id = shift; |
|
535
|
1
|
|
|
|
|
4
|
return $self->_compose("customers/$id/subscriptions", 'delete'); |
|
536
|
|
|
|
|
|
|
} |
|
537
|
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
sub _init { |
|
540
|
24
|
|
|
24
|
|
42
|
my $self = shift; |
|
541
|
|
|
|
|
|
|
|
|
542
|
24
|
|
100
|
|
|
144
|
$self->{-url} ||= URL; |
|
543
|
|
|
|
|
|
|
$self->{-api_key} and |
|
544
|
24
|
100
|
|
|
|
152
|
$self->{-auth} = 'Basic ' . encode_base64($self->{-api_key}) . ':'; |
|
545
|
24
|
100
|
|
|
|
84
|
if (!$self->{-ua}) { |
|
546
|
|
|
|
|
|
|
$self->{-ua} = LWP::UserAgent->new( |
|
547
|
4
|
100
|
|
|
|
19
|
(ref $self->{-ua_args} eq 'HASH' ? %{$self->{-ua_args}} : ()) |
|
|
1
|
|
|
|
|
5
|
|
|
548
|
|
|
|
|
|
|
); |
|
549
|
|
|
|
|
|
|
} |
|
550
|
24
|
|
|
|
|
2951
|
return; |
|
551
|
|
|
|
|
|
|
} |
|
552
|
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
sub _compose { |
|
554
|
25
|
|
|
25
|
|
46
|
my $self = shift; |
|
555
|
25
|
|
|
|
|
44
|
my $resource = shift; |
|
556
|
|
|
|
|
|
|
|
|
557
|
25
|
100
|
|
|
|
86
|
return undef unless $self->{-auth}; |
|
558
|
|
|
|
|
|
|
|
|
559
|
|
|
|
|
|
|
# reset |
|
560
|
24
|
|
|
|
|
48
|
undef $self->{-success}; |
|
561
|
24
|
|
|
|
|
43
|
undef $self->{-error}; |
|
562
|
|
|
|
|
|
|
|
|
563
|
24
|
|
|
|
|
37
|
my $res = undef; |
|
564
|
24
|
|
|
|
|
53
|
my $url = $self->{-url} . $resource; |
|
565
|
|
|
|
|
|
|
|
|
566
|
24
|
|
|
|
|
56
|
my @headers = $self->_fetch_headers; |
|
567
|
|
|
|
|
|
|
|
|
568
|
24
|
100
|
100
|
|
|
166
|
if ($_[0] and $_[0] eq 'delete') { |
|
|
|
100
|
100
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
569
|
|
|
|
|
|
|
$res = $self->{-ua}->request( |
|
570
|
4
|
|
|
|
|
17
|
DELETE $url, @headers |
|
571
|
|
|
|
|
|
|
); |
|
572
|
|
|
|
|
|
|
} elsif (scalar @_ > 1 || (@_ == 1 && ref $_[0] eq 'ARRAY')) { |
|
573
|
|
|
|
|
|
|
$res = $self->{-ua}->request( |
|
574
|
8
|
100
|
|
|
|
57
|
POST $url, @headers, Content => [ @_ == 1 ? @{$_[0]} : @_ ] |
|
|
1
|
|
|
|
|
6
|
|
|
575
|
|
|
|
|
|
|
); |
|
576
|
|
|
|
|
|
|
} else { |
|
577
|
|
|
|
|
|
|
$res = $self->{-ua}->request( |
|
578
|
12
|
|
|
|
|
58
|
GET $url, @headers |
|
579
|
|
|
|
|
|
|
); |
|
580
|
|
|
|
|
|
|
} |
|
581
|
|
|
|
|
|
|
|
|
582
|
24
|
100
|
|
|
|
75561
|
if ($res->is_success) { |
|
583
|
23
|
|
|
|
|
135
|
$self->{-success} = decode_json($res->content); |
|
584
|
23
|
|
100
|
|
|
444
|
return $self->{-success}->{id} || 1; |
|
585
|
|
|
|
|
|
|
} |
|
586
|
|
|
|
|
|
|
|
|
587
|
1
|
|
|
|
|
5
|
$self->{-error} = decode_json($res->content); |
|
588
|
1
|
|
|
|
|
12
|
return 0; |
|
589
|
|
|
|
|
|
|
} |
|
590
|
|
|
|
|
|
|
|
|
591
|
|
|
|
|
|
|
sub _fetch_headers { |
|
592
|
24
|
|
|
24
|
|
37
|
my $self = shift; |
|
593
|
24
|
|
|
|
|
67
|
my %headers = ( Authorization => $self->{-auth} ); |
|
594
|
|
|
|
|
|
|
|
|
595
|
24
|
100
|
|
|
|
73
|
if ($self->{-version}) { |
|
596
|
1
|
|
|
|
|
4
|
$headers{'Stripe-Version'} = $self->{-version}; |
|
597
|
|
|
|
|
|
|
} |
|
598
|
24
|
100
|
|
|
|
56
|
if ($self->{-stripe_account}) { |
|
599
|
|
|
|
|
|
|
# for managed 'oauth' accounts. |
|
600
|
|
|
|
|
|
|
# https://stripe.com/docs/connect/authentication |
|
601
|
1
|
|
|
|
|
2
|
$headers{'Stripe-Account'} = $self->{-stripe_account}; |
|
602
|
|
|
|
|
|
|
} |
|
603
|
24
|
|
|
|
|
79
|
return %headers; |
|
604
|
|
|
|
|
|
|
} |
|
605
|
|
|
|
|
|
|
|
|
606
|
|
|
|
|
|
|
|
|
607
|
|
|
|
|
|
|
=head1 REPOSITORY |
|
608
|
|
|
|
|
|
|
|
|
609
|
|
|
|
|
|
|
L |
|
610
|
|
|
|
|
|
|
|
|
611
|
|
|
|
|
|
|
=head1 SEE ALSO |
|
612
|
|
|
|
|
|
|
|
|
613
|
|
|
|
|
|
|
Stripe.js Documentation L. |
|
614
|
|
|
|
|
|
|
|
|
615
|
|
|
|
|
|
|
Stripe Full API Reference L. |
|
616
|
|
|
|
|
|
|
|
|
617
|
|
|
|
|
|
|
Full featured implementation by Luke Closs L. |
|
618
|
|
|
|
|
|
|
|
|
619
|
|
|
|
|
|
|
=head1 SINGLE FILE INSTALLATION |
|
620
|
|
|
|
|
|
|
|
|
621
|
|
|
|
|
|
|
This module is implemented as a single-file package. |
|
622
|
|
|
|
|
|
|
If you don't want to use the CPAN distribution, you can download C |
|
623
|
|
|
|
|
|
|
from the root directory and renamed it to C: |
|
624
|
|
|
|
|
|
|
|
|
625
|
|
|
|
|
|
|
mv Stripe.pm BusinessStripe.pm |
|
626
|
|
|
|
|
|
|
|
|
627
|
|
|
|
|
|
|
Edit C and remove the C<::> between the package name on |
|
628
|
|
|
|
|
|
|
the first line to: |
|
629
|
|
|
|
|
|
|
|
|
630
|
|
|
|
|
|
|
package BusinessStripe; |
|
631
|
|
|
|
|
|
|
|
|
632
|
|
|
|
|
|
|
Include the file in your program: |
|
633
|
|
|
|
|
|
|
|
|
634
|
|
|
|
|
|
|
use BusinessStripe; |
|
635
|
|
|
|
|
|
|
my $stripe = BusinessStripe->new( |
|
636
|
|
|
|
|
|
|
-api_key => 'c6EiNIusHip8x5hkdIjtur7KNUA3TTpE', |
|
637
|
|
|
|
|
|
|
-env_proxy => 1, |
|
638
|
|
|
|
|
|
|
); |
|
639
|
|
|
|
|
|
|
$stripe->charges_list; |
|
640
|
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
=head1 AUTHOR |
|
642
|
|
|
|
|
|
|
|
|
643
|
|
|
|
|
|
|
Paul Pham (@phamnp) |
|
644
|
|
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
|
646
|
|
|
|
|
|
|
|
|
647
|
|
|
|
|
|
|
Copyright (C) 2012-2019 Aquaron. All Rights Reserved. |
|
648
|
|
|
|
|
|
|
|
|
649
|
|
|
|
|
|
|
This program and library is free software; |
|
650
|
|
|
|
|
|
|
you can redistribute it and/or modify it under the same terms as Perl itself. |
|
651
|
|
|
|
|
|
|
|
|
652
|
|
|
|
|
|
|
=cut |
|
653
|
|
|
|
|
|
|
1; |