ソースを参照

Add and retrieve saved cards

tags/v0.1.0
コミット
593c6b297b
6個のファイルの変更107行の追加22行の削除
  1. +10
    -1
      app/Http/Controllers/BillingController.php
  2. +5
    -0
      config/services.php
  3. +52
    -21
      resources/js/panel/credits.vue
  4. +28
    -0
      resources/js/panel/saved-cards.vue
  5. +9
    -0
      resources/scss/main.scss
  6. +3
    -0
      routes/web.php

+ 10
- 1
app/Http/Controllers/BillingController.php ファイルの表示

@@ -6,6 +6,7 @@ use Illuminate\Http\Request;
use Stripe\Stripe;
use Stripe\Customer;
use Stripe\PaymentIntent;
use Stripe\PaymentMethod;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Auth;

@@ -29,9 +30,17 @@ class BillingController extends Controller
'amount' => $amount,
'currency' => 'usd',
'customer' => Auth::user()->customer_id,
'metadata' => ['integration_check' => 'accept_a_payment']
'metadata' => ['transaction_id' => 'accept_a_payment']
]);

return $intent->client_secret;
}

public function getCards() {
Stripe::setApiKey(env('STRIPE_SECRET'));
return PaymentMethod::all([
'customer' => Auth::user()->customer_id,
'type' => 'card'
]);
}
}

+ 5
- 0
config/services.php ファイルの表示

@@ -30,4 +30,9 @@ return [
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
],

'stripe' => [
'key' => env('STRIPE_KEY'),
'secret' => env('STRIPE_SECRET'),
],

];

+ 52
- 21
resources/js/panel/credits.vue ファイルの表示

@@ -19,9 +19,8 @@
<div id="credits-errors"></div>
</section>


<section id="payment-section">
<h4>Payment Method</h4>
<h4>Select a payment method</h4>

<div class="sliding-menu">
<a @click="selectSaved = true" :class="{selected: selectSaved}">Saved Card</a>
@@ -29,15 +28,19 @@
<div :class="{right: !selectSaved}" class="menu-slider"><div></div></div>
</div>

<saved-cards v-model:picked-card="picked" :cards="cards"
:token="token" v-if="selectSaved"></saved-cards>

<payment-card @set-card="(c) => {card = c}" @card-valid="(val) => {cardValid = val}"
@update-billing-name="billingName = $event.target.value" :stripe="stripe"
v-if="!selectSaved"></payment-card>
v-if="!selectSaved">
</payment-card>

<div id="payment-error"></div>
</section>

<section class="credits-confirm">
<button @click="pay" :disabled="total == 0 || loading || !cardValid || !billingName"
<button @click="pay" :disabled="!ready"
class="brand-btn">Buy<loading v-if="loading"></loading></button>
</section>
</template>
@@ -45,6 +48,7 @@
<script>
import Loading from '../icons/loading.vue'
import PaymentCard from './payment-card.vue'
import SavedCards from './saved-cards.vue'

function total() {
return this.packs.credits10*10.99 + this.packs.credits50*54.99
@@ -75,20 +79,27 @@ function getSecret() {
}

//Gets key from the server then sends it with stripe
//Maybe it should asl attach the user's receipt email
function pay() {
console.log()
console.log('paying')
this.getSecret().then(secret => {
if (!secret) {return}
this.loading = true
return this.stripe.confirmCardPayment(secret, {
payment_method: {
card: this.card,
billing_details: {name:
document.getElementById('billing-name').value},
},
setup_future_usage: document.querySelector(
"#save-card input").value == 'on' ? 'off_session' : null
})
if (!this.selectSaved) {
return this.stripe.confirmCardPayment(secret, {
payment_method: {
card: this.card,
billing_details: {name:
document.getElementById('billing-name').value},
},
setup_future_usage: document.querySelector(
"#save-card input").value == 'on' ? 'off_session' : null
})
} else {
return this.stripe.confirmCardPayment(secret, {
payment_method: this.picked
})
}
}).then(result => {
if (result.error) {
document.getElementById('payment-error').textContent =
@@ -104,19 +115,39 @@ function pay() {
})
}

function getCards() {
fetch('/panel/cards', {
method: 'GET',
headers: {'Content-Type': 'application/json',
'Accept': 'application/json',
'X-XSRF-TOKEN': this.token}
}).then((response) => {response.json().then(data => {
this.cards = data.data
if (this.cards) {
this.picked = this.cards[0].id
}
})})
}
function ready() {
return this.total != 0 && !this.loading && ((this.cardValid &&
this.billingName) || this.picked)
}

export default {
components:{Loading, PaymentCard},
components:{Loading, PaymentCard, SavedCards},
data() {
return {packs: {credits10: 0, credits50: 0,
credits100: 0, credits1000: 0}, loading: false, stripe:
Stripe(process.env.VUE_APP_STRIPE_KEY), card:
null, billingName: null, selectSaved: true, cardValid: false}
window.Stripe(process.env.VUE_APP_STRIPE_KEY), card:
null, billingName: null, selectSaved: true, cardValid: false, cards:
null, picked: null
}
},
computed: {total},
methods: {getSecret, pay},
computed: {total, ready},
methods: {getSecret, pay, getCards},
props: ['token'],
mounted() {
/* this.mountPaymentForm() */
created() {
this.getCards()
}
}
</script>

+ 28
- 0
resources/js/panel/saved-cards.vue ファイルの表示

@@ -0,0 +1,28 @@
<template>
<div v-if="cards">
<div v-for="(card, index) in cards" :key="card.id" class="saved-card">
<span>{{card.card.brand[0].toUpperCase() + card.card.brand.substring(1)}}
(••••{{card.card.last4}})</span>
<input :checked="index === 0" :value="card.id" name="selected-card" type="radio"
@change="$emit('update:pickedCard', card.id)">
</div>
</div>
</template>

<script>
export default {
data() {
return {}
},
mounted() {
if (this.cards) {
this.$emit('update:pickedCard', this.cards[0].id)
}
},
unmounted() {
this.$emit('update:pickedCard', null)
},
props: ['token', 'cards'],
emits: ['update:pickedCard']
}
</script>

+ 9
- 0
resources/scss/main.scss ファイルの表示

@@ -1252,3 +1252,12 @@ div#card-errors {
border-radius: 2px;
}
}

.saved-card {
display: flex;
gap: 15px;
align-items: center;
justify-content: space-around;
max-width: 15em;
margin: auto;
}

+ 3
- 0
routes/web.php ファイルの表示

@@ -89,3 +89,6 @@ Route::post('/panel/orders', [TransactionController::class,

Route::post('/panel/secret', [BillingController::class,
'secret'])->middleware([ 'auth', 'verified' ]);

Route::get('/panel/cards', [BillingController::class,
'getCards'])->middleware([ 'auth', 'verified' ]);

読み込み中…
キャンセル
保存