Skip to content

Commit bba4908

Browse files
committed
chore: update agent skill
1 parent 2a18aed commit bba4908

17 files changed

Lines changed: 84 additions & 57 deletions

File tree

.agents/skills/paykit-architecture/SKILL.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
name: paykit-architecture
3-
description: Use before architectural, API design, provider integration, billing lifecycle, database model, or product-scope decisions in PayKit.
3+
description: not use automatically
44
---
55

66
# PayKit Architecture
@@ -14,3 +14,4 @@ Favor embedded, type-safe billing primitives that keep provider details isolated
1414
PayKit should run inside the user's app, use their database, and expose APIs that
1515
make plans, subscriptions, entitlements, and usage billing feel like normal
1616
application code.
17+

apps/web/src/components/sections/readme-code-content.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export const paykit = createPayKit({
3333
database: env.DATABASE_URL,
3434
products: [free, pro],
3535
on: {
36-
"subscription.activated": ({ customer, plan }) => {
36+
"subscription.activated": async ({ customer, plan }) => {
3737
await sendEmail(customer.email, "Welcome to Pro!")
3838
},
3939
}

e2e/cli/push.test.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,15 +82,13 @@ describe("paykitjs push", () => {
8282
]);
8383

8484
// Verify paid plan (pro) was synced to Stripe.
85-
const providerRows = await ctx.database
85+
const proRows = await ctx.database
8686
.select({ id: product.id, stripeProductId: product.stripeProductId })
8787
.from(product)
8888
.where(eq(product.id, "pro"))
8989
.orderBy(desc(product.version))
9090
.limit(1);
91-
const proProduct = providerRows[0] as
92-
| { id: string; stripeProductId: string | null }
93-
| undefined;
91+
const proProduct = proRows[0] as { id: string; stripeProductId: string | null } | undefined;
9492
expect(proProduct).toBeTruthy();
9593
if (!proProduct?.stripeProductId) {
9694
throw new Error("Missing Stripe product metadata for synced plan");

e2e/test-utils/harness/stripe.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,8 @@ export function createStripeHarness(): ProviderHarness {
9090

9191
const cardPaymentButton = page.locator('[data-testid="card-accordion-item-button"]');
9292
if ((await cardPaymentButton.count()) > 0) {
93-
await cardPaymentButton.evaluate((el) => (el as HTMLElement).click());
93+
await cardPaymentButton.first().waitFor({ state: "visible" });
94+
await cardPaymentButton.first().click();
9495
}
9596

9697
// Stripe's hosted checkout uses custom inputs that require per-key events;
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { describe, expect, it } from "vitest";
2+
3+
import { getWebhookListenCommand } from "../commands/init";
4+
5+
describe("cli/init", () => {
6+
it("uses paykitjs listen when the PayKit CLI is available", () => {
7+
expect(getWebhookListenCommand(3000, true)).toBe(
8+
"paykitjs listen --forward-to localhost:3000/paykit/webhook",
9+
);
10+
});
11+
12+
it("falls back to stripe listen when the PayKit CLI is unavailable", () => {
13+
expect(getWebhookListenCommand(3000, false)).toBe(
14+
"stripe listen --forward-to localhost:3000/paykit/webhook",
15+
);
16+
});
17+
});

packages/paykit/src/cli/commands/init.ts

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
import { exec } from "node:child_process";
2-
import { promisify } from "node:util";
3-
4-
const execAsync = promisify(exec);
5-
62
import fs from "node:fs";
3+
import { createRequire } from "node:module";
74
import path from "node:path";
5+
import { promisify } from "node:util";
86

97
import * as p from "@clack/prompts";
108
import { Command } from "commander";
@@ -32,6 +30,9 @@ import {
3230
} from "../utils/env";
3331
import { capture } from "../utils/telemetry";
3432

33+
const execAsync = promisify(exec);
34+
const require = createRequire(import.meta.url);
35+
3536
function ensureDir(filePath: string): void {
3637
const dir = path.dirname(filePath);
3738
if (!fs.existsSync(dir)) {
@@ -64,6 +65,22 @@ function stripeConfig(): string {
6465
}`;
6566
}
6667

68+
export function detectPaykitCli(): boolean {
69+
try {
70+
require.resolve("paykitjs/package.json");
71+
return true;
72+
} catch {
73+
return false;
74+
}
75+
}
76+
77+
export function getWebhookListenCommand(port: number, hasPaykitCli = detectPaykitCli()): string {
78+
const path = `localhost:${String(port)}/paykit/webhook`;
79+
return hasPaykitCli
80+
? `paykitjs listen --forward-to ${path}`
81+
: `stripe listen --forward-to ${path}`;
82+
}
83+
6784
function generateConfigFile(templateId: string, includeIdentify: boolean): string {
6885
const productImports =
6986
templateId === "saas-starter"
@@ -548,8 +565,7 @@ async function initAction(options: { cwd: string; defaults: boolean }): Promise<
548565
const exec = getExecPrefix(pm);
549566
const c = picocolors.cyan;
550567
const b = picocolors.bold;
551-
const webhookCommand = "stripe listen --forward-to localhost:3000/paykit/webhook";
552-
// TODO: replace with paykitjs listen
568+
const webhookCommand = getWebhookListenCommand(3000);
553569

554570
const isRerun = files.length === 0;
555571
const heading = isRerun

packages/paykit/src/cli/commands/listen.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ function loadDotEnv(cwd: string): void {
9191
dotenv.config({ override: true, path: path.join(cwd, ".env.local"), quiet: true });
9292
}
9393

94-
function getEnvStripeOptions(): { secretKey: string; webhookSecret: string } {
94+
function getEnvStripeOptions(): { secretKey: string; webhookSecret?: string } {
9595
const secretKey = process.env.E2E_STRIPE_SK ?? process.env.STRIPE_SECRET_KEY;
9696
if (!secretKey) {
9797
throw new Error(
@@ -101,11 +101,14 @@ function getEnvStripeOptions(): { secretKey: string; webhookSecret: string } {
101101

102102
return {
103103
secretKey,
104-
webhookSecret:
105-
process.env.E2E_STRIPE_WHSEC ?? process.env.STRIPE_WEBHOOK_SECRET ?? "whsec_placeholder",
104+
webhookSecret: process.env.E2E_STRIPE_WHSEC ?? process.env.STRIPE_WEBHOOK_SECRET,
106105
};
107106
}
108107

108+
function isConfigNotFound(error: unknown): boolean {
109+
return error instanceof Error && error.message.startsWith("No PayKit configuration file found.");
110+
}
111+
109112
function sleep(ms: number): Promise<void> {
110113
return new Promise((resolve) => setTimeout(resolve, ms));
111114
}
@@ -684,7 +687,7 @@ async function loadRelayRuntimeContext(params: {
684687
basePath = config.options.basePath ?? basePath;
685688
stripeOptions = config.options.stripe;
686689
} catch (error) {
687-
if (params.configPath || params.requireConfig) {
690+
if (params.configPath || params.requireConfig || !isConfigNotFound(error)) {
688691
throw error;
689692
}
690693
loadDotEnv(params.cwd);

packages/paykit/src/cli/commands/push.ts

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import picocolors from "picocolors";
66

77
import { assertValidPayKitOptions } from "../../core/validate-options";
88
import {
9-
checkActiveSubscriptionsOnOtherProvider,
109
checkProvider,
1110
checkProviderCustomers,
1211
createPool,
@@ -48,7 +47,7 @@ async function pushAction(options: { config?: string; cwd: string; yes?: boolean
4847

4948
if (!providerResult.account.ok) {
5049
s.stop("");
51-
p.log.error(`Provider\n ${picocolors.red("✖")} ${providerResult.account.message}`);
50+
p.log.error(`Stripe\n ${picocolors.red("✖")} ${providerResult.account.message}`);
5251
p.cancel("Push failed");
5352
process.exit(1);
5453
}
@@ -66,12 +65,8 @@ async function pushAction(options: { config?: string; cwd: string; yes?: boolean
6665

6766
// Preflight checks
6867
s.message("Running preflight checks");
69-
const providerId = "stripe";
70-
const [subscriptionErrors, customerErrors] = await Promise.all([
71-
checkActiveSubscriptionsOnOtherProvider(ctx, providerId),
72-
checkProviderCustomers(ctx, providerResult.customerSample),
73-
]);
74-
const allErrors = [...providerResult.errors, ...subscriptionErrors, ...customerErrors];
68+
const customerErrors = await checkProviderCustomers(ctx, providerResult.customerSample);
69+
const allErrors = [...providerResult.errors, ...customerErrors];
7570

7671
if (allErrors.length > 0) {
7772
s.stop("");

packages/paykit/src/cli/commands/status.ts

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import { Command } from "commander";
55
import picocolors from "picocolors";
66

77
import {
8-
checkActiveSubscriptionsOnOtherProvider,
98
checkDatabase,
109
checkProvider,
1110
checkProviderCustomers,
@@ -79,8 +78,8 @@ async function statusAction(options: {
7978

8079
if (!providerResult.account.ok) {
8180
s.stop("");
82-
p.log.error(`Provider\n ${picocolors.red("✖")} ${providerResult.account.message}`);
83-
p.outro("Fix provider issues before continuing");
81+
p.log.error(`Stripe\n ${picocolors.red("✖")} ${providerResult.account.message}`);
82+
p.outro("Fix Stripe issues before continuing");
8483
await database.end();
8584
process.exit(1);
8685
}
@@ -111,12 +110,8 @@ async function statusAction(options: {
111110
} else {
112111
const { ctx, diffs } = await loadProductDiffs(config, deps);
113112

114-
const providerId = "stripe";
115-
const [subscriptionErrors, customerErrors] = await Promise.all([
116-
checkActiveSubscriptionsOnOtherProvider(ctx, providerId),
117-
checkProviderCustomers(ctx, providerResult.customerSample),
118-
]);
119-
preflightErrors = [...preflightErrors, ...subscriptionErrors, ...customerErrors];
113+
const customerErrors = await checkProviderCustomers(ctx, providerResult.customerSample);
114+
preflightErrors = [...preflightErrors, ...customerErrors];
120115

121116
if (diffs.length === 0) {
122117
productsBlock = `Products\n ${picocolors.dim("No products defined")}`;

packages/paykit/src/cli/utils/shared.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -178,13 +178,6 @@ export async function checkProviderCustomers(
178178
return [];
179179
}
180180

181-
export async function checkActiveSubscriptionsOnOtherProvider(
182-
_ctx: PayKitContext,
183-
_currentProviderId: string,
184-
): Promise<string[]> {
185-
return [];
186-
}
187-
188181
export async function loadProductDiffs(
189182
config: LoadedConfig,
190183
deps: Pick<CliDeps, "createContext" | "dryRunSyncProducts">,

0 commit comments

Comments
 (0)