(
userId: string,
options: GetHighestPrioritySubscriptionOptions = {}
)
| 77 | * leak onto the user's row until the next billing cycle. |
| 78 | */ |
| 79 | export async function getHighestPrioritySubscription( |
| 80 | userId: string, |
| 81 | options: GetHighestPrioritySubscriptionOptions = {} |
| 82 | ) { |
| 83 | const { onError = 'return-null', executor = db } = options |
| 84 | try { |
| 85 | const [personalSubs, memberships] = await Promise.all([ |
| 86 | executor |
| 87 | .select() |
| 88 | .from(subscription) |
| 89 | .where( |
| 90 | and( |
| 91 | eq(subscription.referenceId, userId), |
| 92 | inArray(subscription.status, ENTITLED_SUBSCRIPTION_STATUSES) |
| 93 | ) |
| 94 | ), |
| 95 | executor |
| 96 | .select({ organizationId: member.organizationId }) |
| 97 | .from(member) |
| 98 | .where(eq(member.userId, userId)), |
| 99 | ]) |
| 100 | |
| 101 | const orgIds = memberships.map((m: { organizationId: string }) => m.organizationId) |
| 102 | |
| 103 | let orgSubs: typeof personalSubs = [] |
| 104 | if (orgIds.length > 0) { |
| 105 | // Verify orgs exist to filter out orphaned subscriptions |
| 106 | const existingOrgs = await executor |
| 107 | .select({ id: organization.id }) |
| 108 | .from(organization) |
| 109 | .where(inArray(organization.id, orgIds)) |
| 110 | |
| 111 | const validOrgIds = existingOrgs.map((o) => o.id) |
| 112 | |
| 113 | if (validOrgIds.length > 0) { |
| 114 | orgSubs = await executor |
| 115 | .select() |
| 116 | .from(subscription) |
| 117 | .where( |
| 118 | and( |
| 119 | inArray(subscription.referenceId, validOrgIds), |
| 120 | inArray(subscription.status, ENTITLED_SUBSCRIPTION_STATUSES) |
| 121 | ) |
| 122 | ) |
| 123 | } |
| 124 | } |
| 125 | |
| 126 | if (personalSubs.length === 0 && orgSubs.length === 0) return null |
| 127 | |
| 128 | return pickHighestPrioritySubscription( |
| 129 | [...orgSubs, ...personalSubs], |
| 130 | [checkEnterprisePlan, checkTeamPlan, checkProPlan] |
| 131 | ) |
| 132 | } catch (error) { |
| 133 | logger.error('Error getting highest priority subscription', { error, userId }) |
| 134 | if (onError === 'throw') { |
| 135 | throw error |
| 136 | } |
no test coverage detected