168 lines
4.1 KiB
Vue
168 lines
4.1 KiB
Vue
<template>
|
||
<div>
|
||
<label
|
||
class="block"
|
||
:class="{
|
||
'pointer-events-none h-0.5 opacity-0': step != 'Add Card Details',
|
||
'mt-4': step == 'Add Card Details'
|
||
}"
|
||
>
|
||
<span class="text-sm leading-4 text-gray-700">
|
||
信用卡或借记卡
|
||
</span>
|
||
<div class="form-input mt-2 block w-full pl-3" ref="cardElementRef"></div>
|
||
<ErrorMessage class="mt-1" :message="cardErrorMessage" />
|
||
</label>
|
||
|
||
<div v-if="step == 'Setting up Stripe'" class="mt-8 flex justify-center">
|
||
<Spinner class="h-4 w-4 text-gray-700" />
|
||
</div>
|
||
<ErrorMessage
|
||
class="mt-2"
|
||
:message="createPaymentIntent.error || errorMessage"
|
||
/>
|
||
<div class="mt-8">
|
||
<Button
|
||
v-if="step == 'Get Amount'"
|
||
class="w-full"
|
||
size="md"
|
||
variant="solid"
|
||
label="继续使用Stripe支付"
|
||
:loading="createPaymentIntent.loading"
|
||
@click="createPaymentIntent.submit()"
|
||
/>
|
||
<Button
|
||
v-else-if="step == 'Add Card Details'"
|
||
class="w-full"
|
||
size="md"
|
||
variant="solid"
|
||
label="通过Stripe支付"
|
||
:loading="paymentInProgress"
|
||
@click="onBuyClick"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
<script setup>
|
||
import { Button, ErrorMessage, Spinner, createResource } from 'jingrow-ui';
|
||
import { loadStripe } from '@stripe/stripe-js';
|
||
import { ref, nextTick, inject } from 'vue';
|
||
import { toast } from 'vue-sonner';
|
||
import { DashboardError } from '../../utils/error';
|
||
|
||
const props = defineProps({
|
||
amount: {
|
||
type: Number,
|
||
default: 0
|
||
},
|
||
minimumAmount: {
|
||
type: Number,
|
||
default: 0
|
||
}
|
||
});
|
||
|
||
const emit = defineEmits(['success']);
|
||
|
||
const team = inject('team');
|
||
|
||
const step = ref('Get Amount');
|
||
const clientSecret = ref(null);
|
||
const cardErrorMessage = ref(null);
|
||
const errorMessage = ref(null);
|
||
const paymentInProgress = ref(false);
|
||
|
||
const stripe = ref(null);
|
||
const card = ref(null);
|
||
const elements = ref(null);
|
||
const ready = ref(false);
|
||
|
||
const cardElementRef = ref(null);
|
||
|
||
const createPaymentIntent = createResource({
|
||
url: 'jcloud.api.billing.create_payment_intent_for_buying_credits',
|
||
params: { amount: props.amount },
|
||
validate() {
|
||
if (props.amount < props.minimumAmount && !team.pg.jerp_partner) {
|
||
throw new DashboardError(
|
||
`金额必须大于或等于 ${props.minimumAmount}`
|
||
);
|
||
}
|
||
},
|
||
async onSuccess(data) {
|
||
step.value = 'Setting up Stripe';
|
||
let { publishable_key, client_secret } = data;
|
||
clientSecret.value = client_secret;
|
||
stripe.value = await loadStripe(publishable_key);
|
||
elements.value = stripe.value.elements();
|
||
const style = {
|
||
base: {
|
||
color: '#171717',
|
||
fontFamily: [
|
||
'ui-sans-serif',
|
||
'system-ui',
|
||
'-apple-system',
|
||
'BlinkMacSystemFont',
|
||
'"Segoe UI"',
|
||
'Roboto',
|
||
'"Helvetica Neue"',
|
||
'Arial',
|
||
'"Noto Sans"',
|
||
'sans-serif',
|
||
'"Apple Color Emoji"',
|
||
'"Segoe UI Emoji"',
|
||
'"Segoe UI Symbol"',
|
||
'"Noto Color Emoji"'
|
||
].join(', '),
|
||
fontSmoothing: 'antialiased',
|
||
fontSize: '13px',
|
||
'::placeholder': {
|
||
color: '#C7C7C7'
|
||
}
|
||
},
|
||
invalid: {
|
||
color: '#7C7C7C',
|
||
iconColor: '#7C7C7C'
|
||
}
|
||
};
|
||
card.value = elements.value.create('card', {
|
||
hidePostalCode: true,
|
||
style: style,
|
||
classes: {
|
||
complete: '',
|
||
focus: 'bg-gray-100'
|
||
}
|
||
});
|
||
|
||
step.value = 'Add Card Details';
|
||
nextTick(() => {
|
||
card.value.mount(cardElementRef.value);
|
||
});
|
||
|
||
card.value.addEventListener('change', event => {
|
||
cardErrorMessage.value = event.error?.message || null;
|
||
});
|
||
card.value.addEventListener('ready', () => {
|
||
ready.value = true;
|
||
});
|
||
}
|
||
});
|
||
|
||
async function onBuyClick() {
|
||
paymentInProgress.value = true;
|
||
let payload = await stripe.value.confirmCardPayment(clientSecret.value, {
|
||
payment_method: { card: card.value }
|
||
});
|
||
|
||
if (payload.error) {
|
||
errorMessage.value = payload.error.message;
|
||
paymentInProgress.value = false;
|
||
} else {
|
||
toast.success(
|
||
'支付处理成功,我们将在收到Stripe确认后尽快更新您的账户'
|
||
);
|
||
paymentInProgress.value = false;
|
||
emit('success');
|
||
errorMessage.value = null;
|
||
}
|
||
}
|
||
</script> |