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.
|
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;
|
status: string;
|
||||||
}>
|
}>
|
||||||
>;
|
>;
|
||||||
|
syncProducts: FunctionReference<
|
||||||
|
"action",
|
||||||
|
"internal",
|
||||||
|
{ polarAccessToken: string; server: "sandbox" | "production" },
|
||||||
|
any
|
||||||
|
>;
|
||||||
updateProduct: FunctionReference<
|
updateProduct: FunctionReference<
|
||||||
"mutation",
|
"mutation",
|
||||||
"internal",
|
"internal",
|
||||||
@@ -471,6 +477,57 @@ export declare const components: {
|
|||||||
},
|
},
|
||||||
any
|
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<
|
updateSubscription: FunctionReference<
|
||||||
"mutation",
|
"mutation",
|
||||||
"internal",
|
"internal",
|
||||||
|
|||||||
@@ -85,6 +85,12 @@ export class Polar<
|
|||||||
getCustomerByUserId(ctx: RunQueryCtx, userId: string) {
|
getCustomerByUserId(ctx: RunQueryCtx, userId: string) {
|
||||||
return ctx.runQuery(this.component.lib.getCustomerByUserId, { userId });
|
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(
|
async createCheckoutSession(
|
||||||
ctx: RunMutationCtx,
|
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;
|
status: string;
|
||||||
}>
|
}>
|
||||||
>;
|
>;
|
||||||
|
syncProducts: FunctionReference<
|
||||||
|
"action",
|
||||||
|
"public",
|
||||||
|
{ polarAccessToken: string; server: "sandbox" | "production" },
|
||||||
|
any
|
||||||
|
>;
|
||||||
updateProduct: FunctionReference<
|
updateProduct: FunctionReference<
|
||||||
"mutation",
|
"mutation",
|
||||||
"public",
|
"public",
|
||||||
@@ -457,6 +463,57 @@ export type Mounts = {
|
|||||||
},
|
},
|
||||||
any
|
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<
|
updateSubscription: FunctionReference<
|
||||||
"mutation",
|
"mutation",
|
||||||
"public",
|
"public",
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
|
import { Polar as PolarSdk } from "@polar-sh/sdk";
|
||||||
import { v } from "convex/values";
|
import { v } from "convex/values";
|
||||||
import { mutation, query } from "./_generated/server";
|
import { action, mutation, query } from "./_generated/server";
|
||||||
import schema from "./schema";
|
import schema from "./schema";
|
||||||
import { asyncMap } from "convex-helpers";
|
import { asyncMap } from "convex-helpers";
|
||||||
import { omitSystemFields } from "./util";
|
import { api } from "./_generated/api";
|
||||||
|
import { convertToDatabaseProduct, omitSystemFields } from "./util";
|
||||||
|
|
||||||
export const getCustomerByUserId = query({
|
export const getCustomerByUserId = query({
|
||||||
args: {
|
args: {
|
||||||
@@ -269,3 +271,50 @@ export const listCustomerSubscriptions = query({
|
|||||||
return subscriptions.map(omitSystemFields);
|
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