feat: add stripe feed integration
This commit is contained in:
parent
59d32e0119
commit
09d0f02d84
@ -4,6 +4,7 @@ import { Popover, PopoverContent, PopoverTrigger } from '../ui/popover';
|
|||||||
import { CodeBlock } from '../CodeBlock';
|
import { CodeBlock } from '../CodeBlock';
|
||||||
import { useTranslation } from '@i18next-toolkit/react';
|
import { useTranslation } from '@i18next-toolkit/react';
|
||||||
import { SiSentry } from 'react-icons/si';
|
import { SiSentry } from 'react-icons/si';
|
||||||
|
import { FaStripe } from 'react-icons/fa6';
|
||||||
|
|
||||||
export const FeedIntegration: React.FC<{
|
export const FeedIntegration: React.FC<{
|
||||||
feedId: string;
|
feedId: string;
|
||||||
@ -57,6 +58,22 @@ export const FeedIntegration: React.FC<{
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<FeedIntegrationItem
|
||||||
|
icon={<FaStripe size={32} />}
|
||||||
|
label="Stripe"
|
||||||
|
content={
|
||||||
|
<div>
|
||||||
|
<div className="text-lg font-bold">{t('Receive Webhooks')}</div>
|
||||||
|
|
||||||
|
<div>{t('Add sentry webhook with url')}:</div>
|
||||||
|
|
||||||
|
<CodeBlock
|
||||||
|
code={`${window.location.origin}/open/feed/${props.feedId}/stripe`}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
<div onClick={() => window.open('/feed/playground', '_blank')}>
|
<div onClick={() => window.open('/feed/playground', '_blank')}>
|
||||||
<FeedIntegrationItemTrigger
|
<FeedIntegrationItemTrigger
|
||||||
icon={<LuTestTube2 size={32} />}
|
icon={<LuTestTube2 size={32} />}
|
||||||
|
@ -184,6 +184,121 @@ export const feedIntegrationRouter = router({
|
|||||||
|
|
||||||
logUnknownIntegration('github', input);
|
logUnknownIntegration('github', input);
|
||||||
|
|
||||||
|
return 'Not supported yet';
|
||||||
|
}),
|
||||||
|
stripe: publicProcedure
|
||||||
|
.meta(
|
||||||
|
buildFeedPublicOpenapi({
|
||||||
|
method: 'POST',
|
||||||
|
path: '/{channelId}/stripe',
|
||||||
|
summary: 'integrate with stripe webhook',
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.input(
|
||||||
|
z
|
||||||
|
.object({
|
||||||
|
channelId: z.string(),
|
||||||
|
})
|
||||||
|
.passthrough()
|
||||||
|
)
|
||||||
|
.output(z.string())
|
||||||
|
.mutation(async ({ input, ctx }) => {
|
||||||
|
console.log(input);
|
||||||
|
const { channelId, ...data } = input;
|
||||||
|
const type = data.type;
|
||||||
|
|
||||||
|
const workspaceId = await prisma.feedChannel
|
||||||
|
.findFirst({
|
||||||
|
where: {
|
||||||
|
id: channelId,
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
workspaceId: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((res) => res?.workspaceId);
|
||||||
|
|
||||||
|
if (!workspaceId) {
|
||||||
|
throw new Error('Not found Workspace');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'payment_intent.succeeded') {
|
||||||
|
const amount = get(data, 'data.object.amount_received');
|
||||||
|
const currency = String(
|
||||||
|
get(data, 'data.object.currency')
|
||||||
|
).toUpperCase();
|
||||||
|
const eventId = String(get(data, 'data.id')).toUpperCase();
|
||||||
|
|
||||||
|
await createFeedEvent(workspaceId, {
|
||||||
|
channelId: channelId,
|
||||||
|
eventName: type,
|
||||||
|
eventContent: `You receive a payment of ${currency} **${amount}**`,
|
||||||
|
tags: [],
|
||||||
|
source: 'stripe',
|
||||||
|
senderId: eventId,
|
||||||
|
important: true,
|
||||||
|
payload: data,
|
||||||
|
});
|
||||||
|
return 'ok';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'payment_intent.canceled') {
|
||||||
|
const amount = get(data, 'data.object.amount');
|
||||||
|
const currency = String(
|
||||||
|
get(data, 'data.object.currency')
|
||||||
|
).toUpperCase();
|
||||||
|
const eventId = String(get(data, 'data.id')).toUpperCase();
|
||||||
|
|
||||||
|
await createFeedEvent(workspaceId, {
|
||||||
|
channelId: channelId,
|
||||||
|
eventName: type,
|
||||||
|
eventContent: `A payment has been canceled of ${currency} **${amount}**`,
|
||||||
|
tags: [],
|
||||||
|
source: 'stripe',
|
||||||
|
senderId: eventId,
|
||||||
|
important: false,
|
||||||
|
payload: data,
|
||||||
|
});
|
||||||
|
return 'ok';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'customer.subscription.created') {
|
||||||
|
const eventId = String(get(data, 'data.id')).toUpperCase();
|
||||||
|
const planName = get(data, 'data.object.plan.nickname');
|
||||||
|
|
||||||
|
await createFeedEvent(workspaceId, {
|
||||||
|
channelId: channelId,
|
||||||
|
eventName: type,
|
||||||
|
eventContent: `A customer has been subscribed service: **${planName}**`,
|
||||||
|
tags: [],
|
||||||
|
source: 'stripe',
|
||||||
|
senderId: eventId,
|
||||||
|
important: true,
|
||||||
|
payload: data,
|
||||||
|
});
|
||||||
|
return 'ok';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'customer.subscription.deleted') {
|
||||||
|
const eventId = String(get(data, 'data.id')).toUpperCase();
|
||||||
|
const planName = get(data, 'data.object.plan.nickname');
|
||||||
|
|
||||||
|
await createFeedEvent(workspaceId, {
|
||||||
|
channelId: channelId,
|
||||||
|
eventName: type,
|
||||||
|
eventContent: `A customer cancel subscription service: ${planName}`,
|
||||||
|
tags: [],
|
||||||
|
source: 'stripe',
|
||||||
|
senderId: eventId,
|
||||||
|
important: true,
|
||||||
|
payload: data,
|
||||||
|
});
|
||||||
|
|
||||||
|
return 'ok';
|
||||||
|
}
|
||||||
|
|
||||||
|
logUnknownIntegration('stripe', input);
|
||||||
|
|
||||||
return 'Not supported yet';
|
return 'Not supported yet';
|
||||||
}),
|
}),
|
||||||
tencentCloudAlarm: publicProcedure
|
tencentCloudAlarm: publicProcedure
|
||||||
|
Loading…
Reference in New Issue
Block a user