Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { purchaseUrlVerificationCodeHandler } from "@/app/api/latest/payments/purchases/verification-code-handler";
import { validatePurchaseSession } from "@/lib/payments";
import { getTenancy } from "@/lib/tenancies";
import { getStripeForAccount } from "@/lib/stripe";
import { getPrismaClientForTenancy } from "@/prisma-client";
import { createSmartRouteHandler } from "@/route-handlers/smart-route-handler";
import { SubscriptionStatus } from "@prisma/client";
import { adaptSchema, adminAuthTypeSchema, yupNumber, yupObject, yupString } from "@stackframe/stack-shared/dist/schema-fields";
import { yupNumber, yupObject, yupString } from "@stackframe/stack-shared/dist/schema-fields";
import { addInterval } from "@stackframe/stack-shared/dist/utils/dates";
import { StackAssertionError, StatusError } from "@stackframe/stack-shared/dist/utils/errors";
import { typedToUppercase } from "@stackframe/stack-shared/dist/utils/strings";
Expand All @@ -14,11 +15,6 @@ export const POST = createSmartRouteHandler({
hidden: true,
},
request: yupObject({
auth: yupObject({
type: adminAuthTypeSchema.defined(),
project: adaptSchema.defined(),
tenancy: adaptSchema.defined(),
}).defined(),
body: yupObject({
full_code: yupString().defined(),
price_id: yupString().defined(),
Expand All @@ -29,17 +25,22 @@ export const POST = createSmartRouteHandler({
statusCode: yupNumber().oneOf([200]).defined(),
bodyType: yupString().oneOf(["success"]).defined(),
}),
handler: async ({ auth, body }) => {
handler: async ({ body }) => {
const { full_code, price_id, quantity } = body;
const { data, id: codeId } = await purchaseUrlVerificationCodeHandler.validateCode(full_code);
if (auth.tenancy.id !== data.tenancyId) {
throw new StatusError(400, "Tenancy id does not match value from code data");

const tenancy = await getTenancy(data.tenancyId);
if (!tenancy) {
throw new StackAssertionError("Tenancy not found for test mode purchase session");
}
if (tenancy.config.payments.testMode !== true) {
throw new StatusError(403, "Test mode is not enabled for this project");
}
const prisma = await getPrismaClientForTenancy(auth.tenancy);
const prisma = await getPrismaClientForTenancy(tenancy);

const { selectedPrice, conflictingCatalogSubscriptions } = await validatePurchaseSession({
prisma,
tenancy: auth.tenancy,
tenancy,
codeData: data,
priceId: price_id,
quantity,
Expand All @@ -51,7 +52,7 @@ export const POST = createSmartRouteHandler({
if (!selectedPrice.interval) {
await prisma.oneTimePurchase.create({
data: {
tenancyId: auth.tenancy.id,
tenancyId: tenancy.id,
customerId: data.customerId,
customerType: typedToUppercase(data.product.customerType),
productId: data.productId,
Expand All @@ -66,13 +67,13 @@ export const POST = createSmartRouteHandler({
if (conflictingCatalogSubscriptions.length > 0) {
const conflicting = conflictingCatalogSubscriptions[0];
if (conflicting.stripeSubscriptionId) {
const stripe = await getStripeForAccount({ tenancy: auth.tenancy });
const stripe = await getStripeForAccount({ tenancy });
await stripe.subscriptions.cancel(conflicting.stripeSubscriptionId);
} else if (conflicting.id) {
await prisma.subscription.update({
where: {
tenancyId_id: {
tenancyId: auth.tenancy.id,
tenancyId: tenancy.id,
id: conflicting.id,
},
},
Expand All @@ -83,7 +84,7 @@ export const POST = createSmartRouteHandler({

await prisma.subscription.create({
data: {
tenancyId: auth.tenancy.id,
tenancyId: tenancy.id,
customerId: data.customerId,
customerType: typedToUppercase(data.product.customerType),
status: "active",
Expand All @@ -99,7 +100,7 @@ export const POST = createSmartRouteHandler({
});
}
await purchaseUrlVerificationCodeHandler.revokeCode({
tenancy: auth.tenancy,
tenancy,
id: codeId,
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export const POST = createSmartRouteHandler({
product_id: yupString().defined(),
display_name: yupString().defined(),
}).defined()).defined(),
test_mode: yupBoolean().defined(),
}).defined(),
}),
async handler({ body }) {
Expand Down Expand Up @@ -102,6 +103,7 @@ export const POST = createSmartRouteHandler({
project_id: tenancy.project.id,
already_bought_non_stackable: alreadyBoughtNonStackable,
conflicting_products: conflictingCatalogProducts,
test_mode: tenancy.config.payments.testMode === true,
},
};
},
Expand Down
5 changes: 4 additions & 1 deletion apps/backend/src/lib/payments.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,12 @@ export async function ensureProductIdOrInlineProduct(
}
if (productId) {
const product = getOrUndefined(tenancy.config.payments.products, productId);
if (!product || (product.serverOnly && accessType === "client")) {
if (!product) {
throw new KnownErrors.ProductDoesNotExist(productId, accessType);
}
if (product.serverOnly && accessType === "client") {
throw new StatusError(400, "This product is marked as server-only and cannot be accessed client side!");
}
return product;
} else {
if (!inlineProduct) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ export function ItemDialog({
id="item-id"
value={itemId}
onChange={(e) => {
setItemId(e.target.value);
const nextValue = e.target.value.toLowerCase();
setItemId(nextValue);
if (errors.itemId) {
setErrors(prev => {
const newErrors = { ...prev };
Expand Down
Loading