UNPKG

@nuskin/ns-checkout

Version:

Ecomm3 Checkout module

537 lines (467 loc) • 22 kB
import {UserService, BuyerCookieService} from '@nuskin/ns-account'; import {LocalStorageOrderService, ShopContextService, CartService, AdrService, PaymentType, CheckoutApi } from '@nuskin/ns-shop'; import {ConfigService, RunConfigService, StringService, StoreFrontUtil, PersonalOfferStorageService, SponsorStorageService, WaitingRoomService, FirebaseUtil, events, StoreFrontSponsorStorageService} from '@nuskin/ns-util'; import {StringUtil, ShippingAddressUtil} from '@nuskin/ns-checkout-common'; import {MySiteRestService} from '@nuskin/my-site-api'; import '../app.js'; 'use strict'; angular.module('checkout').controller('OrderConfirmationCtrl', ['$scope','$rootScope','$location','$http','nsUtil', 'tdcService', 'nsADRType', 'nsExternalPayment', function($scope, $rootScope, $location, $http, nsUtil, tdcService, nsADRType, nsExternalPayment) { const SHIPIMMEDIATE = "shipImmediate"; const ORDER = "order"; const ADR = "adrFuture"; const STOREFRONT_HOME = "mysite/mysite-home.html"; const SPONSOR_ID = SponsorStorageService.getSponsor(); let personalOfferLanding = ""; let isPersonalOffer = nuskin.util.isPersonalOffer(); if (isPersonalOffer) { personalOfferLanding = PersonalOfferStorageService.getPersonalOffer().landingPageURL; } WaitingRoomService.deleteAllQueueTickets(); $scope.tdc = tdcService.getTdc('checkout'); $scope.contentLoaded = false; $scope.runConfig = RunConfigService.getRunConfig(); $scope.buyer = BuyerCookieService.getBuyer(); $scope.user = UserService.getUser(); $scope.PaymentType = PaymentType; $scope.editMode = checkoutSetup.editMode; $scope.ownPath = checkoutSetup.ownPath; $scope.adrProgram = checkoutSetup.adrProgram; $scope.bruneiName = checkoutSetup.bruneiName; $scope.indonesiaName = checkoutSetup.indonesiaName; $scope.malaysiaName = checkoutSetup.malaysiaName; $scope.philippinesName = checkoutSetup.philippinesName; $scope.singaporeName = checkoutSetup.singaporeName; $scope.thailandName = checkoutSetup.thailandName; $scope.isGuestCheckout = nuskin.util.allowAnonymousShopping() && nuskin.util.getSponsorId(); $scope.sapItems = CartService.getItemData({sapItems: true}) // If checkout didn't pass the orderList then read it from localStorage (For external payment) if ($rootScope.orderList === undefined) { $rootScope.orderList = LocalStorageOrderService.getOrders(); if ($rootScope.orderList) { $rootScope.order = $rootScope.orderList.getLastCompletedOrder(); } } if ($rootScope.orderList === undefined) { // always going to landing page (cart) if the order is empty... window.location = $scope.runConfig.landing; return; } if ($rootScope.order === undefined) { // always going to landing page (cart) if the order is empty... window.location = $scope.runConfig.landing; //alert($scope.runConfig.landing); return; } $scope.signup = CartService.getCartProperty('inSignup'); $scope.scanCards = CartService.getScanCards(); $scope.adr = AdrService.getAdrDraft(); $scope.nextOrder = $rootScope.orderList.getNextOrder() && $rootScope.order.adr; $scope.nextADROrder = $rootScope.orderList.getNextOrder() && !$rootScope.order.adr; $scope.config = ConfigService.getMarketConfig(); $scope.showWebSavings = !$rootScope.order.adr && $scope.config.checkout.showWebSavings; $scope.webSavings = 0.0; $scope.hideNewOrderBtn = $scope.nextOrder || $scope.nextADROrder || (!$scope.tdc.newOrderText || $scope.tdc.newOrderText.length == 0) || $scope.runConfig.assessmentApp === true || $scope.isGuestCheckout; StringUtil.initStrings($scope, $scope.runConfig); $scope.showPsv = ShopContextService.showPsv($scope.buyer ? $scope.buyer.accountType : 0); $scope.cuotas = $rootScope.order.selectedPayment.useInstallments ? $rootScope.order.selectedPayment.getInstallmentSelectedNumber() : 1; if ($rootScope.order.selectedPayment.useInstallments) { $scope.installmentPaymentsOfLabel = $scope.tdc.installmentPaymentsOfLabel ? $scope.tdc.installmentPaymentsOfLabel.replace('{numPayments}', $rootScope.order.selectedPayment.installmentSelectedNumber) : ""; } if (getTotalOrderPoints() > 0) { AdrService.getAdrPoints({forceRefresh: true}) .then(availablePoints => { $scope.$apply(() => { $scope.user.availablePoints = availablePoints; $scope.usedPoints = getTotalOrderPoints(); UserService.setUser($scope.user); }); }) .catch(console.error); } //********************************************************************************* // Private Functions //********************************************************************************* function convertCartItemsToGtmProducts() { let products = CartService.getItemData({cartItems:true}), gtmProducts = []; products.forEach(product => { var gtmProduct = {}; gtmProduct.id = product.sku; gtmProduct.name = product.title; gtmProduct.price = product.price; gtmProduct.quantity = qty; gtmProduct.coupon = ''; gtmProducts.push(gtmProduct); }) return gtmProducts; } function sendAnalyticsData() { console.log("Sending analytics data..."); if (nuskin.newShop) { var orderType = ORDER; if ($rootScope.order.adr && $scope.adr.shipImmediate) { orderType = SHIPIMMEDIATE; } else if ($rootScope.order.adr) { orderType = ADR; } if (typeof dataLayer != 'undefined' && !nsExternalPayment.hasErrorMessageForCheckoutPage()) { var orderNumber = $rootScope.order.orderId; $.publish(events.shop.CART_CHECKOUT_PURCHASE, { 'ecommerce': { 'currencyCode': $rootScope.order.currencyCode, 'purchase': { 'actionField': { 'id': orderNumber, // Transaction ID. Required for purchases and refunds. 'memberid': $rootScope.order.toJSON().soldToMemberId, 'orderType': orderType, 'affiliation': 'Web Checkout', // Web Checkout or Storefront or Personal Offer 'revenue': $rootScope.order.orderTotals.grandTotal, // Total transaction value (incl. tax and shipping) 'tax': $rootScope.order.orderTotals.tax, 'shipping': $rootScope.order.orderTotals.shipping, 'coupon': $rootScope.order.productDiscountCode }, 'products': CartService.getItemData().map((item) => { return { 'name': item.title, 'id': item.sku, 'price': item.price, 'promoPrice': item.promoPrice, 'psv': item.psv, 'quantity': item.qty }; }) } } }); } } /* if (typeof _satellite !== 'undefined') { //TODO After Japan beta is over remove this check if (window.location.href.indexOf("JP") >= 0) { _satellite.track("japan.complete"); } else { _satellite.track("order.complete"); } } */ } function sendSoundConceptsData(){ var user = UserService.getUser(); var distributorId = user.id; var orderSkus = getCartItemSkus(); var buyerFirstName = user.firstName; var buyerEmail = user.email; // soundConceptsServiceUrl is found in configuration now. See tab_urls.xml. var url = $scope.config.soundConceptsServiceUrl + "?distributor_id="+distributorId + "&sku[]=" + orderSkus+"&first_name="+buyerFirstName+"&email="+buyerEmail; $http({ method: 'POST', url: url, crossDomain: true, data: { "master_username":"nuskinapi", "master_password":"center4order" }, dataType: "json" }).then(function(response) { $scope.signupDetail = response.data; angular.forEach($scope.signupDetail.BeReadResponse.Contact, function(c) { if (c.Value.Type === 'EMAIL') { $scope.signupDetail.username= c.Value.value; } }); $scope.confirmationSpinner = false; }, function(data) { $scope.confirmationSpinner = false; }); } function getCartItemSkus(){ return CartService.getItemData().map(item => item.sku); } function loadCommonStrings() { // Get the PSV label from the common strings var accountType = !$scope.isGuestCheckout ? $scope.buyer.accountType : "20"; StringService.getPsvString(accountType).then(psvLabel => { $scope.psvLabel = psvLabel; $scope.$apply(); }); } function defaultLabelValues() { if(typeof $scope.tdc.scanCardNumber === "undefined") { $scope.tdc.scanCardNumber = 'Scan Card Number'; } if(typeof $scope.tdc.scanCardUserName === "undefined") { $scope.tdc.scanCardUserName = 'Scan Card UserName'; } } function cleanupAdrOneTimePointsRedemption() { if (AdrService.isAdrOneTimeOrder()) { let adrId = AdrService.getAdrOverrideId(); AdrService.getAdrDetails(adrId) .then(adr => adr.SalesOrderDetail.LineItemDetails // Remove one-time and pay-with-points items from the ADR .filter(item => item.LineItem.PayWithPoints || item.LineItem.OneTime) // Item removal request requires line IDs of ADR line items .map(item => { return { LineID: item.LineID, Action: 'DELETE' }; })) .then(itemsToRemove => { if (itemsToRemove.length > 0) { return AdrService.editAdrItems(adrId, itemsToRemove); } }) .catch(console.error); } } function setWebSavings() { var orderTotals = $rootScope.order.orderTotals, tax = orderTotals['taxInclusive'] ? orderTotals['tax'] : 0.0, price = orderTotals['grandTotal'], // price with tax and shipping shipping = orderTotals['shipping'], nonWebPrice, // total of items without savings ... does not include tax or shipping buyer = $scope.buyer, priceType = buyer.priceType == 'PRF' ? 'WHL' : buyer.priceType, savings; nonWebPrice = CartService.getPriceTypeTotal(priceType); savings = nonWebPrice - (price - tax - shipping); $scope.webSavings = savings > 0.0 ? savings : 0.0; } function checkForSignup() { if ($scope.signup) { $scope.confirmationSpinner = true; $http({ method: 'GET', url: ConfigService.getMarketConfig().nsurl_accountService + "/extended/" + $scope.buyer.id + "?eid=" + $scope.user.eid, headers: { 'client_id': ConfigService.getMarketConfig().checkout.clientId, 'client_secret': ConfigService.getMarketConfig().checkout.clientSecret } //params: { cntryCd: $scope.runConfig.country } }).then(function(response) { $scope.signupDetail = response.data; angular.forEach($scope.signupDetail.BeReadResponse.Contact, function(c) { if (c.Value.Type === 'EMAIL') { $scope.signupDetail.username= c.Value.value; } }); $scope.confirmationSpinner = false; }, function() { $scope.confirmationSpinner = false; }); } } // If this is a storefront offer we publish the store front order complete event. if (nuskin.util.isMecommerceStoreFront()) { $scope.isStorefront = true; FirebaseUtil.init(RunConfigService.getEnvironmentCode()); let subdomain = MySiteRestService.getCurrentSubdomain(); let sponsorData = StoreFrontSponsorStorageService.getStoreFrontSponsor(); if (sponsorData && sponsorData.storefront && sponsorData.storefront[subdomain] && sponsorData.storefront[subdomain][$scope.runConfig.country]) { let countryData = sponsorData.storefront[subdomain][$scope.runConfig.country]; if (countryData.lang && countryData.lang[$scope.runConfig.language]) { $scope.congratsMessage = countryData.lang[$scope.runConfig.language].purchaseMessage; } } StoreFrontUtil.incrementSalesCounter(nuskin.util.getSponsorId(), $scope.runConfig.country); // Hide the shopping bag. $(".nav-header-cart").hide(); } sendAnalyticsData(); if ($scope.showWebSavings) { setWebSavings(); } defaultLabelValues(); checkForSignup(); loadCommonStrings(); if ($scope.config.checkout.enableSoundConceptsThankyouEmail) { sendSoundConceptsData(); } // if storefront or pitch they will have a sponsor if (SponsorStorageService.getSponsor()) { let personalOfferObject = PersonalOfferStorageService.getPersonalOffer(); $scope.isPitchOrder = CartService.getCartProperty('isPitchCart'); $scope.personalOfferMessage = $scope.isPitchOrder ? personalOfferObject.congratulations : null; if (personalOfferObject) { $scope.offerCongrats = personalOfferObject.congratulations; } } CheckoutApi.cleanupOrder(); // Cleanup after all $scope objects read order information cleanupAdrOneTimePointsRedemption(); // Do not let user leave page if another order needs to be processed $scope.showNextOrderWarning = false; if ($scope.nextOrder || $scope.nextADROrder) { //Change state back to display name -- hack for mexico city ShippingAddressUtil.mapStateToDisplay($rootScope.order.selectedAddress); window.addEventListener('beforeunload', completeNextOrder); } else { nsADRType.setADRType(nsADRType.NO_SELECTION_TYPE); } $scope.contentLoaded = true; $scope.startNextOrder = function() { window.removeEventListener('beforeunload', completeNextOrder); $location.path("/"); }; $scope.stateValueName = ''; //************************************************************************* // Scope functions //************************************************************************* /** getLineItemClass is just retr */ $scope.getLineItemClass = function(item) { if (item.isAdr && !item.isBusinessPortfolio) { return 'adrRow'; } return 'noAdrRow'; }; $scope.goHome = function() { sessionStorage.setItem('appChangeSpinner', true); if (nuskin.util.isMecommerceStoreFront()) { if (nuskin.util.getSubdomain()) { // If we have a subdomain we can redirect without a query param window.location = STOREFRONT_HOME; } else { window.location = `${STOREFRONT_HOME}?storeId=${SPONSOR_ID}`; } } else if (isPersonalOffer) { window.location = personalOfferLanding; } else if ($scope.runConfig.alternateHomeUrl && $scope.runConfig.alternateHomeUrl.length > 0) { window.location = $scope.runConfig.alternateHomeUrl; if ($scope.runConfig.addSkusToLandingPage === true) { window.location = CartService.getOrderSkuQueryParams(true); } } else { window.location = 'home.html'; } }; $scope.goToCart = function() { if ($scope.runConfig.cartUrl && $scope.runConfig.cartUrl.length > 0) { window.location = $scope.runConfig.cartUrl; } else { window.location = 'cart.html'; } }; $scope.printConfirmation = function() { window.print(); }; $scope.getFormattedCCExpiration = function(){ var formattedCCExpiration = ""; if ($rootScope.order && $rootScope.order.selectedPayment && $rootScope.order.selectedPayment.expMonth && $rootScope.order.selectedPayment.expYear){ formattedCCExpiration = $rootScope.order.selectedPayment.expMonth + '/' + $rootScope.order.selectedPayment.expYear; } return formattedCCExpiration; }; $scope.showPendingAuthorization = function() { return $rootScope.order.paymentClass === 'CCARD' && ($rootScope.order.paymentStatus === 'PENDING_PAYMENT' || $rootScope.order.paymentStatus === 'PAYMENT_PENDING'); }; // payWithPoints logic $scope.showPointsBalance = function() { return getTotalOrderPoints() > 0; }; $scope.numberOfCalls = 0; $scope.isWirePayment = function(p){ return (p.paymentTypeId === PaymentType.PROV || p.paymentTypeId === PaymentType.PWEB || p.paymentTypeId === PaymentType.WIRE || (p.paymentTypeId >= PaymentType.VIB_WIRE && p.paymentTypeId <= PaymentType.CR)); }; $scope.isHidingPaymentIcon = function(p){ return (p.paymentTypeId === PaymentType.MPOS || p.paymentTypeId === PaymentType.PROV || p.paymentTypeId === PaymentType.PWEB || (p.paymentTypeId >= PaymentType.VIB_WIRE && p.paymentTypeId <= PaymentType.CR)); }; /** * Registry that acts as a white list for generic payment validation. This function reduces boilerplate code * by providing a generic check for accepted payments for the following functions within the scope of * the order confirmation controller: * * isIncludingTypeDescription * isHidingPaymentNumber * isHidingExpiration * isHidingBoldTypeDescription * * This function can be abused if it's used improperly. Don't look to this as a registry of * accepted payments for every payment scenario. For example, wired payments are limited and * only contain a small subset of the payments listed below, so this function should not be called in * wired payment checking etc. Some functions (i.e. wired payments) only need to check a couple of * paymentTypeId's and don't need to invoke this function. * * @param paymentTypeId (integer) mapping to the payment type code. * @returns {boolean} true if paymentTypeId is whitelisted else false. */ function genericPaymentCheck(paymentTypeId) { try { switch(paymentTypeId) { case PaymentType.APVN: case PaymentType.BDO: case PaymentType.CIMB: case PaymentType.CITI: case PaymentType.DBS: case PaymentType.HSBC: case PaymentType.KBANK: case PaymentType.KLIK: case PaymentType.MBB: case PaymentType.PBB: case PaymentType.PERMATA: case PaymentType.PWEB: case PaymentType.UOB: case PaymentType.WIRE: return true; // Final check against VIB_WIRE and CR. default: return (paymentTypeId >= PaymentType.VIB_WIRE && paymentTypeId <= PaymentType.CR); } } // Safety catch for undefined or null de-referencing in the try block. catch(e) { console.warn('orderConfirmation -> genericPaymentCheck: ' + e.msg); return false; } } $scope.isIncludingTypeDescription = function(p){ return (p.paymentTypeId === PaymentType.PROV || genericPaymentCheck(p.paymentTypeId)); }; $scope.isHidingPaymentNumber = function(p){ return (p.paymentTypeId === PaymentType.IDEA || p.paymentTypeId === PaymentType.PROV || genericPaymentCheck(p.paymentTypeId)); }; $scope.isHidingExpiration = function(p){ return (p.paymentTypeId === PaymentType.IDEA || p.paymentTypeId === PaymentType.MPOS || p.paymentTypeId === PaymentType.SDD || genericPaymentCheck(p.paymentTypeId)); }; $scope.isHidingBoldTypeDescription = function(p){ return (p.paymentTypeId === PaymentType.PROV || p.paymentTypeId === PaymentType.SDD || genericPaymentCheck(p.paymentTypeId)); }; function getTotalOrderPoints() { return CartService.getItemData({cartItems: true}) .filter(item => item.redeem) .map(item => item.qtyRedeemWithPoints * item.points) .reduce((a, b) => a + b, 0); } function completeNextOrder(e) { e.preventDefault(); e.returnValue = $scope.tdc.nextOrderWarningText; } }]);