Merge pull request #7777 from edx/jsa/baskets

use commerce api v2.
This commit is contained in:
Jim Abramson
2015-04-27 22:30:37 -04:00
23 changed files with 518 additions and 332 deletions

View File

@@ -4,7 +4,7 @@ define(['backbone', 'jquery', 'js/verify_student/photocapture'],
describe("Photo Verification", function () {
beforeEach(function () {
setFixtures('<div id="order-error" style="display: none;"></div><input type="radio" name="contribution" value="35" id="contribution-35" checked="checked"><input type="radio" id="contribution-other" name="contribution" value=""><input type="text" size="9" name="contribution-other-amt" id="contribution-other-amt" value="30"><img id="face_image" src="src="data:image/png;base64,dummy"><img id="photo_id_image" src="src="data:image/png;base64,dummy"><button id="pay_button">pay button</button>');
setFixtures('<div id="order-error" style="display: none;"></div><input type="radio" name="contribution" value="35" id="contribution-35" checked="checked"><input type="radio" id="contribution-other" name="contribution" value=""><input type="text" size="9" name="contribution-other-amt" id="contribution-other-amt" value="30"><img id="face_image" src="src="data:image/png;base64,dummy"><img id="photo_id_image" src="src="data:image/png;base64,dummy"><button class="payment-button">pay button</button>');
});
it('retake photo', function () {
@@ -27,7 +27,7 @@ define(['backbone', 'jquery', 'js/verify_student/photocapture'],
});
submitToPaymentProcessing();
expect(window.submitForm).toHaveBeenCalled();
expect($("#pay_button")).toHaveClass("is-disabled");
expect($(".payment-button")).toHaveClass("is-disabled");
});
it('Error during process', function () {
@@ -44,7 +44,7 @@ define(['backbone', 'jquery', 'js/verify_student/photocapture'],
expect(window.showSubmissionError).toHaveBeenCalled();
// make sure the button isn't disabled
expect($("#pay_button")).not.toHaveClass("is-disabled");
expect($(".payment-button")).not.toHaveClass("is-disabled");
// but also make sure that it was disabled during the ajax call
expect($.fn.addClass).toHaveBeenCalledWith("is-disabled");

View File

@@ -5,7 +5,7 @@ define(['js/common_helpers/ajax_helpers', 'js/student_account/enrollment'],
describe( 'edx.student.account.EnrollmentInterface', function() {
var COURSE_KEY = 'edX/DemoX/Fall',
ENROLL_URL = '/commerce/orders/',
ENROLL_URL = '/commerce/baskets/',
FORWARD_URL = '/course_modes/choose/edX/DemoX/Fall/',
EMBARGO_MSG_URL = '/embargo/blocked-message/enrollment/default/';

View File

@@ -11,8 +11,6 @@ define([
describe( 'edx.verify_student.MakePaymentStepView', function() {
var PAYMENT_URL = "/pay";
var PAYMENT_PARAMS = {
orderId: "test-order",
signature: "abcd1234"
@@ -21,7 +19,7 @@ define([
var STEP_DATA = {
minPrice: "12",
currency: "usd",
purchaseEndpoint: PAYMENT_URL,
processors: ["test-payment-processor"],
courseKey: "edx/test/test",
courseModeSlug: 'verified'
};
@@ -50,15 +48,16 @@ define([
};
var expectPaymentButtonEnabled = function( isEnabled ) {
var appearsDisabled = $( '#pay_button' ).hasClass( 'is-disabled' ),
isDisabled = $( '#pay_button' ).prop( 'disabled' );
var el = $( '.payment-button'),
appearsDisabled = el.hasClass( 'is-disabled' ),
isDisabled = el.prop( 'disabled' );
expect( !appearsDisabled ).toEqual( isEnabled );
expect( !isDisabled ).toEqual( isEnabled );
};
var expectPaymentDisabledBecauseInactive = function() {
var payButton = $( '#pay_button' );
var payButton = $( '.payment_button' );
// Payment button should be hidden
expect( payButton.length ).toEqual(0);
@@ -67,21 +66,22 @@ define([
var goToPayment = function( requests, kwargs ) {
var params = {
contribution: kwargs.amount || "",
course_id: kwargs.courseId || ""
course_id: kwargs.courseId || "",
processor: kwargs.processor || ""
};
// Click the "go to payment" button
$( '#pay_button' ).click();
$( '.payment-button' ).click();
// Verify that the request was made to the server
AjaxHelpers.expectRequest(
requests, "POST", "/verify_student/create_order/",
$.param( params )
AjaxHelpers.expectPostRequest(
requests, "/verify_student/create_order/", $.param( params )
);
// Simulate the server response
if ( kwargs.succeeds ) {
AjaxHelpers.respondWithJson( requests, PAYMENT_PARAMS );
// TODO put fixture responses in the right place
AjaxHelpers.respondWithJson( requests, {payment_page_url: 'http://payment-page-url/', payment_form_data: {foo: 'bar'}} );
} else {
AjaxHelpers.respondWithTextError( requests, 400, SERVER_ERROR_MSG );
}
@@ -95,7 +95,7 @@ define([
expect(form.serialize()).toEqual($.param(params));
expect(form.attr('method')).toEqual("POST");
expect(form.attr('action')).toEqual(PAYMENT_URL);
expect(form.attr('action')).toEqual('http://payment-page-url/');
};
beforeEach(function() {
@@ -114,9 +114,10 @@ define([
goToPayment( requests, {
amount: STEP_DATA.minPrice,
courseId: STEP_DATA.courseKey,
processor: STEP_DATA.processors[0],
succeeds: true
});
expectPaymentSubmitted( view, PAYMENT_PARAMS );
expectPaymentSubmitted( view, {foo: 'bar'} );
});
it( 'by default minimum price is selected if no suggested prices are given', function() {
@@ -129,9 +130,10 @@ define([
goToPayment( requests, {
amount: STEP_DATA.minPrice,
courseId: STEP_DATA.courseKey,
processor: STEP_DATA.processors[0],
succeeds: true
});
expectPaymentSubmitted( view, PAYMENT_PARAMS );
expectPaymentSubmitted( view, {foo: 'bar'} );
});
it( 'min price is always selected even if contribution amount is provided', function() {
@@ -156,6 +158,7 @@ define([
goToPayment( requests, {
amount: STEP_DATA.minPrice,
courseId: STEP_DATA.courseKey,
processor: STEP_DATA.processors[0],
succeeds: false
});

View File

@@ -9,7 +9,7 @@ var edx = edx || {};
edx.student.account.EnrollmentInterface = {
urls: {
orders: '/commerce/orders/',
baskets: '/commerce/baskets/',
},
headers: {
@@ -26,7 +26,7 @@ var edx = edx || {};
data = JSON.stringify(data_obj);
$.ajax({
url: this.urls.orders,
url: this.urls.baskets,
type: 'POST',
contentType: 'application/json; charset=utf-8',
data: data,

View File

@@ -59,7 +59,7 @@ var edx = edx || {};
function( price ) { return Boolean( price ); }
),
currency: el.data('course-mode-currency'),
purchaseEndpoint: el.data('purchase-endpoint'),
processors: el.data('processors'),
verificationDeadline: el.data('verification-deadline'),
courseModeSlug: el.data('course-mode-slug'),
alreadyVerified: el.data('already-verified'),

View File

@@ -69,7 +69,7 @@ function refereshPageMessage() {
}
var submitToPaymentProcessing = function() {
$("#pay_button").addClass('is-disabled').attr('aria-disabled', true);
$(".payment-button").addClass('is-disabled').attr('aria-disabled', true);
var contribution_input = $("input[name='contribution']:checked")
var contribution = 0;
if(contribution_input.attr('id') == 'contribution-other') {
@@ -96,7 +96,7 @@ var submitToPaymentProcessing = function() {
}
},
error:function(xhr,status,error) {
$("#pay_button").removeClass('is-disabled').attr('aria-disabled', false);
$(".payment-button").removeClass('is-disabled').attr('aria-disabled', false);
showSubmissionError()
}
});
@@ -290,7 +290,7 @@ function waitForFlashLoad(func, flash_object) {
$(document).ready(function() {
$(".carousel-nav").addClass('sr');
$("#pay_button").click(function(){
$(".payment-button").click(function(){
analytics.pageview("Payment Form");
submitToPaymentProcessing();
});
@@ -306,7 +306,7 @@ $(document).ready(function() {
// prevent browsers from keeping this button checked
$("#confirm_pics_good").prop("checked", false)
$("#confirm_pics_good").change(function() {
$("#pay_button").toggleClass('disabled');
$(".payment-button").toggleClass('disabled');
$("#reverify_button").toggleClass('disabled');
$("#midcourse_reverify_button").toggleClass('disabled');
});

View File

@@ -3,7 +3,7 @@
*/
var edx = edx || {};
(function( $, _, gettext ) {
(function( $, _, gettext, interpolate_text ) {
'use strict';
edx.verify_student = edx.verify_student || {};
@@ -28,12 +28,45 @@ var edx = edx || {};
};
},
_getProductText: function( modeSlug, isUpgrade ) {
switch ( modeSlug ) {
case "professional":
return gettext( "Professional Education Verified Certificate" );
case "no-id-professional":
return gettext( "Professional Education" );
default:
if ( isUpgrade ) {
return gettext( "Verified Certificate upgrade" );
} else {
return gettext( "Verified Certificate" );
}
}
},
_getPaymentButtonText: function(processorName) {
if (processorName.toLowerCase().substr(0, 11)=='cybersource') {
return gettext('Pay with Credit Card');
} else {
// This is mainly for testing as no other processors are supported right now.
// Translators: 'processor' is the name of a third-party payment processing vendor (example: "PayPal")
return interpolate_text(gettext('Pay with {processor}'), {processor: processorName});
}
},
_getPaymentButtonHtml: function(processorName) {
var self = this;
return _.template(
'<a class="next action-primary payment-button" id="<%- name %>" tab-index="0"><%- text %></a> '
)({name: processorName, text: self._getPaymentButtonText(processorName)});
},
postRender: function() {
var templateContext = this.templateContext(),
hasVisibleReqs = _.some(
templateContext.requirements,
function( isVisible ) { return isVisible; }
);
),
self = this;
// Track a virtual pageview, for easy funnel reconstruction.
window.analytics.page( 'payment', this.templateName );
@@ -59,25 +92,41 @@ var edx = edx || {};
this.setPaymentEnabled( true );
}
// render the name of the product being paid for
$( 'div.payment-buttons span.product-name').append(
self._getProductText( templateContext.courseModeSlug, templateContext.upgrade )
);
// create a button for each payment processor
_.each(templateContext.processors, function(processorName) {
$( 'div.payment-buttons' ).append( self._getPaymentButtonHtml(processorName) );
});
// Handle payment submission
$( '#pay_button' ).on( 'click', _.bind( this.createOrder, this ) );
$( '.payment-button' ).on( 'click', _.bind( this.createOrder, this ) );
},
setPaymentEnabled: function( isEnabled ) {
if ( _.isUndefined( isEnabled ) ) {
isEnabled = true;
}
$( '#pay_button' )
$( '.payment-button' )
.toggleClass( 'is-disabled', !isEnabled )
.prop( 'disabled', !isEnabled )
.attr('aria-disabled', !isEnabled);
},
createOrder: function() {
// This function invokes the create_order endpoint. It will either create an order in
// the lms' shoppingcart or a basket in Otto, depending on which backend the request course
// mode is configured to use. In either case, the checkout process will be triggered,
// and the expected response will consist of an appropriate payment processor endpoint for
// redirection, along with parameters to be passed along in the request.
createOrder: function(event) {
var paymentAmount = this.getPaymentAmount(),
postData = {
'processor': event.target.id,
'contribution': paymentAmount,
'course_id': this.stepData.courseKey,
'course_id': this.stepData.courseKey
};
// Disable the payment button to prevent multiple submissions
@@ -98,21 +147,21 @@ var edx = edx || {};
},
handleCreateOrderResponse: function( paymentParams ) {
// At this point, the order has been created on the server,
handleCreateOrderResponse: function( paymentData ) {
// At this point, the basket has been created on the server,
// and we've received signed payment parameters.
// We need to dynamically construct a form using
// these parameters, then submit it to the payment processor.
// This will send the user to a hosted order page,
// where she can enter credit card information.
// This will send the user to an externally-hosted page
// where she can proceed with payment.
var form = $( '#payment-processor-form' );
$( 'input', form ).remove();
form.attr( 'action', this.stepData.purchaseEndpoint );
form.attr( 'action', paymentData.payment_page_url );
form.attr( 'method', 'POST' );
_.each( paymentParams, function( value, key ) {
_.each( paymentData.payment_form_data, function( value, key ) {
$('<input>').attr({
type: 'hidden',
name: key,
@@ -200,4 +249,4 @@ var edx = edx || {};
});
})( jQuery, _, gettext );
})( jQuery, _, gettext, interpolate_text );