The representation of credits amounts was increased by a factor of 100 to make minimum order amounts more realistic, and pricing information more readable. The view and order controller now validate that the quantity requested is within the service's limits.tags/v0.1.0
@@ -24,8 +24,6 @@ class BillingController extends Controller | |||||
} | } | ||||
//Expects an array 'packs' representing the amount of each multiple of credits. | //Expects an array 'packs' representing the amount of each multiple of credits. | ||||
//This controller should have a way of figuring out how much of each pack | |||||
//was bought later. | |||||
//Should validate that all amounts are positive integers in a reasonable range | //Should validate that all amounts are positive integers in a reasonable range | ||||
public function secret(Request $request) { | public function secret(Request $request) { | ||||
$user = Auth::user(); | $user = Auth::user(); | ||||
@@ -34,14 +32,14 @@ class BillingController extends Controller | |||||
+ $request->packs[ 'credits1000' ]*101000; | + $request->packs[ 'credits1000' ]*101000; | ||||
$transaction = new Transaction; | $transaction = new Transaction; | ||||
$transaction->credits = $request->packs['credits10']*10 + | |||||
$request->packs['credits50']*50 + | |||||
$request->packs['credits100']*100 + | |||||
$request->packs['credits1000']*1000; | |||||
$transaction->credits = $request->packs['credits10']*1000 + | |||||
$request->packs['credits50']*5000 + | |||||
$request->packs['credits100']*10000 + | |||||
$request->packs['credits1000']*100000; | |||||
$transaction->credits_extra = | $transaction->credits_extra = | ||||
$request->packs['credits50']*5 + | |||||
$request->packs['credits100']*10 + | |||||
$request->packs['credits1000']*150; | |||||
$request->packs['credits50']*500 + | |||||
$request->packs['credits100']*1000 + | |||||
$request->packs['credits1000']*15000; | |||||
$transaction->user_id = $user->id; | $transaction->user_id = $user->id; | ||||
$transaction->charge = $amount; | $transaction->charge = $amount; | ||||
@@ -75,6 +73,7 @@ class BillingController extends Controller | |||||
]); | ]); | ||||
} | } | ||||
//Adds correct credit amount to the charged user, precise to two decimal places | |||||
public function chargeEvent(Request $request) { | public function chargeEvent(Request $request) { | ||||
$event = \Stripe\Event::constructFrom($request->all()); | $event = \Stripe\Event::constructFrom($request->all()); | ||||
$charge = $event->data->object; | $charge = $event->data->object; | ||||
@@ -10,7 +10,7 @@ use Illuminate\Support\Facades\Log; | |||||
class OrderController extends Controller | class OrderController extends Controller | ||||
{ | { | ||||
//THis should also reduce user's available credits | |||||
//This should also reduce user's available credits | |||||
public function newOrder(Request $request) { | public function newOrder(Request $request) { | ||||
$user = Auth::user(); | $user = Auth::user(); | ||||
$order = new Order; | $order = new Order; | ||||
@@ -22,7 +22,11 @@ class OrderController extends Controller | |||||
$order->note = $request->note ?: ''; | $order->note = $request->note ?: ''; | ||||
$order->status = 'processing'; | $order->status = 'processing'; | ||||
$cost = ceil($order->quantity*$order->service->price/1000); | |||||
if (!$this->validateOrder($request)){ | |||||
abort(422); | |||||
} | |||||
$cost = ceil($order->quantity*$order->service->price/100000); | |||||
$user->credits = $user->credits - $cost; | $user->credits = $user->credits - $cost; | ||||
if ($cost > $user->credits) { | if ($cost > $user->credits) { | ||||
abort(520, 'Insufficient Credits'); | abort(520, 'Insufficient Credits'); | ||||
@@ -30,4 +34,15 @@ class OrderController extends Controller | |||||
$user->save(); | $user->save(); | ||||
$order->save(); | $order->save(); | ||||
} | } | ||||
// Should probably check for other things like service availability | |||||
public function validateOrder($order) { | |||||
$service = Service::find($order->service); | |||||
if ($order->quantity < $service->minimum || | |||||
$order->quantity > $service->maximum) { | |||||
return false; | |||||
} | |||||
return true; | |||||
} | |||||
} | } |
@@ -19,7 +19,7 @@ class DatabaseSeeder extends Seeder | |||||
*/ | */ | ||||
public function run() | public function run() | ||||
{ | { | ||||
Stripe::setApiKey(env('STRIPE_SECRET')); | |||||
Stripe::setApiKey(config('services.stripe.secret')); | |||||
User::create([ | User::create([ | ||||
'name' => 'test_user_unverified', | 'name' => 'test_user_unverified', | ||||
'email' => 'unverified@example.com', | 'email' => 'unverified@example.com', | ||||
@@ -4,7 +4,7 @@ | |||||
<transition name="fade" mode="out-in"> | <transition name="fade" mode="out-in"> | ||||
<div v-if="active === ''" id="main"> | <div v-if="active === ''" id="main"> | ||||
<section class="welcome-pane"><h3>Welcome, {{user.name}}!</h3></section> | <section class="welcome-pane"><h3>Welcome, {{user.name}}!</h3></section> | ||||
<section class="credits-pane"><img src="../../images/coin-stack.svg" alt="wallet" class="icon"/><p>Credits: {{user.credits}}</p></section> | |||||
<section class="credits-pane"><img src="../../images/coin-stack.svg" alt="wallet" class="icon"/><p>Credits: {{(user.credits/100).toLocaleString('en')}}</p></section> | |||||
<section class="alerts-pane"><h4>News and Announcements</h4> | <section class="alerts-pane"><h4>News and Announcements</h4> | ||||
<p>We've just launched. Thanks for joining us.</p> | <p>We've just launched. Thanks for joining us.</p> | ||||
</section> | </section> | ||||
@@ -2,7 +2,7 @@ | |||||
<section class="services-pane youtube" > | <section class="services-pane youtube" > | ||||
<h4>{{site.charAt(0).toUpperCase() + site.slice(1)}}</h4> | <h4>{{site.charAt(0).toUpperCase() + site.slice(1)}}</h4> | ||||
<ul :key="service.id" v-for="service in filter"> | <ul :key="service.id" v-for="service in filter"> | ||||
<li><span>{{service.name}}</span><span>{{service.price}}</span><span>{{service.minimum.toLocaleString('en')}}</span><span>{{service.maximum.toLocaleString('en')}}</span> | |||||
<li v-if="service.available"><span>{{service.name}}</span><span>{{(service.price/100).toLocaleString('en')}}</span><span>{{service.minimum.toLocaleString('en')}}</span><span>{{service.maximum.toLocaleString('en')}}</span> | |||||
<svg @click="$emit('select', service)" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-plus-square-fill" viewBox="0 0 16 16"> | <svg @click="$emit('select', service)" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-plus-square-fill" viewBox="0 0 16 16"> | ||||
<path d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z"/> | <path d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z"/> | ||||
</svg> | </svg> | ||||
@@ -5,7 +5,7 @@ | |||||
<a href="#credits" :class="{selected: page == 'credits'}">Credits</a> | <a href="#credits" :class="{selected: page == 'credits'}">Credits</a> | ||||
<div :class="page" class="menu-slider"><div></div></div> | <div :class="page" class="menu-slider"><div></div></div> | ||||
</div> | </div> | ||||
<h4 class="credits-display"><img class="icon" src="../../images/coin-stack.svg" alt=""><span> {{credits.toLocaleString('en')}}</span></h4> | |||||
<h4 class="credits-display"><img class="icon" src="../../images/coin-stack.svg" alt=""><span> {{(credits/100).toLocaleString('en')}}</span></h4> | |||||
<template v-if="page == 'new-order'"> | <template v-if="page == 'new-order'"> | ||||
@@ -34,7 +34,7 @@ | |||||
<img v-if="selected.site == 'tiktok'" class="icon" | <img v-if="selected.site == 'tiktok'" class="icon" | ||||
src="../../images/tik-tok.svg" alt=""/> | src="../../images/tik-tok.svg" alt=""/> | ||||
<h3>{{selected.name}}</h3> | <h3>{{selected.name}}</h3> | ||||
<h4>Cost: {{cost.toLocaleString('en')}}</h4> | |||||
<h4>Cost: {{(cost).toLocaleString('en')}}</h4> | |||||
<h4>Quantity</h4> | <h4>Quantity</h4> | ||||
<div><input required :min="selected.minimum" :max="selected.maximum" | <div><input required :min="selected.minimum" :max="selected.maximum" | ||||
type="number" v-model="amount" id="selQty"><span> / | type="number" v-model="amount" id="selQty"><span> / | ||||
@@ -59,6 +59,7 @@ | |||||
<option value="french">French</option> | <option value="french">French</option> | ||||
<option value="spanish">Spanish</option> | <option value="spanish">Spanish</option> | ||||
<option value="german">German</option> | <option value="german">German</option> | ||||
<option value="arabic">Arabic</option> | |||||
</select> | </select> | ||||
</div> | </div> | ||||
</template> | </template> | ||||
@@ -93,11 +94,17 @@ import Loading from '../icons/loading.vue' | |||||
function select(service) { | function select(service) { | ||||
this.completed = false | this.completed = false | ||||
if (this.amount < service.minimum){ | |||||
this.amount = service.minimum; | |||||
} | |||||
if (this.amount > service.maximum){ | |||||
this.amount = service.maximum; | |||||
} | |||||
this.selected = service | this.selected = service | ||||
} | } | ||||
function cost() { | function cost() { | ||||
return Math.ceil(this.selected.price * this.amount / 1000) | |||||
return (this.selected.price * this.amount / 100000).toFixed(2) | |||||
} | } | ||||
function buyService() { | function buyService() { | ||||
@@ -108,6 +115,10 @@ function buyService() { | |||||
document.getElementById('overlay-error').textContent = | document.getElementById('overlay-error').textContent = | ||||
'Insuficient Credits' | 'Insuficient Credits' | ||||
return | return | ||||
} else if (this.amount < this.selected.minimum || this.amount > this.selected.maximum) { | |||||
document.getElementById('overlay-error').textContent = | |||||
'Invalid amount' | |||||
return | |||||
} | } | ||||
this.paying = true | this.paying = true | ||||