From 1f14773789a2cccf34b7616790e02533cee66f14 Mon Sep 17 00:00:00 2001 From: Matnabru Date: Thu, 10 Aug 2023 16:55:55 +0200 Subject: [PATCH] gei stripe 0.4.2 updated webhooks and checkout --- packages/integrations/gei-stripe/package.json | 2 +- .../src/Mutation/createCheckoutSession.ts | 74 +++++++++++++++---- .../gei-stripe/src/utils/customerEvents.ts | 1 + .../src/utils/externalAccountEvents.ts | 3 +- .../gei-stripe/src/utils/invoiceEvents.ts | 2 +- .../gei-stripe/src/utils/stripePriceEvents.ts | 3 +- .../src/utils/stripeProductEvents.ts | 3 +- .../src/utils/stripeSubscriptionEvents.ts | 3 +- .../gei-stripe/src/utils/tax_rateEvents.ts | 3 +- packages/integrations/gei-stripe/stucco.json | 21 ++++++ 10 files changed, 92 insertions(+), 23 deletions(-) diff --git a/packages/integrations/gei-stripe/package.json b/packages/integrations/gei-stripe/package.json index fb29003..989ad13 100644 --- a/packages/integrations/gei-stripe/package.json +++ b/packages/integrations/gei-stripe/package.json @@ -1,6 +1,6 @@ { "name": "gei-stripe", - "version": "0.4.1", + "version": "0.4.2", "description": "Automatically generated by graphql-editor-cli", "main": "lib/index.js", "scripts": { diff --git a/packages/integrations/gei-stripe/src/Mutation/createCheckoutSession.ts b/packages/integrations/gei-stripe/src/Mutation/createCheckoutSession.ts index 52c4703..e5fb534 100644 --- a/packages/integrations/gei-stripe/src/Mutation/createCheckoutSession.ts +++ b/packages/integrations/gei-stripe/src/Mutation/createCheckoutSession.ts @@ -3,6 +3,7 @@ import { newStripe } from '../utils/utils.js'; import { resolverFor } from '../zeus/index.js'; import { FieldResolveInput } from 'stucco-js'; import { MongoOrb } from '../db/orm.js'; +import { WithId } from 'mongodb'; type item = { price: string; quantity?: number; @@ -15,37 +16,47 @@ export const handler = async (input: FieldResolveInput) => const stripe = newStripe(); const subscriptionItems: item[] = []; const oneTimePaymentItems: item[] = []; - const user = await MongoOrb('UserCollection').collection.findOne( - { username }, - ); + const user = await MongoOrb('UserCollection').collection.findOne({ username }); 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); + price = await MongoOrb('StripePriceCollection').collection.findOne({ id: product.productId }); + if(!price) throw new Error("Stripe price does not exist in database"); + if(price.billing_scheme == "tiered"){ + price = await stripe.prices.retrieve(product.productId, { + expand: ['tiers'] + }); + } } else if (product.productId.startsWith('prod_')) { - const { default_price } = await stripe.products.retrieve(product.productId); - if (!default_price) { + const foundProduct = await MongoOrb('StripeProductCollection').collection.findOne({ id: product.productId }); + if(!foundProduct) throw new Error("Stripe product does not exist in database ") + if (!foundProduct.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); + typeof foundProduct.default_price === 'string' + ? await MongoOrb('StripePriceCollection').collection.findOne({ id: foundProduct.default_price }) + : await MongoOrb('StripePriceCollection').collection.findOne({ id: foundProduct.default_price.id }); } else { - throw new Error('Invalid product ID: ' + product.productId); + throw new Error('Invalid ID: ' + product.productId); } - - const quantity = price.type === 'recurring' || price.recurring?.usage_type === 'metered' ? 1 : product.quantity; + if(!price){ + throw new Error("Could not fetch price"); + } + const quantity = + price.type === 'recurring' || price.recurring?.usage_type === 'metered' ? 1 : product.quantity; const item = { price: price.id, ...(quantity && { quantity }) }; - totalAmount += (price.unit_amount || 0) * (quantity || 0); - + if (price.type === 'recurring' && price.tiers) { + totalAmount += calculateTieredPricing(price, quantity); + } else { + totalAmount += (price.unit_amount || 0) * (quantity || 0); + } if (price.type === 'recurring') { subscriptionItems.push(item); } else { @@ -75,7 +86,7 @@ export const handler = async (input: FieldResolveInput) => address: 'auto', name: 'auto', }, - customer: user.stripeId + customer: user.stripeId, }; if (applicationFeeAmount > 0 && applicationFee && oneTimePaymentItems.length > 0) { @@ -85,6 +96,9 @@ export const handler = async (input: FieldResolveInput) => destination: applicationFee.connectAccountId, }, }; + sessionData.invoice_creation = { + enabled: true, + }; } if (applicationFeeAmount > 0 && applicationFee && subscriptionItems.length > 0) { @@ -102,3 +116,31 @@ export const handler = async (input: FieldResolveInput) => }, )(input.arguments, input.source); +function calculateTieredPricing(price: Stripe.Response | WithId, quantity: number) { + if (!price.tiers) return (price.unit_amount || 0) * quantity; + + let totalAmount = 0; + let remainingQuantity = quantity; + let previousTierEnd = 0; // This keeps track of the last tier's "up_to" value + + for (const tier of price.tiers) { + const currentTierQuantity = tier.up_to + ? Math.min(tier.up_to - previousTierEnd, remainingQuantity) + : remainingQuantity; + + if (tier.flat_amount !== null) { + totalAmount += tier.flat_amount * currentTierQuantity; + } else if (tier.unit_amount) { + totalAmount += tier.unit_amount * currentTierQuantity; + } + + if (tier.up_to) { + previousTierEnd = tier.up_to; + remainingQuantity -= currentTierQuantity; + } + + if (remainingQuantity <= 0) break; + } + + return totalAmount; +} diff --git a/packages/integrations/gei-stripe/src/utils/customerEvents.ts b/packages/integrations/gei-stripe/src/utils/customerEvents.ts index aeb05de..0c2fb2a 100644 --- a/packages/integrations/gei-stripe/src/utils/customerEvents.ts +++ b/packages/integrations/gei-stripe/src/utils/customerEvents.ts @@ -12,6 +12,7 @@ export const customerUpdate = async (subEvent: Stripe.Customer) => { return await MongoOrb('StripeCustomerCollection').collection.updateOne( { id: subEvent.id }, { $set: subEventWithoutId }, + { upsert: true } ); }; diff --git a/packages/integrations/gei-stripe/src/utils/externalAccountEvents.ts b/packages/integrations/gei-stripe/src/utils/externalAccountEvents.ts index 9ea2d38..cd7c45c 100644 --- a/packages/integrations/gei-stripe/src/utils/externalAccountEvents.ts +++ b/packages/integrations/gei-stripe/src/utils/externalAccountEvents.ts @@ -12,7 +12,8 @@ export const externalAccountInsert = async (subEvent: ExternalAccount) => { const { id, ...subEventWithoutId } = subEvent; return await MongoOrb('StripeExternalAccountCollection').collection.updateOne( { id: subEvent.id }, - { $set: subEventWithoutId } + { $set: subEventWithoutId }, + { upsert: true } );; }; diff --git a/packages/integrations/gei-stripe/src/utils/invoiceEvents.ts b/packages/integrations/gei-stripe/src/utils/invoiceEvents.ts index c79ab92..420a594 100644 --- a/packages/integrations/gei-stripe/src/utils/invoiceEvents.ts +++ b/packages/integrations/gei-stripe/src/utils/invoiceEvents.ts @@ -5,7 +5,7 @@ const upsertInvoice = async (invoiceEvent: Stripe.Invoice) => { return await MongoOrb('StripeInvoiceCollection').collection.updateOne( { id: invoiceEvent.id }, { $set: { ...invoiceEvent } }, - { upsert: true } // creates a new document if no documents match the filter + { upsert: true } ); }; diff --git a/packages/integrations/gei-stripe/src/utils/stripePriceEvents.ts b/packages/integrations/gei-stripe/src/utils/stripePriceEvents.ts index 58410c8..7efb741 100644 --- a/packages/integrations/gei-stripe/src/utils/stripePriceEvents.ts +++ b/packages/integrations/gei-stripe/src/utils/stripePriceEvents.ts @@ -11,7 +11,8 @@ export const stripePriceInsert = async (subEvent: Stripe.Price) => { const { id, ...subEventWithoutId } = subEvent; return await MongoOrb('StripePriceCollection').collection.updateOne( { id: subEvent.id }, - { $set: subEventWithoutId } + { $set: subEventWithoutId }, + { upsert: true } );; }; diff --git a/packages/integrations/gei-stripe/src/utils/stripeProductEvents.ts b/packages/integrations/gei-stripe/src/utils/stripeProductEvents.ts index c8495fb..f48dd46 100644 --- a/packages/integrations/gei-stripe/src/utils/stripeProductEvents.ts +++ b/packages/integrations/gei-stripe/src/utils/stripeProductEvents.ts @@ -11,7 +11,8 @@ export const stripeProductInsert = async (subEvent: Stripe.Product) => { const { id, ...subEventWithoutId } = subEvent; return await MongoOrb('StripeProductCollection').collection.updateOne( { id: subEvent.id }, - { $set: subEventWithoutId } + { $set: subEventWithoutId }, + { upsert: true } );; }; diff --git a/packages/integrations/gei-stripe/src/utils/stripeSubscriptionEvents.ts b/packages/integrations/gei-stripe/src/utils/stripeSubscriptionEvents.ts index 1516f8c..6a3b750 100644 --- a/packages/integrations/gei-stripe/src/utils/stripeSubscriptionEvents.ts +++ b/packages/integrations/gei-stripe/src/utils/stripeSubscriptionEvents.ts @@ -11,7 +11,8 @@ export const stripeSubscriptionInsert = async (subEvent: Stripe.Subscription) => const { id, ...subEventWithoutId } = subEvent; return await MongoOrb('StripeSubscriptionCollection').collection.updateOne( { id: subEvent.id }, - { $set: subEventWithoutId } + { $set: subEventWithoutId }, + { upsert: true } );; }; diff --git a/packages/integrations/gei-stripe/src/utils/tax_rateEvents.ts b/packages/integrations/gei-stripe/src/utils/tax_rateEvents.ts index dac9316..0ce66e8 100644 --- a/packages/integrations/gei-stripe/src/utils/tax_rateEvents.ts +++ b/packages/integrations/gei-stripe/src/utils/tax_rateEvents.ts @@ -11,7 +11,8 @@ export const taxRateUpdated = async (subEvent: Stripe.TaxRate) => { const { id, ...subEventWithoutId } = subEvent; return await MongoOrb('StripeTaxRateCollection').collection.updateOne( { id: subEvent.id }, - { $set: subEventWithoutId } + { $set: subEventWithoutId }, + { upsert: true } );; }; \ No newline at end of file diff --git a/packages/integrations/gei-stripe/stucco.json b/packages/integrations/gei-stripe/stucco.json index b0adfc3..06096ee 100644 --- a/packages/integrations/gei-stripe/stucco.json +++ b/packages/integrations/gei-stripe/stucco.json @@ -83,6 +83,27 @@ "resolve": { "name": "Mutation.setDefaultPaymentMethod.handler" } + }, + "Query.paymentIntents": { + "name": "paymentIntents", + "description": "List stripe payment intents", + "resolve": { + "name": "Query.paymentIntents.handler" + } + }, + "Query.invoices": { + "name": "invoices", + "description": "List stripe invoices for specific user", + "resolve": { + "name": "Query.invoices.handler" + } + }, + "Query.customer": { + "name": "customer", + "description": "Show stripe customer object with payment methods", + "resolve": { + "name": "Query.customer.handler" + } } }, "azureOpts": {