mirror of
https://github.com/LukeHagar/polar.git
synced 2025-12-06 04:20:58 +00:00
add syncProducts function
This commit is contained in:
13
README.md
13
README.md
@@ -472,3 +472,16 @@ polar.registerRoutes(http, {
|
||||
```
|
||||
|
||||
The webhook handler uses the `webhookSecret` from the Polar client configuration or the `POLAR_WEBHOOK_SECRET` environment variable.
|
||||
|
||||
#### syncProducts
|
||||
|
||||
Sync existing products from Polar (must be run inside an action):
|
||||
|
||||
```ts
|
||||
export const syncProducts = action({
|
||||
args: {},
|
||||
handler: async (ctx) => {
|
||||
await polar.syncProducts(ctx);
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
57
example/convex/_generated/api.d.ts
vendored
57
example/convex/_generated/api.d.ts
vendored
@@ -421,6 +421,12 @@ export declare const components: {
|
||||
status: string;
|
||||
}>
|
||||
>;
|
||||
syncProducts: FunctionReference<
|
||||
"action",
|
||||
"internal",
|
||||
{ polarAccessToken: string; server: "sandbox" | "production" },
|
||||
any
|
||||
>;
|
||||
updateProduct: FunctionReference<
|
||||
"mutation",
|
||||
"internal",
|
||||
@@ -471,6 +477,57 @@ export declare const components: {
|
||||
},
|
||||
any
|
||||
>;
|
||||
updateProducts: FunctionReference<
|
||||
"mutation",
|
||||
"internal",
|
||||
{
|
||||
polarAccessToken: string;
|
||||
products: Array<{
|
||||
createdAt: string;
|
||||
description: string | null;
|
||||
id: string;
|
||||
isArchived: boolean;
|
||||
isRecurring: boolean;
|
||||
medias: Array<{
|
||||
checksumEtag: string | null;
|
||||
checksumSha256Base64: string | null;
|
||||
checksumSha256Hex: string | null;
|
||||
createdAt: string;
|
||||
id: string;
|
||||
isUploaded: boolean;
|
||||
lastModifiedAt: string | null;
|
||||
mimeType: string;
|
||||
name: string;
|
||||
organizationId: string;
|
||||
path: string;
|
||||
publicUrl: string;
|
||||
service?: string;
|
||||
size: number;
|
||||
sizeReadable: string;
|
||||
storageVersion: string | null;
|
||||
version: string | null;
|
||||
}>;
|
||||
metadata?: Record<string, any>;
|
||||
modifiedAt: string | null;
|
||||
name: string;
|
||||
organizationId: string;
|
||||
prices: Array<{
|
||||
amountType?: string;
|
||||
createdAt: string;
|
||||
id: string;
|
||||
isArchived: boolean;
|
||||
modifiedAt: string | null;
|
||||
priceAmount?: number;
|
||||
priceCurrency?: string;
|
||||
productId: string;
|
||||
recurringInterval?: "month" | "year" | null;
|
||||
type?: string;
|
||||
}>;
|
||||
recurringInterval?: "month" | "year" | null;
|
||||
}>;
|
||||
},
|
||||
any
|
||||
>;
|
||||
updateSubscription: FunctionReference<
|
||||
"mutation",
|
||||
"internal",
|
||||
|
||||
@@ -85,6 +85,12 @@ export class Polar<
|
||||
getCustomerByUserId(ctx: RunQueryCtx, userId: string) {
|
||||
return ctx.runQuery(this.component.lib.getCustomerByUserId, { userId });
|
||||
}
|
||||
async syncProducts(ctx: RunActionCtx) {
|
||||
await ctx.runAction(this.component.lib.syncProducts, {
|
||||
polarAccessToken: this.organizationToken,
|
||||
server: this.server,
|
||||
});
|
||||
}
|
||||
async createCheckoutSession(
|
||||
ctx: RunMutationCtx,
|
||||
{
|
||||
|
||||
57
src/component/_generated/api.d.ts
vendored
57
src/component/_generated/api.d.ts
vendored
@@ -407,6 +407,12 @@ export type Mounts = {
|
||||
status: string;
|
||||
}>
|
||||
>;
|
||||
syncProducts: FunctionReference<
|
||||
"action",
|
||||
"public",
|
||||
{ polarAccessToken: string; server: "sandbox" | "production" },
|
||||
any
|
||||
>;
|
||||
updateProduct: FunctionReference<
|
||||
"mutation",
|
||||
"public",
|
||||
@@ -457,6 +463,57 @@ export type Mounts = {
|
||||
},
|
||||
any
|
||||
>;
|
||||
updateProducts: FunctionReference<
|
||||
"mutation",
|
||||
"public",
|
||||
{
|
||||
polarAccessToken: string;
|
||||
products: Array<{
|
||||
createdAt: string;
|
||||
description: string | null;
|
||||
id: string;
|
||||
isArchived: boolean;
|
||||
isRecurring: boolean;
|
||||
medias: Array<{
|
||||
checksumEtag: string | null;
|
||||
checksumSha256Base64: string | null;
|
||||
checksumSha256Hex: string | null;
|
||||
createdAt: string;
|
||||
id: string;
|
||||
isUploaded: boolean;
|
||||
lastModifiedAt: string | null;
|
||||
mimeType: string;
|
||||
name: string;
|
||||
organizationId: string;
|
||||
path: string;
|
||||
publicUrl: string;
|
||||
service?: string;
|
||||
size: number;
|
||||
sizeReadable: string;
|
||||
storageVersion: string | null;
|
||||
version: string | null;
|
||||
}>;
|
||||
metadata?: Record<string, any>;
|
||||
modifiedAt: string | null;
|
||||
name: string;
|
||||
organizationId: string;
|
||||
prices: Array<{
|
||||
amountType?: string;
|
||||
createdAt: string;
|
||||
id: string;
|
||||
isArchived: boolean;
|
||||
modifiedAt: string | null;
|
||||
priceAmount?: number;
|
||||
priceCurrency?: string;
|
||||
productId: string;
|
||||
recurringInterval?: "month" | "year" | null;
|
||||
type?: string;
|
||||
}>;
|
||||
recurringInterval?: "month" | "year" | null;
|
||||
}>;
|
||||
},
|
||||
any
|
||||
>;
|
||||
updateSubscription: FunctionReference<
|
||||
"mutation",
|
||||
"public",
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { Polar as PolarSdk } from "@polar-sh/sdk";
|
||||
import { v } from "convex/values";
|
||||
import { mutation, query } from "./_generated/server";
|
||||
import { action, mutation, query } from "./_generated/server";
|
||||
import schema from "./schema";
|
||||
import { asyncMap } from "convex-helpers";
|
||||
import { omitSystemFields } from "./util";
|
||||
import { api } from "./_generated/api";
|
||||
import { convertToDatabaseProduct, omitSystemFields } from "./util";
|
||||
|
||||
export const getCustomerByUserId = query({
|
||||
args: {
|
||||
@@ -269,3 +271,50 @@ export const listCustomerSubscriptions = query({
|
||||
return subscriptions.map(omitSystemFields);
|
||||
},
|
||||
});
|
||||
|
||||
export const syncProducts = action({
|
||||
args: {
|
||||
polarAccessToken: v.string(),
|
||||
server: v.union(v.literal("sandbox"), v.literal("production")),
|
||||
},
|
||||
handler: async (ctx, args) => {
|
||||
const sdk = new PolarSdk({
|
||||
accessToken: args.polarAccessToken,
|
||||
server: args.server,
|
||||
});
|
||||
let page = 1;
|
||||
let maxPage;
|
||||
do {
|
||||
const products = await sdk.products.list({
|
||||
page,
|
||||
limit: 100,
|
||||
});
|
||||
page = page + 1;
|
||||
maxPage = products.result.pagination.maxPage;
|
||||
await ctx.runMutation(api.lib.updateProducts, {
|
||||
polarAccessToken: args.polarAccessToken,
|
||||
products: products.result.items.map(convertToDatabaseProduct),
|
||||
});
|
||||
} while (maxPage >= page);
|
||||
},
|
||||
});
|
||||
|
||||
export const updateProducts = mutation({
|
||||
args: {
|
||||
polarAccessToken: v.string(),
|
||||
products: v.array(schema.tables.products.validator),
|
||||
},
|
||||
handler: async (ctx, args) => {
|
||||
await asyncMap(args.products, async (product) => {
|
||||
const existingProduct = await ctx.db
|
||||
.query("products")
|
||||
.withIndex("id", (q) => q.eq("id", product.id))
|
||||
.unique();
|
||||
if (existingProduct) {
|
||||
await ctx.db.patch(existingProduct._id, product);
|
||||
return;
|
||||
}
|
||||
await ctx.db.insert("products", product);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user