feat: 🎸 upgrade all Stripe types for v22 compat
This commit is contained in:
@@ -13,7 +13,7 @@ const calculateTieredAmount = ({
|
||||
let quantityCursor = quantity;
|
||||
for (const tier of tiers) {
|
||||
const unitAmount = new Decimal(
|
||||
tier.unit_amount_decimal || tier.unit_amount!,
|
||||
tier.unit_amount_decimal?.toNumber() || tier.unit_amount!,
|
||||
);
|
||||
|
||||
if (notNullish(tier.up_to)) {
|
||||
@@ -54,7 +54,9 @@ export const getSubItemAmount = ({
|
||||
|
||||
if (price.billing_scheme === "per_unit") {
|
||||
if (price.unit_amount_decimal) {
|
||||
return new Decimal(price.unit_amount_decimal).mul(quantity).toNumber();
|
||||
return new Decimal(price.unit_amount_decimal.toNumber())
|
||||
.mul(quantity)
|
||||
.toNumber();
|
||||
} else {
|
||||
return new Decimal(price.unit_amount || 0).mul(quantity).toNumber();
|
||||
}
|
||||
|
||||
@@ -258,7 +258,7 @@ export const handleErrorSkip = (err: Error, c: Context<HonoEnv>) => {
|
||||
// 4. Check Stripe-specific rules
|
||||
for (const rule of STRIPE_RULES) {
|
||||
if (rule.match(err, c)) {
|
||||
const stripeErr = err as Stripe.errors.StripeError;
|
||||
const stripeErr = err as Stripe.ErrorType.StripeError;
|
||||
logger.warn(`${rule.name}, org: ${ctx.org?.slug || "unknown"}`);
|
||||
return createErrorResponse({
|
||||
c,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { initMasterStripe } from "@/external/connect/initStripeCli";
|
||||
import { Scopes } from "@autumn/shared";
|
||||
import { initMasterStripe } from "@/external/connect/initStripeCli";
|
||||
import { createRoute } from "../../honoMiddlewares/routeHandler";
|
||||
|
||||
export const handleGetMasterStripeAccount = createRoute({
|
||||
@@ -10,7 +10,7 @@ export const handleGetMasterStripeAccount = createRoute({
|
||||
|
||||
try {
|
||||
const masterStripe = initMasterStripe({ env });
|
||||
const account = await masterStripe.accounts.retrieve();
|
||||
const account = await masterStripe.accounts.retrieve(null);
|
||||
|
||||
return c.json({
|
||||
id: account.id,
|
||||
|
||||
@@ -10,6 +10,7 @@ import type { AutumnContext } from "@/honoUtils/HonoEnv";
|
||||
import { buildStripeCheckoutSessionItems } from "@/internal/billing/v2/providers/stripe/utils/checkoutSessions/buildStripeCheckoutSessionItems";
|
||||
import { buildAutumnSubscriptionMetadata } from "@/internal/billing/v2/providers/stripe/utils/common/autumnStripeMetadata";
|
||||
import { stripeDiscountsToCheckoutParams } from "@/internal/billing/v2/providers/stripe/utils/discounts/stripeDiscountsToParams";
|
||||
import type { Checkout as CheckoutSessions } from "stripe/resources/Checkout/Sessions.js";
|
||||
|
||||
export const buildStripeCheckoutSessionAction = ({
|
||||
ctx,
|
||||
@@ -45,11 +46,11 @@ export const buildStripeCheckoutSessionAction = ({
|
||||
// Payment-mode checkout has no top-level default_tax_rates, so one-off items take per-line tax_rates.
|
||||
const taxRateId = billingContext.taxRateId;
|
||||
const applyTaxRateToLineItem = (
|
||||
item: Stripe.Checkout.SessionCreateParams.LineItem,
|
||||
): Stripe.Checkout.SessionCreateParams.LineItem =>
|
||||
item: CheckoutSessions.SessionCreateParams.LineItem,
|
||||
): CheckoutSessions.SessionCreateParams.LineItem =>
|
||||
taxRateId ? { ...item, tax_rates: [taxRateId] } : item;
|
||||
|
||||
const lineItems: Stripe.Checkout.SessionCreateParams.LineItem[] = [
|
||||
const lineItems: CheckoutSessions.SessionCreateParams.LineItem[] = [
|
||||
...recurringLineItems.filter((item) => item.quantity !== 0),
|
||||
...oneOffLineItems
|
||||
.filter((item) => item.quantity !== 0)
|
||||
@@ -64,7 +65,7 @@ export const buildStripeCheckoutSessionAction = ({
|
||||
|
||||
// 5. Build subscription_data (only for subscription mode)
|
||||
const subscriptionData:
|
||||
| Stripe.Checkout.SessionCreateParams.SubscriptionData
|
||||
| CheckoutSessions.SessionCreateParams.SubscriptionData
|
||||
| undefined =
|
||||
mode === "subscription"
|
||||
? {
|
||||
@@ -88,7 +89,7 @@ export const buildStripeCheckoutSessionAction = ({
|
||||
|
||||
// 7. Build params. Tax policy is baked in here (not at execute time) so
|
||||
// the action object is self-describing in logs/EXTRA_LOGS.
|
||||
const autumnAutoTax: Partial<Stripe.Checkout.SessionCreateParams> = org.config
|
||||
const autumnAutoTax: Partial<CheckoutSessions.SessionCreateParams> = org.config
|
||||
.automatic_tax
|
||||
? {
|
||||
automatic_tax: { enabled: true },
|
||||
@@ -98,7 +99,7 @@ export const buildStripeCheckoutSessionAction = ({
|
||||
}
|
||||
: {};
|
||||
|
||||
const params: Stripe.Checkout.SessionCreateParams = {
|
||||
const params: CheckoutSessions.SessionCreateParams = {
|
||||
customer: stripeCustomer?.id ?? "none",
|
||||
mode,
|
||||
line_items: lineItems,
|
||||
@@ -112,6 +113,6 @@ export const buildStripeCheckoutSessionAction = ({
|
||||
type: "create",
|
||||
params,
|
||||
checkoutSessionParams:
|
||||
checkoutSessionParams as Partial<Stripe.Checkout.SessionCreateParams>,
|
||||
checkoutSessionParams as Partial<CheckoutSessions.SessionCreateParams>,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type Stripe from "stripe";
|
||||
import { mergeStripeMetadata } from "@/internal/billing/v2/providers/stripe/utils/common/mergeStripeMetadata";
|
||||
import { buildCheckoutSessionMetadata } from "./buildCheckoutSessionMetadata";
|
||||
import type { Checkout as CheckoutSessions } from "stripe/resources/Checkout/Sessions.js";
|
||||
|
||||
/**
|
||||
* Deep-merges subscription_data so user-provided fields (e.g. metadata)
|
||||
@@ -12,9 +13,9 @@ const mergeSubscriptionData = ({
|
||||
userSubscriptionData,
|
||||
}: {
|
||||
userMetadata?: Record<string, string>;
|
||||
paramsSubscriptionData?: Stripe.Checkout.SessionCreateParams.SubscriptionData;
|
||||
userSubscriptionData?: Stripe.Checkout.SessionCreateParams.SubscriptionData;
|
||||
}): Stripe.Checkout.SessionCreateParams.SubscriptionData | undefined => {
|
||||
paramsSubscriptionData?: CheckoutSessions.SessionCreateParams.SubscriptionData;
|
||||
userSubscriptionData?: CheckoutSessions.SessionCreateParams.SubscriptionData;
|
||||
}): CheckoutSessions.SessionCreateParams.SubscriptionData | undefined => {
|
||||
if (!paramsSubscriptionData && !userSubscriptionData && !userMetadata) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -41,16 +42,16 @@ export const buildCheckoutSessionParams = ({
|
||||
autumnMetadataId,
|
||||
userMetadata,
|
||||
}: {
|
||||
params: Stripe.Checkout.SessionCreateParams;
|
||||
checkoutSessionParams?: Partial<Stripe.Checkout.SessionCreateParams>;
|
||||
params: CheckoutSessions.SessionCreateParams;
|
||||
checkoutSessionParams?: Partial<CheckoutSessions.SessionCreateParams>;
|
||||
currency?: string;
|
||||
defaultAllowPromotionCodes?: boolean;
|
||||
defaultInvoiceCreation?: Stripe.Checkout.SessionCreateParams.InvoiceCreation;
|
||||
defaultSavedPaymentMethodOptions?: Stripe.Checkout.SessionCreateParams.SavedPaymentMethodOptions;
|
||||
defaultInvoiceCreation?: CheckoutSessions.SessionCreateParams.InvoiceCreation;
|
||||
defaultSavedPaymentMethodOptions?: CheckoutSessions.SessionCreateParams.SavedPaymentMethodOptions;
|
||||
autumnMetadataId?: string;
|
||||
userMetadata?: Record<string, string>;
|
||||
}): Stripe.Checkout.SessionCreateParams => {
|
||||
const mergedParams: Stripe.Checkout.SessionCreateParams = {
|
||||
}): CheckoutSessions.SessionCreateParams => {
|
||||
const mergedParams: CheckoutSessions.SessionCreateParams = {
|
||||
...(checkoutSessionParams ?? {}),
|
||||
...params,
|
||||
};
|
||||
@@ -83,11 +84,11 @@ export const buildCheckoutSessionParams = ({
|
||||
: mergeSubscriptionData({
|
||||
userMetadata,
|
||||
paramsSubscriptionData: params.subscription_data as
|
||||
| Stripe.Checkout.SessionCreateParams.SubscriptionData
|
||||
| CheckoutSessions.SessionCreateParams.SubscriptionData
|
||||
| undefined,
|
||||
userSubscriptionData:
|
||||
checkoutSessionParams?.subscription_data as
|
||||
| Stripe.Checkout.SessionCreateParams.SubscriptionData
|
||||
| CheckoutSessions.SessionCreateParams.SubscriptionData
|
||||
| undefined,
|
||||
}),
|
||||
};
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
priceUtils,
|
||||
type StripeItemSpec,
|
||||
} from "@autumn/shared";
|
||||
import type Stripe from "stripe";
|
||||
import type { Checkout as CheckoutSessions } from "stripe/resources/Checkout/Sessions.js";
|
||||
import type { AutumnContext } from "@/honoUtils/HonoEnv";
|
||||
import { customerProductsToOneOffStripeItemSpecs } from "@/internal/billing/v2/providers/stripe/utils/stripeItemSpec/customerProductsToOneOffStripeItemSpecs";
|
||||
import { customerProductsToRecurringStripeItemSpecs } from "@/internal/billing/v2/providers/stripe/utils/stripeItemSpec/customerProductsToRecurringStripeItemSpecs";
|
||||
@@ -17,13 +17,13 @@ import { updateOneOffTieredItems } from "./updateOneOffTieredItems";
|
||||
const isZeroAmountInlineLineItem = ({
|
||||
lineItem,
|
||||
}: {
|
||||
lineItem: Stripe.Checkout.SessionCreateParams.LineItem;
|
||||
lineItem: CheckoutSessions.SessionCreateParams.LineItem;
|
||||
}) => {
|
||||
if (!("price_data" in lineItem) || !lineItem.price_data) return false;
|
||||
|
||||
return (
|
||||
lineItem.price_data.unit_amount === 0 ||
|
||||
lineItem.price_data.unit_amount_decimal === "0"
|
||||
lineItem.price_data.unit_amount_decimal?.toNumber() === 0
|
||||
);
|
||||
};
|
||||
|
||||
@@ -34,7 +34,7 @@ const isZeroAmountInlineRecurringStripeItemSpec = ({
|
||||
}) => {
|
||||
if (!stripeItemSpec.stripeInlinePrice?.recurring) return false;
|
||||
|
||||
return stripeItemSpec.stripeInlinePrice.unit_amount_decimal === "0";
|
||||
return stripeItemSpec.stripeInlinePrice.unit_amount_decimal.toNumber() === 0;
|
||||
};
|
||||
|
||||
const getRecurringCadenceKey = ({
|
||||
@@ -94,10 +94,10 @@ const applyAdjustableQuantityToPrepaidLineItem = ({
|
||||
spec,
|
||||
billingContext,
|
||||
}: {
|
||||
lineItem: Stripe.Checkout.SessionCreateParams.LineItem;
|
||||
lineItem: CheckoutSessions.SessionCreateParams.LineItem;
|
||||
spec: StripeItemSpec;
|
||||
billingContext: BillingContext;
|
||||
}): Stripe.Checkout.SessionCreateParams.LineItem => {
|
||||
}): CheckoutSessions.SessionCreateParams.LineItem => {
|
||||
const { autumnPrice, autumnEntitlement, autumnProduct } = spec;
|
||||
|
||||
if (!autumnPrice || !autumnEntitlement || !isPrepaidPrice(autumnPrice)) {
|
||||
@@ -132,7 +132,7 @@ const applyAdjustableQuantityToPrepaidLineItem = ({
|
||||
}),
|
||||
maximum: 999999,
|
||||
},
|
||||
} as Stripe.Checkout.SessionCreateParams.LineItem;
|
||||
} as CheckoutSessions.SessionCreateParams.LineItem;
|
||||
};
|
||||
|
||||
export const buildStripeCheckoutSessionItems = ({
|
||||
@@ -144,8 +144,8 @@ export const buildStripeCheckoutSessionItems = ({
|
||||
billingContext: BillingContext;
|
||||
newCustomerProducts: FullCusProduct[];
|
||||
}): {
|
||||
recurringLineItems: Stripe.Checkout.SessionCreateParams.LineItem[];
|
||||
oneOffLineItems: Stripe.Checkout.SessionCreateParams.LineItem[];
|
||||
recurringLineItems: CheckoutSessions.SessionCreateParams.LineItem[];
|
||||
oneOffLineItems: CheckoutSessions.SessionCreateParams.LineItem[];
|
||||
} => {
|
||||
// 1. Filter customer products by active statuses
|
||||
const activeCustomerProducts = filterCustomerProductsByActiveStatuses({
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
} from "@autumn/shared";
|
||||
import type Stripe from "stripe";
|
||||
import { stripeItemSpecToCheckoutLineItem } from "../stripeItemSpec/stripeItemSpecToStripeParam";
|
||||
import type { Checkout as CheckoutSessions } from "stripe/resources/Checkout/Sessions.js";
|
||||
|
||||
/**
|
||||
* Update one-off items to use inline price_data if they're tiered.
|
||||
@@ -22,7 +23,7 @@ export const updateOneOffTieredItems = ({
|
||||
}: {
|
||||
oneOffItemSpecs: StripeItemSpec[];
|
||||
org: Organization;
|
||||
}): Stripe.Checkout.SessionCreateParams.LineItem[] => {
|
||||
}): CheckoutSessions.SessionCreateParams.LineItem[] => {
|
||||
const currency = orgToCurrency({ org });
|
||||
|
||||
return oneOffItemSpecs.map((item) => {
|
||||
|
||||
@@ -252,7 +252,10 @@ const mergeStripeAndBillingLineItems = ({
|
||||
stripeLineItem.parent?.invoice_item_details?.invoice_item ?? null,
|
||||
stripe_subscription_item_id: stripeSubscriptionItemId,
|
||||
stripe_product_id: (priceDetails?.product as string) ?? null,
|
||||
stripe_price_id: priceDetails?.price ?? null,
|
||||
stripe_price_id:
|
||||
typeof priceDetails?.price === "string"
|
||||
? priceDetails?.price
|
||||
: (priceDetails?.price?.id ?? null),
|
||||
stripe_discountable: stripeLineItem.discountable,
|
||||
|
||||
// Amounts (from Stripe or Autumn depending on discountable flag)
|
||||
@@ -336,7 +339,10 @@ const createDbLineItemFromStripeOnly = ({
|
||||
stripeLineItem.parent?.invoice_item_details?.invoice_item ?? null,
|
||||
stripe_subscription_item_id: stripeSubscriptionItemId,
|
||||
stripe_product_id: (priceDetails?.product as string) ?? null,
|
||||
stripe_price_id: priceDetails?.price ?? null,
|
||||
stripe_price_id:
|
||||
typeof priceDetails?.price === "string"
|
||||
? priceDetails?.price
|
||||
: (priceDetails?.price?.id ?? null),
|
||||
stripe_discountable: stripeLineItem.discountable,
|
||||
|
||||
amount,
|
||||
|
||||
@@ -12,7 +12,7 @@ import type { PayInvoiceResult } from "./payStripeInvoice";
|
||||
const getFailureCodeFromStripeError = ({
|
||||
stripeError,
|
||||
}: {
|
||||
stripeError: Stripe.errors.StripeError;
|
||||
stripeError: Stripe.ErrorType.StripeError;
|
||||
}): "3ds_required" | "payment_failed" => {
|
||||
const authCodes = ["authentication_required", "authentication_not_handled"];
|
||||
|
||||
@@ -61,7 +61,7 @@ export const handleInvoicePaymentFailure = ({
|
||||
}
|
||||
|
||||
// 2. Check if it's a Stripe error
|
||||
const stripeError = error as Stripe.errors.StripeError;
|
||||
const stripeError = error as Stripe.ErrorType.StripeError;
|
||||
const isStripeError = stripeError.type !== undefined;
|
||||
|
||||
if (!isStripeError) throw error;
|
||||
|
||||
@@ -13,7 +13,7 @@ export type PayInvoiceResult = {
|
||||
code: PaymentFailureCode;
|
||||
reason: string;
|
||||
};
|
||||
stripeError?: Stripe.errors.StripeError;
|
||||
stripeError?: Stripe.ErrorType.StripeError;
|
||||
};
|
||||
|
||||
type PayStripeInvoiceParams = {
|
||||
|
||||
@@ -13,7 +13,11 @@ type InlinePriceLike = {
|
||||
divide_by?: number;
|
||||
round?: string;
|
||||
} | null;
|
||||
unit_amount_decimal?: string | number | null;
|
||||
unit_amount_decimal?:
|
||||
| string
|
||||
| number
|
||||
| ReturnType<typeof Stripe.Decimal.from>
|
||||
| null;
|
||||
};
|
||||
|
||||
export type StripePriceShape = {
|
||||
@@ -65,7 +69,7 @@ export const stripePriceToShape = ({
|
||||
intervalCount: price.recurring?.interval_count,
|
||||
tiersMode: price.tiers_mode ?? undefined,
|
||||
transformQuantity: transformQuantityKey(price.transform_quantity),
|
||||
unitAmountDecimal: decimalAmount(price.unit_amount_decimal),
|
||||
unitAmountDecimal: decimalAmount(price.unit_amount_decimal?.toNumber()),
|
||||
});
|
||||
|
||||
export const inlinePriceToShape = ({
|
||||
@@ -80,7 +84,7 @@ export const inlinePriceToShape = ({
|
||||
interval: price.recurring?.interval,
|
||||
intervalCount: price.recurring?.interval_count,
|
||||
transformQuantity: transformQuantityKey(price.transform_quantity),
|
||||
unitAmountDecimal: decimalAmount(price.unit_amount_decimal),
|
||||
unitAmountDecimal: decimalAmount(price.unit_amount_decimal?.toString()),
|
||||
});
|
||||
|
||||
export const stripePriceShapesEqual = (
|
||||
|
||||
@@ -3,13 +3,21 @@ import {
|
||||
type StripeInlinePrice,
|
||||
type StripeItemSpec,
|
||||
} from "@autumn/shared";
|
||||
import type Stripe from "stripe";
|
||||
import Stripe from "stripe";
|
||||
import type { Checkout as CheckoutSessions } from "stripe/resources/Checkout/Sessions.js";
|
||||
|
||||
type StoredPriceParam = { price: string };
|
||||
type RecurringInlinePriceParam = {
|
||||
price_data: Stripe.SubscriptionCreateParams.Item["price_data"];
|
||||
};
|
||||
|
||||
const toStripeInlinePriceData = (stripeInlinePrice: StripeInlinePrice) => ({
|
||||
...stripeInlinePrice,
|
||||
unit_amount_decimal: Stripe.Decimal.from(
|
||||
stripeInlinePrice.unit_amount_decimal.toString(),
|
||||
),
|
||||
});
|
||||
|
||||
/**
|
||||
* Returns the price param for a StripeItemSpec — either a stored price ID or inline price_data.
|
||||
* For inline prices, asserts that `recurring` is present (one-off items should not reach this path).
|
||||
@@ -29,7 +37,7 @@ const toRecurringPriceParam = ({
|
||||
}
|
||||
return {
|
||||
price_data: {
|
||||
...spec.stripeInlinePrice,
|
||||
...toStripeInlinePriceData(spec.stripeInlinePrice),
|
||||
recurring: spec.stripeInlinePrice.recurring,
|
||||
},
|
||||
};
|
||||
@@ -56,9 +64,11 @@ const toPriceParam = ({
|
||||
spec,
|
||||
}: {
|
||||
spec: StripeItemSpec;
|
||||
}): StoredPriceParam | { price_data: StripeInlinePrice } => {
|
||||
}):
|
||||
| StoredPriceParam
|
||||
| { price_data: ReturnType<typeof toStripeInlinePriceData> } => {
|
||||
if (spec.stripeInlinePrice) {
|
||||
return { price_data: spec.stripeInlinePrice };
|
||||
return { price_data: toStripeInlinePriceData(spec.stripeInlinePrice) };
|
||||
}
|
||||
return { price: spec.stripePriceId! };
|
||||
};
|
||||
@@ -68,7 +78,7 @@ export const stripeItemSpecToCheckoutLineItem = ({
|
||||
spec,
|
||||
}: {
|
||||
spec: StripeItemSpec;
|
||||
}): Stripe.Checkout.SessionCreateParams.LineItem => {
|
||||
}): CheckoutSessions.SessionCreateParams.LineItem => {
|
||||
return {
|
||||
...toPriceParam({ spec }),
|
||||
quantity: spec.quantity,
|
||||
|
||||
@@ -77,7 +77,7 @@ export const storeInvoiceLineItems = async ({
|
||||
if (info?.isMetered) return false;
|
||||
|
||||
// Filter $0 empty price placeholders (e.g. stripe_empty_price_id)
|
||||
if (li.pricing?.unit_amount_decimal === "0") return false;
|
||||
if (li.pricing?.unit_amount_decimal?.toNumber() === 0) return false;
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
@@ -18,6 +18,7 @@ import { notNullish } from "@/utils/genUtils.js";
|
||||
import type { AutumnContext } from "../../../honoUtils/HonoEnv.js";
|
||||
import { attachParamsToMetadata } from "../../billing/attach/utils/attachParamsToMetadata.js";
|
||||
import type { AttachParams } from "../cusProducts/AttachParams.js";
|
||||
import type { Checkout as CheckoutSessions } from "stripe/resources/Checkout/Sessions.js";
|
||||
|
||||
export const handleCreateCheckout = async ({
|
||||
ctx,
|
||||
@@ -85,10 +86,10 @@ export const handleCreateCheckout = async ({
|
||||
}
|
||||
|
||||
const checkoutParams = attachParams.checkoutSessionParams as
|
||||
| Partial<Stripe.Checkout.SessionCreateParams>
|
||||
| Partial<CheckoutSessions.SessionCreateParams>
|
||||
| undefined;
|
||||
const checkoutSubscriptionData = checkoutParams?.subscription_data as
|
||||
| Stripe.Checkout.SessionCreateParams.SubscriptionData
|
||||
| CheckoutSessions.SessionCreateParams.SubscriptionData
|
||||
| undefined;
|
||||
const trialEnd =
|
||||
freeTrial && !attachParams.disableFreeTrial
|
||||
@@ -104,7 +105,7 @@ export const handleCreateCheckout = async ({
|
||||
: undefined;
|
||||
|
||||
const subscriptionData:
|
||||
| Stripe.Checkout.SessionCreateParams.SubscriptionData
|
||||
| CheckoutSessions.SessionCreateParams.SubscriptionData
|
||||
| undefined = isRecurring
|
||||
? {
|
||||
...(checkoutSubscriptionData ?? {}),
|
||||
@@ -139,7 +140,7 @@ export const handleCreateCheckout = async ({
|
||||
notNullish(checkoutParams?.payment_method_types) ||
|
||||
notNullish(checkoutParams?.payment_method_configuration);
|
||||
|
||||
let sessionParams: Stripe.Checkout.SessionCreateParams = {
|
||||
let sessionParams: CheckoutSessions.SessionCreateParams = {
|
||||
customer: customer.processor.id,
|
||||
line_items: items,
|
||||
mode: isRecurring ? "subscription" : "payment",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createStripeCli } from "@/external/connect/createStripeCli.js";
|
||||
import { Scopes } from "@autumn/shared";
|
||||
import { createStripeCli } from "@/external/connect/createStripeCli.js";
|
||||
import { createRoute } from "@/honoMiddlewares/routeHandler.js";
|
||||
import { isStripeConnected } from "../../orgUtils.js";
|
||||
|
||||
@@ -15,7 +15,7 @@ export const handleGetStripeAccount = createRoute({
|
||||
|
||||
try {
|
||||
const stripeCli = createStripeCli({ org, env });
|
||||
const accountDetails = await stripeCli.accounts.retrieve();
|
||||
const accountDetails = await stripeCli.accounts.retrieve(null);
|
||||
return c.json(accountDetails);
|
||||
} catch (error) {
|
||||
logger.warn(
|
||||
|
||||
@@ -46,8 +46,8 @@ export const shouldReconnectStripe = async ({
|
||||
const stripeCli = createStripeCli({ org, env });
|
||||
const newKey = new Stripe(stripeKey);
|
||||
|
||||
const oldAccount = await stripeCli.accounts.retrieve();
|
||||
const newAccount = await newKey.accounts.retrieve();
|
||||
const oldAccount = await stripeCli.accounts.retrieve(null);
|
||||
const newAccount = await newKey.accounts.retrieve(null);
|
||||
|
||||
return oldAccount.id !== newAccount.id;
|
||||
} catch (error) {
|
||||
|
||||
@@ -19,7 +19,7 @@ export const handleStripeSecretKey = async ({
|
||||
// 1. Check if key is valid
|
||||
await checkKeyValid(secretKey);
|
||||
const stripe = new Stripe(secretKey);
|
||||
const account = await stripe.accounts.retrieve();
|
||||
const account = await stripe.accounts.retrieve(null);
|
||||
|
||||
// 2. Disconnect existing webhook endpoints
|
||||
const curWebhooks = await stripe.webhookEndpoints.list();
|
||||
|
||||
@@ -6,13 +6,14 @@ import {
|
||||
trace,
|
||||
} from "@opentelemetry/api";
|
||||
import type Stripe from "stripe";
|
||||
import type { StripeConfig } from "stripe/lib.js";
|
||||
import { otelConfig } from "./otelConfig.js";
|
||||
|
||||
const TRACER_NAME = "autumn.stripe";
|
||||
const SPAN_NAME = "stripe.api";
|
||||
const INSTRUMENTED = new WeakSet<object>();
|
||||
|
||||
type StripeHttpClient = NonNullable<Stripe.StripeConfig["httpClient"]>;
|
||||
type StripeHttpClient = NonNullable<StripeConfig["httpClient"]>;
|
||||
|
||||
type StripeApiLike = {
|
||||
httpClient?: StripeHttpClient;
|
||||
|
||||
@@ -84,7 +84,7 @@ export const triggerSubscriptionCreated = async ({
|
||||
created: getUnixTime(subscriptionCreatedAtMs ?? Date.now()),
|
||||
schedule: scheduleId ?? null,
|
||||
} as Stripe.Subscription;
|
||||
const retrieveSubscription: Stripe.SubscriptionsResource["retrieve"] =
|
||||
const retrieveSubscription: Stripe.SubscriptionResource["retrieve"] =
|
||||
async () =>
|
||||
stripeResponse({ object: subscription, requestId: `req_${stripeSubId}` });
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ export const normalizeActualSubItem = ({
|
||||
quantity: item.quantity ?? 0,
|
||||
isInline: !!autumnCusPriceId,
|
||||
unitAmountDecimal: autumnCusPriceId
|
||||
? (item.price.unit_amount_decimal ?? undefined)
|
||||
? (item.price.unit_amount_decimal?.toString() ?? undefined)
|
||||
: undefined,
|
||||
};
|
||||
};
|
||||
@@ -42,7 +42,7 @@ export const normalizeActualPhaseItem = ({
|
||||
autumnCustomerPriceId: autumnCusPriceId || undefined,
|
||||
quantity: item.quantity ?? 0,
|
||||
isInline: !!autumnCusPriceId,
|
||||
unitAmountDecimal,
|
||||
unitAmountDecimal: unitAmountDecimal?.toString(),
|
||||
};
|
||||
};
|
||||
|
||||
@@ -58,7 +58,7 @@ export const normalizeExpectedPhaseItem = ({
|
||||
let unitAmountDecimal: string | undefined;
|
||||
if (hasInlinePrice) {
|
||||
const priceData = (item as { price_data: StripeInlinePrice }).price_data;
|
||||
unitAmountDecimal = priceData.unit_amount_decimal;
|
||||
unitAmountDecimal = priceData.unit_amount_decimal.toString();
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -58,7 +58,10 @@ export const expectStripeInvoiceLineItemPeriodCorrect = async ({
|
||||
Boolean(
|
||||
findPriceFromStripeId({
|
||||
prices: usagePrices,
|
||||
stripePriceId: line.pricing?.price_details?.price ?? "",
|
||||
stripePriceId:
|
||||
typeof line.pricing?.price_details?.price === "string"
|
||||
? line.pricing?.price_details?.price
|
||||
: (line.pricing?.price_details?.price?.id ?? ""),
|
||||
}),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import type Stripe from "stripe";
|
||||
import Stripe from "stripe";
|
||||
import {
|
||||
findMatchingInlinePriceIdForPhaseItem,
|
||||
findMatchingInlineSubscriptionItem,
|
||||
@@ -10,7 +10,7 @@ const inlinePrice = {
|
||||
product: "stripe_prod_inline",
|
||||
currency: "usd",
|
||||
recurring: { interval: "month" as const, interval_count: 1 },
|
||||
unit_amount_decimal: "1000",
|
||||
unit_amount_decimal: Stripe.Decimal.from("1000"),
|
||||
};
|
||||
|
||||
const subscriptionItem = ({
|
||||
|
||||
@@ -15,7 +15,7 @@ import { customerProducts } from "@tests/utils/fixtures/db/customerProducts";
|
||||
import { prices } from "@tests/utils/fixtures/db/prices";
|
||||
import { stripeSubscriptions } from "@tests/utils/fixtures/stripe/subscriptions";
|
||||
import chalk from "chalk";
|
||||
import type Stripe from "stripe";
|
||||
import Stripe from "stripe";
|
||||
import { buildStripeSubscriptionItemsUpdate } from "@/internal/billing/v2/providers/stripe/utils/subscriptionItems/buildStripeSubscriptionItemsUpdate";
|
||||
import {
|
||||
createCustomerPricesForProduct,
|
||||
@@ -968,7 +968,7 @@ describe(
|
||||
const changedItem = currentItems.find(
|
||||
(item) => item.metadata.inline_price === "true",
|
||||
)!;
|
||||
changedItem.price.unit_amount_decimal = "999999";
|
||||
changedItem.price.unit_amount_decimal = Stripe.Decimal.from("999999");
|
||||
|
||||
const result = buildUpdateWithItems({ currentItems });
|
||||
|
||||
|
||||
@@ -40,7 +40,9 @@
|
||||
|
||||
// Explicitly set drizzle-orm path to avoid monorepo issues
|
||||
"drizzle-orm": ["../node_modules/drizzle-orm"],
|
||||
"drizzle-orm/*": ["../node_modules/drizzle-orm/*"]
|
||||
"drizzle-orm/*": ["../node_modules/drizzle-orm/*"],
|
||||
"stripe": ["../node_modules/.bun/node_modules/stripe/esm/stripe.esm.node"],
|
||||
"stripe/*": ["../node_modules/.bun/node_modules/stripe/esm/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src", "tests", "scripts", "experiments", "perf"],
|
||||
|
||||
@@ -13,7 +13,7 @@ export type StripeInlinePrice = {
|
||||
product: string;
|
||||
currency: string;
|
||||
recurring?: Stripe.PriceCreateParams.Recurring;
|
||||
unit_amount_decimal: string;
|
||||
unit_amount_decimal: ReturnType<typeof Stripe.Decimal.from>;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,7 +15,9 @@
|
||||
"@api/*": ["./api/*"],
|
||||
"@models/*": ["./models/*"],
|
||||
"@utils/*": ["./utils/*"],
|
||||
"@autumn/ksuid": ["../packages/ksuid/src/index.ts"]
|
||||
"@autumn/ksuid": ["../packages/ksuid/src/index.ts"],
|
||||
"stripe": ["../node_modules/.bun/node_modules/stripe/esm/stripe.esm.node"],
|
||||
"stripe/*": ["../node_modules/.bun/node_modules/stripe/esm/*"]
|
||||
}
|
||||
},
|
||||
"include": ["./**/*"],
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { Decimal } from "decimal.js";
|
||||
import { Decimal as DecimalJS } from "decimal.js";
|
||||
import Stripe from "stripe";
|
||||
|
||||
type StripeDecimal = ReturnType<typeof Stripe.Decimal.from>;
|
||||
/**
|
||||
* Zero-decimal currencies that Stripe handles without decimal places.
|
||||
* These currencies don't require multiplying/dividing by 100.
|
||||
@@ -38,11 +40,11 @@ export const atmnToStripeAmount = ({
|
||||
if (ZERO_DECIMAL_CURRENCIES.includes(currency.toUpperCase())) {
|
||||
return amount;
|
||||
}
|
||||
return new Decimal(amount).mul(100).round().toNumber();
|
||||
return new DecimalJS(amount).mul(100).round().toNumber();
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts an Autumn amount to a Stripe decimal string.
|
||||
* Converts an Autumn amount to a Stripe Decimal class.
|
||||
* For most currencies, multiplies by 100 and returns as string with decimal places.
|
||||
* For zero-decimal currencies like JPY, returns the amount as-is with decimal places.
|
||||
* Used for Stripe API calls that require unit_amount_decimal as a string.
|
||||
@@ -52,16 +54,20 @@ export const atmnToStripeAmountDecimal = ({
|
||||
currency = "USD",
|
||||
decimalPlaces = 10,
|
||||
}: {
|
||||
amount: number | Decimal;
|
||||
amount: number | DecimalJS;
|
||||
currency?: string;
|
||||
decimalPlaces?: number;
|
||||
}): string => {
|
||||
const decimal = amount instanceof Decimal ? amount : new Decimal(amount);
|
||||
}): StripeDecimal => {
|
||||
const decimal = amount instanceof DecimalJS ? amount : new DecimalJS(amount);
|
||||
|
||||
if (ZERO_DECIMAL_CURRENCIES.includes(currency.toUpperCase())) {
|
||||
return decimal.toDecimalPlaces(decimalPlaces).toString();
|
||||
return Stripe.Decimal.from(
|
||||
decimal.toDecimalPlaces(decimalPlaces).toString(),
|
||||
);
|
||||
}
|
||||
return decimal.mul(100).toDecimalPlaces(decimalPlaces).toString();
|
||||
return Stripe.Decimal.from(
|
||||
decimal.mul(100).toDecimalPlaces(decimalPlaces).toString(),
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -75,23 +81,23 @@ export const stripeToAtmnAmount = ({
|
||||
decimalPlaces = 10,
|
||||
round = true,
|
||||
}: {
|
||||
amount: number;
|
||||
amount: number | StripeDecimal;
|
||||
currency?: string;
|
||||
decimalPlaces?: number;
|
||||
round?: boolean;
|
||||
}): number => {
|
||||
let finalAmount = amount;
|
||||
let finalAmount = typeof amount === "number" ? amount : amount.toNumber();
|
||||
|
||||
if (!ZERO_DECIMAL_CURRENCIES.includes(currency.toUpperCase())) {
|
||||
finalAmount = new Decimal(amount).div(100).toNumber();
|
||||
finalAmount = new DecimalJS(finalAmount).div(100).toNumber();
|
||||
}
|
||||
|
||||
if (round) {
|
||||
return new Decimal(finalAmount).toDecimalPlaces(decimalPlaces).toNumber();
|
||||
return new DecimalJS(finalAmount).toDecimalPlaces(decimalPlaces).toNumber();
|
||||
}
|
||||
|
||||
if (decimalPlaces) {
|
||||
return new Decimal(finalAmount).toDecimalPlaces(decimalPlaces).toNumber();
|
||||
return new DecimalJS(finalAmount).toDecimalPlaces(decimalPlaces).toNumber();
|
||||
}
|
||||
|
||||
return finalAmount;
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
} from "@utils/productUtils/priceUtils/classifyPriceUtils";
|
||||
import { atmnToStripeAmountDecimal } from "@utils/productUtils/priceUtils/convertAmountUtils";
|
||||
import { Decimal } from "decimal.js";
|
||||
import type Stripe from "stripe";
|
||||
import Stripe from "stripe";
|
||||
|
||||
/**
|
||||
* Builds the Stripe tier array for a V2 prepaid price.
|
||||
@@ -33,7 +33,7 @@ export const priceToStripePrepaidV2Tiers = ({
|
||||
price: Price;
|
||||
entitlement: Entitlement;
|
||||
org: Organization;
|
||||
}) => {
|
||||
}): Stripe.PriceCreateParams.Tier[] => {
|
||||
const config = price.config as UsagePriceConfig;
|
||||
|
||||
const tiers: Stripe.PriceCreateParams.Tier[] = [];
|
||||
@@ -42,7 +42,7 @@ export const priceToStripePrepaidV2Tiers = ({
|
||||
// allowance. Applies to both graduated and volume pricing.
|
||||
if (entitlement.allowance) {
|
||||
tiers.push({
|
||||
unit_amount_decimal: "0",
|
||||
unit_amount_decimal: Stripe.Decimal.zero,
|
||||
up_to: entitlement.allowance,
|
||||
});
|
||||
}
|
||||
@@ -90,10 +90,12 @@ export const priceToStripePrepaidV2Tiers = ({
|
||||
.ceil()
|
||||
.toNumber(),
|
||||
|
||||
unit_amount_decimal: new Decimal(tier.unit_amount_decimal ?? 0)
|
||||
.mul(config.billing_units ?? 1)
|
||||
.toString(),
|
||||
unit_amount_decimal: Stripe.Decimal.from(
|
||||
new Decimal(tier.unit_amount_decimal?.toNumber() ?? 0)
|
||||
.mul(config.billing_units ?? 1)
|
||||
.toString(),
|
||||
),
|
||||
}));
|
||||
|
||||
return dividedTiers;
|
||||
return dividedTiers satisfies unknown as Stripe.PriceCreateParams.Tier[];
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user