@nuskin/ns-checkout
Version:
Ecomm3 Checkout module
537 lines (467 loc) • 22 kB
JavaScript
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;
}
}]);