♻️ Export bot-engine code into its own package
This commit is contained in:
@ -0,0 +1,124 @@
|
||||
import { TRPCError } from '@trpc/server'
|
||||
import {
|
||||
PaymentInputOptions,
|
||||
PaymentInputRuntimeOptions,
|
||||
SessionState,
|
||||
StripeCredentials,
|
||||
} from '@typebot.io/schemas'
|
||||
import Stripe from 'stripe'
|
||||
import { decrypt } from '@typebot.io/lib/api/encryption'
|
||||
import { parseVariables } from '../../../variables/parseVariables'
|
||||
import prisma from '@typebot.io/lib/prisma'
|
||||
|
||||
export const computePaymentInputRuntimeOptions =
|
||||
(state: SessionState) => (options: PaymentInputOptions) =>
|
||||
createStripePaymentIntent(state)(options)
|
||||
|
||||
const createStripePaymentIntent =
|
||||
(state: SessionState) =>
|
||||
async (options: PaymentInputOptions): Promise<PaymentInputRuntimeOptions> => {
|
||||
const {
|
||||
resultId,
|
||||
typebot: { variables },
|
||||
} = state.typebotsQueue[0]
|
||||
const isPreview = !resultId
|
||||
if (!options.credentialsId)
|
||||
throw new TRPCError({
|
||||
code: 'BAD_REQUEST',
|
||||
message: 'Missing credentialsId',
|
||||
})
|
||||
const stripeKeys = await getStripeInfo(options.credentialsId)
|
||||
if (!stripeKeys)
|
||||
throw new TRPCError({
|
||||
code: 'NOT_FOUND',
|
||||
message: 'Credentials not found',
|
||||
})
|
||||
const stripe = new Stripe(
|
||||
isPreview && stripeKeys?.test?.secretKey
|
||||
? stripeKeys.test.secretKey
|
||||
: stripeKeys.live.secretKey,
|
||||
{ apiVersion: '2022-11-15' }
|
||||
)
|
||||
const amount = Math.round(
|
||||
Number(parseVariables(variables)(options.amount)) *
|
||||
(isZeroDecimalCurrency(options.currency) ? 1 : 100)
|
||||
)
|
||||
if (isNaN(amount))
|
||||
throw new TRPCError({
|
||||
code: 'BAD_REQUEST',
|
||||
message:
|
||||
'Could not parse amount, make sure your block is configured correctly',
|
||||
})
|
||||
// Create a PaymentIntent with the order amount and currency
|
||||
const receiptEmail = parseVariables(variables)(
|
||||
options.additionalInformation?.email
|
||||
)
|
||||
const paymentIntent = await stripe.paymentIntents.create({
|
||||
amount,
|
||||
currency: options.currency,
|
||||
receipt_email: receiptEmail === '' ? undefined : receiptEmail,
|
||||
description: options.additionalInformation?.description,
|
||||
automatic_payment_methods: {
|
||||
enabled: true,
|
||||
},
|
||||
})
|
||||
|
||||
if (!paymentIntent.client_secret)
|
||||
throw new TRPCError({
|
||||
code: 'BAD_REQUEST',
|
||||
message: 'Could not create payment intent',
|
||||
})
|
||||
|
||||
const priceFormatter = new Intl.NumberFormat(
|
||||
options.currency === 'EUR' ? 'fr-FR' : undefined,
|
||||
{
|
||||
style: 'currency',
|
||||
currency: options.currency,
|
||||
}
|
||||
)
|
||||
|
||||
return {
|
||||
paymentIntentSecret: paymentIntent.client_secret,
|
||||
publicKey:
|
||||
isPreview && stripeKeys.test?.publicKey
|
||||
? stripeKeys.test.publicKey
|
||||
: stripeKeys.live.publicKey,
|
||||
amountLabel: priceFormatter.format(
|
||||
amount / (isZeroDecimalCurrency(options.currency) ? 1 : 100)
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
const getStripeInfo = async (
|
||||
credentialsId: string
|
||||
): Promise<StripeCredentials['data'] | undefined> => {
|
||||
const credentials = await prisma.credentials.findUnique({
|
||||
where: { id: credentialsId },
|
||||
})
|
||||
if (!credentials) return
|
||||
return (await decrypt(
|
||||
credentials.data,
|
||||
credentials.iv
|
||||
)) as StripeCredentials['data']
|
||||
}
|
||||
|
||||
// https://stripe.com/docs/currencies#zero-decimal
|
||||
const isZeroDecimalCurrency = (currency: string) =>
|
||||
[
|
||||
'BIF',
|
||||
'CLP',
|
||||
'DJF',
|
||||
'GNF',
|
||||
'JPY',
|
||||
'KMF',
|
||||
'KRW',
|
||||
'MGA',
|
||||
'PYG',
|
||||
'RWF',
|
||||
'UGX',
|
||||
'VND',
|
||||
'VUV',
|
||||
'XAF',
|
||||
'XOF',
|
||||
'XPF',
|
||||
].includes(currency)
|
Reference in New Issue
Block a user