Files
LynkedUpPro_CRM/src/modules/payments/services/checkoutService.js
T
2026-06-12 20:49:34 +05:30

77 lines
2.9 KiB
JavaScript

/**
* Checkout service — the only place the frontend talks to Stripe / the payment
* API. The UI calls these functions; it never handles card data.
*
* `startCheckout` follows Stripe's recommended hosted-checkout flow:
* 1. Ask our backend to create a Checkout Session (amount resolved server-side).
* 2. Redirect the browser to the Stripe-hosted `session.url`.
* 3. Stripe handles payment, then redirects back to success_url / cancel_url.
*
* A Stripe.js `redirectToCheckout({ sessionId })` fallback (which uses the
* publishable key) is kept for older API behavior, but `session.url` is the
* current default and avoids loading Stripe.js for the redirect.
*/
import { loadStripe } from '@stripe/stripe-js';
import { stripeConfig } from '../config/stripeConfig';
let stripePromise;
const getStripe = () => {
if (!stripePromise) stripePromise = loadStripe(stripeConfig.publishableKey);
return stripePromise;
};
/**
* Create a session for an invoice and redirect to Stripe's hosted page.
* Throws (with a friendly message) if the backend isn't configured/reachable.
*/
export async function startCheckout({ invoiceId, customerEmail, fallback } = {}) {
if (!stripeConfig.isConfigured) {
const err = new Error(
'Stripe is not configured yet. Add your publishable key to enable payments.'
);
err.code = 'stripe_not_configured';
throw err;
}
const res = await fetch(`${stripeConfig.apiBase}/create-checkout-session`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
// `fallback` carries the invoice's amount/title for demo invoices the
// owner created in localStorage (the server's static map won't have them).
body: JSON.stringify({ invoiceId, customerEmail, fallback }),
});
const data = await res.json().catch(() => ({}));
if (!res.ok) {
throw new Error(data.error || 'Could not start checkout. Please try again.');
}
// Preferred: redirect straight to the Stripe-hosted Checkout URL.
if (data.url) {
window.location.assign(data.url);
return;
}
// Fallback: Stripe.js redirect using the session id + publishable key.
const stripe = await getStripe();
if (!stripe) throw new Error('Unable to initialize Stripe.');
const { error } = await stripe.redirectToCheckout({ sessionId: data.id });
if (error) throw new Error(error.message);
}
/**
* Fetch the verified status of a completed Checkout Session (used by the
* success return page). Returns the trimmed status object from the backend.
*/
export async function getSessionStatus(sessionId) {
const res = await fetch(
`${stripeConfig.apiBase}/session-status?session_id=${encodeURIComponent(sessionId)}`
);
const data = await res.json().catch(() => ({}));
if (!res.ok) {
throw new Error(data.error || 'Could not verify payment status.');
}
return data;
}