@polar-sh/better-auth
Version:
Polar integration for better-auth
754 lines (746 loc) • 24 kB
JavaScript
;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
checkout: () => checkout,
polar: () => polar,
polarClient: () => polarClient,
portal: () => portal,
usage: () => usage,
webhooks: () => webhooks
});
module.exports = __toCommonJS(index_exports);
// src/hooks/customer.ts
var import_api = require("better-auth/api");
var onUserCreate = (options) => async (user, ctx) => {
if (ctx && options.createCustomerOnSignUp) {
try {
const params = options.getCustomerCreateParams ? await options.getCustomerCreateParams({
user
}) : {};
const { result: existingCustomers } = await options.client.customers.list({ email: user.email });
const existingCustomer = existingCustomers.items[0];
if (existingCustomer) {
if (existingCustomer.externalId !== user.id) {
await options.client.customers.update({
id: existingCustomer.id,
customerUpdate: {
externalId: user.id,
...params
}
});
}
} else {
const customer = await options.client.customers.create({
...params,
email: user.email,
name: user.name,
externalId: user.id
});
console.log(customer);
}
} catch (e) {
if (e instanceof Error) {
throw new import_api.APIError("INTERNAL_SERVER_ERROR", {
message: `Polar customer creation failed. Error: ${e.message}`
});
}
throw new import_api.APIError("INTERNAL_SERVER_ERROR", {
message: `Polar customer creation failed. Error: ${e}`
});
}
}
};
var onUserUpdate = (options) => async (user, ctx) => {
if (ctx && options.createCustomerOnSignUp) {
try {
await options.client.customers.updateExternal({
externalId: user.id,
customerUpdateExternalID: {
email: user.email,
name: user.name
}
});
} catch (e) {
if (e instanceof Error) {
ctx.context.logger.error(
`Polar customer update failed. Error: ${e.message}`
);
} else {
ctx.context.logger.error(`Polar customer update failed. Error: ${e}`);
}
}
}
};
// src/client.ts
var polarClient = () => {
return {
id: "polar-client",
$InferServerPlugin: {}
};
};
// src/plugins/portal.ts
var import_api2 = require("better-auth/api");
var import_api3 = require("better-auth/api");
var import_plugins = require("better-auth/plugins");
var import_zod = require("zod");
var portal = () => (polar2) => {
return {
portal: (0, import_plugins.createAuthEndpoint)(
"/customer/portal",
{
method: "GET",
use: [import_api3.sessionMiddleware]
},
async (ctx) => {
if (!ctx.context.session?.user.id) {
throw new import_api2.APIError("BAD_REQUEST", {
message: "User not found"
});
}
try {
const customerSession = await polar2.customerSessions.create({
customerExternalId: ctx.context.session?.user.id
});
return ctx.json({
url: customerSession.customerPortalUrl,
redirect: true
});
} catch (e) {
if (e instanceof Error) {
ctx.context.logger.error(
`Polar customer portal creation failed. Error: ${e.message}`
);
}
throw new import_api2.APIError("INTERNAL_SERVER_ERROR", {
message: "Customer portal creation failed"
});
}
}
),
state: (0, import_plugins.createAuthEndpoint)(
"/customer/state",
{
method: "GET",
use: [import_api3.sessionMiddleware]
},
async (ctx) => {
if (!ctx.context.session.user.id) {
throw new import_api2.APIError("BAD_REQUEST", {
message: "User not found"
});
}
try {
const state = await polar2.customers.getStateExternal({
externalId: ctx.context.session?.user.id
});
return ctx.json(state);
} catch (e) {
if (e instanceof Error) {
ctx.context.logger.error(
`Polar subscriptions list failed. Error: ${e.message}`
);
}
throw new import_api2.APIError("INTERNAL_SERVER_ERROR", {
message: "Subscriptions list failed"
});
}
}
),
benefits: (0, import_plugins.createAuthEndpoint)(
"/customer/benefits/list",
{
method: "GET",
query: import_zod.z.object({
page: import_zod.z.coerce.number().optional(),
limit: import_zod.z.coerce.number().optional()
}).optional(),
use: [import_api3.sessionMiddleware]
},
async (ctx) => {
if (!ctx.context.session.user.id) {
throw new import_api2.APIError("BAD_REQUEST", {
message: "User not found"
});
}
try {
const customerSession = await polar2.customerSessions.create({
customerExternalId: ctx.context.session?.user.id
});
const benefits = await polar2.customerPortal.benefitGrants.list(
{ customerSession: customerSession.token },
{
page: ctx.query?.page,
limit: ctx.query?.limit
}
);
return ctx.json(benefits);
} catch (e) {
if (e instanceof Error) {
ctx.context.logger.error(
`Polar benefits list failed. Error: ${e.message}`
);
}
throw new import_api2.APIError("INTERNAL_SERVER_ERROR", {
message: "Benefits list failed"
});
}
}
),
subscriptions: (0, import_plugins.createAuthEndpoint)(
"/customer/subscriptions/list",
{
method: "GET",
query: import_zod.z.object({
referenceId: import_zod.z.string().optional(),
page: import_zod.z.coerce.number().optional(),
limit: import_zod.z.coerce.number().optional(),
active: import_zod.z.coerce.boolean().optional()
}).optional(),
use: [import_api3.sessionMiddleware]
},
async (ctx) => {
if (!ctx.context.session.user.id) {
throw new import_api2.APIError("BAD_REQUEST", {
message: "User not found"
});
}
if (ctx.query?.referenceId) {
try {
const subscriptions = await polar2.subscriptions.list({
page: ctx.query?.page,
limit: ctx.query?.limit,
active: ctx.query?.active,
metadata: {
referenceId: ctx.query?.referenceId
}
});
return ctx.json(subscriptions);
} catch (e) {
console.log(e);
if (e instanceof Error) {
ctx.context.logger.error(
`Polar subscriptions list with referenceId failed. Error: ${e.message}`
);
}
throw new import_api2.APIError("INTERNAL_SERVER_ERROR", {
message: "Subscriptions list with referenceId failed"
});
}
}
try {
const customerSession = await polar2.customerSessions.create({
customerExternalId: ctx.context.session?.user.id
});
const subscriptions = await polar2.customerPortal.subscriptions.list(
{ customerSession: customerSession.token },
{
page: ctx.query?.page,
limit: ctx.query?.limit,
active: ctx.query?.active
}
);
return ctx.json(subscriptions);
} catch (e) {
if (e instanceof Error) {
ctx.context.logger.error(
`Polar subscriptions list failed. Error: ${e.message}`
);
}
throw new import_api2.APIError("INTERNAL_SERVER_ERROR", {
message: "Polar subscriptions list failed"
});
}
}
),
orders: (0, import_plugins.createAuthEndpoint)(
"/customer/orders/list",
{
method: "GET",
query: import_zod.z.object({
page: import_zod.z.coerce.number().optional(),
limit: import_zod.z.coerce.number().optional(),
productBillingType: import_zod.z.enum(["recurring", "one_time"]).optional()
}).optional(),
use: [import_api3.sessionMiddleware]
},
async (ctx) => {
if (!ctx.context.session.user.id) {
throw new import_api2.APIError("BAD_REQUEST", {
message: "User not found"
});
}
try {
const customerSession = await polar2.customerSessions.create({
customerExternalId: ctx.context.session?.user.id
});
const orders = await polar2.customerPortal.orders.list(
{ customerSession: customerSession.token },
{
page: ctx.query?.page,
limit: ctx.query?.limit,
productBillingType: ctx.query?.productBillingType
}
);
return ctx.json(orders);
} catch (e) {
if (e instanceof Error) {
ctx.context.logger.error(
`Polar orders list failed. Error: ${e.message}`
);
}
throw new import_api2.APIError("INTERNAL_SERVER_ERROR", {
message: "Orders list failed"
});
}
}
)
};
};
// src/plugins/checkout.ts
var import_api4 = require("better-auth/api");
var import_plugins2 = require("better-auth/plugins");
var import_zod2 = require("zod");
var checkout = (checkoutOptions = {}) => (polar2) => {
return {
checkout: (0, import_plugins2.createAuthEndpoint)(
"/checkout",
{
method: "POST",
body: import_zod2.z.object({
products: import_zod2.z.union([import_zod2.z.array(import_zod2.z.string()), import_zod2.z.string()]).optional(),
slug: import_zod2.z.string().optional(),
referenceId: import_zod2.z.string().optional(),
customFieldData: import_zod2.z.record(
import_zod2.z.string(),
import_zod2.z.union([import_zod2.z.string(), import_zod2.z.number(), import_zod2.z.boolean()])
).optional(),
metadata: import_zod2.z.record(
import_zod2.z.string(),
import_zod2.z.union([import_zod2.z.string(), import_zod2.z.number(), import_zod2.z.boolean()])
).optional()
})
},
async (ctx) => {
const session = await (0, import_api4.getSessionFromCtx)(ctx);
let productIds = [];
if (ctx.body.slug) {
const resolvedProducts = await (typeof checkoutOptions.products === "function" ? checkoutOptions.products() : checkoutOptions.products);
const productId = resolvedProducts?.find(
(product) => product.slug === ctx.body.slug
)?.productId;
if (!productId) {
throw new import_api4.APIError("BAD_REQUEST", {
message: "Product not found"
});
}
productIds = [productId];
} else {
productIds = Array.isArray(ctx.body.products) ? ctx.body.products.filter((id) => id !== void 0) : [ctx.body.products].filter((id) => id !== void 0);
}
if (checkoutOptions.authenticatedUsersOnly && !session?.user.id) {
throw new import_api4.APIError("UNAUTHORIZED", {
message: "You must be logged in to checkout"
});
}
try {
const checkout2 = await polar2.checkouts.create({
customerExternalId: session?.user.id,
products: productIds,
successUrl: checkoutOptions.successUrl ? new URL(
checkoutOptions.successUrl,
ctx.request?.url
).toString() : void 0,
metadata: ctx.body.referenceId ? {
referenceId: ctx.body.referenceId,
...ctx.body.metadata
} : ctx.body.metadata,
customFieldData: ctx.body.customFieldData
});
return ctx.json({
url: checkout2.url,
redirect: true
});
} catch (e) {
if (e instanceof Error) {
ctx.context.logger.error(
`Polar checkout creation failed. Error: ${e.message}`
);
}
throw new import_api4.APIError("INTERNAL_SERVER_ERROR", {
message: "Checkout creation failed"
});
}
}
)
};
};
// src/plugins/usage.ts
var import_api5 = require("better-auth/api");
var import_zod3 = require("zod");
var usage = (_usageOptions) => (polar2) => {
return {
meters: (0, import_api5.createAuthEndpoint)(
"/usage/meters/list",
{
method: "GET",
use: [import_api5.sessionMiddleware],
query: import_zod3.z.object({
page: import_zod3.z.coerce.number().optional(),
limit: import_zod3.z.coerce.number().optional()
})
},
async (ctx) => {
if (!ctx.context.session.user.id) {
throw new import_api5.APIError("BAD_REQUEST", {
message: "User not found"
});
}
try {
const customerSession = await polar2.customerSessions.create({
customerExternalId: ctx.context.session.user.id
});
const customerMeters = await polar2.customerPortal.customerMeters.list(
{ customerSession: customerSession.token },
{
page: ctx.query?.page,
limit: ctx.query?.limit
}
);
return ctx.json(customerMeters);
} catch (e) {
if (e instanceof Error) {
ctx.context.logger.error(
`Polar meters list failed. Error: ${e.message}`
);
}
throw new import_api5.APIError("INTERNAL_SERVER_ERROR", {
message: "Meters list failed"
});
}
}
),
ingestion: (0, import_api5.createAuthEndpoint)(
"/usage/ingest",
{
method: "POST",
body: import_zod3.z.object({
event: import_zod3.z.string(),
metadata: import_zod3.z.record(
import_zod3.z.string(),
import_zod3.z.union([import_zod3.z.string(), import_zod3.z.number(), import_zod3.z.boolean()])
)
}),
use: [import_api5.sessionMiddleware]
},
async (ctx) => {
if (!ctx.context.session.user.id) {
throw new import_api5.APIError("BAD_REQUEST", {
message: "User not found"
});
}
try {
const ingestion = await polar2.events.ingest({
events: [
{
name: ctx.body.event,
metadata: ctx.body.metadata,
externalCustomerId: ctx.context.session.user.id
}
]
});
return ctx.json(ingestion);
} catch (e) {
if (e instanceof Error) {
ctx.context.logger.error(
`Polar ingestion failed. Error: ${e.message}`
);
}
throw new import_api5.APIError("INTERNAL_SERVER_ERROR", {
message: "Ingestion failed"
});
}
}
)
};
};
// src/plugins/webhooks.ts
var import_webhooks = require("@polar-sh/sdk/webhooks.js");
var import_api6 = require("better-auth/api");
var webhooks = (options) => (_polar) => {
return {
polarWebhooks: (0, import_api6.createAuthEndpoint)(
"/polar/webhooks",
{
method: "POST",
metadata: {
isAction: false
},
cloneRequest: true
},
async (ctx) => {
const {
secret,
onPayload,
onCheckoutCreated,
onCheckoutUpdated,
onOrderCreated,
onOrderPaid,
onOrderRefunded,
onRefundCreated,
onRefundUpdated,
onSubscriptionCreated,
onSubscriptionUpdated,
onSubscriptionActive,
onSubscriptionCanceled,
onSubscriptionRevoked,
onSubscriptionUncanceled,
onProductCreated,
onProductUpdated,
onOrganizationUpdated,
onBenefitCreated,
onBenefitUpdated,
onBenefitGrantCreated,
onBenefitGrantUpdated,
onBenefitGrantRevoked,
onCustomerCreated,
onCustomerUpdated,
onCustomerDeleted,
onCustomerStateChanged
} = options;
if (!ctx.request?.body) {
throw new import_api6.APIError("INTERNAL_SERVER_ERROR");
}
const buf = await ctx.request.text();
let event;
try {
if (!secret) {
throw new import_api6.APIError("INTERNAL_SERVER_ERROR", {
message: "Polar webhook secret not found"
});
}
const headers = {
"webhook-id": ctx.request.headers.get("webhook-id"),
"webhook-timestamp": ctx.request.headers.get(
"webhook-timestamp"
),
"webhook-signature": ctx.request.headers.get(
"webhook-signature"
)
};
event = (0, import_webhooks.validateEvent)(buf, headers, secret);
} catch (err) {
if (err instanceof Error) {
ctx.context.logger.error(`${err.message}`);
throw new import_api6.APIError("BAD_REQUEST", {
message: `Webhook Error: ${err.message}`
});
}
throw new import_api6.APIError("BAD_REQUEST", {
message: `Webhook Error: ${err}`
});
}
try {
if (onPayload) {
onPayload(event);
}
switch (event.type) {
case "checkout.created":
if (onCheckoutCreated) {
onCheckoutCreated(event);
}
break;
case "checkout.updated":
if (onCheckoutUpdated) {
onCheckoutUpdated(event);
}
break;
case "order.created":
if (onOrderCreated) {
onOrderCreated(event);
}
break;
case "order.paid":
if (onOrderPaid) {
onOrderPaid(event);
}
break;
case "subscription.created":
if (onSubscriptionCreated) {
onSubscriptionCreated(event);
}
break;
case "subscription.updated":
if (onSubscriptionUpdated) {
onSubscriptionUpdated(event);
}
break;
case "subscription.active":
if (onSubscriptionActive) {
onSubscriptionActive(event);
}
break;
case "subscription.canceled":
if (onSubscriptionCanceled) {
onSubscriptionCanceled(event);
}
break;
case "subscription.uncanceled":
if (onSubscriptionUncanceled) {
onSubscriptionUncanceled(event);
}
break;
case "subscription.revoked":
if (onSubscriptionRevoked) {
onSubscriptionRevoked(event);
}
break;
case "product.created":
if (onProductCreated) {
onProductCreated(event);
}
break;
case "product.updated":
if (onProductUpdated) {
onProductUpdated(event);
}
break;
case "organization.updated":
if (onOrganizationUpdated) {
onOrganizationUpdated(event);
}
break;
case "benefit.created":
if (onBenefitCreated) {
onBenefitCreated(event);
}
break;
case "benefit.updated":
if (onBenefitUpdated) {
onBenefitUpdated(event);
}
break;
case "benefit_grant.created":
if (onBenefitGrantCreated) {
onBenefitGrantCreated(event);
}
break;
case "benefit_grant.updated":
if (onBenefitGrantUpdated) {
onBenefitGrantUpdated(event);
}
break;
case "benefit_grant.revoked":
if (onBenefitGrantRevoked) {
onBenefitGrantRevoked(event);
}
break;
case "order.refunded":
if (onOrderRefunded) {
onOrderRefunded(event);
}
break;
case "refund.created":
if (onRefundCreated) {
onRefundCreated(event);
}
break;
case "refund.updated":
if (onRefundUpdated) {
onRefundUpdated(event);
}
break;
case "customer.created":
if (onCustomerCreated) {
onCustomerCreated(event);
}
break;
case "customer.updated":
if (onCustomerUpdated) {
onCustomerUpdated(event);
}
break;
case "customer.deleted":
if (onCustomerDeleted) {
onCustomerDeleted(event);
}
break;
case "customer.state_changed":
if (onCustomerStateChanged) {
onCustomerStateChanged(event);
}
break;
}
} catch (e) {
if (e instanceof Error) {
ctx.context.logger.error(
`Polar webhook failed. Error: ${e.message}`
);
} else {
ctx.context.logger.error(`Polar webhook failed. Error: ${e}`);
}
throw new import_api6.APIError("BAD_REQUEST", {
message: "Webhook error: See server logs for more information."
});
}
return ctx.json({ received: true });
}
)
};
};
// src/index.ts
var polar = (options) => {
const plugins = options.use.map((use) => use(options.client)).reduce((acc, plugin) => {
Object.assign(acc, plugin);
return acc;
}, {});
return {
id: "polar",
endpoints: {
...plugins
},
init() {
return {
options: {
databaseHooks: {
user: {
create: {
after: onUserCreate(options)
},
update: {
after: onUserUpdate(options)
}
}
}
}
};
}
};
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
checkout,
polar,
polarClient,
portal,
usage,
webhooks
});
//# sourceMappingURL=index.cjs.map