feat(subscription): add Stripe express checkout logging
This commit is contained in:
@@ -46,6 +46,10 @@
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
.expressCheckout {
|
||||
min-height: 48px;
|
||||
}
|
||||
|
||||
.error {
|
||||
margin: 0;
|
||||
color: #c0392b;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
import { useState, type FormEvent } from "react";
|
||||
import {
|
||||
Elements,
|
||||
ExpressCheckoutElement,
|
||||
PaymentElement,
|
||||
useElements,
|
||||
useStripe,
|
||||
@@ -106,6 +107,57 @@ function StripePaymentForm({
|
||||
const [errorMessage, setErrorMessage] = useState<string | null>(null);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
const handleExpressReady = (event: Parameters<
|
||||
NonNullable<React.ComponentProps<typeof ExpressCheckoutElement>["onReady"]>
|
||||
>[0]) => {
|
||||
log.important(
|
||||
"info",
|
||||
"[stripe-payment-dialog] ExpressCheckoutElement ready",
|
||||
{
|
||||
availablePaymentMethods: event.availablePaymentMethods ?? null,
|
||||
userAgent: navigator.userAgent,
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
const handleExpressAvailablePaymentMethodsChange = (event: Parameters<
|
||||
NonNullable<
|
||||
React.ComponentProps<
|
||||
typeof ExpressCheckoutElement
|
||||
>["onAvailablePaymentMethodsChange"]
|
||||
>
|
||||
>[0]) => {
|
||||
log.important(
|
||||
"info",
|
||||
"[stripe-payment-dialog] ExpressCheckoutElement available payment methods changed",
|
||||
{
|
||||
paymentMethods: event.paymentMethods ?? null,
|
||||
userAgent: navigator.userAgent,
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
const handleExpressLoadError = (event: Parameters<
|
||||
NonNullable<
|
||||
React.ComponentProps<typeof ExpressCheckoutElement>["onLoadError"]
|
||||
>
|
||||
>[0]) => {
|
||||
log.warn("[stripe-payment-dialog] ExpressCheckoutElement load failed", {
|
||||
type: event.error.type,
|
||||
code: event.error.code,
|
||||
message: event.error.message,
|
||||
declineCode: event.error.decline_code,
|
||||
});
|
||||
};
|
||||
|
||||
const handleExpressCancel = (event: Parameters<
|
||||
NonNullable<React.ComponentProps<typeof ExpressCheckoutElement>["onCancel"]>
|
||||
>[0]) => {
|
||||
log.debug("[stripe-payment-dialog] Express checkout cancelled", {
|
||||
elementType: event.elementType,
|
||||
});
|
||||
};
|
||||
|
||||
const handleLoadError = (event: Parameters<
|
||||
NonNullable<React.ComponentProps<typeof PaymentElement>["onLoadError"]>
|
||||
>[0]) => {
|
||||
@@ -121,16 +173,37 @@ function StripePaymentForm({
|
||||
);
|
||||
};
|
||||
|
||||
const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {
|
||||
event.preventDefault();
|
||||
if (!stripe || !elements || isSubmitting) return;
|
||||
const confirmStripePayment = async (
|
||||
source: "payment_element" | "express_checkout",
|
||||
expressEvent?: Parameters<
|
||||
NonNullable<
|
||||
React.ComponentProps<typeof ExpressCheckoutElement>["onConfirm"]
|
||||
>
|
||||
>[0],
|
||||
) => {
|
||||
if (!stripe || !elements || isSubmitting) {
|
||||
expressEvent?.paymentFailed({
|
||||
reason: "fail",
|
||||
message: "Payment is not ready yet. Please try again.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
log.debug("[stripe-payment-dialog] confirming payment", {
|
||||
source,
|
||||
expressPaymentType: expressEvent?.expressPaymentType,
|
||||
});
|
||||
setIsSubmitting(true);
|
||||
setErrorMessage(null);
|
||||
|
||||
const { error: submitError } = await elements.submit();
|
||||
if (submitError) {
|
||||
setErrorMessage(submitError.message ?? "Please check your payment info.");
|
||||
const message = submitError.message ?? "Please check your payment info.";
|
||||
setErrorMessage(message);
|
||||
expressEvent?.paymentFailed({
|
||||
reason: "invalid_payment_data",
|
||||
message,
|
||||
});
|
||||
setIsSubmitting(false);
|
||||
return;
|
||||
}
|
||||
@@ -148,16 +221,59 @@ function StripePaymentForm({
|
||||
});
|
||||
|
||||
if (error) {
|
||||
setErrorMessage(error.message ?? "Payment confirmation failed.");
|
||||
const message = error.message ?? "Payment confirmation failed.";
|
||||
setErrorMessage(message);
|
||||
expressEvent?.paymentFailed({
|
||||
reason: "fail",
|
||||
message,
|
||||
});
|
||||
setIsSubmitting(false);
|
||||
return;
|
||||
}
|
||||
|
||||
log.info("[stripe-payment-dialog] payment confirmed", {
|
||||
source,
|
||||
expressPaymentType: expressEvent?.expressPaymentType,
|
||||
});
|
||||
onConfirmed?.();
|
||||
};
|
||||
|
||||
const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
|
||||
event.preventDefault();
|
||||
void confirmStripePayment("payment_element");
|
||||
};
|
||||
|
||||
const handleExpressConfirm = (event: Parameters<
|
||||
NonNullable<React.ComponentProps<typeof ExpressCheckoutElement>["onConfirm"]>
|
||||
>[0]) => {
|
||||
void confirmStripePayment("express_checkout", event);
|
||||
};
|
||||
|
||||
return (
|
||||
<form className={styles.form} onSubmit={handleSubmit}>
|
||||
<div className={styles.expressCheckout}>
|
||||
<ExpressCheckoutElement
|
||||
options={{
|
||||
buttonHeight: 48,
|
||||
layout: {
|
||||
maxColumns: 1,
|
||||
maxRows: 3,
|
||||
},
|
||||
paymentMethods: {
|
||||
applePay: "auto",
|
||||
googlePay: "auto",
|
||||
link: "auto",
|
||||
},
|
||||
}}
|
||||
onReady={handleExpressReady}
|
||||
onLoadError={handleExpressLoadError}
|
||||
onAvailablePaymentMethodsChange={
|
||||
handleExpressAvailablePaymentMethodsChange
|
||||
}
|
||||
onCancel={handleExpressCancel}
|
||||
onConfirm={handleExpressConfirm}
|
||||
/>
|
||||
</div>
|
||||
<PaymentElement onLoadError={handleLoadError} />
|
||||
{errorMessage ? (
|
||||
<p role="alert" className={styles.error}>
|
||||
|
||||
Reference in New Issue
Block a user