jcloud/dashboard/src2/components/BuyPrepaidCredits.vue

202 lines
4.6 KiB
Vue

<template>
<div>
<FormControl
v-if="step == 'Get Amount'"
class="mb-2"
label="Credits"
v-model.number="creditsToBuy"
name="amount"
autocomplete="off"
type="number"
:min="minimumAmount"
/>
<label
class="block"
:class="{
'h-0.5 opacity-0': step != 'Add Card Details',
'mt-4': step == 'Add Card Details'
}"
>
<span class="text-sm leading-4 text-gray-700">
Credit or Debit Card
</span>
<div
class="form-input mt-2 block w-full py-2 pl-3"
ref="card-element"
></div>
<ErrorMessage class="mt-1" :message="cardErrorMessage" />
</label>
<FormControl
v-if="step == 'Get Amount'"
label="Total Amount"
disabled
v-model="total"
name="total"
autocomplete="off"
type="number"
/>
<div v-if="step == 'Setting up Stripe'" class="mt-8 flex justify-center">
<Spinner class="h-4 w-4 text-gray-600" />
</div>
<ErrorMessage
class="mt-2"
:message="$resources.createPaymentIntent.error || errorMessage"
/>
<div class="mt-4 flex w-full justify-between">
<StripeLogo />
<div v-if="step == 'Get Amount'">
<Button
variant="solid"
@click="$resources.createPaymentIntent.submit()"
:loading="$resources.createPaymentIntent.loading"
>
Next
</Button>
</div>
<div v-if="step == 'Add Card Details'">
<Button @click="$emit('cancel')"> Cancel </Button>
<Button
class="ml-2"
variant="solid"
@click="onBuyClick"
:loading="paymentInProgress"
>
Buy Credits
</Button>
</div>
</div>
</div>
</template>
<script>
import StripeLogo from '@/components/StripeLogo.vue';
import { loadStripe } from '@stripe/stripe-js';
export default {
name: 'BuyPrepaidCredits',
components: {
StripeLogo
},
props: {
minimumAmount: {
default: 0
}
},
mounted() {
this.updateTotal();
},
watch: {
creditsToBuy() {
this.updateTotal();
}
},
data() {
return {
step: 'Get Amount', // Get Amount / Add Card Details
clientSecret: null,
creditsToBuy: this.minimumAmount || null,
total: this.minimumAmount,
cardErrorMessage: null,
errorMessage: null,
paymentInProgress: false
};
},
resources: {
createPaymentIntent() {
return {
url: 'jcloud.api.billing.create_payment_intent_for_buying_credits',
params: {
amount: this.creditsToBuy
},
validate() {
if (
this.creditsToBuy < this.minimumAmount &&
!this.$account.team.jerp_partner
) {
return `Amount must be greater than ${this.minimumAmount}`;
}
},
async onSuccess(data) {
this.step = 'Setting up Stripe';
let { publishable_key, client_secret } = data;
this.clientSecret = client_secret;
this.stripe = await loadStripe(publishable_key);
this.elements = this.stripe.elements();
let theme = this.$theme;
let style = {
base: {
color: theme.colors.black,
fontFamily: theme.fontFamily.sans.join(', '),
fontSmoothing: 'antialiased',
fontSize: '13px',
'::placeholder': {
color: theme.colors.gray['400']
}
},
invalid: {
color: theme.colors.red['600'],
iconColor: theme.colors.red['600']
}
};
this.card = this.elements.create('card', {
hidePostalCode: true,
style: style,
classes: {
complete: '',
focus: 'bg-gray-100'
}
});
this.step = 'Add Card Details';
this.$nextTick(() => {
this.card.mount(this.$refs['card-element']);
});
this.card.addEventListener('change', event => {
this.cardErrorMessage = event.error?.message || null;
});
this.card.addEventListener('ready', () => {
this.ready = true;
});
}
};
}
},
methods: {
updateTotal() {
if (this.$account.team.currency === 'CNY') {
this.total = Number(
(
this.creditsToBuy +
this.creditsToBuy * this.$account.billing_info.gst_percentage
).toFixed(2)
);
} else {
this.total = this.creditsToBuy;
}
},
setupStripe() {
this.$resources.createPaymentIntent.submit();
},
async onBuyClick() {
this.paymentInProgress = true;
let payload = await this.stripe.confirmCardPayment(this.clientSecret, {
payment_method: {
card: this.card
}
});
this.paymentInProgress = false;
if (payload.error) {
this.errorMessage = payload.error.message;
} else {
this.$emit('success');
this.errorMessage = null;
this.creditsToBuy = null;
}
}
}
};
</script>