You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
docuseal/app/javascript/submission_form/payment_step.vue

182 lines
4.1 KiB

<template>
<label
v-if="!modelValue && !sessionId"
:for="field.uuid"
class="label text-2xl mb-2"
>{{ field.name || defaultName }}
</label>
<div>
<input
type="text"
:value="modelValue"
hidden
:name="`values[${field.uuid}]`"
class="hidden"
>
<div
v-if="modelValue && !sessionId"
class=" text-2xl mb-2"
>
Already paid
</div>
<div v-else>
<button
v-if="sessionId"
disabled
class="base-button w-full"
>
<IconLoader
width="22"
class="animate-spin"
/>
<span>
{{ t('processing') }}...
</span>
</button>
<button
v-else
:id="field.uuid"
class="btn bg-[#7B73FF] text-white hover:bg-[#0A2540] text-lg w-full"
:class="{ disabled: isCreatingCheckout }"
:disabled="isCreatingCheckout"
@click.prevent="startCheckout"
>
<IconInnerShadowTop
v-if="isCreatingCheckout"
width="22"
class="animate-spin"
/>
<IconBrandStripe
v-else
width="22"
/>
<span>
{{ t('pay_with_strip') }}
</span>
</button>
</div>
</div>
</template>
<script>
import { IconBrandStripe, IconInnerShadowTop, IconLoader } from '@tabler/icons-vue'
export default {
name: 'PaymentStep',
components: {
IconBrandStripe,
IconInnerShadowTop,
IconLoader
},
inject: ['baseUrl', 't'],
props: {
modelValue: {
type: String,
required: false,
default: ''
},
field: {
type: Object,
required: true
},
submitterSlug: {
type: String,
required: true
}
},
emits: ['focus', 'submit', 'update:model-value', 'attached'],
data () {
return {
isCreatingCheckout: false
}
},
computed: {
queryParams () {
return new URLSearchParams(window.location.search)
},
sessionId () {
return this.queryParams.get('stripe_session_id')
},
defaultName () {
const { price, currency } = this.field.preferences || {}
const formattedPrice = new Intl.NumberFormat([], {
style: 'currency',
currency
}).format(price)
return `Pay ${formattedPrice}`
}
},
mounted () {
if (this.sessionId) {
this.$emit('submit')
}
},
methods: {
async submit () {
if (this.sessionId) {
return fetch((this.baseUrl || '/embed').replace('/embed', '/api/stripe_payments/' + this.sessionId), {
method: 'PUT',
body: JSON.stringify({
submitter_slug: this.submitterSlug
}),
headers: { 'Content-Type': 'application/json' }
}).then(async (resp) => {
if (resp.status === 422 || resp.status === 500) {
const data = await resp.json()
alert(data.error || 'Unexpected error')
return Promise.reject(new Error(data.error))
}
const attachment = await resp.json()
window.history.replaceState({}, document.title, window.location.pathname)
this.$emit('update:model-value', attachment.uuid)
this.$emit('attached', attachment)
return resp
})
} else {
return Promise.resolve({})
}
},
startCheckout () {
this.isCreatingCheckout = true
fetch((this.baseUrl || '/embed').replace('/embed', '/api/stripe_payments'), {
method: 'POST',
body: JSON.stringify({
submitter_slug: this.submitterSlug,
field_uuid: this.field.uuid
}),
headers: { 'Content-Type': 'application/json' }
}).then(async (resp) => {
if (resp.status === 422 || resp.status === 500) {
const data = await resp.json()
alert(data.message || 'Unexpected error')
return Promise.reject(new Error(data.message))
}
const { url } = await resp.json()
const link = document.createElement('a')
link.href = url
if (url) {
link.click()
}
}).finally(() => {
this.isCreatingCheckout = false
})
}
}
}
</script>