@ -1387,6 +1387,9 @@
|
||||
},
|
||||
"amount": {
|
||||
"type": "string"
|
||||
},
|
||||
"retryMessageContent": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
@ -981,6 +981,9 @@
|
||||
},
|
||||
"amount": {
|
||||
"type": "string"
|
||||
},
|
||||
"retryMessageContent": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@ -4392,6 +4395,9 @@
|
||||
},
|
||||
"amount": {
|
||||
"type": "string"
|
||||
},
|
||||
"retryMessageContent": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
@ -12,6 +12,7 @@ import {
|
||||
SessionState,
|
||||
SetVariableBlock,
|
||||
WebhookBlock,
|
||||
defaultPaymentInputOptions,
|
||||
} from '@typebot.io/schemas'
|
||||
import { isInputBlock, byId } from '@typebot.io/lib'
|
||||
import { executeGroup } from './executeGroup'
|
||||
@ -167,7 +168,7 @@ const parseRetryMessage = (
|
||||
const retryMessage =
|
||||
'retryMessageContent' in block.options && block.options.retryMessageContent
|
||||
? block.options.retryMessageContent
|
||||
: 'Invalid message. Please, try again.'
|
||||
: parseDefaultRetryMessage(block)
|
||||
return {
|
||||
messages: [
|
||||
{
|
||||
@ -182,6 +183,15 @@ const parseRetryMessage = (
|
||||
}
|
||||
}
|
||||
|
||||
const parseDefaultRetryMessage = (block: InputBlock): string => {
|
||||
switch (block.type) {
|
||||
case InputBlockType.PAYMENT:
|
||||
return defaultPaymentInputOptions.retryMessageContent as string
|
||||
default:
|
||||
return 'Invalid message. Please, try again.'
|
||||
}
|
||||
}
|
||||
|
||||
const saveAnswer =
|
||||
(state: SessionState, block: InputBlock, itemId?: string) =>
|
||||
async (reply: string): Promise<SessionState> => {
|
||||
@ -271,6 +281,8 @@ export const isReplyValid = (inputValue: string, block: Block): boolean => {
|
||||
return validatePhoneNumber(inputValue)
|
||||
case InputBlockType.URL:
|
||||
return validateUrl(inputValue)
|
||||
case InputBlockType.PAYMENT:
|
||||
return inputValue !== 'fail'
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@typebot.io/js",
|
||||
"version": "0.1.10",
|
||||
"version": "0.1.11",
|
||||
"description": "Javascript library to display typebots on your website",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
|
@ -47,6 +47,7 @@ export const Bot = (props: BotProps & { class?: string }) => {
|
||||
const typebotIdFromProps =
|
||||
typeof props.typebot === 'string' ? props.typebot : undefined
|
||||
const { data, error } = await getInitialChatReplyQuery({
|
||||
stripeRedirectStatus: urlParams.get('redirect_status') ?? undefined,
|
||||
typebot: props.typebot,
|
||||
apiHost: props.apiHost,
|
||||
isPreview: props.isPreview ?? false,
|
||||
@ -134,8 +135,9 @@ export const Bot = (props: BotProps & { class?: string }) => {
|
||||
apiHost: props.apiHost,
|
||||
isPreview:
|
||||
typeof props.typebot !== 'string' || (props.isPreview ?? false),
|
||||
typebotId: initialChatReply.typebot.id,
|
||||
resultId: initialChatReply.resultId,
|
||||
sessionId: initialChatReply.sessionId,
|
||||
typebot: initialChatReply.typebot,
|
||||
}}
|
||||
onNewInputBlock={props.onNewInputBlock}
|
||||
onNewLogs={props.onNewLogs}
|
||||
|
@ -54,7 +54,7 @@ export const FileUploadForm = (props: Props) => {
|
||||
setIsUploading(true)
|
||||
const urls = await uploadFiles({
|
||||
basePath: `${props.context.apiHost ?? guessApiHost()}/api/typebots/${
|
||||
props.context.typebotId
|
||||
props.context.typebot.id
|
||||
}/blocks/${props.block.id}`,
|
||||
files: [
|
||||
{
|
||||
@ -79,7 +79,7 @@ export const FileUploadForm = (props: Props) => {
|
||||
setIsUploading(true)
|
||||
const urls = await uploadFiles({
|
||||
basePath: `${props.context.apiHost ?? guessApiHost()}/api/typebots/${
|
||||
props.context.typebotId
|
||||
props.context.typebot.id
|
||||
}/blocks/${props.block.id}`,
|
||||
files: files.map((file) => ({
|
||||
file: file,
|
||||
|
@ -4,6 +4,10 @@ import type { Stripe, StripeElements } from '@stripe/stripe-js'
|
||||
import { BotContext } from '@/types'
|
||||
import type { PaymentInputOptions, RuntimeOptions } from '@typebot.io/schemas'
|
||||
import { loadStripe } from '@/lib/stripe'
|
||||
import {
|
||||
removePaymentInProgressFromStorage,
|
||||
setPaymentInProgressInStorage,
|
||||
} from '../helpers/paymentInProgressStorage'
|
||||
|
||||
type Props = {
|
||||
context: BotContext
|
||||
@ -51,11 +55,14 @@ export const StripePaymentForm = (props: Props) => {
|
||||
|
||||
setIsLoading(true)
|
||||
|
||||
setPaymentInProgressInStorage({
|
||||
sessionId: props.context.sessionId,
|
||||
typebot: props.context.typebot,
|
||||
})
|
||||
const { error, paymentIntent } = await stripe.confirmPayment({
|
||||
elements,
|
||||
confirmParams: {
|
||||
// TO-DO: Handle redirection correctly.
|
||||
return_url: props.context.apiHost,
|
||||
return_url: window.location.href,
|
||||
payment_method_data: {
|
||||
billing_details: {
|
||||
name: props.options.additionalInformation?.name,
|
||||
@ -71,6 +78,7 @@ export const StripePaymentForm = (props: Props) => {
|
||||
},
|
||||
redirect: 'if_required',
|
||||
})
|
||||
removePaymentInProgressFromStorage()
|
||||
|
||||
setIsLoading(false)
|
||||
if (error?.type === 'validation_error') return
|
||||
|
@ -0,0 +1,15 @@
|
||||
import { BotContext } from '@/types'
|
||||
|
||||
export const setPaymentInProgressInStorage = (state: {
|
||||
sessionId: string
|
||||
typebot: BotContext['typebot']
|
||||
}) => {
|
||||
sessionStorage.setItem('typebotPaymentInProgress', JSON.stringify(state))
|
||||
}
|
||||
|
||||
export const getPaymentInProgressInStorage = () =>
|
||||
sessionStorage.getItem('typebotPaymentInProgress')
|
||||
|
||||
export const removePaymentInProgressFromStorage = () => {
|
||||
sessionStorage.removeItem('typebotPaymentInProgress')
|
||||
}
|
@ -13,6 +13,7 @@ import { PreviewMessage, PreviewMessageProps } from './PreviewMessage'
|
||||
import { isDefined } from '@typebot.io/lib'
|
||||
import { BubbleParams } from '../types'
|
||||
import { Bot, BotProps } from '../../../components/Bot'
|
||||
import { getPaymentInProgressInStorage } from '@/features/blocks/inputs/payment/helpers/paymentInProgressStorage'
|
||||
|
||||
export type BubbleProps = BotProps &
|
||||
BubbleParams & {
|
||||
@ -51,6 +52,8 @@ export const Bubble = (props: BubbleProps) => {
|
||||
const autoShowDelay = bubbleProps.autoShowDelay
|
||||
const previewMessageAutoShowDelay =
|
||||
bubbleProps.previewMessage?.autoShowDelay
|
||||
const paymentInProgress = getPaymentInProgressInStorage()
|
||||
if (paymentInProgress) openBot()
|
||||
if (isDefined(autoShowDelay)) {
|
||||
setTimeout(() => {
|
||||
openBot()
|
||||
|
@ -11,6 +11,7 @@ import { CommandData } from '../../commands'
|
||||
import { isDefined, isNotDefined } from '@typebot.io/lib'
|
||||
import { PopupParams } from '../types'
|
||||
import { Bot, BotProps } from '../../../components/Bot'
|
||||
import { getPaymentInProgressInStorage } from '@/features/blocks/inputs/payment/helpers/paymentInProgressStorage'
|
||||
|
||||
export type PopupProps = BotProps &
|
||||
PopupParams & {
|
||||
@ -41,7 +42,8 @@ export const Popup = (props: PopupProps) => {
|
||||
)
|
||||
|
||||
onMount(() => {
|
||||
if (popupProps.defaultOpen) openBot()
|
||||
const paymentInProgress = getPaymentInProgressInStorage()
|
||||
if (popupProps.defaultOpen || paymentInProgress) openBot()
|
||||
window.addEventListener('message', processIncomingEvent)
|
||||
const autoShowDelay = popupProps.autoShowDelay
|
||||
if (isDefined(autoShowDelay)) {
|
||||
|
@ -1,7 +1,11 @@
|
||||
import { InitialChatReply } from '@/types'
|
||||
import { BotContext, InitialChatReply } from '@/types'
|
||||
import { guessApiHost } from '@/utils/guessApiHost'
|
||||
import type { SendMessageInput, StartParams } from '@typebot.io/schemas'
|
||||
import { isNotDefined, isNotEmpty, sendRequest } from '@typebot.io/lib'
|
||||
import {
|
||||
getPaymentInProgressInStorage,
|
||||
removePaymentInProgressFromStorage,
|
||||
} from '@/features/blocks/inputs/payment/helpers/paymentInProgressStorage'
|
||||
|
||||
export async function getInitialChatReplyQuery({
|
||||
typebot,
|
||||
@ -10,17 +14,29 @@ export async function getInitialChatReplyQuery({
|
||||
prefilledVariables,
|
||||
startGroupId,
|
||||
resultId,
|
||||
stripeRedirectStatus,
|
||||
}: StartParams & {
|
||||
stripeRedirectStatus?: string
|
||||
apiHost?: string
|
||||
}) {
|
||||
if (isNotDefined(typebot))
|
||||
throw new Error('Typebot ID is required to get initial messages')
|
||||
|
||||
return sendRequest<InitialChatReply>({
|
||||
const paymentInProgressStateStr = getPaymentInProgressInStorage() ?? undefined
|
||||
const paymentInProgressState = paymentInProgressStateStr
|
||||
? (JSON.parse(paymentInProgressStateStr) as {
|
||||
sessionId: string
|
||||
typebot: BotContext['typebot']
|
||||
})
|
||||
: undefined
|
||||
if (paymentInProgressState) removePaymentInProgressFromStorage()
|
||||
const { data, error } = await sendRequest<InitialChatReply>({
|
||||
method: 'POST',
|
||||
url: `${isNotEmpty(apiHost) ? apiHost : guessApiHost()}/api/v1/sendMessage`,
|
||||
body: {
|
||||
startParams: {
|
||||
startParams: paymentInProgressState
|
||||
? undefined
|
||||
: {
|
||||
isPreview,
|
||||
typebot,
|
||||
prefilledVariables,
|
||||
@ -28,6 +44,24 @@ export async function getInitialChatReplyQuery({
|
||||
resultId,
|
||||
isStreamEnabled: true,
|
||||
},
|
||||
sessionId: paymentInProgressState?.sessionId,
|
||||
message: paymentInProgressState
|
||||
? stripeRedirectStatus === 'failed'
|
||||
? 'fail'
|
||||
: 'Success'
|
||||
: undefined,
|
||||
} satisfies SendMessageInput,
|
||||
})
|
||||
|
||||
return {
|
||||
data: data
|
||||
? {
|
||||
...data,
|
||||
...(paymentInProgressState
|
||||
? { typebot: paymentInProgressState.typebot }
|
||||
: {}),
|
||||
}
|
||||
: undefined,
|
||||
error,
|
||||
}
|
||||
}
|
||||
|
@ -6,10 +6,11 @@ export type InputSubmitContent = {
|
||||
}
|
||||
|
||||
export type BotContext = {
|
||||
typebotId: string
|
||||
typebot: InitialChatReply['typebot']
|
||||
resultId?: string
|
||||
isPreview: boolean
|
||||
apiHost?: string
|
||||
sessionId: string
|
||||
}
|
||||
|
||||
export type InitialChatReply = ChatReply & {
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@typebot.io/nextjs",
|
||||
"version": "0.1.10",
|
||||
"version": "0.1.11",
|
||||
"description": "Convenient library to display typebots on your Next.js website",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@typebot.io/react",
|
||||
"version": "0.1.10",
|
||||
"version": "0.1.11",
|
||||
"description": "Convenient library to display typebots on your Next.js website",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
|
@ -42,6 +42,7 @@ export const paymentInputOptionsSchema = optionBaseSchema.merge(
|
||||
credentialsId: z.string().optional(),
|
||||
currency: z.string(),
|
||||
amount: z.string().optional(),
|
||||
retryMessageContent: z.string().optional(),
|
||||
})
|
||||
)
|
||||
|
||||
@ -77,6 +78,7 @@ export const stripeCredentialsSchema = z
|
||||
export const defaultPaymentInputOptions: PaymentInputOptions = {
|
||||
provider: PaymentProvider.STRIPE,
|
||||
labels: { button: 'Pay', success: 'Success' },
|
||||
retryMessageContent: 'Payment failed. Please, try again.',
|
||||
currency: 'USD',
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user