From 8fd5967f7cb454003ecf2d0d9a7ea70364b44b7f Mon Sep 17 00:00:00 2001 From: Darin McBride Date: Wed, 14 Oct 2015 22:58:36 -0600 Subject: [PATCH] Add promotional code. Each essentia purchase will go through the promotions to see what, if any, bonus they may receive. Promos can be advertised via the UI later since they're returned back with each RPC call. --- docs/Intro.pod | 12 ++- lib/Lacuna/DB/Result/Promotion.pm | 98 +++++++++++++++++++ lib/Lacuna/DB/Result/Promotion/Bonus50.pm | 11 +++ .../DB/Result/Promotion/BonusEssentia.pm | 37 +++++++ lib/Lacuna/DB/ResultSet/Promotion.pm | 26 +++++ lib/Lacuna/RPC.pm | 7 +- lib/Lacuna/Web/Pay.pm | 29 +++++- var/messages/promo/bonus_essentia.txt | 5 + var/upgrades/3.0915.sql | 10 ++ var/www/public/changes.txt | 1 + 10 files changed, 232 insertions(+), 4 deletions(-) create mode 100644 lib/Lacuna/DB/Result/Promotion.pm create mode 100644 lib/Lacuna/DB/Result/Promotion/Bonus50.pm create mode 100644 lib/Lacuna/DB/Result/Promotion/BonusEssentia.pm create mode 100644 lib/Lacuna/DB/ResultSet/Promotion.pm create mode 100644 var/messages/promo/bonus_essentia.txt create mode 100644 var/upgrades/3.0915.sql diff --git a/docs/Intro.pod b/docs/Intro.pod index 2d89fe03..91b65d69 100644 --- a/docs/Intro.pod +++ b/docs/Intro.pod @@ -131,7 +131,17 @@ Most methods will provide a status block as part of the response. This is used t "x" : [ -15, 15 ], "y" : [ -15, 15 ], "z" : [ -15, 15 ] - } + }, + "promotions" : [ + { + "description" : "With a purchase of essentia, receive a 50% bonus!", + "end_date" : "31 10 2015 00:00:00 +0000", + "start_date : "01 10 2015 00:00:00 +0000", + "type" : "Bonus50", + "min_purchase" : null, # essentia, not $ + "max_purchase" : null # essentia, not $ + } + ] }, "empire" : { # this block is not always included See get_status() in Empire diff --git a/lib/Lacuna/DB/Result/Promotion.pm b/lib/Lacuna/DB/Result/Promotion.pm new file mode 100644 index 00000000..cfdacaaa --- /dev/null +++ b/lib/Lacuna/DB/Result/Promotion.pm @@ -0,0 +1,98 @@ +package Lacuna::DB::Result::Promotion; + +use Moose; +use utf8; +extends 'Lacuna::DB::Result'; +use Lacuna::Util qw(format_date); + +__PACKAGE__->load_components('DynamicSubclass'); +__PACKAGE__->table('promotion'); +__PACKAGE__->add_columns( + start_date => { data_type => 'datetime', is_nullable => 0 }, + end_date => { data_type => 'datetime', is_nullable => 0 }, + type => { data_type => 'varchar', size => 30, is_nullable => 0 }, # Look at typecast_map below + min_purchase => { data_type => 'int', is_nullable => 1, }, + max_purchase => { data_type => 'int', is_nullable => 1, }, + payload => { data_type => 'mediumblob', is_nullable => 1, 'serializer_class' => 'JSON' }, +); + +our @types = qw( + Bonus50 + ); + +__PACKAGE__->typecast_map(type => { + map { $_ => __PACKAGE__ . '::' . $_ } @types +}); + +sub ui_details { + my ($self) = @_; + + my @with; + push @with, sprintf "minimum purchase of %d", $self->min_purchase if defined $self->min_purchase; + push @with, sprintf "maximum purchase of %d", $self->max_purchase if defined $self->max_purchase; + + my $description = ''; + if (@with) { + if (@with == 2 && $self->min_purchase == $self->max_purchase) + { + $description = sprintf "With a purchase of %d essentia, "; + } + else + { + $description = sprintf "With a %s essentia, ", join ' and ', @with if @with; + } + $description .= $self->description; + } + else + { + $description = "With a purchase of essentia, " . $self->description; + } + + return { + start_date => format_date($self->start_date), + end_date => format_date($self->end_date), + type => $self->type, + min_purchase => $self->min_purchase, + max_purchase => $self->max_purchase, + description => ucfirst $description, + }; +} + + +# checks min/max purchase amounts (if any) +sub purchase_applies +{ + my ($self, $opts) = @_; + + return 0 if defined $self->min_purchase && $opts->{amount} < $self->min_purchase; + return 0 if defined $self->max_purchase && $opts->{amount} > $self->max_purchase; + + return 1; +} + +# opts: +# amount (essentia) +# empire (object) +# cost (in USD) +# transaction_id +# type (iTransact, Paypal, etc.) + +sub essentia_purchased +{ + my ($self, $opts) = @_; + + return; # not all promotions are necessarily E-based? +} + +around essentia_purchased => sub { + my ($orig, $self, $opts) = @_; + + # pre-check this automatically. + return unless $self->purchase_applies($opts); + + return $self->$orig($opts); +}; + + +no Moose; +__PACKAGE__->meta->make_immutable(inline_constructor => 0); diff --git a/lib/Lacuna/DB/Result/Promotion/Bonus50.pm b/lib/Lacuna/DB/Result/Promotion/Bonus50.pm new file mode 100644 index 00000000..5fa76655 --- /dev/null +++ b/lib/Lacuna/DB/Result/Promotion/Bonus50.pm @@ -0,0 +1,11 @@ +package Lacuna::DB::Result::Promotion::Bonus50; + +use Moose; +use utf8; +no warnings qw(uninitialized); +extends 'Lacuna::DB::Result::Promotion::BonusEssentia'; + +use constant bonus_percent => 50; + +no Moose; +__PACKAGE__->meta->make_immutable(inline_constructor => 0); diff --git a/lib/Lacuna/DB/Result/Promotion/BonusEssentia.pm b/lib/Lacuna/DB/Result/Promotion/BonusEssentia.pm new file mode 100644 index 00000000..8db1c0eb --- /dev/null +++ b/lib/Lacuna/DB/Result/Promotion/BonusEssentia.pm @@ -0,0 +1,37 @@ +package Lacuna::DB::Result::Promotion::BonusEssentia; + +use Moose; +use utf8; +no warnings qw(uninitialized); +extends 'Lacuna::DB::Result::Promotion'; + +sub essentia_purchased +{ + my ($self, $opts) = @_; + + my $bonus = int( $opts->{amount} * $self->bonus_percent() / 10 + 0.5 ) / 10; + my $empire = $opts->{empire}; + + $empire->add_essentia({ + amount => $bonus, + reason => sprintf('Bonus %d%%', $self->bonus_percent()), + type => 'paid', + transaction_id => $opts->{transaction_id}, + }); + $empire->update; + $empire->send_predefined_message( + tags => ['Alert'], + filename => 'promo/bonus_essentia.txt', + params => [$self->bonus_percent(), $bonus, $opts->{transaction_id}], + ); + +} + +sub description { + my ($self) = @_; + + sprintf "receive a %d%% bonus!", $self->bonus_percent; +} + +no Moose; +__PACKAGE__->meta->make_immutable(inline_constructor => 0); diff --git a/lib/Lacuna/DB/ResultSet/Promotion.pm b/lib/Lacuna/DB/ResultSet/Promotion.pm new file mode 100644 index 00000000..ad86bf5e --- /dev/null +++ b/lib/Lacuna/DB/ResultSet/Promotion.pm @@ -0,0 +1,26 @@ +package Lacuna::DB::ResultSet::Promotion; + +use Moose; +use utf8; +no warnings qw(uninitialized); + +extends 'Lacuna::DB::ResultSet'; + +sub current_promotions_rs +{ + my $self = shift; + + $self->search( + { + start_date => [ undef, { '<=' => \q{UTC_TIMESTAMP()} }], + end_date => [ undef, { '>' => \q{UTC_TIMESTAMP()} }], + }); +} + +sub current_promotions { + my ($self) = @_; + $self->current_promotions_rs->all; +} + +no Moose; +__PACKAGE__->meta->make_immutable(inline_constructor => 0); diff --git a/lib/Lacuna/RPC.pm b/lib/Lacuna/RPC.pm index 0a489a36..29c1852c 100644 --- a/lib/Lacuna/RPC.pm +++ b/lib/Lacuna/RPC.pm @@ -55,7 +55,7 @@ sub get_empire_by_session { } my $ipm = $session->ip_address eq $ipr; my @caller; - for (my $i = 1;!@caller || $caller[0] !~ /Lacuna::RPC/;++$i) { + for (my $i = 1;@caller and $caller[0] !~ /Lacuna::RPC/;++$i) { @caller = caller($i); } $log->info(sprintf "ACTUAL:ipr=%s,ipe=%s,ipm=%s,ses=%s,sat:%d,rpc=%s", $ipr, $session->ip_address, $ipm, $session_id, $session->is_sitter ? 1 : 0, $caller[3]); @@ -157,6 +157,11 @@ sub format_status { $out{server}{announcement} = 1; } $out{empire} = $empire->get_status; + + if (my @promos = Lacuna->db->resultset('Promotion')->current_promotions) + { + $out{server}{promotions} = [ map $_->ui_details, @promos ]; + } } if (defined $body) { $out{body} = $body->get_status($empire); diff --git a/lib/Lacuna/Web/Pay.pm b/lib/Lacuna/Web/Pay.pm index 4ba9005a..bb636930 100644 --- a/lib/Lacuna/Web/Pay.pm +++ b/lib/Lacuna/Web/Pay.pm @@ -559,9 +559,16 @@ sub www_buy_currency_cc { $empire->send_predefined_message( tags => ['Alert'], filename => 'purchase_essentia.txt', - params => [$amount, $transaction_id], + params => [$amount, $transaction_id], ); - + $self->_run_promotions({ + amount => $amount, + empire => $empire, + cost => $request->param('total'), + type => 'iTransact', + transaction_id => $transaction_id + }); + my $script = " try { window.opener.YAHOO.lacuna.Essentia.paymentFinished(); @@ -727,6 +734,13 @@ sub www_paypal_ec_checkout { filename => 'purchase_essentia.txt', params => [$amount, $transaction_id], ); + $self->_run_promotions({ + amount => $amount, + empire => $empire, + cost => $order->{total}, + type => 'PayPal', + transaction_id => $transaction_id + }); Lacuna->cache->delete( 'paypal_order', $user_id ); my $script = " @@ -738,6 +752,17 @@ sub www_paypal_ec_checkout { return $self->wrap('Thank you! The essentia will be added to your account momentarily.'); } +sub _run_promotions +{ + my ($self, $opts) = @_; + + my $rs = Lacuna->db->resultset("Promotion")->current_promotions_rs; + while (my $promo = $rs->next()) + { + $promo->essentia_purchased($opts); + } +} + no Moose; __PACKAGE__->meta->make_immutable(inline_constructor => 0); diff --git a/var/messages/promo/bonus_essentia.txt b/var/messages/promo/bonus_essentia.txt new file mode 100644 index 00000000..2789cb3f --- /dev/null +++ b/var/messages/promo/bonus_essentia.txt @@ -0,0 +1,5 @@ +Promotional Essentia Added +~~~ +Due to the current %s%%-bonus promotions, an extra %s has been credited to your empire. + +Transaction ID: %s diff --git a/var/upgrades/3.0915.sql b/var/upgrades/3.0915.sql new file mode 100644 index 00000000..5f9d4365 --- /dev/null +++ b/var/upgrades/3.0915.sql @@ -0,0 +1,10 @@ +CREATE TABLE promotion ( + id integer(11) NOT NULL auto_increment, + start_date datetime NOT NULL, + end_date datetime NOT NULL, + type varchar(30) NOT NULL, + min_purchase integer NULL, + max_purchase integer NULL, + payload mediumblob NULL, + PRIMARY KEY (id) +); diff --git a/var/www/public/changes.txt b/var/www/public/changes.txt index 057ba7fd..5e396877 100644 --- a/var/www/public/changes.txt +++ b/var/www/public/changes.txt @@ -1,6 +1,7 @@ 3.0915: - Fix: Reduce the wild resource fluctuations during planet ticking. - Mod: Evein plans get an extra 5 days start time for building. + - Add: Lacuna promotions. 3.0914: - UI : Evein draining re-added. -- 2.51.2