Consolidate Kreditkarte/PayPal into one "Online-Zahlung" checkout option
Both already route through the same Stripe PaymentIntent (automatic_payment_methods: enabled — Stripe's own recommended Payment Element pattern, letting Stripe itself decide which eligible method to show). Pre-selecting one of two identical-behind-the-scenes rows before the payment step was redundant friction, not a real choice. Collapses them into one option with a hint text explaining the actual instrument is picked on the next screen; Überweisung is unaffected. Also refines paymentMethodTitle from a neutral "Online-Zahlung" placeholder (snapshotted at order-creation time, before the customer has picked an instrument) to the real one Stripe reports, once payment confirms — carried through to both the stored order and the sessionStorage snapshot shown on /bestellbestaetigung. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -343,7 +343,15 @@ export async function POST(request: Request) {
|
||||
subtotal: finalSubtotal,
|
||||
shippingCost: finalShippingCost,
|
||||
shippingMethodTitle: shippingMethod.title,
|
||||
paymentMethodTitle: paymentMethod.title,
|
||||
// The checkout UI collapses Kreditkarte/PayPal into one "Online-
|
||||
// Zahlung" pre-selection (see groupPaymentMethodsForCheckout) — the
|
||||
// customer hasn't actually chosen an instrument yet at this point,
|
||||
// Stripe's Payment Element does that next. Snapshotting the specific
|
||||
// resolved row's title here would just record whichever row happened
|
||||
// to be the group's representative id, not what was really picked.
|
||||
// The webhook route refines this to the real instrument
|
||||
// ("Kreditkarte"/"PayPal") once Stripe reports it, via confirm-payment.
|
||||
paymentMethodTitle: requiresPayment ? "Online-Zahlung" : paymentMethod.title,
|
||||
discountCode: body.discountCode || null,
|
||||
discountAmount,
|
||||
total,
|
||||
@@ -476,7 +484,7 @@ export async function POST(request: Request) {
|
||||
}
|
||||
: {}),
|
||||
shippingCost: finalShippingCost,
|
||||
paymentMethodTitle: paymentMethod.title,
|
||||
paymentMethodTitle: requiresPayment ? "Online-Zahlung" : paymentMethod.title,
|
||||
discountCode: body.discountCode || null,
|
||||
discountAmount,
|
||||
vatExempt,
|
||||
|
||||
@@ -17,5 +17,15 @@ export async function GET(request: Request) {
|
||||
const order = await getCustomerOrderDetail(session.token, session.customer.id, orderNumber);
|
||||
if (!order) return NextResponse.json({ ok: false, reason: "Bestellung nicht gefunden." }, { status: 404 });
|
||||
|
||||
return NextResponse.json({ ok: true, status: order.status, paymentStatus: order.paymentStatus });
|
||||
return NextResponse.json({
|
||||
ok: true,
|
||||
status: order.status,
|
||||
paymentStatus: order.paymentStatus,
|
||||
// Refined from the checkout-time "Online-Zahlung" placeholder to the
|
||||
// actual instrument (Kreditkarte/PayPal) once confirm-payment sets it
|
||||
// — see resolveStripePaymentMethodLabel's own comment. Returned here
|
||||
// so VerarbeitungContent can patch the pending sessionStorage snapshot
|
||||
// before promoting it, so /bestellbestaetigung shows the real one.
|
||||
paymentMethodTitle: order.paymentMethodTitle,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import Stripe from "stripe";
|
||||
import { verifyStripeWebhookSignature } from "../../../lib/payments/stripeProvider";
|
||||
import { verifyStripeWebhookSignature, resolveStripePaymentMethodLabel } from "../../../lib/payments/stripeProvider";
|
||||
import { sendConfirmedPaymentEmail, type ConfirmPaymentOrderSnapshot } from "../../../lib/payments/confirmPaymentEmail";
|
||||
import { sendCriticalAlert } from "../../../lib/alertAdmin";
|
||||
|
||||
@@ -46,6 +46,11 @@ export async function POST(request: Request) {
|
||||
return NextResponse.json({ ok: false, reason: "orderId metadata missing" }, { status: 409 });
|
||||
}
|
||||
|
||||
// Best-effort — see resolveStripePaymentMethodLabel's own comment. Only
|
||||
// meaningful on the "paid" path; a failed payment never gets a
|
||||
// paymentMethodTitle refinement (the order becomes 'cancelled' outright).
|
||||
const paymentMethodTitle = paymentStatus === "paid" ? await resolveStripePaymentMethodLabel(intent) : undefined;
|
||||
|
||||
const res = await fetch(`${PAYLOAD_URL}/api/orders/${orderId}/confirm-payment`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
@@ -56,6 +61,7 @@ export async function POST(request: Request) {
|
||||
paymentStatus,
|
||||
providerReference,
|
||||
paidAt: new Date().toISOString(),
|
||||
...(paymentMethodTitle ? { paymentMethodTitle } : {}),
|
||||
}),
|
||||
}).catch((err) => {
|
||||
sendCriticalAlert("confirm-payment-Aufruf ans Backend fehlgeschlagen", { orderId, providerReference, error: String(err) });
|
||||
|
||||
Reference in New Issue
Block a user