changes
This commit is contained in:
BIN
fusion_clover/static/description/fusion_clover.png
Normal file
BIN
fusion_clover/static/description/fusion_clover.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.9 MiB |
@@ -1,33 +1,40 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { _t } from '@web/core/l10n/translation';
|
||||
import { patch } from '@web/core/utils/patch';
|
||||
import { rpc } from '@web/core/network/rpc';
|
||||
import { _t } from "@web/core/l10n/translation";
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
import { rpc } from "@web/core/network/rpc";
|
||||
import { loadJS } from "@web/core/assets";
|
||||
|
||||
import { PaymentForm } from '@payment/interactions/payment_form';
|
||||
import { PaymentForm } from "@payment/interactions/payment_form";
|
||||
|
||||
const CLOVER_SDK_URL_TEST = "https://checkout.sandbox.dev.clover.com/sdk.js";
|
||||
const CLOVER_SDK_URL_PROD = "https://checkout.clover.com/sdk.js";
|
||||
|
||||
patch(PaymentForm.prototype, {
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
this.cloverFormData = {};
|
||||
this._detectedCardType = 'other';
|
||||
this._selectedCardType = 'other';
|
||||
this.cloverInstance = null;
|
||||
this.cloverElements = null;
|
||||
this.cloverMountedElements = {};
|
||||
this._detectedCardType = "other";
|
||||
this._selectedCardType = "other";
|
||||
},
|
||||
|
||||
// #=== DOM MANIPULATION ===#
|
||||
|
||||
async _prepareInlineForm(providerId, providerCode, paymentOptionId, paymentMethodCode, flow) {
|
||||
if (providerCode !== 'clover') {
|
||||
if (providerCode !== "clover") {
|
||||
await super._prepareInlineForm(...arguments);
|
||||
return;
|
||||
}
|
||||
|
||||
if (flow === 'token') {
|
||||
if (flow === "token") {
|
||||
return;
|
||||
}
|
||||
|
||||
this._setPaymentFlow('direct');
|
||||
this._setPaymentFlow("direct");
|
||||
|
||||
const radio = document.querySelector('input[name="o_payment_radio"]:checked');
|
||||
const inlineForm = this._getInlineForm(radio);
|
||||
@@ -37,45 +44,135 @@ patch(PaymentForm.prototype, {
|
||||
return;
|
||||
}
|
||||
|
||||
const rawValues = cloverContainer.dataset['cloverInlineFormValues'];
|
||||
const rawValues = cloverContainer.dataset["cloverInlineFormValues"];
|
||||
if (rawValues) {
|
||||
this.cloverFormData = JSON.parse(rawValues);
|
||||
}
|
||||
|
||||
this._setupCardFormatting(cloverContainer);
|
||||
this._setupTerminalToggle(cloverContainer);
|
||||
this._setupSurcharge(cloverContainer);
|
||||
|
||||
try {
|
||||
await this._loadCloverSDK(this.cloverFormData.is_test);
|
||||
this._initialiseCloverIframe(cloverContainer);
|
||||
} catch (error) {
|
||||
this._showCloverSdkError(
|
||||
cloverContainer,
|
||||
error?.message || _t("Could not initialise the Clover payment form."),
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
_detectCardBrand(number) {
|
||||
const num = (number || '').replace(/\D/g, '');
|
||||
if (num.length < 2) return 'other';
|
||||
const prefix2 = num.substring(0, 2);
|
||||
if (prefix2 === '34' || prefix2 === '37') return 'amex';
|
||||
if (num[0] === '4') return 'visa';
|
||||
const p2 = parseInt(prefix2, 10);
|
||||
if (p2 >= 51 && p2 <= 55) return 'mastercard';
|
||||
if (num.length >= 4) {
|
||||
const p4 = parseInt(num.substring(0, 4), 10);
|
||||
if (p4 >= 2221 && p4 <= 2720) return 'mastercard';
|
||||
async _loadCloverSDK(isTest) {
|
||||
const url = isTest ? CLOVER_SDK_URL_TEST : CLOVER_SDK_URL_PROD;
|
||||
await loadJS(url);
|
||||
if (typeof window.Clover === "undefined") {
|
||||
throw new Error(_t("Clover SDK failed to load."));
|
||||
}
|
||||
return 'other';
|
||||
},
|
||||
|
||||
_initialiseCloverIframe(container) {
|
||||
const data = this.cloverFormData;
|
||||
const apiAccessKey = data.public_key;
|
||||
const merchantId = data.merchant_id;
|
||||
|
||||
if (!apiAccessKey) {
|
||||
this._showCloverSdkError(
|
||||
container,
|
||||
_t("This Clover provider has no Public API Key (PAKMS) configured. Add it on the payment provider record before customers can pay online."),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const cloverConfig = { merchantId };
|
||||
if (data.locale) {
|
||||
cloverConfig.locale = data.locale;
|
||||
}
|
||||
// eslint-disable-next-line no-undef
|
||||
this.cloverInstance = new Clover(apiAccessKey, cloverConfig);
|
||||
this.cloverElements = this.cloverInstance.elements();
|
||||
|
||||
const styles = {
|
||||
input: {
|
||||
fontSize: "16px",
|
||||
fontFamily: "inherit",
|
||||
color: "#212529",
|
||||
padding: "10px 12px",
|
||||
},
|
||||
".invalid": { color: "#dc3545" },
|
||||
};
|
||||
|
||||
const mounts = [
|
||||
["CARD_NUMBER", "#clover-card-number", "#clover-card-number-errors"],
|
||||
["CARD_DATE", "#clover-card-date", "#clover-card-date-errors"],
|
||||
["CARD_CVV", "#clover-card-cvv", "#clover-card-cvv-errors"],
|
||||
["CARD_POSTAL_CODE", "#clover-card-postal-code", "#clover-card-postal-code-errors"],
|
||||
];
|
||||
|
||||
for (const [type, mountSelector, errorSelector] of mounts) {
|
||||
const element = this.cloverElements.create(type, styles);
|
||||
element.mount(mountSelector);
|
||||
element.addEventListener("change", (event) => {
|
||||
const errorEl = container.querySelector(errorSelector);
|
||||
if (!errorEl) return;
|
||||
if (event && event[type] && event[type].error) {
|
||||
errorEl.textContent = event[type].error;
|
||||
} else {
|
||||
errorEl.textContent = "";
|
||||
}
|
||||
if (type === "CARD_NUMBER" && event && event.CARD_NUMBER) {
|
||||
const brand = (event.CARD_NUMBER.brand || "").toLowerCase();
|
||||
if (brand) {
|
||||
const mapped = this._mapCloverBrandToOdoo(brand);
|
||||
if (mapped !== this._detectedCardType) {
|
||||
this._detectedCardType = mapped;
|
||||
if (mapped !== "other") {
|
||||
this._selectedCardType = mapped;
|
||||
}
|
||||
this._updateSurchargeDisplay(container);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
this.cloverMountedElements[type] = element;
|
||||
}
|
||||
},
|
||||
|
||||
_mapCloverBrandToOdoo(brand) {
|
||||
const map = {
|
||||
visa: "visa",
|
||||
mastercard: "mastercard",
|
||||
mc: "mastercard",
|
||||
amex: "amex",
|
||||
"american_express": "amex",
|
||||
discover: "other",
|
||||
"diners_club": "other",
|
||||
jcb: "other",
|
||||
unionpay: "other",
|
||||
unknown: "other",
|
||||
};
|
||||
return map[brand] || "other";
|
||||
},
|
||||
|
||||
_showCloverSdkError(container, message) {
|
||||
const banner = container.querySelector("#clover-sdk-error");
|
||||
const messageEl = container.querySelector("#clover-sdk-error-message");
|
||||
if (banner) banner.classList.remove("d-none");
|
||||
if (messageEl) messageEl.textContent = message;
|
||||
},
|
||||
|
||||
_setupSurcharge(container) {
|
||||
const surchargeConfig = this.cloverFormData.surcharge;
|
||||
if (!surchargeConfig || !surchargeConfig.enabled) return;
|
||||
|
||||
const cardTypeSection = container.querySelector('.o_clover_card_type_section');
|
||||
const surchargeNotice = container.querySelector('.o_clover_surcharge_notice');
|
||||
|
||||
const cardTypeSection = container.querySelector(".o_clover_card_type_section");
|
||||
if (cardTypeSection) {
|
||||
cardTypeSection.style.display = 'block';
|
||||
cardTypeSection.style.display = "block";
|
||||
}
|
||||
|
||||
const cardTypeRadios = container.querySelectorAll('input[name="clover_card_type"]');
|
||||
cardTypeRadios.forEach(radio => {
|
||||
radio.addEventListener('change', () => {
|
||||
cardTypeRadios.forEach((radio) => {
|
||||
radio.addEventListener("change", () => {
|
||||
this._selectedCardType = radio.value;
|
||||
this._updateSurchargeDisplay(container);
|
||||
});
|
||||
@@ -88,137 +185,82 @@ patch(PaymentForm.prototype, {
|
||||
const surchargeConfig = this.cloverFormData.surcharge;
|
||||
if (!surchargeConfig || !surchargeConfig.enabled) return;
|
||||
|
||||
const cardType = this._detectedCardType !== 'other'
|
||||
const cardType = this._detectedCardType !== "other"
|
||||
? this._detectedCardType
|
||||
: this._selectedCardType;
|
||||
|
||||
const rate = surchargeConfig[cardType] || surchargeConfig['other'] || 0;
|
||||
const amount = this.cloverFormData.minor_amount || 0;
|
||||
const rate = surchargeConfig[cardType] || surchargeConfig["other"] || 0;
|
||||
const minorAmount = this.cloverFormData.minor_amount || 0;
|
||||
|
||||
const baseAmount = amount / 100;
|
||||
const baseAmount = minorAmount / 100;
|
||||
const feeAmount = Math.round(baseAmount * rate) / 100;
|
||||
|
||||
const rateEl = container.querySelector('#clover_surcharge_rate');
|
||||
const amountEl = container.querySelector('#clover_surcharge_amount');
|
||||
const noticeEl = container.querySelector('.o_clover_surcharge_notice');
|
||||
const rateEl = container.querySelector("#clover_surcharge_rate");
|
||||
const amountEl = container.querySelector("#clover_surcharge_amount");
|
||||
const noticeEl = container.querySelector(".o_clover_surcharge_notice");
|
||||
|
||||
if (rateEl) rateEl.textContent = rate.toFixed(2);
|
||||
if (amountEl) amountEl.textContent = `$${feeAmount.toFixed(2)}`;
|
||||
if (noticeEl) {
|
||||
noticeEl.style.display = rate > 0 ? 'block' : 'none';
|
||||
noticeEl.style.display = rate > 0 ? "block" : "none";
|
||||
}
|
||||
|
||||
const radioToCheck = container.querySelector(
|
||||
`input[name="clover_card_type"][value="${cardType}"]`
|
||||
`input[name="clover_card_type"][value="${cardType}"]`,
|
||||
);
|
||||
if (radioToCheck && !radioToCheck.checked) {
|
||||
radioToCheck.checked = true;
|
||||
}
|
||||
},
|
||||
|
||||
_setupCardFormatting(container) {
|
||||
const cardInput = container.querySelector('#clover_card_number');
|
||||
if (cardInput) {
|
||||
cardInput.addEventListener('input', (e) => {
|
||||
let value = e.target.value.replace(/\D/g, '');
|
||||
let formatted = '';
|
||||
for (let i = 0; i < value.length && i < 16; i++) {
|
||||
if (i > 0 && i % 4 === 0) {
|
||||
formatted += ' ';
|
||||
}
|
||||
formatted += value[i];
|
||||
}
|
||||
e.target.value = formatted;
|
||||
|
||||
const detected = this._detectCardBrand(value);
|
||||
if (detected !== this._detectedCardType) {
|
||||
this._detectedCardType = detected;
|
||||
if (detected !== 'other') {
|
||||
this._selectedCardType = detected;
|
||||
}
|
||||
this._updateSurchargeDisplay(
|
||||
e.target.closest('.o_clover_payment_form')
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const expiryInput = container.querySelector('#clover_expiry');
|
||||
if (expiryInput) {
|
||||
expiryInput.addEventListener('input', (e) => {
|
||||
let value = e.target.value.replace(/\D/g, '');
|
||||
if (value.length >= 2) {
|
||||
value = value.substring(0, 2) + '/' + value.substring(2, 4);
|
||||
}
|
||||
e.target.value = value;
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_setupTerminalToggle(container) {
|
||||
const terminalCheckbox = container.querySelector('#clover_use_terminal');
|
||||
const terminalSelect = container.querySelector('#clover_terminal_select_wrapper');
|
||||
const cardFields = container.querySelectorAll(
|
||||
'#clover_card_number, #clover_expiry, #clover_cvv, #clover_cardholder'
|
||||
);
|
||||
const terminalCheckbox = container.querySelector("#clover_use_terminal");
|
||||
const terminalSelect = container.querySelector("#clover_terminal_select_wrapper");
|
||||
const iframeForm = container.querySelector(".o_clover_iframe_form");
|
||||
|
||||
if (!terminalCheckbox) {
|
||||
return;
|
||||
}
|
||||
|
||||
terminalCheckbox.addEventListener('change', () => {
|
||||
terminalCheckbox.addEventListener("change", () => {
|
||||
if (terminalCheckbox.checked) {
|
||||
if (terminalSelect) {
|
||||
terminalSelect.style.display = 'block';
|
||||
}
|
||||
cardFields.forEach(f => {
|
||||
f.closest('.mb-3').style.display = 'none';
|
||||
f.removeAttribute('required');
|
||||
});
|
||||
if (terminalSelect) terminalSelect.style.display = "block";
|
||||
if (iframeForm) iframeForm.style.display = "none";
|
||||
this._loadTerminals(container);
|
||||
} else {
|
||||
if (terminalSelect) {
|
||||
terminalSelect.style.display = 'none';
|
||||
}
|
||||
cardFields.forEach(f => {
|
||||
f.closest('.mb-3').style.display = 'block';
|
||||
if (f.id !== 'clover_cardholder') {
|
||||
f.setAttribute('required', 'required');
|
||||
}
|
||||
});
|
||||
if (terminalSelect) terminalSelect.style.display = "none";
|
||||
if (iframeForm) iframeForm.style.display = "block";
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
async _loadTerminals(container) {
|
||||
const selectEl = container.querySelector('#clover_terminal_select');
|
||||
const selectEl = container.querySelector("#clover_terminal_select");
|
||||
if (!selectEl || selectEl.options.length > 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const terminals = await rpc('/payment/clover/terminals', {
|
||||
const terminals = await rpc("/payment/clover/terminals", {
|
||||
provider_id: this.cloverFormData.provider_id,
|
||||
});
|
||||
|
||||
selectEl.innerHTML = '';
|
||||
selectEl.innerHTML = "";
|
||||
if (terminals && terminals.length > 0) {
|
||||
terminals.forEach(t => {
|
||||
const option = document.createElement('option');
|
||||
terminals.forEach((t) => {
|
||||
const option = document.createElement("option");
|
||||
option.value = t.id;
|
||||
option.textContent = `${t.name} (${t.status})`;
|
||||
selectEl.appendChild(option);
|
||||
});
|
||||
} else {
|
||||
const option = document.createElement('option');
|
||||
option.value = '';
|
||||
option.textContent = _t('No terminals available');
|
||||
const option = document.createElement("option");
|
||||
option.value = "";
|
||||
option.textContent = _t("No terminals available");
|
||||
selectEl.appendChild(option);
|
||||
}
|
||||
} catch {
|
||||
const option = document.createElement('option');
|
||||
option.value = '';
|
||||
option.textContent = _t('Failed to load terminals');
|
||||
const option = document.createElement("option");
|
||||
option.value = "";
|
||||
option.textContent = _t("Failed to load terminals");
|
||||
selectEl.appendChild(option);
|
||||
}
|
||||
},
|
||||
@@ -226,18 +268,18 @@ patch(PaymentForm.prototype, {
|
||||
// #=== PAYMENT FLOW ===#
|
||||
|
||||
async _initiatePaymentFlow(providerCode, paymentOptionId, paymentMethodCode, flow) {
|
||||
if (providerCode !== 'clover' || flow === 'token') {
|
||||
if (providerCode !== "clover" || flow === "token") {
|
||||
await super._initiatePaymentFlow(...arguments);
|
||||
return;
|
||||
}
|
||||
|
||||
const radio = document.querySelector('input[name="o_payment_radio"]:checked');
|
||||
const inlineForm = this._getInlineForm(radio);
|
||||
const useTerminal = inlineForm.querySelector('#clover_use_terminal');
|
||||
const useTerminal = inlineForm.querySelector("#clover_use_terminal");
|
||||
|
||||
if (useTerminal && useTerminal.checked) {
|
||||
const terminalId = inlineForm.querySelector('#clover_terminal_select').value;
|
||||
if (!terminalId) {
|
||||
const terminalSelect = inlineForm.querySelector("#clover_terminal_select");
|
||||
if (!terminalSelect || !terminalSelect.value) {
|
||||
this._displayErrorDialog(
|
||||
_t("Terminal Required"),
|
||||
_t("Please select a terminal device."),
|
||||
@@ -245,64 +287,27 @@ patch(PaymentForm.prototype, {
|
||||
this._enableButton();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
const validationError = this._validateCardInputs(inlineForm);
|
||||
if (validationError) {
|
||||
this._displayErrorDialog(
|
||||
_t("Invalid Card Details"),
|
||||
validationError,
|
||||
);
|
||||
this._enableButton();
|
||||
return;
|
||||
}
|
||||
} else if (!this.cloverInstance) {
|
||||
this._displayErrorDialog(
|
||||
_t("Payment form not ready"),
|
||||
_t("The Clover payment form has not finished loading. Please wait a moment and try again."),
|
||||
);
|
||||
this._enableButton();
|
||||
return;
|
||||
}
|
||||
|
||||
await super._initiatePaymentFlow(...arguments);
|
||||
},
|
||||
|
||||
_validateCardInputs(inlineForm) {
|
||||
const cardNumber = inlineForm.querySelector('#clover_card_number');
|
||||
const expiry = inlineForm.querySelector('#clover_expiry');
|
||||
const cvv = inlineForm.querySelector('#clover_cvv');
|
||||
|
||||
const cardDigits = cardNumber.value.replace(/\D/g, '');
|
||||
if (cardDigits.length < 13 || cardDigits.length > 19) {
|
||||
return _t("Please enter a valid card number.");
|
||||
}
|
||||
|
||||
const expiryValue = expiry.value;
|
||||
if (!/^\d{2}\/\d{2}$/.test(expiryValue)) {
|
||||
return _t("Please enter a valid expiry date (MM/YY).");
|
||||
}
|
||||
|
||||
const [month, year] = expiryValue.split('/').map(Number);
|
||||
if (month < 1 || month > 12) {
|
||||
return _t("Invalid expiry month.");
|
||||
}
|
||||
|
||||
const now = new Date();
|
||||
const expiryDate = new Date(2000 + year, month);
|
||||
if (expiryDate <= now) {
|
||||
return _t("Card has expired.");
|
||||
}
|
||||
|
||||
const cvvValue = cvv.value.replace(/\D/g, '');
|
||||
if (cvvValue.length < 3 || cvvValue.length > 4) {
|
||||
return _t("Please enter a valid CVV.");
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
async _processDirectFlow(providerCode, paymentOptionId, paymentMethodCode, processingValues) {
|
||||
if (providerCode !== 'clover') {
|
||||
if (providerCode !== "clover") {
|
||||
await super._processDirectFlow(...arguments);
|
||||
return;
|
||||
}
|
||||
|
||||
const radio = document.querySelector('input[name="o_payment_radio"]:checked');
|
||||
const inlineForm = this._getInlineForm(radio);
|
||||
const useTerminal = inlineForm.querySelector('#clover_use_terminal');
|
||||
const useTerminal = inlineForm.querySelector("#clover_use_terminal");
|
||||
|
||||
if (useTerminal && useTerminal.checked) {
|
||||
await this._processTerminalPayment(processingValues, inlineForm);
|
||||
@@ -313,36 +318,73 @@ patch(PaymentForm.prototype, {
|
||||
|
||||
_getSelectedCardType(inlineForm) {
|
||||
const checked = inlineForm.querySelector('input[name="clover_card_type"]:checked');
|
||||
return checked ? checked.value : 'other';
|
||||
return checked ? checked.value : "other";
|
||||
},
|
||||
|
||||
async _processCardPayment(processingValues, inlineForm) {
|
||||
const cardNumber = inlineForm.querySelector('#clover_card_number').value.replace(/\D/g, '');
|
||||
const expiry = inlineForm.querySelector('#clover_expiry').value;
|
||||
const cvv = inlineForm.querySelector('#clover_cvv').value;
|
||||
const cardholder = inlineForm.querySelector('#clover_cardholder').value;
|
||||
const cardType = this._detectedCardType !== 'other'
|
||||
if (!this.cloverInstance) {
|
||||
this._displayErrorDialog(
|
||||
_t("Payment form error"),
|
||||
_t("Clover SDK has not been initialised. Please reload the page."),
|
||||
);
|
||||
this._enableButton();
|
||||
return;
|
||||
}
|
||||
|
||||
const cardType = this._detectedCardType !== "other"
|
||||
? this._detectedCardType
|
||||
: this._getSelectedCardType(inlineForm);
|
||||
|
||||
const [expMonth, expYear] = expiry.split('/').map(Number);
|
||||
let result;
|
||||
try {
|
||||
result = await this.cloverInstance.createToken();
|
||||
} catch (error) {
|
||||
this._displayErrorDialog(
|
||||
_t("Card validation error"),
|
||||
error?.message || _t("Could not validate the card details."),
|
||||
);
|
||||
this._enableButton();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!result || result.errors) {
|
||||
const messages = [];
|
||||
if (result && result.errors) {
|
||||
Object.values(result.errors).forEach((value) => {
|
||||
if (typeof value === "string") {
|
||||
messages.push(value);
|
||||
} else if (value && value.error) {
|
||||
messages.push(value.error);
|
||||
}
|
||||
});
|
||||
}
|
||||
this._displayErrorDialog(
|
||||
_t("Invalid Card Details"),
|
||||
messages.join(" ") || _t("Please check the card details and try again."),
|
||||
);
|
||||
this._enableButton();
|
||||
return;
|
||||
}
|
||||
|
||||
const token = result.token;
|
||||
if (!token) {
|
||||
this._displayErrorDialog(
|
||||
_t("Tokenization Failed"),
|
||||
_t("Clover did not return a card token. Please try again."),
|
||||
);
|
||||
this._enableButton();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await rpc('/payment/clover/process_card', {
|
||||
const response = await rpc("/payment/clover/process_card", {
|
||||
reference: processingValues.reference,
|
||||
card_number: cardNumber,
|
||||
exp_month: expMonth,
|
||||
exp_year: 2000 + expYear,
|
||||
cvv: cvv,
|
||||
cardholder_name: cardholder,
|
||||
card_token: token,
|
||||
card_type: cardType,
|
||||
});
|
||||
|
||||
if (result.error) {
|
||||
this._displayErrorDialog(
|
||||
_t("Payment Failed"),
|
||||
result.error,
|
||||
);
|
||||
if (response.error) {
|
||||
this._displayErrorDialog(_t("Payment Failed"), response.error);
|
||||
this._enableButton();
|
||||
return;
|
||||
}
|
||||
@@ -358,25 +400,19 @@ patch(PaymentForm.prototype, {
|
||||
},
|
||||
|
||||
async _processTerminalPayment(processingValues, inlineForm) {
|
||||
const terminalId = inlineForm.querySelector('#clover_terminal_select').value;
|
||||
const terminalId = inlineForm.querySelector("#clover_terminal_select").value;
|
||||
const cardType = this._getSelectedCardType(inlineForm);
|
||||
|
||||
try {
|
||||
const result = await rpc('/payment/clover/send_to_terminal', {
|
||||
const result = await rpc("/payment/clover/send_to_terminal", {
|
||||
reference: processingValues.reference,
|
||||
terminal_id: parseInt(terminalId),
|
||||
card_type: cardType,
|
||||
});
|
||||
|
||||
if (result.error) {
|
||||
this._displayErrorDialog(
|
||||
_t("Terminal Payment Failed"),
|
||||
result.error,
|
||||
);
|
||||
this._displayErrorDialog(_t("Terminal Payment Failed"), result.error);
|
||||
this._enableButton();
|
||||
return;
|
||||
}
|
||||
|
||||
this._showTerminalWaitingScreen(processingValues, terminalId);
|
||||
} catch (error) {
|
||||
this._displayErrorDialog(
|
||||
@@ -388,7 +424,7 @@ patch(PaymentForm.prototype, {
|
||||
},
|
||||
|
||||
_showTerminalWaitingScreen(processingValues, terminalId) {
|
||||
const container = document.querySelector('.o_clover_payment_form');
|
||||
const container = document.querySelector(".o_clover_payment_form");
|
||||
if (container) {
|
||||
container.innerHTML = `
|
||||
<div class="text-center p-4">
|
||||
@@ -405,14 +441,12 @@ patch(PaymentForm.prototype, {
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
this._pollTerminalStatus(processingValues, terminalId);
|
||||
},
|
||||
|
||||
async _pollTerminalStatus(processingValues, terminalId, attempt = 0) {
|
||||
const maxAttempts = 60;
|
||||
const pollInterval = 3000;
|
||||
|
||||
if (attempt >= maxAttempts) {
|
||||
this._displayErrorDialog(
|
||||
_t("Timeout"),
|
||||
@@ -421,26 +455,26 @@ patch(PaymentForm.prototype, {
|
||||
this._enableButton();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await rpc('/payment/clover/terminal_status', {
|
||||
const result = await rpc("/payment/clover/terminal_status", {
|
||||
reference: processingValues.reference,
|
||||
terminal_id: parseInt(terminalId),
|
||||
});
|
||||
|
||||
const statusEl = document.getElementById('clover_terminal_status');
|
||||
|
||||
if (result.status === 'CLOSED' || result.status === 'CAPTURED'
|
||||
|| result.status === 'AUTH' || result.status === 'AUTHORIZED') {
|
||||
const statusEl = document.getElementById("clover_terminal_status");
|
||||
if (
|
||||
result.status === "CLOSED" || result.status === "CAPTURED"
|
||||
|| result.status === "AUTH" || result.status === "AUTHORIZED"
|
||||
) {
|
||||
if (statusEl) {
|
||||
statusEl.textContent = _t("Payment completed! Redirecting...");
|
||||
}
|
||||
window.location.href = processingValues.return_url;
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.status === 'DECLINED' || result.status === 'FAILED'
|
||||
|| result.status === 'FAIL') {
|
||||
if (
|
||||
result.status === "DECLINED" || result.status === "FAILED"
|
||||
|| result.status === "FAIL"
|
||||
) {
|
||||
this._displayErrorDialog(
|
||||
_t("Payment Declined"),
|
||||
_t("The payment was declined at the terminal."),
|
||||
@@ -448,11 +482,9 @@ patch(PaymentForm.prototype, {
|
||||
this._enableButton();
|
||||
return;
|
||||
}
|
||||
|
||||
if (statusEl) {
|
||||
statusEl.textContent = _t("Status: ") + (result.status || _t("Pending"));
|
||||
}
|
||||
|
||||
setTimeout(
|
||||
() => this._pollTerminalStatus(processingValues, terminalId, attempt + 1),
|
||||
pollInterval,
|
||||
|
||||
Reference in New Issue
Block a user