diff --git a/src/PowderCoating.Web/Views/Payment/Index.cshtml b/src/PowderCoating.Web/Views/Payment/Index.cshtml
index 744bc6a..6f40a74 100644
--- a/src/PowderCoating.Web/Views/Payment/Index.cshtml
+++ b/src/PowderCoating.Web/Views/Payment/Index.cshtml
@@ -75,11 +75,11 @@
$
- Max: @Model.BalanceDue.ToString("C")
+ Max: @Model.TotalWithSurcharge.ToString("C")
@@ -116,7 +116,7 @@
const STRIPE_PK = '@Model.StripePublishableKey';
const ACCOUNT_ID = '@Model.StripeAccountId';
const TOKEN = '@Model.Token';
- const MAX_AMOUNT = @Model.BalanceDue.ToString("F2");
+ const MAX_TOTAL = @Model.TotalWithSurcharge.ToString("F2");
const SURCHARGE_TYPE = '@Model.SurchargeType';
const SURCHARGE_VALUE = @Model.SurchargeValue.ToString("F4");
const SUCCESS_URL = `/pay/${TOKEN}/success`;
@@ -125,9 +125,8 @@
let elements, paymentElement;
async function initStripe() {
- const amount = parseFloat(document.getElementById('paymentAmount').value) || MAX_AMOUNT;
- const surcharge = calcSurcharge(amount);
- const total = Math.round((amount + surcharge) * 100) / 100;
+ // Input value is the total the customer pays (including fee)
+ const total = parseFloat(document.getElementById('paymentAmount').value) || MAX_TOTAL;
elements = stripe.elements({
mode: 'payment',
@@ -141,12 +140,15 @@
paymentElement.mount('#payment-element');
}
- function calcSurcharge(amount) {
+ // Back-calculates the base invoice amount from a total that includes the fee.
+ // Server receives the base and adds surcharge on top — both paths arrive at the same
+ // PaymentIntent amount so Stripe.js never sees an amount mismatch.
+ function calcBaseFromTotal(total) {
if (SURCHARGE_TYPE === 'Percent')
- return Math.round(amount * (SURCHARGE_VALUE / 100) * 100) / 100;
+ return Math.round(total / (1 + SURCHARGE_VALUE / 100) * 100) / 100;
if (SURCHARGE_TYPE === 'Flat')
- return SURCHARGE_VALUE;
- return 0;
+ return Math.max(0, Math.round((total - SURCHARGE_VALUE) * 100) / 100);
+ return total;
}
function formatCurrency(val) {
@@ -154,9 +156,9 @@
}
function updateSurcharge() {
- const amount = Math.min(parseFloat(document.getElementById('paymentAmount').value) || 0, MAX_AMOUNT);
- const surcharge = calcSurcharge(amount);
- const total = amount + surcharge;
+ const total = Math.min(parseFloat(document.getElementById('paymentAmount').value) || 0, MAX_TOTAL);
+ const base = calcBaseFromTotal(total);
+ const surcharge = Math.round((total - base) * 100) / 100;
const surchargeEl = document.getElementById('surchargeDisplay');
const totalEl = document.getElementById('totalWithSurchargeDisplay');
@@ -164,7 +166,10 @@
if (surchargeEl) surchargeEl.textContent = formatCurrency(surcharge);
if (totalEl) totalEl.textContent = formatCurrency(total);
- if (btnAmtEl) btnAmtEl.textContent = surcharge > 0 ? formatCurrency(total) : formatCurrency(amount);
+ if (btnAmtEl) btnAmtEl.textContent = formatCurrency(total);
+
+ // Keep Elements in sync so confirmPayment amount matches the PaymentIntent
+ if (elements) elements.update({ amount: Math.round(total * 100) });
}
async function submitPayment() {
@@ -172,13 +177,21 @@
const msgEl = document.getElementById('payment-message');
const amountInput = document.getElementById('paymentAmount');
- const amount = parseFloat(amountInput.value);
- if (!amount || amount <= 0 || amount > MAX_AMOUNT) {
+ const total = parseFloat(amountInput.value);
+ if (!total || total <= 0 || total > MAX_TOTAL) {
msgEl.textContent = 'Please enter a valid payment amount.';
msgEl.style.display = '';
return;
}
+ // Back-calculate base; server adds surcharge on top as usual
+ const base = calcBaseFromTotal(total);
+ if (base <= 0) {
+ msgEl.textContent = 'Payment amount must exceed the convenience fee.';
+ msgEl.style.display = '';
+ return;
+ }
+
btn.disabled = true;
document.getElementById('submitText').style.display = 'none';
document.getElementById('submitSpinner').style.display = '';
@@ -195,13 +208,13 @@
return;
}
- // Create PaymentIntent on server
+ // Create PaymentIntent on server — send base amount (server adds surcharge)
let clientSecret;
try {
const resp = await fetch(`/pay/${TOKEN}/intent`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ amount })
+ body: JSON.stringify({ amount: base })
});
const data = await resp.json();
if (!resp.ok) {