Merge branch 'staging'
This commit is contained in:
33
.github/workflows/test.yml
vendored
33
.github/workflows/test.yml
vendored
@@ -5,7 +5,6 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- test/cicd
|
||||
env:
|
||||
DATABASE_URL: ${{ secrets.SUPABASE_URL }}
|
||||
UNIT_TEST_AUTUMN_SECRET_KEY: ${{ secrets.AUTUMN_KEY }}
|
||||
@@ -18,6 +17,7 @@ env:
|
||||
STRIPE_WEBHOOK_URL: ${{ secrets.STRIPE_WEBHOOK_URL }}
|
||||
BETTER_AUTH_SECRET: ${{ secrets.BETTER_AUTH_SECRET }}
|
||||
HYPERBROWSER_API_KEY: ${{ secrets.HYPERBROWSER_API_KEY }}
|
||||
STRIPE_TEST_KEY: ${{ secrets.STRIPE_TEST_KEY }}
|
||||
|
||||
jobs:
|
||||
test:
|
||||
@@ -28,13 +28,12 @@ jobs:
|
||||
uses: actions/checkout@v4.2.2
|
||||
|
||||
- name: Set up bun
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
uses: oven-sh/setup-bun@v2
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '22'
|
||||
cache: 'bun'
|
||||
|
||||
- name: Install dependencies
|
||||
# run: bun install --no-frozen-lockfile
|
||||
@@ -102,8 +101,9 @@ jobs:
|
||||
echo "TESTS_ORG_ID=${{ env.TESTS_ORG_ID }}" >> .env
|
||||
echo "LOCALTUNNEL_RESERVED_KEY=${{ env.LOCALTUNNEL_RESERVED_KEY }}" >> .env
|
||||
echo "BETTER_AUTH_SECRET=${{ env.BETTER_AUTH_SECRET }}" >> .env
|
||||
echo "STRIPE_WEBHOOK_URL=${{ env.STRIPE_WEBHOOK_URL }}" >> .env
|
||||
echo "HYPERBROWSER_API_KEY=${{ env.HYPERBROWSER_API_KEY }}" >> .env
|
||||
echo "STRIPE_TEST_KEY=${{ env.STRIPE_TEST_KEY }}" >> .env
|
||||
echo "STRIPE_WEBHOOK_URL=${{ env.STRIPE_WEBHOOK_URL }}" >> .env
|
||||
cat .env
|
||||
|
||||
- name: Build shared
|
||||
@@ -112,7 +112,7 @@ jobs:
|
||||
bun run build
|
||||
|
||||
- name: Startup server
|
||||
run: sudo docker compose -f docker/docker-compose.ci.yml up --detach
|
||||
run: sudo docker compose -f docker-compose.ci.yml up --detach
|
||||
|
||||
- name: Wait for server to be ready
|
||||
run: |
|
||||
@@ -139,20 +139,23 @@ jobs:
|
||||
chmod +x ./shell/g3.sh
|
||||
./shell/g3.sh
|
||||
|
||||
- name: Run G4 tests
|
||||
run: |
|
||||
cd server/
|
||||
chmod +x ./shell/g4.sh
|
||||
./shell/g4.sh
|
||||
# - name: Run G4 tests
|
||||
# run: |
|
||||
# cd server/
|
||||
# chmod +x ./shell/g4.sh
|
||||
# ./shell/g4.sh
|
||||
|
||||
- name: Message
|
||||
run:
|
||||
echo "We're all setup!"
|
||||
# - name: Message
|
||||
# run:
|
||||
# echo "We're all setup!"
|
||||
|
||||
# - name: Check logs
|
||||
# if: always()
|
||||
# run: sudo docker compose -f docker-compose.unix.yml logs server
|
||||
# run: sudo docker compose -f docker-compose.ci.yml logs server
|
||||
- name: Check logs
|
||||
if: always()
|
||||
run: sudo docker compose -f docker-compose.ci.yml logs localtunnel
|
||||
|
||||
- name: Close Docker Containers
|
||||
if: always()
|
||||
run: sudo docker compose -f docker/docker-compose.unix.yml down
|
||||
run: sudo docker compose -f docker-compose.ci.yml down
|
||||
@@ -18,7 +18,7 @@ services:
|
||||
|
||||
shared:
|
||||
build:
|
||||
dockerfile: ./dev.dockerfile
|
||||
dockerfile: docker/dev.dockerfile
|
||||
context: .
|
||||
target: shared
|
||||
volumes:
|
||||
@@ -33,7 +33,7 @@ services:
|
||||
# Vite frontend
|
||||
vite:
|
||||
build:
|
||||
dockerfile: ./dev.dockerfile
|
||||
dockerfile: docker/dev.dockerfile
|
||||
context: .
|
||||
target: vite
|
||||
ports:
|
||||
@@ -52,7 +52,7 @@ services:
|
||||
# Main Express server
|
||||
server:
|
||||
build:
|
||||
dockerfile: ./dev.dockerfile
|
||||
dockerfile: docker/dev.dockerfile
|
||||
context: .
|
||||
target: server
|
||||
ports:
|
||||
@@ -82,7 +82,7 @@ services:
|
||||
# BullMQ Workers
|
||||
workers:
|
||||
build:
|
||||
dockerfile: ./dev.dockerfile
|
||||
dockerfile: docker/dev.dockerfile
|
||||
context: .
|
||||
target: workers
|
||||
volumes:
|
||||
@@ -102,11 +102,13 @@ services:
|
||||
# Run localtunnel
|
||||
localtunnel:
|
||||
build:
|
||||
dockerfile: ./dev.dockerfile
|
||||
dockerfile: docker/dev.dockerfile
|
||||
context: .
|
||||
target: localtunnel
|
||||
volumes:
|
||||
- ./server:/app/server
|
||||
environment:
|
||||
- LOCALTUNNEL_RESERVED_KEY=${LOCALTUNNEL_RESERVED_KEY}
|
||||
depends_on:
|
||||
- server
|
||||
restart: unless-stopped
|
||||
@@ -10,4 +10,6 @@ MOCHA_PARALLEL=true $MOCHA_SETUP \
|
||||
'tests/attach/downgrade/*.ts' \
|
||||
'tests/attach/free/*.ts'
|
||||
|
||||
$MOCHA_CMD 'tests/attach/entities/*.ts'
|
||||
|
||||
# 'tests/attach/basic/basic2.ts' \
|
||||
@@ -12,10 +12,10 @@ $MOCHA_CMD \
|
||||
'tests/attach/migrations/*.ts' \
|
||||
'tests/attach/newVersion/*.ts' \
|
||||
'tests/attach/upgradeOld/*.ts' \
|
||||
'tests/attach/entities/*.ts' \
|
||||
'tests/attach/others/*.ts' \
|
||||
'tests/attach/updateEnts/*.ts' \
|
||||
'tests/advanced/check/*.ts' \
|
||||
'tests/attach/others/*.ts'
|
||||
# 'tests/attach/entities/*.ts' \
|
||||
|
||||
$MOCHA_CMD 'tests/attach/prepaid/*.ts'
|
||||
17
server/src/external/autumn/autumnCli.ts
vendored
17
server/src/external/autumn/autumnCli.ts
vendored
@@ -398,6 +398,23 @@ export class AutumnInt {
|
||||
},
|
||||
};
|
||||
|
||||
stripe = {
|
||||
connect: async (params: {
|
||||
testApiKey: string;
|
||||
liveApiKey: string;
|
||||
successUrl: string;
|
||||
defaultCurrency: string;
|
||||
}) => {
|
||||
const data = await this.post(`/org/stripe`, params);
|
||||
return data;
|
||||
},
|
||||
|
||||
delete: async () => {
|
||||
const data = await this.delete(`/org/stripe`);
|
||||
return data;
|
||||
},
|
||||
};
|
||||
|
||||
track = async (params: TrackParams) => {
|
||||
const data = await this.post(`/track`, params);
|
||||
return data;
|
||||
|
||||
@@ -23,6 +23,8 @@ import expireRouter from "../customers/expire/expireRouter.js";
|
||||
import { handleSetupPayment } from "../customers/attach/handleSetupPayment.js";
|
||||
import { internalFeatureRouter } from "../features/internalFeatureRouter.js";
|
||||
import { analyticsRouter } from "../analytics/analyticsRouter.js";
|
||||
import { handleConnectStripe } from "../orgs/handlers/handleConnectStripe.js";
|
||||
import { handleDeleteStripe } from "../orgs/handlers/handleDeleteStripe.js";
|
||||
|
||||
const apiRouter: Router = Router();
|
||||
|
||||
@@ -59,5 +61,7 @@ apiRouter.post("/setup_payment", handleSetupPayment);
|
||||
|
||||
// Analytics
|
||||
apiRouter.use("/query", analyticsRouter);
|
||||
apiRouter.post("/org/stripe", handleConnectStripe);
|
||||
apiRouter.delete("/org/stripe", handleDeleteStripe);
|
||||
|
||||
export { apiRouter };
|
||||
|
||||
112
server/src/internal/orgs/handlers/handleConnectStripe.ts
Normal file
112
server/src/internal/orgs/handlers/handleConnectStripe.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
import { routeHandler } from "@/utils/routerUtils.js";
|
||||
import express, { Router } from "express";
|
||||
import Stripe from "stripe";
|
||||
|
||||
import RecaseError, { handleRequestError } from "@/utils/errorUtils.js";
|
||||
import { encryptData } from "@/utils/encryptUtils.js";
|
||||
|
||||
import { ErrCode } from "@/errors/errCodes.js";
|
||||
|
||||
import {
|
||||
checkKeyValid,
|
||||
createWebhookEndpoint,
|
||||
} from "@/external/stripe/stripeOnboardingUtils.js";
|
||||
|
||||
import { OrgService } from "../OrgService.js";
|
||||
import { AppEnv } from "@autumn/shared";
|
||||
import { nullish } from "@/utils/genUtils.js";
|
||||
import { clearOrgCache } from "../orgUtils/clearOrgCache.js";
|
||||
|
||||
export const handleConnectStripe = async (req: any, res: any) =>
|
||||
routeHandler({
|
||||
req,
|
||||
res,
|
||||
action: "connect stripe",
|
||||
handler: async (req: any, res: any) => {
|
||||
let { testApiKey, liveApiKey, successUrl, defaultCurrency } = req.body;
|
||||
let { db, orgId, logtail: logger } = req;
|
||||
if (!testApiKey || !liveApiKey || !successUrl) {
|
||||
throw new RecaseError({
|
||||
message: "Missing required fields",
|
||||
code: ErrCode.StripeKeyInvalid,
|
||||
statusCode: 400,
|
||||
});
|
||||
}
|
||||
|
||||
// 1. Check if API keys are valid
|
||||
try {
|
||||
await clearOrgCache({
|
||||
db,
|
||||
orgId,
|
||||
logger,
|
||||
});
|
||||
|
||||
await checkKeyValid(testApiKey);
|
||||
await checkKeyValid(liveApiKey);
|
||||
|
||||
// Get default currency from Stripe
|
||||
let stripe = new Stripe(testApiKey);
|
||||
let account = await stripe.accounts.retrieve();
|
||||
if (nullish(defaultCurrency) && nullish(account.default_currency)) {
|
||||
throw new RecaseError({
|
||||
message: "Default currency not set",
|
||||
code: ErrCode.StripeKeyInvalid,
|
||||
statusCode: 500,
|
||||
});
|
||||
} else if (nullish(defaultCurrency)) {
|
||||
defaultCurrency = account.default_currency;
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error("Error checking stripe keys", error);
|
||||
throw new RecaseError({
|
||||
message: error.message || "Invalid Stripe API keys",
|
||||
code: ErrCode.StripeKeyInvalid,
|
||||
statusCode: 500,
|
||||
data: error,
|
||||
});
|
||||
}
|
||||
// 2. Create webhook endpoint
|
||||
let testWebhook: Stripe.WebhookEndpoint;
|
||||
let liveWebhook: Stripe.WebhookEndpoint;
|
||||
try {
|
||||
testWebhook = await createWebhookEndpoint(
|
||||
testApiKey,
|
||||
AppEnv.Sandbox,
|
||||
req.orgId
|
||||
);
|
||||
liveWebhook = await createWebhookEndpoint(
|
||||
liveApiKey,
|
||||
AppEnv.Live,
|
||||
req.orgId
|
||||
);
|
||||
} catch (error) {
|
||||
throw new RecaseError({
|
||||
message: "Error creating stripe webhook",
|
||||
code: ErrCode.StripeKeyInvalid,
|
||||
statusCode: 500,
|
||||
data: error,
|
||||
});
|
||||
}
|
||||
|
||||
// 1. Update org in Supabase
|
||||
await OrgService.update({
|
||||
db,
|
||||
orgId: req.orgId,
|
||||
updates: {
|
||||
stripe_connected: true,
|
||||
default_currency: defaultCurrency,
|
||||
stripe_config: {
|
||||
test_api_key: encryptData(testApiKey),
|
||||
live_api_key: encryptData(liveApiKey),
|
||||
test_webhook_secret: encryptData(testWebhook.secret as string),
|
||||
live_webhook_secret: encryptData(liveWebhook.secret as string),
|
||||
success_url: successUrl,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
res.status(200).json({
|
||||
message: "Stripe connected",
|
||||
});
|
||||
},
|
||||
});
|
||||
59
server/src/internal/orgs/handlers/handleDeleteStripe.ts
Normal file
59
server/src/internal/orgs/handlers/handleDeleteStripe.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { routeHandler } from "@/utils/routerUtils.js";
|
||||
import { OrgService } from "../OrgService.js";
|
||||
import { createStripeCli } from "@/external/stripe/utils.js";
|
||||
import { clearOrgCache } from "../orgUtils/clearOrgCache.js";
|
||||
import { AppEnv } from "@autumn/shared";
|
||||
|
||||
export const handleDeleteStripe = async (req: any, res: any) =>
|
||||
routeHandler({
|
||||
req,
|
||||
res,
|
||||
action: "delete stripe",
|
||||
handler: async (req: any, res: any) => {
|
||||
const org = await OrgService.getFromReq(req);
|
||||
|
||||
let { db, orgId, logtail: logger } = req;
|
||||
await clearOrgCache({
|
||||
db,
|
||||
orgId,
|
||||
logger,
|
||||
});
|
||||
|
||||
// 2. Delete webhook endpoint
|
||||
try {
|
||||
const testStripeCli = createStripeCli({ org, env: AppEnv.Sandbox });
|
||||
const liveStripeCli = createStripeCli({ org, env: AppEnv.Live });
|
||||
|
||||
const testWebhooks = await testStripeCli.webhookEndpoints.list();
|
||||
for (const webhook of testWebhooks.data) {
|
||||
if (webhook.url.includes(org.id)) {
|
||||
await testStripeCli.webhookEndpoints.del(webhook.id);
|
||||
}
|
||||
}
|
||||
|
||||
const liveWebhooks = await liveStripeCli.webhookEndpoints.list();
|
||||
for (const webhook of liveWebhooks.data) {
|
||||
if (webhook.url.includes(org.id)) {
|
||||
await liveStripeCli.webhookEndpoints.del(webhook.id);
|
||||
}
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error("Error deleting stripe webhook(s)");
|
||||
console.error(error.message);
|
||||
}
|
||||
|
||||
await OrgService.update({
|
||||
db,
|
||||
orgId: req.orgId,
|
||||
updates: {
|
||||
stripe_connected: false,
|
||||
stripe_config: null,
|
||||
default_currency: undefined,
|
||||
},
|
||||
});
|
||||
|
||||
res.status(200).json({
|
||||
message: "Stripe disconnected",
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -29,7 +29,6 @@ describe("Initialize org for tests", () => {
|
||||
this.timeout(1000000000);
|
||||
this.org = await clearOrg({ orgSlug: ORG_SLUG, env: DEFAULT_ENV });
|
||||
this.env = DEFAULT_ENV;
|
||||
// this.sb = createSupabaseClient();
|
||||
let { db, client } = initDrizzle();
|
||||
|
||||
this.db = db;
|
||||
|
||||
@@ -174,6 +174,7 @@ describe(`${chalk.yellowBright(`${testCase}: Testing attach with new version`)}`
|
||||
stripeCli,
|
||||
testClockId,
|
||||
advanceTo: addMonths(curUnix, 1).getTime(),
|
||||
waitForSeconds: 30,
|
||||
});
|
||||
|
||||
await advanceTestClock({
|
||||
@@ -187,7 +188,7 @@ describe(`${chalk.yellowBright(`${testCase}: Testing attach with new version`)}`
|
||||
const invoice = customer.invoices[0];
|
||||
expect(invoice.total).to.equal(
|
||||
invoiceTotal,
|
||||
"invoice total after 1 cycle should be correct",
|
||||
"invoice total after 1 cycle should be correct"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,161 +1,161 @@
|
||||
import { assert } from "chai";
|
||||
import { features, products } from "tests/global.js";
|
||||
import { initCustomer } from "@/utils/scriptUtils/initCustomer.js";
|
||||
import { getPublicAxiosInstance } from "tests/utils/setup.js";
|
||||
import { completeCheckoutForm } from "tests/utils/stripeUtils.js";
|
||||
import { timeout } from "tests/utils/genUtils.js";
|
||||
import { ErrCode } from "@autumn/shared";
|
||||
import { compareMainProduct } from "tests/utils/compare.js";
|
||||
import { AutumnCli } from "tests/cli/AutumnCli.js";
|
||||
import chalk from "chalk";
|
||||
import { setupBefore } from "tests/before.js";
|
||||
// import { assert } from "chai";
|
||||
// import { features, products } from "tests/global.js";
|
||||
// import { initCustomer } from "@/utils/scriptUtils/initCustomer.js";
|
||||
// import { getPublicAxiosInstance } from "tests/utils/setup.js";
|
||||
// import { completeCheckoutForm } from "tests/utils/stripeUtils.js";
|
||||
// import { timeout } from "tests/utils/genUtils.js";
|
||||
// import { ErrCode } from "@autumn/shared";
|
||||
// import { compareMainProduct } from "tests/utils/compare.js";
|
||||
// import { AutumnCli } from "tests/cli/AutumnCli.js";
|
||||
// import chalk from "chalk";
|
||||
// import { setupBefore } from "tests/before.js";
|
||||
|
||||
const testCase = "others4";
|
||||
describe(`${chalk.yellowBright("others4: Testing publishable key")}`, () => {
|
||||
// 1. Initialize customer with card
|
||||
let customerId = testCase;
|
||||
const bearerPublicAxios = getPublicAxiosInstance({
|
||||
withBearer: true,
|
||||
});
|
||||
// const testCase = "others4";
|
||||
// describe(`${chalk.yellowBright("others4: Testing publishable key")}`, () => {
|
||||
// // 1. Initialize customer with card
|
||||
// let customerId = testCase;
|
||||
// const bearerPublicAxios = getPublicAxiosInstance({
|
||||
// withBearer: true,
|
||||
// });
|
||||
|
||||
before(async function () {
|
||||
await setupBefore(this);
|
||||
await initCustomer({
|
||||
customerId,
|
||||
db: this.db,
|
||||
org: this.org,
|
||||
env: this.env,
|
||||
autumn: this.autumnJs,
|
||||
});
|
||||
});
|
||||
// before(async function () {
|
||||
// await setupBefore(this);
|
||||
// await initCustomer({
|
||||
// customerId,
|
||||
// db: this.db,
|
||||
// org: this.org,
|
||||
// env: this.env,
|
||||
// autumn: this.autumnJs,
|
||||
// });
|
||||
// });
|
||||
|
||||
it("should return a 401 if the pkey is invalid", async function () {
|
||||
this.timeout(30000);
|
||||
const axiosInstance = getPublicAxiosInstance({
|
||||
withBearer: true,
|
||||
pkey: "am_pk_test_invalid",
|
||||
});
|
||||
// it("should return a 401 if the pkey is invalid", async function () {
|
||||
// this.timeout(30000);
|
||||
// const axiosInstance = getPublicAxiosInstance({
|
||||
// withBearer: true,
|
||||
// pkey: "am_pk_test_invalid",
|
||||
// });
|
||||
|
||||
try {
|
||||
const { data } = await axiosInstance.post("/v1/attach", {
|
||||
customer_id: customerId,
|
||||
product_id: products.pro.id,
|
||||
});
|
||||
// try {
|
||||
// const { data } = await axiosInstance.post("/v1/attach", {
|
||||
// customer_id: customerId,
|
||||
// product_id: products.pro.id,
|
||||
// });
|
||||
|
||||
throw new Error("Should not be able to attach");
|
||||
} catch (error: any) {
|
||||
assert.equal(error.response.status, 401);
|
||||
}
|
||||
});
|
||||
// throw new Error("Should not be able to attach");
|
||||
// } catch (error: any) {
|
||||
// assert.equal(error.response.status, 401);
|
||||
// }
|
||||
// });
|
||||
|
||||
it("should return checkout URL for both bearer key", async function () {
|
||||
this.timeout(30000);
|
||||
const axiosInstanceBearer = getPublicAxiosInstance({
|
||||
withBearer: true,
|
||||
});
|
||||
// it("should return checkout URL for both bearer key", async function () {
|
||||
// this.timeout(30000);
|
||||
// const axiosInstanceBearer = getPublicAxiosInstance({
|
||||
// withBearer: true,
|
||||
// });
|
||||
|
||||
// 1. Should be able to upgrade to pro
|
||||
const { data } = await axiosInstanceBearer.post("/v1/attach", {
|
||||
customer_id: customerId,
|
||||
product_id: products.pro.id,
|
||||
});
|
||||
// // 1. Should be able to upgrade to pro
|
||||
// const { data } = await axiosInstanceBearer.post("/v1/attach", {
|
||||
// customer_id: customerId,
|
||||
// product_id: products.pro.id,
|
||||
// });
|
||||
|
||||
assert.exists(data.checkout_url);
|
||||
// assert.exists(data.checkout_url);
|
||||
|
||||
await completeCheckoutForm(data.checkout_url);
|
||||
// await completeCheckoutForm(data.checkout_url);
|
||||
|
||||
await timeout(5000);
|
||||
});
|
||||
// await timeout(5000);
|
||||
// });
|
||||
|
||||
it("should have customer with product", async function () {
|
||||
this.timeout(30000);
|
||||
const res = await AutumnCli.getCustomer(customerId);
|
||||
compareMainProduct({
|
||||
sent: products.pro,
|
||||
cusRes: res,
|
||||
});
|
||||
});
|
||||
// it("should have customer with product", async function () {
|
||||
// this.timeout(30000);
|
||||
// const res = await AutumnCli.getCustomer(customerId);
|
||||
// compareMainProduct({
|
||||
// sent: products.pro,
|
||||
// cusRes: res,
|
||||
// });
|
||||
// });
|
||||
|
||||
it("should return error if try to upgrade or downgrade without pkey", async function () {
|
||||
this.timeout(30000);
|
||||
const axiosInstance = getPublicAxiosInstance({
|
||||
withBearer: true,
|
||||
});
|
||||
// it("should return error if try to upgrade or downgrade without pkey", async function () {
|
||||
// this.timeout(30000);
|
||||
// const axiosInstance = getPublicAxiosInstance({
|
||||
// withBearer: true,
|
||||
// });
|
||||
|
||||
try {
|
||||
await axiosInstance.post("/v1/attach", {
|
||||
customer_id: customerId,
|
||||
product_id: products.premium.id,
|
||||
});
|
||||
// try {
|
||||
// await axiosInstance.post("/v1/attach", {
|
||||
// customer_id: customerId,
|
||||
// product_id: products.premium.id,
|
||||
// });
|
||||
|
||||
throw new Error("Should not be able to attach");
|
||||
} catch (error: any) {
|
||||
assert.equal(error.response.status, 400);
|
||||
assert.equal(error.response.data.code, ErrCode.InvalidRequest);
|
||||
}
|
||||
});
|
||||
// throw new Error("Should not be able to attach");
|
||||
// } catch (error: any) {
|
||||
// assert.equal(error.response.status, 400);
|
||||
// assert.equal(error.response.data.code, ErrCode.InvalidRequest);
|
||||
// }
|
||||
// });
|
||||
|
||||
it("should return error if try to downgrade to free", async function () {
|
||||
try {
|
||||
await bearerPublicAxios.post("/v1/attach", {
|
||||
customer_id: customerId,
|
||||
product_id: products.free.id,
|
||||
});
|
||||
// it("should return error if try to downgrade to free", async function () {
|
||||
// try {
|
||||
// await bearerPublicAxios.post("/v1/attach", {
|
||||
// customer_id: customerId,
|
||||
// product_id: products.free.id,
|
||||
// });
|
||||
|
||||
throw new Error("Should not be able to attach");
|
||||
} catch (error: any) {
|
||||
assert.equal(error.response.status, 400);
|
||||
assert.equal(error.response.data.code, ErrCode.InvalidRequest);
|
||||
}
|
||||
});
|
||||
// throw new Error("Should not be able to attach");
|
||||
// } catch (error: any) {
|
||||
// assert.equal(error.response.status, 400);
|
||||
// assert.equal(error.response.data.code, ErrCode.InvalidRequest);
|
||||
// }
|
||||
// });
|
||||
|
||||
// Next, check entitled for pro
|
||||
it("should return correct metered1 amount for pro", async function () {
|
||||
const { data } = await bearerPublicAxios.post("/v1/entitled", {
|
||||
customer_id: customerId,
|
||||
feature_id: features.metered1.id,
|
||||
});
|
||||
// // Next, check entitled for pro
|
||||
// it("should return correct metered1 amount for pro", async function () {
|
||||
// const { data } = await bearerPublicAxios.post("/v1/entitled", {
|
||||
// customer_id: customerId,
|
||||
// feature_id: features.metered1.id,
|
||||
// });
|
||||
|
||||
assert.equal(data.allowed, true);
|
||||
const metered1Balance = data.balances.find(
|
||||
(b: any) => b.feature_id === features.metered1.id,
|
||||
);
|
||||
assert.equal(
|
||||
metered1Balance.balance,
|
||||
products.pro.entitlements.metered1.allowance,
|
||||
);
|
||||
});
|
||||
// assert.equal(data.allowed, true);
|
||||
// const metered1Balance = data.balances.find(
|
||||
// (b: any) => b.feature_id === features.metered1.id,
|
||||
// );
|
||||
// assert.equal(
|
||||
// metered1Balance.balance,
|
||||
// products.pro.entitlements.metered1.allowance,
|
||||
// );
|
||||
// });
|
||||
|
||||
it("should return same balance for entitled with bearer and x-publishable-key", async function () {
|
||||
const { data } = await bearerPublicAxios.post("/v1/entitled", {
|
||||
customer_id: customerId,
|
||||
feature_id: features.metered1.id,
|
||||
});
|
||||
// it("should return same balance for entitled with bearer and x-publishable-key", async function () {
|
||||
// const { data } = await bearerPublicAxios.post("/v1/entitled", {
|
||||
// customer_id: customerId,
|
||||
// feature_id: features.metered1.id,
|
||||
// });
|
||||
|
||||
assert.equal(data.allowed, true);
|
||||
const metered1Balance = data.balances.find(
|
||||
(b: any) => b.feature_id === features.metered1.id,
|
||||
);
|
||||
assert.equal(
|
||||
metered1Balance.balance,
|
||||
products.pro.entitlements.metered1.allowance,
|
||||
);
|
||||
});
|
||||
// assert.equal(data.allowed, true);
|
||||
// const metered1Balance = data.balances.find(
|
||||
// (b: any) => b.feature_id === features.metered1.id,
|
||||
// );
|
||||
// assert.equal(
|
||||
// metered1Balance.balance,
|
||||
// products.pro.entitlements.metered1.allowance,
|
||||
// );
|
||||
// });
|
||||
|
||||
it("should return error when try to send event", async function () {
|
||||
try {
|
||||
await bearerPublicAxios.post("/v1/events", {
|
||||
customer_id: customerId,
|
||||
event_name: features.metered1.id,
|
||||
properties: {
|
||||
value: 10,
|
||||
},
|
||||
});
|
||||
// it("should return error when try to send event", async function () {
|
||||
// try {
|
||||
// await bearerPublicAxios.post("/v1/events", {
|
||||
// customer_id: customerId,
|
||||
// event_name: features.metered1.id,
|
||||
// properties: {
|
||||
// value: 10,
|
||||
// },
|
||||
// });
|
||||
|
||||
throw new Error("Should not be able to send event");
|
||||
} catch (error: any) {
|
||||
assert.equal(error.response.status, 401);
|
||||
assert.equal(error.response.data.code, ErrCode.EndpointNotPublic);
|
||||
}
|
||||
});
|
||||
});
|
||||
// throw new Error("Should not be able to send event");
|
||||
// } catch (error: any) {
|
||||
// assert.equal(error.response.status, 401);
|
||||
// assert.equal(error.response.data.code, ErrCode.EndpointNotPublic);
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
|
||||
@@ -120,7 +120,7 @@ describe(`${chalk.yellowBright(`attach/${testCase}: upgrade quantity, prorate im
|
||||
stripeCli,
|
||||
testClockId,
|
||||
advanceTo: addWeeks(new Date(), 2).getTime(),
|
||||
waitForSeconds: 10,
|
||||
waitForSeconds: 30,
|
||||
});
|
||||
|
||||
await attachAndExpectCorrect({
|
||||
|
||||
@@ -125,7 +125,7 @@ describe(`${chalk.yellowBright(`attach/${testCase}: Testing prepaid reset`)}`, (
|
||||
const customer = await autumn.customers.get(customerId);
|
||||
const newBalance = options[0].quantity - usage;
|
||||
expect(customer.features[TestFeature.Messages].balance).to.equal(
|
||||
newBalance,
|
||||
newBalance
|
||||
);
|
||||
});
|
||||
|
||||
@@ -135,14 +135,14 @@ describe(`${chalk.yellowBright(`attach/${testCase}: Testing prepaid reset`)}`, (
|
||||
testClockId,
|
||||
advanceTo: addHours(
|
||||
addMonths(new Date(), 1),
|
||||
hoursToFinalizeInvoice,
|
||||
hoursToFinalizeInvoice
|
||||
).getTime(),
|
||||
waitForSeconds: 25,
|
||||
waitForSeconds: 30,
|
||||
});
|
||||
|
||||
const customer = await autumn.customers.get(customerId);
|
||||
expect(customer.features[TestFeature.Messages].balance).to.equal(
|
||||
options[0].quantity,
|
||||
options[0].quantity
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -76,6 +76,27 @@ export const clearOrg = async ({
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const autumn = new AutumnInt();
|
||||
|
||||
if (process.env.STRIPE_TEST_KEY) {
|
||||
console.log(`Reconnecting stripe...`);
|
||||
|
||||
try {
|
||||
await autumn.stripe.delete();
|
||||
} catch (error) {}
|
||||
try {
|
||||
await autumn.stripe.connect({
|
||||
testApiKey: process.env.STRIPE_TEST_KEY!,
|
||||
liveApiKey: process.env.STRIPE_TEST_KEY!,
|
||||
successUrl: "https://useautumn.com",
|
||||
defaultCurrency: "usd",
|
||||
});
|
||||
} catch (error: any) {
|
||||
console.error("Error reconnecting stripe", error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
const { db, client } = initDrizzle();
|
||||
const org = await OrgService.getBySlug({ db, slug: orgSlug });
|
||||
|
||||
|
||||
Reference in New Issue
Block a user