-
Notifications
You must be signed in to change notification settings - Fork 28
/
webhook-received.ts
129 lines (114 loc) 路 4.24 KB
/
webhook-received.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import {inngest} from 'inngest/inngest.server'
import {STRIPE_WEBHOOK_RECEIVED_EVENT} from '@skillrecordings/inngest'
import {prisma} from '@skillrecordings/database'
import {NonRetriableError} from 'inngest'
import {postToSlack} from '@skillrecordings/skill-api'
import {WebClient} from '@slack/web-api'
import {paymentOptions} from 'pages/api/skill/[...skillRecordings]'
import {z} from 'zod'
const stripe = paymentOptions.providers.stripe?.paymentClient
const CustomerSchema = z.object({id: z.string(), email: z.string()})
export const stripeWebhookReceived = inngest.createFunction(
{id: `stripe-webhook-received`, name: 'Stripe Webhook Received'},
{event: STRIPE_WEBHOOK_RECEIVED_EVENT},
async ({event, step}) => {
if (!stripe) {
throw new NonRetriableError('Payment provider (Stripe) is missing')
}
const stripeAccountId = await step.run(
'get stripe account id',
async () => {
const merchantAccount = await prisma.merchantAccount.findFirst({
where: {
label: 'stripe',
},
select: {
identifier: true,
},
})
if (!merchantAccount) {
throw new NonRetriableError('no stripe account found')
}
return merchantAccount.identifier
},
)
switch (event.data.type) {
case 'invoice.sent':
case 'invoice.overdue':
case 'invoice.payment_succeeded':
const invoice = await step.run('get invoice', async () => {
return await stripe.invoices.retrieve(event.data.data.object.id)
})
if (!invoice) {
throw new NonRetriableError('no invoice found')
}
const customer = await step.run('get customer', async () => {
return CustomerSchema.parse(
await stripe.customers.retrieve(invoice.customer as string),
)
})
if (!customer) {
throw new NonRetriableError('no customer found')
}
await step.run('post invoice to slack', async () => {
function getInvoiceMessageForType(type: string) {
switch (type) {
case 'invoice.sent':
return {
text: 'was sent',
icon_emoji: ':envelope:',
}
case 'invoice.overdue':
return {
text: 'is overdue',
icon_emoji: ':warning:',
}
case 'invoice.payment_succeeded':
return {
text: 'was paid',
icon_emoji: ':moneybag:',
}
default:
return {}
}
}
const message = getInvoiceMessageForType(event.data.type)
return await postToSlack({
webClient: new WebClient(process.env.SLACK_TOKEN),
channel: process.env.SLACK_INVOICE_POST_CHANNEL,
username: 'Epic Web Invoices',
icon_emoji: message.icon_emoji,
text: `${customer.email} ${message.text}:`,
attachments: [
{
mrkdwn_in: ['text'],
text: `The invoice to <https://dashboard.stripe.com/customers/${
customer.id
}?account=${stripeAccountId}|${customer.email}> ${
message.text
} for ${
invoice.amount_due / 100
} ${invoice.currency.toUpperCase()} (<${
invoice.hosted_invoice_url
}|payment link>|<${
invoice.invoice_pdf
}|pdf>) \n\n- ${invoice.lines.data
.map((line) => `*${line.description}*`)
.join('\n- ')}`,
color:
process.env.NODE_ENV === 'production' ? '#4893c9' : '#c97948',
title: `Invoice ${invoice.number} for ${
invoice.amount_due / 100
} ${invoice.currency.toUpperCase()} ${
process.env.NODE_ENV === 'production' ? '' : '[DEV]'
}`,
title_link: `https://dashboard.stripe.com/invoices/${invoice.id}?account=${stripeAccountId}`,
},
],
})
})
return {invoice, customer}
}
return 'yup, here we are'
},
)