/** * 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', contentType: 'application/json', headers: { 'X-CSRFToken': $.cookie('csrftoken') } }).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"); }); }