@paykit-sdk/polar
Version:
Polar provider for PayKit
202 lines (197 loc) • 7.58 kB
JavaScript
// src/index.ts
import { validateEnvVars } from "@paykit-sdk/core";
// src/polar-provider.ts
import {
toPaykitEvent,
headersExtractor
} from "@paykit-sdk/core";
import { Polar, ServerList } from "@polar-sh/sdk";
import { validateEvent } from "@polar-sh/sdk/webhooks";
// lib/mapper.ts
import {
stringifyObjectValues
} from "@paykit-sdk/core";
var toPaykitCheckout = (checkout) => {
return {
id: checkout.id,
payment_url: checkout.url,
customer_id: checkout.customerId,
session_type: checkout.subscriptionId ? "recurring" : "one_time",
products: checkout.products.map((product) => ({ id: product.id, quantity: 1 })),
metadata: checkout.metadata ?? null,
currency: checkout.currency,
amount: checkout.amount
};
};
var toPaykitCustomer = (customer) => {
return { id: customer.id, email: customer.email, name: customer.name ?? void 0 };
};
var toPaykitSubscriptionStatus = (status) => {
if (status === "active") return "active";
if (status === "past_due" || status === "incomplete") return "past_due";
if (status === "canceled" || status === "unpaid") return "canceled";
if (status === "incomplete_expired") return "expired";
throw new Error(`Unhandled status: ${status}`);
};
var toPaykitSubscription = (subscription) => {
return {
id: subscription.id,
customer_id: subscription.customerId,
status: toPaykitSubscriptionStatus(subscription.status),
current_period_start: new Date(subscription.currentPeriodStart),
current_period_end: new Date(subscription.currentPeriodEnd),
metadata: stringifyObjectValues({ ...subscription.metadata ?? {}, $customFieldData: subscription.customFieldData })
};
};
var toPaykitInvoice = (invoice) => {
return {
id: invoice.id,
amount: invoice.totalAmount,
currency: invoice.currency,
metadata: stringifyObjectValues({ ...invoice.metadata ?? {}, $customFieldData: invoice.customFieldData }),
customer_id: invoice.customerId,
billing_mode: invoice.billingMode
};
};
// src/polar-provider.ts
var PolarProvider = class {
constructor(config) {
this.config = config;
this.productionURL = ServerList["production"];
this.sandboxURL = ServerList["sandbox"];
/**
* Checkout management
*/
this.createCheckout = async (params) => {
const { metadata, item_id, provider_metadata } = params;
const response = await this.polar.checkouts.create({ metadata, products: [item_id], ...provider_metadata });
return toPaykitCheckout(response);
};
this.retrieveCheckout = async (id) => {
const response = await this.polar.checkouts.get({ id });
return toPaykitCheckout(response);
};
/**
* Customer management
*/
this.createCustomer = async (params) => {
const { email, name, metadata } = params;
const response = await this.polar.customers.create({ email, name, ...metadata && { metadata } });
return toPaykitCustomer(response);
};
this.updateCustomer = async (id, params) => {
const { email, name, metadata } = params;
const response = await this.polar.customers.update({
id,
customerUpdate: { ...email && { email }, ...name && { name }, ...metadata && { metadata } }
});
return toPaykitCustomer(response);
};
this.retrieveCustomer = async (id) => {
const response = await this.polar.customers.get({ id });
return toPaykitCustomer(response);
};
/**
* Subscription management
*/
this.cancelSubscription = async (id) => {
await this.polar.subscriptions.revoke({ id });
return null;
};
this.retrieveSubscription = async (id) => {
const response = await this.polar.subscriptions.get({ id });
return toPaykitSubscription(response);
};
this.updateSubscription = async (id, params) => {
const response = await this.polar.subscriptions.update({ id, subscriptionUpdate: { ...params.metadata } });
return toPaykitSubscription(response);
};
/**
* Webhook management
*/
this.handleWebhook = async (params) => {
const { body, headers, webhookSecret } = params;
const webhookHeaders = headersExtractor(headers, ["webhook-id", "webhook-timestamp", "webhook-signature"]).reduce(
(acc, kv) => {
acc[kv.key] = Array.isArray(kv.value) ? kv.value.join(",") : kv.value;
return acc;
},
{}
);
const { data, type } = validateEvent(body, webhookHeaders, webhookSecret);
const id = webhookHeaders["webhook-id"];
const timestamp = webhookHeaders["webhook-timestamp"];
const webhookHandlers = {
/**
* Invoice
*/
"order.paid": (data2) => {
const { status, metadata } = data2;
if (status !== "paid") return null;
return toPaykitEvent({
type: "$invoicePaid",
created: parseInt(timestamp),
id,
data: toPaykitInvoice({ ...data2, metadata: { ...metadata ?? {} }, billingMode: "one_time" })
});
},
"order.created": (data2) => {
const { billingReason, metadata, status } = data2;
if (["subscription_create", "subscription_cycle"].includes(billingReason)) {
return toPaykitEvent({
type: "$invoicePaid",
created: parseInt(timestamp),
id,
data: toPaykitInvoice({ ...data2, metadata: { ...metadata ?? {} }, billingMode: "recurring" })
});
}
return null;
},
/**
* Customer
*/
"customer.created": (data2) => {
return toPaykitEvent({ type: "$customerCreated", created: parseInt(timestamp), id, data: toPaykitCustomer(data2) });
},
"customer.updated": (data2) => {
return toPaykitEvent({ type: "$customerUpdated", created: parseInt(timestamp), id, data: toPaykitCustomer(data2) });
},
"customer.deleted": () => {
return toPaykitEvent({ type: "$customerDeleted", created: parseInt(timestamp), id, data: null });
},
/**
* Subscription
*/
"subscription.updated": (data2) => {
return toPaykitEvent({ type: "$subscriptionUpdated", created: parseInt(timestamp), id, data: toPaykitSubscription(data2) });
},
"subscription.created": (data2) => {
return toPaykitEvent({ type: "$subscriptionCreated", created: parseInt(timestamp), id, data: toPaykitSubscription(data2) });
},
"subscription.revoked": (data2) => {
return toPaykitEvent({ type: "$subscriptionCancelled", created: parseInt(timestamp), id, data: toPaykitSubscription(data2) });
}
};
const handler = webhookHandlers[type];
if (!handler) throw new Error(`Unhandled event type: ${type}`);
const result = handler(data);
if (!result) throw new Error(`Unhandled event type: ${type}`);
return result;
};
const { accessToken, server, ...rest } = config;
this.polar = new Polar({ accessToken, serverURL: server === "sandbox" ? this.sandboxURL : this.productionURL, ...rest });
}
};
// src/index.ts
var createPolar = (config) => {
return new PolarProvider(config);
};
var polar = () => {
const envVars = validateEnvVars(["POLAR_ACCESS_TOKEN"], process.env);
const isDev = process.env.NODE_ENV === "development";
return createPolar({ debug: true, accessToken: envVars.POLAR_ACCESS_TOKEN, server: isDev ? "sandbox" : "production" });
};
export {
createPolar,
polar
};