-
Notifications
You must be signed in to change notification settings - Fork 728
Expand file tree
/
Copy pathpaymentService.js
More file actions
114 lines (91 loc) · 2.79 KB
/
Copy pathpaymentService.js
File metadata and controls
114 lines (91 loc) · 2.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
import Stripe from "stripe";
import { env } from "../config/env.js";
let stripeClient;
export function setStripeClientForTests(client) {
stripeClient = client;
}
export function resetStripeClientForTests() {
stripeClient = undefined;
}
function getStripeClient() {
if (stripeClient) {
return stripeClient;
}
if (!env.stripeSecretKey) {
throw new Error("STRIPE_SECRET_KEY is required to create a payment intent");
}
stripeClient = new Stripe(env.stripeSecretKey);
return stripeClient;
}
function validateAmount(amount) {
if (amount === undefined || amount === null) {
throw new Error("amount is required and must be a positive integer");
}
if (!Number.isInteger(amount) || amount <= 0) {
throw new Error("amount must be a positive integer in the smallest currency unit");
}
return amount;
}
function validateCurrency(currency) {
const normalized = (currency ?? "usd").toString().trim().toLowerCase();
if (!/^[a-z]{3}$/.test(normalized)) {
throw new Error("currency must be a valid three-letter ISO currency code");
}
return normalized;
}
function validateMetadata(metadata) {
if (metadata === undefined) {
return undefined;
}
if (!metadata || Array.isArray(metadata) || typeof metadata !== "object") {
throw new Error("metadata must be an object when provided");
}
return Object.fromEntries(
Object.entries(metadata).map(([key, value]) => {
if (!key || key.length > 40) {
throw new Error("metadata keys must be between 1 and 40 characters");
}
if (value === null || typeof value === "object") {
throw new Error("metadata values must be strings, numbers, or booleans");
}
return [key, String(value)];
})
);
}
export async function createPaymentIntent(payload) {
const amount = validateAmount(payload?.amount);
const currency = validateCurrency(payload?.currency);
const metadata = validateMetadata(payload?.metadata);
try {
const paymentIntent = await getStripeClient().paymentIntents.create({
amount,
currency,
...(metadata ? { metadata } : {})
});
return {
paymentId: paymentIntent.id,
clientSecret: paymentIntent.client_secret,
amount: paymentIntent.amount,
currency: paymentIntent.currency,
provider: "stripe"
};
} catch (error) {
if (error?.type?.startsWith("Stripe") || error?.rawType?.startsWith("Stripe")) {
throw error;
}
throw error;
}
}
export async function smokeTestPaymentIntent() {
if (process.env.STRIPE_PAYMENT_SMOKE_TEST !== "true") {
return { skipped: true, reason: "STRIPE_PAYMENT_SMOKE_TEST is not true" };
}
return {
skipped: false,
paymentIntent: await createPaymentIntent({
amount: 100,
currency: "usd",
metadata: { smokeTest: "true" }
})
};
}