jcloud/dashboard/src2/components/billing/BuyCreditsStripe.vue
2025-04-12 17:39:38 +08:00

168 lines
4.1 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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>