103 lines
3.5 KiB
JavaScript
103 lines
3.5 KiB
JavaScript
// One-time PayPal setup: creates the product + the recurring billing plans and
|
||||
|
|
// prints the plan IDs to paste into your environment.
|
|||
|
|
//
|
|||
|
|
// 1. Set PAYPAL_CLIENT_ID / PAYPAL_SECRET (and PAYPAL_ENVIRONMENT) in .env.local
|
|||
|
|
// 2. node scripts/paypal-setup-plans.mjs
|
|||
|
|
// 3. Copy the printed PAYPAL_*_PLAN_ID lines into .env.local / production env
|
|||
|
|
//
|
|||
|
|
// Amounts mirror the app's pricing (Pro $29/mo, Landlord $59/mo); yearly is
|
|||
|
|
// billed at 10× monthly (~2 months free). Adjust in the PayPal dashboard if you
|
|||
|
|
// want different annual pricing. Safe to re-run (it creates fresh plans).
|
|||
|
|
import { config } from "dotenv"
|
|||
|
|
|
|||
|
|
config({ path: ".env.local", quiet: true })
|
|||
|
|
|
|||
|
|
const ENV = process.env.PAYPAL_ENVIRONMENT === "live" ? "live" : "sandbox"
|
|||
|
|
const BASE = ENV === "live" ? "https://api-m.paypal.com" : "https://api-m.sandbox.paypal.com"
|
|||
|
|
const id = process.env.PAYPAL_CLIENT_ID
|
|||
|
|
const secret = process.env.PAYPAL_SECRET
|
|||
|
|
|
|||
|
|
if (!id || !secret) {
|
|||
|
|
console.error("[paypal-setup] Set PAYPAL_CLIENT_ID and PAYPAL_SECRET in .env.local first.")
|
|||
|
|
process.exit(1)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async function getToken() {
|
|||
|
|
const r = await fetch(`${BASE}/v1/oauth2/token`, {
|
|||
|
|
method: "POST",
|
|||
|
|
headers: {
|
|||
|
|
Authorization: `Basic ${Buffer.from(`${id}:${secret}`).toString("base64")}`,
|
|||
|
|
"Content-Type": "application/x-www-form-urlencoded",
|
|||
|
|
},
|
|||
|
|
body: "grant_type=client_credentials",
|
|||
|
|
})
|
|||
|
|
if (!r.ok) throw new Error(`auth ${r.status}: ${await r.text()}`)
|
|||
|
|
return (await r.json()).access_token
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const token = await getToken()
|
|||
|
|
const post = (path, body) =>
|
|||
|
|
fetch(`${BASE}${path}`, {
|
|||
|
|
method: "POST",
|
|||
|
|
headers: {
|
|||
|
|
Authorization: `Bearer ${token}`,
|
|||
|
|
"Content-Type": "application/json",
|
|||
|
|
Prefer: "return=representation",
|
|||
|
|
},
|
|||
|
|
body: JSON.stringify(body),
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
console.log(`[paypal-setup] Environment: ${ENV}`)
|
|||
|
|
|
|||
|
|
const prodRes = await post("/v1/catalogs/products", {
|
|||
|
|
name: "Property Management Network",
|
|||
|
|
description: "Property Management Network subscription",
|
|||
|
|
type: "SERVICE",
|
|||
|
|
category: "SOFTWARE",
|
|||
|
|
})
|
|||
|
|
if (!prodRes.ok) {
|
|||
|
|
console.error("[paypal-setup] product creation failed:", await prodRes.text())
|
|||
|
|
process.exit(1)
|
|||
|
|
}
|
|||
|
|
const product = await prodRes.json()
|
|||
|
|
console.log(`[paypal-setup] Product: ${product.id}`)
|
|||
|
|
|
|||
|
|
const AMOUNTS = { pro: 29, landlord: 59 }
|
|||
|
|
const envLines = []
|
|||
|
|
|
|||
|
|
for (const plan of ["pro", "landlord"]) {
|
|||
|
|
for (const interval of ["month", "year"]) {
|
|||
|
|
const amount = interval === "month" ? AMOUNTS[plan] : AMOUNTS[plan] * 10
|
|||
|
|
const res = await post("/v1/billing/plans", {
|
|||
|
|
product_id: product.id,
|
|||
|
|
name: `${plan[0].toUpperCase()}${plan.slice(1)} ${interval === "month" ? "Monthly" : "Yearly"}`,
|
|||
|
|
status: "ACTIVE",
|
|||
|
|
billing_cycles: [
|
|||
|
|
{
|
|||
|
|
frequency: { interval_unit: interval === "month" ? "MONTH" : "YEAR", interval_count: 1 },
|
|||
|
|
tenure_type: "REGULAR",
|
|||
|
|
sequence: 1,
|
|||
|
|
total_cycles: 0,
|
|||
|
|
pricing_scheme: { fixed_price: { value: amount.toFixed(2), currency_code: "USD" } },
|
|||
|
|
},
|
|||
|
|
],
|
|||
|
|
payment_preferences: {
|
|||
|
|
auto_bill_outstanding: true,
|
|||
|
|
setup_fee_failure_action: "CONTINUE",
|
|||
|
|
payment_failure_threshold: 2,
|
|||
|
|
},
|
|||
|
|
})
|
|||
|
|
if (!res.ok) {
|
|||
|
|
console.error(`[paypal-setup] plan ${plan}/${interval} failed:`, await res.text())
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
const p = await res.json()
|
|||
|
|
const key = `PAYPAL_${plan.toUpperCase()}_${interval === "month" ? "MONTHLY" : "YEARLY"}_PLAN_ID`
|
|||
|
|
console.log(` ${plan}/${interval} $${amount} → ${p.id}`)
|
|||
|
|
envLines.push(`${key}=${p.id}`)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
console.log("\n[paypal-setup] Add these to your environment:\n")
|
|||
|
|
console.log(envLines.join("\n"))
|