Skip to content

Instantly share code, notes, and snippets.

@joshrainwater
Last active August 29, 2015 14:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save joshrainwater/3abcb3f7cb397ed03703 to your computer and use it in GitHub Desktop.
Save joshrainwater/3abcb3f7cb397ed03703 to your computer and use it in GitHub Desktop.
UltraCart Connector
// The app definitions get me locations for redirects, and the path to our UltraCart proxy js file.
<?php require_once( '../app/definitions.php');
session_start(); ?>
// The UltraCart connections are below. We're going to be pulling quite a bit from
// https://github.com/UltraCart/responsive_checkout
// because it's easier. But it also links into PHP files and Wordpress rather than backbone like the sample.
// Note: This will change, but can stay at the live URL for all sites once we've moved.
var pathToProxy = '<?php echo PATH_TO_PROXY; ?>';
var merchantId = ''; // Removed for privacy's sake
// Various Definitions. Necessary for redirects, that sort of jazz.
var kitRemoved = '<?php echo KIT_REMOVED ?>';
var myCart = null; // will contain a reference to the cart later.
// ---------------------------------------------------------------------------
// FUNCTIONS
// ---------------------------------------------------------------------------
function loadCart() {
console.log('loadCart');
// if there's a cart id cookie named UltraCartShoppingCartID, then it will automatically be picked up by the REST API.
// if there's not, then a new cart will be returned. Either way, it's a trivial call to retrieve the cart.
var restUrl = pathToProxy + '?_url=/rest/cart';
jQuery.ajax({
url: restUrl,
type: 'GET', // Notice
headers: { "cache-control": "no-cache", "X-UC-Merchant-Id": merchantId },
dataType: 'json'
}).done(function (cart) {
if (cart && cart.cartId) {
// It runs backwards, since we run this if we're logged out.
if(<?php echo (isset($_SESSION['uid'])) ? 'false' : 'true'; ?>) {
window.myCart = cart;
emptyCart();
} else {
window.myCart = cart;
// the cart id cooke may already be set, but it won't hurt to set it again.
jQuery.cookie('UltraCartShoppingCartID', cart.cartId, { expires: 7, path: '/' });
displayCart(cart);
displayCartCheckout(cart);
}
}
});
}
// We need this when we log out of the system.
function emptyCart() {
console.log('emptyCart');
myCart['items'] = [];
var restUrl = pathToProxy + '?_url=/rest/cart';
jQuery.ajax({
url: restUrl,
type: 'POST', // Notice
headers: { "cache-control": "no-cache" },
contentType: 'application/json; charset=UTF-8',
data: JSON.stringify(myCart),
dataType: 'json'
}).done(function (cart) {
});
}
function addToCart(itemData) {
console.log('addToCart');
var restUrl = pathToProxy + '?_url=/rest/cart';
var itemId = itemData['ProductCode'];
var isItemKit = itemData['kit'];
if (itemId) {
if (!myCart) {
// this shouldn't happen, since loadCart should return back a valid cart. this code is here just in case.
myCart = {'merchantId': merchantId, 'cartId': null}; // passing null will create a new cart for us.
}
if (!myCart.items) {
myCart['items'] = [];
}
// If this is a kit, we need to splice it out, since we can only have 1 kit in the cart at a time.
if(isItemKit=='true') {
var needToOverwriteKit = false;
for(var i=0; i<myCart.items.length; i++) {
if(myCart['items'][i]['kit']==true) {
// console.log('splice it out right here.');
myCart['items'].splice(i, 1);
needToOverwriteKit = true;
}
}
}
// We need to cycle through the existing cart; if the itemId is already in the cart, we just update quantity.
var alreadyInCart = false;
for(var i=0; i<myCart.items.length; i++) {
console.log(itemId);
console.log(myCart['items'][i]['itemId']);
if(myCart['items'][i]['itemId']==itemId) {
myCart['items'][i]['quantity']++;
alreadyInCart = true;
console.log(myCart['items'][i]['quantity']);
}
}
// Define the variations hashmap
var variationHashMap = {};
jQuery('.variation').each(function(idx, el) {
if (el.selectedIndex >= 0) {
variationHashMap[el.name] = el.options[el.selectedIndex].value;
}
});
if(!alreadyInCart)
myCart.items.push({itemId: itemId,
manufacturerSuggestedRetailPrice: itemData['RetailPrice'],
unitCost: itemData['UnitPrice'],
arbitraryUnitCost: itemData['UnitPrice'],
quantity: itemData['Quantity'],
variations: variationHashMap});
jQuery.ajax({
url: restUrl,
type: 'POST', // Notice
headers: { "cache-control": "no-cache" },
contentType: 'application/json; charset=UTF-8',
data: JSON.stringify(myCart),
dataType: 'json'
}).done(function (cart) {
displayCart(cart);
window.myCart = cart;
// If this is a kit, we have extra steps to take at this point.
// Namely, we need to log if the person has signed the upgrade form, and we need to redirect to the addon page.
if(itemData['kit']==='true') {
// If the upgrade 'form' was signed:
if($('input[name=Upgrade_Received__c]').val()==='Yes') {
console.log('upgrade ajax');
// We need to submit the upgrade form piece here.
jQuery.ajax({
url: 'https://pumpingessentials.com/wp-content/themes/PumpingEssentials2.0/library/ajax/app-interface.php',
type: 'POST', // Notice
data: itemData,
}).done(function (results) {
console.log(results);
$('#orderForm').find('.form-message').html(results).show();
$('#orderForm').find('input[type=submit]').removeClass('button-submitted');
window.location.href='<?php echo ORDERED_UNSIGNED; ?>';
});
} else {
window.location.href='<?php echo ORDERED_UNSIGNED; ?>';
}
}
// TODO: checkout, redirect etc is taken care of in another place.
});
}
}
function removeFromCart(itemId) {
console.log('removeFromCart');
var restUrl = pathToProxy + '?_url=/rest/cart';
if (itemId) {
if (!myCart) {
// this shouldn't happen, since loadCart should return back a valid cart. this code is here just in case.
myCart = {'merchantId': merchantId, 'cartId': null}; // passing null will create a new cart for us.
}
if (!myCart.items) {
myCart['items'] = [];
}
// We need to do two thing here:
// If this item was a kit, we need to redirect back to packages, since a package has to be purchased
// We need to pull the item out of the cart.
var itemKitRemoved = false;
for(var i=0; i<myCart.items.length; i++) {
if(myCart['items'][i]['itemId']==itemId) {
if(myCart['items'][i]['kit']==true)
itemKitRemoved = true;
myCart['items'].splice(i, 1);
}
}
jQuery.ajax({
url: restUrl,
type: 'POST', // Notice
headers: { "cache-control": "no-cache" },
contentType: 'application/json; charset=UTF-8',
data: JSON.stringify(myCart),
dataType: 'json'
}).done(function (cart) {
displayCart(cart);
displayCartCheckout(cart);
window.myCart = cart;
// If this is a kit, we're triggering a full site load, since we want to travel to another page.
if(itemKitRemoved) {
window.location.href=kitRemoved;
}
// TODO: checkout, redirect etc is taken care of in another place.
});
}
}
function updateCartQuantities(cartData) {
console.log('updateCartQuantities');
var restUrl = pathToProxy + '?_url=/rest/cart';
if (!myCart) {
// this shouldn't happen, since loadCart should return back a valid cart. this code is here just in case.
myCart = {'merchantId': merchantId, 'cartId': null}; // passing null will create a new cart for us.
}
if (!myCart.items) {
myCart['items'] = [];
}
// Now we just cycle through everything in the cart, and update the quantities based on the fields.
for(var i=0; i<myCart.items.length;i++) {
myCart['items'][i]['quantity'] = cartData[myCart['items'][i]['itemId']];
}
// The AJAX probably isn't super necessary, it's mostly here just in case we leave the page to do stuff.
jQuery.ajax({
url: restUrl,
type: 'POST', // Notice
headers: { "cache-control": "no-cache" },
contentType: 'application/json; charset=UTF-8',
data: JSON.stringify(myCart),
dataType: 'json'
}).done(function (cart) {
displayCart(cart);
displayCartCheckout(cart);
window.myCart = cart;
});
}
function displayCart(cart) {
console.log('displayCart');
if (cart) {
if(cart.items.length>0){
var html = '<small>Click on any item to remove it from your cart.</small><ul>';
for( var i=0; i<cart.items.length; i++){
html += '<li><a class="cart-remove" id="'+ cart.items[i]['itemId'] +'" onclick="removeFromCart(\''+ cart.items[i]['itemId'] +'\')"><div class="remove-icon"><i class="fi-minus-circle"></i></div><div class="item-text">' + cart.items[i]['description'] + ' ('+ cart.items[i]['quantity'] +')</div></a></li>';
}
html += '</ul><a class="button radius" href="<?php echo CHECKOUT; ?>" alt="Checkout Now">Go to Checkout</a>';
$('.showcart').html(html);
$('#sidebar-box-6').show();
} else {
$('.showcart').html('');
$('#sidebar-box-6').hide();
}
}
}
// This is the function to display the cart on the Checkout Page.
function displayCartCheckout(cart) {
console.log('displayCartCheckout');
if (cart) {
if(cart.items.length>0){
var checkoutInfo = '<div class="large-12 columns" id="cartContents"><form id="quantityForm"><table style="width:100%"><thead><tr><th>Remove</th><th>Description</th><th>Quantity</th><th>Retail</th><th><b>Your Price</b></th></tr></thead><tbody>';
for( var i=0; i<cart.items.length; i++){
if(cart.items[i]['kit']===true)
checkoutInfo += '<tr><td><a class="cart-remove" id="'+ cart.items[i]['itemId'] +'" onclick="removeFromCart(\''+ cart.items[i]['itemId'] +'\')"><i class="fi-minus-circle"></i></td><td>'+ cart.items[i]['description'] +'</td><td>'+ cart.items[i]['quantity'] +'</td><td>$'+ cart.items[i]['manufacturerSuggestedRetailPrice'] +'</td><td>$'+ cart.items[i]['unitCost'] +'</td></tr>';
else
checkoutInfo += '<tr><td><a class="cart-remove" id="'+ cart.items[i]['itemId'] +'" onclick="removeFromCart(\''+ cart.items[i]['itemId'] +'\')"><i class="fi-minus-circle"></i></td><td>'+ cart.items[i]['description'] +'</td><td><input class="quantity" name="'+ cart.items[i]['itemId'] +'" value="'+ cart.items[i]['quantity'] +'"/></td><td>$'+ cart.items[i]['manufacturerSuggestedRetailPrice'] +'</td><td>$'+ cart.items[i]['unitCost'] +'</td></tr>';
}
checkoutInfo += '<thead><tr><th colspan="2"></th><th><input class="button radius tiny" id="updateQuantities" type="submit" value="Update Cart"/></th><th>Total</th><th>$'+ cart.total +'</th></tr></thead></tbody></table></form></div>';
$('#checkoutInfo').html(checkoutInfo);
if(cart.total>0)
$('#paymentInfo').slideDown();
}
}
}
function checkout(cartData) {
console.log('checkout');
var restUrl = pathToProxy + '?_url=/rest/cart';
cart = myCart;
// First, we need to update the cart with all the info we got on this page. This is lots and lots.
// Shipping Information
cart['shipToFirstName'] = cartData['shipToFirstName'];
cart['shipToLastName'] = cartData['shipToLastName'];
cart['shipToPhone'] = cartData['Phone'];
cart['shipToAddress1'] = cartData['ShippingStreet'];
cart['shipToCity'] = cartData['ShippingCity'];
cart['shipToState'] = cartData['ShippingState'];
cart['shipToPostalCode'] = cartData['ShippingPostalCode'];
cart['shipToCountry'] = 'US';
if(cartData['ShippingSameAsBilling']=='1') {
// Billing Information
cart['billToFirstName'] = cartData['shipToFirstName'];
cart['billToLastName'] = cartData['shipToLastName'];
cart['billToPhone'] = cartData['Phone'];
cart['billToAddress1'] = cartData['ShippingStreet'];
cart['billToCity'] = cartData['ShippingCity'];
cart['billToState'] = cartData['ShippingState'];
cart['billToPostalCode'] = cartData['ShippingPostalCode'];
cart['billToCountry'] = 'US';
} else {
// Billing Informations
cart['billToFirstName'] = cartData['billToFirstName'];
cart['billToLastName'] = cartData['billToLastName'];
cart['billToPhone'] = cartData['Phone'];
cart['billToAddress1'] = cartData['BillingStreet'];
cart['billToCity'] = cartData['BillingCity'];
cart['billToState'] = cartData['BillingState'];
cart['billToPostalCode'] = cartData['BillingPostalCode'];
cart['billToCountry'] = 'US';
}
// Other Info
cart['email'] = cartData['email'];
cart['emailConfirm'] = cartData['email'];
cart['shippingMethod'] = 'We Handle';
cart['customField1'] = 'Website'; // customField1: Source__c
cart['customField2'] = 'true';
// Payment Information
// We have two options: either we take a credit card, or if it's free we'll just pass it as a fake purchase order.
if(cart.total==0) {
cart['paymentMethod'] = 'Purchase Order';
cart['purchaseOrderNumber'] = '000';
// Force everything into accounts receivable
// We need to do a little trickery here. If we have any free kit items, they need to be pushed to be .01. Just in case.
for(var i=0; i<cart.items.length; i++) {
if(cart['items'][i]['kit']==true) {
if(cart['items'][i]['arbitraryUnitCost']==0) {
cart['customField2'] = 'false';
cart['items'][i]['arbitraryUnitCost'] = .01;
cart['items'][i]['unitCost'] = .01;
}
}
}
} else {
cart['paymentMethod'] = 'Credit Card';
cart['creditCardType'] = cartData['CreditCardType'];
cart['creditCardNumber'] = cartData['CreditCardNumber'];
cart['creditCardExpirationMonth'] = cartData['ExpirationMonth'];
cart['creditCardExpirationYear'] = cartData['ExpirationYear'];
cart['creditCardVerificationNumber'] = cartData['CardVerificationNumber'];
}
// Notice: The checkout call does not take a cart. It takes a CheckoutRequest which contains a cart.
// Since the checkout process hands of to UltraCart to handle upsells, etc., we must also provide the
// return point.
var checkoutRequest = {
'cart': cart,
errorParameterName: 'error', // if there are errors after the handoff, the redirect page will receive those errors using this http parameter
errorReturnUrl: document.URL, // this same page.
secureHostName: 'https://pumpingessentials.com' // can be null. defaults to secure.ultracart.com if null. could also be www.mystore.com if that was your url.
// the secureHostName is where the checkout finishes on (receipt). many merchants have dozens of sites. So, if this isn't secure.ultracart and
// you have more than one, you must specify it.
};
console.log(checkoutRequest);
jQuery.ajax({
url: restUrl,
type: 'POST', // Notice
headers: { "cache-control": "no-cache" },
contentType: 'application/json; charset=UTF-8',
data: JSON.stringify(cart),
dataType: 'json'
}).done(function (cart) {
console.log('afterCartUpdate');
console.log(cart);
checkoutRequest.cart = cart;
jQuery.ajax({
url: restUrl + '/checkout',
type: 'POST', // Notice
headers: { "cache-control": "no-cache" },
contentType: 'application/json; charset=UTF-8',
data: JSON.stringify(checkoutRequest),
dataType: 'json'
}).done(function (checkoutResult) {
console.log('FinalCheckoutResult');
console.log(checkoutResult);
if(checkoutResult.redirectToUrl) {
console.log('success!');
// We're good now, so redirect them to the redirectUrl.
window.location.href=checkoutResult.redirectToUrl;
exit();
}
// If we get here, there are errors in the cart somewhere.
var results = '';
for(i=0; i<checkoutResult.errors.length;i++)
results += '<small class="error">'+ checkoutResult.errors[i] +'</small>';
$('#checkoutResults').html('<p>'+ checkoutResult.errors +'</p>');
});
});
}
jQuery(document).ready(function ($) {
loadCart();
// orderForm for Kit and Addon-Products Pages.
$("#orderForm").submit(function(event){
if($('#purchased').val()==='true') {
console.log($('#purchased').val());
return;
}
var formArray = serializeForm('#orderForm');
addToCart(formArray);
event.preventDefault();
});
$("input[name=purchased]").change(function(){
$("#orderForm").trigger('submit');
});
$('.orderButton').click(function(event){
$('input[name=ProductCode]').val($(this).attr('id'));
$('input[name=UnitPrice]').val($(this).attr('unitPrice'));
$('#orderForm').submit();
});
// Specific functions for the checkout page.
$("#ShippingSameAsBilling").change(function(){
if($(this).prop('checked'))
$("#billingInfo").slideUp();
else
$("#billingInfo").slideDown();
});
// orderForm Checkout Form for Checkout Page.
$("#checkoutForm").submit(function(event){
$('#checkoutResults').show();
var formArray = serializeForm('#checkoutForm');
checkout(formArray);
event.preventDefault();
});
// This will update the quantities in the cart when clicked.
$('#checkoutInfo').on('submit', '#quantityForm', function(event){
var formArray = serializeForm('#quantityForm');
updateCartQuantities(formArray);
event.preventDefault();
});
});
/*
* Form Elements for Kits
* - orderForm
* - purchased (only used to trigger redirect)
* - kit (we need to remove any previous kits if a kit gets added to the cart...)
* - ProductCode
* - UnitPrice
* - variations (most have at least size, some have color as well.)
* - BraCode (not really using this but it's there)
* - PumpCode (same as the last parenthesis)
* - quantity (mostly just 1)
*/
// ---------------------------------------------------------------------------
// Extra Utility Functions
// ---------------------------------------------------------------------------
// jQuery Cookie Plugin v1.3.1, https://github.com/carhartl/jquery-cookie, Copyright 2013 Klaus Hartl, Released under the MIT license
(function(e){if(typeof define==="function"&&define.amd){define(["jquery"],e)}else{e(jQuery)}})(function(e){function n(e){return e}function r(e){return decodeURIComponent(e.replace(t," "))}function i(e){if(e.indexOf('"')===0){e=e.slice(1,-1).replace(/\\"/g,'"').replace(/\\\\/g,"\\")}try{return s.json?JSON.parse(e):e}catch(t){}}var t=/\+/g;var s=e.cookie=function(t,o,u){if(o!==undefined){u=e.extend({},s.defaults,u);if(typeof u.expires==="number"){var a=u.expires,f=u.expires=new Date;f.setDate(f.getDate()+a)}o=s.json?JSON.stringify(o):String(o);return document.cookie=[s.raw?t:encodeURIComponent(t),"=",s.raw?o:encodeURIComponent(o),u.expires?"; expires="+u.expires.toUTCString():"",u.path?"; path="+u.path:"",u.domain?"; domain="+u.domain:"",u.secure?"; secure":""].join("")}var l=s.raw?n:r;var c=document.cookie.split("; ");var h=t?undefined:{};for(var p=0,d=c.length;p<d;p++){var v=c[p].split("=");var m=l(v.shift());var g=l(v.join("="));if(t&&t===m){h=i(g);break}if(!t){h[m]=i(g)}}return h};s.defaults={};e.removeCookie=function(t,n){if(e.cookie(t)!==undefined){e.cookie(t,"",e.extend({},n,{expires:-1}));return true}return false}})
// Douglas Crockford's json2, just in case we have an old browser.
if(typeof JSON!=="object"){JSON={}}(function(){"use strict";function f(e){return e<10?"0"+e:e}function quote(e){escapable.lastIndex=0;return escapable.test(e)?'"'+e.replace(escapable,function(e){var t=meta[e];return typeof t==="string"?t:"\\u"+("0000"+e.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+e+'"'}function str(e,t){var n,r,i,s,o=gap,u,a=t[e];if(a&&typeof a==="object"&&typeof a.toJSON==="function"){a=a.toJSON(e)}if(typeof rep==="function"){a=rep.call(t,e,a)}switch(typeof a){case"string":return quote(a);case"number":return isFinite(a)?String(a):"null";case"boolean":case"null":return String(a);case"object":if(!a){return"null"}gap+=indent;u=[];if(Object.prototype.toString.apply(a)==="[object Array]"){s=a.length;for(n=0;n<s;n+=1){u[n]=str(n,a)||"null"}i=u.length===0?"[]":gap?"[\n"+gap+u.join(",\n"+gap)+"\n"+o+"]":"["+u.join(",")+"]";gap=o;return i}if(rep&&typeof rep==="object"){s=rep.length;for(n=0;n<s;n+=1){if(typeof rep[n]==="string"){r=rep[n];i=str(r,a);if(i){u.push(quote(r)+(gap?": ":":")+i)}}}}else{for(r in a){if(Object.prototype.hasOwnProperty.call(a,r)){i=str(r,a);if(i){u.push(quote(r)+(gap?": ":":")+i)}}}}i=u.length===0?"{}":gap?"{\n"+gap+u.join(",\n"+gap)+"\n"+o+"}":"{"+u.join(",")+"}";gap=o;return i}}if(typeof Date.prototype.toJSON!=="function"){Date.prototype.toJSON=function(e){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z":null};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(e){return this.valueOf()}}var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={"\b":"\\b"," ":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},rep;if(typeof JSON.stringify!=="function"){JSON.stringify=function(e,t,n){var r;gap="";indent="";if(typeof n==="number"){for(r=0;r<n;r+=1){indent+=" "}}else if(typeof n==="string"){indent=n}rep=t;if(t&&typeof t!=="function"&&(typeof t!=="object"||typeof t.length!=="number")){throw new Error("JSON.stringify")}return str("",{"":e})}}if(typeof JSON.parse!=="function"){JSON.parse=function(text,reviver){function walk(e,t){var n,r,i=e[t];if(i&&typeof i==="object"){for(n in i){if(Object.prototype.hasOwnProperty.call(i,n)){r=walk(i,n);if(r!==undefined){i[n]=r}else{delete i[n]}}}}return reviver.call(e,t,i)}var j;text=String(text);cx.lastIndex=0;if(cx.test(text)){text=text.replace(cx,function(e){return"\\u"+("0000"+e.charCodeAt(0).toString(16)).slice(-4)})}if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,""))){j=eval("("+text+")");return typeof reviver==="function"?walk({"":j},""):j}throw new SyntaxError("JSON.parse")}}})()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment