diff --git a/src/client/components/feed/FeedIntegration.tsx b/src/client/components/feed/FeedIntegration.tsx
index 879e1e7..5b9199e 100644
--- a/src/client/components/feed/FeedIntegration.tsx
+++ b/src/client/components/feed/FeedIntegration.tsx
@@ -4,6 +4,7 @@ import { Popover, PopoverContent, PopoverTrigger } from '../ui/popover';
import { CodeBlock } from '../CodeBlock';
import { useTranslation } from '@i18next-toolkit/react';
import { SiSentry } from 'react-icons/si';
+import { FaStripe } from 'react-icons/fa6';
export const FeedIntegration: React.FC<{
feedId: string;
@@ -57,6 +58,22 @@ export const FeedIntegration: React.FC<{
}
/>
+ }
+ label="Stripe"
+ content={
+
+
{t('Receive Webhooks')}
+
+
{t('Add sentry webhook with url')}:
+
+
+
+ }
+ />
+
window.open('/feed/playground', '_blank')}>
}
diff --git a/src/server/trpc/routers/feed/integration.ts b/src/server/trpc/routers/feed/integration.ts
index fbf441a..90f8514 100644
--- a/src/server/trpc/routers/feed/integration.ts
+++ b/src/server/trpc/routers/feed/integration.ts
@@ -184,6 +184,121 @@ export const feedIntegrationRouter = router({
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';
}),
tencentCloudAlarm: publicProcedure