Skip to content

Commit

Permalink
🔨 updated checkout for application fees
Browse files Browse the repository at this point in the history
  • Loading branch information
Matnabru committed Aug 4, 2023
1 parent 66985d1 commit a48ddd5
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 90 deletions.
2 changes: 1 addition & 1 deletion packages/integrations/gei-stripe/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ input CreateNewUserCheckoutSessionInput{
}

input CreateCheckoutSessionInput{
userEmail: String!
username: String!
"""
Return url after successful transaction
"""
Expand Down
160 changes: 75 additions & 85 deletions packages/integrations/gei-stripe/src/Mutation/createCheckoutSession.ts
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);
8 changes: 4 additions & 4 deletions packages/integrations/gei-stripe/src/zeus/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -926,7 +926,7 @@ setDefaultPaymentMethod?: [{ payload: ValueTypes["setDefaultPaymentMethodInput"]
applicationFee?: ValueTypes["ApplicationFeeInput"] | undefined | null | Variable<any, string>
};
["CreateCheckoutSessionInput"]: {
userEmail: string | Variable<any, string>,
username: string | Variable<any, string>,
/** Return url after successful transaction */
successUrl: string | Variable<any, string>,
cancelUrl: string | Variable<any, string>,
Expand Down Expand Up @@ -1188,7 +1188,7 @@ setDefaultPaymentMethod?: [{ payload: ResolverInputTypes["setDefaultPaymentMetho
applicationFee?: ResolverInputTypes["ApplicationFeeInput"] | undefined | null
};
["CreateCheckoutSessionInput"]: {
userEmail: string,
username: string,
/** Return url after successful transaction */
successUrl: string,
cancelUrl: string,
Expand Down Expand Up @@ -1447,7 +1447,7 @@ export type ModelTypes = {
applicationFee?: ModelTypes["ApplicationFeeInput"] | undefined
};
["CreateCheckoutSessionInput"]: {
userEmail: string,
username: string,
/** Return url after successful transaction */
successUrl: string,
cancelUrl: string,
Expand Down Expand Up @@ -1702,7 +1702,7 @@ export type GraphQLTypes = {
applicationFee?: GraphQLTypes["ApplicationFeeInput"] | undefined
};
["CreateCheckoutSessionInput"]: {
userEmail: string,
username: string,
/** Return url after successful transaction */
successUrl: string,
cancelUrl: string,
Expand Down

0 comments on commit a48ddd5

Please sign in to comment.