File "klarna-checkout-for-woocommerce.js"

Full Path: /home/siazco/grocery.siazco.se/wp-content/plugins/klarna-checkout-for-woocommerce/assets/js/klarna-checkout-for-woocommerce.js
File size: 22.46 KB
MIME-type: text/plain
Charset: utf-8

/* global kco_params */
jQuery( function ( $ ) {
	// Check if we have params.
	if ( "undefined" === typeof kco_params ) {
		return false
	}
	var kco_wc = {
		bodyEl: $( "body" ),
		checkoutFormSelector: $( "form.checkout" ),

		// Payment method.
		paymentMethodEl: $( 'input[name="payment_method"]' ),
		paymentMethod: "",
		selectAnotherSelector: "#klarna-checkout-select-other",

		// Form fields.
		shippingUpdated: false,
		validation: false,

		preventPaymentMethodChange: false,

		timeout: null,
		interval: null,

		// True or false if we need to update the Klarna order. Set to false on initial page load.
		klarnaUpdateNeeded: false,
		shippingEmailExists: false,
		shippingPhoneExists: false,

		/**
		 * Triggers on document ready.
		 */
		documentReady: function () {
			kco_wc.log( kco_params )
			if ( 0 < kco_wc.paymentMethodEl.length ) {
				kco_wc.paymentMethod = kco_wc.paymentMethodEl.filter( ":checked" ).val()
			} else {
				kco_wc.paymentMethod = "kco"
			}

			if ( "kco" === kco_wc.paymentMethod ) {
				$( "#ship-to-different-address-checkbox" ).prop( "checked", true )
			}

			if ( ! kco_params.pay_for_order ) {
				kco_wc.moveExtraCheckoutFields()
				kco_wc.updateShipping( false )
			}

			// Handle events when the Klarna modal is closed when the purchase is not complete.
			// Klarna does not provide an event listener for when the modal is closed.
			let modalWasOpen = false
			const modalClassName = "klarna-checkout-fso-open"
			const observer = new MutationObserver( function ( mutations ) {
				mutations.forEach( function ( mutation ) {
					if ( "attributes" === mutation.type && "class" === mutation.attributeName ) {
						if ( ! $( "html" ).hasClass( modalClassName ) ) {
							if ( modalWasOpen ) {
								// Wait for the Klarna modal to disappear before checking if any errors are found.
								const noticeClassName = kco_params.pay_for_order
									? "div.woocommerce-notices-wrapper"
									: "form.checkout"
								const notices = $( noticeClassName )

								// Scroll to error notices if found.
								if ( notices.length && notices.find( ".woocommerce-error" ).length ) {
									$( "html, body" ).animate(
										{
											scrollTop: notices.offset().top - 100,
										},
										1000,
									)
								}

								// Unlock the order review table and checkout form.
								kco_wc.unblock()
								modalWasOpen = false
							}
						} else {
							modalWasOpen = true
						}
					}
				} )
			} )

			observer.observe( document.querySelector( "html" ), { attributes: true, attributeFilter: [ "class" ] } )
		},

		/**
		 * Unblock the checkout form and order review.
		 */
		unblock: function () {
			kco_wc.checkoutFormSelector.removeClass( "processing" )
			$( ".woocommerce-checkout-review-order-table" ).unblock()
			$( kco_wc.checkoutFormSelector ).unblock()
		},

		/**
		 * Resumes the Klarna Iframe
		 * @param {boolean} autoResumeBool
		 */
		kcoSuspend: function ( autoResumeBool ) {
			if ( window._klarnaCheckout && ! kco_wc.validation ) {
				window._klarnaCheckout( function ( api ) {
					api.suspend( {
						autoResume: {
							enabled: autoResumeBool,
						},
					} )
				} )
			}
		},

		/**
		 * Resumes the KCO Iframe
		 */
		kcoResume: function () {
			if ( window._klarnaCheckout && ! kco_wc.validation ) {
				window._klarnaCheckout( function ( api ) {
					api.resume()
				} )
			}
		},

		/**
		 * When the customer changes from KCO to other payment methods.
		 * @param {Event} e
		 */
		changeFromKco: function ( e ) {
			e.preventDefault()

			$( kco_wc.checkoutFormSelector ).block( {
				message: null,
				overlayCSS: {
					background: "#fff",
					opacity: 0.6,
				},
			} )

			$.ajax( {
				type: "POST",
				dataType: "json",
				data: {
					kco: false,
					nonce: kco_params.change_payment_method_nonce,
				},
				url: kco_params.change_payment_method_url,
				success: function ( data ) {},
				error: function ( data ) {},
				complete: function ( data ) {
					kco_wc.log( data.responseJSON )
					window.location.href = data.responseJSON.data.redirect
				},
			} )
		},

		/**
		 * When the customer changes to KCO from other payment methods.
		 */
		maybeChangeToKco: function () {
			if ( ! kco_wc.preventPaymentMethodChange ) {
				kco_wc.log( $( this ).val() )

				if ( "kco" === $( this ).val() ) {
					$( ".woocommerce-info" ).remove()

					$( kco_wc.checkoutFormSelector ).block( {
						message: null,
						overlayCSS: {
							background: "#fff",
							opacity: 0.6,
						},
					} )

					$.ajax( {
						type: "POST",
						data: {
							kco: true,
							nonce: kco_params.change_payment_method_nonce,
						},
						dataType: "json",
						url: kco_params.change_payment_method_url,
						success: function ( data ) {},
						error: function ( data ) {},
						complete: function ( data ) {
							kco_wc.log( data.responseJSON )
							window.location.href = data.responseJSON.data.redirect
						},
					} )
				}
			}
		},

		/**
		 * Moves all non standard fields to the extra checkout fields.
		 */
		moveExtraCheckoutFields: function () {
			// Move order comments.
			$( ".woocommerce-additional-fields" ).appendTo( "#kco-extra-checkout-fields" )
			var form = $( 'form[name="checkout"] input, form[name="checkout"] select, textarea' )
			var checkout_add_ons_moved = false
			for ( i = 0; i < form.length; i++ ) {
				var name = form[ i ].name.replace( "[]", "\\[\\]" ) // Escape any empty "array" keys to prevent errors.
				// Check if field is inside the order review.
				if ( $( "table.woocommerce-checkout-review-order-table" ).find( form[ i ] ).length ) {
					continue
				}

				// Check if this is a standard field.
				if ( -1 === $.inArray( name, kco_params.standard_woo_checkout_fields ) ) {
					// This is not a standard Woo field, move to our div.
					if (
						"wc_checkout_add_ons" ===
						$( "p#" + name + "_field" )
							.parent()
							.attr( "id" )
					) {
						// Check if this is an add on field.
						if ( ! checkout_add_ons_moved ) {
							checkout_add_ons_moved = true
							$( "div#wc_checkout_add_ons" ).appendTo( "#kco-extra-checkout-fields" )
						}
					} else if ( 0 < $( "p#" + name + "_field" ).length ) {
						if ( name === "shipping_phone" ) {
							kco_wc.shippingPhoneExists = true
						}
						if ( name === "shipping_email" ) {
							kco_wc.shippingEmailExists = true
						}
						$( "p#" + name + "_field" ).appendTo( "#kco-extra-checkout-fields" )
					} else {
						$( 'input[name="' + name + '"]' )
							.closest( "p" )
							.appendTo( "#kco-extra-checkout-fields" )
					}
				}
			}
		},

		/**
		 * Display Shipping Price in order review if Display shipping methods in iframe settings is active.
		 */
		maybeDisplayShippingPrice: function () {
			// Check if we already have set the price. If we have, return.
			if ( $( ".kco-shipping" ).length ) {
				return
			}

			if (
				"kco" === kco_wc.paymentMethod &&
				"yes" === kco_params.shipping_methods_in_iframe &&
				"no" === kco_params.is_confirmation_page
			) {
				if ( $( '#shipping_method input[type="radio"]' ).length > 1 ) {
					// Multiple shipping options available.
					$( '#shipping_method input[type="radio"]:checked' ).each( function () {
						var idVal = $( this ).attr( "id" )
						var shippingPrice = $( 'label[for="' + idVal + '"]' ).text()
						$( ".woocommerce-shipping-totals td" ).html( shippingPrice )
						$( ".woocommerce-shipping-totals td" ).addClass( "kco-shipping" )
					} )
				} else if ( $( '#shipping_method input[type="hidden"]' ).length === 1 ) {
					// Only one shipping option available.
					var idVal = $( '#shipping_method input[name="shipping_method[0]"]' ).attr( "id" )
					var shippingPrice = $( 'label[for="' + idVal + '"]' ).text()
					$( ".woocommerce-shipping-totals td" ).html( shippingPrice )
					$( ".woocommerce-shipping-totals td" ).addClass( "kco-shipping" )
				} else {
					// No shipping method is available.
					$( ".woocommerce-shipping-totals td" ).html( kco_params.no_shipping_message )
				}
			}
		},

		/**
		 * Updates the cart in case of a change in product quantity.
		 */
		updateCart: function () {
			kco_wc.kcoSuspend( true )
			$.ajax( {
				type: "POST",
				url: kco_params.update_cart_url,
				data: {
					checkout: $( "form.checkout" ).serialize(),
					nonce: kco_params.update_cart_nonce,
				},
				dataType: "json",
				success: function ( data ) {},
				error: function ( data ) {},
				complete: function ( data ) {
					$( "body" ).trigger( "update_checkout" )
					kco_wc.kcoResume()
				},
			} )
		},

		/**
		 * Gets the Klarna order and starts the order submission
		 */
		getKlarnaOrder: function () {
			console.log( "getKlarnaOrder" )
			kco_wc.preventPaymentMethodChange = true
			$( ".woocommerce-checkout-review-order-table" ).block( {
				message: null,
				overlayCSS: {
					background: "#fff",
					opacity: 0.6,
				},
			} )

			var ajax = $.ajax( {
				type: "POST",
				url: kco_params.get_klarna_order_url,
				data: {
					nonce: kco_params.get_klarna_order_nonce,
				},
				dataType: "json",
				success: function ( data ) {
					kco_wc.setCustomerData( data.data )

					// Check Terms checkbox, if it exists.
					if ( 0 < $( "form.checkout #terms" ).length ) {
						$( "form.checkout #terms" ).prop( "checked", true )
					}
					console.log( "success" )
				},
				error: function ( data ) {
					console.log( "error" )
					window.location.reload()
				},
				complete: function ( data ) {},
			} )

			return ajax
		},

		/**
		 * Sets the customer data.
		 * @param {array} data
		 */
		setCustomerData: function ( data ) {
			kco_wc.log( "setCustomerData", data )

			if ( typeof data !== "object" || data === null ) {
				return
			}

			if ( "billing_address" in data && data.billing_address !== null ) {
				// Billing fields.
				$( "#billing_first_name" ).val(
					"given_name" in data.billing_address ? data.billing_address.given_name : "",
				)
				$( "#billing_last_name" ).val(
					"family_name" in data.billing_address ? data.billing_address.family_name : "",
				)
				$( "#billing_company" ).val(
					"organization_name" in data.billing_address ? data.billing_address.organization_name : "",
				)
				$( "#billing_address_1" ).val(
					"street_address" in data.billing_address ? data.billing_address.street_address : "",
				)
				$( "#billing_address_2" ).val(
					"street_address2" in data.billing_address ? data.billing_address.street_address2 : "",
				)
				$( "#billing_city" ).val( "city" in data.billing_address ? data.billing_address.city : "" )
				$( "#billing_postcode" ).val(
					"postal_code" in data.billing_address ? data.billing_address.postal_code : "",
				)
				$( "#billing_phone" ).val( "phone" in data.billing_address ? data.billing_address.phone : "" )
				$( "#billing_email" ).val( "email" in data.billing_address ? data.billing_address.email : "" )
				$( "#billing_country" ).val(
					"country" in data.billing_address ? data.billing_address.country.toUpperCase() : "",
				)
				$( "#billing_state" ).val( "region" in data.billing_address ? data.billing_address.region : "" )
				// Trigger changes
				$( "#billing_email" ).change()
				$( "#billing_email" ).blur()
			}

			if ( "shipping_address" in data && data.shipping_address !== null ) {
				$( "#ship-to-different-address-checkbox" ).prop( "checked", true )

				// Shipping fields.
				$( "#shipping_first_name" ).val(
					"given_name" in data.shipping_address ? data.shipping_address.given_name : "",
				)
				$( "#shipping_last_name" ).val(
					"family_name" in data.shipping_address ? data.shipping_address.family_name : "",
				)
				$( "#shipping_company" ).val(
					"organization_name" in data.shipping_address ? data.shipping_address.organization_name : "",
				)
				$( "#shipping_address_1" ).val(
					"street_address" in data.shipping_address ? data.shipping_address.street_address : "",
				)
				$( "#shipping_address_2" ).val(
					"street_address2" in data.shipping_address ? data.shipping_address.street_address2 : "",
				)
				$( "#shipping_city" ).val( "city" in data.shipping_address ? data.shipping_address.city : "" )
				$( "#shipping_postcode" ).val(
					"postal_code" in data.shipping_address ? data.shipping_address.postal_code : "",
				)
				$( "#shipping_country" ).val(
					"country" in data.shipping_address ? data.shipping_address.country.toUpperCase() : "",
				)
				$( "#shipping_state" ).val( "region" in data.shipping_address ? data.shipping_address.region : "" )

				// extra shipping fields (email, phone).
				if ( kco_wc.shippingEmailExists === true && $( "#shipping_email" ) ) {
					$( "#shipping_email" ).val( "email" in data.shipping_address ? data.shipping_address.email : "" )
				}
				if ( kco_wc.shippingPhoneExists === true && $( "#shipping_phone" ) ) {
					$( "#shipping_phone" ).val( "phone" in data.shipping_address ? data.shipping_address.phone : "" )
				}
			}
		},

		/**
		 * Checks the URL for the hashtag.
		 * @param {function} callback
		 */
		checkUrl: function ( callback ) {
			if ( window.location.hash ) {
				var currentHash = window.location.hash
				if ( -1 < currentHash.indexOf( "#klarna-success" ) ) {
					kco_wc.logToFile( "klarna-success hashtag detected in URL." )
					callback( { should_proceed: true } )
					// Clear the interval.
					clearInterval( kco_wc.interval )
					// Remove the timeout.
					clearTimeout( kco_wc.timeout )
					// Remove the processing class from the form.
					kco_wc.unblock()
				}
			}
		},

		/**
		 * Fails the order with Klarna on a checkout error and timeout.
		 * @param {function} callback
		 * @param {string} event
		 */
		failOrder: function ( event, error_message, callback ) {
			callback( { should_proceed: false, message: $( error_message ).text().trim() } )
			kco_wc.validation = false
			var className = kco_params.pay_for_order ? "div.woocommerce-notices-wrapper" : "form.checkout"
			// Update the checkout and re-enable the form.
			$( "body" ).trigger( "update_checkout" )

			// Print error messages, and trigger checkout_error, and scroll to notices.
			$( ".woocommerce-NoticeGroup-checkout, .woocommerce-error, .woocommerce-message" ).remove()
			$( className ).prepend(
				'<div class="woocommerce-NoticeGroup woocommerce-NoticeGroup-checkout">' + error_message + "</div>",
			) // eslint-disable-line max-len
			$( className ).removeClass( "processing" ).unblock()
			$( className ).find( ".input-text, select, input:checkbox" ).trigger( "validate" ).blur()
			$( document.body ).trigger( "checkout_error", [ error_message ] )
		},

		/**
		 * Logs the message to the klarna checkout log in WooCommerce.
		 * @param {string} message
		 */
		logToFile: function ( message ) {
			$.ajax( {
				url: kco_params.log_to_file_url,
				type: "POST",
				dataType: "json",
				data: {
					message: message,
					nonce: kco_params.log_to_file_nonce,
				},
			} )
		},

		/**
		 * Logs messages to the console.
		 * @param {string} messages
		 */
		log: function ( ...messages ) {
			if ( kco_params.logging ) {
				console.log( messages )
			}
		},

		updateShipping: function ( data ) {
			kco_wc.kcoSuspend( true )
			$( "#kco_shipping_data" ).val( JSON.stringify( data ) )
			$( "body" ).trigger( "kco_shipping_option_changed", [ data ] )
			$( "body" ).trigger( "update_checkout" )
		},

		convertCountry: function ( country ) {
			return Object.keys( kco_params.countries ).find( ( key ) => kco_params.countries[ key ] === country )
		},

		placeKlarnaOrder: function ( callback ) {
			kco_wc.getKlarnaOrder().done( function ( response ) {
				if ( response.success ) {
					$( ".woocommerce-checkout-review-order-table" ).block( {
						message: null,
						overlayCSS: {
							background: "#fff",
							opacity: 0.6,
						},
					} )
					$.ajax( {
						type: "POST",
						url: kco_params.submit_order,
						data: $( "form.checkout" ).serialize(),
						dataType: "json",
						success: function ( data ) {
							try {
								if ( "success" === data.result ) {
									let email = "N/A"
									if (
										"billing_address" in response.data &&
										"email" in response.data.billing_address
									) {
										email = response.data.shipping_address.email
									} else if (
										"shipping_address" in response.data &&
										"email" in response.data.shipping_address
									) {
										email = response.data.shipping_address.email
									}

									kco_wc.logToFile(
										"Successfully placed order [" +
											email +
											']. Sending "should_proceed: true" to Klarna',
									)
									callback( { should_proceed: true } )
								} else {
									throw "Result failed"
								}
							} catch ( err ) {
								if ( data.messages ) {
									kco_wc.logToFile( "Checkout error | " + data.messages )
									kco_wc.failOrder( "submission", data.messages, callback )
								} else {
									kco_wc.logToFile( "Checkout error | No message" )
									kco_wc.failOrder(
										"submission",
										'<div class="woocommerce-error">Checkout error</div>',
										callback,
									)
								}
							}
						},
						error: function ( data ) {
							try {
								kco_wc.logToFile( "AJAX error | " + JSON.stringify( data ) )
							} catch ( e ) {
								kco_wc.logToFile( "AJAX error | Failed to parse error message." )
							}
							kco_wc.failOrder(
								"ajax-error",
								'<div class="woocommerce-error">Internal Server Error</div>',
								callback,
							)
						},
					} )
				} else {
					kco_wc.failOrder(
						"get_order",
						'<div class="woocommerce-error">' + "Failed to get the order from Klarna." + "</div>",
						callback,
					)
				}
				kco_wc.validation = false
			} )
		},

		update_checkout: $.Callbacks( "unique stopOnFalse" ),
		/**
		 * Initiates the script.
		 */
		init: function () {
			/* If this is order received page, abort ASAP to render the snippet faster. */
			if ( "yes" === kco_params.is_order_received_page ) {
				return
			}

			$( document ).ready( kco_wc.documentReady )
			kco_wc.bodyEl.on( "update_checkout", function () {
				kco_wc.kcoSuspend( true )
			} )
			kco_wc.bodyEl.on( "updated_checkout", kco_wc.kcoResume )
			kco_wc.bodyEl.on( "updated_checkout", kco_wc.maybeDisplayShippingPrice )
			kco_wc.bodyEl.on( "change", "input.qty", kco_wc.updateCart )
			kco_wc.bodyEl.on( "change", 'input[name="payment_method"]', kco_wc.maybeChangeToKco )
			kco_wc.bodyEl.on( "click", kco_wc.selectAnotherSelector, kco_wc.changeFromKco )
			kco_wc.update_checkout.add( function () {
				kco_wc.log( "fire update_checkout" )
				$( "form.checkout" ).trigger( "update_checkout" )

				// stopOnFalse ensure this callback won't be triggered more than once in a quick succession.
				return false
			} )

			if ( "function" === typeof window._klarnaCheckout ) {
				window._klarnaCheckout( function ( api ) {
					api.on( {
						shipping_address_change: function ( data ) {
							// The shipping_address_change event is triggered when Checkout has detected a complete and valid shipping address for the customer. The shipping address will always be the same as billing address, unless a separate shipping address has been provided by the customer.
							kco_wc.log( "shipping_address_change", data )

							var country = kco_wc.convertCountry( data.country.toUpperCase() )

							// 'billing' => Default to customer billing address
							// 'shipping' => Default to customer shipping address
							// 'billing_only' => Force shipping to the customer billing address only.
							if ( "billing_only" === kco_params.woocommerce_ship_to_destination ) {
								"given_name" in data && $( "#billing_first_name" ).val( data.given_name )
								"family_name" in data && $( "#billing_last_name" ).val( data.family_name )
								"postal_code" in data && $( "#billing_postcode" ).val( data.postal_code )
								"country" in data && $( "#billing_country" ).val( country )
								"email" in data && $( "#billing_email" ).val( data.email )
								$( "#billing_country" ).change()
								$( "#billing_email" ).change()
								$( "#billing_email" ).blur()

								kco_wc.update_checkout.fire()
							} else {
								$( "#ship-to-different-address-checkbox" ).prop( "checked", true )
								$( "#ship-to-different-address-checkbox" ).change()
								$( "#ship-to-different-address-checkbox" ).blur()
								"given_name" in data && $( "#shipping_first_name" ).val( data.given_name )
								"family_name" in data && $( "#shipping_last_name" ).val( data.family_name )
								"postal_code" in data && $( "#shipping_postcode" ).val( data.postal_code )
								"country" in data && $( "#shipping_country" ).val( country )
								$( "#shipping_country" ).change()

								$( "form.checkout" ).trigger( "update_checkout" )
							}
						},
						billing_address_change: function ( data ) {
							// The billing_address_change event is triggered when Checkout has detected a complete and valid billing address for the customer.
							kco_wc.log( "billing_address_change", data )

							var country = kco_wc.convertCountry( data.country.toUpperCase() )
							"given_name" in data && $( "#billing_first_name" ).val( data.given_name )
							"family_name" in data && $( "#billing_last_name" ).val( data.family_name )
							"postal_code" in data && $( "#billing_postcode" ).val( data.postal_code )
							"country" in data && $( "#billing_country" ).val( country )
							"email" in data && $( "#billing_email" ).val( data.email )
							$( "#billing_country" ).change()
							$( "#billing_email" ).change()
							$( "#billing_email" ).blur()

							kco_wc.update_checkout.fire()
						},
						change: function ( data ) {
							// The change event is triggered when the user changes postal code, country or email in their billing address. It is also triggered for given/family name except in the AT & DE markets.
							kco_wc.log( "change", data )
						},
						order_total_change: function ( data ) {
							kco_wc.log( "order_total_change", data )
						},
						shipping_option_change: function ( data ) {
							kco_wc.log( "shipping_option_change", data )
							kco_wc.log( data )
							kco_wc.updateShipping( data )
						},
						can_not_complete_order: function ( data ) {
							kco_wc.log( "can_not_complete_order", data )
						},
						validation_callback: function ( data, callback ) {
							kco_wc.validation = true
							kco_wc.logToFile( "validation_callback from Klarna triggered" )
							if ( kco_params.pay_for_order ) {
								callback( { should_proceed: true } )
							} else {
								kco_wc.placeKlarnaOrder( callback )
							}
						},
						customer: function ( customer ) {
							let customer_type = "person" === customer.type ? "b2c" : "b2b"
							$.ajax( {
								url: kco_params.customer_type_changed_url,
								type: "POST",
								data: {
									customer_type,
								},
							} )
						},
					} )
				} )
			}
		},
	}

	kco_wc.init()
} )