Merge pull request #233 from useautumn/johnyeocx/eng-628-write-body-query-response-zod-schemas
feat: Add OpenAPI schemas for Features, Entities, Core, and Customers (ENG-628)
This commit is contained in:
9
shared/api/common/commonResponses.ts
Normal file
9
shared/api/common/commonResponses.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { z } from "zod/v4";
|
||||
|
||||
export const SuccessResponseSchema = z
|
||||
.object({
|
||||
success: z.boolean(),
|
||||
})
|
||||
.meta({
|
||||
id: "SuccessResponse",
|
||||
});
|
||||
182
shared/api/core/coreOpModels.ts
Normal file
182
shared/api/core/coreOpModels.ts
Normal file
@@ -0,0 +1,182 @@
|
||||
import { z } from "zod/v4";
|
||||
|
||||
// Cancel Schemas
|
||||
export const CancelBodySchema = z
|
||||
.object({
|
||||
customer_id: z.string().meta({
|
||||
description: "The ID of the customer",
|
||||
example: "cus_123",
|
||||
}),
|
||||
product_id: z.string().meta({
|
||||
description: "The ID of the product to cancel",
|
||||
example: "pro_plan",
|
||||
}),
|
||||
entity_id: z.string().nullish().meta({
|
||||
description: "The ID of the entity (optional)",
|
||||
example: "entity_123",
|
||||
}),
|
||||
cancel_immediately: z.boolean().optional().meta({
|
||||
description: "Whether to cancel the product immediately or at period end",
|
||||
example: false,
|
||||
}),
|
||||
prorate: z.boolean().nullish().meta({
|
||||
description:
|
||||
"Whether to prorate the cancellation (defaults to true if not specified)",
|
||||
example: true,
|
||||
}),
|
||||
})
|
||||
.meta({
|
||||
id: "CancelBody",
|
||||
description: "Parameters for canceling a customer's product",
|
||||
});
|
||||
|
||||
export const CancelResultSchema = z
|
||||
.object({
|
||||
success: z.boolean().meta({
|
||||
description: "Whether the cancellation was successful",
|
||||
example: true,
|
||||
}),
|
||||
customer_id: z.string().meta({
|
||||
description: "The ID of the customer",
|
||||
example: "cus_123",
|
||||
}),
|
||||
product_id: z.string().meta({
|
||||
description: "The ID of the canceled product",
|
||||
example: "pro_plan",
|
||||
}),
|
||||
})
|
||||
.meta({
|
||||
id: "CancelResult",
|
||||
description: "Result of a product cancellation",
|
||||
});
|
||||
|
||||
// Track Schemas
|
||||
export const TrackParamsSchema = z
|
||||
.object({
|
||||
customer_id: z.string().nonempty().meta({
|
||||
description: "The ID of the customer",
|
||||
example: "cus_123",
|
||||
}),
|
||||
customer_data: z.any().nullish().meta({
|
||||
description:
|
||||
"Customer data to create or update the customer if they don't exist",
|
||||
example: { name: "John Doe", email: "john@example.com" },
|
||||
}),
|
||||
event_name: z.string().nonempty().optional().meta({
|
||||
description: "The name of the event to track",
|
||||
example: "api_call",
|
||||
}),
|
||||
feature_id: z.string().optional().meta({
|
||||
description:
|
||||
"The ID of the feature (alternative to event_name for usage events)",
|
||||
example: "api_calls",
|
||||
}),
|
||||
properties: z.record(z.string(), z.any()).nullish().meta({
|
||||
description: "Additional properties for the event",
|
||||
example: { endpoint: "/api/users" },
|
||||
}),
|
||||
timestamp: z.number().nullish().meta({
|
||||
description: "Unix timestamp in milliseconds when the event occurred",
|
||||
example: 1717000000000,
|
||||
}),
|
||||
idempotency_key: z.string().nullish().meta({
|
||||
description: "Idempotency key to prevent duplicate events",
|
||||
example: "evt_abc123",
|
||||
}),
|
||||
value: z.number().nullish().meta({
|
||||
description: "The value/count of the event",
|
||||
example: 1,
|
||||
}),
|
||||
set_usage: z.boolean().nullish().meta({
|
||||
description: "Whether to set the usage to this value instead of increment",
|
||||
example: false,
|
||||
}),
|
||||
entity_id: z.string().nullish().meta({
|
||||
description: "The ID of the entity this event is associated with",
|
||||
example: "entity_123",
|
||||
}),
|
||||
entity_data: z.any().nullish().meta({
|
||||
description: "Data for creating the entity if it doesn't exist",
|
||||
example: { name: "Team Alpha" },
|
||||
}),
|
||||
})
|
||||
.meta({
|
||||
id: "TrackParams",
|
||||
description: "Parameters for tracking an event",
|
||||
});
|
||||
|
||||
export const TrackResultSchema = z
|
||||
.object({
|
||||
id: z.string().meta({
|
||||
description: "The ID of the created event",
|
||||
example: "evt_123",
|
||||
}),
|
||||
code: z.string().meta({
|
||||
description: "Response code",
|
||||
example: "event_received",
|
||||
}),
|
||||
customer_id: z.string().meta({
|
||||
description: "The ID of the customer",
|
||||
example: "cus_123",
|
||||
}),
|
||||
entity_id: z.string().optional().meta({
|
||||
description: "The ID of the entity (if provided)",
|
||||
example: "entity_123",
|
||||
}),
|
||||
event_name: z.string().optional().meta({
|
||||
description: "The name of the event",
|
||||
example: "api_call",
|
||||
}),
|
||||
feature_id: z.string().optional().meta({
|
||||
description: "The ID of the feature (if provided)",
|
||||
example: "api_calls",
|
||||
}),
|
||||
})
|
||||
.meta({
|
||||
id: "TrackResult",
|
||||
description: "Result of tracking an event",
|
||||
});
|
||||
|
||||
// Query Schemas
|
||||
export const QueryParamsSchema = z
|
||||
.object({
|
||||
customer_id: z.string().meta({
|
||||
description: "The ID of the customer to query analytics for",
|
||||
example: "cus_123",
|
||||
}),
|
||||
feature_id: z.union([z.string(), z.array(z.string())]).meta({
|
||||
description: "The feature ID(s) to query",
|
||||
example: "api_calls",
|
||||
}),
|
||||
range: z
|
||||
.enum(["24h", "7d", "30d", "90d", "last_cycle"] as const)
|
||||
.nullish()
|
||||
.meta({
|
||||
description:
|
||||
"Time range for the query (defaults to last_cycle if not provided)",
|
||||
example: "7d",
|
||||
}),
|
||||
})
|
||||
.meta({
|
||||
id: "QueryParams",
|
||||
description: "Parameters for querying analytics data",
|
||||
});
|
||||
|
||||
export const QueryResultSchema = z
|
||||
.object({
|
||||
list: z.array(z.any()).meta({
|
||||
description: "List of usage data points",
|
||||
example: [{ period: 1717000000000, count: 100 }],
|
||||
}),
|
||||
})
|
||||
.meta({
|
||||
id: "QueryResult",
|
||||
description: "Result of an analytics query",
|
||||
});
|
||||
|
||||
export type CancelBody = z.infer<typeof CancelBodySchema>;
|
||||
export type CancelResult = z.infer<typeof CancelResultSchema>;
|
||||
export type TrackParams = z.infer<typeof TrackParamsSchema>;
|
||||
export type TrackResult = z.infer<typeof TrackResultSchema>;
|
||||
export type QueryParams = z.infer<typeof QueryParamsSchema>;
|
||||
export type QueryResult = z.infer<typeof QueryResultSchema>;
|
||||
@@ -4,6 +4,14 @@ import {
|
||||
ExtAttachBodySchema,
|
||||
ExtCheckoutParamsSchema,
|
||||
} from "@api/models.js";
|
||||
import {
|
||||
CancelBodySchema,
|
||||
CancelResultSchema,
|
||||
QueryParamsSchema,
|
||||
QueryResultSchema,
|
||||
TrackParamsSchema,
|
||||
TrackResultSchema,
|
||||
} from "./coreOpModels.js";
|
||||
|
||||
export const coreOps = {
|
||||
"/core": {
|
||||
@@ -39,5 +47,56 @@ export const coreOps = {
|
||||
},
|
||||
},
|
||||
},
|
||||
"/cancel": {
|
||||
post: {
|
||||
summary: "Cancel Product",
|
||||
tags: ["core"],
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": { schema: CancelBodySchema },
|
||||
},
|
||||
},
|
||||
responses: {
|
||||
"200": {
|
||||
description: "200 OK",
|
||||
content: { "application/json": { schema: CancelResultSchema } },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"/track": {
|
||||
post: {
|
||||
summary: "Track Event",
|
||||
tags: ["core"],
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": { schema: TrackParamsSchema },
|
||||
},
|
||||
},
|
||||
responses: {
|
||||
"200": {
|
||||
description: "200 OK",
|
||||
content: { "application/json": { schema: TrackResultSchema } },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"/query": {
|
||||
post: {
|
||||
summary: "Query Analytics",
|
||||
tags: ["core"],
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": { schema: QueryParamsSchema },
|
||||
},
|
||||
},
|
||||
responses: {
|
||||
"200": {
|
||||
description: "200 OK",
|
||||
content: { "application/json": { schema: QueryResultSchema } },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
167
shared/api/customers/customerOpModels.ts
Normal file
167
shared/api/customers/customerOpModels.ts
Normal file
@@ -0,0 +1,167 @@
|
||||
import { z } from "zod/v4";
|
||||
|
||||
// Create Customer Params (based on handlePostCustomer logic)
|
||||
export const CreateCustomerParamsSchema = z
|
||||
.object({
|
||||
id: z
|
||||
.string()
|
||||
.refine(
|
||||
(val) => {
|
||||
if (val === "") return false;
|
||||
if (val.includes("@")) return false;
|
||||
if (val.includes(" ")) return false;
|
||||
if (val.includes(".")) return false;
|
||||
return /^[a-zA-Z0-9_-]+$/.test(val);
|
||||
},
|
||||
{
|
||||
message:
|
||||
"ID can only contain letters, numbers, underscores, and hyphens",
|
||||
},
|
||||
)
|
||||
.nullish()
|
||||
.meta({
|
||||
description: "Your unique identifier for the customer",
|
||||
example: "cus_123",
|
||||
}),
|
||||
name: z.string().nullish().meta({
|
||||
description: "Customer's name",
|
||||
example: "John Doe",
|
||||
}),
|
||||
email: z
|
||||
.string()
|
||||
.email({ message: "not a valid email address" })
|
||||
.or(z.literal(""))
|
||||
.nullish()
|
||||
.meta({
|
||||
description: "Customer's email address",
|
||||
example: "john@example.com",
|
||||
}),
|
||||
fingerprint: z.string().nullish().meta({
|
||||
description:
|
||||
"Unique identifier (eg, serial number) to detect duplicate customers and prevent free trial abuse",
|
||||
example: "fp_123abc",
|
||||
}),
|
||||
metadata: z.record(z.any(), z.any()).default({}).nullish().meta({
|
||||
description: "Additional metadata for the customer",
|
||||
example: { company: "Acme Inc" },
|
||||
}),
|
||||
stripe_id: z.string().nullish().meta({
|
||||
description: "Stripe customer ID if you already have one",
|
||||
example: "cus_stripe123",
|
||||
}),
|
||||
entity_id: z.string().nullish().meta({
|
||||
description: "Entity ID to associate with the customer",
|
||||
example: "entity_123",
|
||||
}),
|
||||
entity_data: z.any().nullish().meta({
|
||||
description: "Data for creating an entity",
|
||||
example: { name: "Team Alpha", feature_id: "seats" },
|
||||
}),
|
||||
})
|
||||
.meta({
|
||||
id: "CreateCustomerParams",
|
||||
description: "Parameters for creating a customer",
|
||||
});
|
||||
|
||||
// Update Customer Params (based on handleUpdateCustomer logic)
|
||||
export const UpdateCustomerParamsSchema = z
|
||||
.object({
|
||||
id: z
|
||||
.string()
|
||||
.refine(
|
||||
(val) => {
|
||||
if (val === "") return false;
|
||||
if (val.includes("@")) return false;
|
||||
if (val.includes(" ")) return false;
|
||||
if (val.includes(".")) return false;
|
||||
return /^[a-zA-Z0-9_-]+$/.test(val);
|
||||
},
|
||||
{
|
||||
message:
|
||||
"ID can only contain letters, numbers, underscores, and hyphens",
|
||||
},
|
||||
)
|
||||
.nullish()
|
||||
.meta({
|
||||
description:
|
||||
"New unique identifier for the customer (cannot be changed to null)",
|
||||
example: "cus_123",
|
||||
}),
|
||||
name: z.string().nullish().meta({
|
||||
description: "Customer's name",
|
||||
example: "John Doe",
|
||||
}),
|
||||
email: z
|
||||
.string()
|
||||
.email({ message: "not a valid email address" })
|
||||
.or(z.literal(""))
|
||||
.nullish()
|
||||
.meta({
|
||||
description: "Customer's email address",
|
||||
example: "john@example.com",
|
||||
}),
|
||||
fingerprint: z.string().nullish().meta({
|
||||
description:
|
||||
"Unique identifier (eg, serial number) to detect duplicate customers",
|
||||
example: "fp_123abc",
|
||||
}),
|
||||
metadata: z.record(z.any(), z.any()).nullish().meta({
|
||||
description:
|
||||
"Additional metadata for the customer (set individual keys to null to delete them)",
|
||||
example: { company: "Acme Inc" },
|
||||
}),
|
||||
stripe_id: z.string().nullish().meta({
|
||||
description: "Stripe customer ID",
|
||||
example: "cus_stripe123",
|
||||
}),
|
||||
})
|
||||
.meta({
|
||||
id: "UpdateCustomerParams",
|
||||
description: "Parameters for updating a customer",
|
||||
});
|
||||
|
||||
// List Customers Query (based on the docs)
|
||||
export const ListCustomersQuerySchema = z
|
||||
.object({
|
||||
limit: z.number().int().min(10).max(100).default(10).optional().meta({
|
||||
description: "Maximum number of customers to return",
|
||||
example: 10,
|
||||
}),
|
||||
offset: z.number().int().min(0).default(0).optional().meta({
|
||||
description: "Number of customers to skip before returning results",
|
||||
example: 0,
|
||||
}),
|
||||
})
|
||||
.meta({
|
||||
id: "ListCustomersQuery",
|
||||
description: "Query parameters for listing customers",
|
||||
});
|
||||
|
||||
// List Customers Response
|
||||
export const ListCustomersResponseSchema = z
|
||||
.object({
|
||||
list: z.array(z.any()).meta({
|
||||
description: "List of customers",
|
||||
}),
|
||||
total: z.number().int().meta({
|
||||
description: "Total number of customers available",
|
||||
example: 100,
|
||||
}),
|
||||
limit: z.number().int().meta({
|
||||
description: "Maximum number of customers returned",
|
||||
example: 10,
|
||||
}),
|
||||
offset: z.number().int().meta({
|
||||
description: "Number of customers skipped before returning results",
|
||||
example: 0,
|
||||
}),
|
||||
})
|
||||
.meta({
|
||||
id: "ListCustomersResponse",
|
||||
description: "Response for listing customers",
|
||||
});
|
||||
|
||||
export type CreateCustomerParams = z.infer<typeof CreateCustomerParamsSchema>;
|
||||
export type UpdateCustomerParams = z.infer<typeof UpdateCustomerParamsSchema>;
|
||||
export type ListCustomersQuery = z.infer<typeof ListCustomersQuerySchema>;
|
||||
export type ListCustomersResponse = z.infer<typeof ListCustomersResponseSchema>;
|
||||
113
shared/api/customers/customersOpenApi.ts
Normal file
113
shared/api/customers/customersOpenApi.ts
Normal file
@@ -0,0 +1,113 @@
|
||||
import { z } from "zod/v4";
|
||||
import { APICustomerSchema } from "./apiCustomer.js";
|
||||
import {
|
||||
CreateCustomerParamsSchema,
|
||||
ListCustomersResponseSchema,
|
||||
UpdateCustomerParamsSchema,
|
||||
} from "./customerOpModels.js";
|
||||
import { SuccessResponseSchema } from "../common/commonResponses.js";
|
||||
|
||||
export const customerOps = {
|
||||
"/customers": {
|
||||
get: {
|
||||
summary: "List Customers",
|
||||
tags: ["customers"],
|
||||
requestParams: {
|
||||
query: z.object({
|
||||
limit: z.number().int().min(10).max(100).optional(),
|
||||
offset: z.number().int().min(0).optional(),
|
||||
}),
|
||||
},
|
||||
responses: {
|
||||
"200": {
|
||||
description: "200 OK",
|
||||
content: {
|
||||
"application/json": { schema: ListCustomersResponseSchema },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
post: {
|
||||
summary: "Create Customer",
|
||||
tags: ["customers"],
|
||||
requestParams: {
|
||||
query: z.object({
|
||||
expand: z.string().optional(),
|
||||
}),
|
||||
},
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": { schema: CreateCustomerParamsSchema },
|
||||
},
|
||||
},
|
||||
responses: {
|
||||
"200": {
|
||||
description: "200 OK",
|
||||
content: { "application/json": { schema: APICustomerSchema } },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"/customers/{customer_id}": {
|
||||
get: {
|
||||
summary: "Get Customer",
|
||||
tags: ["customers"],
|
||||
requestParams: {
|
||||
path: z.object({
|
||||
customer_id: z.string(),
|
||||
}),
|
||||
query: z.object({
|
||||
expand: z.string().optional(),
|
||||
}),
|
||||
},
|
||||
responses: {
|
||||
"200": {
|
||||
description: "200 OK",
|
||||
content: { "application/json": { schema: APICustomerSchema } },
|
||||
},
|
||||
},
|
||||
},
|
||||
post: {
|
||||
summary: "Update Customer",
|
||||
tags: ["customers"],
|
||||
requestParams: {
|
||||
path: z.object({
|
||||
customer_id: z.string(),
|
||||
}),
|
||||
query: z.object({
|
||||
expand: z.string().optional(),
|
||||
}),
|
||||
},
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": { schema: UpdateCustomerParamsSchema },
|
||||
},
|
||||
},
|
||||
responses: {
|
||||
"200": {
|
||||
description: "200 OK",
|
||||
content: { "application/json": { schema: APICustomerSchema } },
|
||||
},
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
summary: "Delete Customer",
|
||||
tags: ["customers"],
|
||||
requestParams: {
|
||||
path: z.object({
|
||||
customer_id: z.string(),
|
||||
}),
|
||||
},
|
||||
responses: {
|
||||
"200": {
|
||||
description: "200 OK",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: SuccessResponseSchema,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
5
shared/api/entities/apiEntity.ts
Normal file
5
shared/api/entities/apiEntity.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { EntityResponseSchema } from "@models/cusModels/entityModels/entityResModels.js";
|
||||
import type { z } from "zod/v4";
|
||||
|
||||
export const APIEntitySchema = EntityResponseSchema;
|
||||
export type APIEntity = z.infer<typeof APIEntitySchema>;
|
||||
100
shared/api/entities/entitiesOpenApi.ts
Normal file
100
shared/api/entities/entitiesOpenApi.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
import { z } from "zod/v4";
|
||||
import { APIEntitySchema } from "./apiEntity.js";
|
||||
import { CreateEntityParamsSchema } from "./entityOpModels.js";
|
||||
import { SuccessResponseSchema } from "../common/commonResponses.js";
|
||||
|
||||
const EntityListResponseSchema = z
|
||||
.object({
|
||||
data: z.array(APIEntitySchema),
|
||||
})
|
||||
.meta({
|
||||
id: "EntityListResponse",
|
||||
});
|
||||
|
||||
export const entityOps = {
|
||||
"/customers/{customer_id}/entities": {
|
||||
get: {
|
||||
summary: "List Entities",
|
||||
tags: ["entities"],
|
||||
requestParams: {
|
||||
path: z.object({
|
||||
customer_id: z.string(),
|
||||
}),
|
||||
query: z.object({
|
||||
expand: z.string().optional(),
|
||||
}),
|
||||
},
|
||||
responses: {
|
||||
"200": {
|
||||
description: "200 OK",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: EntityListResponseSchema,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
post: {
|
||||
summary: "Create Entity",
|
||||
tags: ["entities"],
|
||||
requestParams: {
|
||||
path: z.object({
|
||||
customer_id: z.string(),
|
||||
}),
|
||||
},
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": { schema: CreateEntityParamsSchema },
|
||||
},
|
||||
},
|
||||
responses: {
|
||||
"200": {
|
||||
description: "200 OK",
|
||||
content: { "application/json": { schema: APIEntitySchema } },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"/customers/{customer_id}/entities/{entity_id}": {
|
||||
get: {
|
||||
summary: "Get Entity",
|
||||
tags: ["entities"],
|
||||
requestParams: {
|
||||
path: z.object({
|
||||
customer_id: z.string(),
|
||||
entity_id: z.string(),
|
||||
}),
|
||||
query: z.object({
|
||||
expand: z.string().optional(),
|
||||
}),
|
||||
},
|
||||
responses: {
|
||||
"200": {
|
||||
description: "200 OK",
|
||||
content: { "application/json": { schema: APIEntitySchema } },
|
||||
},
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
summary: "Delete Entity",
|
||||
tags: ["entities"],
|
||||
requestParams: {
|
||||
path: z.object({
|
||||
customer_id: z.string(),
|
||||
entity_id: z.string(),
|
||||
}),
|
||||
},
|
||||
responses: {
|
||||
"200": {
|
||||
description: "200 OK",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: SuccessResponseSchema,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
39
shared/api/entities/entityOpModels.ts
Normal file
39
shared/api/entities/entityOpModels.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { z } from "zod/v4";
|
||||
|
||||
// Create Entity Params (based on CreateEntitySchema from shared/models)
|
||||
export const CreateEntityParamsSchema = z
|
||||
.object({
|
||||
id: z.string().meta({
|
||||
description: "The ID of the entity",
|
||||
example: "entity_123",
|
||||
}),
|
||||
name: z.string().nullish().meta({
|
||||
description: "The name of the entity",
|
||||
example: "Team Alpha",
|
||||
}),
|
||||
feature_id: z.string().meta({
|
||||
description: "The ID of the feature this entity is associated with",
|
||||
example: "seats",
|
||||
}),
|
||||
})
|
||||
.meta({
|
||||
id: "CreateEntityParams",
|
||||
description: "Parameters for creating an entity",
|
||||
});
|
||||
|
||||
// Get Entity Query Params
|
||||
export const GetEntityQuerySchema = z
|
||||
.object({
|
||||
expand: z.string().optional().meta({
|
||||
description:
|
||||
"Comma-separated list of fields to expand (e.g., 'invoices')",
|
||||
example: "invoices",
|
||||
}),
|
||||
})
|
||||
.meta({
|
||||
id: "GetEntityQuery",
|
||||
description: "Query parameters for getting an entity",
|
||||
});
|
||||
|
||||
export type CreateEntityParams = z.infer<typeof CreateEntityParamsSchema>;
|
||||
export type GetEntityQuery = z.infer<typeof GetEntityQuerySchema>;
|
||||
@@ -10,7 +10,7 @@ export enum APIFeatureType {
|
||||
export const APIFeatureSchema = z.object({
|
||||
id: z.string(),
|
||||
name: z.string().nullish(),
|
||||
type: z.enum(APIFeatureType),
|
||||
type: z.nativeEnum(APIFeatureType),
|
||||
display: z
|
||||
.object({
|
||||
singular: z.string(),
|
||||
|
||||
97
shared/api/features/featureOpModels.ts
Normal file
97
shared/api/features/featureOpModels.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
import { APIFeatureSchema, APIFeatureType } from "./apiFeature.js";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
// Create Feature Params
|
||||
export const CreateFeatureParamsSchema = z
|
||||
.object({
|
||||
id: z.string().meta({
|
||||
description: "The ID of the feature",
|
||||
example: "feature_123",
|
||||
}),
|
||||
name: z.string().nullish().meta({
|
||||
description: "The name of the feature",
|
||||
example: "API Calls",
|
||||
}),
|
||||
type: z.nativeEnum(APIFeatureType).meta({
|
||||
description: "The type of the feature",
|
||||
example: "single_use",
|
||||
}),
|
||||
display: z
|
||||
.object({
|
||||
singular: z.string(),
|
||||
plural: z.string(),
|
||||
})
|
||||
.nullish()
|
||||
.meta({
|
||||
description: "Display names for the feature",
|
||||
example: { singular: "API Call", plural: "API Calls" },
|
||||
}),
|
||||
credit_schema: z
|
||||
.array(
|
||||
z.object({
|
||||
metered_feature_id: z.string(),
|
||||
credit_cost: z.number(),
|
||||
}),
|
||||
)
|
||||
.nullish()
|
||||
.meta({
|
||||
description:
|
||||
"Credit schema for credit system features (only applicable when type is credit_system)",
|
||||
example: [{ metered_feature_id: "api_calls", credit_cost: 10 }],
|
||||
}),
|
||||
})
|
||||
.meta({
|
||||
id: "CreateFeatureParams",
|
||||
description: "Parameters for creating a feature",
|
||||
});
|
||||
|
||||
// Update Feature Params
|
||||
export const UpdateFeatureParamsSchema = z
|
||||
.object({
|
||||
id: z.string().optional().meta({
|
||||
description: "The ID of the feature",
|
||||
example: "feature_123",
|
||||
}),
|
||||
name: z.string().nullish().meta({
|
||||
description: "The name of the feature",
|
||||
example: "API Calls",
|
||||
}),
|
||||
type: z.nativeEnum(APIFeatureType).optional().meta({
|
||||
description: "The type of the feature",
|
||||
example: "single_use",
|
||||
}),
|
||||
display: z
|
||||
.object({
|
||||
singular: z.string(),
|
||||
plural: z.string(),
|
||||
})
|
||||
.nullish()
|
||||
.meta({
|
||||
description: "Display names for the feature",
|
||||
example: { singular: "API Call", plural: "API Calls" },
|
||||
}),
|
||||
credit_schema: z
|
||||
.array(
|
||||
z.object({
|
||||
metered_feature_id: z.string(),
|
||||
credit_cost: z.number(),
|
||||
}),
|
||||
)
|
||||
.nullish()
|
||||
.meta({
|
||||
description:
|
||||
"Credit schema for credit system features (only applicable when type is credit_system)",
|
||||
example: [{ metered_feature_id: "api_calls", credit_cost: 10 }],
|
||||
}),
|
||||
archived: z.boolean().nullish().meta({
|
||||
description: "Whether the feature is archived",
|
||||
example: false,
|
||||
}),
|
||||
})
|
||||
.meta({
|
||||
id: "UpdateFeatureParams",
|
||||
description: "Parameters for updating a feature",
|
||||
});
|
||||
|
||||
export type CreateFeatureParams = z.infer<typeof CreateFeatureParamsSchema>;
|
||||
export type UpdateFeatureParams = z.infer<typeof UpdateFeatureParamsSchema>;
|
||||
106
shared/api/features/featuresOpenApi.ts
Normal file
106
shared/api/features/featuresOpenApi.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
import { APIFeatureSchema } from "./apiFeature.js";
|
||||
import {
|
||||
CreateFeatureParamsSchema,
|
||||
UpdateFeatureParamsSchema,
|
||||
} from "./featureOpModels.js";
|
||||
import { z } from "zod/v4";
|
||||
import { SuccessResponseSchema } from "../common/commonResponses.js";
|
||||
|
||||
const FeatureListResponseSchema = z
|
||||
.object({
|
||||
list: z.array(APIFeatureSchema),
|
||||
})
|
||||
.meta({
|
||||
id: "FeatureListResponse",
|
||||
});
|
||||
|
||||
export const featureOps = {
|
||||
"/features": {
|
||||
get: {
|
||||
summary: "List Features",
|
||||
tags: ["features"],
|
||||
requestParams: {
|
||||
query: z.object({
|
||||
include_archived: z.boolean().optional(),
|
||||
}),
|
||||
},
|
||||
responses: {
|
||||
"200": {
|
||||
description: "200 OK",
|
||||
content: { "application/json": { schema: FeatureListResponseSchema } },
|
||||
},
|
||||
},
|
||||
},
|
||||
post: {
|
||||
summary: "Create Feature",
|
||||
tags: ["features"],
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": { schema: CreateFeatureParamsSchema },
|
||||
},
|
||||
},
|
||||
responses: {
|
||||
"200": {
|
||||
description: "200 OK",
|
||||
content: { "application/json": { schema: APIFeatureSchema } },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"/features/{featureId}": {
|
||||
get: {
|
||||
summary: "Get Feature",
|
||||
tags: ["features"],
|
||||
requestParams: {
|
||||
path: z.object({
|
||||
featureId: z.string(),
|
||||
}),
|
||||
},
|
||||
responses: {
|
||||
"200": {
|
||||
description: "200 OK",
|
||||
content: { "application/json": { schema: APIFeatureSchema } },
|
||||
},
|
||||
},
|
||||
},
|
||||
post: {
|
||||
summary: "Update Feature",
|
||||
tags: ["features"],
|
||||
requestParams: {
|
||||
path: z.object({
|
||||
featureId: z.string(),
|
||||
}),
|
||||
},
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": { schema: UpdateFeatureParamsSchema },
|
||||
},
|
||||
},
|
||||
responses: {
|
||||
"200": {
|
||||
description: "200 OK",
|
||||
content: { "application/json": { schema: APIFeatureSchema } },
|
||||
},
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
summary: "Delete Feature",
|
||||
tags: ["features"],
|
||||
requestParams: {
|
||||
path: z.object({
|
||||
featureId: z.string(),
|
||||
}),
|
||||
},
|
||||
responses: {
|
||||
"200": {
|
||||
description: "200 OK",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: SuccessResponseSchema,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -1,6 +1,8 @@
|
||||
// Core
|
||||
export * from "./core/attachModels.js";
|
||||
export * from "./core/checkoutModels.js";
|
||||
export * from "./core/coreOpModels.js";
|
||||
export * from "./core/coreOpenApi.js";
|
||||
|
||||
// Customers
|
||||
|
||||
@@ -8,17 +10,29 @@ export * from "./customers/apiCustomer.js";
|
||||
export * from "./customers/components/apiCusFeature.js";
|
||||
export * from "./customers/components/apiCusProduct.js";
|
||||
export * from "./customers/components/apiCusReferral.js";
|
||||
export * from "./customers/customerOpModels.js";
|
||||
export * from "./customers/customersOpenApi.js";
|
||||
|
||||
// Entities
|
||||
export * from "./entities/apiEntity.js";
|
||||
export * from "./entities/entityOpModels.js";
|
||||
export * from "./entities/entitiesOpenApi.js";
|
||||
|
||||
// Features
|
||||
export * from "./features/apiFeature.js";
|
||||
export * from "./features/featureOpModels.js";
|
||||
export * from "./features/featuresOpenApi.js";
|
||||
|
||||
// Others
|
||||
export * from "./others/apiDiscount.js";
|
||||
export * from "./others/apiInvoice.js";
|
||||
|
||||
// Product
|
||||
export * from "./products/apiFreeTrial.js";
|
||||
export * from "./products/apiProduct.js";
|
||||
export * from "./products/apiProductItem.js";
|
||||
export * from "./products/productOpModels.js";
|
||||
export * from "./products/productsOpenApi.js";
|
||||
|
||||
// export * from "./products/apiFreeTrial.js";
|
||||
// export * from "./products/apiProduct.js";
|
||||
|
||||
@@ -4,6 +4,9 @@ import yaml from "yaml";
|
||||
import { z } from "zod/v4";
|
||||
import { createDocument } from "zod-openapi";
|
||||
import { coreOps } from "./core/coreOpenApi.js";
|
||||
import { customerOps } from "./customers/customersOpenApi.js";
|
||||
import { entityOps } from "./entities/entitiesOpenApi.js";
|
||||
import { featureOps } from "./features/featuresOpenApi.js";
|
||||
import { productOps } from "./products/productsOpenApi.js";
|
||||
|
||||
const API_VERSION = "1.2.0";
|
||||
@@ -33,7 +36,7 @@ const document = createDocument({
|
||||
.object({
|
||||
message: z.string(),
|
||||
code: z.string(),
|
||||
env: z.enum(AppEnv),
|
||||
env: z.nativeEnum(AppEnv),
|
||||
})
|
||||
.meta({
|
||||
id: "AutumnError",
|
||||
@@ -52,15 +55,24 @@ const document = createDocument({
|
||||
paths: {
|
||||
...productOps,
|
||||
...coreOps,
|
||||
...featureOps,
|
||||
...customerOps,
|
||||
...entityOps,
|
||||
},
|
||||
});
|
||||
|
||||
// Export to YAML file during build
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
try {
|
||||
const yamlContent = yaml.stringify(document);
|
||||
writeFileSync("./openapi.yaml", yamlContent, "utf8");
|
||||
console.log("OpenAPI document exported to openapi-customer.yaml");
|
||||
// Export as JSON (YAML export has issues with zod schemas)
|
||||
const jsonStr = JSON.stringify(document, null, 2);
|
||||
writeFileSync("./openapi.json", jsonStr, "utf8");
|
||||
console.log("OpenAPI document exported to openapi.json");
|
||||
|
||||
// TODO: Fix YAML export - currently fails with "Tag not resolved for Function value"
|
||||
// const yamlContent = yaml.stringify(document);
|
||||
// writeFileSync("./openapi.yaml", yamlContent, "utf8");
|
||||
// console.log("OpenAPI document exported to openapi.yaml");
|
||||
} catch (error) {
|
||||
console.error("Failed to export OpenAPI document:", error);
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
export * from "./features/updateFeatureParams.js";
|
||||
// export * from "./features/updateFeatureParams.js";
|
||||
|
||||
@@ -41,6 +41,7 @@ export const UpdateProductV2ParamsSchema = CreateProductV2ParamsSchema.extend({
|
||||
is_default: z.boolean().optional(),
|
||||
version: z.number().optional(),
|
||||
group: z.string().optional(),
|
||||
archived: z.boolean().optional(),
|
||||
|
||||
// items: z.array(CreateProductItemParamsSchema).optional(),
|
||||
free_trial: CreateFreeTrialSchema.nullish(),
|
||||
|
||||
19426
shared/openapi.json
Normal file
19426
shared/openapi.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -17,196 +17,23 @@ paths:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
minLength: 1
|
||||
pattern: ^[a-zA-Z0-9_-]+$
|
||||
name:
|
||||
type: string
|
||||
is_add_on:
|
||||
default: false
|
||||
type: boolean
|
||||
is_default:
|
||||
default: false
|
||||
type: boolean
|
||||
version:
|
||||
type: number
|
||||
group:
|
||||
default: ""
|
||||
type: string
|
||||
items:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
feature_id:
|
||||
anyOf:
|
||||
- type: string
|
||||
- type: "null"
|
||||
feature_type:
|
||||
anyOf:
|
||||
- type: string
|
||||
enum:
|
||||
- single_use
|
||||
- continuous_use
|
||||
- boolean
|
||||
- static
|
||||
- type: "null"
|
||||
included_usage:
|
||||
anyOf:
|
||||
- anyOf:
|
||||
- type: number
|
||||
- type: string
|
||||
const: inf
|
||||
- type: "null"
|
||||
interval:
|
||||
anyOf:
|
||||
- type: string
|
||||
enum:
|
||||
- minute
|
||||
- hour
|
||||
- day
|
||||
- week
|
||||
- month
|
||||
- quarter
|
||||
- semi_annual
|
||||
- year
|
||||
- type: "null"
|
||||
interval_count:
|
||||
anyOf:
|
||||
- type: number
|
||||
- type: "null"
|
||||
entity_feature_id:
|
||||
anyOf:
|
||||
- type: string
|
||||
- type: "null"
|
||||
usage_model:
|
||||
anyOf:
|
||||
- type: string
|
||||
enum:
|
||||
- prepaid
|
||||
- pay_per_use
|
||||
- type: "null"
|
||||
price:
|
||||
anyOf:
|
||||
- type: number
|
||||
- type: "null"
|
||||
tiers:
|
||||
anyOf:
|
||||
- type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
to:
|
||||
anyOf:
|
||||
- type: number
|
||||
- type: string
|
||||
const: inf
|
||||
amount:
|
||||
type: number
|
||||
required:
|
||||
- to
|
||||
- amount
|
||||
- type: "null"
|
||||
billing_units:
|
||||
anyOf:
|
||||
- type: number
|
||||
- type: "null"
|
||||
usage_limit:
|
||||
anyOf:
|
||||
- type: number
|
||||
- type: "null"
|
||||
reset_usage_when_enabled:
|
||||
anyOf:
|
||||
- type: boolean
|
||||
- type: "null"
|
||||
config:
|
||||
anyOf:
|
||||
- type: object
|
||||
properties:
|
||||
on_increase:
|
||||
anyOf:
|
||||
- type: string
|
||||
enum:
|
||||
- bill_immediately
|
||||
- prorate_immediately
|
||||
- prorate_next_cycle
|
||||
- bill_next_cycle
|
||||
- type: "null"
|
||||
on_decrease:
|
||||
anyOf:
|
||||
- type: string
|
||||
enum:
|
||||
- prorate
|
||||
- prorate_immediately
|
||||
- prorate_next_cycle
|
||||
- none
|
||||
- no_prorations
|
||||
- type: "null"
|
||||
rollover:
|
||||
anyOf:
|
||||
- type: object
|
||||
properties:
|
||||
max:
|
||||
anyOf:
|
||||
- type: number
|
||||
- type: "null"
|
||||
duration:
|
||||
default: month
|
||||
type: string
|
||||
enum:
|
||||
- month
|
||||
- forever
|
||||
length:
|
||||
type: number
|
||||
required:
|
||||
- max
|
||||
- length
|
||||
- type: "null"
|
||||
- type: "null"
|
||||
created_at:
|
||||
anyOf:
|
||||
- type: number
|
||||
- type: "null"
|
||||
entitlement_id:
|
||||
anyOf:
|
||||
- type: string
|
||||
- type: "null"
|
||||
price_id:
|
||||
anyOf:
|
||||
- type: string
|
||||
- type: "null"
|
||||
price_config:
|
||||
anyOf:
|
||||
- {}
|
||||
- type: "null"
|
||||
free_trial:
|
||||
type: object
|
||||
properties:
|
||||
length:
|
||||
anyOf:
|
||||
- type: string
|
||||
- type: number
|
||||
unique_fingerprint:
|
||||
default: false
|
||||
type: boolean
|
||||
duration:
|
||||
default: day
|
||||
type: string
|
||||
enum:
|
||||
- day
|
||||
- month
|
||||
- year
|
||||
card_required:
|
||||
default: true
|
||||
type: boolean
|
||||
required:
|
||||
- length
|
||||
required:
|
||||
- id
|
||||
- name
|
||||
$ref: "#/components/schemas/CreateProductParams"
|
||||
responses:
|
||||
"200":
|
||||
description: 200 OK
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Product"
|
||||
patch:
|
||||
summary: Update Product
|
||||
tags:
|
||||
- products
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/UpdateProductParams"
|
||||
responses:
|
||||
"200":
|
||||
description: 200 OK
|
||||
@@ -216,6 +43,390 @@ paths:
|
||||
$ref: "#/components/schemas/Product"
|
||||
components:
|
||||
schemas:
|
||||
CreateProductParams:
|
||||
description: Create Product
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
minLength: 1
|
||||
pattern: ^[a-zA-Z0-9_-]+$
|
||||
name:
|
||||
type: string
|
||||
is_add_on:
|
||||
default: false
|
||||
type: boolean
|
||||
is_default:
|
||||
default: false
|
||||
type: boolean
|
||||
version:
|
||||
type: number
|
||||
group:
|
||||
default: ""
|
||||
type: string
|
||||
items:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
feature_id:
|
||||
anyOf:
|
||||
- type: string
|
||||
- type: "null"
|
||||
feature_type:
|
||||
anyOf:
|
||||
- type: string
|
||||
enum:
|
||||
- single_use
|
||||
- continuous_use
|
||||
- boolean
|
||||
- static
|
||||
- type: "null"
|
||||
included_usage:
|
||||
anyOf:
|
||||
- anyOf:
|
||||
- type: number
|
||||
- type: string
|
||||
const: inf
|
||||
- type: "null"
|
||||
interval:
|
||||
anyOf:
|
||||
- type: string
|
||||
enum:
|
||||
- minute
|
||||
- hour
|
||||
- day
|
||||
- week
|
||||
- month
|
||||
- quarter
|
||||
- semi_annual
|
||||
- year
|
||||
- type: "null"
|
||||
interval_count:
|
||||
anyOf:
|
||||
- type: number
|
||||
- type: "null"
|
||||
entity_feature_id:
|
||||
anyOf:
|
||||
- type: string
|
||||
- type: "null"
|
||||
usage_model:
|
||||
anyOf:
|
||||
- type: string
|
||||
enum:
|
||||
- prepaid
|
||||
- pay_per_use
|
||||
- type: "null"
|
||||
price:
|
||||
anyOf:
|
||||
- type: number
|
||||
- type: "null"
|
||||
tiers:
|
||||
anyOf:
|
||||
- type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
to:
|
||||
anyOf:
|
||||
- type: number
|
||||
- type: string
|
||||
const: inf
|
||||
amount:
|
||||
type: number
|
||||
required:
|
||||
- to
|
||||
- amount
|
||||
- type: "null"
|
||||
billing_units:
|
||||
anyOf:
|
||||
- type: number
|
||||
- type: "null"
|
||||
usage_limit:
|
||||
anyOf:
|
||||
- type: number
|
||||
- type: "null"
|
||||
reset_usage_when_enabled:
|
||||
anyOf:
|
||||
- type: boolean
|
||||
- type: "null"
|
||||
config:
|
||||
anyOf:
|
||||
- type: object
|
||||
properties:
|
||||
on_increase:
|
||||
anyOf:
|
||||
- type: string
|
||||
enum:
|
||||
- bill_immediately
|
||||
- prorate_immediately
|
||||
- prorate_next_cycle
|
||||
- bill_next_cycle
|
||||
- type: "null"
|
||||
on_decrease:
|
||||
anyOf:
|
||||
- type: string
|
||||
enum:
|
||||
- prorate
|
||||
- prorate_immediately
|
||||
- prorate_next_cycle
|
||||
- none
|
||||
- no_prorations
|
||||
- type: "null"
|
||||
rollover:
|
||||
anyOf:
|
||||
- type: object
|
||||
properties:
|
||||
max:
|
||||
anyOf:
|
||||
- type: number
|
||||
- type: "null"
|
||||
duration:
|
||||
default: month
|
||||
type: string
|
||||
enum:
|
||||
- month
|
||||
- forever
|
||||
length:
|
||||
type: number
|
||||
required:
|
||||
- max
|
||||
- length
|
||||
- type: "null"
|
||||
- type: "null"
|
||||
created_at:
|
||||
anyOf:
|
||||
- type: number
|
||||
- type: "null"
|
||||
entitlement_id:
|
||||
anyOf:
|
||||
- type: string
|
||||
- type: "null"
|
||||
price_id:
|
||||
anyOf:
|
||||
- type: string
|
||||
- type: "null"
|
||||
price_config:
|
||||
anyOf:
|
||||
- {}
|
||||
- type: "null"
|
||||
free_trial:
|
||||
anyOf:
|
||||
- type: object
|
||||
properties:
|
||||
length:
|
||||
anyOf:
|
||||
- type: string
|
||||
- type: number
|
||||
unique_fingerprint:
|
||||
default: false
|
||||
type: boolean
|
||||
duration:
|
||||
default: day
|
||||
type: string
|
||||
enum:
|
||||
- day
|
||||
- month
|
||||
- year
|
||||
card_required:
|
||||
default: true
|
||||
type: boolean
|
||||
required:
|
||||
- length
|
||||
- type: "null"
|
||||
required:
|
||||
- id
|
||||
- name
|
||||
UpdateProductParams:
|
||||
description: Update Product
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
minLength: 1
|
||||
pattern: ^[a-zA-Z0-9_-]+$
|
||||
name:
|
||||
type: string
|
||||
is_add_on:
|
||||
type: boolean
|
||||
is_default:
|
||||
type: boolean
|
||||
version:
|
||||
type: number
|
||||
group:
|
||||
type: string
|
||||
items:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
feature_id:
|
||||
anyOf:
|
||||
- type: string
|
||||
- type: "null"
|
||||
feature_type:
|
||||
anyOf:
|
||||
- type: string
|
||||
enum:
|
||||
- single_use
|
||||
- continuous_use
|
||||
- boolean
|
||||
- static
|
||||
- type: "null"
|
||||
included_usage:
|
||||
anyOf:
|
||||
- anyOf:
|
||||
- type: number
|
||||
- type: string
|
||||
const: inf
|
||||
- type: "null"
|
||||
interval:
|
||||
anyOf:
|
||||
- type: string
|
||||
enum:
|
||||
- minute
|
||||
- hour
|
||||
- day
|
||||
- week
|
||||
- month
|
||||
- quarter
|
||||
- semi_annual
|
||||
- year
|
||||
- type: "null"
|
||||
interval_count:
|
||||
anyOf:
|
||||
- type: number
|
||||
- type: "null"
|
||||
entity_feature_id:
|
||||
anyOf:
|
||||
- type: string
|
||||
- type: "null"
|
||||
usage_model:
|
||||
anyOf:
|
||||
- type: string
|
||||
enum:
|
||||
- prepaid
|
||||
- pay_per_use
|
||||
- type: "null"
|
||||
price:
|
||||
anyOf:
|
||||
- type: number
|
||||
- type: "null"
|
||||
tiers:
|
||||
anyOf:
|
||||
- type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
to:
|
||||
anyOf:
|
||||
- type: number
|
||||
- type: string
|
||||
const: inf
|
||||
amount:
|
||||
type: number
|
||||
required:
|
||||
- to
|
||||
- amount
|
||||
- type: "null"
|
||||
billing_units:
|
||||
anyOf:
|
||||
- type: number
|
||||
- type: "null"
|
||||
usage_limit:
|
||||
anyOf:
|
||||
- type: number
|
||||
- type: "null"
|
||||
reset_usage_when_enabled:
|
||||
anyOf:
|
||||
- type: boolean
|
||||
- type: "null"
|
||||
config:
|
||||
anyOf:
|
||||
- type: object
|
||||
properties:
|
||||
on_increase:
|
||||
anyOf:
|
||||
- type: string
|
||||
enum:
|
||||
- bill_immediately
|
||||
- prorate_immediately
|
||||
- prorate_next_cycle
|
||||
- bill_next_cycle
|
||||
- type: "null"
|
||||
on_decrease:
|
||||
anyOf:
|
||||
- type: string
|
||||
enum:
|
||||
- prorate
|
||||
- prorate_immediately
|
||||
- prorate_next_cycle
|
||||
- none
|
||||
- no_prorations
|
||||
- type: "null"
|
||||
rollover:
|
||||
anyOf:
|
||||
- type: object
|
||||
properties:
|
||||
max:
|
||||
anyOf:
|
||||
- type: number
|
||||
- type: "null"
|
||||
duration:
|
||||
default: month
|
||||
type: string
|
||||
enum:
|
||||
- month
|
||||
- forever
|
||||
length:
|
||||
type: number
|
||||
required:
|
||||
- max
|
||||
- length
|
||||
- type: "null"
|
||||
- type: "null"
|
||||
created_at:
|
||||
anyOf:
|
||||
- type: number
|
||||
- type: "null"
|
||||
entitlement_id:
|
||||
anyOf:
|
||||
- type: string
|
||||
- type: "null"
|
||||
price_id:
|
||||
anyOf:
|
||||
- type: string
|
||||
- type: "null"
|
||||
price_config:
|
||||
anyOf:
|
||||
- {}
|
||||
- type: "null"
|
||||
free_trial:
|
||||
anyOf:
|
||||
- type: object
|
||||
properties:
|
||||
length:
|
||||
anyOf:
|
||||
- type: string
|
||||
- type: number
|
||||
unique_fingerprint:
|
||||
default: false
|
||||
type: boolean
|
||||
duration:
|
||||
default: day
|
||||
type: string
|
||||
enum:
|
||||
- day
|
||||
- month
|
||||
- year
|
||||
card_required:
|
||||
default: true
|
||||
type: boolean
|
||||
required:
|
||||
- length
|
||||
- type: "null"
|
||||
archived:
|
||||
type: boolean
|
||||
Product:
|
||||
description: A product
|
||||
type: object
|
||||
|
||||
Reference in New Issue
Block a user