feat: fixes and formatter
This commit is contained in:
+246
-323
@@ -1,15 +1,15 @@
|
||||
const admin = require("firebase-admin");
|
||||
const { HttpsError, onCall } = require("firebase-functions/https");
|
||||
const admin = require('firebase-admin')
|
||||
const { HttpsError, onCall } = require('firebase-functions/https')
|
||||
|
||||
let FieldValue = null;
|
||||
let FieldValue = null
|
||||
try {
|
||||
({ FieldValue } = require("firebase-admin/firestore"));
|
||||
;({ FieldValue } = require('firebase-admin/firestore'))
|
||||
} catch (error) {
|
||||
console.warn("[stripe] FieldValue import failed", error?.message);
|
||||
console.warn('[stripe] FieldValue import failed', error?.message)
|
||||
}
|
||||
|
||||
const { REGION, refsList } = require("../index");
|
||||
const STRIPE_MODE = "test";
|
||||
const { REGION, refsList } = require('../index')
|
||||
const STRIPE_MODE = 'test'
|
||||
const {
|
||||
getStripeClient,
|
||||
getReturnUrls,
|
||||
@@ -19,23 +19,20 @@ const {
|
||||
formatCheckoutSessionResponse,
|
||||
getPortalConfigurationId,
|
||||
mapStripeErrorToHttps,
|
||||
} = require("../helpers/stripe");
|
||||
} = require('../helpers/stripe')
|
||||
|
||||
const paymentsCollection = admin.firestore().collection("payments");
|
||||
const paymentsCollection = admin.firestore().collection('payments')
|
||||
|
||||
const getServerTimestamp = () => {
|
||||
if (FieldValue?.serverTimestamp) {
|
||||
return FieldValue.serverTimestamp();
|
||||
return FieldValue.serverTimestamp()
|
||||
}
|
||||
const fallback = admin.firestore?.FieldValue;
|
||||
const fallback = admin.firestore?.FieldValue
|
||||
if (fallback?.serverTimestamp) {
|
||||
return fallback.serverTimestamp();
|
||||
return fallback.serverTimestamp()
|
||||
}
|
||||
throw new HttpsError(
|
||||
"failed-precondition",
|
||||
"Firestore FieldValue.serverTimestamp indisponible.",
|
||||
);
|
||||
};
|
||||
throw new HttpsError('failed-precondition', 'Firestore FieldValue.serverTimestamp indisponible.')
|
||||
}
|
||||
|
||||
const normalizePaymentIntent = (paymentIntent) => ({
|
||||
id: paymentIntent.id,
|
||||
@@ -48,53 +45,44 @@ const normalizePaymentIntent = (paymentIntent) => ({
|
||||
created: paymentIntent.created,
|
||||
latest_charge: paymentIntent.latest_charge,
|
||||
metadata: paymentIntent.metadata,
|
||||
});
|
||||
})
|
||||
|
||||
const createCheckoutSession = onCall({ region: REGION }, async (request) => {
|
||||
try {
|
||||
const uid = request?.auth?.uid;
|
||||
const uid = request?.auth?.uid
|
||||
if (!uid) {
|
||||
throw new HttpsError(
|
||||
"unauthenticated",
|
||||
"Connecte-toi pour créer une session Stripe.",
|
||||
);
|
||||
throw new HttpsError('unauthenticated', 'Connecte-toi pour créer une session Stripe.')
|
||||
}
|
||||
|
||||
const requestedUserId =
|
||||
typeof request?.data?.userID === "string"
|
||||
? request.data.userID.trim()
|
||||
: null;
|
||||
typeof request?.data?.userID === 'string' ? request.data.userID.trim() : null
|
||||
|
||||
if (requestedUserId && requestedUserId !== uid) {
|
||||
throw new HttpsError(
|
||||
"permission-denied",
|
||||
"Tu ne peux créer une session que pour ton propre compte.",
|
||||
);
|
||||
'permission-denied',
|
||||
'Tu ne peux créer une session que pour ton propre compte.'
|
||||
)
|
||||
}
|
||||
|
||||
const productList = Array.isArray(request?.data?.productList)
|
||||
? request.data.productList
|
||||
: [];
|
||||
const productList = Array.isArray(request?.data?.productList) ? request.data.productList : []
|
||||
|
||||
const stripe = getStripeClient();
|
||||
const { lineItems, summary, hasSubscription } =
|
||||
await buildCheckoutLineItems(productList, { stripe });
|
||||
const stripe = getStripeClient()
|
||||
const { lineItems, summary, hasSubscription } = await buildCheckoutLineItems(productList, {
|
||||
stripe,
|
||||
})
|
||||
|
||||
const mode = hasSubscription ? "subscription" : "payment";
|
||||
const { successUrl, cancelUrl } = getReturnUrls(request?.data?.returnUrls);
|
||||
const mode = hasSubscription ? 'subscription' : 'payment'
|
||||
const { successUrl, cancelUrl } = getReturnUrls(request?.data?.returnUrls)
|
||||
|
||||
const { customerId } = await ensureStripeCustomer({
|
||||
uid,
|
||||
stripe,
|
||||
refsList,
|
||||
createIfMissing: true,
|
||||
});
|
||||
})
|
||||
|
||||
if (!customerId) {
|
||||
throw new HttpsError(
|
||||
"internal",
|
||||
"Impossible de retrouver le client Stripe associé.",
|
||||
);
|
||||
throw new HttpsError('internal', 'Impossible de retrouver le client Stripe associé.')
|
||||
}
|
||||
|
||||
const session = await stripe.checkout.sessions.create({
|
||||
@@ -107,12 +95,12 @@ const createCheckoutSession = onCall({ region: REGION }, async (request) => {
|
||||
metadata: {
|
||||
firebaseUID: uid,
|
||||
},
|
||||
});
|
||||
})
|
||||
|
||||
await paymentsCollection.doc(session.id).set({
|
||||
userId: uid,
|
||||
customerId,
|
||||
status: session.status || "created",
|
||||
status: session.status || 'created',
|
||||
mode,
|
||||
createdAt: getServerTimestamp(),
|
||||
updatedAt: getServerTimestamp(),
|
||||
@@ -123,59 +111,53 @@ const createCheckoutSession = onCall({ region: REGION }, async (request) => {
|
||||
amountTotal: session.amount_total,
|
||||
currency: session.currency,
|
||||
paymentStatus: session.payment_status,
|
||||
});
|
||||
})
|
||||
|
||||
return formatCheckoutSessionResponse(session);
|
||||
return formatCheckoutSessionResponse(session)
|
||||
} catch (error) {
|
||||
console.error("[createCheckoutSession] error", error);
|
||||
console.error('[createCheckoutSession] error', error)
|
||||
if (error instanceof HttpsError) {
|
||||
throw error;
|
||||
throw error
|
||||
}
|
||||
throw mapStripeErrorToHttps(
|
||||
error,
|
||||
"Création de la session Stripe impossible.",
|
||||
);
|
||||
throw mapStripeErrorToHttps(error, 'Création de la session Stripe impossible.')
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
const getPremiumStatus = onCall({ region: REGION }, async (request) => {
|
||||
try {
|
||||
const uid = request?.auth?.uid;
|
||||
const uid = request?.auth?.uid
|
||||
if (!uid) {
|
||||
throw new HttpsError(
|
||||
"unauthenticated",
|
||||
"Connecte-toi pour consulter ton statut Stripe.",
|
||||
);
|
||||
throw new HttpsError('unauthenticated', 'Connecte-toi pour consulter ton statut Stripe.')
|
||||
}
|
||||
|
||||
const stripe = getStripeClient();
|
||||
const stripe = getStripeClient()
|
||||
const { customerId } = await ensureStripeCustomer({
|
||||
uid,
|
||||
stripe,
|
||||
refsList,
|
||||
createIfMissing: false,
|
||||
});
|
||||
})
|
||||
|
||||
if (!customerId) {
|
||||
return {
|
||||
customerId: null,
|
||||
subscriptions: [],
|
||||
invoices: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const [subscriptions, invoices] = await Promise.all([
|
||||
stripe.subscriptions.list({
|
||||
customer: customerId,
|
||||
status: "all",
|
||||
expand: ["data.items.data.price"],
|
||||
status: 'all',
|
||||
expand: ['data.items.data.price'],
|
||||
limit: 20,
|
||||
}),
|
||||
stripe.invoices.list({
|
||||
customer: customerId,
|
||||
limit: 20,
|
||||
}),
|
||||
]);
|
||||
])
|
||||
|
||||
const formattedSubscriptions = subscriptions.data.map((subscription) => ({
|
||||
id: subscription.id,
|
||||
@@ -193,7 +175,7 @@ const getPremiumStatus = onCall({ region: REGION }, async (request) => {
|
||||
currency: item.price?.currency,
|
||||
recurring: item.price?.recurring,
|
||||
})),
|
||||
}));
|
||||
}))
|
||||
|
||||
const formattedInvoices = invoices.data.map((invoice) => ({
|
||||
id: invoice.id,
|
||||
@@ -205,179 +187,153 @@ const getPremiumStatus = onCall({ region: REGION }, async (request) => {
|
||||
hosted_invoice_url: invoice.hosted_invoice_url,
|
||||
invoice_pdf: invoice.invoice_pdf,
|
||||
created: invoice.created,
|
||||
}));
|
||||
}))
|
||||
|
||||
return {
|
||||
customerId,
|
||||
subscriptions: formattedSubscriptions,
|
||||
invoices: formattedInvoices,
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("[getPremiumStatus] error", error);
|
||||
console.error('[getPremiumStatus] error', error)
|
||||
if (error instanceof HttpsError) {
|
||||
throw error;
|
||||
throw error
|
||||
}
|
||||
throw mapStripeErrorToHttps(
|
||||
error,
|
||||
"Impossible de récupérer le statut Stripe.",
|
||||
);
|
||||
throw mapStripeErrorToHttps(error, 'Impossible de récupérer le statut Stripe.')
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
const createStripeCustomerPortalSession = onCall(
|
||||
{ region: REGION },
|
||||
async (request) => {
|
||||
try {
|
||||
const uid = request?.auth?.uid;
|
||||
if (!uid) {
|
||||
throw new HttpsError(
|
||||
"unauthenticated",
|
||||
"Connecte-toi pour ouvrir le portail client Stripe.",
|
||||
);
|
||||
}
|
||||
|
||||
const stripe = getStripeClient();
|
||||
const { customerId } = await ensureStripeCustomer({
|
||||
uid,
|
||||
stripe,
|
||||
refsList,
|
||||
createIfMissing: false,
|
||||
});
|
||||
|
||||
if (!customerId) {
|
||||
throw new HttpsError(
|
||||
"failed-precondition",
|
||||
"Aucun client Stripe associé à cet utilisateur.",
|
||||
);
|
||||
}
|
||||
|
||||
const portalConfigurationId = await getPortalConfigurationId(stripe);
|
||||
if (!portalConfigurationId) {
|
||||
const modeLabel = STRIPE_MODE === "prod" ? "production" : "test";
|
||||
const envKeySuffix = STRIPE_MODE === "prod" ? "PROD" : "TEST";
|
||||
throw new HttpsError(
|
||||
"failed-precondition",
|
||||
`Configure le portail client Stripe en mode ${modeLabel} ou renseigne STRIPE_PORTAL_CONFIGURATION_${envKeySuffix}.`,
|
||||
);
|
||||
}
|
||||
|
||||
const { successUrl } = getReturnUrls();
|
||||
|
||||
const portalSession = await stripe.billingPortal.sessions.create({
|
||||
customer: customerId,
|
||||
return_url: successUrl,
|
||||
configuration: portalConfigurationId,
|
||||
});
|
||||
|
||||
return {
|
||||
id: portalSession.id,
|
||||
url: portalSession.url,
|
||||
created: portalSession.created,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("[createStripeCustomerPortalSession] error", error);
|
||||
if (error instanceof HttpsError) {
|
||||
throw error;
|
||||
}
|
||||
throw mapStripeErrorToHttps(
|
||||
error,
|
||||
"Ouverture du portail Stripe impossible.",
|
||||
);
|
||||
const createStripeCustomerPortalSession = onCall({ region: REGION }, async (request) => {
|
||||
try {
|
||||
const uid = request?.auth?.uid
|
||||
if (!uid) {
|
||||
throw new HttpsError('unauthenticated', 'Connecte-toi pour ouvrir le portail client Stripe.')
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
const stripe = getStripeClient()
|
||||
const { customerId } = await ensureStripeCustomer({
|
||||
uid,
|
||||
stripe,
|
||||
refsList,
|
||||
createIfMissing: false,
|
||||
})
|
||||
|
||||
if (!customerId) {
|
||||
throw new HttpsError('failed-precondition', 'Aucun client Stripe associé à cet utilisateur.')
|
||||
}
|
||||
|
||||
const portalConfigurationId = await getPortalConfigurationId(stripe)
|
||||
if (!portalConfigurationId) {
|
||||
const modeLabel = STRIPE_MODE === 'prod' ? 'production' : 'test'
|
||||
const envKeySuffix = STRIPE_MODE === 'prod' ? 'PROD' : 'TEST'
|
||||
throw new HttpsError(
|
||||
'failed-precondition',
|
||||
`Configure le portail client Stripe en mode ${modeLabel} ou renseigne STRIPE_PORTAL_CONFIGURATION_${envKeySuffix}.`
|
||||
)
|
||||
}
|
||||
|
||||
const { successUrl } = getReturnUrls()
|
||||
|
||||
const portalSession = await stripe.billingPortal.sessions.create({
|
||||
customer: customerId,
|
||||
return_url: successUrl,
|
||||
configuration: portalConfigurationId,
|
||||
})
|
||||
|
||||
return {
|
||||
id: portalSession.id,
|
||||
url: portalSession.url,
|
||||
created: portalSession.created,
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[createStripeCustomerPortalSession] error', error)
|
||||
if (error instanceof HttpsError) {
|
||||
throw error
|
||||
}
|
||||
throw mapStripeErrorToHttps(error, 'Ouverture du portail Stripe impossible.')
|
||||
}
|
||||
})
|
||||
|
||||
const resolveStripeConnectAccountId = (userData) => {
|
||||
if (!userData || typeof userData !== "object") {
|
||||
return null;
|
||||
if (!userData || typeof userData !== 'object') {
|
||||
return null
|
||||
}
|
||||
|
||||
const candidatePaths = [
|
||||
["stripeConnectAccountId"],
|
||||
["stripeConnectId"],
|
||||
["stripeAccountId"],
|
||||
["stripeConnectAccount"],
|
||||
["stripeAccount"],
|
||||
["stripe", "connectAccountId"],
|
||||
["stripe", "accountId"],
|
||||
["providers", "stripeConnect", "accountId"],
|
||||
];
|
||||
['stripeConnectAccountId'],
|
||||
['stripeConnectId'],
|
||||
['stripeAccountId'],
|
||||
['stripeConnectAccount'],
|
||||
['stripeAccount'],
|
||||
['stripe', 'connectAccountId'],
|
||||
['stripe', 'accountId'],
|
||||
['providers', 'stripeConnect', 'accountId'],
|
||||
]
|
||||
|
||||
for (const path of candidatePaths) {
|
||||
let current = userData;
|
||||
let current = userData
|
||||
for (const key of path) {
|
||||
if (!current || typeof current !== "object") {
|
||||
current = null;
|
||||
break;
|
||||
if (!current || typeof current !== 'object') {
|
||||
current = null
|
||||
break
|
||||
}
|
||||
current = current[key];
|
||||
current = current[key]
|
||||
}
|
||||
|
||||
if (typeof current === "string" && current.trim()) {
|
||||
return current.trim();
|
||||
if (typeof current === 'string' && current.trim()) {
|
||||
return current.trim()
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
return null
|
||||
}
|
||||
|
||||
const ensureStripeConnectAccount = async ({
|
||||
uid,
|
||||
stripe,
|
||||
userRef,
|
||||
userData,
|
||||
}) => {
|
||||
const ensureStripeConnectAccount = async ({ uid, stripe, userRef, userData }) => {
|
||||
if (!uid || !stripe) {
|
||||
return { connectAccountId: null, userData, createdAccount: false };
|
||||
return { connectAccountId: null, userData, createdAccount: false }
|
||||
}
|
||||
|
||||
let connectAccountId = resolveStripeConnectAccountId(userData);
|
||||
let connectAccountId = resolveStripeConnectAccountId(userData)
|
||||
if (connectAccountId) {
|
||||
return { connectAccountId, userData, createdAccount: false };
|
||||
return { connectAccountId, userData, createdAccount: false }
|
||||
}
|
||||
|
||||
let authRecord = null;
|
||||
let authRecord = null
|
||||
try {
|
||||
authRecord = await admin.auth().getUser(uid);
|
||||
authRecord = await admin.auth().getUser(uid)
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
"[ensureStripeConnectAccount] Impossible de récupérer auth user",
|
||||
error,
|
||||
);
|
||||
console.warn('[ensureStripeConnectAccount] Impossible de récupérer auth user', error)
|
||||
}
|
||||
|
||||
const email = userData?.email || authRecord?.email || undefined;
|
||||
const email = userData?.email || authRecord?.email || undefined
|
||||
|
||||
const accountParams = {
|
||||
type: "express",
|
||||
type: 'express',
|
||||
capabilities: {
|
||||
card_payments: { requested: true },
|
||||
transfers: { requested: true },
|
||||
},
|
||||
metadata: {
|
||||
firebaseUID: uid,
|
||||
appMode: STRIPE_MODE || "test",
|
||||
appMode: STRIPE_MODE || 'test',
|
||||
},
|
||||
};
|
||||
|
||||
if (email) {
|
||||
accountParams.email = email;
|
||||
}
|
||||
|
||||
const account = await stripe.accounts.create(accountParams);
|
||||
connectAccountId = account?.id;
|
||||
if (email) {
|
||||
accountParams.email = email
|
||||
}
|
||||
|
||||
const account = await stripe.accounts.create(accountParams)
|
||||
connectAccountId = account?.id
|
||||
|
||||
if (!connectAccountId) {
|
||||
throw new HttpsError(
|
||||
"internal",
|
||||
"Stripe n’a pas renvoyé d’identifiant de compte Connect.",
|
||||
);
|
||||
throw new HttpsError('internal', 'Stripe n’a pas renvoyé d’identifiant de compte Connect.')
|
||||
}
|
||||
|
||||
const providersConnect = {
|
||||
...(userData?.providers?.stripeConnect || {}),
|
||||
accountId: connectAccountId,
|
||||
};
|
||||
}
|
||||
|
||||
if (userRef) {
|
||||
await userRef.set(
|
||||
@@ -391,8 +347,8 @@ const ensureStripeConnectAccount = async ({
|
||||
},
|
||||
},
|
||||
},
|
||||
{ merge: true },
|
||||
);
|
||||
{ merge: true }
|
||||
)
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -406,170 +362,140 @@ const ensureStripeConnectAccount = async ({
|
||||
},
|
||||
},
|
||||
createdAccount: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const createStripeConnectLoginLink = onCall(
|
||||
{ region: REGION },
|
||||
async (request) => {
|
||||
try {
|
||||
const uid = request?.auth?.uid;
|
||||
if (!uid) {
|
||||
throw new HttpsError(
|
||||
"unauthenticated",
|
||||
"Connecte-toi pour ouvrir Stripe Connect.",
|
||||
);
|
||||
}
|
||||
|
||||
const userRef = refsList?.users?.doc(uid);
|
||||
const snapshot = userRef ? await userRef.get() : null;
|
||||
let userData = snapshot?.exists ? snapshot.data() : null;
|
||||
|
||||
const stripe = getStripeClient();
|
||||
const ensureResult = await ensureStripeConnectAccount({
|
||||
uid,
|
||||
stripe,
|
||||
userRef,
|
||||
userData,
|
||||
});
|
||||
|
||||
const connectAccountId = ensureResult.connectAccountId;
|
||||
userData = ensureResult.userData;
|
||||
|
||||
if (!connectAccountId) {
|
||||
throw new HttpsError(
|
||||
"failed-precondition",
|
||||
"Impossible de créer un compte Stripe Connect pour cet utilisateur.",
|
||||
);
|
||||
}
|
||||
|
||||
const redirectUrl = getReturnBaseUrl();
|
||||
|
||||
const loginLink = await stripe.accounts.createLoginLink(
|
||||
connectAccountId,
|
||||
redirectUrl
|
||||
? {
|
||||
redirect_url: redirectUrl,
|
||||
}
|
||||
: undefined,
|
||||
);
|
||||
|
||||
if (!loginLink?.url) {
|
||||
throw new HttpsError(
|
||||
"internal",
|
||||
"Stripe Connect n’a pas renvoyé de lien de connexion.",
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
id: loginLink.id,
|
||||
url: loginLink.url,
|
||||
created: loginLink.created,
|
||||
connectAccountId,
|
||||
createdAccount: ensureResult.createdAccount,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("[createStripeConnectLoginLink] error", error);
|
||||
if (error instanceof HttpsError) {
|
||||
throw error;
|
||||
}
|
||||
throw mapStripeErrorToHttps(
|
||||
error,
|
||||
"Ouverture de Stripe Connect impossible.",
|
||||
);
|
||||
const createStripeConnectLoginLink = onCall({ region: REGION }, async (request) => {
|
||||
try {
|
||||
const uid = request?.auth?.uid
|
||||
if (!uid) {
|
||||
throw new HttpsError('unauthenticated', 'Connecte-toi pour ouvrir Stripe Connect.')
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
const userRef = refsList?.users?.doc(uid)
|
||||
const snapshot = userRef ? await userRef.get() : null
|
||||
let userData = snapshot?.exists ? snapshot.data() : null
|
||||
|
||||
const stripe = getStripeClient()
|
||||
const ensureResult = await ensureStripeConnectAccount({
|
||||
uid,
|
||||
stripe,
|
||||
userRef,
|
||||
userData,
|
||||
})
|
||||
|
||||
const connectAccountId = ensureResult.connectAccountId
|
||||
userData = ensureResult.userData
|
||||
|
||||
if (!connectAccountId) {
|
||||
throw new HttpsError(
|
||||
'failed-precondition',
|
||||
'Impossible de créer un compte Stripe Connect pour cet utilisateur.'
|
||||
)
|
||||
}
|
||||
|
||||
const redirectUrl = getReturnBaseUrl()
|
||||
|
||||
const loginLink = await stripe.accounts.createLoginLink(
|
||||
connectAccountId,
|
||||
redirectUrl
|
||||
? {
|
||||
redirect_url: redirectUrl,
|
||||
}
|
||||
: undefined
|
||||
)
|
||||
|
||||
if (!loginLink?.url) {
|
||||
throw new HttpsError('internal', 'Stripe Connect n’a pas renvoyé de lien de connexion.')
|
||||
}
|
||||
|
||||
return {
|
||||
id: loginLink.id,
|
||||
url: loginLink.url,
|
||||
created: loginLink.created,
|
||||
connectAccountId,
|
||||
createdAccount: ensureResult.createdAccount,
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[createStripeConnectLoginLink] error', error)
|
||||
if (error instanceof HttpsError) {
|
||||
throw error
|
||||
}
|
||||
throw mapStripeErrorToHttps(error, 'Ouverture de Stripe Connect impossible.')
|
||||
}
|
||||
})
|
||||
|
||||
const verifyStripePayment = onCall({ region: REGION }, async (request) => {
|
||||
try {
|
||||
const uid = request?.auth?.uid;
|
||||
const uid = request?.auth?.uid
|
||||
if (!uid) {
|
||||
throw new HttpsError(
|
||||
"unauthenticated",
|
||||
"Connecte-toi pour vérifier un paiement Stripe.",
|
||||
);
|
||||
throw new HttpsError('unauthenticated', 'Connecte-toi pour vérifier un paiement Stripe.')
|
||||
}
|
||||
|
||||
const rawPaymentId =
|
||||
typeof request?.data?.paymentId === "string"
|
||||
? request.data.paymentId.trim()
|
||||
: "";
|
||||
typeof request?.data?.paymentId === 'string' ? request.data.paymentId.trim() : ''
|
||||
|
||||
if (!rawPaymentId) {
|
||||
throw new HttpsError(
|
||||
"invalid-argument",
|
||||
"Fournis un identifiant de paiement Stripe.",
|
||||
);
|
||||
throw new HttpsError('invalid-argument', 'Fournis un identifiant de paiement Stripe.')
|
||||
}
|
||||
|
||||
const stripe = getStripeClient();
|
||||
const stripe = getStripeClient()
|
||||
|
||||
const fetchPaymentIntent = async (paymentIntentId) =>
|
||||
stripe.paymentIntents.retrieve(paymentIntentId, {
|
||||
expand: ["latest_charge"],
|
||||
});
|
||||
expand: ['latest_charge'],
|
||||
})
|
||||
|
||||
const fetchCheckoutSession = async (sessionId) =>
|
||||
stripe.checkout.sessions.retrieve(sessionId, {
|
||||
expand: ["payment_intent"],
|
||||
});
|
||||
expand: ['payment_intent'],
|
||||
})
|
||||
|
||||
let paymentIntent = null;
|
||||
let checkoutSession = null;
|
||||
let paymentType = null;
|
||||
let paymentIntent = null
|
||||
let checkoutSession = null
|
||||
let paymentType = null
|
||||
|
||||
if (rawPaymentId.startsWith("pi_")) {
|
||||
paymentIntent = await fetchPaymentIntent(rawPaymentId);
|
||||
paymentType = "payment_intent";
|
||||
} else if (rawPaymentId.startsWith("cs_")) {
|
||||
checkoutSession = await fetchCheckoutSession(rawPaymentId);
|
||||
if (rawPaymentId.startsWith('pi_')) {
|
||||
paymentIntent = await fetchPaymentIntent(rawPaymentId)
|
||||
paymentType = 'payment_intent'
|
||||
} else if (rawPaymentId.startsWith('cs_')) {
|
||||
checkoutSession = await fetchCheckoutSession(rawPaymentId)
|
||||
paymentIntent =
|
||||
checkoutSession?.payment_intent &&
|
||||
typeof checkoutSession.payment_intent === "object"
|
||||
checkoutSession?.payment_intent && typeof checkoutSession.payment_intent === 'object'
|
||||
? checkoutSession.payment_intent
|
||||
: checkoutSession?.payment_intent
|
||||
? await fetchPaymentIntent(checkoutSession.payment_intent)
|
||||
: null;
|
||||
paymentType = "checkout_session";
|
||||
: null
|
||||
paymentType = 'checkout_session'
|
||||
} else {
|
||||
try {
|
||||
paymentIntent = await fetchPaymentIntent(rawPaymentId);
|
||||
paymentType = "payment_intent";
|
||||
paymentIntent = await fetchPaymentIntent(rawPaymentId)
|
||||
paymentType = 'payment_intent'
|
||||
} catch (intentError) {
|
||||
try {
|
||||
checkoutSession = await fetchCheckoutSession(rawPaymentId);
|
||||
paymentType = "checkout_session";
|
||||
checkoutSession = await fetchCheckoutSession(rawPaymentId)
|
||||
paymentType = 'checkout_session'
|
||||
paymentIntent =
|
||||
checkoutSession?.payment_intent &&
|
||||
typeof checkoutSession.payment_intent === "object"
|
||||
checkoutSession?.payment_intent && typeof checkoutSession.payment_intent === 'object'
|
||||
? checkoutSession.payment_intent
|
||||
: checkoutSession?.payment_intent
|
||||
? await fetchPaymentIntent(checkoutSession.payment_intent)
|
||||
: null;
|
||||
: null
|
||||
} catch (sessionError) {
|
||||
console.error("[verifyStripePayment] lookup failure", {
|
||||
console.error('[verifyStripePayment] lookup failure', {
|
||||
intentError,
|
||||
sessionError,
|
||||
});
|
||||
throw new HttpsError(
|
||||
"not-found",
|
||||
"Aucun paiement Stripe trouvé avec cet identifiant.",
|
||||
);
|
||||
})
|
||||
throw new HttpsError('not-found', 'Aucun paiement Stripe trouvé avec cet identifiant.')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!paymentIntent && !checkoutSession) {
|
||||
throw new HttpsError(
|
||||
"not-found",
|
||||
"Aucun paiement Stripe trouvé avec cet identifiant.",
|
||||
);
|
||||
throw new HttpsError('not-found', 'Aucun paiement Stripe trouvé avec cet identifiant.')
|
||||
}
|
||||
|
||||
const normalizedIntent = paymentIntent
|
||||
? normalizePaymentIntent(paymentIntent)
|
||||
: null;
|
||||
const normalizedIntent = paymentIntent ? normalizePaymentIntent(paymentIntent) : null
|
||||
|
||||
const response = {
|
||||
type: paymentType,
|
||||
@@ -594,41 +520,38 @@ const verifyStripePayment = onCall({ region: REGION }, async (request) => {
|
||||
checkoutSession?.status ??
|
||||
null,
|
||||
currency: normalizedIntent?.currency ?? checkoutSession?.currency ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
const paymentDocId =
|
||||
paymentType === "checkout_session"
|
||||
? checkoutSession?.id
|
||||
: normalizedIntent?.id;
|
||||
paymentType === 'checkout_session' ? checkoutSession?.id : normalizedIntent?.id
|
||||
|
||||
if (paymentDocId) {
|
||||
const paymentDocRef = paymentsCollection.doc(paymentDocId);
|
||||
const existing = await paymentDocRef.get();
|
||||
const paymentDocRef = paymentsCollection.doc(paymentDocId)
|
||||
const existing = await paymentDocRef.get()
|
||||
if (existing.exists) {
|
||||
await paymentDocRef.set(
|
||||
{
|
||||
status: response.status,
|
||||
paymentStatus: checkoutSession?.payment_status,
|
||||
amountTotal:
|
||||
checkoutSession?.amount_total ?? normalizedIntent?.amount,
|
||||
amountTotal: checkoutSession?.amount_total ?? normalizedIntent?.amount,
|
||||
amountReceived: normalizedIntent?.amount_received,
|
||||
currency: response.currency,
|
||||
updatedAt: getServerTimestamp(),
|
||||
},
|
||||
{ merge: true },
|
||||
);
|
||||
{ merge: true }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
return response
|
||||
} catch (error) {
|
||||
console.error("[verifyStripePayment] error", error);
|
||||
console.error('[verifyStripePayment] error', error)
|
||||
if (error instanceof HttpsError) {
|
||||
throw error;
|
||||
throw error
|
||||
}
|
||||
throw mapStripeErrorToHttps(error, "Vérification du paiement impossible.");
|
||||
throw mapStripeErrorToHttps(error, 'Vérification du paiement impossible.')
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
module.exports = {
|
||||
createCheckoutSession,
|
||||
@@ -636,4 +559,4 @@ module.exports = {
|
||||
createStripeCustomerPortalSession,
|
||||
createStripeConnectLoginLink,
|
||||
verifyStripePayment,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user