File Coverage

blib/lib/Dancer2/Plugin/Interchange6.pm
Criterion Covered Total %
statement 71 71 100.0
branch 17 18 94.4
condition 2 2 100.0
subroutine 19 19 100.0
pod 13 13 100.0
total 122 123 99.1


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