fix: updated new subscription fail to add invoice_checkout metadata type
This commit is contained in:
@@ -26,5 +26,6 @@ BUN_PARALLEL_COMPACT \
|
|||||||
'server/tests/interval/multiSub' \
|
'server/tests/interval/multiSub' \
|
||||||
'server/tests/billing/cancel' \
|
'server/tests/billing/cancel' \
|
||||||
'server/tests/billing/new-billing-subscription' \
|
'server/tests/billing/new-billing-subscription' \
|
||||||
|
'server/tests/billing/invoice-action-required/new-subscription' \
|
||||||
--max=6
|
--max=6
|
||||||
|
|
||||||
|
|||||||
@@ -74,13 +74,7 @@ const main = async () => {
|
|||||||
db,
|
db,
|
||||||
logger,
|
logger,
|
||||||
};
|
};
|
||||||
await Promise.all([
|
await Promise.all([cronTask(), runProductCron(), runInvoiceCron({ ctx })]);
|
||||||
cronTask(),
|
|
||||||
runProductCron(),
|
|
||||||
runInvoiceCron({ ctx }),
|
|
||||||
|
|
||||||
// TODO: Add runUsageCron({ ctx })
|
|
||||||
]);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
new CronJob(
|
new CronJob(
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { type Metadata, MetadataType, metadata } from "@autumn/shared";
|
import { type Metadata, MetadataType, metadata } from "@autumn/shared";
|
||||||
|
|
||||||
import { and, eq, lt } from "drizzle-orm";
|
import { and, eq, isNotNull, lt, or } from "drizzle-orm";
|
||||||
import { createStripeCli } from "../../external/connect/createStripeCli";
|
import { createStripeCli } from "../../external/connect/createStripeCli";
|
||||||
|
import { invoiceToSubId } from "../../external/stripe/stripeInvoiceUtils";
|
||||||
import type { AttachParams } from "../../internal/customers/cusProducts/AttachParams";
|
import type { AttachParams } from "../../internal/customers/cusProducts/AttachParams";
|
||||||
import { MetadataService } from "../../internal/metadata/MetadataService";
|
import { MetadataService } from "../../internal/metadata/MetadataService";
|
||||||
import type { CronContext } from "../utils/CronContext";
|
import type { CronContext } from "../utils/CronContext";
|
||||||
@@ -13,73 +14,91 @@ export const handleVoidInvoiceCron = async ({
|
|||||||
ctx: CronContext;
|
ctx: CronContext;
|
||||||
metadata: Metadata;
|
metadata: Metadata;
|
||||||
}) => {
|
}) => {
|
||||||
try {
|
const { logger, db } = ctx;
|
||||||
const { logger, db } = ctx;
|
const data = metadata.data as AttachParams;
|
||||||
const data = metadata.data as AttachParams;
|
const { org, customer } = data;
|
||||||
const { org, customer } = data;
|
const stripeCli = createStripeCli({ org, env: customer.env });
|
||||||
const stripeCli = createStripeCli({ org, env: customer.env });
|
|
||||||
|
|
||||||
if (!metadata.stripe_invoice_id) {
|
if (!metadata.stripe_invoice_id) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const invoice = await stripeCli.invoices.retrieve(
|
const invoice = await stripeCli.invoices.retrieve(metadata.stripe_invoice_id);
|
||||||
metadata.stripe_invoice_id,
|
const subId = invoiceToSubId({ invoice });
|
||||||
);
|
const voidSub = metadata.type === MetadataType.InvoiceCheckout;
|
||||||
console.log(
|
|
||||||
`Invoice: ${metadata.stripe_invoice_id} for customer ${customer.id} (org: ${org.slug})`,
|
|
||||||
);
|
|
||||||
if (invoice.status === "open") {
|
|
||||||
try {
|
|
||||||
await stripeCli.invoices.voidInvoice(metadata.stripe_invoice_id);
|
|
||||||
logger.info(
|
|
||||||
`voided invoice ${metadata.stripe_invoice_id} for customer ${customer.id} (org: ${org.slug})`,
|
|
||||||
);
|
|
||||||
|
|
||||||
await MetadataService.delete({
|
console.log(
|
||||||
db,
|
`Invoice: ${metadata.stripe_invoice_id} for customer ${customer.id} (org: ${org.slug})`,
|
||||||
id: metadata.id,
|
);
|
||||||
});
|
|
||||||
} catch (error) {
|
if (invoice.status === "open") {
|
||||||
logger.error(`Error voiding invoice: ${error}`);
|
try {
|
||||||
|
await stripeCli.invoices.voidInvoice(metadata.stripe_invoice_id);
|
||||||
|
logger.info(
|
||||||
|
`voided invoice ${metadata.stripe_invoice_id} for customer ${customer.id} (org: ${org.slug})`,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (voidSub && subId) {
|
||||||
|
logger.info(`Voiding sub ${subId} [created through invoice checkout]`);
|
||||||
|
try {
|
||||||
|
await stripeCli.subscriptions.cancel(subId);
|
||||||
|
} catch (error) {
|
||||||
|
logger.warn(`Error voiding sub ${subId}: ${error}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (invoice.status === "void") {
|
|
||||||
await MetadataService.delete({
|
await MetadataService.delete({
|
||||||
db,
|
db,
|
||||||
id: metadata.id,
|
id: metadata.id,
|
||||||
});
|
});
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`Error voiding invoice: ${error}`);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} else if (
|
||||||
console.log("Error running invoice cron:", error);
|
invoice.status === "void" ||
|
||||||
|
invoice.status === "paid" ||
|
||||||
|
invoice.status === "uncollectible"
|
||||||
|
) {
|
||||||
|
await MetadataService.delete({
|
||||||
|
db,
|
||||||
|
id: metadata.id,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const runInvoiceCron = async ({ ctx }: { ctx: CronContext }) => {
|
export const runInvoiceCron = async ({ ctx }: { ctx: CronContext }) => {
|
||||||
console.log("Running invoice cron");
|
try {
|
||||||
const { db } = ctx;
|
console.log("Running invoice cron");
|
||||||
|
const { db } = ctx;
|
||||||
|
|
||||||
// 1. Fetch from metadata invoices
|
// 1. Fetch from metadata invoices
|
||||||
const invoices = await db
|
const invoices = await db
|
||||||
.select()
|
.select()
|
||||||
.from(metadata)
|
.from(metadata)
|
||||||
.where(
|
.where(
|
||||||
and(
|
and(
|
||||||
eq(metadata.type, MetadataType.InvoiceActionRequired),
|
or(
|
||||||
lt(metadata.expires_at, Date.now()),
|
eq(metadata.type, MetadataType.InvoiceActionRequired),
|
||||||
),
|
eq(metadata.type, MetadataType.InvoiceCheckout),
|
||||||
);
|
),
|
||||||
|
lt(metadata.expires_at, Date.now()),
|
||||||
|
isNotNull(metadata.stripe_invoice_id),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
const batchSize = 50;
|
const batchSize = 50;
|
||||||
for (let i = 0; i < invoices.length; i += batchSize) {
|
for (let i = 0; i < invoices.length; i += batchSize) {
|
||||||
const batch = invoices.slice(i, i + batchSize);
|
const batch = invoices.slice(i, i + batchSize);
|
||||||
|
|
||||||
const promises = [];
|
const promises = [];
|
||||||
for (const metadata of batch) {
|
for (const metadata of batch) {
|
||||||
promises.push(handleVoidInvoiceCron({ ctx, metadata }));
|
promises.push(handleVoidInvoiceCron({ ctx, metadata }));
|
||||||
|
}
|
||||||
|
await Promise.all(promises);
|
||||||
|
console.log(`Handled ${i + batch.length}/${invoices.length} invoices`);
|
||||||
|
console.log("----------------------------------\n");
|
||||||
}
|
}
|
||||||
await Promise.all(promises);
|
console.log("FINISHED INVOICE CRON");
|
||||||
console.log(`Handled ${i + batch.length}/${invoices.length} invoices`);
|
} catch (error) {
|
||||||
console.log("----------------------------------\n");
|
console.error("Error running invoice cron:", error);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
console.log("FINISHED INVOICE CRON");
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ export const handleInvoiceCheckoutPaid = async ({
|
|||||||
metadata: Metadata;
|
metadata: Metadata;
|
||||||
}) => {
|
}) => {
|
||||||
const { logger, org, env, db } = ctx;
|
const { logger, org, env, db } = ctx;
|
||||||
|
logger.info(
|
||||||
|
`invoice.paid, handling invoice checkout paid for metadata: ${metadata.id}`,
|
||||||
|
);
|
||||||
|
|
||||||
const { subId, anchorToUnix, config, ...rest } =
|
const { subId, anchorToUnix, config, ...rest } =
|
||||||
metadata.data as AttachParams;
|
metadata.data as AttachParams;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { type AttachConfig, ErrCode } from "@autumn/shared";
|
import { type AttachConfig, ErrCode } from "@autumn/shared";
|
||||||
|
import type { Logger } from "@server/external/logtail/logtailUtils";
|
||||||
import type Stripe from "stripe";
|
import type Stripe from "stripe";
|
||||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||||
import { getCusPaymentMethod } from "@/external/stripe/stripeCusUtils.js";
|
import { getCusPaymentMethod } from "@/external/stripe/stripeCusUtils.js";
|
||||||
@@ -30,7 +31,7 @@ export const createStripeSub2 = async ({
|
|||||||
config: AttachConfig;
|
config: AttachConfig;
|
||||||
billingCycleAnchorUnix?: number;
|
billingCycleAnchorUnix?: number;
|
||||||
itemSet: ItemSet;
|
itemSet: ItemSet;
|
||||||
logger: any;
|
logger: Logger;
|
||||||
}) => {
|
}) => {
|
||||||
const { customer, invoiceOnly, freeTrial, org, now, rewards, metadata } =
|
const { customer, invoiceOnly, freeTrial, org, now, rewards, metadata } =
|
||||||
attachParams;
|
attachParams;
|
||||||
@@ -69,6 +70,7 @@ export const createStripeSub2 = async ({
|
|||||||
payment_behavior: isCustomPaymentMethod
|
payment_behavior: isCustomPaymentMethod
|
||||||
? "default_incomplete"
|
? "default_incomplete"
|
||||||
: "allow_incomplete",
|
: "allow_incomplete",
|
||||||
|
|
||||||
add_invoice_items: invoiceItems,
|
add_invoice_items: invoiceItems,
|
||||||
collection_method: invoiceOnly ? "send_invoice" : "charge_automatically",
|
collection_method: invoiceOnly ? "send_invoice" : "charge_automatically",
|
||||||
days_until_due: invoiceOnly ? 30 : undefined,
|
days_until_due: invoiceOnly ? 30 : undefined,
|
||||||
|
|||||||
@@ -4,14 +4,17 @@ import {
|
|||||||
type AttachFunctionResponse,
|
type AttachFunctionResponse,
|
||||||
AttachFunctionResponseSchema,
|
AttachFunctionResponseSchema,
|
||||||
AttachScenario,
|
AttachScenario,
|
||||||
ErrCode, isTrialing,
|
ErrCode,
|
||||||
|
isTrialing,
|
||||||
MetadataType,
|
MetadataType,
|
||||||
SuccessCode
|
SuccessCode,
|
||||||
} from "@autumn/shared";
|
} from "@autumn/shared";
|
||||||
|
import { addMinutes } from "date-fns";
|
||||||
import type Stripe from "stripe";
|
import type Stripe from "stripe";
|
||||||
import { getEarliestPeriodEnd } from "@/external/stripe/stripeSubUtils/convertSubUtils.js";
|
import { getEarliestPeriodEnd } from "@/external/stripe/stripeSubUtils/convertSubUtils.js";
|
||||||
import { getStripeSubItems2 } from "@/external/stripe/stripeSubUtils/getStripeSubItems.js";
|
import { getStripeSubItems2 } from "@/external/stripe/stripeSubUtils/getStripeSubItems.js";
|
||||||
import { subIsCanceled } from "@/external/stripe/stripeSubUtils.js";
|
import { subIsCanceled } from "@/external/stripe/stripeSubUtils.js";
|
||||||
|
import { attachParamsToMetadata } from "@/internal/billing/attach/utils/attachParamsToMetadata.js";
|
||||||
import { createFullCusProduct } from "@/internal/customers/add-product/createFullCusProduct.js";
|
import { createFullCusProduct } from "@/internal/customers/add-product/createFullCusProduct.js";
|
||||||
import { handleCreateCheckout } from "@/internal/customers/add-product/handleCreateCheckout.js";
|
import { handleCreateCheckout } from "@/internal/customers/add-product/handleCreateCheckout.js";
|
||||||
import type { AttachParams } from "@/internal/customers/cusProducts/AttachParams.js";
|
import type { AttachParams } from "@/internal/customers/cusProducts/AttachParams.js";
|
||||||
@@ -32,8 +35,6 @@ import { subToNewSchedule } from "../../mergeUtils/subToNewSchedule.js";
|
|||||||
import { handleUpgradeFlowSchedule } from "../upgradeFlow/handleUpgradeFlowSchedule.js";
|
import { handleUpgradeFlowSchedule } from "../upgradeFlow/handleUpgradeFlowSchedule.js";
|
||||||
import { updateStripeSub2 } from "../upgradeFlow/updateStripeSub2.js";
|
import { updateStripeSub2 } from "../upgradeFlow/updateStripeSub2.js";
|
||||||
import { createStripeSub2 } from "./createStripeSub2.js";
|
import { createStripeSub2 } from "./createStripeSub2.js";
|
||||||
import { attachParamsToMetadata } from "@/internal/billing/attach/utils/attachParamsToMetadata.js";
|
|
||||||
import { addMinutes } from "date-fns";
|
|
||||||
|
|
||||||
export const handlePaidProduct = async ({
|
export const handlePaidProduct = async ({
|
||||||
ctx,
|
ctx,
|
||||||
@@ -210,21 +211,31 @@ export const handlePaidProduct = async ({
|
|||||||
logger,
|
logger,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const subInvoice: Stripe.Invoice | undefined = (sub.latest_invoice as Stripe.Invoice)
|
|
||||||
|
|
||||||
if(subInvoice && subInvoice.status === "open" && !config.invoiceCheckout) {
|
const subInvoice: Stripe.Invoice | undefined =
|
||||||
|
sub.latest_invoice as Stripe.Invoice;
|
||||||
|
|
||||||
|
if (
|
||||||
|
subInvoice &&
|
||||||
|
subInvoice.status === "open" &&
|
||||||
|
!config.invoiceCheckout
|
||||||
|
) {
|
||||||
logger.info(
|
logger.info(
|
||||||
`[update subscription] invoice action required: ${subInvoice.id}`,
|
`[create subscription] invoice checkout created because invoice is open: ${subInvoice.id}`,
|
||||||
);
|
);
|
||||||
const metadata = await attachParamsToMetadata({
|
const metadata = await attachParamsToMetadata({
|
||||||
db: ctx.db,
|
db: ctx.db,
|
||||||
attachParams,
|
attachParams: {
|
||||||
type: MetadataType.InvoiceActionRequired,
|
...attachParams,
|
||||||
|
subId: sub.id,
|
||||||
|
anchorToUnix: sub.billing_cycle_anchor * 1000,
|
||||||
|
config,
|
||||||
|
},
|
||||||
|
type: MetadataType.InvoiceCheckout,
|
||||||
stripeInvoiceId: subInvoice.id as string,
|
stripeInvoiceId: subInvoice.id as string,
|
||||||
expiresAt: addMinutes(Date.now(), 10).getTime(),
|
expiresAt: addMinutes(Date.now(), 10).getTime(),
|
||||||
});
|
});
|
||||||
|
|
||||||
await stripeCli.invoices.update(subInvoice.id, {
|
await stripeCli.invoices.update(subInvoice.id, {
|
||||||
metadata: {
|
metadata: {
|
||||||
autumn_metadata_id: metadata.id,
|
autumn_metadata_id: metadata.id,
|
||||||
@@ -241,7 +252,6 @@ export const handlePaidProduct = async ({
|
|||||||
error instanceof RecaseError &&
|
error instanceof RecaseError &&
|
||||||
!invoiceOnly &&
|
!invoiceOnly &&
|
||||||
error.code === ErrCode.CreateStripeSubscriptionFailed
|
error.code === ErrCode.CreateStripeSubscriptionFailed
|
||||||
|
|
||||||
) {
|
) {
|
||||||
return await handleCreateCheckout({
|
return await handleCreateCheckout({
|
||||||
ctx,
|
ctx,
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
|||||||
import { constructFeatureItem } from "@/utils/scriptUtils/constructItem.js";
|
import { constructFeatureItem } from "@/utils/scriptUtils/constructItem.js";
|
||||||
import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js";
|
import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js";
|
||||||
import { initProductsV0 } from "@/utils/scriptUtils/testUtils/initProductsV0.js";
|
import { initProductsV0 } from "@/utils/scriptUtils/testUtils/initProductsV0.js";
|
||||||
import { CusService } from "../../src/internal/customers/CusService";
|
|
||||||
import { initCustomerV3 } from "../../src/utils/scriptUtils/testUtils/initCustomerV3";
|
import { initCustomerV3 } from "../../src/utils/scriptUtils/testUtils/initCustomerV3";
|
||||||
|
|
||||||
const free = constructProduct({
|
const free = constructProduct({
|
||||||
@@ -33,8 +32,8 @@ const pro = constructProduct({
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
const premium = constructProduct({
|
const oneOff = constructProduct({
|
||||||
type: "premium",
|
type: "one_off",
|
||||||
items: [
|
items: [
|
||||||
constructFeatureItem({
|
constructFeatureItem({
|
||||||
featureId: TestFeature.Messages,
|
featureId: TestFeature.Messages,
|
||||||
@@ -65,11 +64,11 @@ describe(`${chalk.yellowBright("temp: temporary script for testing")}`, () => {
|
|||||||
const autumnV1: AutumnInt = new AutumnInt({ version: ApiVersion.V1_2 });
|
const autumnV1: AutumnInt = new AutumnInt({ version: ApiVersion.V1_2 });
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await CusService.deleteByOrgId({
|
// await CusService.deleteByOrgId({
|
||||||
db: ctx.db,
|
// db: ctx.db,
|
||||||
orgId: ctx.org.id,
|
// orgId: ctx.org.id,
|
||||||
env: ctx.env,
|
// env: ctx.env,
|
||||||
});
|
// });
|
||||||
|
|
||||||
await initCustomerV3({
|
await initCustomerV3({
|
||||||
ctx,
|
ctx,
|
||||||
@@ -80,19 +79,21 @@ describe(`${chalk.yellowBright("temp: temporary script for testing")}`, () => {
|
|||||||
|
|
||||||
await initProductsV0({
|
await initProductsV0({
|
||||||
ctx,
|
ctx,
|
||||||
products: [free, pro, premium],
|
products: [free, pro, oneOff],
|
||||||
prefix: testCase,
|
prefix: testCase,
|
||||||
});
|
});
|
||||||
|
|
||||||
await autumnV1.attach({
|
const res = await autumnV1.attach({
|
||||||
customer_id: customerId,
|
customer_id: customerId,
|
||||||
product_id: pro.id,
|
product_id: pro.id,
|
||||||
|
invoice: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
await autumnV1.attach({
|
console.log(res);
|
||||||
customer_id: customerId,
|
// await autumnV1.attach({
|
||||||
product_id: free.id,
|
// customer_id: customerId,
|
||||||
});
|
// product_id: free.id,
|
||||||
|
// });
|
||||||
// await autumnV1.attach({
|
// await autumnV1.attach({
|
||||||
// customer_id: customerId,
|
// customer_id: customerId,
|
||||||
// product_id: pro.id,
|
// product_id: pro.id,
|
||||||
|
|||||||
@@ -1,19 +1,16 @@
|
|||||||
import { beforeAll, describe, expect, it } from "bun:test";
|
import { beforeAll, describe, expect, it } from "bun:test";
|
||||||
import { ApiVersion, SuccessCode } from "@autumn/shared";
|
import { ApiVersion, SuccessCode } from "@autumn/shared";
|
||||||
import { TestFeature } from "@tests/setup/v2Features.js";
|
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||||
|
import { expectProductAttached } from "@tests/utils/expectUtils/expectProductAttached";
|
||||||
|
import { completeInvoiceCheckout } from "@tests/utils/stripeUtils/completeInvoiceCheckout";
|
||||||
import ctx from "@tests/utils/testInitUtils/createTestContext.js";
|
import ctx from "@tests/utils/testInitUtils/createTestContext.js";
|
||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||||
import { constructFeatureItem } from "@/utils/scriptUtils/constructItem.js";
|
import { constructFeatureItem } from "@/utils/scriptUtils/constructItem.js";
|
||||||
import {
|
import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js";
|
||||||
constructProduct
|
|
||||||
} from "@/utils/scriptUtils/createTestProducts.js";
|
|
||||||
import { initProductsV0 } from "@/utils/scriptUtils/testUtils/initProductsV0.js";
|
|
||||||
import { initCustomerV3 } from "@/utils/scriptUtils/testUtils/initCustomerV3";
|
import { initCustomerV3 } from "@/utils/scriptUtils/testUtils/initCustomerV3";
|
||||||
import { expectProductAttached } from "@tests/utils/expectUtils/expectProductAttached";
|
import { initProductsV0 } from "@/utils/scriptUtils/testUtils/initProductsV0.js";
|
||||||
import { completeInvoiceCheckout } from "@tests/utils/stripeUtils/completeInvoiceCheckout";
|
import { expectSubToBeCorrect } from "../../../merged/mergeUtils/expectSubCorrect";
|
||||||
import { attachAuthenticatePaymentMethod } from "@/external/stripe/stripeCusUtils";
|
|
||||||
import { completeInvoiceConfirmation } from "@tests/utils/stripeUtils/completeInvoiceConfirmation";
|
|
||||||
|
|
||||||
const pro = constructProduct({
|
const pro = constructProduct({
|
||||||
type: "pro",
|
type: "pro",
|
||||||
@@ -46,10 +43,10 @@ const oneOff = constructProduct({
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
const testCase = "temp";
|
const testCase = "new-subscription-action-required1";
|
||||||
|
|
||||||
describe(`${chalk.yellowBright("temp: temporary script for testing")}`, () => {
|
describe(`${chalk.yellowBright("new-subscription-action-required1: new subscription, invoice action required (payment failed)")}`, () => {
|
||||||
const customerId = "temp";
|
const customerId = "new-subscription-action-required1";
|
||||||
|
|
||||||
const autumnV1: AutumnInt = new AutumnInt({ version: ApiVersion.V1_2 });
|
const autumnV1: AutumnInt = new AutumnInt({ version: ApiVersion.V1_2 });
|
||||||
|
|
||||||
@@ -69,10 +66,11 @@ describe(`${chalk.yellowBright("temp: temporary script for testing")}`, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should call attach and get invoice action required", async () => {
|
it("should call attach and get invoice action required", async () => {
|
||||||
let attachRes = await autumnV1.attach({
|
const attachRes = await autumnV1.attach({
|
||||||
customer_id: customerId,
|
customer_id: customerId,
|
||||||
product_id: pro.id,
|
product_id: pro.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(attachRes.code).toBe(SuccessCode.InvoiceActionRequired);
|
expect(attachRes.code).toBe(SuccessCode.InvoiceActionRequired);
|
||||||
expect(attachRes.checkout_url).toBeDefined();
|
expect(attachRes.checkout_url).toBeDefined();
|
||||||
expect(attachRes.checkout_url).toContain("invoice.stripe.com");
|
expect(attachRes.checkout_url).toContain("invoice.stripe.com");
|
||||||
@@ -81,7 +79,7 @@ describe(`${chalk.yellowBright("temp: temporary script for testing")}`, () => {
|
|||||||
await completeInvoiceCheckout({
|
await completeInvoiceCheckout({
|
||||||
url: attachRes.checkout_url,
|
url: attachRes.checkout_url,
|
||||||
});
|
});
|
||||||
})
|
});
|
||||||
|
|
||||||
it("should have attached product after completing invoice action required", async () => {
|
it("should have attached product after completing invoice action required", async () => {
|
||||||
const customer = await autumnV1.customers.get(customerId);
|
const customer = await autumnV1.customers.get(customerId);
|
||||||
@@ -89,57 +87,12 @@ describe(`${chalk.yellowBright("temp: temporary script for testing")}`, () => {
|
|||||||
customer,
|
customer,
|
||||||
product: pro,
|
product: pro,
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
it("should call attach for one-off and get invoice action required", async () => {
|
await expectSubToBeCorrect({
|
||||||
let attachRes = await autumnV1.attach({
|
db: ctx.db,
|
||||||
customer_id: customerId,
|
|
||||||
product_id: oneOff.id,
|
|
||||||
});
|
|
||||||
expect(attachRes.code).toBe(SuccessCode.InvoiceActionRequired);
|
|
||||||
expect(attachRes.checkout_url).toBeDefined();
|
|
||||||
expect(attachRes.checkout_url).toContain("invoice.stripe.com");
|
|
||||||
expect(attachRes.message).toBe("Payment action required");
|
|
||||||
|
|
||||||
await completeInvoiceCheckout({
|
|
||||||
url: attachRes.checkout_url,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should have attached product after completing invoice action required", async () => {
|
|
||||||
const customer = await autumnV1.customers.get(customerId);
|
|
||||||
expectProductAttached({
|
|
||||||
customer,
|
|
||||||
product: oneOff,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should call attach for pro and get invoice action required with 3ds", async () => {
|
|
||||||
await attachAuthenticatePaymentMethod({
|
|
||||||
ctx,
|
|
||||||
customerId,
|
customerId,
|
||||||
});
|
org: ctx.org,
|
||||||
|
env: ctx.env,
|
||||||
let attachRes = await autumnV1.attach({
|
|
||||||
customer_id: customerId,
|
|
||||||
product_id: premium.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(attachRes.code).toBe(SuccessCode.InvoiceActionRequired);
|
|
||||||
expect(attachRes.checkout_url).toBeDefined();
|
|
||||||
expect(attachRes.checkout_url).toContain("invoice.stripe.com");
|
|
||||||
expect(attachRes.message).toBe("Payment action required");
|
|
||||||
|
|
||||||
await completeInvoiceConfirmation({
|
|
||||||
url: attachRes.checkout_url,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should have attached product after completing invoice action required", async () => {
|
|
||||||
const customer = await autumnV1.customers.get(customerId);
|
|
||||||
expectProductAttached({
|
|
||||||
customer,
|
|
||||||
product: premium,
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
Reference in New Issue
Block a user