Skip to content

Instantly share code, notes, and snippets.

@SysPete
Created September 9, 2014 14:05
Show Gist options
  • Save SysPete/719cb0156d1cdc532c11 to your computer and use it in GitHub Desktop.
Save SysPete/719cb0156d1cdc532c11 to your computer and use it in GitHub Desktop.
any [ 'get', 'post' ] => '/checkout' => require_login sub {
return forward 404 if config->{shop_disabled};
return redirect '/cart' if cart->count == 0;
my ( $data, $form, $form_last, $forms_ref, $params );
my $posted = 1;
$form_last = 'personal_details';
$forms_ref = session 'form';
foreach my $name (qw/personal_details confirm_order payment_details/) {
$form = $forms_ref->{$name};
unless ( defined $form && $form->{valid} ) {
$form_last = $name;
last;
}
}
debug "form $form_last";
$params = params;
if ( $form_last eq 'personal_details' ) {
if ( scalar keys %$params == 0 ) {
debug "pristine form";
undef $posted;
my $ship_adr = shop_address->search(
{
users_id => session('logged_in_user_id'),
type => 'shipping',
},
{
order_by => 'last_modified DESC',
rows => 1,
},
)->single;
if ($ship_adr) {
$data->{result} = {
address => $ship_adr->address,
address2 => $ship_adr->address_2,
postal_code => $ship_adr->postal_code,
city => $ship_adr->city,
country => $ship_adr->country_iso_code,
telephone => $ship_adr->phone,
};
$data->{result}->{state} = $ship_adr->State->state_iso_code
if $ship_adr->states_id;
}
}
else {
$data = validator( $params, $form_last );
if ( $data->{valid} ) {
my $result = $data->{result};
$result->{valid} = 1;
# create shipping address and push data into session
my $user = shop_user->search(
{ users_id => session('logged_in_user_id') },
{ rows => 1 } )->single;
error "Failed to find user in DB" unless $user;
my $data = {
type => 'shipping',
users_id => $user->users_id,
first_name => $user->first_name,
last_name => $user->last_name,
address => $result->{address},
address_2 => $result->{address2},
postal_code => $result->{postal_code},
city => $result->{city},
phone => $result->{telephone},
country_iso_code => $result->{country},
};
if ( defined $result->{state} ) {
$data->{State}->{state_iso_code} = $result->{state};
}
my $ship_address = shop_address->find_or_create($data);
error "Failed to create shipping address" unless $ship_address;
$result->{ship_address_id} = $ship_address->id;
# update session
my $forms_ref = session 'form';
$forms_ref->{$form_last} = $result;
session 'form' => $forms_ref;
return redirect '/checkout';
}
}
my $countries =
shop_country->search( { active => 1 }, { order_by => 'name' } );
my $country = var('geo_country_code') || 'MT';
$country = $data->{result}->{country} if $data->{result}->{country};
my $states =
shop_schema->resultset('State')
->search( { active => 1, country_iso_code => $country },
{ order_by => 'name' } );
my $state = var('geo_state_code') || '';
$state = $data->{result}->{state} if $data->{result}->{state};
# jquery to handle add/remove state list on country change + datepicker
my $script = q|
$(document).ready(function () {
$("#country").change(function(){
var countryName = $('#country').val();
$.get('/component/select_states/' + countryName, function(data) {
$('#states').replaceWith(data);
});
});
$('input.date').datepicker({
startDate: "| . DateTime->now->add( days => 7 )->dmy . q|",
format: "dd-mm-yyyy",
autoclose: true
});
});
|;
return template '/checkout_personal_details' => {
country => $country,
countries => $countries,
data => $data->{result},
posted => $posted,
script => $script,
state => $state,
states => $states,
state_label => $country eq 'CA' ? 'Province' : 'State',
title => 'Checkout',
};
}
elsif ( $form_last eq 'confirm_order' ) {
if ( scalar keys %$params == 0 ) {
debug "pristine form";
}
else {
$data = validator( $params, $form_last );
if ( $data->{valid} ) {
# stash things in session and redirect back to /checkout
$form = $data->{result};
$form->{valid} = 1;
$forms_ref->{$form_last} = $form;
session 'form' => $forms_ref;
redirect '/checkout';
}
}
return template '/checkout_confirm_order' => {
cart => cart,
data => $data->{result},
title => 'Checkout',
};
}
elsif ( $form_last eq 'payment_details' ) {
my $cart = cart;
if ( scalar keys %$params == 0 ) {
debug "pristine form";
undef $posted;
my $user =
shop_user->search( { users_id => session('logged_in_user_id') },
{ rows => 1 } )->single;
my $billing_adr = shop_address->search(
{
users_id => $user->users_id,
type => 'billing',
},
{
order_by => 'last_modified DESC',
rows => 1,
},
)->single;
if ($billing_adr) {
$data->{result} = {
address => $billing_adr->address,
address2 => $billing_adr->address_2,
postal_code => $billing_adr->postal_code,
city => $billing_adr->city,
country => $billing_adr->country_iso_code,
};
$data->{result}->{state} = $billing_adr->State->state_iso_code
if $billing_adr->states_id;
}
else {
# use address from personal_details in the session
my $form = $forms_ref->{personal_details};
$data->{result} = {
address => $form->{address},
address2 => $form->{address2},
postal_code => $form->{postal_code},
city => $form->{city},
country => $form->{country},
state => $form->{state},
};
}
}
else {
$data = validator( $params, $form_last );
if ( $data->{valid} ) {
# all seems OK so create order
my $result = $data->{result};
my $users_id = session('logged_in_user_id');
my ( $form, $ship_address, $bill_address, $addr_values );
my $user = shop_user->find(
{ users_id => session('logged_in_user_id') } );
$form = $forms_ref->{personal_details};
$ship_address = shop_address->search(
{ addresses_id => $form->{ship_address_id} },
{ rows => 1 } )->single;
my $bill_data = {
users_id => $user->users_id,
type => 'billing',
first_name => $user->first_name,
last_name => $user->last_name,
address => $result->{address},
address_2 => $result->{address2},
postal_code => $result->{postal_code},
city => $result->{city},
phone => '',
country_iso_code => $result->{country},
};
if ( defined $result->{state} ) {
my $state = shop_schema->resultset('State')->search(
{
country_iso_code => $result->{country},
state_iso_code => $result->{state}
},
{ rows => 1 }
)->single;
if ($state) {
}
$bill_data->{states_id} = $state->states_id;
}
$bill_address =
shop_address->search( $bill_data, { rows => 1 } )->single;
$bill_address = shop_address->create($bill_data)
unless $bill_address;
# attempt to collect payment
my $amount = $result->{amount};
$amount = cart->total if ($amount > cart->total);
$amount = sprintf( "%.2f", $amount );
my $expiration = sprintf( "%02d/%02d",
$result->{cc_month}, $result->{cc_year} );
my %payment_data = (
action => 'Normal Authorization',
amount => $amount,
card_number => $result->{cc_number},
cvv => $result->{cvv_number},
expiration => $expiration,
address => $result->{address},
city => $result->{city},
region => $result->{region},
zip => $result->{postal_code},
country => $result->{country},
);
my $braintree_config =
config->{plugins}->{Interchange6}->{Payment}->{providers}
->{Braintree};
my $tx = Business::OnlinePayment->new(
'Braintree',
merchant_id => $braintree_config->{merchant_id},
public_key => $braintree_config->{public_key},
private_key => $braintree_config->{private_key},
);
if ( $braintree_config->{test_transaction} ) {
$tx->test_transaction(1);
}
$tx->content(%payment_data);
$tx->submit();
if ( $tx->is_success() ) {
info "Payment success: " . Dumper($tx);
if ( $tx->can('popup_url') ) {
debug( "Payment redirect: ", $tx->popup_url() );
return redirect $tx->popup_url();
}
debug "Payment successful: ", $tx->authorization;
# order date
my $order_date = DateTime->now->iso8601;
# create orderlines
my @orderlines;
my $position = 1;
my @cart_products = cart->products_array;
for my $product (@cart_products) {
my $ol_prod = shop_product( $product->sku );
my %orderline_product = (
sku => $ol_prod->sku,
order_position => $position++,
name => $ol_prod->name,
short_description => $ol_prod->short_description,
description => $ol_prod->description,
weight => $ol_prod->weight,
quantity => $product->quantity,
price => $ol_prod->price,
subtotal => $ol_prod->price * $product->quantity,
);
push @orderlines, \%orderline_product;
}
# create Order
my $parser =
DateTime::Format::Natural->new( format => 'dd/mm/yyy' );
my %order_info = (
users_id => $users_id,
billing_addresses_id => $bill_address->id,
shipping_addresses_id => $ship_address->id,
subtotal => cart->subtotal,
total_cost => cart->total,
order_date => $order_date,
order_number => $tx->order_number,
start_date => $parser->parse_datetime( $form->{start} ),
end_date => $parser->parse_datetime( $form->{end} ),
status => 'success',
site => var('site'),
Orderline => \@orderlines,
);
my $order = shop_order->create( \%order_info );
# create PaymentOrder
shop_schema->resultset('PaymentOrder')->create(
{
payment_mode => 'Braintree',
payment_action => $payment_data{action},
payment_id => $tx->order_number,
auth_code => $tx->authorization,
users_id => $user->id,
sessions_id => session->id,
orders_id => $order->id,
amount => $payment_data{amount},
status => 'success',
}
);
# email to customer
$form = $forms_ref->{personal_details};
my $name = var 'name';
my $site = var 'site';
my $from = "info\@dive${site}.com";
my $to = join( '',
$user->first_name, " ", $user->last_name,
" <", $user->email, ">" );
my $params = {
first_name => $user->first_name,
last_name => $user->last_name,
email => $user->email,
phone => $form->{telephone},
name => $name,
site => $site,
from => $from,
order => $order,
orderlines => \@orderlines,
paid => $payment_data{amount},
};
# email to customer
send_email(
template => 'email/order_confirmation',
from => var('short_name') . " <$from>",
to => $to,
subject => "Thank you for your order/booking with "
. var('short_name'),
params => $params,
);
# email to diveshack
send_email(
template => 'email/order_confirmation',
from => var('short_name') . " <$from>",
to => config->{email_to},
subject => "website booking: for " . var('short_name'),
params => $params,
);
# email to Peter Mottram
send_email(
template => 'email/order_confirmation',
from => var('short_name') . " <$from>",
to => '[email protected]',
subject => "website booking: for " . var('short_name'),
params => $params,
);
# clear up
session form => undef;
cart->clear;
return template 'order' => {
title => "Thankyou for your order",
message => "You should soon receive a confirmation "
. "email from us. Please get in touch if you do not "
. "receive this within the next few hours.",
order => $order,
};
}
else {
error "Payment failed: " . Dumper($tx);
$data->{result}->{err_cc_number} = "Payment failed. "
. "Check card details or try a different card.";
delete $data->{result}->{cc_number};
delete $data->{result}->{cvv_number};
}
}
else {
# scrub useless cc_number and cvv_number
delete $form->{result}->{cc_number};
delete $form->{result}->{cvv_number};
}
}
my $countries =
shop_country->search( { active => 1 }, { order_by => 'name' } );
my $country = var('geo_country_code') || 'MT';
$country = $data->{result}->{country} if $data->{result}->{country};
my $states =
shop_schema->resultset('State')
->search( { active => 1, country_iso_code => $country },
{ order_by => 'name' } );
my $state = var('geo_state_code') || '';
$state = $data->{result}->{state} if $data->{result}->{state};
# jquery to handle add/remove state list on country change
# and to change voucher amount field visibility
# + braintree ajax
my $cse_key = config->{plugins}->{Interchange6}->{Payment}->{providers}
->{Braintree}->{cse_key};
my $script = q(
$(document).ready(function () {
$("#country").change(function(){
var countryName = $('#country').val();
$.get('/component/select_states/' + countryName, function(data) {
$('#states').replaceWith(data);
});
});
$("#amount").change(function(){
$("#voucher_full, #voucher_deposit").hide();
$('#voucher_' + $(this).find('option:selected').attr('id')).show();
})
});
) . qq(
var braintree = Braintree.create("$cse_key");
braintree.onSubmitEncryptForm('payment-form');
);
# dummy card details in development environment
my $braintree_config =
config->{plugins}->{Interchange6}->{Payment}->{providers}
->{Braintree};
if ( config->{environment} eq 'development' || $braintree_config->{test_transaction} ) {
$data->{result}->{cc_number} = '4111 1111 1111 1111'
unless $data->{result}->{cc_number};
$data->{result}->{cvv_number} = '111'
unless $data->{result}->{cvv_number};
$data->{result}->{cc_month} = '12'
unless $data->{result}->{cc_month};
$data->{result}->{cc_year} = '2018'
unless $data->{result}->{cc_year};
$data->{result}->{cc_holder} = 'John Smith'
unless $data->{result}->{cc_holder};
}
# month/years for CC checkout
my ( @months, @years );
my $dtl = DateTime::Locale->load( config->{locale} );
my $cur_date = DateTime->now( locale => $dtl );
my $i = 1;
for my $name ( @{ $dtl->month_stand_alone_abbreviated } ) {
push( @months, { value => $i++, label => ucfirst($name) } );
}
for my $year (
$cur_date->year .. $cur_date->clone->add( years => 10 )->year )
{
push( @years, { value => $year, label => $year } );
}
# calculate voucher_amount
my @rec_course_skus = @{ var('rec_course_skus') };
my $rec_courses_total = 0;
my $voucher_amount = 0;
foreach my $product ( $cart->products_array ) {
if ( grep { $_ eq $product->sku } @rec_course_skus ) {
$rec_courses_total += $product->price * $product->quantity;
}
}
if ( $rec_courses_total >= 400 ) {
$voucher_amount = int( $rec_courses_total / 100 ) * 10;
}
# display template
if ($posted) {
delete $data->{result}->{cc_number};
delete $data->{result}->{cvv_number};
}
return template '/checkout_payment_details' => {
cart => $cart,
country => $country,
countries => $countries,
data => $data->{result},
months => \@months,
posted => $posted,
script => $script,
state => $state,
states => $states,
state_label => $country eq 'CA' ? 'Province' : 'State',
title => 'Checkout',
voucher_amount => $voucher_amount,
years => \@years,
};
}
# we should never get here
forward 404;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment