Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(stripe): handle Stripe subscription events #9768

Merged
merged 3 commits into from
May 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions packages/server/billing/stripeWebhookHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,41 @@ const eventLookup = {
}
`
}
},
subscription: {
updated: {
getVars: ({customer, id}: {customer: string; id: string}) => ({
customerId: customer,
subscriptionId: id
}),
query: `
mutation StripeUpdateSubscription($customerId: ID!, $subscriptionId: ID!) {
stripeUpdateSubscription(customerId: $customerId, subscriptionId: $subscriptionId)
}
`
},
created: {
getVars: ({customer, id}: {customer: string; id: string}) => ({
customerId: customer,
subscriptionId: id
}),
query: `
mutation StripeCreateSubscription($customerId: ID!, $subscriptionId: ID!) {
stripeUpdateSubscription(customerId: $customerId, subscriptionId: $subscriptionId)
}
`
},
deleted: {
getVars: ({customer, id}: {customer: string; id: string}) => ({
customerId: customer,
subscriptionId: id
}),
query: `
mutation StripeDeleteSubscription($customerId: ID!, $subscriptionId: ID!) {
stripeDeleteSubscription(customerId: $customerId, subscriptionId: $subscriptionId)
}
`
}
}
}
} as const
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import getRethink from '../../../database/rethinkDriver'
import Organization from '../../../database/types/Organization'
import {isSuperUser} from '../../../utils/authorization'
import {getStripeManager} from '../../../utils/stripe'
import {MutationResolvers} from '../resolverTypes'

const stripeDeleteSubscription: MutationResolvers['stripeDeleteSubscription'] = async (
_source,
{customerId, subscriptionId},
{authToken, dataLoader}
) => {
const r = await getRethink()
// AUTH
if (!isSuperUser(authToken)) {
throw new Error('Don’t be rude.')
}

// RESOLUTION
const manager = getStripeManager()
const stripeCustomer = await manager.retrieveCustomer(customerId)
if (stripeCustomer.deleted) {
throw new Error('Customer was deleted')
}

const {
metadata: {orgId}
} = stripeCustomer
if (!orgId) {
throw new Error(`orgId not found on metadata for customer ${customerId}`)
}
const org: Organization = await dataLoader.get('organizations').load(orgId)

const {stripeSubscriptionId} = org
if (stripeSubscriptionId !== subscriptionId) {
throw new Error('Subscription ID does not match')
}

await r
.table('Organization')
.get(orgId)
.update({
stripeSubscriptionId: r.literal()
})
.run()

return true
}

export default stripeDeleteSubscription
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import getRethink from '../../../database/rethinkDriver'
import {isSuperUser} from '../../../utils/authorization'
import {getStripeManager} from '../../../utils/stripe'
import {MutationResolvers} from '../resolverTypes'

const stripeUpdateSubscription: MutationResolvers['stripeUpdateSubscription'] = async (
_source,
{customerId, subscriptionId},
{authToken}
) => {
const r = await getRethink()
// AUTH
if (!isSuperUser(authToken)) {
throw new Error('Don’t be rude.')
}

// RESOLUTION
const manager = getStripeManager()
const stripeCustomer = await manager.retrieveCustomer(customerId)
if (stripeCustomer.deleted) {
throw new Error('Customer was deleted')
}

const {
metadata: {orgId}
} = stripeCustomer
if (!orgId) {
throw new Error(`orgId not found on metadata for customer ${customerId}`)
}

await r
.table('Organization')
.get(orgId)
.update({
stripeSubscriptionId: subscriptionId
})
.run()

return true
}

export default stripeUpdateSubscription
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,12 @@ const upgradeToTeamTier: MutationResolvers['upgradeToTeamTier'] = async (
return standardError(new Error('Organization does not have a subscription'), {userId: viewerId})
}

if (tier !== 'starter') {
return standardError(new Error('Organization is not on the starter tier'), {
if (tier === 'enterprise') {
return standardError(new Error("Can not change an org's plan from enterprise to team"), {
userId: viewerId
})
} else if (tier === 'team') {
return standardError(new Error('Org is already on team tier'), {
userId: viewerId
})
}
Expand Down
29 changes: 29 additions & 0 deletions packages/server/graphql/private/typeDefs/_legacy.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,35 @@ type Mutation {
customerId: ID!
): Boolean

"""
When stripe tells us a subscription was updated, update the details in our own DB
"""
stripeUpdateSubscription(
"""
The stripe customer ID, or stripeId
"""
customerId: ID!
"""
The stripe subscription ID
"""
subscriptionId: ID!
): Boolean

"""
When stripe tells us a subscription was deleted, update the details in our own DB
"""
stripeDeleteSubscription(
"""
The stripe customer ID, or stripeId
"""
customerId: ID!
"""
The stripe subscription ID
"""
subscriptionId: ID!
): Boolean


"""
When a new invoiceitem is sent from stripe, tag it with metadata
"""
Expand Down
Loading