Skip to content
Draft
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
49 changes: 49 additions & 0 deletions src/contracts/subscription.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { oc } from "@orpc/contract";
import { z } from "zod";
import { SubscriptionSchema } from "../schemas/subscription";

export const CreateRenewalCheckoutInputSchema = z.object({
subscriptionId: z.string(),
});

export const CreateRenewalCheckoutOutputSchema = z.object({
checkoutId: z.string(),
});

export const CancelSubscriptionInputSchema = z.object({
subscriptionId: z.string(),
});

export const CancelSubscriptionOutputSchema = z.object({
ok: z.boolean(),
});

export const GetSubscriptionInputSchema = z.object({
subscriptionId: z.string(),
});

export type CreateRenewalCheckout = z.infer<
typeof CreateRenewalCheckoutInputSchema
>;
export type CancelSubscriptionInput = z.infer<
typeof CancelSubscriptionInputSchema
>;
export type GetSubscriptionInput = z.infer<typeof GetSubscriptionInputSchema>;

export const createRenewalCheckoutContract = oc
.input(CreateRenewalCheckoutInputSchema)
.output(CreateRenewalCheckoutOutputSchema);

export const cancelSubscriptionContract = oc
.input(CancelSubscriptionInputSchema)
.output(CancelSubscriptionOutputSchema);

export const getSubscriptionContract = oc
.input(GetSubscriptionInputSchema)
.output(SubscriptionSchema);

export const subscription = {
createRenewalCheckout: createRenewalCheckoutContract,
cancel: cancelSubscriptionContract,
get: getSubscriptionContract,
};
22 changes: 21 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { checkout } from "./contracts/checkout";
import { onboarding } from "./contracts/onboarding";
import { products } from "./contracts/products";
import { subscription } from "./contracts/subscription";

export type {
ConfirmCheckout,
Expand All @@ -17,6 +18,11 @@ export type {
StartDeviceAuth as StartDeviceAuthInput,
StartDeviceAuthResponse,
} from "./contracts/onboarding";
export type {
CancelSubscriptionInput,
CreateRenewalCheckout,
GetSubscriptionInput,
} from "./contracts/subscription";
export type { Checkout } from "./schemas/checkout";
export { CheckoutSchema } from "./schemas/checkout";
export type { Currency } from "./schemas/currency";
Expand All @@ -27,8 +33,22 @@ export {
ProductPriceSchema,
ListProductsOutputSchema,
} from "./contracts/products";
export type {
RecurringInterval,
Subscription,
SubscriptionStatus,
SubscriptionWebhookEvent,
SubscriptionWebhookPayload,
} from "./schemas/subscription";
export {
RecurringIntervalSchema,
SubscriptionSchema,
SubscriptionStatusSchema,
SubscriptionWebhookEventSchema,
SubscriptionWebhookPayloadSchema,
} from "./schemas/subscription";

export const contract = { checkout, onboarding, products };
export const contract = { checkout, onboarding, products, subscription };

export type { MetadataValidationError } from "./validation/metadata-validation";
export {
Expand Down
51 changes: 51 additions & 0 deletions src/schemas/subscription.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { z } from "zod";
import { CurrencySchema } from "./currency";

export const SubscriptionStatusSchema = z.enum([
"active",
"past_due",
"canceled",
]);

export const RecurringIntervalSchema = z.enum(["MONTH", "QUARTER", "YEAR"]);

export const SubscriptionSchema = z.object({
id: z.string(),
customerId: z.string(),
customerEmail: z.string(),
productId: z.string(),
amount: z.number(),
currency: CurrencySchema,
recurringInterval: RecurringIntervalSchema,
status: SubscriptionStatusSchema,
currentPeriodStart: z.string(), // ISO date
currentPeriodEnd: z.string(), // ISO date
cancelAtPeriodEnd: z.boolean().optional(),
endsAt: z.string().optional(), // ISO date (if scheduled to end)
endedAt: z.string().optional(), // ISO date (if ended)
canceledAt: z.string().optional(), // ISO date (if canceled)
startedAt: z.string(), // ISO date
});

export const SubscriptionWebhookEventSchema = z.enum([
"subscription.created",
"subscription.renewed",
"subscription.canceled",
"subscription.payment_failed",
]);

export const SubscriptionWebhookPayloadSchema = z.object({
handler: z.literal("webhooks"),
event: SubscriptionWebhookEventSchema,
subscription: SubscriptionSchema,
});

export type Subscription = z.infer<typeof SubscriptionSchema>;
export type SubscriptionStatus = z.infer<typeof SubscriptionStatusSchema>;
export type RecurringInterval = z.infer<typeof RecurringIntervalSchema>;
export type SubscriptionWebhookEvent = z.infer<
typeof SubscriptionWebhookEventSchema
>;
export type SubscriptionWebhookPayload = z.infer<
typeof SubscriptionWebhookPayloadSchema
>;