chore: add assign product to org script
This commit is contained in:
221
packages/payments/scripts/assign-product-to-org.ts
Normal file
221
packages/payments/scripts/assign-product-to-org.ts
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
import { db } from '@openpanel/db';
|
||||||
|
import { Polar } from '@polar-sh/sdk';
|
||||||
|
import inquirer from 'inquirer';
|
||||||
|
import inquirerAutocomplete from 'inquirer-autocomplete-prompt';
|
||||||
|
import { getSuccessUrl } from '..';
|
||||||
|
|
||||||
|
// Register the autocomplete prompt
|
||||||
|
inquirer.registerPrompt('autocomplete', inquirerAutocomplete);
|
||||||
|
|
||||||
|
interface Answers {
|
||||||
|
isProduction: boolean;
|
||||||
|
polarApiKey: string;
|
||||||
|
productId: string;
|
||||||
|
organizationId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function promptForInput() {
|
||||||
|
// Get all organizations first
|
||||||
|
const organizations = await db.organization.findMany({
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
name: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Step 1: Collect Polar credentials first
|
||||||
|
const polarCredentials = await inquirer.prompt<{
|
||||||
|
isProduction: boolean;
|
||||||
|
polarApiKey: string;
|
||||||
|
polarOrganizationId: string;
|
||||||
|
}>([
|
||||||
|
{
|
||||||
|
type: 'list',
|
||||||
|
name: 'isProduction',
|
||||||
|
message: 'Is this for production?',
|
||||||
|
choices: [
|
||||||
|
{ name: 'Yes', value: true },
|
||||||
|
{ name: 'No', value: false },
|
||||||
|
],
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'string',
|
||||||
|
name: 'polarApiKey',
|
||||||
|
message: 'Enter your Polar API key:',
|
||||||
|
validate: (input: string) => {
|
||||||
|
if (!input) return 'API key is required';
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Step 2: Initialize Polar client and fetch products
|
||||||
|
const polar = new Polar({
|
||||||
|
accessToken: polarCredentials.polarApiKey,
|
||||||
|
server: polarCredentials.isProduction ? 'production' : 'sandbox',
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('Fetching products from Polar...');
|
||||||
|
const productsResponse = await polar.products.list({
|
||||||
|
limit: 100,
|
||||||
|
isArchived: false,
|
||||||
|
sorting: ['price_amount'],
|
||||||
|
});
|
||||||
|
|
||||||
|
const products = productsResponse.result.items;
|
||||||
|
|
||||||
|
if (products.length === 0) {
|
||||||
|
throw new Error('No products found in Polar');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 3: Continue with product selection and organization selection
|
||||||
|
const restOfAnswers = await inquirer.prompt<{
|
||||||
|
productId: string;
|
||||||
|
organizationId: string;
|
||||||
|
}>([
|
||||||
|
{
|
||||||
|
type: 'autocomplete',
|
||||||
|
name: 'productId',
|
||||||
|
message: 'Select product:',
|
||||||
|
source: (answersSoFar: any, input = '') => {
|
||||||
|
return products
|
||||||
|
.filter(
|
||||||
|
(product) =>
|
||||||
|
product.name.toLowerCase().includes(input.toLowerCase()) ||
|
||||||
|
product.id.toLowerCase().includes(input.toLowerCase()),
|
||||||
|
)
|
||||||
|
.map((product) => {
|
||||||
|
const price = product.prices?.[0];
|
||||||
|
const priceStr =
|
||||||
|
price && 'priceAmount' in price && price.priceAmount
|
||||||
|
? `$${(price.priceAmount / 100).toFixed(2)}/${price.recurringInterval || 'month'}`
|
||||||
|
: 'No price';
|
||||||
|
return {
|
||||||
|
name: `${product.name} (${priceStr})`,
|
||||||
|
value: product.id,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'autocomplete',
|
||||||
|
name: 'organizationId',
|
||||||
|
message: 'Select organization:',
|
||||||
|
source: (answersSoFar: any, input = '') => {
|
||||||
|
return organizations
|
||||||
|
.filter(
|
||||||
|
(org) =>
|
||||||
|
org.name.toLowerCase().includes(input.toLowerCase()) ||
|
||||||
|
org.id.toLowerCase().includes(input.toLowerCase()),
|
||||||
|
)
|
||||||
|
.map((org) => ({
|
||||||
|
name: `${org.name} (${org.id})`,
|
||||||
|
value: org.id,
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...polarCredentials,
|
||||||
|
...restOfAnswers,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
console.log('Assigning existing product to organization...');
|
||||||
|
const input = await promptForInput();
|
||||||
|
|
||||||
|
const polar = new Polar({
|
||||||
|
accessToken: input.polarApiKey,
|
||||||
|
server: input.isProduction ? 'production' : 'sandbox',
|
||||||
|
});
|
||||||
|
|
||||||
|
const organization = await db.organization.findUniqueOrThrow({
|
||||||
|
where: {
|
||||||
|
id: input.organizationId,
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
name: true,
|
||||||
|
createdBy: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
email: true,
|
||||||
|
firstName: true,
|
||||||
|
lastName: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
projects: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!organization.createdBy) {
|
||||||
|
throw new Error(
|
||||||
|
`Organization ${organization.name} does not have a creator. Cannot proceed.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = organization.createdBy;
|
||||||
|
|
||||||
|
// Fetch product details for review
|
||||||
|
const product = await polar.products.get({ id: input.productId });
|
||||||
|
const price = product.prices?.[0];
|
||||||
|
const priceStr =
|
||||||
|
price && 'priceAmount' in price && price.priceAmount
|
||||||
|
? `$${(price.priceAmount / 100).toFixed(2)}/${price.recurringInterval || 'month'}`
|
||||||
|
: 'No price';
|
||||||
|
|
||||||
|
console.log('\nReview the following settings:');
|
||||||
|
console.table({
|
||||||
|
product: product.name,
|
||||||
|
price: priceStr,
|
||||||
|
organization: organization.name,
|
||||||
|
email: user.email,
|
||||||
|
name:
|
||||||
|
[user.firstName, user.lastName].filter(Boolean).join(' ') || 'No name',
|
||||||
|
});
|
||||||
|
|
||||||
|
const { confirmed } = await inquirer.prompt([
|
||||||
|
{
|
||||||
|
type: 'confirm',
|
||||||
|
name: 'confirmed',
|
||||||
|
message: 'Do you want to proceed?',
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!confirmed) {
|
||||||
|
console.log('Operation canceled');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkoutLink = await polar.checkoutLinks.create({
|
||||||
|
paymentProcessor: 'stripe',
|
||||||
|
productId: input.productId,
|
||||||
|
allowDiscountCodes: false,
|
||||||
|
metadata: {
|
||||||
|
organizationId: organization.id,
|
||||||
|
userId: user.id,
|
||||||
|
},
|
||||||
|
successUrl: getSuccessUrl(
|
||||||
|
input.isProduction
|
||||||
|
? 'https://dashboard.openpanel.dev'
|
||||||
|
: 'http://localhost:3000',
|
||||||
|
organization.id,
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('\nCheckout link created:');
|
||||||
|
console.table(checkoutLink);
|
||||||
|
console.log('\nProduct assigned successfully!');
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
||||||
|
.catch(console.error)
|
||||||
|
.finally(() => db.$disconnect());
|
||||||
Reference in New Issue
Block a user