The receipt page now retrieves data for orders instead of baskets. Going forward baskets will be deleted after an order has been placed, so there should be no permanent references to baskets. Orders will continue to be persisted permanently. ECOM-2653
320 lines
11 KiB
JavaScript
320 lines
11 KiB
JavaScript
/**
|
|
* View for the receipt page.
|
|
*/
|
|
var edx = edx || {};
|
|
|
|
(function ($, _, _s, Backbone) {
|
|
'use strict';
|
|
|
|
edx.commerce = edx.commerce || {};
|
|
|
|
edx.commerce.ReceiptView = Backbone.View.extend({
|
|
useEcommerceApi: true,
|
|
ecommerceBasketId: null,
|
|
ecommerceOrderNumber: null,
|
|
|
|
initialize: function () {
|
|
this.ecommerceBasketId = $.url('?basket_id');
|
|
this.ecommerceOrderNumber = $.url('?orderNum');
|
|
this.useEcommerceApi = this.ecommerceBasketId || this.ecommerceOrderNumber;
|
|
_.bindAll(this, 'renderReceipt', 'renderError', 'getProviderData', 'renderProvider', 'getCourseData');
|
|
|
|
/* Mix non-conflicting functions from underscore.string (all but include, contains, and reverse) into
|
|
* the Underscore namespace.
|
|
*/
|
|
_.mixin(_s.exports());
|
|
|
|
this.render();
|
|
},
|
|
|
|
renderReceipt: function (data) {
|
|
var templateHtml = $("#receipt-tpl").html(),
|
|
context = {
|
|
platformName: this.$el.data('platform-name'),
|
|
verified: this.$el.data('verified').toLowerCase() === 'true'
|
|
},
|
|
providerId;
|
|
|
|
// Add the receipt info to the template context
|
|
this.courseKey = this.getOrderCourseKey(data);
|
|
this.username = this.$el.data('username');
|
|
_.extend(context, {
|
|
receipt: this.receiptContext(data),
|
|
courseKey: this.courseKey
|
|
});
|
|
|
|
this.$el.html(_.template(templateHtml, context));
|
|
|
|
this.trackLinks();
|
|
|
|
this.renderCourseNamePlaceholder(this.courseKey);
|
|
|
|
providerId = this.getCreditProviderId(data);
|
|
if (providerId) {
|
|
this.getProviderData(providerId).then(this.renderProvider, this.renderError)
|
|
}
|
|
},
|
|
renderCourseNamePlaceholder: function (courseId) {
|
|
// Display the course Id or name (if available) in the placeholder
|
|
var $courseNamePlaceholder = $(".course_name_placeholder");
|
|
$courseNamePlaceholder.text(courseId);
|
|
|
|
this.getCourseData(courseId).then(function (responseData) {
|
|
$courseNamePlaceholder.text(responseData.name);
|
|
});
|
|
},
|
|
renderProvider: function (context) {
|
|
var templateHtml = $("#provider-tpl").html(),
|
|
providerDiv = this.$el.find("#receipt-provider");
|
|
context.course_key = this.courseKey;
|
|
context.username = this.username;
|
|
context.platformName = this.$el.data('platform-name');
|
|
providerDiv.html(_.template(templateHtml, context)).removeClass('hidden');
|
|
},
|
|
|
|
renderError: function () {
|
|
// Display an error
|
|
$('#error-container').removeClass('hidden');
|
|
},
|
|
|
|
render: function () {
|
|
var self = this,
|
|
orderId = this.ecommerceOrderNumber || this.ecommerceBasketId || $.url('?payment-order-num');
|
|
|
|
if (orderId && this.$el.data('is-payment-complete') === 'True') {
|
|
// Get the order details
|
|
self.$el.removeClass('hidden');
|
|
self.getReceiptData(orderId).then(self.renderReceipt, self.renderError);
|
|
} else {
|
|
self.renderError();
|
|
}
|
|
},
|
|
|
|
trackLinks: function () {
|
|
var $verifyNowButton = $('#verify_now_button'),
|
|
$verifyLaterButton = $('#verify_later_button');
|
|
|
|
// Track a virtual pageview, for easy funnel reconstruction.
|
|
window.analytics.page('payment', 'receipt');
|
|
|
|
// Track the user's decision to verify immediately
|
|
window.analytics.trackLink($verifyNowButton, 'edx.bi.user.verification.immediate', {
|
|
category: 'verification'
|
|
});
|
|
|
|
// Track the user's decision to defer their verification
|
|
window.analytics.trackLink($verifyLaterButton, 'edx.bi.user.verification.deferred', {
|
|
category: 'verification'
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Retrieve receipt data from Oscar (via LMS).
|
|
* @param {string} orderId Identifier of the order that was purchased.
|
|
* @return {object} JQuery Promise.
|
|
*/
|
|
getReceiptData: function (orderId) {
|
|
var urlFormat = '/shoppingcart/receipt/%s/';
|
|
|
|
if (this.ecommerceOrderNumber) {
|
|
urlFormat = '/api/commerce/v1/orders/%s/';
|
|
} else if (this.ecommerceBasketId){
|
|
urlFormat = '/api/commerce/v0/baskets/%s/order/';
|
|
}
|
|
|
|
|
|
return $.ajax({
|
|
url: _.sprintf(urlFormat, orderId),
|
|
type: 'GET',
|
|
dataType: 'json'
|
|
}).retry({times: 5, timeout: 2000, statusCodes: [404]});
|
|
},
|
|
/**
|
|
* Retrieve credit provider data from LMS.
|
|
* @param {string} providerId The providerId of the credit provider.
|
|
* @return {object} JQuery Promise.
|
|
*/
|
|
getProviderData: function (providerId) {
|
|
var providerUrl = '/api/credit/v1/providers/%s/';
|
|
|
|
return $.ajax({
|
|
url: _.sprintf(providerUrl, providerId),
|
|
type: 'GET',
|
|
dataType: 'json'
|
|
}).retry({times: 5, timeout: 2000, statusCodes: [404]});
|
|
},
|
|
/**
|
|
* Retrieve course data from LMS.
|
|
* @param {string} courseId The courseId of the course.
|
|
* @return {object} JQuery Promise.
|
|
*/
|
|
getCourseData: function (courseId) {
|
|
var courseDetailUrl = '/api/course_structure/v0/courses/%s/';
|
|
return $.ajax({
|
|
url: _.sprintf(courseDetailUrl, courseId),
|
|
type: 'GET',
|
|
dataType: 'json'
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Construct the template context from data received
|
|
* from the E-Commerce API.
|
|
*
|
|
* @param {object} order Receipt data received from the server
|
|
* @return {object} Receipt template context.
|
|
*/
|
|
receiptContext: function (order) {
|
|
var self = this,
|
|
receiptContext;
|
|
|
|
if (this.useEcommerceApi) {
|
|
receiptContext = {
|
|
orderNum: order.number,
|
|
currency: order.currency,
|
|
purchasedDatetime: order.date_placed,
|
|
totalCost: self.formatMoney(order.total_excl_tax),
|
|
isRefunded: false,
|
|
items: [],
|
|
billedTo: null
|
|
};
|
|
|
|
if (order.billing_address) {
|
|
receiptContext.billedTo = {
|
|
firstName: order.billing_address.first_name,
|
|
lastName: order.billing_address.last_name,
|
|
city: order.billing_address.city,
|
|
state: order.billing_address.state,
|
|
postalCode: order.billing_address.postcode,
|
|
country: order.billing_address.country
|
|
}
|
|
}
|
|
|
|
receiptContext.items = _.map(
|
|
order.lines,
|
|
function (line) {
|
|
return {
|
|
lineDescription: line.description,
|
|
cost: self.formatMoney(line.line_price_excl_tax)
|
|
};
|
|
}
|
|
);
|
|
} else {
|
|
receiptContext = {
|
|
orderNum: order.orderNum,
|
|
currency: order.currency,
|
|
purchasedDatetime: order.purchase_datetime,
|
|
totalCost: self.formatMoney(order.total_cost),
|
|
isRefunded: order.status === "refunded",
|
|
billedTo: {
|
|
firstName: order.billed_to.first_name,
|
|
lastName: order.billed_to.last_name,
|
|
city: order.billed_to.city,
|
|
state: order.billed_to.state,
|
|
postalCode: order.billed_to.postal_code,
|
|
country: order.billed_to.country
|
|
},
|
|
items: []
|
|
};
|
|
|
|
receiptContext.items = _.map(
|
|
order.items,
|
|
function (item) {
|
|
return {
|
|
lineDescription: item.line_desc,
|
|
cost: self.formatMoney(item.line_cost)
|
|
};
|
|
}
|
|
);
|
|
}
|
|
|
|
return receiptContext;
|
|
},
|
|
|
|
getOrderCourseKey: function (order) {
|
|
var length, items;
|
|
if (this.useEcommerceApi) {
|
|
length = order.lines.length;
|
|
for (var i = 0; i < length; i++) {
|
|
var line = order.lines[i],
|
|
attributeValues = _.find(line.product.attribute_values, function (attribute) {
|
|
return attribute.name === 'course_key'
|
|
});
|
|
|
|
// This method assumes that all items in the order are related to a single course.
|
|
if (attributeValues != undefined) {
|
|
return attributeValues['value'];
|
|
}
|
|
}
|
|
} else {
|
|
items = _.filter(order.items, function (item) {
|
|
return item.course_key;
|
|
});
|
|
|
|
if (items.length > 0) {
|
|
return items[0].course_key;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
},
|
|
|
|
formatMoney: function (moneyStr) {
|
|
return Number(moneyStr).toFixed(2);
|
|
},
|
|
|
|
/**
|
|
* Check whether the payment is for the credit course or not.
|
|
*
|
|
* @param {object} order Receipt data received from the server
|
|
* @return {string} String of the provider_id or null.
|
|
*/
|
|
getCreditProviderId: function (order) {
|
|
var attributeValues,
|
|
line = order.lines[0];
|
|
if (this.useEcommerceApi) {
|
|
attributeValues = _.find(line.product.attribute_values, function (attribute) {
|
|
return attribute.name === 'credit_provider';
|
|
});
|
|
|
|
// This method assumes that all items in the order are related to a single course.
|
|
if (attributeValues != undefined) {
|
|
return attributeValues['value'];
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
});
|
|
|
|
new edx.commerce.ReceiptView({
|
|
el: $('#receipt-container')
|
|
});
|
|
|
|
})(jQuery, _, _.str, Backbone);
|
|
|
|
function completeOrder(event) { // jshint ignore:line
|
|
var courseKey = $(event).data("course-key"),
|
|
username = $(event).data("username"),
|
|
providerId = $(event).data("provider"),
|
|
$errorContainer = $("#error-container");
|
|
|
|
try {
|
|
event.preventDefault();
|
|
} catch (err) {
|
|
// Ignore the error as not all event inputs have the preventDefault method.
|
|
}
|
|
|
|
analytics.track(
|
|
"edx.bi.credit.clicked_complete_credit",
|
|
{
|
|
category: "credit",
|
|
label: courseKey
|
|
}
|
|
);
|
|
|
|
edx.commerce.credit.createCreditRequest(providerId, courseKey, username).fail(function () {
|
|
$errorContainer.removeClass("hidden");
|
|
});
|
|
}
|