Browse Source

Setup card payments

tags/v0.1.0
Immanuel Onyeka 3 years ago
parent
commit
f027f0a407
4 changed files with 120 additions and 12 deletions
  1. +1
    -0
      .env
  2. +4
    -0
      app/Http/Controllers/BillingController.php
  3. +75
    -11
      resources/js/panel/credits.vue
  4. +40
    -1
      resources/scss/main.scss

+ 1
- 0
.env View File

@@ -49,4 +49,5 @@ MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"

STRIPE_KEY=pk_test_51IeSwmBPMoXn2pf9VrXisJ7ubIjWHfnNWm1Iac8WB8H7hmBiJAw3GZrD8If9shKoSfSvT9my9lGdv0O0UzsbI4Iq00gn0MpA8S
VUE_APP_STRIPE_KEY=pk_test_51IeSwmBPMoXn2pf9VrXisJ7ubIjWHfnNWm1Iac8WB8H7hmBiJAw3GZrD8If9shKoSfSvT9my9lGdv0O0UzsbI4Iq00gn0MpA8S
STRIPE_SECRET=sk_test_51IeSwmBPMoXn2pf9h2APPrSD8oGc5RmsO8xj6dkVtM6FrHtD84Gw5M6Mm4epaicTqRm4hpuTny8E593hNdAiKMes006IBMfAcc

+ 4
- 0
app/Http/Controllers/BillingController.php View File

@@ -13,6 +13,7 @@ class BillingController extends Controller
//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
public function secret(Request $request) {
Stripe::setApiKey(env('STRIPE_SECRET'));
Log::debug($request->packs);
@@ -21,9 +22,12 @@ class BillingController extends Controller
$request->packs[ 'credits50' ]*5499 + $request->packs[ 'credits100' ]*10999
+ $request->packs[ 'credits1000' ]*101000;

// Pass customer and setup_future_usage values here. Maybe metadata can
// also hold pack information
$intent = PaymentIntent::create([
'amount' => $amount,
'currency' => 'usd',
/* 'customer' => Auth::user().customer_id, */
'metadata' => ['integration_check' => 'accept_a_payment']
]);



+ 75
- 11
resources/js/panel/credits.vue View File

@@ -15,23 +15,42 @@
<div class="credits-pane"><div><h2>1000 Credits</h2><span>+150 Free Credits</span></div>
<h3>$1010.00</h3> <div><span>Qty</span><input min="0" max="1000" v-model="packs.credits1000" type="number"></div>
</div>
<h3>Total: ${{total.toLocaleString('en')}}</h3>
<div id="credits-errors"></div>
</section>

<section id="payment-section">
<h4>Payment Method</h4>
<form id="payment-form" action="">

<label for="name">Name on Card</label>
<input id="billing-name" type="name">
<div id="card-element"></div>
<div id="card-errors"></div>
<div id=save-card>
<input name="save-card" value="Save card" type="checkbox">
<label for="">Save Card</label>
</div>

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

<section class="credits-confirm">
<h3>Total: ${{total.toLocaleString('en')}}</h3>
<button @click="getSecret" :disabled="total == 0 || loading" class="brand-btn">Continue</button>
<button @click="pay" :disabled="total == 0 || loading" class="brand-btn">Pay</button>
</section>
</template>

<script>

function total() {
return this.packs.credits10*10.99 + this.packs.credits50*54.99
+ this.packs.credits100*109.99 + this.packs.credits1000*1010
}

//Gets the secret key specific to chosen payment amount and user
function getSecret() {
fetch('/panel/secret', {
this.loading = true
return fetch('/panel/secret', {
method: 'POST',
headers: {'Content-Type': 'application/json',
'Accept': 'application/json',
@@ -41,21 +60,66 @@ function getSecret() {
if (response.ok) {
return response.text()
} else {
//handle errors here
console.log('an error occured')
document.getElementById('credits-errors').textContent =
`${response.status}: ${response.statusText}`
}
}).then(secret => {
this.loading = false
return secret
})
}

function mountPaymentForm() {
this.card = this.stripe.elements().create('card')
this.card.mount('#card-element')

this.card.on('change', function(event) {
let displayError = document.getElementById('card-errors');
if (event.error) {
displayError.textContent = event.error.message;
} else {
displayError.textContent = '';
}
});
}

//Gets key from the server then sends it with stripe
function pay() {
this.getSecret().then(secret => {
if (!secret) {return}
return this.stripe.confirmCardPayment(secret, {
payment_method: {
card: this.card,
billing_details: {name:
document.getElementById('billing-name').value}
}
})
}).then(result => {
if (result.error) {
document.getElementById('payment-error').textContent =
result.error.message
} else if (result.paymentIntent.status === 'succeeded') {
document.getElementById('payment-error').textContent =
'Purchase completed'
} else {
document.getElementById('payment-error').textContent =
'An unknown error occured'
}
}).then(data => {
console.log(data)
})
}

export default {
data() {
return {packs: {credits10: 0, credits50: 0,
credits100: 0, credits1000: 0}, loading: false}
credits100: 0, credits1000: 0}, loading: false, stripe:
Stripe(process.env.VUE_APP_STRIPE_KEY), card:
null}
},
computed: {total},
methods: {getSecret},
props: ['token']
methods: {getSecret, mountPaymentForm, pay},
props: ['token'],
mounted() {
this.mountPaymentForm()
}
}
</script>

+ 40
- 1
resources/scss/main.scss View File

@@ -1175,7 +1175,7 @@ button .loading-icon {
margin: 0;
}

.credits-confirm h3 {
.select-credits h3 {
text-align: center;
}

@@ -1194,3 +1194,42 @@ button .loading-icon {
font-size: 1.1rem;
min-width: 7em;
}

div#card-errors {
margin: 5%;
text-align: center;
}

#payment-section {
h4 {
text-align: center;
}

form {
margin-top: 2em;
}

label {
display: block;
}

input {
margin: 1em 0;
}

#save-card {
display: flex;
gap: 10px;
align-items: center;
justify-content: center;
}
}

#card-element {
border: 2px solid grey;
border-radius: 4px;
padding: 10px;
input {
}

}

Loading…
Cancel
Save