You've already forked AstralRinth
forked from didirus/AstralRinth
refactor: migrate to common eslint+prettier configs (#4168)
* refactor: migrate to common eslint+prettier configs * fix: prettier frontend * feat: config changes * fix: lint issues * fix: lint * fix: type imports * fix: cyclical import issue * fix: lockfile * fix: missing dep * fix: switch to tabs * fix: continue switch to tabs * fix: rustfmt parity * fix: moderation lint issue * fix: lint issues * fix: ui intl * fix: lint issues * Revert "fix: rustfmt parity" This reverts commit cb99d2376c321d813d4b7fc7e2a213bb30a54711. * feat: revert last rs
This commit is contained in:
@@ -1,41 +1,41 @@
|
||||
import { computed, onMounted, onUnmounted, type Ref } from 'vue'
|
||||
import { useElementSize } from '@vueuse/core'
|
||||
import { computed, onMounted, onUnmounted, type Ref } from 'vue'
|
||||
|
||||
export interface DynamicFontSizeOptions {
|
||||
containerElement: Ref<HTMLElement | null>
|
||||
text: Ref<string | undefined>
|
||||
baseFontSize?: number
|
||||
minFontSize?: number
|
||||
maxFontSize?: number
|
||||
availableWidthRatio?: number
|
||||
maxContainerWidth?: number
|
||||
padding?: number
|
||||
fontFamily?: string
|
||||
fontWeight?: string | number
|
||||
containerElement: Ref<HTMLElement | null>
|
||||
text: Ref<string | undefined>
|
||||
baseFontSize?: number
|
||||
minFontSize?: number
|
||||
maxFontSize?: number
|
||||
availableWidthRatio?: number
|
||||
maxContainerWidth?: number
|
||||
padding?: number
|
||||
fontFamily?: string
|
||||
fontWeight?: string | number
|
||||
}
|
||||
|
||||
export function useDynamicFontSize(options: DynamicFontSizeOptions) {
|
||||
const {
|
||||
containerElement,
|
||||
text,
|
||||
baseFontSize = 1.25,
|
||||
minFontSize = 0.75,
|
||||
maxFontSize = 2,
|
||||
availableWidthRatio = 0.9,
|
||||
maxContainerWidth = 400,
|
||||
padding = 24,
|
||||
fontFamily = 'inherit',
|
||||
fontWeight = 'inherit',
|
||||
} = options
|
||||
const {
|
||||
containerElement,
|
||||
text,
|
||||
baseFontSize = 1.25,
|
||||
minFontSize = 0.75,
|
||||
maxFontSize = 2,
|
||||
availableWidthRatio = 0.9,
|
||||
maxContainerWidth = 400,
|
||||
padding = 24,
|
||||
fontFamily = 'inherit',
|
||||
fontWeight = 'inherit',
|
||||
} = options
|
||||
|
||||
const { width: containerWidth } = useElementSize(containerElement)
|
||||
let measurementElement: HTMLElement | null = null
|
||||
const { width: containerWidth } = useElementSize(containerElement)
|
||||
let measurementElement: HTMLElement | null = null
|
||||
|
||||
const createMeasurementElement = () => {
|
||||
if (measurementElement) return measurementElement
|
||||
const createMeasurementElement = () => {
|
||||
if (measurementElement) return measurementElement
|
||||
|
||||
measurementElement = document.createElement('div')
|
||||
measurementElement.style.cssText = `
|
||||
measurementElement = document.createElement('div')
|
||||
measurementElement.style.cssText = `
|
||||
position: absolute;
|
||||
top: -9999px;
|
||||
left: -9999px;
|
||||
@@ -45,73 +45,73 @@ export function useDynamicFontSize(options: DynamicFontSizeOptions) {
|
||||
font-family: ${fontFamily};
|
||||
font-weight: ${fontWeight};
|
||||
`
|
||||
measurementElement.setAttribute('aria-hidden', 'true')
|
||||
document.body.appendChild(measurementElement)
|
||||
measurementElement.setAttribute('aria-hidden', 'true')
|
||||
document.body.appendChild(measurementElement)
|
||||
|
||||
return measurementElement
|
||||
}
|
||||
return measurementElement
|
||||
}
|
||||
|
||||
const cleanupMeasurementElement = () => {
|
||||
if (measurementElement?.parentNode) {
|
||||
measurementElement.parentNode.removeChild(measurementElement)
|
||||
measurementElement = null
|
||||
}
|
||||
}
|
||||
const cleanupMeasurementElement = () => {
|
||||
if (measurementElement?.parentNode) {
|
||||
measurementElement.parentNode.removeChild(measurementElement)
|
||||
measurementElement = null
|
||||
}
|
||||
}
|
||||
|
||||
const measureTextWidth = (textContent: string, fontSize: number): number => {
|
||||
if (!textContent) return 0
|
||||
const measureTextWidth = (textContent: string, fontSize: number): number => {
|
||||
if (!textContent) return 0
|
||||
|
||||
const element = createMeasurementElement()
|
||||
element.style.fontSize = `${fontSize}rem`
|
||||
element.textContent = textContent
|
||||
const element = createMeasurementElement()
|
||||
element.style.fontSize = `${fontSize}rem`
|
||||
element.textContent = textContent
|
||||
|
||||
return element.getBoundingClientRect().width
|
||||
}
|
||||
return element.getBoundingClientRect().width
|
||||
}
|
||||
|
||||
const findOptimalFontSize = (textContent: string, availableWidth: number): number => {
|
||||
let low = minFontSize
|
||||
let high = maxFontSize
|
||||
let bestSize = minFontSize
|
||||
const findOptimalFontSize = (textContent: string, availableWidth: number): number => {
|
||||
let low = minFontSize
|
||||
let high = maxFontSize
|
||||
let bestSize = minFontSize
|
||||
|
||||
const maxWidth = measureTextWidth(textContent, maxFontSize)
|
||||
if (maxWidth <= availableWidth) return maxFontSize
|
||||
const maxWidth = measureTextWidth(textContent, maxFontSize)
|
||||
if (maxWidth <= availableWidth) return maxFontSize
|
||||
|
||||
for (let i = 0; i < 8; i++) {
|
||||
const mid = (low + high) / 2
|
||||
const width = measureTextWidth(textContent, mid)
|
||||
for (let i = 0; i < 8; i++) {
|
||||
const mid = (low + high) / 2
|
||||
const width = measureTextWidth(textContent, mid)
|
||||
|
||||
if (width <= availableWidth) {
|
||||
bestSize = mid
|
||||
low = mid
|
||||
} else {
|
||||
high = mid
|
||||
}
|
||||
if (width <= availableWidth) {
|
||||
bestSize = mid
|
||||
low = mid
|
||||
} else {
|
||||
high = mid
|
||||
}
|
||||
|
||||
if (high - low < 0.01) break
|
||||
}
|
||||
if (high - low < 0.01) break
|
||||
}
|
||||
|
||||
return Math.max(bestSize, minFontSize)
|
||||
}
|
||||
return Math.max(bestSize, minFontSize)
|
||||
}
|
||||
|
||||
const fontSize = computed(() => {
|
||||
if (!text.value || !containerWidth.value) return `${baseFontSize}rem`
|
||||
const fontSize = computed(() => {
|
||||
if (!text.value || !containerWidth.value) return `${baseFontSize}rem`
|
||||
|
||||
const availableWidth =
|
||||
Math.min(containerWidth.value * availableWidthRatio, maxContainerWidth) - padding
|
||||
const availableWidth =
|
||||
Math.min(containerWidth.value * availableWidthRatio, maxContainerWidth) - padding
|
||||
|
||||
const baseWidth = measureTextWidth(text.value, baseFontSize)
|
||||
if (baseWidth <= availableWidth) return `${baseFontSize}rem`
|
||||
const baseWidth = measureTextWidth(text.value, baseFontSize)
|
||||
if (baseWidth <= availableWidth) return `${baseFontSize}rem`
|
||||
|
||||
const optimalSize = findOptimalFontSize(text.value, availableWidth)
|
||||
return `${optimalSize}rem`
|
||||
})
|
||||
const optimalSize = findOptimalFontSize(text.value, availableWidth)
|
||||
return `${optimalSize}rem`
|
||||
})
|
||||
|
||||
onMounted(createMeasurementElement)
|
||||
onUnmounted(cleanupMeasurementElement)
|
||||
onMounted(createMeasurementElement)
|
||||
onUnmounted(cleanupMeasurementElement)
|
||||
|
||||
return {
|
||||
fontSize,
|
||||
containerWidth,
|
||||
cleanup: cleanupMeasurementElement,
|
||||
}
|
||||
return {
|
||||
fontSize,
|
||||
containerWidth,
|
||||
cleanup: cleanupMeasurementElement,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
import { createFormatter, type FormatOptions, type Formatter } from '@vintl/how-ago'
|
||||
import type { IntlController } from '@vintl/vintl/controller'
|
||||
import { useVIntl } from '@vintl/vintl'
|
||||
import type { IntlController } from '@vintl/vintl/controller'
|
||||
import { computed } from 'vue'
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
const formatters = new WeakMap<IntlController<any>, Formatter>()
|
||||
|
||||
export function useRelativeTime(): Formatter {
|
||||
const vintl = useVIntl()
|
||||
const vintl = useVIntl()
|
||||
|
||||
let formatter = formatters.get(vintl)
|
||||
let formatter = formatters.get(vintl)
|
||||
|
||||
if (formatter == null) {
|
||||
const formatterRef = computed(() => createFormatter(vintl.intl))
|
||||
const defaultOptions: FormatOptions = { roundingMode: 'halfExpand' as const }
|
||||
if (formatter == null) {
|
||||
const formatterRef = computed(() => createFormatter(vintl.intl))
|
||||
const defaultOptions: FormatOptions = { roundingMode: 'halfExpand' as const }
|
||||
|
||||
formatter = (value, options) => formatterRef.value(value, { ...options, ...defaultOptions })
|
||||
formatters.set(vintl, formatter)
|
||||
}
|
||||
formatter = (value, options) => formatterRef.value(value, { ...options, ...defaultOptions })
|
||||
formatters.set(vintl, formatter)
|
||||
}
|
||||
|
||||
return formatter
|
||||
return formatter
|
||||
}
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export * from './how-ago'
|
||||
export * from './dynamic-font-size'
|
||||
export * from './how-ago'
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
import type Stripe from 'stripe'
|
||||
import { type Stripe as StripeJs, loadStripe, type StripeElements } from '@stripe/stripe-js'
|
||||
import { computed, ref, type Ref } from 'vue'
|
||||
import { loadStripe, type Stripe as StripeJs, type StripeElements } from '@stripe/stripe-js'
|
||||
import type { ContactOption } from '@stripe/stripe-js/dist/stripe-js/elements/address'
|
||||
import type Stripe from 'stripe'
|
||||
import { computed, type Ref, ref } from 'vue'
|
||||
|
||||
import type {
|
||||
ServerPlan,
|
||||
BasePaymentIntentResponse,
|
||||
ChargeRequestType,
|
||||
CreatePaymentIntentRequest,
|
||||
CreatePaymentIntentResponse,
|
||||
PaymentRequestType,
|
||||
ServerBillingInterval,
|
||||
UpdatePaymentIntentRequest,
|
||||
UpdatePaymentIntentResponse,
|
||||
BasePaymentIntentResponse,
|
||||
ChargeRequestType,
|
||||
CreatePaymentIntentRequest,
|
||||
CreatePaymentIntentResponse,
|
||||
PaymentRequestType,
|
||||
ServerBillingInterval,
|
||||
ServerPlan,
|
||||
UpdatePaymentIntentRequest,
|
||||
UpdatePaymentIntentResponse,
|
||||
} from '../utils/billing.ts'
|
||||
|
||||
// export type CreateElements = (
|
||||
@@ -24,383 +25,386 @@ import type {
|
||||
// }
|
||||
|
||||
export const useStripe = (
|
||||
publishableKey: string,
|
||||
customer: Stripe.Customer,
|
||||
paymentMethods: Stripe.PaymentMethod[],
|
||||
currency: string,
|
||||
product: Ref<ServerPlan | undefined>,
|
||||
interval: Ref<ServerBillingInterval>,
|
||||
region: Ref<string | undefined>,
|
||||
project: Ref<string | undefined>,
|
||||
initiatePayment: (
|
||||
body: CreatePaymentIntentRequest | UpdatePaymentIntentRequest,
|
||||
) => Promise<CreatePaymentIntentResponse | UpdatePaymentIntentResponse>,
|
||||
onError: (err: Error) => void,
|
||||
publishableKey: string,
|
||||
customer: Stripe.Customer,
|
||||
paymentMethods: Stripe.PaymentMethod[],
|
||||
currency: string,
|
||||
product: Ref<ServerPlan | undefined>,
|
||||
interval: Ref<ServerBillingInterval>,
|
||||
region: Ref<string | undefined>,
|
||||
project: Ref<string | undefined>,
|
||||
initiatePayment: (
|
||||
body: CreatePaymentIntentRequest | UpdatePaymentIntentRequest,
|
||||
) => Promise<CreatePaymentIntentResponse | UpdatePaymentIntentResponse>,
|
||||
onError: (err: Error) => void,
|
||||
) => {
|
||||
const stripe = ref<StripeJs | null>(null)
|
||||
const stripe = ref<StripeJs | null>(null)
|
||||
|
||||
let elements: StripeElements | undefined = undefined
|
||||
const elementsLoaded = ref<0 | 1 | 2>(0)
|
||||
const loadingElementsFailed = ref<boolean>(false)
|
||||
let elements: StripeElements | undefined = undefined
|
||||
const elementsLoaded = ref<0 | 1 | 2>(0)
|
||||
const loadingElementsFailed = ref<boolean>(false)
|
||||
|
||||
const paymentMethodLoading = ref(false)
|
||||
const loadingFailed = ref<string>()
|
||||
const paymentIntentId = ref<string>()
|
||||
const tax = ref<number>()
|
||||
const total = ref<number>()
|
||||
const confirmationToken = ref<string>()
|
||||
const submittingPayment = ref(false)
|
||||
const selectedPaymentMethod = ref<Stripe.PaymentMethod>()
|
||||
const inputtedPaymentMethod = ref<Stripe.PaymentMethod>()
|
||||
const clientSecret = ref<string>()
|
||||
const completingPurchase = ref<boolean>(false)
|
||||
const paymentMethodLoading = ref(false)
|
||||
const loadingFailed = ref<string>()
|
||||
const paymentIntentId = ref<string>()
|
||||
const tax = ref<number>()
|
||||
const total = ref<number>()
|
||||
const confirmationToken = ref<string>()
|
||||
const submittingPayment = ref(false)
|
||||
const selectedPaymentMethod = ref<Stripe.PaymentMethod>()
|
||||
const inputtedPaymentMethod = ref<Stripe.PaymentMethod>()
|
||||
const clientSecret = ref<string>()
|
||||
const completingPurchase = ref<boolean>(false)
|
||||
|
||||
async function initialize() {
|
||||
stripe.value = await loadStripe(publishableKey)
|
||||
}
|
||||
async function initialize() {
|
||||
stripe.value = await loadStripe(publishableKey)
|
||||
}
|
||||
|
||||
function createIntent(body: CreatePaymentIntentRequest): Promise<CreatePaymentIntentResponse> {
|
||||
return initiatePayment(body) as Promise<CreatePaymentIntentResponse>
|
||||
}
|
||||
function createIntent(body: CreatePaymentIntentRequest): Promise<CreatePaymentIntentResponse> {
|
||||
return initiatePayment(body) as Promise<CreatePaymentIntentResponse>
|
||||
}
|
||||
|
||||
function updateIntent(body: UpdatePaymentIntentRequest): Promise<UpdatePaymentIntentResponse> {
|
||||
return initiatePayment(body) as Promise<UpdatePaymentIntentResponse>
|
||||
}
|
||||
function updateIntent(body: UpdatePaymentIntentRequest): Promise<UpdatePaymentIntentResponse> {
|
||||
return initiatePayment(body) as Promise<UpdatePaymentIntentResponse>
|
||||
}
|
||||
|
||||
const planPrices = computed(() => {
|
||||
return product.value?.prices.find((x) => x.currency_code === currency)
|
||||
})
|
||||
const planPrices = computed(() => {
|
||||
return product.value?.prices.find((x) => x.currency_code === currency)
|
||||
})
|
||||
|
||||
const createElements = (options) => {
|
||||
const styles = getComputedStyle(document.body)
|
||||
const createElements = (options) => {
|
||||
const styles = getComputedStyle(document.body)
|
||||
|
||||
if (!stripe.value) {
|
||||
throw new Error('Stripe API not yet loaded')
|
||||
}
|
||||
if (!stripe.value) {
|
||||
throw new Error('Stripe API not yet loaded')
|
||||
}
|
||||
|
||||
elements = stripe.value.elements({
|
||||
appearance: {
|
||||
variables: {
|
||||
colorPrimary: styles.getPropertyValue('--color-brand'),
|
||||
colorBackground: styles.getPropertyValue('--experimental-color-button-bg'),
|
||||
colorText: styles.getPropertyValue('--color-base'),
|
||||
colorTextPlaceholder: styles.getPropertyValue('--color-secondary'),
|
||||
colorDanger: styles.getPropertyValue('--color-red'),
|
||||
fontFamily: styles.getPropertyValue('--font-standard'),
|
||||
spacingUnit: '0.25rem',
|
||||
borderRadius: '0.75rem',
|
||||
},
|
||||
},
|
||||
loader: 'never',
|
||||
...options,
|
||||
})
|
||||
elements = stripe.value.elements({
|
||||
appearance: {
|
||||
variables: {
|
||||
colorPrimary: styles.getPropertyValue('--color-brand'),
|
||||
colorBackground: styles.getPropertyValue('--experimental-color-button-bg'),
|
||||
colorText: styles.getPropertyValue('--color-base'),
|
||||
colorTextPlaceholder: styles.getPropertyValue('--color-secondary'),
|
||||
colorDanger: styles.getPropertyValue('--color-red'),
|
||||
fontFamily: styles.getPropertyValue('--font-standard'),
|
||||
spacingUnit: '0.25rem',
|
||||
borderRadius: '0.75rem',
|
||||
},
|
||||
},
|
||||
loader: 'never',
|
||||
...options,
|
||||
})
|
||||
|
||||
const paymentElement = elements.create('payment', {
|
||||
layout: {
|
||||
type: 'tabs',
|
||||
defaultCollapsed: false,
|
||||
},
|
||||
})
|
||||
paymentElement.mount('#payment-element')
|
||||
const paymentElement = elements.create('payment', {
|
||||
layout: {
|
||||
type: 'tabs',
|
||||
defaultCollapsed: false,
|
||||
},
|
||||
})
|
||||
paymentElement.mount('#payment-element')
|
||||
|
||||
const contacts: ContactOption[] = []
|
||||
const contacts: ContactOption[] = []
|
||||
|
||||
paymentMethods.forEach((method) => {
|
||||
const addr = method.billing_details?.address
|
||||
if (
|
||||
addr &&
|
||||
addr.line1 &&
|
||||
addr.city &&
|
||||
addr.postal_code &&
|
||||
addr.country &&
|
||||
addr.state &&
|
||||
method.billing_details.name
|
||||
) {
|
||||
contacts.push({
|
||||
address: {
|
||||
line1: addr.line1,
|
||||
line2: addr.line2 ?? undefined,
|
||||
city: addr.city,
|
||||
state: addr.state,
|
||||
postal_code: addr.postal_code,
|
||||
country: addr.country,
|
||||
},
|
||||
name: method.billing_details.name,
|
||||
})
|
||||
}
|
||||
})
|
||||
paymentMethods.forEach((method) => {
|
||||
const addr = method.billing_details?.address
|
||||
if (
|
||||
addr &&
|
||||
addr.line1 &&
|
||||
addr.city &&
|
||||
addr.postal_code &&
|
||||
addr.country &&
|
||||
addr.state &&
|
||||
method.billing_details.name
|
||||
) {
|
||||
contacts.push({
|
||||
address: {
|
||||
line1: addr.line1,
|
||||
line2: addr.line2 ?? undefined,
|
||||
city: addr.city,
|
||||
state: addr.state,
|
||||
postal_code: addr.postal_code,
|
||||
country: addr.country,
|
||||
},
|
||||
name: method.billing_details.name,
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
const addressElement = elements.create('address', {
|
||||
mode: 'billing',
|
||||
contacts: contacts.length > 0 ? contacts : undefined,
|
||||
})
|
||||
addressElement.mount('#address-element')
|
||||
const addressElement = elements.create('address', {
|
||||
mode: 'billing',
|
||||
contacts: contacts.length > 0 ? contacts : undefined,
|
||||
})
|
||||
addressElement.mount('#address-element')
|
||||
|
||||
return { elements, paymentElement, addressElement }
|
||||
}
|
||||
return { elements, paymentElement, addressElement }
|
||||
}
|
||||
|
||||
const primaryPaymentMethodId = computed<string | null>(() => {
|
||||
if (customer && customer.invoice_settings && customer.invoice_settings.default_payment_method) {
|
||||
const method = customer.invoice_settings.default_payment_method
|
||||
if (typeof method === 'string') {
|
||||
return method
|
||||
} else {
|
||||
return method.id
|
||||
}
|
||||
} else if (paymentMethods && paymentMethods[0] && paymentMethods[0].id) {
|
||||
return paymentMethods[0].id
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
})
|
||||
const primaryPaymentMethodId = computed<string | null>(() => {
|
||||
if (customer && customer.invoice_settings && customer.invoice_settings.default_payment_method) {
|
||||
const method = customer.invoice_settings.default_payment_method
|
||||
if (typeof method === 'string') {
|
||||
return method
|
||||
} else {
|
||||
return method.id
|
||||
}
|
||||
} else if (paymentMethods && paymentMethods[0] && paymentMethods[0].id) {
|
||||
return paymentMethods[0].id
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
})
|
||||
|
||||
const loadStripeElements = async () => {
|
||||
loadingFailed.value = undefined
|
||||
try {
|
||||
if (!customer && primaryPaymentMethodId.value) {
|
||||
paymentMethodLoading.value = true
|
||||
await refreshPaymentIntent(primaryPaymentMethodId.value, false)
|
||||
paymentMethodLoading.value = false
|
||||
}
|
||||
const loadStripeElements = async () => {
|
||||
loadingFailed.value = undefined
|
||||
try {
|
||||
if (!customer && primaryPaymentMethodId.value) {
|
||||
paymentMethodLoading.value = true
|
||||
await refreshPaymentIntent(primaryPaymentMethodId.value, false)
|
||||
paymentMethodLoading.value = false
|
||||
}
|
||||
|
||||
if (!selectedPaymentMethod.value) {
|
||||
elementsLoaded.value = 0
|
||||
if (!selectedPaymentMethod.value) {
|
||||
elementsLoaded.value = 0
|
||||
|
||||
const {
|
||||
elements: newElements,
|
||||
addressElement,
|
||||
paymentElement,
|
||||
} = createElements({
|
||||
mode: 'payment',
|
||||
currency: currency.toLowerCase(),
|
||||
amount: product.value?.prices.find((x) => x.currency_code === currency)?.prices.intervals[
|
||||
interval.value
|
||||
],
|
||||
paymentMethodCreation: 'manual',
|
||||
setupFutureUsage: 'off_session',
|
||||
})
|
||||
const {
|
||||
elements: newElements,
|
||||
addressElement,
|
||||
paymentElement,
|
||||
} = createElements({
|
||||
mode: 'payment',
|
||||
currency: currency.toLowerCase(),
|
||||
amount: product.value?.prices.find((x) => x.currency_code === currency)?.prices.intervals[
|
||||
interval.value
|
||||
],
|
||||
paymentMethodCreation: 'manual',
|
||||
setupFutureUsage: 'off_session',
|
||||
})
|
||||
|
||||
elements = newElements
|
||||
paymentElement.on('ready', () => {
|
||||
elementsLoaded.value += 1
|
||||
})
|
||||
addressElement.on('ready', () => {
|
||||
elementsLoaded.value += 1
|
||||
})
|
||||
}
|
||||
} catch (err) {
|
||||
loadingFailed.value = String(err)
|
||||
console.log(err)
|
||||
}
|
||||
}
|
||||
elements = newElements
|
||||
paymentElement.on('ready', () => {
|
||||
elementsLoaded.value += 1
|
||||
})
|
||||
addressElement.on('ready', () => {
|
||||
elementsLoaded.value += 1
|
||||
})
|
||||
}
|
||||
} catch (err) {
|
||||
loadingFailed.value = String(err)
|
||||
console.log(err)
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshPaymentIntent(id: string, confirmation: boolean) {
|
||||
try {
|
||||
paymentMethodLoading.value = true
|
||||
if (!confirmation) {
|
||||
selectedPaymentMethod.value = paymentMethods.find((x) => x.id === id)
|
||||
}
|
||||
async function refreshPaymentIntent(id: string, confirmation: boolean) {
|
||||
try {
|
||||
paymentMethodLoading.value = true
|
||||
if (!confirmation) {
|
||||
selectedPaymentMethod.value = paymentMethods.find((x) => x.id === id)
|
||||
}
|
||||
|
||||
const requestType: PaymentRequestType = confirmation
|
||||
? {
|
||||
type: 'confirmation_token',
|
||||
token: id,
|
||||
}
|
||||
: {
|
||||
type: 'payment_method',
|
||||
id: id,
|
||||
}
|
||||
const requestType: PaymentRequestType = confirmation
|
||||
? {
|
||||
type: 'confirmation_token',
|
||||
token: id,
|
||||
}
|
||||
: {
|
||||
type: 'payment_method',
|
||||
id: id,
|
||||
}
|
||||
|
||||
if (!product.value) {
|
||||
return handlePaymentError('No product selected')
|
||||
}
|
||||
if (!product.value) {
|
||||
return handlePaymentError('No product selected')
|
||||
}
|
||||
|
||||
const charge: ChargeRequestType = {
|
||||
type: 'new',
|
||||
product_id: product.value?.id,
|
||||
interval: interval.value,
|
||||
}
|
||||
const charge: ChargeRequestType = {
|
||||
type: 'new',
|
||||
product_id: product.value?.id,
|
||||
interval: interval.value,
|
||||
}
|
||||
|
||||
let result: BasePaymentIntentResponse
|
||||
let result: BasePaymentIntentResponse
|
||||
|
||||
const metadata: CreatePaymentIntentRequest['metadata'] = {
|
||||
type: 'pyro',
|
||||
server_region: region.value,
|
||||
source: project.value
|
||||
? {
|
||||
project_id: project.value,
|
||||
}
|
||||
: {},
|
||||
}
|
||||
const metadata: CreatePaymentIntentRequest['metadata'] = {
|
||||
type: 'pyro',
|
||||
server_region: region.value,
|
||||
source: project.value
|
||||
? {
|
||||
project_id: project.value,
|
||||
}
|
||||
: {},
|
||||
}
|
||||
|
||||
if (paymentIntentId.value) {
|
||||
result = await updateIntent({
|
||||
...requestType,
|
||||
charge,
|
||||
existing_payment_intent: paymentIntentId.value,
|
||||
metadata,
|
||||
})
|
||||
console.log(`Updated payment intent: ${interval.value} for ${result.total}`)
|
||||
} else {
|
||||
;({
|
||||
payment_intent_id: paymentIntentId.value,
|
||||
client_secret: clientSecret.value,
|
||||
...result
|
||||
} = await createIntent({
|
||||
...requestType,
|
||||
charge,
|
||||
metadata: metadata,
|
||||
}))
|
||||
console.log(`Created payment intent: ${interval.value} for ${result.total}`)
|
||||
}
|
||||
if (paymentIntentId.value) {
|
||||
result = await updateIntent({
|
||||
...requestType,
|
||||
charge,
|
||||
existing_payment_intent: paymentIntentId.value,
|
||||
metadata,
|
||||
})
|
||||
console.log(`Updated payment intent: ${interval.value} for ${result.total}`)
|
||||
} else {
|
||||
;({
|
||||
payment_intent_id: paymentIntentId.value,
|
||||
client_secret: clientSecret.value,
|
||||
...result
|
||||
} = await createIntent({
|
||||
...requestType,
|
||||
charge,
|
||||
metadata: metadata,
|
||||
}))
|
||||
console.log(`Created payment intent: ${interval.value} for ${result.total}`)
|
||||
}
|
||||
|
||||
tax.value = result.tax
|
||||
total.value = result.total
|
||||
tax.value = result.tax
|
||||
total.value = result.total
|
||||
|
||||
if (confirmation) {
|
||||
confirmationToken.value = id
|
||||
if (result.payment_method) {
|
||||
inputtedPaymentMethod.value = result.payment_method
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
handlePaymentError(err as string)
|
||||
}
|
||||
paymentMethodLoading.value = false
|
||||
}
|
||||
if (confirmation) {
|
||||
confirmationToken.value = id
|
||||
if (result.payment_method) {
|
||||
inputtedPaymentMethod.value = result.payment_method
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
handlePaymentError(err as string)
|
||||
}
|
||||
paymentMethodLoading.value = false
|
||||
}
|
||||
|
||||
async function createConfirmationToken() {
|
||||
if (!elements) {
|
||||
return handlePaymentError('No elements')
|
||||
}
|
||||
if (!stripe.value) {
|
||||
return handlePaymentError('No stripe')
|
||||
}
|
||||
async function createConfirmationToken() {
|
||||
if (!elements) {
|
||||
return handlePaymentError('No elements')
|
||||
}
|
||||
if (!stripe.value) {
|
||||
return handlePaymentError('No stripe')
|
||||
}
|
||||
|
||||
const { error, confirmationToken: confirmation } = await stripe.value.createConfirmationToken({
|
||||
elements,
|
||||
})
|
||||
const { error, confirmationToken: confirmation } = await stripe.value.createConfirmationToken({
|
||||
elements,
|
||||
})
|
||||
|
||||
if (error) {
|
||||
handlePaymentError(error.message ?? 'Unknown error creating confirmation token')
|
||||
return
|
||||
}
|
||||
if (error) {
|
||||
handlePaymentError(error.message ?? 'Unknown error creating confirmation token')
|
||||
return
|
||||
}
|
||||
|
||||
return confirmation.id
|
||||
}
|
||||
return confirmation.id
|
||||
}
|
||||
|
||||
function handlePaymentError(err: string | Error) {
|
||||
paymentMethodLoading.value = false
|
||||
completingPurchase.value = false
|
||||
onError(typeof err === 'string' ? new Error(err) : err)
|
||||
}
|
||||
function handlePaymentError(err: string | Error) {
|
||||
paymentMethodLoading.value = false
|
||||
completingPurchase.value = false
|
||||
onError(typeof err === 'string' ? new Error(err) : err)
|
||||
}
|
||||
|
||||
async function createNewPaymentMethod() {
|
||||
paymentMethodLoading.value = true
|
||||
async function createNewPaymentMethod() {
|
||||
paymentMethodLoading.value = true
|
||||
|
||||
if (!elements) {
|
||||
return handlePaymentError('No elements')
|
||||
}
|
||||
if (!elements) {
|
||||
return handlePaymentError('No elements')
|
||||
}
|
||||
|
||||
const { error: submitError } = await elements.submit()
|
||||
const { error: submitError } = await elements.submit()
|
||||
|
||||
if (submitError) {
|
||||
return handlePaymentError(submitError.message ?? 'Unknown error creating payment method')
|
||||
}
|
||||
if (submitError) {
|
||||
return handlePaymentError(submitError.message ?? 'Unknown error creating payment method')
|
||||
}
|
||||
|
||||
const token = await createConfirmationToken()
|
||||
if (!token) {
|
||||
return handlePaymentError('Failed to create confirmation token')
|
||||
}
|
||||
await refreshPaymentIntent(token, true)
|
||||
const token = await createConfirmationToken()
|
||||
if (!token) {
|
||||
return handlePaymentError('Failed to create confirmation token')
|
||||
}
|
||||
await refreshPaymentIntent(token, true)
|
||||
|
||||
if (!planPrices.value) {
|
||||
return handlePaymentError('No plan prices')
|
||||
}
|
||||
if (!total.value) {
|
||||
return handlePaymentError('No total amount')
|
||||
}
|
||||
if (!planPrices.value) {
|
||||
return handlePaymentError('No plan prices')
|
||||
}
|
||||
if (!total.value) {
|
||||
return handlePaymentError('No total amount')
|
||||
}
|
||||
|
||||
elements.update({ currency: planPrices.value.currency_code.toLowerCase(), amount: total.value })
|
||||
elements.update({
|
||||
currency: planPrices.value.currency_code.toLowerCase(),
|
||||
amount: total.value,
|
||||
})
|
||||
|
||||
elementsLoaded.value = 0
|
||||
confirmationToken.value = token
|
||||
paymentMethodLoading.value = false
|
||||
elementsLoaded.value = 0
|
||||
confirmationToken.value = token
|
||||
paymentMethodLoading.value = false
|
||||
|
||||
return token
|
||||
}
|
||||
return token
|
||||
}
|
||||
|
||||
async function selectPaymentMethod(paymentMethod: Stripe.PaymentMethod | undefined) {
|
||||
selectedPaymentMethod.value = paymentMethod
|
||||
if (paymentMethod === undefined) {
|
||||
await loadStripeElements()
|
||||
} else {
|
||||
refreshPaymentIntent(paymentMethod.id, false)
|
||||
}
|
||||
}
|
||||
async function selectPaymentMethod(paymentMethod: Stripe.PaymentMethod | undefined) {
|
||||
selectedPaymentMethod.value = paymentMethod
|
||||
if (paymentMethod === undefined) {
|
||||
await loadStripeElements()
|
||||
} else {
|
||||
refreshPaymentIntent(paymentMethod.id, false)
|
||||
}
|
||||
}
|
||||
|
||||
const loadingElements = computed(() => elementsLoaded.value < 2)
|
||||
const loadingElements = computed(() => elementsLoaded.value < 2)
|
||||
|
||||
async function submitPayment(returnUrl: string) {
|
||||
completingPurchase.value = true
|
||||
const secert = clientSecret.value
|
||||
async function submitPayment(returnUrl: string) {
|
||||
completingPurchase.value = true
|
||||
const secert = clientSecret.value
|
||||
|
||||
if (!secert) {
|
||||
return handlePaymentError('No client secret')
|
||||
}
|
||||
if (!secert) {
|
||||
return handlePaymentError('No client secret')
|
||||
}
|
||||
|
||||
if (!stripe.value) {
|
||||
return handlePaymentError('No stripe')
|
||||
}
|
||||
if (!stripe.value) {
|
||||
return handlePaymentError('No stripe')
|
||||
}
|
||||
|
||||
submittingPayment.value = true
|
||||
const { error } = await stripe.value.confirmPayment({
|
||||
clientSecret: secert,
|
||||
confirmParams: {
|
||||
confirmation_token: confirmationToken.value,
|
||||
return_url: `${returnUrl}?priceId=${product.value?.prices.find((x) => x.currency_code === currency)?.id}&plan=${interval.value}`,
|
||||
},
|
||||
})
|
||||
submittingPayment.value = true
|
||||
const { error } = await stripe.value.confirmPayment({
|
||||
clientSecret: secert,
|
||||
confirmParams: {
|
||||
confirmation_token: confirmationToken.value,
|
||||
return_url: `${returnUrl}?priceId=${product.value?.prices.find((x) => x.currency_code === currency)?.id}&plan=${interval.value}`,
|
||||
},
|
||||
})
|
||||
|
||||
if (error) {
|
||||
handlePaymentError(error.message ?? 'Unknown error submitting payment')
|
||||
return false
|
||||
}
|
||||
submittingPayment.value = false
|
||||
completingPurchase.value = false
|
||||
return true
|
||||
}
|
||||
if (error) {
|
||||
handlePaymentError(error.message ?? 'Unknown error submitting payment')
|
||||
return false
|
||||
}
|
||||
submittingPayment.value = false
|
||||
completingPurchase.value = false
|
||||
return true
|
||||
}
|
||||
|
||||
async function reloadPaymentIntent() {
|
||||
console.log('selected:', selectedPaymentMethod.value)
|
||||
console.log('token:', confirmationToken.value)
|
||||
if (selectedPaymentMethod.value) {
|
||||
await refreshPaymentIntent(selectedPaymentMethod.value.id, false)
|
||||
} else if (confirmationToken.value) {
|
||||
await refreshPaymentIntent(confirmationToken.value, true)
|
||||
} else {
|
||||
throw new Error('No payment method selected')
|
||||
}
|
||||
}
|
||||
async function reloadPaymentIntent() {
|
||||
console.log('selected:', selectedPaymentMethod.value)
|
||||
console.log('token:', confirmationToken.value)
|
||||
if (selectedPaymentMethod.value) {
|
||||
await refreshPaymentIntent(selectedPaymentMethod.value.id, false)
|
||||
} else if (confirmationToken.value) {
|
||||
await refreshPaymentIntent(confirmationToken.value, true)
|
||||
} else {
|
||||
throw new Error('No payment method selected')
|
||||
}
|
||||
}
|
||||
|
||||
const hasPaymentMethod = computed(() => selectedPaymentMethod.value || confirmationToken.value)
|
||||
const hasPaymentMethod = computed(() => selectedPaymentMethod.value || confirmationToken.value)
|
||||
|
||||
return {
|
||||
initializeStripe: initialize,
|
||||
selectPaymentMethod,
|
||||
reloadPaymentIntent,
|
||||
primaryPaymentMethodId,
|
||||
selectedPaymentMethod,
|
||||
inputtedPaymentMethod,
|
||||
hasPaymentMethod,
|
||||
createNewPaymentMethod,
|
||||
loadingElements,
|
||||
loadingElementsFailed,
|
||||
paymentMethodLoading,
|
||||
loadStripeElements,
|
||||
tax,
|
||||
total,
|
||||
submitPayment,
|
||||
completingPurchase,
|
||||
}
|
||||
return {
|
||||
initializeStripe: initialize,
|
||||
selectPaymentMethod,
|
||||
reloadPaymentIntent,
|
||||
primaryPaymentMethodId,
|
||||
selectedPaymentMethod,
|
||||
inputtedPaymentMethod,
|
||||
hasPaymentMethod,
|
||||
createNewPaymentMethod,
|
||||
loadingElements,
|
||||
loadingElementsFailed,
|
||||
paymentMethodLoading,
|
||||
loadStripeElements,
|
||||
tax,
|
||||
total,
|
||||
submitPayment,
|
||||
completingPurchase,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user