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 |
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 |
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 |
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; |