@aixyz/stripe 0.0.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.ts +140 -0
- package/package.json +34 -3
package/index.ts
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import express, { type Request, type Response, type NextFunction } from "express";
|
|
2
|
+
import Stripe from "stripe";
|
|
3
|
+
import type { AixyzServer } from "aixyz/server";
|
|
4
|
+
|
|
5
|
+
let stripe: Stripe | null = null;
|
|
6
|
+
|
|
7
|
+
function initializeStripe(secretKey: string): void {
|
|
8
|
+
if (stripe) return;
|
|
9
|
+
|
|
10
|
+
try {
|
|
11
|
+
stripe = new Stripe(secretKey, {
|
|
12
|
+
apiVersion: "2026-01-28.clover" as any,
|
|
13
|
+
});
|
|
14
|
+
console.log("[Stripe] Initialized successfully");
|
|
15
|
+
} catch (error) {
|
|
16
|
+
console.warn("[Stripe] Failed to initialize:", error instanceof Error ? error.message : error);
|
|
17
|
+
stripe = null;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function createPaymentIntent(options: { priceInCents: number }): Promise<{
|
|
22
|
+
clientSecret: string;
|
|
23
|
+
paymentIntentId: string;
|
|
24
|
+
}> {
|
|
25
|
+
if (!stripe) throw new Error("Stripe not configured");
|
|
26
|
+
|
|
27
|
+
const paymentIntent = await stripe.paymentIntents.create({
|
|
28
|
+
amount: options.priceInCents,
|
|
29
|
+
currency: "usd",
|
|
30
|
+
automatic_payment_methods: { enabled: true },
|
|
31
|
+
metadata: {
|
|
32
|
+
consumed: "false",
|
|
33
|
+
expected_amount: String(options.priceInCents),
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
console.log(`[Stripe] Created PaymentIntent: ${paymentIntent.id}`);
|
|
38
|
+
return {
|
|
39
|
+
clientSecret: paymentIntent.client_secret!,
|
|
40
|
+
paymentIntentId: paymentIntent.id,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function validateAndConsumePaymentIntent(
|
|
45
|
+
paymentIntentId: string,
|
|
46
|
+
expectedAmountCents: number,
|
|
47
|
+
): Promise<{
|
|
48
|
+
valid: boolean;
|
|
49
|
+
error?: string;
|
|
50
|
+
}> {
|
|
51
|
+
if (!stripe) return { valid: false, error: "Stripe not configured" };
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
const paymentIntent = await stripe.paymentIntents.retrieve(paymentIntentId);
|
|
55
|
+
|
|
56
|
+
if (paymentIntent.status !== "succeeded") {
|
|
57
|
+
return { valid: false, error: "Payment not completed" };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (paymentIntent.amount < expectedAmountCents) {
|
|
61
|
+
console.log(`[Stripe] Amount mismatch: got ${paymentIntent.amount}, expected ${expectedAmountCents}`);
|
|
62
|
+
return { valid: false, error: "Invalid payment amount" };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (!paymentIntent.metadata.expected_amount) {
|
|
66
|
+
console.log(`[Stripe] PaymentIntent missing expected_amount metadata: ${paymentIntentId}`);
|
|
67
|
+
return { valid: false, error: "Invalid payment source" };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (paymentIntent.metadata.consumed === "true") {
|
|
71
|
+
return { valid: false, error: "Payment already used" };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
await stripe.paymentIntents.update(paymentIntentId, {
|
|
75
|
+
metadata: { ...paymentIntent.metadata, consumed: "true" },
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
console.log(`[Stripe] PaymentIntent validated and consumed: ${paymentIntentId}`);
|
|
79
|
+
return { valid: true };
|
|
80
|
+
} catch (error) {
|
|
81
|
+
console.error("[Stripe] Error validating PaymentIntent %s:", paymentIntentId, error);
|
|
82
|
+
return { valid: false, error: "Invalid payment ID" };
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Experimental Stripe PaymentIntent adapter for aixyz.
|
|
88
|
+
* Automatically configures Stripe payment validation from environment variables.
|
|
89
|
+
*
|
|
90
|
+
* Environment variables:
|
|
91
|
+
* - STRIPE_SECRET_KEY: Stripe secret key (required to enable Stripe)
|
|
92
|
+
* - STRIPE_PRICE_CENTS: Price per request in cents (default: 100)
|
|
93
|
+
*/
|
|
94
|
+
export function experimental_useStripePaymentIntent(app: AixyzServer): void {
|
|
95
|
+
const secretKey = process.env.STRIPE_SECRET_KEY;
|
|
96
|
+
const priceInCents = Number(process.env.STRIPE_PRICE_CENTS) || 100;
|
|
97
|
+
|
|
98
|
+
if (!secretKey) {
|
|
99
|
+
console.log("[Stripe] STRIPE_SECRET_KEY not set, Stripe payments disabled");
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
initializeStripe(secretKey);
|
|
104
|
+
|
|
105
|
+
// Add endpoint to create payment intents
|
|
106
|
+
app.express.post("/stripe/create-payment-intent", express.json(), async (req: Request, res: Response) => {
|
|
107
|
+
console.log("[Stripe] create-payment-intent endpoint hit");
|
|
108
|
+
try {
|
|
109
|
+
const result = await createPaymentIntent({ priceInCents });
|
|
110
|
+
res.json(result);
|
|
111
|
+
} catch (error) {
|
|
112
|
+
const message = error instanceof Error ? error.message : "Failed to create payment intent";
|
|
113
|
+
console.error("[Stripe] Failed to create payment intent:", message);
|
|
114
|
+
const status = message === "Stripe not configured" ? 503 : 500;
|
|
115
|
+
res.status(status).json({ error: message });
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// Add middleware that checks for Stripe payments
|
|
120
|
+
app.express.use(async (req: Request, res: Response, next: NextFunction) => {
|
|
121
|
+
if (!stripe) {
|
|
122
|
+
return next();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const stripePaymentIntentId = req.headers["x-stripe-payment-intent-id"] as string;
|
|
126
|
+
if (stripePaymentIntentId) {
|
|
127
|
+
const result = await validateAndConsumePaymentIntent(stripePaymentIntentId, priceInCents);
|
|
128
|
+
if (result.valid) {
|
|
129
|
+
return next();
|
|
130
|
+
}
|
|
131
|
+
return res.status(402).json({
|
|
132
|
+
error: "Payment Required",
|
|
133
|
+
message: result.error,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// No Stripe payment provided, continue to x402
|
|
138
|
+
return next();
|
|
139
|
+
});
|
|
140
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,37 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aixyz/stripe",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "Stripe payment adapter for aixyz agents",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"ai",
|
|
7
|
+
"agent",
|
|
8
|
+
"stripe",
|
|
9
|
+
"payment",
|
|
10
|
+
"aixyz"
|
|
11
|
+
],
|
|
12
|
+
"homepage": "https://ai-xyz.dev",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/AgentlyHQ/aixyz/issues"
|
|
15
|
+
},
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/AgentlyHQ/aixyz.git"
|
|
19
|
+
},
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"author": "AgentlyHQ",
|
|
22
|
+
"type": "module",
|
|
23
|
+
"files": [
|
|
24
|
+
"**/*.ts"
|
|
25
|
+
],
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"express": "^5.2.1"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/express": "^5.0.6",
|
|
31
|
+
"typescript": "^5.9.3"
|
|
32
|
+
},
|
|
33
|
+
"peerDependencies": {
|
|
34
|
+
"aixyz": "workspace:*",
|
|
35
|
+
"stripe": "^20"
|
|
36
|
+
}
|
|
6
37
|
}
|