File Coverage

blib/lib/Dancer/Plugin/Interchange6.pm
Criterion Covered Total %
statement 27 82 32.9
branch 0 16 0.0
condition 0 9 0.0
subroutine 9 25 36.0
pod n/a
total 36 132 27.2


line stmt bran cond sub pod time code
1             package Dancer::Plugin::Interchange6;
2              
3 1     1   20893 use strict;
  1         2  
  1         25  
4 1     1   4 use warnings;
  1         1  
  1         30  
5              
6 1     1   1056 use Dancer qw(:syntax !before !after);
  1         260150  
  1         4  
7 1     1   1233 use Dancer::Plugin;
  1         1208  
  1         78  
8 1     1   684 use Dancer::Plugin::DBIC;
  1         38168  
  1         83  
9 1     1   871 use Dancer::Plugin::Auth::Extensible;
  1         6615  
  1         116  
10              
11 1     1   733 use Dancer::Plugin::Interchange6::Cart;
  1         4  
  1         36  
12 1     1   835 use Dancer::Plugin::Interchange6::Business::OnlinePayment;
  1         5  
  1         35  
13              
14 1     1   720 use Class::Load ':all';
  1         9779  
  1         1253  
15              
16             =head1 NAME
17              
18             Dancer::Plugin::Interchange6 - Interchange6 Shop Plugin for Dancer
19              
20             =head1 VERSION
21              
22             Version 0.100
23              
24             =cut
25              
26             our $VERSION = '0.100';
27              
28             =head1 REQUIREMENTS
29              
30             All Interchange6 Dancer applications need to use the L<Dancer::Session::DBIC>
31             engine.
32              
33             The easiest way to configure this is in your main module, just after all
34             the C<use> statements:
35              
36             set session => 'DBIC';
37             set session_options => {schema => schema};
38              
39             =head1 CONFIGURATION
40              
41             Available configuration options:
42              
43             plugins:
44             Interchange6:
45             cart_class: MyApp::Cart
46             carts_var_name: some_other_var
47              
48             =over
49              
50             =item * cart_class
51              
52             If you wish to subclass the cart you can have L</shop_cart> return your
53             subclassed cart instead. You set the cart class via C<cart_class>.
54             Defaults to L<Dancer::Plugin::Interchange6::Cart>.
55              
56             =item * carts_var_name
57              
58             The plugin caches carts in a L<Dancer/var> and the name of the var used can
59             be set via C<carts_var_name>. Defaults to C<ic6_carts>.
60              
61             =back
62              
63             =head1 ROUTES
64              
65             You can use the L<Dancer::Plugin::Interchange6::Routes> plugin bundled with this
66             plugin to setup standard routes for:
67              
68             =over 4
69              
70             =item product listing
71              
72             =item product display
73              
74             =item cart display
75              
76             =item checkout form
77              
78             =back
79              
80             To enable these routes, you put the C<shop_setup_routes> keyword at the end
81             of your main module:
82              
83             package MyShop;
84              
85             use Dancer ':syntax';
86             use Dancer::Plugin::Interchange6;
87             use Dancer::Plugin::Interchange6::Routes;
88              
89             get '/shop' => sub {
90             ...
91             };
92              
93             ...
94              
95             shop_setup_routes;
96              
97             true;
98              
99             Please refer to L<Dancer::Plugin::Interchange6::Routes> for configuration options
100             and further information.
101              
102             =head1 KEYWORDS
103              
104             =head2 shop_cart
105              
106             Returns L<Dancer::Plugin::Interchange6::Cart> object.
107              
108              
109             =head2 shop_charge
110              
111             Creates payment order and authorizes amount.
112              
113             =head2 shop_redirect
114              
115             Calls L<Interchange6::Schema::ResultSet::UriRedirect/redirect> with given args.
116              
117             =head2 shop_schema
118              
119             Returns L<Interchange6::Schema> object.
120              
121             =head2 shop_...
122              
123             Accessors for L<Interchange6::Schema> result classes. You can use it
124             to retrieve a single object or the corresponding result set.
125              
126             shop_product('F0001')->uri;
127              
128             shop_navigation->search({type => 'manufacturer',
129             active => 1});
130              
131             Available accessors are C<shop_address>, C<shop_attribute>, C<shop_country>,
132             C<shop_message>, C<shop_navigation>, C<shop_order>, C<shop_product>,
133             C<shop_state> and C<shop_user>.
134              
135             =head1 HOOKS
136              
137             This plugin installs the following hooks:
138              
139             =head2 Add to cart
140              
141             The functions registered for these hooks receive the cart object
142             and the item to be added as parameters.
143              
144             =over 4
145              
146             =item before_cart_add_validate
147              
148             Triggered before item is validated for adding to the cart.
149              
150             =item before_cart_add
151              
152             Triggered before item is added to the cart.
153              
154             =item after_cart_add
155              
156             Triggered after item is added to the cart.
157             Used by DBI backend to save item to the database.
158              
159             =back
160              
161             =head2 Update cart
162              
163             The functions registered for these hooks receive the cart object,
164             the current item in the cart and the updated item.
165              
166             =over 4
167              
168             =item before_cart_update
169              
170             Triggered before cart item is updated (changing quantity).
171              
172             =item after_cart_update
173              
174             Triggered after cart item is updated (changing quantity).
175             Used by DBI backend to update item to the database.
176              
177             =back
178              
179             =head2 Remove from cart
180              
181             The functions registered for these hooks receive the cart object
182             and the item to be added as parameters.
183              
184             =over 4
185              
186             =item before_cart_remove_validate
187              
188             Triggered before item is validated for removal.
189             Receives cart object and item SKU.
190              
191             =item before_cart_remove
192              
193             Triggered before item is removed from the cart.
194             Receives cart object and item.
195              
196             =item after_cart_remove
197              
198             Triggered after item is removed from the cart.
199             Used by DBI backend to delete item from the database.
200             Receives cart object and item.
201              
202             =back
203              
204             =head2 Clear cart
205              
206             =over 4
207              
208             =item before_cart_clear
209              
210             Triggered before cart is cleared.
211              
212             =item after_cart_clear
213              
214             Triggered after cart is cleared.
215              
216             =back
217              
218             =head2 Rename cart
219              
220             The functions registered for these hooks receive the cart object,
221             the old name and the new name.
222              
223             =over 4
224              
225             =item before_cart_rename
226              
227             Triggered before cart is renamed.
228              
229             =item after_cart_rename
230              
231             Triggered after cart is renamed.
232              
233             =item before_cart_set_users_id
234              
235             Triggered before users_id is set for the cart.
236              
237             =item after_cart_set_users_id
238              
239             Triggered after users_id is set for the cart.
240              
241             =item before_cart_set_sessions_id
242              
243             Triggered before sessions_id is set for the cart.
244              
245             =item after_cart_set_sessions_id
246              
247             Triggered after sessions_id is set for the cart.
248              
249             =back
250              
251             =head1 EXPIRE DBIC SESSIONS
252              
253             This command expires/manages DBIC sessions and carts. NOTE: For proper
254             functionality please copy/link to Dancer App/bin directory.
255              
256             interchange6-expire-sessions
257              
258             =cut
259              
260             register_hook(qw/before_cart_add_validate
261             before_cart_add after_cart_add
262             before_cart_update after_cart_update
263             before_cart_remove_validate
264             before_cart_remove after_cart_remove
265             before_cart_rename after_cart_rename
266             before_cart_clear after_cart_clear
267             before_cart_set_users_id after_cart_set_users_id
268             before_cart_set_sessions_id after_cart_set_sessions_id
269             /);
270              
271             register shop_schema => sub {
272 0     0     _shop_schema(@_);
273             };
274              
275             register shop_address => sub {
276 0     0     _shop_resultset('Address', @_);
277             };
278              
279             register shop_attribute => sub {
280 0     0     _shop_resultset('Attribute', @_);
281             };
282              
283             register shop_country => sub {
284 0     0     _shop_resultset('Country', @_);
285             };
286              
287             register shop_message => sub {
288 0     0     _shop_resultset('Message', @_);
289             };
290              
291             register shop_navigation => sub {
292 0     0     _shop_resultset('Navigation', @_);
293             };
294              
295             register shop_order => sub {
296 0     0     _shop_resultset('Order', @_);
297             };
298              
299             register shop_product => sub {
300 0     0     _shop_resultset('Product', @_);
301             };
302              
303             register shop_state => sub {
304 0     0     _shop_resultset('State', @_);
305             };
306              
307             register shop_redirect => sub {
308 0     0     return resultset('UriRedirect')->redirect($_[0]);
309             };
310              
311             register shop_user => sub {
312 0     0     _shop_resultset('User', @_);
313             };
314              
315             register shop_charge => sub {
316 0     0     my (%args) = @_;
317 0           my ($schema, $bop_object, $payment_settings, $provider, $provider_settings);
318              
319 0           $payment_settings = plugin_setting->{payment};
320              
321             # determine payment provider
322 0 0 0       if (exists $args{provider} && $args{provider}) {
323 0           $provider = $args{provider};
324             }
325             else {
326 0           $provider = $payment_settings->{default_provider};
327             }
328              
329 0 0         if (exists $payment_settings->{providers}->{$provider}) {
330 0           $provider_settings = $payment_settings->{providers}->{$provider};
331             }
332             else {
333 0           die "Settings for provider $provider missing.";
334             }
335              
336             my %payment_data = (payment_mode => $provider,
337             status => 'request',
338             sessions_id => session->id,
339             payment_action => 'charge',
340             amount => $args{amount},
341 0           users_id => session('logged_in_user_id'),
342             );
343              
344             # create payment order
345 0           $schema = _shop_schema();
346              
347 0           my $payment_order = $schema->resultset('PaymentOrder')->create(\%payment_data);
348              
349             # create BOP object wrapper with provider settings
350 0           $bop_object = Dancer::Plugin::Interchange6::Business::OnlinePayment->new($provider, %$provider_settings);
351              
352 0           $bop_object->payment_order($payment_order);
353              
354             # call charge method
355 0           $bop_object->charge(%args);
356              
357 0 0         if ($bop_object->is_success) {
358 0           $payment_order->update({
359             status => 'success',
360             auth_code => $bop_object->authorization,
361             });
362             }
363             else {
364 0           $payment_order->update({
365             status => 'failure',
366             payment_error_code => $bop_object->error_code,
367             payment_error_message => $bop_object->error_message,
368             });
369             }
370              
371 0           return $bop_object;
372             };
373              
374             register cart => \&_shop_cart;
375             register shop_cart => \&_shop_cart;
376              
377             sub _shop_cart {
378              
379 0     0     my ( %args, $user_ref );
380              
381             # cart name from arg or default 'main'
382 0 0         $args{name} = @_ == 1 ? $_[0] : 'main';
383              
384             # set name of var we will stash carts in
385 0   0       my $var = plugin_setting->{carts_var_name} || 'ic6_carts';
386 0           debug "carts_var_name: $var";
387              
388             # cart class
389             my $cart_class = plugin_setting->{cart_class}
390 0   0       || 'Dancer::Plugin::Interchange6::Cart';
391 0           load_class $cart_class;
392              
393 0   0       my $carts = var $var // {};
394              
395 0 0         if ( !defined $carts->{ $args{name} } ) {
396              
397             # can't find this cart in stash
398              
399 0           $args{sessions_id} = session->id;
400 0     0     $args{execute_hook} = sub { execute_hook(@_) };
  0            
401              
402 0 0         if ( $user_ref = logged_in_user ) {
403              
404             # user is logged in
405 0           $args{users_id} = $user_ref->users_id;
406             }
407              
408 0           $carts->{ $args{name} } = $cart_class->new(%args);
409             }
410              
411             # stash carts back in var
412 0           var $var => $carts;
413              
414 0           return $carts->{ $args{name} };
415             }
416              
417             sub _shop_schema {
418 0     0     my $schema_key;
419              
420 0 0         if (@_) {
421 0           $schema_key = $_[0];
422             }
423             else {
424 0           $schema_key = 'default';
425             }
426              
427 0           return schema($schema_key);
428             };
429              
430             sub _shop_resultset {
431 0     0     my ($name, $key) = @_;
432              
433 0 0         if (defined $key) {
434 0           return resultset($name)->find($key);
435             }
436              
437 0           return resultset($name);
438             };
439              
440             register_plugin;
441              
442             =head1 ACKNOWLEDGEMENTS
443              
444             The L<Dancer> developers and community for their great application framework
445             and for their quick and competent support.
446              
447             Peter Mottram for his patches.
448              
449             =head1 LICENSE AND COPYRIGHT
450              
451             Copyright 2010-2015 Stefan Hornburg (Racke).
452              
453             This program is free software; you can redistribute it and/or modify it
454             under the terms of either: the GNU General Public License as published
455             by the Free Software Foundation; or the Artistic License.
456              
457             See http://dev.perl.org/licenses/ for more information.
458              
459             =head1 SEE ALSO
460              
461             L<Interchange6>, L<Interchange6::Schema>
462              
463             =cut
464              
465             1;