MIVAPAY: Submit Payment Once and Disable Button

Miva Knowledge Base
Want to start an online store? We work with you from start to finish, from commerce platform to design to SEO.
Experience counts, and we have a lot.
Important Notice: This information is for internal reference only. Use at your own risk.

MIVAPAY: Submit Payment Once and Disable Button

Scot Ranney • December 05, 2024


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

mvkb_mivapay mvkb_opay