File Coverage

blib/lib/Dancer/Plugin/Interchange6.pm
Criterion Covered Total %
statement 80 80 100.0
branch 17 18 94.4
condition 6 6 100.0
subroutine 24 24 100.0
pod n/a
total 127 128 99.2


line stmt bran cond sub pod time code
1             package Dancer::Plugin::Interchange6;
2              
3 5     5   1938230 use strict;
  5         9  
  5         119  
4 5     5   22 use warnings;
  5         7  
  5         109  
5              
6 5     5   583 use Dancer qw(:syntax !before !after);
  5         148775  
  5         24  
7 5     5   3970 use Dancer::Plugin;
  5         4876  
  5         274  
8 5     5   2128 use Dancer::Plugin::DBIC;
  5         35355  
  5         240  
9 5     5   2410 use Dancer::Plugin::Auth::Extensible;
  5         18545  
  5         397  
10              
11 5     5   2189 use Dancer::Plugin::Interchange6::Cart;
  5         15  
  5         154  
12 5     5   2588 use Dancer::Plugin::Interchange6::Business::OnlinePayment;
  5         13  
  5         169  
13              
14 5     5   26 use Module::Runtime 'use_module';
  5         6  
  5         34  
15              
16             =head1 NAME
17              
18             Dancer::Plugin::Interchange6 - Interchange6 Shop Plugin for Dancer
19              
20             =head1 VERSION
21              
22             Version 0.120
23              
24             =cut
25              
26             our $VERSION = '0.120';
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 107     107   38169 _shop_schema(@_);
273             };
274              
275             register shop_address => sub {
276 3     3   13194 _shop_resultset('Address', @_);
277             };
278              
279             register shop_attribute => sub {
280 3     3   11058 _shop_resultset('Attribute', @_);
281             };
282              
283             register shop_country => sub {
284 4     4   8606 _shop_resultset('Country', @_);
285             };
286              
287             register shop_message => sub {
288 5     5   34667 _shop_resultset('Message', @_);
289             };
290              
291             register shop_navigation => sub {
292 13     13   6261 _shop_resultset('Navigation', @_);
293             };
294              
295             register shop_order => sub {
296 3     3   12845 _shop_resultset('Order', @_);
297             };
298              
299             register shop_product => sub {
300 59     59   10839 _shop_resultset('Product', @_);
301             };
302              
303             register shop_state => sub {
304 3     3   9549 _shop_resultset('State', @_);
305             };
306              
307             register shop_redirect => sub {
308 8     8   13587 return resultset('UriRedirect')->redirect($_[0]);
309             };
310              
311             register shop_user => sub {
312 9     9   12138 _shop_resultset('User', @_);
313             };
314              
315             register shop_charge => sub {
316 6     6   24325 my (%args) = @_;
317 6         8 my ($schema, $bop_object, $payment_settings, $provider, $provider_settings);
318              
319 6         15 $payment_settings = plugin_setting->{payment};
320              
321 6 50       228 die "No payment setting" unless $payment_settings;
322              
323             # determine payment provider
324 6 100       12 if ( $args{provider} ) {
325 4         5 $provider = $args{provider};
326             }
327             else {
328 2         6 $provider = $payment_settings->{default_provider};
329             }
330              
331 6 100       13 if (exists $payment_settings->{providers}->{$provider}) {
332 5         9 $provider_settings = $payment_settings->{providers}->{$provider};
333             }
334             else {
335 1         10 die "Settings for provider $provider missing.";
336             }
337              
338             my %payment_data = (payment_mode => $provider,
339             status => 'request',
340             sessions_id => session->id,
341             payment_action => 'charge',
342             amount => $args{amount},
343 5         13 users_id => session('logged_in_user_id'),
344             );
345              
346             # create payment order
347 5         22115 $schema = _shop_schema();
348              
349 5         253 my $payment_order = $schema->resultset('PaymentOrder')->create(\%payment_data);
350              
351             # create BOP object wrapper with provider settings
352 4         12178 $bop_object = Dancer::Plugin::Interchange6::Business::OnlinePayment->new($provider, %$provider_settings);
353              
354 4         68 $bop_object->payment_order($payment_order);
355              
356             # call charge method
357 4         15 $bop_object->charge(%args);
358              
359 3 100       33 if ($bop_object->is_success) {
360 2         17 $payment_order->update({
361             status => 'success',
362             auth_code => $bop_object->authorization,
363             });
364             }
365             else {
366 1         9 $payment_order->update({
367             status => 'failure',
368             payment_error_code => $bop_object->error_code,
369             payment_error_message => $bop_object->error_message,
370             });
371             }
372              
373 3         5304 return $bop_object;
374             };
375              
376             register cart => \&_shop_cart;
377             register shop_cart => \&_shop_cart;
378              
379             sub _shop_cart {
380              
381 59     59   51984 my ( %args, $user_ref );
382              
383             # cart name from arg or default 'main'
384 59 100       257 $args{name} = @_ == 1 ? $_[0] : 'main';
385              
386             # set name of var we will stash carts in
387 59   100     206 my $var = plugin_setting->{carts_var_name} || 'ic6_carts';
388 59         1792 debug "carts_var_name: $var";
389              
390             # cart class
391             my $cart_class = plugin_setting->{cart_class}
392 59   100     2480 || 'Dancer::Plugin::Interchange6::Cart';
393              
394 59   100     1404 my $carts = var($var) || {};
395              
396 59 100       856 if ( !defined $carts->{ $args{name} } ) {
397              
398             # can't find this cart in stash
399              
400 56         179 $args{sessions_id} = session->id;
401              
402 56 100       128981 if ( $user_ref = logged_in_user ) {
403              
404             # user is logged in
405 12         113773 $args{users_id} = $user_ref->users_id;
406             }
407              
408 56         96945 $carts->{ $args{name} } = use_module($cart_class)->new(%args);
409             }
410              
411             # stash carts back in var
412 59         35048 var $var => $carts;
413              
414 59         828 return $carts->{ $args{name} };
415             }
416              
417             sub _shop_schema {
418 112     112   144 my $schema_key;
419              
420 112 100       326 if (@_) {
421 2         3 $schema_key = $_[0];
422             }
423             else {
424 110         183 $schema_key = 'default';
425             }
426              
427 112         462 return schema($schema_key);
428             };
429              
430             sub _shop_resultset {
431 102     102   150 my ($name, $key) = @_;
432              
433 102 100       284 if (defined $key) {
434 28         117 return resultset($name)->find($key);
435             }
436              
437 74         191 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-2016 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;