File Coverage

blib/lib/Business/Stripe.pm
Criterion Covered Total %
statement 107 107 100.0
branch 34 34 100.0
condition 20 21 95.2
subroutine 25 25 100.0
pod 15 15 100.0
total 201 202 99.5


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;