Updated April 2025 from working code on https://www.applesofgold.com
Add this code to the miva pay js (and possibly other) settings to disable the submit button. This code is from Nicholas Adkins. It was installed on https://www.applesofgold.com and appears to work great.
First see if the online version of the code is available, if so use it instead because it will be up to date: https://snippets.cacher.io/snippet/35096acc04a268d5a6ac
Miva Pay JS Settings
<input id="mivapay_response_xml" name="ResponseXML" type="hidden" value="" />
<input id="mivapay_response_signature" name="ResponseSignature" type="hidden" value="" />
<iframe id="mivapay_frame" name="MivaPay_Frame" src="" title="Payment Details" style="width: 100%; height: 0; border: 0 none; visibility: hidden;"></iframe>
<script type="text/javascript">
function stoi(value) {
return parseInt(value, 10);
}
function stoi_def_nonneg(value, default_value) {
value = stoi(value);
return ((isNaN(value) || (value < 0) || value === Number.POSITIVE_INFINITY || value === Number.NEGATIVE_INFINITY) ? default_value : value);
}
function AddEvent(obj, eventType, fn) {
if (obj.addEventListener) {
obj.addEventListener(eventType, fn, false);
return true;
}
else if (obj.attachEvent) {
var r = obj.attachEvent('on' + eventType, fn);
return r;
}
else {
return false;
}
}
function parentForm(element) {
while (element.nodeName.toLowerCase() !== 'form' && element.parentNode) {
element = element.parentNode;
}
if (element.nodeName.toLowerCase() !== 'form') {
return;
}
return element;
}
function MivaPay() {
var self = this;
this.submitting = false;
this.request_xml = '&mvtj:mivapay:paymentcardfields:request_xml;';
this.request_signature = '&mvtj:mivapay:paymentcardfields:request_signature;';
this.event_receivemessage = function (event) {
self.Receive_Message(event);
<mvt:comment>
#
# hide spinner when the miva pay stuff finally loads
#
</mvt:comment>
document.getElementById('mivapay-iframe-spinner').style.display='none';
};
AddEvent(window, 'message', this.event_receivemessage);
this.element_frame = document.getElementById('mivapay_frame');
this.element_response_xml = document.getElementById('mivapay_response_xml');
this.element_response_signature = document.getElementById('mivapay_response_signature');
if (this.element_form === document.getElementById('mivapay_form')) {
this.element_form_request_xml = document.getElementById('mivapay_form_request_xml');
this.element_form_request_signature = document.getElementById('mivapay_form_request_signature');
}
else {
this.element_form = document.createElement('form');
this.element_form.id = 'mivapay_form';
this.element_form.target = 'MivaPay_Frame';
this.element_form.method = 'POST';
this.element_form.action = '&mvtj:paymentsettings:mivapay:payment_url_sep;';
this.element_form_request_xml = document.createElement('input');
this.element_form_request_xml.id = 'mivapay_form_request_xml';
this.element_form_request_xml.type = 'hidden';
this.element_form_request_xml.name = 'RequestXML';
this.element_form_request_signature = document.createElement('input');
this.element_form_request_signature.id = 'mivapay_form_request_signature';
this.element_form_request_signature.type = 'hidden';
this.element_form_request_signature.name = 'RequestSignature';
this.element_form.appendChild(this.element_form_request_xml);
this.element_form.appendChild(this.element_form_request_signature);
document.body.appendChild(this.element_form);
}
this.element_form_request_xml.value = this.request_xml;
this.element_form_request_signature.value = this.request_signature;
this.element_form.submit();
}
MivaPay.prototype.Submit = function (callback) {
this.submit_callback = callback;
if (this.element_frame && !this.submitting) {
this.submitting = true;
this.element_frame.contentWindow.postMessage('submit', '&mvtj:paymentsettings:mivapay:base_url;');
this.EnableDisableButtons();
}
};
MivaPay.prototype.Receive_Message = function (event) {
var origin, response, signature;
origin = event.origin || event.originalEvent.origin;
this.submitting = false;
if ('&mvtj:paymentsettings:mivapay:base_url;/'.indexOf(origin + '/') !== 0) {
return;
}
if (event && (typeof event.data === 'string')) {
if (event.data.indexOf('dimensions:') === 0) {
if (this.element_frame) {
this.element_frame.style.width = '100%';
this.element_frame.style.height = stoi_def_nonneg(event.data.split(':')[2], 0) + 'px';
this.element_frame.style.visibility = 'visible';
}
}
else if (event.data.indexOf('response:') === 0) {
response = event.data.split(':')[1];
signature = event.data.split(':')[2];
this.element_response_xml.value = response;
this.element_response_signature.value = signature;
if (typeof this.submit_callback === 'function') {
this.submit_callback();
}
}
else if (event.data.indexOf('error:') === 0) {
this.element_response_xml.value = '';
this.element_response_signature.value = '';
this.EnableDisableButtons();
if (typeof this.submit_callback === 'function') {
this.submit_callback();
}
}
else {
this.EnableDisableButtons();
}
}
};
MivaPay.prototype.EnableDisableButtons = function () {
var self = this;
self.checkout_buttons = document.querySelector('[data-hook="opay-form"]')?.querySelectorAll('[type="submit"]') || [];
if (!self.checkout_buttons.length) {
return;
}
var i;
var enabled_disabled = self.submitting ? true : false;
for (i = 0; i < self.checkout_buttons.length; i++) {
const button = self.checkout_buttons[i];
if (enabled_disabled) {
button.disabled = true;
button.value = 'Processing';
if (button.nodeName === 'BUTTON') {
button.innerText = 'Processing';
} else {
button.value = 'Processing';
}
} else {
button.removeAttribute('disabled');
if (button.nodeName === 'BUTTON') {
button.innerText = 'Place Order';
} else {
button.value = 'Place Order';
}
}
}
}
var MivaPay = new MivaPay();
</script>
Miva Pay CSS
{
"selector": "html",
"properties": {
"box-sizing": "border-box",
"height": "100%",
"min-height": "100%",
"font-family": "-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif",
"font-size": "13px",
"-webkit-font-smoothing": "antialiased",
"-moz-osx-font-smoothing": "grayscale",
"line-height": "1.5",
"-webkit-text-size-adjust": "100%",
"-ms-text-size-adjust": "100%",
"color": "#111",
"-webkit-tap-highlight-color": "rgba(0, 0, 0, 0)",
"touch-action": "manipulation"
}
},
{
"selector": "body",
"properties": {
"margin": "0"
}
},
{
"selector": ".form_row.invalid input[type=\"text\"],.form_row.invalid select",
"properties": {
"border-color": "#b13138"
}
},
{
"selector": ".form_row.invalid label",
"properties": {
"color": "#b13138"
}
},
{
"selector": ".bold",
"properties": {
"font-weight": "600"
}
},
{
"selector": "*, *::before, *::after ",
"properties": {
"box-sizing": "inherit"
}
},
{
"selector": "#document-sizer",
"properties": {
"min-width": "100%"
}
},
{
"selector": "#payment-fields",
"properties": {
"display": "flex",
"flex-wrap": "wrap",
"gap": "1rem",
"justify-content": "space-between",
"padding": "0 0.25rem",
"width": "100%"
}
},
{
"selector": "#mivapay_form",
"properties": {
"position": "relative",
"height": "auto",
"clear": "both",
"overflow": "auto",
"zoom": "1"
}
},
{
"selector": ".form_row",
"properties": {
"display": "flex",
"flex": "1",
"flex-wrap": "wrap",
"gap": "0 0.5rem"
}
},
{
"selector": ".form_row label",
"properties": {
"display": "inline-flex",
"flex-basis": "100%",
"font-size": "13px",
"margin-bottom": "0.5em"
}
},
{
"selector": "input[type='text']",
"properties": {
"appearance": "none",
"background-color": "#fff",
"border": "1px solid #ccc",
"border-radius": "0.25rem",
"display": "inline-block",
"flex": "1",
"font-family": "inherit",
"font-size": "13px",
"line-height": "1",
"margin-bottom": "0.25em",
"padding": "calc(1.23em - 1px) calc(1.7em - 1px)"
}
},
{
"selector": "select",
"properties": {
"appearance": "none",
"background-color": "#fff",
"border": "1px solid #ccc",
"border-radius": "0.25rem",
"box-shadow": "0 0 0 0 rgba(17, 17, 17, 0.2)",
"cursor": "pointer",
"flex": "1",
"font-family": "inherit",
"margin-bottom": "0.25em",
"padding": "calc(1.23em - 1px) calc(1.7em - 1px)",
"transition": "box-shadow 0.25s ease, border-color 0.25s ease"
}
},
{
"selector": "input[type='text']:focus:not(:focus-visible), select:focus:not(:focus-visible)",
"properties": {
"outline": "none"
}
},
{
"selector": "input[type='text']:focus-visible, select:focus-visible",
"properties": {
"outline": "2px solid #3a58fc",
"outline-offset": "1px"
}
},
{
"selector": ".mvp_exp_date_container",
"properties": {
"display": "grid",
"grid-template-columns": "repeat(2, 1fr)"
}
},
{
"selector": ".mvp_exp_date_container label",
"properties": {
"grid-column": "span 2"
}
},
{
"selector": ".mvp_cvv_container",
"properties": {
"flex": "0"
}
},
{
"selector": "#mvp_addressfields_container.mvp_addressfields_container",
"properties": {
"display": "flex",
"width": "100%",
"align-items": "center",
"flex-direction": "column"
}
},
{
"selector": "#mvp_addressfields_container.mvp_addressfields_hide",
"properties": {
"display": "none"
}
},
{
"selector": ".mvp_storetoken_container",
"properties": {
"justify-content": "flex-start"
}
},
{
"selector": ".mvp_storetoken",
"properties": {
"filter": "grayscale(1)",
"margin-right": "1em"
}
},
{
"selector": "::placeholder",
"properties": {
"line-height": "normal"
}
},
{
"at-rule": "media",
"media": "screen and (max-width: 747px)",
"selectors":
[
{
"selector": ".mvp_exp_date_container",
"properties": {
"flex-basis": "auto"
}
}
]
},
{
"at-rule": "media",
"media": "screen and (min-width: 748px)",
"selectors":
[
{
"selector": "#mvp_cardtype_container",
"properties": {
"float": "none"
}
}
]
}
Add to cart and related HTML - this isn't the whole thing, just shows you certain aspects of ids and whatnot. I think stock html probably works fine.
<form id="js-opay-form" data-hook="opay-form" method="post" action="&mvte:url;">
<fieldset>
<legend>&mvt:page:name;</legend>
<mvt:if expr="ISNULL l.settings:payment:payment_url">
<input type="hidden" name="Action" value="AUTH" />
</mvt:if>
<mvt:item name="payment" />
<input data-hook="payment-method" type="hidden" name="PaymentMethod" value="&mvte:global:PaymentMethod;">
<input type="hidden" name="SplitPaymentData" value="&mvte:global:SplitPaymentData;" />
<p class="c-heading-foxtrot">Payment Information</p>
<mvt:comment>
#
# add a spinner that takes up the space until mivapay payment fields show up
#
</mvt:comment>
<div class="text-center" id="mivapay-iframe-spinner" style="margin-top: 30px; margin-bottom: 30px;">
<div class="spinner-border spinner-border-sm text-secondary" style="height: 3em; width: 3em;" role="status" >
<span class="sr-only">Loading...</span>
</div>
</div>
<mvt:if expr="NOT ISNULL l.settings:payment:message">
<p class="x-messages x-messages--info">&mvt:payment:message;</p>
</mvt:if>
<mvt:if expr="l.settings:paymentsettings:mivapay:enabled AND ( l.settings:mivapay:paymentcardtype:id OR l.settings:mivapay:paymentcard:id )">
<div class="c-form-list">
<div class="c-form-list__item c-form-list__item--full">
<div class="t-payment-form t-&mvte:global:payment_module_class;">
<div class="t-payment-form__heading">
<span class="u-text-medium">
Payment Method: <span data-hook="payment-method-display">&mvte:payment:desc;</span>
<a class="u-text-regular" href="&mvte:urls:OSEL:secure;" title="Edit Payment Method">Edit</a>
</span>
<span class="c-heading--subheading--x-small u-icon-secure" aria-hidden="true"></span>
</div>
<div class="t-payment-form__fields">
<mvt:item name="mivapay"/>
</div>
</div>
</div>
</div>
etc...
</form>
https://www.scotsscripts.com/mvblog/mivapay-submit-payment-once-and-disable-button.html