Skip to content

Commit

Permalink
gei-stripe 0.3.0 with collecting payment method and stripe connect
Browse files Browse the repository at this point in the history
  • Loading branch information
Matnabru committed Aug 2, 2023
1 parent f0913e6 commit 8ccf005
Show file tree
Hide file tree
Showing 23 changed files with 1,064 additions and 350 deletions.
2 changes: 1 addition & 1 deletion packages/integrations/gei-stripe/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "gei-stripe",
"version": "0.2.2",
"version": "0.3.0",
"description": "Automatically generated by graphql-editor-cli",
"main": "lib/index.js",
"scripts": {
Expand Down
103 changes: 95 additions & 8 deletions packages/integrations/gei-stripe/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,92 @@ type Query{
}

type Mutation{
"""
Creates stripe customer for further purchases, links with user "email" field in UserCollection
"""
initStripeCustomer(
initStripeCustomerInput: InitStripeCustomerInput!
): Boolean!
createPaymentSession(
payload: CreatePaymentSessionPayload!
createCheckoutSession(
payload: CreateCheckoutSessionInput!
): String!
createNewUserPaymentSession(
payload: CreateNewUserPaymentSessionPayload!
createNewUserCheckoutSession(
payload: CreateNewUserCheckoutSessionInput!
): String!
createCustomerPortal(
payload: CreateCustomerPortalPayload!
payload: CreateCustomerPortalInput!
): String!
createConnectAccount(
payload: CreateConnectAccountInput!
): Boolean!
"""
Gather payment method id using Stripe.js or a pre-built solution like Stripe Elements
"""
attachPaymentMethod(
payload: AttachPaymentMethodInput!
): Boolean!
setDefaultPaymentMethod(
payload: setDefaultPaymentMethodInput!
): Boolean!
"""
entry point for Weebhooks.
"""
webhook: String
}

input setDefaultPaymentMethodInput{
attachedPaymentMethodId: String!
customerId: String!
}

input AttachPaymentMethodInput {
paymentMethodId: String!
customerId: String!
}

input CreateConnectAccountInput {
type: ConnectAccountType!
country: String!
email: String!
business_type: ConnectAccountBusinessType!
bankAccount: BankAccountInput!
}

enum ConnectAccountBusinessType{
company
government_entity
individual
non_profit
}

enum ConnectAccountType {
standard
express
custom
}

input BankAccountInput {
country: String!
"""
Required supported currency for the country https://stripe.com/docs/payouts
"""
currency: String!
"""
IBAN account number
"""
account_number: String!
"""
Required when attaching the bank account to a Customer
"""
account_holder_name: String!
account_holder_type: BankAccountHolderType!
}

enum BankAccountHolderType {
individual
company
}

input SubscriptionFilter {
customerId: String
}
Expand Down Expand Up @@ -76,31 +144,50 @@ input InitStripeCustomerInput{
address: AddressInput
}

input CreateNewUserPaymentSessionPayload{
input CreateNewUserCheckoutSessionInput{
"""
Return url after successful transaction
"""
successUrl: String!
cancelUrl: String!
products: [ProductInput!]!
"""
Define amount to transfer into stripe connect account and set the rest for application fees
"""
applicationFee: ApplicationFeeInput
}

input CreatePaymentSessionPayload{
input CreateCheckoutSessionInput{
userEmail: String!
"""
Return url after successful transaction
"""
successUrl: String!
cancelUrl: String!
products: [ProductInput!]!
"""
Define amount to transfer into stripe connect account and set the rest for application fees
"""
applicationFee: ApplicationFeeInput
}

input ApplicationFeeInput {
"""
Value from 0-100
"""
feePercentage: Int!
"""
Connect Account (not stripe customer) id
"""
connectAccountId: String!
}

input ProductInput{
productId: String!
quantity: Int!
}

input CreateCustomerPortalPayload{
input CreateCustomerPortalInput{
userEmail: String!
returnUrl: String!
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { FieldResolveInput } from 'stucco-js';
import { resolverFor } from '../zeus/index.js';
import { newStripe } from '../utils/utils.js';

export const handler = async (input: FieldResolveInput) =>
resolverFor(
'Mutation',
'attachPaymentMethod',
async ({ payload: { customerId, paymentMethodId } }) => {
const stripe = newStripe();

const attachedPaymentMethod = await stripe.paymentMethods.attach(paymentMethodId, {
customer: customerId,
});

return true;
},
)(input.arguments, input.source);
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ type item = {
quantity?: number;
};
export const handler = async (input: FieldResolveInput) =>
resolverFor('Mutation', 'createPaymentSession', async ({ payload: { successUrl, cancelUrl, products, userEmail } }) => {
resolverFor('Mutation', 'createCheckoutSession', async ({ payload: { successUrl, cancelUrl, products, userEmail } }) => {
const stripe = newStripe();
const user = await MongoOrb('UserCollection').collection.findOne(
{ email: userEmail },
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { FieldResolveInput } from 'stucco-js';
import { resolverFor } from '../zeus/index.js';
import { newStripe } from '../utils/utils.js';

export const handler = async (input: FieldResolveInput) =>
resolverFor(
'Mutation',
'createConnectAccount',
async ({ payload: { type, country, email, bankAccount, business_type } }) => {
const stripe = newStripe();

const account = await stripe.accounts.create({
type: type,
country,
email,
business_type,
});

const accountToken = await stripe.tokens.create({
bank_account: {
country,
currency: bankAccount.currency,
account_holder_name: bankAccount.account_holder_name,
account_holder_type: bankAccount.account_holder_type,
account_number: bankAccount.account_number,
},
});

const stripeBankAccount = await stripe.accounts.createExternalAccount(account.id, {
external_account: accountToken.id,
});

return true;
},
)(input.arguments, input.source);
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import Stripe from 'stripe';
import { newStripe } from '../utils/utils.js';
import { resolverFor } from '../zeus/index.js';
import { FieldResolveInput } from 'stucco-js';
type item = {
price: string;
quantity?: number;
};
export const handler = async (input: FieldResolveInput) =>
resolverFor(
'Mutation',
'createNewUserCheckoutSession',
async ({ payload: { successUrl, cancelUrl, products, applicationFee } }) => {
const stripe = newStripe();
const subscriptionItems: item[] = [];
const oneTimePaymentItems: item[] = [];

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 }) };

totalAmount += (price.unit_amount || 0) * (quantity || 0);

if (price.type === 'recurring') {
subscriptionItems.push(item);
} else {
oneTimePaymentItems.push(item);
}
}),
);

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 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',
};

if (applicationFeeAmount > 0 && applicationFee) {
sessionData.payment_intent_data = {
application_fee_amount: applicationFeeAmount,
transfer_data: {
destination: applicationFee.connectAccountId,
},
};
}

const session = await stripe.checkout.sessions.create(sessionData);
return session.url;
}
},
)(input.arguments, input.source);

This file was deleted.

Loading

0 comments on commit 8ccf005

Please sign in to comment.