@@ -13,6 +13,16 @@ use App\Models\Transaction; | |||||
class BillingController extends Controller | class BillingController extends Controller | ||||
{ | { | ||||
protected $stripe; | |||||
protected $user; | |||||
public function __construct() { | |||||
$this->stripe = new \Stripe\StripeClient(env('STRIPE_SECRET')); | |||||
Stripe::setApiKey(env('STRIPE_SECRET')); | |||||
$this->user = Auth::user(); | |||||
} | |||||
//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 | //This controller should have a way of figuring out how much of each pack | ||||
//was bought later. | //was bought later. | ||||
@@ -20,6 +30,7 @@ class BillingController extends Controller | |||||
public function secret(Request $request) { | public function secret(Request $request) { | ||||
Stripe::setApiKey(env('STRIPE_SECRET')); | Stripe::setApiKey(env('STRIPE_SECRET')); | ||||
$user = Auth::user(); | |||||
$amount = $request->packs[ 'credits10' ]*1099 + | $amount = $request->packs[ 'credits10' ]*1099 + | ||||
$request->packs[ 'credits50' ]*5499 + $request->packs[ 'credits100' ]*10999 | $request->packs[ 'credits50' ]*5499 + $request->packs[ 'credits100' ]*10999 | ||||
+ $request->packs[ 'credits1000' ]*101000; | + $request->packs[ 'credits1000' ]*101000; | ||||
@@ -34,7 +45,7 @@ class BillingController extends Controller | |||||
$request->packs['credits100']*10 + | $request->packs['credits100']*10 + | ||||
$request->packs['credits1000']*150; | $request->packs['credits1000']*150; | ||||
$transaction->user_id = Auth::user()->id; | $transaction->user_id = $user->id; | ||||
$transaction->charge = $amount; | $transaction->charge = $amount; | ||||
$transaction->status = 'processing'; | $transaction->status = 'processing'; | ||||
$transaction->completed = false; | $transaction->completed = false; | ||||
@@ -43,13 +54,18 @@ class BillingController extends Controller | |||||
$intent = PaymentIntent::create([ | $intent = PaymentIntent::create([ | ||||
'amount' => $amount, | 'amount' => $amount, | ||||
'currency' => 'usd', | 'currency' => 'usd', | ||||
'customer' => Auth::user()->customer_id, | 'customer' => $user->customer_id, | ||||
'description' => "You have received $total_credits credits.", | 'description' => "You have received $total_credits credits.", | ||||
'receipt_email' => Auth::user()->email, | 'receipt_email' => Auth::user()->email, | ||||
'metadata' => ['transaction_id' => $transaction->id] | 'metadata' => ['transaction_id' => $transaction->id] | ||||
]); | ]); | ||||
$transaction->intent_id = $intent->id; | $transaction->intent_id = $intent->id; | ||||
//Save the card as a default if none is set and it was selected | |||||
if ($user->payment_method == null && $request->card) { | |||||
$this->changeDefaultCard($request->card); | |||||
} | |||||
$transaction->save(); | $transaction->save(); | ||||
return $intent->client_secret; | return $intent->client_secret; | ||||
} | } | ||||
@@ -71,7 +87,6 @@ class BillingController extends Controller | |||||
$user = $transaction->user; | $user = $transaction->user; | ||||
if ($event->type == 'charge.succeeded') { | if ($event->type == 'charge.succeeded') { | ||||
Log::debug($charge->metadata['transaction_id']); | |||||
$user->credits = $user->credits + $transaction->credits + $transaction->credits_extra; | $user->credits = $user->credits + $transaction->credits + $transaction->credits_extra; | ||||
$transaction->status = 'completed'; | $transaction->status = 'completed'; | ||||
$transaction->completed = true; | $transaction->completed = true; | ||||
@@ -83,4 +98,28 @@ class BillingController extends Controller | |||||
$transaction->save(); | $transaction->save(); | ||||
} | } | ||||
} | } | ||||
public function changeDefaultCard(String $card) { | |||||
$user = Auth::user(); | |||||
$user->payment_method = $card; | |||||
$user->save(); | |||||
$cards = $this->getCards(); | |||||
Log::debug($this->user->customer_id); | |||||
return PaymentMethod::all([ | |||||
'customer' => Auth::user()->customer_id, | |||||
'type' => 'card' | |||||
]); | |||||
} | |||||
public function deleteCard(Request $request) { | |||||
$this->stripe->paymentMethods->detach($request->card); | |||||
$user = Auth::user(); | |||||
if ($request->card == $user->payment_method) { | |||||
$user->payment_method = null; | |||||
$user->save(); | |||||
} | |||||
return ($this->getCards()); | |||||
} | |||||
} | } |
@@ -21,7 +21,7 @@ class CreateUsersTable extends Migration | |||||
$table->timestamp('email_verified_at')->nullable(); | $table->timestamp('email_verified_at')->nullable(); | ||||
$table->string('password'); | $table->string('password'); | ||||
$table->enum('role', ['client', 'guest', 'admin']); | $table->enum('role', ['client', 'guest', 'admin']); | ||||
$table->string('payment_method'); | $table->string('payment_method')->default(''); | ||||
$table->boolean('active')->default(true); | $table->boolean('active')->default(true); | ||||
$table->unsignedBigInteger('credits')->default(0); | $table->unsignedBigInteger('credits')->default(0); | ||||
$table->rememberToken(); | $table->rememberToken(); | ||||
@@ -28,7 +28,7 @@ | |||||
<div :class="{right: !selectSaved}" class="menu-slider"><div></div></div> | <div :class="{right: !selectSaved}" class="menu-slider"><div></div></div> | ||||
</div> | </div> | ||||
<saved-cards v-model:picked-card="picked" :cards="cards" | <saved-cards :preferred="preferred" v-model:picked-card="picked" :cards="cards" | ||||
:token="token" v-if="selectSaved"></saved-cards> | :token="token" v-if="selectSaved"></saved-cards> | ||||
<payment-card @set-card="(c) => {card = c}" @card-valid="(val) => {cardValid = val}" | <payment-card @set-card="(c) => {card = c}" @card-valid="(val) => {cardValid = val}" | ||||
@@ -148,7 +148,7 @@ export default { | |||||
}, | }, | ||||
computed: {total, ready}, | computed: {total, ready}, | ||||
methods: {getSecret, pay, getCards}, | methods: {getSecret, pay, getCards}, | ||||
props: ['token'], | props: ['preferred', 'token'], | ||||
emits: ['purchaseComplete'], | emits: ['purchaseComplete'], | ||||
created() { | created() { | ||||
this.getCards() | this.getCards() | ||||
@@ -6,7 +6,7 @@ | |||||
<div v-for="(card, index) in cards" :key="card.id" class="saved-card"> | <div v-for="(card, index) in cards" :key="card.id" class="saved-card"> | ||||
<span>{{card.card.brand[0].toUpperCase() + card.card.brand.substring(1)}} | <span>{{card.card.brand[0].toUpperCase() + card.card.brand.substring(1)}} | ||||
(••••{{card.card.last4}})</span> | (••••{{card.card.last4}})</span> | ||||
<span><input :checked="index === 0" :value="card.id" name="selected-card" type="radio" | <span><input :checked="card.id == preferred" :value="card.id" name="selected-card" type="radio" | ||||
@change="change(card.id)"></span> | @change="change(card.id)"></span> | ||||
<span><img @click="remove(card.id)" src="../../images/close-icon-black.svg"/></span> | <span><img @click="remove(card.id)" src="../../images/close-icon-black.svg"/></span> | ||||
</div> | </div> | ||||
@@ -35,22 +35,22 @@ function change(card) { | |||||
body: JSON.stringify({'card': card}) | body: JSON.stringify({'card': card}) | ||||
}).then((response) => { | }).then((response) => { | ||||
if (response.ok) { | if (response.ok) { | ||||
console.log('ok') | |||||
response.json().then((data) => { | response.json().then((data) => { | ||||
this.cards = data | this.cards = data.data | ||||
}) | }) | ||||
} else { | } else { | ||||
console.log('bad') | console.log('bad') | ||||
document.getElementById("billing-error").textContent = | document.getElementById("billing-error").textContent = | ||||
`${response.status}: ${response.statusText}` | `${response.status}: ${response.statusText}` | ||||
} | } | ||||
response.json().then(data => { | |||||
console.log(data)} | |||||
) | |||||
}) | }) | ||||
} | } | ||||
function remove(card) { | function remove(card) { | ||||
if (card.length == 1) { | |||||
return | |||||
} | |||||
fetch('/panel/delete-card', { | fetch('/panel/delete-card', { | ||||
method: 'POST', | method: 'POST', | ||||
headers: {'Content-Type': 'application/json', | headers: {'Content-Type': 'application/json', | ||||
@@ -59,18 +59,13 @@ function remove(card) { | |||||
body: JSON.stringify({'card': card}) | body: JSON.stringify({'card': card}) | ||||
}).then((response) => { | }).then((response) => { | ||||
if (response.ok) { | if (response.ok) { | ||||
console.log('ok') | |||||
response.json().then((data) => { | response.json().then((data) => { | ||||
this.cards = data | this.cards = data.data | ||||
}) | }) | ||||
} else { | } else { | ||||
console.log('bad') | |||||
document.getElementById("billing-error").textContent = | document.getElementById("billing-error").textContent = | ||||
`${response.status}: ${response.statusText}` | `${response.status}: ${response.statusText}` | ||||
} | } | ||||
response.json().then(data => { | |||||
console.log(data)} | |||||
) | |||||
}) | }) | ||||
} | } | ||||
@@ -82,6 +77,6 @@ export default { | |||||
created() { | created() { | ||||
this.get() | this.get() | ||||
}, | }, | ||||
props: ['token',], | props: ['token', 'preferred'], | ||||
} | } | ||||
</script> | </script> |
@@ -28,7 +28,7 @@ | |||||
</div> | </div> | ||||
<PastOrders :orders="orders" v-else-if="active === '#orders'" id="main"> | <PastOrders :orders="orders" v-else-if="active === '#orders'" id="main"> | ||||
</PastOrders> | </PastOrders> | ||||
<NewOrder :token="token" :active="active" :credits="user.credits" v-else-if="active === | <NewOrder :preferred="user.payment_method" :token="token" :active="active" :credits="user.credits" v-else-if="active === | ||||
'#new-order' || active === '#credits'" id="main" @update-user='getUser' @update-orders='getOrders'> | '#new-order' || active === '#credits'" id="main" @update-user='getUser' @update-orders='getOrders'> | ||||
</NewOrder> | </NewOrder> | ||||
<div id="main" v-else-if="active === '#exit'"> | <div id="main" v-else-if="active === '#exit'"> | ||||
@@ -3,7 +3,7 @@ | |||||
<div v-for="(card, index) in cards" :key="card.id" class="saved-card"> | <div v-for="(card, index) in cards" :key="card.id" class="saved-card"> | ||||
<span>{{card.card.brand[0].toUpperCase() + card.card.brand.substring(1)}} | <span>{{card.card.brand[0].toUpperCase() + card.card.brand.substring(1)}} | ||||
(••••{{card.card.last4}})</span> | (••••{{card.card.last4}})</span> | ||||
<input :checked="index === 0" :value="card.id" name="selected-card" type="radio" | <input :checked="index === 0 || card.id == preferred" :value="card.id" name="selected-card" type="radio" | ||||
@change="$emit('update:pickedCard', card.id)"> | @change="$emit('update:pickedCard', card.id)"> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
@@ -22,7 +22,7 @@ | |||||
unmounted() { | unmounted() { | ||||
this.$emit('update:pickedCard', null) | this.$emit('update:pickedCard', null) | ||||
}, | }, | ||||
props: ['token', 'cards'], | props: ['token', 'cards', 'preferred'], | ||||
emits: ['update:pickedCard'] | emits: ['update:pickedCard'] | ||||
} | } | ||||
</script> | </script> |
@@ -81,7 +81,7 @@ | |||||
</template> | </template> | ||||
<credits @purchase-complete="$emit('updateUser')" :token="token" v-if="page == 'credits'"></credits> | <credits @purchase-complete="$emit('updateUser')" :preferred="preferred" :token="token" v-if="page == 'credits'"></credits> | ||||
</div> | </div> | ||||
</template> | </template> | ||||
@@ -163,7 +163,7 @@ export default { | |||||
paying: false, url: '', completed: false} | paying: false, url: '', completed: false} | ||||
}, | }, | ||||
components: {ServicePane, Loading, Credits}, | components: {ServicePane, Loading, Credits}, | ||||
props: ['token', 'credits', 'active'], | props: ['token', 'credits', 'active', 'preferred'], | ||||
created() { | created() { | ||||
fetch("/panel/services", { | fetch("/panel/services", { | ||||
method: 'GET', | method: 'GET', | ||||
@@ -3,7 +3,7 @@ | |||||
<h3>Settings</h3> | <h3>Settings</h3> | ||||
<section class="billing-pane"> | <section class="billing-pane"> | ||||
<h4>Billing</h4> | <h4>Billing</h4> | ||||
<edit-cards :token="token"></edit-cards> | <edit-cards :preferred="user.payment_method" :token="token"></edit-cards> | ||||
</section> | </section> | ||||
<section class="change-name-pane"> | <section class="change-name-pane"> | ||||
<h4>Name</h4> | <h4>Name</h4> | ||||
@@ -63,6 +63,13 @@ Route::get('/panel/user', function (Request $request) { | |||||
return $request->user(); | return $request->user(); | ||||
})->middleware([ 'auth', 'verified' ]); | })->middleware([ 'auth', 'verified' ]); | ||||
Route::post('/panel/change-card', function (Request $request) { | |||||
return App::make(BillingController::class)->changeDefaultCard($request->card); | |||||
})->middleware([ 'auth', 'verified' ]); | |||||
Route::post('/panel/delete-card', [BillingController::class, | |||||
'deleteCard'])->middleware([ 'auth', 'verified' ]); | |||||
Route::get('/panel/orders', [UserController::class, | Route::get('/panel/orders', [UserController::class, | ||||
'getOrders'])->middleware([ 'auth', 'verified' ]); | 'getOrders'])->middleware([ 'auth', 'verified' ]); | ||||