-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
🔨 updated checkout for application fees
- Loading branch information
Showing
3 changed files
with
80 additions
and
90 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
160 changes: 75 additions & 85 deletions
160
packages/integrations/gei-stripe/src/Mutation/createCheckoutSession.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,103 +1,93 @@ | ||
import { MongoOrb } from '../db/orm.js'; | ||
import Stripe from 'stripe'; | ||
import { newStripe } from '../utils/utils.js'; | ||
import { resolverFor } from '../zeus/index.js'; | ||
import { FieldResolveInput } from 'stucco-js'; | ||
import { MongoOrb } from '../db/orm.js'; | ||
type item = { | ||
price: string; | ||
quantity?: number; | ||
}; | ||
export const handler = async (input: FieldResolveInput) => | ||
resolverFor('Mutation', 'createCheckoutSession', async ({ payload: { successUrl, cancelUrl, products, userEmail } }) => { | ||
const stripe = newStripe(); | ||
const user = await MongoOrb('UserCollection').collection.findOne( | ||
{ email: userEmail }, | ||
resolverFor( | ||
'Mutation', | ||
'createCheckoutSession', | ||
async ({ payload: { successUrl, cancelUrl, products, applicationFee, username } }) => { | ||
const stripe = newStripe(); | ||
const subscriptionItems: item[] = []; | ||
const oneTimePaymentItems: item[] = []; | ||
const user = await MongoOrb('UserCollection').collection.findOne( | ||
{ username }, | ||
); | ||
if (!user) { | ||
throw new Error('Invalid product or customer'); | ||
} | ||
if (!user.stripeId) { | ||
throw new Error('Stripe customer not initialized'); | ||
} | ||
if (!user) { | ||
throw new Error('Invalid product or customer'); | ||
} | ||
let totalAmount = 0; | ||
|
||
|
||
await Promise.all( | ||
products.map(async (product) => { | ||
let price; | ||
if (product.productId.startsWith('price_')) { | ||
price = await stripe.prices.retrieve(product.productId); | ||
} else if (product.productId.startsWith('prod_')) { | ||
const { default_price } = await stripe.products.retrieve(product.productId); | ||
if (!default_price) { | ||
throw new Error('Cannot find product ' + product.productId + ' default price'); | ||
} | ||
price = | ||
typeof default_price === 'string' | ||
? await stripe.prices.retrieve(default_price) | ||
: await stripe.prices.retrieve(default_price.id); | ||
} else { | ||
throw new Error('Invalid product ID: ' + product.productId); | ||
} | ||
|
||
const quantity = price.type === 'recurring' || price.recurring?.usage_type === 'metered' ? 1 : product.quantity; | ||
const item = { price: price.id, ...(quantity && { quantity }) }; | ||
|
||
const subscriptionItems: item[] = []; | ||
const oneTimePaymentItems: item[] = []; | ||
totalAmount += (price.unit_amount || 0) * (quantity || 0); | ||
|
||
await Promise.all( | ||
products.map(async (product) => { | ||
let price; | ||
if (product.productId.startsWith('price_')) { | ||
price = await stripe.prices.retrieve(product.productId); | ||
} else if (product.productId.startsWith('prod_')) { | ||
const { default_price } = await stripe.products.retrieve(product.productId); | ||
if (!default_price) { | ||
throw new Error('Cannot find product ' + product.productId + ' default price'); | ||
if (price.type === 'recurring') { | ||
subscriptionItems.push(item); | ||
} else { | ||
oneTimePaymentItems.push(item); | ||
} | ||
price = | ||
typeof default_price === 'string' | ||
? await stripe.prices.retrieve(default_price) | ||
: await stripe.prices.retrieve(default_price.id); | ||
} else { | ||
throw new Error('Invalid product ID: ' + product.productId); | ||
} | ||
}), | ||
); | ||
|
||
const applicationFeeAmount = applicationFee ? Math.round((totalAmount * applicationFee.feePercentage) / 100) : 0; | ||
|
||
if (subscriptionItems.length > 0 && oneTimePaymentItems.length > 0) { | ||
throw new Error('Cannot handle subscription items and one-time payment items in the same request'); | ||
} | ||
|
||
if (subscriptionItems.length > 0 || oneTimePaymentItems.length > 0) { | ||
const lineItems = subscriptionItems.length > 0 ? subscriptionItems : oneTimePaymentItems; | ||
const mode = subscriptionItems.length > 0 ? 'subscription' : 'payment'; | ||
|
||
const item = { | ||
price: price.id, | ||
...(!price.recurring | ||
? { quantity: product.quantity } | ||
: price.recurring.usage_type !== 'metered' && { quantity: 1 }), | ||
const sessionData: Stripe.Checkout.SessionCreateParams = { | ||
success_url: successUrl, | ||
cancel_url: cancelUrl, | ||
line_items: lineItems, | ||
mode: mode, | ||
tax_id_collection: { enabled: true }, | ||
automatic_tax: { enabled: true }, | ||
billing_address_collection: 'required', | ||
customer: user.stripeId | ||
}; | ||
|
||
if (price.type === 'recurring') { | ||
subscriptionItems.push(item); | ||
} else { | ||
oneTimePaymentItems.push(item); | ||
if (applicationFeeAmount > 0 && applicationFee) { | ||
sessionData.payment_intent_data = { | ||
application_fee_amount: applicationFeeAmount, | ||
transfer_data: { | ||
destination: applicationFee.connectAccountId, | ||
}, | ||
}; | ||
} | ||
}), | ||
); | ||
|
||
if (subscriptionItems.length > 0 && oneTimePaymentItems.length > 0) { | ||
throw new Error('Cannot handle subscription items and one-time payment items in the same request'); | ||
} | ||
if (subscriptionItems.length > 0) { | ||
const session = await stripe.checkout.sessions.create({ | ||
success_url: successUrl, | ||
cancel_url: cancelUrl, | ||
line_items: subscriptionItems, | ||
mode: 'subscription', | ||
customer: user.stripeId, | ||
tax_id_collection: { enabled: true }, | ||
subscription_data: { | ||
metadata: { assignedTo: user.email }, | ||
}, | ||
automatic_tax: { | ||
enabled: true, | ||
}, | ||
billing_address_collection: 'required', | ||
customer_update: { | ||
address: 'auto', | ||
name: 'auto', | ||
}, | ||
}); | ||
return session.url; | ||
} | ||
const session = await stripe.checkout.sessions.create(sessionData); | ||
return session.url; | ||
} | ||
}, | ||
)(input.arguments, input.source); | ||
|
||
if (oneTimePaymentItems.length > 0) { | ||
const session = await stripe.checkout.sessions.create({ | ||
success_url: successUrl, | ||
cancel_url: cancelUrl, | ||
line_items: oneTimePaymentItems, | ||
mode: 'payment', | ||
customer: user.stripeId, | ||
tax_id_collection: { enabled: true }, | ||
automatic_tax: { | ||
enabled: true, | ||
}, | ||
billing_address_collection: 'required', | ||
customer_update: { | ||
address: 'auto', | ||
name: 'auto', | ||
}, | ||
}); | ||
return session.url; | ||
} | ||
})(input.arguments, input.source); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters