From 5734d2a8836585359740206d3460d72a7fd4509a Mon Sep 17 00:00:00 2001
From: asadiqbal08 <aiqbal@edx.org>
Date: Fri, 6 Jun 2014 14:51:56 +0500
Subject: [PATCH] asadiqbal08:[EX-6] Set up data models and shopping cart
 enhancements in the LMS

Added username into Authors file

[Ex-12] Add user to input coupon code in the Shopping Cart

[Ex-13] Discount should be reflected in the Payment confirmation page and email

added E-commerce Tab in Instructor Dashboard

added name/email in authors file

removed the is_active column, change the colors scheme, fixed bugs

wip

wip

test github account

STORE_BILLING_INFO set to True

cybersource api update, reference number updated, merchant_data removed from params

View Course buttons on receipt

link for course added to receipt

receipt.html view course button - func update

receipt.html course link update

move new CyberSource implementation to a separate file so that we can keep the original

remove config changes

remove config changes

remove coupon redemption during clear cart and update test cases

[Ex-11]added test cases(E-commerce Tab Instuctor Dashboard)

update data model max_length to 255

remove array paid_course_ids init in views.py

removed the is_active filter=false, added styling to the inactive coupon codes

remove coupon redemption during clear cart and update test cases

[Ex-11]added test cases(E-commerce Tab Instuctor Dashboard)

update data model max_length to 255

Add column to the list of coupons in the E-Commerce tab

Add ability for microsites to specify custom CyberSource secret configuration, i.e. run under different accounts

make the new CyberSource2 also microsite aware

updating migration for student and shopping cart apps

added user signup functionality that orignated from the Microsites

added non-microsite user signup tests

fix the hard coded callback URL to localhost

add comment

Modify e-commerce instructor tab to show a total amount above the coupon listings for admin finance user

made changes as suggested by diana khuang

add the CourseAccessRoles table to the Django Admin website

shopping cart coupon checkout changes as suggested by Jason Bau

changes are made according to the suggesstions on PR#4172

changes made in the coupons file

changes in the coupons get_coupon_info view

fix merge conflict resolution error

changes in the remove_coupon view json response

changes as suggested by David Baumgold

pep8/pylint fixes

Changes as suggested by jasonBau

don't assume item in shopping cart is a PaidCourseRegistration

fix up some logging

changed the urls of the coupon views and use the post to get the values from the request
---
 AUTHORS                                       |   2 +
 common/djangoapps/student/admin.py            |   4 +-
 .../0037_auto__add_courseregistrationcode.py  | 179 ++++++++
 .../0038_auto__add_usersignupsource.py        | 180 ++++++++
 .../0039_auto__del_courseregistrationcode.py  | 174 ++++++++
 common/djangoapps/student/models.py           |  12 +
 common/djangoapps/student/roles.py            |   7 +
 .../student/tests/test_microsite.py           |  51 +++
 common/djangoapps/student/views.py            |  20 +-
 .../instructor/tests/test_ecommerce.py        | 193 +++++++++
 lms/djangoapps/instructor/views/coupons.py    | 135 ++++++
 .../instructor/views/instructor_dashboard.py  |  39 ++
 lms/djangoapps/shoppingcart/exceptions.py     |  12 +
 ...demption__chg_field_certificateitem_cou.py | 189 ++++++++
 ...gistrationcode__add_coupon__chg_field_c.py | 216 ++++++++++
 lms/djangoapps/shoppingcart/models.py         |  88 +++-
 .../shoppingcart/processors/CyberSource.py    |  34 +-
 .../shoppingcart/processors/CyberSource2.py   | 404 ++++++++++++++++++
 .../processors/tests/test_CyberSource.py      |  29 ++
 .../shoppingcart/tests/test_views.py          | 201 ++++++++-
 lms/djangoapps/shoppingcart/urls.py           |   1 +
 lms/djangoapps/shoppingcart/views.py          |  59 ++-
 .../sass/course/instructor/_instructor_2.scss | 270 +++++++++++-
 lms/static/sass/views/_shoppingcart.scss      |  79 +++-
 .../add_coupon_modal.html                     |  62 +++
 .../instructor_dashboard_2/e-commerce.html    | 256 +++++++++++
 .../edit_coupon_modal.html                    |  63 +++
 .../shoppingcart/cybersource_form.html        |   3 +-
 lms/templates/shoppingcart/list.html          |  64 ++-
 lms/templates/shoppingcart/receipt.html       |  12 +-
 lms/urls.py                                   |   8 +
 31 files changed, 3009 insertions(+), 37 deletions(-)
 create mode 100644 common/djangoapps/student/migrations/0037_auto__add_courseregistrationcode.py
 create mode 100644 common/djangoapps/student/migrations/0038_auto__add_usersignupsource.py
 create mode 100644 common/djangoapps/student/migrations/0039_auto__del_courseregistrationcode.py
 create mode 100644 common/djangoapps/student/tests/test_microsite.py
 create mode 100644 lms/djangoapps/instructor/tests/test_ecommerce.py
 create mode 100644 lms/djangoapps/instructor/views/coupons.py
 create mode 100644 lms/djangoapps/shoppingcart/migrations/0008_auto__add_coupons__add_couponredemption__chg_field_certificateitem_cou.py
 create mode 100644 lms/djangoapps/shoppingcart/migrations/0009_auto__del_coupons__add_courseregistrationcode__add_coupon__chg_field_c.py
 create mode 100644 lms/djangoapps/shoppingcart/processors/CyberSource2.py
 create mode 100644 lms/templates/instructor/instructor_dashboard_2/add_coupon_modal.html
 create mode 100644 lms/templates/instructor/instructor_dashboard_2/e-commerce.html
 create mode 100644 lms/templates/instructor/instructor_dashboard_2/edit_coupon_modal.html

diff --git a/AUTHORS b/AUTHORS
index 3510bcd3ab5..1db151f2407 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -158,3 +158,5 @@ Tim Babych <tim.babych@gmail.com>
 Brandon DeRosier <btd@cheesekeg.com>
 Daniel Li <swli@edx.org>
 Daniel Friedman <dfriedman@edx.org>
+Asad Iqbal <aiqbal@edx.org>
+Muhammad Shoaib <mshoaib@edx.org>
diff --git a/common/djangoapps/student/admin.py b/common/djangoapps/student/admin.py
index 209f7cf6c0a..e64caedd4f5 100644
--- a/common/djangoapps/student/admin.py
+++ b/common/djangoapps/student/admin.py
@@ -3,7 +3,7 @@ django admin pages for courseware model
 '''
 
 from student.models import UserProfile, UserTestGroup, CourseEnrollmentAllowed
-from student.models import CourseEnrollment, Registration, PendingNameChange
+from student.models import CourseEnrollment, Registration, PendingNameChange, CourseAccessRole
 from ratelimitbackend import admin
 
 admin.site.register(UserProfile)
@@ -17,3 +17,5 @@ admin.site.register(CourseEnrollmentAllowed)
 admin.site.register(Registration)
 
 admin.site.register(PendingNameChange)
+
+admin.site.register(CourseAccessRole)
diff --git a/common/djangoapps/student/migrations/0037_auto__add_courseregistrationcode.py b/common/djangoapps/student/migrations/0037_auto__add_courseregistrationcode.py
new file mode 100644
index 00000000000..5853fcdd34b
--- /dev/null
+++ b/common/djangoapps/student/migrations/0037_auto__add_courseregistrationcode.py
@@ -0,0 +1,179 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        # Adding model 'CourseRegistrationCode'
+        db.create_table('student_courseregistrationcode', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('code', self.gf('django.db.models.fields.CharField')(max_length=32, db_index=True)),
+            ('course_id', self.gf('xmodule_django.models.CourseKeyField')(max_length=255, db_index=True)),
+            ('transaction_group_name', self.gf('django.db.models.fields.CharField')(db_index=True, max_length=255, null=True, blank=True)),
+            ('created_by', self.gf('django.db.models.fields.related.ForeignKey')(related_name='created_by_user', to=orm['auth.User'])),
+            ('created_at', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime(2014, 6, 24, 0, 0))),
+            ('redeemed_by', self.gf('django.db.models.fields.related.ForeignKey')(related_name='redeemed_by_user', null=True, to=orm['auth.User'])),
+            ('redeemed_at', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime(2014, 6, 24, 0, 0), null=True)),
+        ))
+        db.send_create_signal('student', ['CourseRegistrationCode'])
+
+
+    def backwards(self, orm):
+        # Deleting model 'CourseRegistrationCode'
+        db.delete_table('student_courseregistrationcode')
+
+
+    models = {
+        'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        'auth.permission': {
+            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        'auth.user': {
+            'Meta': {'object_name': 'User'},
+            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'contenttypes.contenttype': {
+            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        'student.anonymoususerid': {
+            'Meta': {'object_name': 'AnonymousUserId'},
+            'anonymous_user_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'}),
+            'course_id': ('xmodule_django.models.CourseKeyField', [], {'db_index': 'True', 'max_length': '255', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+        },
+        'student.courseaccessrole': {
+            'Meta': {'unique_together': "(('user', 'org', 'course_id', 'role'),)", 'object_name': 'CourseAccessRole'},
+            'course_id': ('xmodule_django.models.CourseKeyField', [], {'db_index': 'True', 'max_length': '255', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'org': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '64', 'blank': 'True'}),
+            'role': ('django.db.models.fields.CharField', [], {'max_length': '64', 'db_index': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+        },
+        'student.courseenrollment': {
+            'Meta': {'ordering': "('user', 'course_id')", 'unique_together': "(('user', 'course_id'),)", 'object_name': 'CourseEnrollment'},
+            'course_id': ('xmodule_django.models.CourseKeyField', [], {'max_length': '255', 'db_index': 'True'}),
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'db_index': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'mode': ('django.db.models.fields.CharField', [], {'default': "'honor'", 'max_length': '100'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+        },
+        'student.courseenrollmentallowed': {
+            'Meta': {'unique_together': "(('email', 'course_id'),)", 'object_name': 'CourseEnrollmentAllowed'},
+            'auto_enroll': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'course_id': ('xmodule_django.models.CourseKeyField', [], {'max_length': '255', 'db_index': 'True'}),
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'db_index': 'True', 'blank': 'True'}),
+            'email': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+        },
+        'student.courseregistrationcode': {
+            'Meta': {'object_name': 'CourseRegistrationCode'},
+            'code': ('django.db.models.fields.CharField', [], {'max_length': '32', 'db_index': 'True'}),
+            'course_id': ('xmodule_django.models.CourseKeyField', [], {'max_length': '255', 'db_index': 'True'}),
+            'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2014, 6, 24, 0, 0)'}),
+            'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'created_by_user'", 'to': "orm['auth.User']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'redeemed_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2014, 6, 24, 0, 0)', 'null': 'True'}),
+            'redeemed_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'redeemed_by_user'", 'null': 'True', 'to': "orm['auth.User']"}),
+            'transaction_group_name': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'null': 'True', 'blank': 'True'})
+        },
+        'student.loginfailures': {
+            'Meta': {'object_name': 'LoginFailures'},
+            'failure_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'lockout_until': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+        },
+        'student.passwordhistory': {
+            'Meta': {'object_name': 'PasswordHistory'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'time_set': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+        },
+        'student.pendingemailchange': {
+            'Meta': {'object_name': 'PendingEmailChange'},
+            'activation_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32', 'db_index': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'new_email': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True'})
+        },
+        'student.pendingnamechange': {
+            'Meta': {'object_name': 'PendingNameChange'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'new_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'rationale': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True'})
+        },
+        'student.registration': {
+            'Meta': {'object_name': 'Registration', 'db_table': "'auth_registration'"},
+            'activation_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32', 'db_index': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'unique': 'True'})
+        },
+        'student.userprofile': {
+            'Meta': {'object_name': 'UserProfile', 'db_table': "'auth_userprofile'"},
+            'allow_certificate': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'city': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'country': ('django_countries.fields.CountryField', [], {'max_length': '2', 'null': 'True', 'blank': 'True'}),
+            'courseware': ('django.db.models.fields.CharField', [], {'default': "'course.xml'", 'max_length': '255', 'blank': 'True'}),
+            'gender': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '6', 'null': 'True', 'blank': 'True'}),
+            'goals': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'language': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'blank': 'True'}),
+            'level_of_education': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '6', 'null': 'True', 'blank': 'True'}),
+            'location': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'blank': 'True'}),
+            'mailing_address': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'meta': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'profile'", 'unique': 'True', 'to': "orm['auth.User']"}),
+            'year_of_birth': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'})
+        },
+        'student.userstanding': {
+            'Meta': {'object_name': 'UserStanding'},
+            'account_status': ('django.db.models.fields.CharField', [], {'max_length': '31', 'blank': 'True'}),
+            'changed_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'standing_last_changed_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'standing'", 'unique': 'True', 'to': "orm['auth.User']"})
+        },
+        'student.usertestgroup': {
+            'Meta': {'object_name': 'UserTestGroup'},
+            'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '32', 'db_index': 'True'}),
+            'users': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.User']", 'db_index': 'True', 'symmetrical': 'False'})
+        }
+    }
+
+    complete_apps = ['student']
\ No newline at end of file
diff --git a/common/djangoapps/student/migrations/0038_auto__add_usersignupsource.py b/common/djangoapps/student/migrations/0038_auto__add_usersignupsource.py
new file mode 100644
index 00000000000..fd6c7a883c0
--- /dev/null
+++ b/common/djangoapps/student/migrations/0038_auto__add_usersignupsource.py
@@ -0,0 +1,180 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        # Adding model 'UserSignupSource'
+        db.create_table('student_usersignupsource', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('user_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])),
+            ('site', self.gf('django.db.models.fields.CharField')(max_length=255, db_index=True)),
+        ))
+        db.send_create_signal('student', ['UserSignupSource'])
+
+
+    def backwards(self, orm):
+        # Deleting model 'UserSignupSource'
+        db.delete_table('student_usersignupsource')
+
+
+    models = {
+        'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        'auth.permission': {
+            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        'auth.user': {
+            'Meta': {'object_name': 'User'},
+            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'contenttypes.contenttype': {
+            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        'student.anonymoususerid': {
+            'Meta': {'object_name': 'AnonymousUserId'},
+            'anonymous_user_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'}),
+            'course_id': ('xmodule_django.models.CourseKeyField', [], {'db_index': 'True', 'max_length': '255', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+        },
+        'student.courseaccessrole': {
+            'Meta': {'unique_together': "(('user', 'org', 'course_id', 'role'),)", 'object_name': 'CourseAccessRole'},
+            'course_id': ('xmodule_django.models.CourseKeyField', [], {'db_index': 'True', 'max_length': '255', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'org': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '64', 'blank': 'True'}),
+            'role': ('django.db.models.fields.CharField', [], {'max_length': '64', 'db_index': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+        },
+        'student.courseenrollment': {
+            'Meta': {'ordering': "('user', 'course_id')", 'unique_together': "(('user', 'course_id'),)", 'object_name': 'CourseEnrollment'},
+            'course_id': ('xmodule_django.models.CourseKeyField', [], {'max_length': '255', 'db_index': 'True'}),
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'db_index': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'mode': ('django.db.models.fields.CharField', [], {'default': "'honor'", 'max_length': '100'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+        },
+        'student.courseenrollmentallowed': {
+            'Meta': {'unique_together': "(('email', 'course_id'),)", 'object_name': 'CourseEnrollmentAllowed'},
+            'auto_enroll': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'course_id': ('xmodule_django.models.CourseKeyField', [], {'max_length': '255', 'db_index': 'True'}),
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'db_index': 'True', 'blank': 'True'}),
+            'email': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+        },
+        'student.courseregistrationcode': {
+            'Meta': {'object_name': 'CourseRegistrationCode'},
+            'code': ('django.db.models.fields.CharField', [], {'max_length': '32', 'db_index': 'True'}),
+            'course_id': ('xmodule_django.models.CourseKeyField', [], {'max_length': '255', 'db_index': 'True'}),
+            'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2014, 6, 25, 0, 0)'}),
+            'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'created_by_user'", 'to': "orm['auth.User']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'redeemed_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2014, 6, 25, 0, 0)', 'null': 'True'}),
+            'redeemed_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'redeemed_by_user'", 'null': 'True', 'to': "orm['auth.User']"}),
+            'transaction_group_name': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'null': 'True', 'blank': 'True'})
+        },
+        'student.loginfailures': {
+            'Meta': {'object_name': 'LoginFailures'},
+            'failure_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'lockout_until': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+        },
+        'student.passwordhistory': {
+            'Meta': {'object_name': 'PasswordHistory'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'time_set': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+        },
+        'student.pendingemailchange': {
+            'Meta': {'object_name': 'PendingEmailChange'},
+            'activation_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32', 'db_index': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'new_email': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True'})
+        },
+        'student.pendingnamechange': {
+            'Meta': {'object_name': 'PendingNameChange'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'new_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'rationale': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True'})
+        },
+        'student.registration': {
+            'Meta': {'object_name': 'Registration', 'db_table': "'auth_registration'"},
+            'activation_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32', 'db_index': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'unique': 'True'})
+        },
+        'student.userprofile': {
+            'Meta': {'object_name': 'UserProfile', 'db_table': "'auth_userprofile'"},
+            'allow_certificate': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'city': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'country': ('django_countries.fields.CountryField', [], {'max_length': '2', 'null': 'True', 'blank': 'True'}),
+            'courseware': ('django.db.models.fields.CharField', [], {'default': "'course.xml'", 'max_length': '255', 'blank': 'True'}),
+            'gender': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '6', 'null': 'True', 'blank': 'True'}),
+            'goals': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'language': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'blank': 'True'}),
+            'level_of_education': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '6', 'null': 'True', 'blank': 'True'}),
+            'location': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'blank': 'True'}),
+            'mailing_address': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'meta': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'profile'", 'unique': 'True', 'to': "orm['auth.User']"}),
+            'year_of_birth': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'})
+        },
+        'student.usersignupsource': {
+            'Meta': {'object_name': 'UserSignupSource'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'site': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+            'user_id': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+        },
+        'student.userstanding': {
+            'Meta': {'object_name': 'UserStanding'},
+            'account_status': ('django.db.models.fields.CharField', [], {'max_length': '31', 'blank': 'True'}),
+            'changed_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'standing_last_changed_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'standing'", 'unique': 'True', 'to': "orm['auth.User']"})
+        },
+        'student.usertestgroup': {
+            'Meta': {'object_name': 'UserTestGroup'},
+            'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '32', 'db_index': 'True'}),
+            'users': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.User']", 'db_index': 'True', 'symmetrical': 'False'})
+        }
+    }
+
+    complete_apps = ['student']
\ No newline at end of file
diff --git a/common/djangoapps/student/migrations/0039_auto__del_courseregistrationcode.py b/common/djangoapps/student/migrations/0039_auto__del_courseregistrationcode.py
new file mode 100644
index 00000000000..4960e2cbc4d
--- /dev/null
+++ b/common/djangoapps/student/migrations/0039_auto__del_courseregistrationcode.py
@@ -0,0 +1,174 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        # Deleting model 'CourseRegistrationCode'
+        db.delete_table('student_courseregistrationcode')
+
+
+    def backwards(self, orm):
+        # Adding model 'CourseRegistrationCode'
+        db.create_table('student_courseregistrationcode', (
+            ('code', self.gf('django.db.models.fields.CharField')(max_length=32, db_index=True)),
+            ('transaction_group_name', self.gf('django.db.models.fields.CharField')(blank=True, max_length=255, null=True, db_index=True)),
+            ('redeemed_by', self.gf('django.db.models.fields.related.ForeignKey')(related_name='redeemed_by_user', null=True, to=orm['auth.User'])),
+            ('course_id', self.gf('xmodule_django.models.CourseKeyField')(max_length=255, db_index=True)),
+            ('redeemed_at', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime(2014, 6, 25, 0, 0), null=True)),
+            ('created_at', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime(2014, 6, 25, 0, 0))),
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('created_by', self.gf('django.db.models.fields.related.ForeignKey')(related_name='created_by_user', to=orm['auth.User'])),
+        ))
+        db.send_create_signal('student', ['CourseRegistrationCode'])
+
+
+    models = {
+        'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        'auth.permission': {
+            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        'auth.user': {
+            'Meta': {'object_name': 'User'},
+            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'contenttypes.contenttype': {
+            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        'student.anonymoususerid': {
+            'Meta': {'object_name': 'AnonymousUserId'},
+            'anonymous_user_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'}),
+            'course_id': ('xmodule_django.models.CourseKeyField', [], {'db_index': 'True', 'max_length': '255', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+        },
+        'student.courseaccessrole': {
+            'Meta': {'unique_together': "(('user', 'org', 'course_id', 'role'),)", 'object_name': 'CourseAccessRole'},
+            'course_id': ('xmodule_django.models.CourseKeyField', [], {'db_index': 'True', 'max_length': '255', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'org': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '64', 'blank': 'True'}),
+            'role': ('django.db.models.fields.CharField', [], {'max_length': '64', 'db_index': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+        },
+        'student.courseenrollment': {
+            'Meta': {'ordering': "('user', 'course_id')", 'unique_together': "(('user', 'course_id'),)", 'object_name': 'CourseEnrollment'},
+            'course_id': ('xmodule_django.models.CourseKeyField', [], {'max_length': '255', 'db_index': 'True'}),
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'db_index': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'mode': ('django.db.models.fields.CharField', [], {'default': "'honor'", 'max_length': '100'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+        },
+        'student.courseenrollmentallowed': {
+            'Meta': {'unique_together': "(('email', 'course_id'),)", 'object_name': 'CourseEnrollmentAllowed'},
+            'auto_enroll': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'course_id': ('xmodule_django.models.CourseKeyField', [], {'max_length': '255', 'db_index': 'True'}),
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'db_index': 'True', 'blank': 'True'}),
+            'email': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+        },
+        'student.loginfailures': {
+            'Meta': {'object_name': 'LoginFailures'},
+            'failure_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'lockout_until': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+        },
+        'student.passwordhistory': {
+            'Meta': {'object_name': 'PasswordHistory'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'time_set': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+        },
+        'student.pendingemailchange': {
+            'Meta': {'object_name': 'PendingEmailChange'},
+            'activation_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32', 'db_index': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'new_email': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True'})
+        },
+        'student.pendingnamechange': {
+            'Meta': {'object_name': 'PendingNameChange'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'new_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'rationale': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True'})
+        },
+        'student.registration': {
+            'Meta': {'object_name': 'Registration', 'db_table': "'auth_registration'"},
+            'activation_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32', 'db_index': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'unique': 'True'})
+        },
+        'student.userprofile': {
+            'Meta': {'object_name': 'UserProfile', 'db_table': "'auth_userprofile'"},
+            'allow_certificate': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'city': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'country': ('django_countries.fields.CountryField', [], {'max_length': '2', 'null': 'True', 'blank': 'True'}),
+            'courseware': ('django.db.models.fields.CharField', [], {'default': "'course.xml'", 'max_length': '255', 'blank': 'True'}),
+            'gender': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '6', 'null': 'True', 'blank': 'True'}),
+            'goals': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'language': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'blank': 'True'}),
+            'level_of_education': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '6', 'null': 'True', 'blank': 'True'}),
+            'location': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'blank': 'True'}),
+            'mailing_address': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'meta': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'profile'", 'unique': 'True', 'to': "orm['auth.User']"}),
+            'year_of_birth': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'})
+        },
+        'student.usersignupsource': {
+            'Meta': {'object_name': 'UserSignupSource'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'site': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+            'user_id': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+        },
+        'student.userstanding': {
+            'Meta': {'object_name': 'UserStanding'},
+            'account_status': ('django.db.models.fields.CharField', [], {'max_length': '31', 'blank': 'True'}),
+            'changed_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'standing_last_changed_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'standing'", 'unique': 'True', 'to': "orm['auth.User']"})
+        },
+        'student.usertestgroup': {
+            'Meta': {'object_name': 'UserTestGroup'},
+            'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '32', 'db_index': 'True'}),
+            'users': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.User']", 'db_index': 'True', 'symmetrical': 'False'})
+        }
+    }
+
+    complete_apps = ['student']
\ No newline at end of file
diff --git a/common/djangoapps/student/models.py b/common/djangoapps/student/models.py
index b487088b8c7..40d4ac0d6c4 100644
--- a/common/djangoapps/student/models.py
+++ b/common/djangoapps/student/models.py
@@ -271,6 +271,15 @@ class UserProfile(models.Model):
         self.save()
 
 
+class UserSignupSource(models.Model):
+    """
+    This table contains information about users registering
+    via Micro-Sites
+    """
+    user_id = models.ForeignKey(User, db_index=True)
+    site = models.CharField(max_length=255, db_index=True)
+
+
 def unique_id_for_user(user, save=True):
     """
     Return a unique id for a user, suitable for inserting into
@@ -1035,6 +1044,9 @@ class CourseAccessRole(models.Model):
         """
         return self._key < other._key
 
+    def __unicode__(self):
+        return "[CourseAccessRole] user: {}   role: {}   org: {}   course: {}".format(self.user.username, self.role, self.org, self.course_id)
+
 
 #### Helper methods for use from python manage.py shell and other classes.
 
diff --git a/common/djangoapps/student/roles.py b/common/djangoapps/student/roles.py
index 640b7cd7033..6061ce1ea2d 100644
--- a/common/djangoapps/student/roles.py
+++ b/common/djangoapps/student/roles.py
@@ -201,6 +201,13 @@ class CourseInstructorRole(CourseRole):
         super(CourseInstructorRole, self).__init__(self.ROLE, *args, **kwargs)
 
 
+class CourseFinanceAdminRole(CourseRole):
+    """A course Instructor"""
+    ROLE = 'finance_admin'
+
+    def __init__(self, *args, **kwargs):
+        super(CourseFinanceAdminRole, self).__init__(self.ROLE, *args, **kwargs)
+
 class CourseBetaTesterRole(CourseRole):
     """A course Beta Tester"""
     ROLE = 'beta_testers'
diff --git a/common/djangoapps/student/tests/test_microsite.py b/common/djangoapps/student/tests/test_microsite.py
new file mode 100644
index 00000000000..b447ad3a13c
--- /dev/null
+++ b/common/djangoapps/student/tests/test_microsite.py
@@ -0,0 +1,51 @@
+"""
+Test for User Creation from Micro-Sites
+"""
+from django.test import TestCase
+from student.models import UserSignupSource
+import mock
+from django.core.urlresolvers import reverse
+
+
+def fake_site_name(name, default=None):  # pylint: disable=W0613
+    """
+    create a fake microsite site name
+    """
+    if name == 'SITE_NAME':
+        return 'openedx.localhost'
+    else:
+        return None
+
+
+class TestMicrosite(TestCase):
+    """Test for Account Creation from a white labeled Micro-Sites"""
+    def setUp(self):
+        self.username = "test_user"
+        self.url = reverse("create_account")
+        self.params = {
+            "username": self.username,
+            "email": "test@example.org",
+            "password": "testpass",
+            "name": "Test User",
+            "honor_code": "true",
+            "terms_of_service": "true",
+        }
+
+    @mock.patch("microsite_configuration.microsite.get_value", fake_site_name)
+    def test_user_signup_source(self):
+        """
+        test to create a user form the microsite and see that it record has been
+        saved in the UserSignupSource Table
+        """
+        response = self.client.post(self.url, self.params)
+        self.assertEqual(response.status_code, 200)
+        self.assertGreater(len(UserSignupSource.objects.filter(site='openedx.localhost')), 0)
+
+    def test_user_signup_from_non_micro_site(self):
+        """
+        test to create a user form the non-microsite. The record should not be saved
+        in the UserSignupSource Table
+        """
+        response = self.client.post(self.url, self.params)
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(len(UserSignupSource.objects.filter(site='openedx.localhost')), 0)
diff --git a/common/djangoapps/student/views.py b/common/djangoapps/student/views.py
index cd69ebc046d..0f408ab0dd8 100644
--- a/common/djangoapps/student/views.py
+++ b/common/djangoapps/student/views.py
@@ -30,6 +30,9 @@ from django.utils.translation import ugettext as _, get_language
 from django.views.decorators.cache import never_cache
 from django.views.decorators.http import require_POST, require_GET
 
+from django.db.models.signals import post_save
+from django.dispatch import receiver
+
 from django.template.response import TemplateResponse
 
 from ratelimitbackend.exceptions import RateLimitException
@@ -42,7 +45,7 @@ from student.models import (
     Registration, UserProfile, PendingNameChange,
     PendingEmailChange, CourseEnrollment, unique_id_for_user,
     CourseEnrollmentAllowed, UserStanding, LoginFailures,
-    create_comments_service_user, PasswordHistory
+    create_comments_service_user, PasswordHistory, UserSignupSource
 )
 from student.forms import PasswordResetFormNoActive
 
@@ -1021,6 +1024,21 @@ class AccountValidationError(Exception):
         super(AccountValidationError, self).__init__(message)
         self.field = field
 
+
+@receiver(post_save, sender=User)
+def user_signup_handler(sender, **kwargs):  # pylint: disable=W0613
+    """
+    handler that saves the user Signup Source
+    when the user is created
+    """
+    if 'created' in kwargs and kwargs['created']:
+        site = microsite.get_value('SITE_NAME')
+        if site:
+            user_signup_source = UserSignupSource(user_id=kwargs['instance'], site=site)
+            user_signup_source.save()
+            log.info(u'user {} originated from a white labeled "Microsite"'.format(kwargs['instance'].id))
+
+
 def _do_create_account(post_vars):
     """
     Given cleaned post variables, create the User and UserProfile objects, as well as the
diff --git a/lms/djangoapps/instructor/tests/test_ecommerce.py b/lms/djangoapps/instructor/tests/test_ecommerce.py
new file mode 100644
index 00000000000..8cb4b1435a4
--- /dev/null
+++ b/lms/djangoapps/instructor/tests/test_ecommerce.py
@@ -0,0 +1,193 @@
+"""
+Unit tests for Ecommerce feature flag in new instructor dashboard.
+"""
+
+from django.test.utils import override_settings
+from django.core.urlresolvers import reverse
+
+from courseware.tests.tests import TEST_DATA_MONGO_MODULESTORE
+from student.tests.factories import AdminFactory
+from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
+from xmodule.modulestore.tests.factories import CourseFactory
+
+from course_modes.models import CourseMode
+from shoppingcart.models import Coupon, PaidCourseRegistration
+from mock import patch
+from student.roles import CourseFinanceAdminRole
+
+
+# pylint: disable=E1101
+@override_settings(MODULESTORE=TEST_DATA_MONGO_MODULESTORE)
+class TestECommerceDashboardViews(ModuleStoreTestCase):
+    """
+    Check for email view on the new instructor dashboard
+    for Mongo-backed courses
+    """
+    def setUp(self):
+        self.course = CourseFactory.create()
+
+        # Create instructor account
+        self.instructor = AdminFactory.create()
+        self.client.login(username=self.instructor.username, password="test")
+        mode = CourseMode(
+            course_id=self.course.id.to_deprecated_string(), mode_slug='honor',
+            mode_display_name='honor', min_price=10, currency='usd'
+        )
+        mode.save()
+        # URL for instructor dash
+        self.url = reverse('instructor_dashboard', kwargs={'course_id': self.course.id.to_deprecated_string()})
+        self.e_commerce_link = '<a href="" data-section="e-commerce">E-Commerce</a>'
+        CourseFinanceAdminRole(self.course.id).add_users(self.instructor)
+
+    def tearDown(self):
+        """
+        Undo all patches.
+        """
+        patch.stopall()
+
+    def test_pass_e_commerce_tab_in_instructor_dashboard(self):
+        """
+        Test Pass E-commerce Tab is in the Instructor Dashboard
+        """
+        response = self.client.get(self.url)
+        self.assertTrue(self.e_commerce_link in response.content)
+
+    def test_user_has_finance_admin_rights_in_e_commerce_tab(self):
+        response = self.client.get(self.url)
+        self.assertTrue(self.e_commerce_link in response.content)
+
+        # Total amount html should render in e-commerce page, total amount will be 0
+        total_amount = PaidCourseRegistration.get_total_amount_of_purchased_item(self.course.id)
+        self.assertTrue('<span>Total Amount: <span>$' + str(total_amount) + '</span></span>' in response.content)
+
+        # removing the course finance_admin role of login user
+        CourseFinanceAdminRole(self.course.id).remove_users(self.instructor)
+
+        # total amount should not be visible in e-commerce page if the user is not finance admin
+        url = reverse('instructor_dashboard', kwargs={'course_id': self.course.id.to_deprecated_string()})
+        response = self.client.post(url)
+        total_amount = PaidCourseRegistration.get_total_amount_of_purchased_item(self.course.id)
+        self.assertFalse('<span>Total Amount: <span>$' + str(total_amount) + '</span></span>' in response.content)
+
+    def test_add_coupon(self):
+        """
+        Test Add Coupon Scenarios. Handle all the HttpResponses return by add_coupon view
+        """
+        # URL for add_coupon
+        add_coupon_url = reverse('add_coupon', kwargs={'course_id': self.course.id.to_deprecated_string()})
+        data = {
+            'code': 'A2314', 'course_id': self.course.id.to_deprecated_string(),
+            'description': 'ADSADASDSAD', 'created_by': self.instructor, 'discount': 5
+        }
+        response = self.client.post(add_coupon_url, data)
+        self.assertTrue("coupon with the coupon code ({code}) added successfully".format(code=data['code']) in response.content)
+
+        data = {
+            'code': 'A2314', 'course_id': self.course.id.to_deprecated_string(),
+            'description': 'asdsasda', 'created_by': self.instructor, 'discount': 111
+        }
+        response = self.client.post(add_coupon_url, data)
+        self.assertTrue("coupon with the coupon code ({code}) already exist".format(code='A2314') in response.content)
+
+        response = self.client.post(self.url)
+        self.assertTrue('<td>ADSADASDSAD</td>' in response.content)
+        self.assertTrue('<td>A2314</td>' in response.content)
+        self.assertFalse('<td>111</td>' in response.content)
+
+    def test_delete_coupon(self):
+        """
+        Test Delete Coupon Scenarios. Handle all the HttpResponses return by remove_coupon view
+        """
+        coupon = Coupon(
+            code='AS452', description='asdsadsa', course_id=self.course.id.to_deprecated_string(),
+            percentage_discount=10, created_by=self.instructor
+        )
+
+        coupon.save()
+
+        response = self.client.post(self.url)
+        self.assertTrue('<td>AS452</td>' in response.content)
+
+        # URL for remove_coupon
+        delete_coupon_url = reverse('remove_coupon', kwargs={'course_id': self.course.id.to_deprecated_string()})
+        response = self.client.post(delete_coupon_url, {'id': coupon.id})
+        self.assertTrue('coupon with the coupon id ({coupon_id}) updated successfully'.format(coupon_id=coupon.id) in response.content)
+
+        coupon.is_active = False
+        coupon.save()
+
+        response = self.client.post(delete_coupon_url, {'id': coupon.id})
+        self.assertTrue('coupon with the coupon id ({coupon_id}) is already inactive'.format(coupon_id=coupon.id) in response.content)
+
+        response = self.client.post(delete_coupon_url, {'id': 24454})
+        self.assertTrue('coupon with the coupon id ({coupon_id}) DoesNotExist'.format(coupon_id=24454) in response.content)
+
+        response = self.client.post(delete_coupon_url, {'id': ''})
+        self.assertTrue('coupon id is None' in response.content)
+
+    def test_get_coupon_info(self):
+        """
+        Test Edit Coupon Info Scenarios. Handle all the HttpResponses return by edit_coupon_info view
+        """
+        coupon = Coupon(
+            code='AS452', description='asdsadsa', course_id=self.course.id.to_deprecated_string(),
+            percentage_discount=10, created_by=self.instructor
+        )
+        coupon.save()
+        # URL for edit_coupon_info
+        edit_url = reverse('get_coupon_info', kwargs={'course_id': self.course.id.to_deprecated_string()})
+        response = self.client.post(edit_url, {'id': coupon.id})
+        self.assertTrue('coupon with the coupon id ({coupon_id}) updated successfully'.format(coupon_id=coupon.id) in response.content)
+
+        response = self.client.post(edit_url, {'id': 444444})
+        self.assertTrue('coupon with the coupon id ({coupon_id}) DoesNotExist'.format(coupon_id=444444) in response.content)
+
+        response = self.client.post(edit_url, {'id': ''})
+        self.assertTrue('coupon id not found"' in response.content)
+
+        coupon.is_active = False
+        coupon.save()
+
+        response = self.client.post(edit_url, {'id': coupon.id})
+        self.assertTrue("coupon with the coupon id ({coupon_id}) is already inactive".format(coupon_id=coupon.id) in response.content)
+
+    def test_update_coupon(self):
+        """
+        Test Update Coupon Info Scenarios. Handle all the HttpResponses return by update_coupon view
+        """
+        coupon = Coupon(
+            code='AS452', description='asdsadsa', course_id=self.course.id.to_deprecated_string(),
+            percentage_discount=10, created_by=self.instructor
+        )
+        coupon.save()
+        response = self.client.post(self.url)
+        self.assertTrue('<td>AS452</td>' in response.content)
+        data = {
+            'coupon_id': coupon.id, 'code': 'update_code', 'discount': '12',
+            'course_id': coupon.course_id.to_deprecated_string()
+        }
+        # URL for update_coupon
+        update_coupon_url = reverse('update_coupon', kwargs={'course_id': self.course.id.to_deprecated_string()})
+        response = self.client.post(update_coupon_url, data=data)
+        self.assertTrue('coupon with the coupon id ({coupon_id}) updated Successfully'.format(coupon_id=coupon.id)in response.content)
+
+        response = self.client.post(self.url)
+        self.assertTrue('<td>update_code</td>' in response.content)
+        self.assertTrue('<td>12</td>' in response.content)
+
+        data['coupon_id'] = 1000  # Coupon Not Exist with this ID
+        response = self.client.post(update_coupon_url, data=data)
+        self.assertTrue('coupon with the coupon id ({coupon_id}) DoesNotExist'.format(coupon_id=1000) in response.content)
+
+        data['coupon_id'] = ''  # Coupon id is not provided
+        response = self.client.post(update_coupon_url, data=data)
+        self.assertTrue('coupon id not found' in response.content)
+
+        coupon1 = Coupon(
+            code='11111', description='coupon', course_id=self.course.id.to_deprecated_string(),
+            percentage_discount=20, created_by=self.instructor
+        )
+        coupon1.save()
+        data = {'coupon_id': coupon.id, 'code': '11111', 'discount': '12'}
+        response = self.client.post(update_coupon_url, data=data)
+        self.assertTrue('coupon with the coupon id ({coupon_id}) already exist'.format(coupon_id=coupon.id) in response.content)
diff --git a/lms/djangoapps/instructor/views/coupons.py b/lms/djangoapps/instructor/views/coupons.py
new file mode 100644
index 00000000000..7251e470c5b
--- /dev/null
+++ b/lms/djangoapps/instructor/views/coupons.py
@@ -0,0 +1,135 @@
+"""
+E-commerce Tab Instructor Dashboard Coupons Operations views
+"""
+from django.contrib.auth.decorators import login_required
+from django.core.exceptions import ObjectDoesNotExist
+from django.db.models import Q
+from django.views.decorators.http import require_POST
+from django.utils.translation import ugettext as _
+from util.json_request import JsonResponse
+from django.http import HttpResponse, HttpResponseNotFound
+from shoppingcart.models import Coupon
+
+import logging
+
+log = logging.getLogger(__name__)
+
+
+@require_POST
+@login_required
+def remove_coupon(request, course_id):  # pylint: disable=W0613
+    """
+    remove the coupon against the coupon id
+    set the coupon is_active flag to false
+    """
+    coupon_id = request.POST.get('id', None)
+    if not coupon_id:
+        return JsonResponse({
+            'message': _('coupon id is None')
+        }, status=400)  # status code 400: Bad Request
+
+    try:
+        coupon = Coupon.objects.get(id=coupon_id)
+    except ObjectDoesNotExist:
+        return JsonResponse({
+            'message': _('coupon with the coupon id ({coupon_id}) DoesNotExist').format(coupon_id=coupon_id)
+        }, status=400)  # status code 400: Bad Request
+    if not coupon.is_active:
+        return JsonResponse({
+            'message': _('coupon with the coupon id ({coupon_id}) is already inactive').format(coupon_id=coupon_id)
+        }, status=400)  # status code 400: Bad Request
+    coupon.is_active = False
+    coupon.save()
+    return JsonResponse({
+        'message': _('coupon with the coupon id ({coupon_id}) updated successfully').format(coupon_id=coupon_id)
+    })  # status code 200: OK by default
+
+
+@require_POST
+@login_required
+def add_coupon(request, course_id):  # pylint: disable=W0613
+    """
+    add coupon in the Coupons Table
+    """
+    code = request.POST.get('code')
+
+    # check if the code is already in the Coupons Table and active
+    coupon = Coupon.objects.filter(is_active=True, code=code)
+
+    if coupon:
+        return HttpResponseNotFound(_("coupon with the coupon code ({code}) already exist").format(code=code))
+
+    description = request.POST.get('description')
+    course_id = request.POST.get('course_id')
+    discount = request.POST.get('discount')
+    coupon = Coupon(
+        code=code, description=description, course_id=course_id,
+        percentage_discount=discount, created_by_id=request.user.id
+    )
+    coupon.save()
+    return HttpResponse(_("coupon with the coupon code ({code}) added successfully").format(code=code))
+
+
+@require_POST
+@login_required
+def update_coupon(request, course_id):  # pylint: disable=W0613
+    """
+    update the coupon object in the database
+    """
+    coupon_id = request.POST.get('coupon_id', None)
+    if not coupon_id:
+        return HttpResponseNotFound(_("coupon id not found"))
+
+    try:
+        coupon = Coupon.objects.get(pk=coupon_id)
+    except ObjectDoesNotExist:
+        return HttpResponseNotFound(_("coupon with the coupon id ({coupon_id}) DoesNotExist").format(coupon_id=coupon_id))
+
+    code = request.POST.get('code')
+    filtered_coupons = Coupon.objects.filter(~Q(id=coupon_id), code=code, is_active=True)
+
+    if filtered_coupons:
+        return HttpResponseNotFound(_("coupon with the coupon id ({coupon_id}) already exists").format(coupon_id=coupon_id))
+
+    description = request.POST.get('description')
+    course_id = request.POST.get('course_id')
+    discount = request.POST.get('discount')
+    coupon.code = code
+    coupon.description = description
+    coupon.course_id = course_id
+    coupon.percentage_discount = discount
+    coupon.save()
+    return HttpResponse(_("coupon with the coupon id ({coupon_id}) updated Successfully").format(coupon_id=coupon_id))
+
+
+@require_POST
+@login_required
+def get_coupon_info(request, course_id):  # pylint: disable=W0613
+    """
+    get the coupon information to display in the pop up form
+    """
+    coupon_id = request.POST.get('id', None)
+    if not coupon_id:
+        return JsonResponse({
+            'message': _("coupon id not found")
+        }, status=400)  # status code 400: Bad Request
+
+    try:
+        coupon = Coupon.objects.get(id=coupon_id)
+    except ObjectDoesNotExist:
+        return JsonResponse({
+            'message': _("coupon with the coupon id ({coupon_id}) DoesNotExist").format(coupon_id=coupon_id)
+        }, status=400)  # status code 400: Bad Request
+
+    if not coupon.is_active:
+        return JsonResponse({
+            'message': _("coupon with the coupon id ({coupon_id}) is already inactive").format(coupon_id=coupon_id)
+        }, status=400)  # status code 400: Bad Request
+
+    return JsonResponse({
+        'coupon_code': coupon.code,
+        'coupon_description': coupon.description,
+        'coupon_course_id': coupon.course_id.to_deprecated_string(),
+        'coupon_discount': coupon.percentage_discount,
+        'message': _('coupon with the coupon id ({coupon_id}) updated successfully').format(coupon_id=coupon_id)
+    })  # status code 200: OK by default
diff --git a/lms/djangoapps/instructor/views/instructor_dashboard.py b/lms/djangoapps/instructor/views/instructor_dashboard.py
index abebd0fb116..f2e7fba5e1c 100644
--- a/lms/djangoapps/instructor/views/instructor_dashboard.py
+++ b/lms/djangoapps/instructor/views/instructor_dashboard.py
@@ -26,6 +26,10 @@ from courseware.courses import get_course_by_id, get_cms_course_link, get_course
 from django_comment_client.utils import has_forum_access
 from django_comment_common.models import FORUM_ROLE_ADMINISTRATOR
 from student.models import CourseEnrollment
+from shoppingcart.models import Coupon, PaidCourseRegistration
+from course_modes.models import CourseMode
+from student.roles import CourseFinanceAdminRole
+
 from bulk_email.models import CourseAuthorization
 from class_dashboard.dashboard_data import get_section_display_name, get_array_section_has_problem
 
@@ -49,6 +53,7 @@ def instructor_dashboard_2(request, course_id):
     access = {
         'admin': request.user.is_staff,
         'instructor': has_access(request.user, 'instructor', course),
+        'finance_admin': CourseFinanceAdminRole(course_key).has_user(request.user),
         'staff': has_access(request.user, 'staff', course),
         'forum_admin': has_forum_access(
             request.user, course_key, FORUM_ROLE_ADMINISTRATOR
@@ -66,6 +71,12 @@ def instructor_dashboard_2(request, course_id):
         _section_analytics(course_key, access),
     ]
 
+    #check if there is corresponding entry in the CourseMode Table related to the Instructor Dashboard course
+    course_honor_mode = CourseMode.mode_for_course(course_key, 'honor')
+    course_mode_has_price = False
+    if course_honor_mode and course_honor_mode.min_price > 0:
+        course_mode_has_price = True
+
     if (settings.FEATURES.get('INDIVIDUAL_DUE_DATES') and access['instructor']):
         sections.insert(3, _section_extensions(course))
 
@@ -77,6 +88,11 @@ def instructor_dashboard_2(request, course_id):
     if settings.FEATURES['CLASS_DASHBOARD'] and access['staff']:
         sections.append(_section_metrics(course_key, access))
 
+     # Gate access to Ecommerce tab
+    if course_mode_has_price:
+        sections.append(_section_e_commerce(course_key, access))
+
+
     studio_url = None
     if is_studio_course:
         studio_url = get_cms_course_link(course)
@@ -111,6 +127,29 @@ section_display_name will be used to generate link titles in the nav bar.
 """  # pylint: disable=W0105
 
 
+def _section_e_commerce(course_key, access):
+    """ Provide data for the corresponding dashboard section """
+    coupons = Coupon.objects.filter(course_id=course_key).order_by('-is_active')
+    total_amount = None
+    if access['finance_admin']:
+        total_amount = PaidCourseRegistration.get_total_amount_of_purchased_item(course_key)
+
+    section_data = {
+        'section_key': 'e-commerce',
+        'section_display_name': _('E-Commerce'),
+        'access': access,
+        'course_id': course_key.to_deprecated_string(),
+        'ajax_remove_coupon_url': reverse('remove_coupon', kwargs={'course_id': course_key.to_deprecated_string()}),
+        'ajax_get_coupon_info': reverse('get_coupon_info', kwargs={'course_id': course_key.to_deprecated_string()}),
+        'ajax_update_coupon': reverse('update_coupon', kwargs={'course_id': course_key.to_deprecated_string()}),
+        'ajax_add_coupon': reverse('add_coupon', kwargs={'course_id': course_key.to_deprecated_string()}),
+        'instructor_url': reverse('instructor_dashboard', kwargs={'course_id': course_key.to_deprecated_string()}),
+        'coupons': coupons,
+        'total_amount': total_amount,
+    }
+    return section_data
+
+
 def _section_course_info(course_key, access):
     """ Provide data for the corresponding dashboard section """
     course = get_course_by_id(course_key, depth=None)
diff --git a/lms/djangoapps/shoppingcart/exceptions.py b/lms/djangoapps/shoppingcart/exceptions.py
index b6f826040bf..8f4b30c583a 100644
--- a/lms/djangoapps/shoppingcart/exceptions.py
+++ b/lms/djangoapps/shoppingcart/exceptions.py
@@ -28,6 +28,18 @@ class CourseDoesNotExistException(InvalidCartItem):
     pass
 
 
+class CouponDoesNotExistException(InvalidCartItem):
+    pass
+
+
+class CouponAlreadyExistException(InvalidCartItem):
+    pass
+
+
+class ItemDoesNotExistAgainstCouponException(InvalidCartItem):
+    pass
+
+
 class ReportException(Exception):
     pass
 
diff --git a/lms/djangoapps/shoppingcart/migrations/0008_auto__add_coupons__add_couponredemption__chg_field_certificateitem_cou.py b/lms/djangoapps/shoppingcart/migrations/0008_auto__add_coupons__add_couponredemption__chg_field_certificateitem_cou.py
new file mode 100644
index 00000000000..8ff9a0d7ae5
--- /dev/null
+++ b/lms/djangoapps/shoppingcart/migrations/0008_auto__add_coupons__add_couponredemption__chg_field_certificateitem_cou.py
@@ -0,0 +1,189 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        # Adding model 'Coupons'
+        db.create_table('shoppingcart_coupons', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('code', self.gf('django.db.models.fields.CharField')(max_length=32, db_index=True)),
+            ('description', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
+            ('course_id', self.gf('xmodule_django.models.CourseKeyField')(max_length=255)),
+            ('percentage_discount', self.gf('django.db.models.fields.IntegerField')(default=0)),
+            ('created_by', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])),
+            ('created_at', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime(2014, 6, 24, 0, 0))),
+            ('is_active', self.gf('django.db.models.fields.BooleanField')(default=True)),
+        ))
+        db.send_create_signal('shoppingcart', ['Coupons'])
+
+        # Adding model 'CouponRedemption'
+        db.create_table('shoppingcart_couponredemption', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('order', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['shoppingcart.Order'])),
+            ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])),
+            ('coupon', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['shoppingcart.Coupons'])),
+        ))
+        db.send_create_signal('shoppingcart', ['CouponRedemption'])
+
+
+        # Changing field 'CertificateItem.course_id'
+        db.alter_column('shoppingcart_certificateitem', 'course_id', self.gf('xmodule_django.models.CourseKeyField')(max_length=128))
+
+        # Changing field 'PaidCourseRegistrationAnnotation.course_id'
+        db.alter_column('shoppingcart_paidcourseregistrationannotation', 'course_id', self.gf('xmodule_django.models.CourseKeyField')(unique=True, max_length=128))
+
+        # Changing field 'PaidCourseRegistration.course_id'
+        db.alter_column('shoppingcart_paidcourseregistration', 'course_id', self.gf('xmodule_django.models.CourseKeyField')(max_length=128))
+        # Adding field 'OrderItem.discount_price'
+        db.add_column('shoppingcart_orderitem', 'discount_price',
+                      self.gf('django.db.models.fields.DecimalField')(null=True, max_digits=30, decimal_places=2),
+                      keep_default=False)
+
+
+    def backwards(self, orm):
+        # Deleting model 'Coupons'
+        db.delete_table('shoppingcart_coupons')
+
+        # Deleting model 'CouponRedemption'
+        db.delete_table('shoppingcart_couponredemption')
+
+
+        # Changing field 'CertificateItem.course_id'
+        db.alter_column('shoppingcart_certificateitem', 'course_id', self.gf('django.db.models.fields.CharField')(max_length=128))
+
+        # Changing field 'PaidCourseRegistrationAnnotation.course_id'
+        db.alter_column('shoppingcart_paidcourseregistrationannotation', 'course_id', self.gf('django.db.models.fields.CharField')(max_length=128, unique=True))
+
+        # Changing field 'PaidCourseRegistration.course_id'
+        db.alter_column('shoppingcart_paidcourseregistration', 'course_id', self.gf('django.db.models.fields.CharField')(max_length=128))
+        # Deleting field 'OrderItem.discount_price'
+        db.delete_column('shoppingcart_orderitem', 'discount_price')
+
+
+    models = {
+        'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        'auth.permission': {
+            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        'auth.user': {
+            'Meta': {'object_name': 'User'},
+            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'contenttypes.contenttype': {
+            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        'shoppingcart.certificateitem': {
+            'Meta': {'object_name': 'CertificateItem', '_ormbases': ['shoppingcart.OrderItem']},
+            'course_enrollment': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['student.CourseEnrollment']"}),
+            'course_id': ('xmodule_django.models.CourseKeyField', [], {'max_length': '128', 'db_index': 'True'}),
+            'mode': ('django.db.models.fields.SlugField', [], {'max_length': '50'}),
+            'orderitem_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['shoppingcart.OrderItem']", 'unique': 'True', 'primary_key': 'True'})
+        },
+        'shoppingcart.couponredemption': {
+            'Meta': {'object_name': 'CouponRedemption'},
+            'coupon': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['shoppingcart.Coupons']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'order': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['shoppingcart.Order']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+        },
+        'shoppingcart.coupons': {
+            'Meta': {'object_name': 'Coupons'},
+            'code': ('django.db.models.fields.CharField', [], {'max_length': '32', 'db_index': 'True'}),
+            'course_id': ('xmodule_django.models.CourseKeyField', [], {'max_length': '255'}),
+            'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2014, 6, 24, 0, 0)'}),
+            'created_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
+            'description': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'percentage_discount': ('django.db.models.fields.IntegerField', [], {'default': '0'})
+        },
+        'shoppingcart.order': {
+            'Meta': {'object_name': 'Order'},
+            'bill_to_cardtype': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}),
+            'bill_to_ccnum': ('django.db.models.fields.CharField', [], {'max_length': '8', 'blank': 'True'}),
+            'bill_to_city': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
+            'bill_to_country': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
+            'bill_to_first': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
+            'bill_to_last': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
+            'bill_to_postalcode': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}),
+            'bill_to_state': ('django.db.models.fields.CharField', [], {'max_length': '8', 'blank': 'True'}),
+            'bill_to_street1': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}),
+            'bill_to_street2': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}),
+            'currency': ('django.db.models.fields.CharField', [], {'default': "'usd'", 'max_length': '8'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'processor_reply_dump': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'purchase_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'refunded_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'status': ('django.db.models.fields.CharField', [], {'default': "'cart'", 'max_length': '32'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+        },
+        'shoppingcart.orderitem': {
+            'Meta': {'object_name': 'OrderItem'},
+            'currency': ('django.db.models.fields.CharField', [], {'default': "'usd'", 'max_length': '8'}),
+            'discount_price': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '30', 'decimal_places': '2'}),
+            'fulfilled_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'db_index': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'line_desc': ('django.db.models.fields.CharField', [], {'default': "'Misc. Item'", 'max_length': '1024'}),
+            'order': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['shoppingcart.Order']"}),
+            'qty': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
+            'refund_requested_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'db_index': 'True'}),
+            'report_comments': ('django.db.models.fields.TextField', [], {'default': "''"}),
+            'service_fee': ('django.db.models.fields.DecimalField', [], {'default': '0.0', 'max_digits': '30', 'decimal_places': '2'}),
+            'status': ('django.db.models.fields.CharField', [], {'default': "'cart'", 'max_length': '32', 'db_index': 'True'}),
+            'unit_cost': ('django.db.models.fields.DecimalField', [], {'default': '0.0', 'max_digits': '30', 'decimal_places': '2'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+        },
+        'shoppingcart.paidcourseregistration': {
+            'Meta': {'object_name': 'PaidCourseRegistration', '_ormbases': ['shoppingcart.OrderItem']},
+            'course_id': ('xmodule_django.models.CourseKeyField', [], {'max_length': '128', 'db_index': 'True'}),
+            'mode': ('django.db.models.fields.SlugField', [], {'default': "'honor'", 'max_length': '50'}),
+            'orderitem_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['shoppingcart.OrderItem']", 'unique': 'True', 'primary_key': 'True'})
+        },
+        'shoppingcart.paidcourseregistrationannotation': {
+            'Meta': {'object_name': 'PaidCourseRegistrationAnnotation'},
+            'annotation': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+            'course_id': ('xmodule_django.models.CourseKeyField', [], {'unique': 'True', 'max_length': '128', 'db_index': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+        },
+        'student.courseenrollment': {
+            'Meta': {'ordering': "('user', 'course_id')", 'unique_together': "(('user', 'course_id'),)", 'object_name': 'CourseEnrollment'},
+            'course_id': ('xmodule_django.models.CourseKeyField', [], {'max_length': '255', 'db_index': 'True'}),
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'db_index': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'mode': ('django.db.models.fields.CharField', [], {'default': "'honor'", 'max_length': '100'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+        }
+    }
+
+    complete_apps = ['shoppingcart']
\ No newline at end of file
diff --git a/lms/djangoapps/shoppingcart/migrations/0009_auto__del_coupons__add_courseregistrationcode__add_coupon__chg_field_c.py b/lms/djangoapps/shoppingcart/migrations/0009_auto__del_coupons__add_courseregistrationcode__add_coupon__chg_field_c.py
new file mode 100644
index 00000000000..3850c104999
--- /dev/null
+++ b/lms/djangoapps/shoppingcart/migrations/0009_auto__del_coupons__add_courseregistrationcode__add_coupon__chg_field_c.py
@@ -0,0 +1,216 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        # Deleting model 'Coupons'
+        db.delete_table('shoppingcart_coupons')
+
+        # Adding model 'CourseRegistrationCode'
+        db.create_table('shoppingcart_courseregistrationcode', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('code', self.gf('django.db.models.fields.CharField')(max_length=32, db_index=True)),
+            ('course_id', self.gf('xmodule_django.models.CourseKeyField')(max_length=255, db_index=True)),
+            ('transaction_group_name', self.gf('django.db.models.fields.CharField')(db_index=True, max_length=255, null=True, blank=True)),
+            ('created_by', self.gf('django.db.models.fields.related.ForeignKey')(related_name='created_by_user', to=orm['auth.User'])),
+            ('created_at', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime(2014, 7, 1, 0, 0))),
+            ('redeemed_by', self.gf('django.db.models.fields.related.ForeignKey')(related_name='redeemed_by_user', null=True, to=orm['auth.User'])),
+            ('redeemed_at', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime(2014, 7, 1, 0, 0), null=True)),
+        ))
+        db.send_create_signal('shoppingcart', ['CourseRegistrationCode'])
+
+        # Adding model 'Coupon'
+        db.create_table('shoppingcart_coupon', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('code', self.gf('django.db.models.fields.CharField')(max_length=32, db_index=True)),
+            ('description', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
+            ('course_id', self.gf('xmodule_django.models.CourseKeyField')(max_length=255)),
+            ('percentage_discount', self.gf('django.db.models.fields.IntegerField')(default=0)),
+            ('created_by', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])),
+            ('created_at', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime(2014, 7, 1, 0, 0))),
+            ('is_active', self.gf('django.db.models.fields.BooleanField')(default=True)),
+        ))
+        db.send_create_signal('shoppingcart', ['Coupon'])
+
+
+        # Changing field 'CouponRedemption.coupon'
+        db.alter_column('shoppingcart_couponredemption', 'coupon_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['shoppingcart.Coupon']))
+        # Deleting field 'OrderItem.discount_price'
+        db.delete_column('shoppingcart_orderitem', 'discount_price')
+
+        # Adding field 'OrderItem.list_price'
+        db.add_column('shoppingcart_orderitem', 'list_price',
+                      self.gf('django.db.models.fields.DecimalField')(null=True, max_digits=30, decimal_places=2),
+                      keep_default=False)
+
+
+    def backwards(self, orm):
+        # Adding model 'Coupons'
+        db.create_table('shoppingcart_coupons', (
+            ('code', self.gf('django.db.models.fields.CharField')(max_length=32, db_index=True)),
+            ('percentage_discount', self.gf('django.db.models.fields.IntegerField')(default=0)),
+            ('description', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
+            ('course_id', self.gf('xmodule_django.models.CourseKeyField')(max_length=255)),
+            ('created_at', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime(2014, 6, 24, 0, 0))),
+            ('is_active', self.gf('django.db.models.fields.BooleanField')(default=True)),
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('created_by', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])),
+        ))
+        db.send_create_signal('shoppingcart', ['Coupons'])
+
+        # Deleting model 'CourseRegistrationCode'
+        db.delete_table('shoppingcart_courseregistrationcode')
+
+        # Deleting model 'Coupon'
+        db.delete_table('shoppingcart_coupon')
+
+
+        # Changing field 'CouponRedemption.coupon'
+        db.alter_column('shoppingcart_couponredemption', 'coupon_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['shoppingcart.Coupons']))
+        # Adding field 'OrderItem.discount_price'
+        db.add_column('shoppingcart_orderitem', 'discount_price',
+                      self.gf('django.db.models.fields.DecimalField')(null=True, max_digits=30, decimal_places=2),
+                      keep_default=False)
+
+        # Deleting field 'OrderItem.list_price'
+        db.delete_column('shoppingcart_orderitem', 'list_price')
+
+
+    models = {
+        'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        'auth.permission': {
+            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        'auth.user': {
+            'Meta': {'object_name': 'User'},
+            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'contenttypes.contenttype': {
+            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        'shoppingcart.certificateitem': {
+            'Meta': {'object_name': 'CertificateItem', '_ormbases': ['shoppingcart.OrderItem']},
+            'course_enrollment': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['student.CourseEnrollment']"}),
+            'course_id': ('xmodule_django.models.CourseKeyField', [], {'max_length': '128', 'db_index': 'True'}),
+            'mode': ('django.db.models.fields.SlugField', [], {'max_length': '50'}),
+            'orderitem_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['shoppingcart.OrderItem']", 'unique': 'True', 'primary_key': 'True'})
+        },
+        'shoppingcart.coupon': {
+            'Meta': {'object_name': 'Coupon'},
+            'code': ('django.db.models.fields.CharField', [], {'max_length': '32', 'db_index': 'True'}),
+            'course_id': ('xmodule_django.models.CourseKeyField', [], {'max_length': '255'}),
+            'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2014, 7, 1, 0, 0)'}),
+            'created_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
+            'description': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'percentage_discount': ('django.db.models.fields.IntegerField', [], {'default': '0'})
+        },
+        'shoppingcart.couponredemption': {
+            'Meta': {'object_name': 'CouponRedemption'},
+            'coupon': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['shoppingcart.Coupon']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'order': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['shoppingcart.Order']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+        },
+        'shoppingcart.courseregistrationcode': {
+            'Meta': {'object_name': 'CourseRegistrationCode'},
+            'code': ('django.db.models.fields.CharField', [], {'max_length': '32', 'db_index': 'True'}),
+            'course_id': ('xmodule_django.models.CourseKeyField', [], {'max_length': '255', 'db_index': 'True'}),
+            'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2014, 7, 1, 0, 0)'}),
+            'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'created_by_user'", 'to': "orm['auth.User']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'redeemed_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2014, 7, 1, 0, 0)', 'null': 'True'}),
+            'redeemed_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'redeemed_by_user'", 'null': 'True', 'to': "orm['auth.User']"}),
+            'transaction_group_name': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'null': 'True', 'blank': 'True'})
+        },
+        'shoppingcart.order': {
+            'Meta': {'object_name': 'Order'},
+            'bill_to_cardtype': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}),
+            'bill_to_ccnum': ('django.db.models.fields.CharField', [], {'max_length': '8', 'blank': 'True'}),
+            'bill_to_city': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
+            'bill_to_country': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
+            'bill_to_first': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
+            'bill_to_last': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
+            'bill_to_postalcode': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}),
+            'bill_to_state': ('django.db.models.fields.CharField', [], {'max_length': '8', 'blank': 'True'}),
+            'bill_to_street1': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}),
+            'bill_to_street2': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}),
+            'currency': ('django.db.models.fields.CharField', [], {'default': "'usd'", 'max_length': '8'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'processor_reply_dump': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'purchase_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'refunded_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'status': ('django.db.models.fields.CharField', [], {'default': "'cart'", 'max_length': '32'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+        },
+        'shoppingcart.orderitem': {
+            'Meta': {'object_name': 'OrderItem'},
+            'currency': ('django.db.models.fields.CharField', [], {'default': "'usd'", 'max_length': '8'}),
+            'fulfilled_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'db_index': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'line_desc': ('django.db.models.fields.CharField', [], {'default': "'Misc. Item'", 'max_length': '1024'}),
+            'list_price': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '30', 'decimal_places': '2'}),
+            'order': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['shoppingcart.Order']"}),
+            'qty': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
+            'refund_requested_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'db_index': 'True'}),
+            'report_comments': ('django.db.models.fields.TextField', [], {'default': "''"}),
+            'service_fee': ('django.db.models.fields.DecimalField', [], {'default': '0.0', 'max_digits': '30', 'decimal_places': '2'}),
+            'status': ('django.db.models.fields.CharField', [], {'default': "'cart'", 'max_length': '32', 'db_index': 'True'}),
+            'unit_cost': ('django.db.models.fields.DecimalField', [], {'default': '0.0', 'max_digits': '30', 'decimal_places': '2'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+        },
+        'shoppingcart.paidcourseregistration': {
+            'Meta': {'object_name': 'PaidCourseRegistration', '_ormbases': ['shoppingcart.OrderItem']},
+            'course_id': ('xmodule_django.models.CourseKeyField', [], {'max_length': '128', 'db_index': 'True'}),
+            'mode': ('django.db.models.fields.SlugField', [], {'default': "'honor'", 'max_length': '50'}),
+            'orderitem_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['shoppingcart.OrderItem']", 'unique': 'True', 'primary_key': 'True'})
+        },
+        'shoppingcart.paidcourseregistrationannotation': {
+            'Meta': {'object_name': 'PaidCourseRegistrationAnnotation'},
+            'annotation': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+            'course_id': ('xmodule_django.models.CourseKeyField', [], {'unique': 'True', 'max_length': '128', 'db_index': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+        },
+        'student.courseenrollment': {
+            'Meta': {'ordering': "('user', 'course_id')", 'unique_together': "(('user', 'course_id'),)", 'object_name': 'CourseEnrollment'},
+            'course_id': ('xmodule_django.models.CourseKeyField', [], {'max_length': '255', 'db_index': 'True'}),
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'db_index': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'mode': ('django.db.models.fields.CharField', [], {'default': "'honor'", 'max_length': '100'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+        }
+    }
+
+    complete_apps = ['shoppingcart']
\ No newline at end of file
diff --git a/lms/djangoapps/shoppingcart/models.py b/lms/djangoapps/shoppingcart/models.py
index 8e54a977b62..406257fd016 100644
--- a/lms/djangoapps/shoppingcart/models.py
+++ b/lms/djangoapps/shoppingcart/models.py
@@ -31,7 +31,7 @@ from xmodule_django.models import CourseKeyField
 from verify_student.models import SoftwareSecurePhotoVerification
 
 from .exceptions import (InvalidCartItem, PurchasedCallbackException, ItemAlreadyInCartException,
-                         AlreadyEnrolledInCourseException, CourseDoesNotExistException)
+                         AlreadyEnrolledInCourseException, CourseDoesNotExistException, CouponAlreadyExistException, ItemDoesNotExistAgainstCouponException)
 
 from microsite_configuration import microsite
 
@@ -217,6 +217,7 @@ class OrderItem(models.Model):
     status = models.CharField(max_length=32, default='cart', choices=ORDER_STATUSES, db_index=True)
     qty = models.IntegerField(default=1)
     unit_cost = models.DecimalField(default=0.0, decimal_places=2, max_digits=30)
+    list_price = models.DecimalField(decimal_places=2, max_digits=30, null=True)
     line_desc = models.CharField(default="Misc. Item", max_length=1024)
     currency = models.CharField(default="usd", max_length=8)  # lower case ISO currency codes
     fulfilled_time = models.DateTimeField(null=True, db_index=True)
@@ -304,6 +305,78 @@ class OrderItem(models.Model):
         return ''
 
 
+class CourseRegistrationCode(models.Model):
+    """
+    This table contains registration codes
+    With registration code, a user can register for a course for free
+    """
+    code = models.CharField(max_length=32, db_index=True)
+    course_id = CourseKeyField(max_length=255, db_index=True)
+    transaction_group_name = models.CharField(max_length=255, db_index=True, null=True, blank=True)
+    created_by = models.ForeignKey(User, related_name='created_by_user')
+    created_at = models.DateTimeField(default=datetime.now(pytz.utc))
+    redeemed_by = models.ForeignKey(User, null=True, related_name='redeemed_by_user')
+    redeemed_at = models.DateTimeField(default=datetime.now(pytz.utc), null=True)
+
+
+class Coupon(models.Model):
+    """
+    This table contains coupon codes
+    A user can get a discount offer on course if provide coupon code
+    """
+    code = models.CharField(max_length=32, db_index=True)
+    description = models.CharField(max_length=255, null=True, blank=True)
+    course_id = CourseKeyField(max_length=255)
+    percentage_discount = models.IntegerField(default=0)
+    created_by = models.ForeignKey(User)
+    created_at = models.DateTimeField(default=datetime.now(pytz.utc))
+    is_active = models.BooleanField(default=True)
+
+
+class CouponRedemption(models.Model):
+    """
+    This table contain coupon redemption info
+    """
+    order = models.ForeignKey(Order, db_index=True)
+    user = models.ForeignKey(User, db_index=True)
+    coupon = models.ForeignKey(Coupon, db_index=True)
+
+    @classmethod
+    def get_discount_price(cls, percentage_discount, value):
+        """
+        return discounted price against coupon
+        """
+        discount = Decimal("{0:.2f}".format(Decimal(percentage_discount / 100.00) * value))
+        return value - discount
+
+    @classmethod
+    def add_coupon_redemption(cls, coupon, order):
+        """
+        add coupon info into coupon_redemption model
+        """
+        cart_items = order.orderitem_set.all().select_subclasses()
+
+        for item in cart_items:
+            if getattr(item, 'course_id'):
+                if item.course_id == coupon.course_id:
+                    coupon_redemption, created = cls.objects.get_or_create(order=order, user=order.user, coupon=coupon)
+                    if not created:
+                        log.exception("Coupon '{0}' already exist for user '{1}' against order id '{2}'"
+                                      .format(coupon.code, order.user.username, order.id))
+                        raise CouponAlreadyExistException
+
+                    discount_price = cls.get_discount_price(coupon.percentage_discount, item.unit_cost)
+                    item.list_price = item.unit_cost
+                    item.unit_cost = discount_price
+                    item.save()
+                    log.info("Discount generated for user {0} against order id '{1}' "
+                             .format(order.user.username, order.id))
+                    return coupon_redemption
+
+        log.warning("Course item does not exist for coupon '{0}'".format(coupon.code))
+        raise ItemDoesNotExistAgainstCouponException
+
+
 class PaidCourseRegistration(OrderItem):
     """
     This is an inventory item for paying for a course registration
@@ -319,6 +392,19 @@ class PaidCourseRegistration(OrderItem):
         return course_id in [item.paidcourseregistration.course_id
                              for item in order.orderitem_set.all().select_subclasses("paidcourseregistration")]
 
+    @classmethod
+    def get_total_amount_of_purchased_item(cls, course_key):
+        """
+        This will return the total amount of money that a purchased course generated
+        """
+        total_cost = 0
+        result = cls.objects.filter(course_id=course_key, status='purchased').aggregate(total=Sum('unit_cost', field='qty * unit_cost'))  # pylint: disable=E1101
+
+        if result['total'] is not None:
+            total_cost = result['total']
+
+        return total_cost
+
     @classmethod
     @transaction.commit_on_success
     def add_to_order(cls, order, course_id, mode_slug=CourseMode.DEFAULT_MODE_SLUG, cost=None, currency=None):
diff --git a/lms/djangoapps/shoppingcart/processors/CyberSource.py b/lms/djangoapps/shoppingcart/processors/CyberSource.py
index 5c980073d71..b2ad8ccd660 100644
--- a/lms/djangoapps/shoppingcart/processors/CyberSource.py
+++ b/lms/djangoapps/shoppingcart/processors/CyberSource.py
@@ -16,8 +16,25 @@ from django.utils.translation import ugettext as _
 from edxmako.shortcuts import render_to_string
 from shoppingcart.models import Order
 from shoppingcart.processors.exceptions import *
+from microsite_configuration import microsite
 
 
+def get_cybersource_config():
+    """
+    This method will return any microsite specific cybersource configuration, otherwise
+    we return the default configuration
+    """
+    config_key = microsite.get_value('cybersource_config_key')
+    config = {}
+    if config_key:
+        # The microsite CyberSource configuration will be subkeys inside of the normal default
+        # CyberSource configuration
+        config = settings.CC_PROCESSOR['CyberSource']['microsites'][config_key]
+    else:
+        config = settings.CC_PROCESSOR['CyberSource']
+
+    return config
+
 def process_postpay_callback(params):
     """
     The top level call to this module, basically
@@ -53,7 +70,7 @@ def processor_hash(value):
     """
     Performs the base64(HMAC_SHA1(key, value)) used by CyberSource Hosted Order Page
     """
-    shared_secret = settings.CC_PROCESSOR['CyberSource'].get('SHARED_SECRET', '')
+    shared_secret = get_cybersource_config().get('SHARED_SECRET', '')
     hash_obj = hmac.new(shared_secret.encode('utf-8'), value.encode('utf-8'), sha1)
     return binascii.b2a_base64(hash_obj.digest())[:-1]  # last character is a '\n', which we don't want
 
@@ -63,9 +80,9 @@ def sign(params, signed_fields_key='orderPage_signedFields', full_sig_key='order
     params needs to be an ordered dict, b/c cybersource documentation states that order is important.
     Reverse engineered from PHP version provided by cybersource
     """
-    merchant_id = settings.CC_PROCESSOR['CyberSource'].get('MERCHANT_ID', '')
-    order_page_version = settings.CC_PROCESSOR['CyberSource'].get('ORDERPAGE_VERSION', '7')
-    serial_number = settings.CC_PROCESSOR['CyberSource'].get('SERIAL_NUMBER', '')
+    merchant_id = get_cybersource_config().get('MERCHANT_ID', '')
+    order_page_version = get_cybersource_config().get('ORDERPAGE_VERSION', '7')
+    serial_number = get_cybersource_config().get('SERIAL_NUMBER', '')
 
     params['merchantID'] = merchant_id
     params['orderPage_timestamp'] = int(time.time() * 1000)
@@ -123,7 +140,7 @@ def get_purchase_params(cart):
     return params
 
 def get_purchase_endpoint():
-    return settings.CC_PROCESSOR['CyberSource'].get('PURCHASE_ENDPOINT', '')
+    return get_cybersource_config().get('PURCHASE_ENDPOINT', '')
 
 def payment_accepted(params):
     """
@@ -215,7 +232,9 @@ def record_purchase(params, order):
 
 def get_processor_decline_html(params):
     """Have to parse through the error codes to return a helpful message"""
-    payment_support_email = settings.PAYMENT_SUPPORT_EMAIL
+
+    # see if we have an override in the microsites
+    payment_support_email = microsite.get_value('payment_support_email', settings.PAYMENT_SUPPORT_EMAIL)
 
     msg = dedent(_(
             """
@@ -238,7 +257,8 @@ def get_processor_decline_html(params):
 def get_processor_exception_html(exception):
     """Return error HTML associated with exception"""
 
-    payment_support_email = settings.PAYMENT_SUPPORT_EMAIL
+    # see if we have an override in the microsites
+    payment_support_email = microsite.get_value('payment_support_email', settings.PAYMENT_SUPPORT_EMAIL)
     if isinstance(exception, CCProcessorDataException):
         msg = dedent(_(
                 """
diff --git a/lms/djangoapps/shoppingcart/processors/CyberSource2.py b/lms/djangoapps/shoppingcart/processors/CyberSource2.py
new file mode 100644
index 00000000000..ecc928f8258
--- /dev/null
+++ b/lms/djangoapps/shoppingcart/processors/CyberSource2.py
@@ -0,0 +1,404 @@
+### Implementation of support for the Cybersource Credit card processor using the new
+### Secure Acceptance API. The previous Hosted Order Page API is being deprecated as of 9/14
+### It is mostly the same as the CyberSource.py file, but we have a new file so that we can
+### maintain some backwards-compatibility in case of a need to quickly roll back (i.e.
+### configuration change rather than code rollback )
+
+### The name of this file should be used as the key of the dict in the CC_PROCESSOR setting
+### Implementes interface as specified by __init__.py
+
+import hmac
+import binascii
+import re
+import json
+import uuid
+from datetime import datetime
+from collections import OrderedDict, defaultdict
+from decimal import Decimal, InvalidOperation
+from hashlib import sha256
+from textwrap import dedent
+from django.conf import settings
+from django.utils.translation import ugettext as _
+from edxmako.shortcuts import render_to_string
+from shoppingcart.models import Order
+from shoppingcart.processors.exceptions import *
+from microsite_configuration import microsite
+from django.core.urlresolvers import reverse
+
+
+def get_cybersource_config():
+    """
+    This method will return any microsite specific cybersource configuration, otherwise
+    we return the default configuration
+    """
+    config_key = microsite.get_value('cybersource_config_key')
+    config = {}
+    if config_key:
+        # The microsite CyberSource configuration will be subkeys inside of the normal default
+        # CyberSource configuration
+        config = settings.CC_PROCESSOR['CyberSource2']['microsites'][config_key]
+    else:
+        config = settings.CC_PROCESSOR['CyberSource2']
+
+    return config
+
+
+def process_postpay_callback(params):
+    """
+    The top level call to this module, basically
+    This function is handed the callback request after the customer has entered the CC info and clicked "buy"
+    on the external Hosted Order Page.
+    It is expected to verify the callback and determine if the payment was successful.
+    It returns {'success':bool, 'order':Order, 'error_html':str}
+    If successful this function must have the side effect of marking the order purchased and calling the
+    purchased_callbacks of the cart  items.
+    If unsuccessful this function should not have those side effects but should try to figure out why and
+    return a helpful-enough error message in error_html.
+    """
+    try:
+        result = payment_accepted(params)
+        if result['accepted']:
+            # SUCCESS CASE first, rest are some sort of oddity
+            record_purchase(params, result['order'])
+            return {'success': True,
+                    'order': result['order'],
+                    'error_html': ''}
+        else:
+            return {'success': False,
+                    'order': result['order'],
+                    'error_html': get_processor_decline_html(params)}
+    except CCProcessorException as error:
+        return {'success': False,
+                'order': None,  # due to exception we may not have the order
+                'error_html': get_processor_exception_html(error)}
+
+
+def processor_hash(value):
+    """
+    Performs the base64(HMAC_SHA1(key, value)) used by CyberSource Hosted Order Page
+    """
+    secret_key = get_cybersource_config().get('SECRET_KEY', '')
+    hash_obj = hmac.new(secret_key, value, sha256)
+    return binascii.b2a_base64(hash_obj.digest())[:-1]  # last character is a '\n', which we don't want
+
+
+def sign(params, signed_fields_key='signed_field_names', full_sig_key='signature'):
+    """
+    params needs to be an ordered dict, b/c cybersource documentation states that order is important.
+    Reverse engineered from PHP version provided by cybersource
+    """
+    fields = u",".join(params.keys())
+    params[signed_fields_key] = fields
+
+    signed_fields = params.get(signed_fields_key, '').split(',')
+    values = u",".join([u"{0}={1}".format(i, params.get(i, '')) for i in signed_fields])
+    params[full_sig_key] = processor_hash(values)
+    params[signed_fields_key] = fields
+
+    return params
+
+
+def render_purchase_form_html(cart):
+    """
+    Renders the HTML of the hidden POST form that must be used to initiate a purchase with CyberSource
+    """
+    return render_to_string('shoppingcart/cybersource_form.html', {
+        'action': get_purchase_endpoint(),
+        'params': get_signed_purchase_params(cart),
+    })
+
+
+def get_signed_purchase_params(cart):
+    """
+    This method will return a digitally signed set of CyberSource parameters
+    """
+    return sign(get_purchase_params(cart))
+
+
+def get_purchase_params(cart):
+    """
+    This method will build out a dictionary of parameters needed by CyberSource to complete the transaction
+    """
+    total_cost = cart.total_cost
+    amount = "{0:0.2f}".format(total_cost)
+    params = OrderedDict()
+
+    params['amount'] = amount
+    params['currency'] = cart.currency
+    params['orderNumber'] = "OrderId: {0:d}".format(cart.id)
+
+    params['access_key'] = get_cybersource_config().get('ACCESS_KEY', '')
+    params['profile_id'] = get_cybersource_config().get('PROFILE_ID', '')
+    params['reference_number'] = cart.id
+    params['transaction_type'] = 'sale'
+
+    params['locale'] = 'en'
+    params['signed_date_time'] =  datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ')
+    params['signed_field_names'] = 'access_key,profile_id,amount,currency,transaction_type,reference_number,signed_date_time,locale,transaction_uuid,signed_field_names,unsigned_field_names,orderNumber'
+    params['unsigned_field_names'] = ''
+    params['transaction_uuid'] = uuid.uuid4()
+    params['payment_method'] = 'card'
+
+    if hasattr(cart, 'context') and 'request_domain' in cart.context:
+        params['override_custom_receipt_page'] = '{0}{1}'.format(
+            cart.context['request_domain'],
+            reverse('shoppingcart.views.postpay_callback')
+        )
+
+    return params
+
+
+def get_purchase_endpoint():
+    """
+    Helper function to return the CyberSource endpoint configuration
+    """
+    return get_cybersource_config().get('PURCHASE_ENDPOINT', '')
+
+
+def payment_accepted(params):
+    """
+    Check that cybersource has accepted the payment
+    params: a dictionary of POST parameters returned by CyberSource in their post-payment callback
+
+    returns: true if the payment was correctly accepted, for the right amount
+             false if the payment was not accepted
+
+    raises: CCProcessorDataException if the returned message did not provide required parameters
+            CCProcessorWrongAmountException if the amount charged is different than the order amount
+
+    """
+    #make sure required keys are present and convert their values to the right type
+    valid_params = {}
+    for key, key_type in [('req_reference_number', int),
+                          ('req_currency', str),
+                          ('decision', str)]:
+        if key not in params:
+            raise CCProcessorDataException(
+                _("The payment processor did not return a required parameter: {0}".format(key))
+            )
+        try:
+            valid_params[key] = key_type(params[key])
+        except ValueError:
+            raise CCProcessorDataException(
+                _("The payment processor returned a badly-typed value {0} for param {1}.".format(params[key], key))
+            )
+
+    try:
+        order = Order.objects.get(id=valid_params['req_reference_number'])
+    except Order.DoesNotExist:
+        raise CCProcessorDataException(_("The payment processor accepted an order whose number is not in our system."))
+
+    if valid_params['decision'] == 'ACCEPT':
+        try:
+            # Moved reading of charged_amount here from the valid_params loop above because
+            # only 'ACCEPT' messages have a 'ccAuthReply_amount' parameter
+            charged_amt = Decimal(params['auth_amount'])
+        except InvalidOperation:
+            raise CCProcessorDataException(
+                _("The payment processor returned a badly-typed value {0} for param {1}.".format(
+                    params['auth_amount'], 'auth_amount'))
+            )
+
+        if charged_amt == order.total_cost and valid_params['req_currency'] == order.currency:
+            return {'accepted': True,
+                    'amt_charged': charged_amt,
+                    'currency': valid_params['req_currency'],
+                    'order': order}
+        else:
+            raise CCProcessorWrongAmountException(
+                _("The amount charged by the processor {0} {1} is different than the total cost of the order {2} {3}."
+                    .format(charged_amt, valid_params['req_currency'],
+                            order.total_cost, order.currency))
+            )
+    else:
+        return {'accepted': False,
+                'amt_charged': 0,
+                'currency': 'usd',
+                'order': order}
+
+
+def record_purchase(params, order):
+    """
+    Record the purchase and run purchased_callbacks
+    """
+    ccnum_str = params.get('req_card_number', '')
+    mm = re.search("\d", ccnum_str)
+    if mm:
+        ccnum = ccnum_str[mm.start():]
+    else:
+        ccnum = "####"
+
+    order.purchase(
+        first=params.get('req_bill_to_forename', ''),
+        last=params.get('req_bill_to_surname', ''),
+        street1=params.get('req_bill_to_address_line1', ''),
+        street2=params.get('req_bill_to_address_line2', ''),
+        city=params.get('req_bill_to_address_city', ''),
+        state=params.get('req_bill_to_address_state', ''),
+        country=params.get('req_bill_to_address_country', ''),
+        postalcode=params.get('req_bill_to_address_postal_code', ''),
+        ccnum=ccnum,
+        cardtype=CARDTYPE_MAP[params.get('req_card_type', '')],
+        processor_reply_dump=json.dumps(params)
+    )
+
+
+def get_processor_decline_html(params):
+    """Have to parse through the error codes to return a helpful message"""
+    payment_support_email = microsite.get_value('payment_support_email', settings.PAYMENT_SUPPORT_EMAIL)
+
+    msg = dedent(_(
+            """
+            <p class="error_msg">
+            Sorry! Our payment processor did not accept your payment.
+            The decision they returned was <span class="decision">{decision}</span>,
+            and the reason was <span class="reason">{reason_code}:{reason_msg}</span>.
+            You were not charged. Please try a different form of payment.
+            Contact us with payment-related questions at {email}.
+            </p>
+            """))
+
+    return msg.format(
+        decision=params['decision'],
+        reason_code=params['reason_code'],
+        reason_msg=REASONCODE_MAP[params['reason_code']],
+        email=payment_support_email
+    )
+
+
+def get_processor_exception_html(exception):
+    """Return error HTML associated with exception"""
+
+    payment_support_email = microsite.get_value('payment_support_email', settings.PAYMENT_SUPPORT_EMAIL)
+    if isinstance(exception, CCProcessorDataException):
+        msg = dedent(_(
+                """
+                <p class="error_msg">
+                Sorry! Our payment processor sent us back a payment confirmation that had inconsistent data!
+                We apologize that we cannot verify whether the charge went through and take further action on your order.
+                The specific error message is: <span class="exception_msg">{msg}</span>.
+                Your credit card may possibly have been charged.  Contact us with payment-specific questions at {email}.
+                </p>
+                """.format(msg=exception.message, email=payment_support_email)))
+        return msg
+    elif isinstance(exception, CCProcessorWrongAmountException):
+        msg = dedent(_(
+                """
+                <p class="error_msg">
+                Sorry! Due to an error your purchase was charged for a different amount than the order total!
+                The specific error message is: <span class="exception_msg">{msg}</span>.
+                Your credit card has probably been charged. Contact us with payment-specific questions at {email}.
+                </p>
+                """.format(msg=exception.message, email=payment_support_email)))
+        return msg
+
+    # fallthrough case, which basically never happens
+    return '<p class="error_msg">EXCEPTION!</p>'
+
+
+CARDTYPE_MAP = defaultdict(lambda: "UNKNOWN")
+CARDTYPE_MAP.update(
+    {
+        '001': 'Visa',
+        '002': 'MasterCard',
+        '003': 'American Express',
+        '004': 'Discover',
+        '005': 'Diners Club',
+        '006': 'Carte Blanche',
+        '007': 'JCB',
+        '014': 'EnRoute',
+        '021': 'JAL',
+        '024': 'Maestro',
+        '031': 'Delta',
+        '033': 'Visa Electron',
+        '034': 'Dankort',
+        '035': 'Laser',
+        '036': 'Carte Bleue',
+        '037': 'Carta Si',
+        '042': 'Maestro Int.',
+        '043': 'GE Money UK card'
+    }
+)
+
+REASONCODE_MAP = defaultdict(lambda: "UNKNOWN REASON")
+REASONCODE_MAP.update(
+    {
+        '100': _('Successful transaction.'),
+        '102': _('One or more fields in the request contains invalid data.'),
+        '104': dedent(_(
+            """
+            The access_key and transaction_uuid fields for this authorization request matches the access_key and
+            transaction_uuid of another authorization request that you sent in the last 15 minutes.
+            Possible fix: retry the payment after 15 minutes.
+            """)),
+        '110': _('Only a partial amount was approved.'),
+        '200': dedent(_(
+            """
+            The authorization request was approved by the issuing bank but declined by CyberSource
+            becouse it did not pass the Address Verification System (AVS).
+            """)),
+        '201': dedent(_(
+            """
+            The issuing bank has questions about the request. You do not receive an
+            authorization code programmatically, but you might receive one verbally by calling the processor.
+            Possible fix: retry with another form of payment
+            """)),
+        '202': dedent(_(
+            """
+            Expired card. You might also receive this if the expiration date you
+            provided does not match the date the issuing bank has on file.
+            Possible fix: retry with another form of payment
+            """)),
+        '203': dedent(_(
+            """
+            General decline of the card. No other information provided by the issuing bank.
+            Possible fix: retry with another form of payment
+            """)),
+        '204': _('Insufficient funds in the account. Possible fix: retry with another form of payment'),
+        # 205 was Stolen or lost card.  Might as well not show this message to the person using such a card.
+        '205': _('Stolen or lost card'),
+        '207': _('Issuing bank unavailable. Possible fix: retry again after a few minutes'),
+        '208': dedent(_(
+            """
+            Inactive card or card not authorized for card-not-present transactions.
+            Possible fix: retry with another form of payment
+            """)),
+        '210': _('The card has reached the credit limit. Possible fix: retry with another form of payment'),
+        '211': _('Invalid card verification number (CVN). Possible fix: retry with another form of payment'),
+        # 221 was The customer matched an entry on the processor's negative file.
+        # Might as well not show this message to the person using such a card.
+        '221': _('The customer matched an entry on the processors negative file.'),
+        '222': _('Account frozen. Possible fix: retry with another form of payment'),
+        '230': dedent(_(
+            """
+            The authorization request was approved by the issuing bank but declined by
+            CyberSource because it did not pass the CVN check.
+            Possible fix: retry with another form of payment
+            """)),
+        '231': _('Invalid account number. Possible fix: retry with another form of payment'),
+        '232': dedent(_(
+            """
+            The card type is not accepted by the payment processor.
+            Possible fix: retry with another form of payment
+            """)),
+        '233': _('General decline by the processor.  Possible fix: retry with another form of payment'),
+        '234': dedent(_(
+            """
+            There is a problem with the information in your CyberSource account.  Please let us know at {0}
+            """.format(settings.PAYMENT_SUPPORT_EMAIL))),
+        '236': _('Processor Failure.  Possible fix: retry the payment'),
+        '240': dedent(_(
+            """
+            The card type sent is invalid or does not correlate with the credit card number.
+            Possible fix: retry with the same card or another form of payment
+            """)),
+        '475': _('The cardholder is enrolled for payer authentication'),
+        '476': _('Payer authentication could not be authenticated'),
+        '520': dedent(_(
+            """
+            The authorization request was approved by the issuing bank but declined by CyberSource based
+            on your legacy Smart Authorization settings.
+            Possible fix: retry with a different form of payment.
+            """)),
+    }
+)
diff --git a/lms/djangoapps/shoppingcart/processors/tests/test_CyberSource.py b/lms/djangoapps/shoppingcart/processors/tests/test_CyberSource.py
index 6f708f3bc3d..8d1c9aeb519 100644
--- a/lms/djangoapps/shoppingcart/processors/tests/test_CyberSource.py
+++ b/lms/djangoapps/shoppingcart/processors/tests/test_CyberSource.py
@@ -10,6 +10,8 @@ from shoppingcart.models import Order, OrderItem
 from shoppingcart.processors.CyberSource import *
 from shoppingcart.processors.exceptions import *
 from mock import patch, Mock
+from microsite_configuration import microsite
+import mock
 
 
 TEST_CC_PROCESSOR = {
@@ -19,10 +21,28 @@ TEST_CC_PROCESSOR = {
         'SERIAL_NUMBER': '12345',
         'ORDERPAGE_VERSION': '7',
         'PURCHASE_ENDPOINT': '',
+        'microsites': {
+            'test_microsite': {
+                'SHARED_SECRET': 'secret_override',
+                'MERCHANT_ID': 'edx_test_override',
+                'SERIAL_NUMBER': '12345_override',
+                'ORDERPAGE_VERSION': '7',
+                'PURCHASE_ENDPOINT': '',
+            }
+        }
     }
 }
 
 
+def fakemicrosite(name, default=None):
+    """
+    This is a test mocking function to return a microsite configuration
+    """
+    if name == 'cybersource_config_key':
+        return 'test_microsite'
+    else:
+        return None
+
 @override_settings(CC_PROCESSOR=TEST_CC_PROCESSOR)
 class CyberSourceTests(TestCase):
 
@@ -33,6 +53,15 @@ class CyberSourceTests(TestCase):
         self.assertEqual(settings.CC_PROCESSOR['CyberSource']['MERCHANT_ID'], 'edx_test')
         self.assertEqual(settings.CC_PROCESSOR['CyberSource']['SHARED_SECRET'], 'secret')
 
+    def test_microsite_no_override_settings(self):
+        self.assertEqual(get_cybersource_config()['MERCHANT_ID'], 'edx_test')
+        self.assertEqual(get_cybersource_config()['SHARED_SECRET'], 'secret')
+
+    @mock.patch("microsite_configuration.microsite.get_value", fakemicrosite)
+    def test_microsite_override_settings(self):
+        self.assertEqual(get_cybersource_config()['MERCHANT_ID'], 'edx_test_override')
+        self.assertEqual(get_cybersource_config()['SHARED_SECRET'], 'secret_override')
+
     def test_hash(self):
         """
         Tests the hash function.  Basically just hardcodes the answer.
diff --git a/lms/djangoapps/shoppingcart/tests/test_views.py b/lms/djangoapps/shoppingcart/tests/test_views.py
index fc7cd7158d4..8f0e93f8095 100644
--- a/lms/djangoapps/shoppingcart/tests/test_views.py
+++ b/lms/djangoapps/shoppingcart/tests/test_views.py
@@ -14,7 +14,7 @@ from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
 from xmodule.modulestore.tests.factories import CourseFactory
 from courseware.tests.tests import TEST_DATA_MONGO_MODULESTORE
 from shoppingcart.views import _can_download_report, _get_date_from_str
-from shoppingcart.models import Order, CertificateItem, PaidCourseRegistration
+from shoppingcart.models import Order, CertificateItem, PaidCourseRegistration, Coupon
 from student.tests.factories import UserFactory
 from student.models import CourseEnrollment
 from course_modes.models import CourseMode
@@ -22,7 +22,8 @@ from edxmako.shortcuts import render_to_response
 from shoppingcart.processors import render_purchase_form_html
 from mock import patch, Mock
 from shoppingcart.views import initialize_report
-
+from decimal import Decimal
+from student.tests.factories import AdminFactory
 
 def mock_render_purchase_form_html(*args, **kwargs):
     return render_purchase_form_html(*args, **kwargs)
@@ -45,7 +46,10 @@ class ShoppingCartViewsTests(ModuleStoreTestCase):
         self.user = UserFactory.create()
         self.user.set_password('password')
         self.user.save()
+        self.instructor = AdminFactory.create()
         self.cost = 40
+        self.coupon_code = 'abcde'
+        self.percentage_discount = 10
         self.course = CourseFactory.create(org='MITx', number='999', display_name='Robot Super Course')
         self.course_key = self.course.id
         self.course_mode = CourseMode(course_id=self.course_key,
@@ -58,6 +62,29 @@ class ShoppingCartViewsTests(ModuleStoreTestCase):
         self.cart = Order.get_cart_for_user(self.user)
         self.addCleanup(patcher.stop)
 
+    def get_discount(self):
+        """
+        This method simple return the discounted amount
+        """
+        val = Decimal("{0:.2f}".format(Decimal(self.percentage_discount / 100.00) * self.cost))
+        return self.cost - val
+
+    def add_coupon(self, course_key, is_active):
+        """
+        add dummy coupon into models
+        """
+        coupon = Coupon(code=self.coupon_code, description='testing code', course_id=course_key,
+                        percentage_discount=self.percentage_discount, created_by=self.user, is_active=is_active)
+        coupon.save()
+
+    def add_course_to_user_cart(self):
+        """
+        adding course to user cart
+        """
+        self.login_user()
+        reg_item = PaidCourseRegistration.add_to_order(self.cart, self.course_key)
+        return reg_item
+
     def login_user(self):
         self.client.login(username=self.user.username, password="password")
 
@@ -72,6 +99,141 @@ class ShoppingCartViewsTests(ModuleStoreTestCase):
         self.assertEqual(resp.status_code, 400)
         self.assertIn('The course {0} is already in your cart.'.format(self.course_key.to_deprecated_string()), resp.content)
 
+    def test_course_discount_invalid_coupon(self):
+        self.add_coupon(self.course_key, True)
+        self.add_course_to_user_cart()
+        non_existing_code = "non_existing_code"
+        resp = self.client.post(reverse('shoppingcart.views.use_coupon'), {'coupon_code': non_existing_code})
+        self.assertEqual(resp.status_code, 404)
+        self.assertIn("Discount does not exist against coupon '{0}'.".format(non_existing_code), resp.content)
+
+    def test_course_discount_inactive_coupon(self):
+        self.add_coupon(self.course_key, False)
+        self.add_course_to_user_cart()
+        resp = self.client.post(reverse('shoppingcart.views.use_coupon'), {'coupon_code': self.coupon_code})
+        self.assertEqual(resp.status_code, 400)
+        self.assertIn("Coupon '{0}' is inactive.".format(self.coupon_code), resp.content)
+
+    def test_course_does_not_exist_in_cart_against_valid_coupon(self):
+        course_key = self.course_key.to_deprecated_string() + 'testing'
+        self.add_coupon(course_key, True)
+        self.add_course_to_user_cart()
+
+        resp = self.client.post(reverse('shoppingcart.views.use_coupon'), {'coupon_code': self.coupon_code})
+        self.assertEqual(resp.status_code, 404)
+        self.assertIn("Coupon '{0}' is not valid for any course in the shopping cart.".format(self.coupon_code), resp.content)
+
+    def test_course_discount_for_valid_active_coupon_code(self):
+
+        self.add_coupon(self.course_key, True)
+        self.add_course_to_user_cart()
+
+        resp = self.client.post(reverse('shoppingcart.views.use_coupon'), {'coupon_code': self.coupon_code})
+        self.assertEqual(resp.status_code, 200)
+
+        # unit price should be updated for that course
+        item = self.cart.orderitem_set.all().select_subclasses()[0]
+        self.assertEquals(item.unit_cost, self.get_discount())
+
+        # after getting 10 percent discount
+        self.assertEqual(self.cart.total_cost, self.get_discount())
+
+        # now testing coupon code already used scenario, reusing the same coupon code
+        resp = self.client.post(reverse('shoppingcart.views.use_coupon'), {'coupon_code': self.coupon_code})
+        self.assertEqual(resp.status_code, 400)
+        self.assertIn("Coupon '{0}' already used.".format(self.coupon_code), resp.content)
+
+    @patch('shoppingcart.views.log.debug')
+    def test_non_existing_coupon_redemption_on_removing_item(self, debug_log):
+
+        reg_item = self.add_course_to_user_cart()
+        resp = self.client.post(reverse('shoppingcart.views.remove_item', args=[]),
+                                {'id': reg_item.id})
+        debug_log.assert_called_with(
+            'Coupon redemption does not exist for order item id={0}.'.format(reg_item.id))
+
+        self.assertEqual(resp.status_code, 200)
+        self.assertEquals(self.cart.orderitem_set.count(), 0)
+
+    @patch('shoppingcart.views.log.info')
+    def test_existing_coupon_redemption_on_removing_item(self, info_log):
+
+        self.add_coupon(self.course_key, True)
+        reg_item = self.add_course_to_user_cart()
+
+        resp = self.client.post(reverse('shoppingcart.views.use_coupon'), {'coupon_code': self.coupon_code})
+        self.assertEqual(resp.status_code, 200)
+
+        resp = self.client.post(reverse('shoppingcart.views.remove_item', args=[]),
+                                {'id': reg_item.id})
+
+        self.assertEqual(resp.status_code, 200)
+        self.assertEquals(self.cart.orderitem_set.count(), 0)
+        info_log.assert_called_with(
+            'Coupon "{0}" redemption entry removed for user "{1}" for order item "{2}"'.format(self.coupon_code, self.user, reg_item.id))
+
+    @patch('shoppingcart.views.log.info')
+    def test_coupon_discount_for_multiple_courses_in_cart(self, info_log):
+
+        reg_item = self.add_course_to_user_cart()
+        self.add_coupon(self.course_key, True)
+        cert_item = CertificateItem.add_to_order(self.cart, self.verified_course_key, self.cost, 'honor')
+        self.assertEquals(self.cart.orderitem_set.count(), 2)
+
+        resp = self.client.post(reverse('shoppingcart.views.use_coupon'), {'coupon_code': self.coupon_code})
+        self.assertEqual(resp.status_code, 200)
+
+        # unit_cost should be updated for that particular course for which coupon code is registered
+        items = self.cart.orderitem_set.all().select_subclasses()
+        for item in items:
+            if item.id == reg_item.id:
+                self.assertEquals(item.unit_cost, self.get_discount())
+            elif item.id == cert_item.id:
+                self.assertEquals(item.list_price, None)
+
+        # Delete the discounted item, corresponding coupon redemption should be removed for that particular discounted item
+        resp = self.client.post(reverse('shoppingcart.views.remove_item', args=[]),
+                                {'id': reg_item.id})
+
+        self.assertEqual(resp.status_code, 200)
+        self.assertEquals(self.cart.orderitem_set.count(), 1)
+        info_log.assert_called_with(
+            'Coupon "{0}" redemption entry removed for user "{1}" for order item "{2}"'.format(self.coupon_code, self.user, reg_item.id))
+
+    @patch('shoppingcart.views.log.info')
+    def test_delete_certificate_item(self, info_log):
+
+        reg_item = self.add_course_to_user_cart()
+        cert_item = CertificateItem.add_to_order(self.cart, self.verified_course_key, self.cost, 'honor')
+        self.assertEquals(self.cart.orderitem_set.count(), 2)
+
+        # Delete the discounted item, corresponding coupon redemption should be removed for that particular discounted item
+        resp = self.client.post(reverse('shoppingcart.views.remove_item', args=[]),
+                                {'id': cert_item.id})
+
+        self.assertEqual(resp.status_code, 200)
+        self.assertEquals(self.cart.orderitem_set.count(), 1)
+        info_log.assert_called_with(
+            'order item {0} removed for user {1}'.format(cert_item.id, self.user))
+
+    @patch('shoppingcart.views.log.info')
+    def test_remove_coupon_redemption_on_clear_cart(self, info_log):
+
+        reg_item = self.add_course_to_user_cart()
+        CertificateItem.add_to_order(self.cart, self.verified_course_key, self.cost, 'honor')
+        self.assertEquals(self.cart.orderitem_set.count(), 2)
+
+        self.add_coupon(self.course_key, True)
+        resp = self.client.post(reverse('shoppingcart.views.use_coupon'), {'coupon_code': self.coupon_code})
+        self.assertEqual(resp.status_code, 200)
+
+        resp = self.client.post(reverse('shoppingcart.views.clear_cart', args=[]))
+        self.assertEqual(resp.status_code, 200)
+        self.assertEquals(self.cart.orderitem_set.count(), 0)
+
+        info_log.assert_called_with(
+            'Coupon redemption entry removed for user {0} for order {1}'.format(self.user, reg_item.id))
+
     def test_add_course_to_cart_already_registered(self):
         CourseEnrollment.enroll(self.user, self.course_key)
         self.login_user()
@@ -188,6 +350,41 @@ class ShoppingCartViewsTests(ModuleStoreTestCase):
         resp2 = self.client.get(reverse('shoppingcart.views.show_receipt', args=[1000]))
         self.assertEqual(resp2.status_code, 404)
 
+    def test_total_amount_of_purchased_course(self):
+        self.add_course_to_user_cart()
+        self.assertEquals(self.cart.orderitem_set.count(), 1)
+        self.add_coupon(self.course_key, True)
+        resp = self.client.post(reverse('shoppingcart.views.use_coupon'), {'coupon_code': self.coupon_code})
+        self.assertEqual(resp.status_code, 200)
+
+        self.cart.purchase(first='FirstNameTesting123', street1='StreetTesting123')
+
+        # Total amount of a particular course that is purchased by different users
+        total_amount = PaidCourseRegistration.get_total_amount_of_purchased_item(self.course_key)
+        self.assertEqual(total_amount, 36)
+
+        self.client.login(username=self.instructor.username, password="test")
+        cart = Order.get_cart_for_user(self.instructor)
+        PaidCourseRegistration.add_to_order(cart, self.course_key)
+        cart.purchase(first='FirstNameTesting123', street1='StreetTesting123')
+
+        total_amount = PaidCourseRegistration.get_total_amount_of_purchased_item(self.course_key)
+        self.assertEqual(total_amount, 76)
+
+    @patch('shoppingcart.views.render_to_response', render_mock)
+    def test_show_receipt_success_with_valid_coupon_code(self):
+        self.add_course_to_user_cart()
+        self.add_coupon(self.course_key, True)
+
+        resp = self.client.post(reverse('shoppingcart.views.use_coupon'), {'coupon_code': self.coupon_code})
+        self.assertEqual(resp.status_code, 200)
+        self.cart.purchase(first='FirstNameTesting123', street1='StreetTesting123')
+
+        resp = self.client.get(reverse('shoppingcart.views.show_receipt', args=[self.cart.id]))
+        self.assertEqual(resp.status_code, 200)
+        self.assertIn('FirstNameTesting123', resp.content)
+        self.assertIn(str(self.get_discount()), resp.content)
+
     @patch('shoppingcart.views.render_to_response', render_mock)
     def test_show_receipt_success(self):
         reg_item = PaidCourseRegistration.add_to_order(self.cart, self.course_key)
diff --git a/lms/djangoapps/shoppingcart/urls.py b/lms/djangoapps/shoppingcart/urls.py
index 2dd083c88c7..14ea008f97e 100644
--- a/lms/djangoapps/shoppingcart/urls.py
+++ b/lms/djangoapps/shoppingcart/urls.py
@@ -14,6 +14,7 @@ if settings.FEATURES['ENABLE_SHOPPING_CART']:
         url(r'^clear/$', 'clear_cart'),
         url(r'^remove_item/$', 'remove_item'),
         url(r'^add/course/{}/$'.format(settings.COURSE_ID_PATTERN), 'add_course_to_cart', name='add_course_to_cart'),
+        url(r'^use_coupon/$', 'use_coupon'),
     )
 
 if settings.FEATURES.get('ENABLE_PAYMENT_FAKE'):
diff --git a/lms/djangoapps/shoppingcart/views.py b/lms/djangoapps/shoppingcart/views.py
index a01648e0cb0..7f60780cc01 100644
--- a/lms/djangoapps/shoppingcart/views.py
+++ b/lms/djangoapps/shoppingcart/views.py
@@ -14,9 +14,10 @@ from edxmako.shortcuts import render_to_response
 from opaque_keys.edx.locations import SlashSeparatedCourseKey
 from shoppingcart.reports import RefundReport, ItemizedPurchaseReport, UniversityRevenueShareReport, CertificateStatusReport
 from student.models import CourseEnrollment
-from .exceptions import ItemAlreadyInCartException, AlreadyEnrolledInCourseException, CourseDoesNotExistException, ReportTypeDoesNotExistException
-from .models import Order, PaidCourseRegistration, OrderItem
+from .exceptions import ItemAlreadyInCartException, AlreadyEnrolledInCourseException, CourseDoesNotExistException, ReportTypeDoesNotExistException, CouponAlreadyExistException, ItemDoesNotExistAgainstCouponException
+from .models import Order, PaidCourseRegistration, OrderItem, Coupon, CouponRedemption
 from .processors import process_postpay_callback, render_purchase_form_html
+import json
 
 log = logging.getLogger("shoppingcart")
 
@@ -69,6 +70,16 @@ def show_cart(request):
     cart = Order.get_cart_for_user(request.user)
     total_cost = cart.total_cost
     cart_items = cart.orderitem_set.all()
+
+    # add the request protocol, domain, and port to the cart object so that any specific
+    # CC_PROCESSOR implementation can construct callback URLs, if necessary
+    cart.context = {
+        'request_domain': '{0}://{1}'.format(
+            'https' if request.is_secure() else 'http',
+            request.get_host()
+        )
+    }
+
     form_html = render_purchase_form_html(cart)
     return render_to_response("shoppingcart/list.html",
                               {'shoppingcart_items': cart_items,
@@ -81,6 +92,11 @@ def show_cart(request):
 def clear_cart(request):
     cart = Order.get_cart_for_user(request.user)
     cart.clear()
+    coupon_redemption = CouponRedemption.objects.filter(user=request.user, order=cart.id)
+    if coupon_redemption:
+        coupon_redemption.delete()
+        log.info('Coupon redemption entry removed for user {0} for order {1}'.format(request.user, cart.id))
+
     return HttpResponse('Cleared')
 
 
@@ -90,12 +106,50 @@ def remove_item(request):
     try:
         item = OrderItem.objects.get(id=item_id, status='cart')
         if item.user == request.user:
+            order_item_course_id = None
+            if hasattr(item, 'paidcourseregistration'):
+                order_item_course_id = item.paidcourseregistration.course_id
             item.delete()
+            log.info('order item {0} removed for user {1}'.format(item_id, request.user))
+            try:
+                coupon_redemption = CouponRedemption.objects.get(user=request.user, order=item.order_id)
+                if order_item_course_id == coupon_redemption.coupon.course_id:
+                    coupon_redemption.delete()
+                    log.info('Coupon "{0}" redemption entry removed for user "{1}" for order item "{2}"'
+                             .format(coupon_redemption.coupon.code, request.user, item_id))
+            except CouponRedemption.DoesNotExist:
+                log.debug('Coupon redemption does not exist for order item id={0}.'.format(item_id))
     except OrderItem.DoesNotExist:
         log.exception('Cannot remove cart OrderItem id={0}. DoesNotExist or item is already purchased'.format(item_id))
     return HttpResponse('OK')
 
 
+@login_required
+def use_coupon(request):
+    """
+    This method generate discount against valid coupon code and save its entry into coupon redemption table
+    """
+    coupon_code = request.POST["coupon_code"]
+    try:
+        coupon = Coupon.objects.get(code=coupon_code)
+    except Coupon.DoesNotExist:
+        return HttpResponseNotFound(_("Discount does not exist against coupon '{0}'.".format(coupon_code)))
+
+    if coupon.is_active:
+        try:
+            cart = Order.get_cart_for_user(request.user)
+            CouponRedemption.add_coupon_redemption(coupon, cart)
+        except CouponAlreadyExistException:
+            return HttpResponseBadRequest(_("Coupon '{0}' already used.".format(coupon_code)))
+        except ItemDoesNotExistAgainstCouponException:
+            return HttpResponseNotFound(_("Coupon '{0}' is not valid for any course in the shopping cart.".format(coupon_code)))
+
+        response = HttpResponse(json.dumps({'response': 'success'}), content_type="application/json")
+        return response
+    else:
+        return HttpResponseBadRequest(_("Coupon '{0}' is inactive.".format(coupon_code)))
+
+
 @csrf_exempt
 @require_POST
 def postpay_callback(request):
@@ -122,6 +176,7 @@ def show_receipt(request, ordernum):
     Displays a receipt for a particular order.
     404 if order is not yet purchased or request.user != order.user
     """
+
     try:
         order = Order.objects.get(id=ordernum)
     except Order.DoesNotExist:
diff --git a/lms/static/sass/course/instructor/_instructor_2.scss b/lms/static/sass/course/instructor/_instructor_2.scss
index d1c3964d933..76a491e1cd4 100644
--- a/lms/static/sass/course/instructor/_instructor_2.scss
+++ b/lms/static/sass/course/instructor/_instructor_2.scss
@@ -597,7 +597,7 @@ section.instructor-dashboard-content-2 {
     float: left;
     clear: both;
     margin-top: 25px;
-  
+
     .metrics-left, .metrics-left-header {
       position: relative;
       width: 30%;
@@ -611,7 +611,7 @@ section.instructor-dashboard-content-2 {
     .metrics-section.metrics-left {
     	height: 640px;
     }
-    
+
     .metrics-right, .metrics-right-header {
       position: relative;
       width: 65%;
@@ -627,7 +627,7 @@ section.instructor-dashboard-content-2 {
     .metrics-section.metrics-right {
     	height: 295px;
     }
-          
+
     svg {
       .stacked-bar {
         cursor: pointer;
@@ -775,3 +775,267 @@ input[name="subject"] {
     font-weight: bold;
   }
 }
+
+.ecommerce-wrapper{
+  h2{
+    height: 26px;
+    line-height: 26px;
+    span{
+      float: right;
+      font-size: 16px;
+      font-weight: bold;
+      span{
+        background: #ddd;
+        padding: 2px 9px;
+        border-radius: 2px;
+        float: none;
+        font-weight: 400;
+      }
+    }
+  }
+  span.tip{
+    padding: 10px 15px;
+    display: block;
+    border-top: 1px solid #ddd;
+    border-bottom: 1px solid #ddd;
+    background: #f8f4ec;
+    color: #3c3c3c;
+    line-height: 30px;
+    .add{
+      @include button(simple, $blue);
+      @extend .button-reset;
+      font-size: em(13);
+      float: right;
+    }
+  }
+
+}
+#e-commerce{
+  .coupon-errors {
+    background: #FFEEF5;color:#B72667;text-align: center;padding: 10px 0px;
+    font-family: "Open Sans",Verdana,Geneva,sans-serif,sans-serif;font-size: 15px;
+    border-bottom: 1px solid #B72667;
+    margin-bottom: 20px;
+    display: none;
+    }
+  .content{
+    padding: 0 !important;
+  }
+  .coupons-table {
+    width: 100%;
+    tr:nth-child(even){
+        background-color: #f8f8f8;
+        border-bottom: 1px solid #f3f3f3;
+      }
+    tr.always-gray{
+      background: #eee !important;
+      border-top: 2px solid #FFFFFF;
+    }
+    tr.always-white{
+      background: #fff !important;
+      td{
+        padding: 30px 0px 10px;
+      }
+    }
+    .coupons-headings {
+      height: 40px;
+      border-bottom: 1px solid #BEBEBE;
+
+      th:nth-child(5){
+        text-align: center;
+        width: 120px;
+      }
+      th:first-child{
+        padding-left: 20px;
+      }
+      th {
+        text-align: left;
+        border-bottom: 1px solid $border-color-1;
+
+        &.c_code {
+          width: 170px;
+        }
+        &.c_count {
+          width: 85px;
+        }
+        &.c_course_id {
+          width: 320px;
+          word-wrap: break-word;
+        }
+        &.c_discount {
+          width: 90px;
+        }
+        &.c_action {
+          width: 89px;
+        }
+        &.c_dsc{
+          width: 260px;
+          word-wrap: break-word;
+        }
+      }
+    }
+
+    // in_active coupon rows style
+    .inactive_coupon{
+      background:  #FFF0F0 !important;
+      text-decoration: line-through;
+      color: rgba(51,51,51,0.2);
+      border-bottom: 1px solid #fff;
+      td {
+        a {
+          color:  rgba(51,51,51,0.2);
+        }
+      }
+    }
+
+    // coupon items style
+    .coupons-items {
+      td {
+        padding: 10px 0px;
+        position: relative;
+        line-height: normal;
+        span.old-price{
+          left: -75px;
+          position: relative;
+          text-decoration: line-through;
+          color: red;
+          font-size: 12px;
+          top: -1px;
+        }
+      }
+      td:nth-child(5),td:first-child{
+        padding-left: 20px;
+      }
+      td:nth-child(2){
+         line-height: 22px;
+        padding-right: 0px;
+        word-wrap: break-word;
+      }
+      td:nth-child(5){
+        padding-left: 0;
+        text-align: center;
+      }
+      td{
+        a.edit-right{
+          margin-left: 15px;
+        }
+      }
+    }
+  }
+
+  // coupon edit and add modals 
+  #add-coupon-modal, #edit-coupon-modal{
+    .inner-wrapper {
+      background: #fff;
+    }
+    top:-95px !important;
+    width: 650px;
+    margin-left: -325px;
+    border-radius: 2px;
+    input[type="submit"]#update_coupon_button{
+      @include button(simple, $blue);
+      @extend .button-reset;
+    }
+    input[type="submit"]#add_coupon_button{
+      @include button(simple, $blue);
+      @extend .button-reset;
+    }
+    .modal-form-error {
+      box-shadow: inset 0 -1px 2px 0 #f3d9db;
+      -webkit-box-sizing: border-box;
+      -moz-box-sizing: border-box;
+      box-sizing: border-box;
+      margin: 20px 0 10px 0 !important;
+      padding: 20px;
+      border: none;
+      border-bottom: 3px solid #a0050e;
+      background: #fbf2f3;
+    }
+    ol.list-input{
+      li{
+        width: 278px;
+        float: left;
+        label.required:after {
+          content: "*";
+          margin-left: 5px;
+        }
+      }
+      li:nth-child(even){
+        margin-left: 30px !important;
+      }
+      li:nth-child(3), li:nth-child(4){
+        margin-left: 0px !important;
+        width: 100%;
+      }
+      li:nth-child(3) {
+        margin-bottom: 0px !important;
+        textarea {
+          min-height: 100px;
+        }
+      }
+      li:last-child{
+        margin-bottom: 0px !important;
+      }
+
+    }
+    #coupon-content {
+      padding: 20px;
+      header {
+        margin: 0;
+        padding: 0;
+        h2 {
+          font-size: 24px;
+          font-weight: 100;
+          color: #1580b0;
+          text-align: left;
+        }
+      }
+      .instructions p {
+        margin-bottom: 5px;
+      }
+      form {
+        border-radius: 0;
+        box-shadow: none;
+        margin: 0;
+        border: none;
+        padding: 0;
+        .group-form {
+          margin: 0;
+          padding-top: 0;
+          padding-bottom: 20px;
+        }
+        .list-input {
+          margin: 0;
+          padding: 0;
+          list-style: none;
+        }
+        .readonly {
+          background-color: #eee !important;
+          color: #aaa;
+        }
+        .field {
+          margin: 0 0 20px 0;
+        }
+        .field.required label {
+          font-weight: 600;
+        }
+        .field label {
+          -webkit-transition: color 0.15s ease-in-out 0s;
+          -moz-transition: color 0.15s ease-in-out 0s;
+          transition: color 0.15s ease-in-out 0s;
+          margin: 0 0 5px 0;
+          color: #333;
+        }
+        .field.text input {
+          background: #fff;
+          margin-bottom: 0;
+        }
+        .field input {
+          width: 100%;
+          margin: 0;
+          padding: 10px 15px;
+        }
+      }
+    }
+  }
+}
diff --git a/lms/static/sass/views/_shoppingcart.scss b/lms/static/sass/views/_shoppingcart.scss
index 1b3da66893d..6c080851814 100644
--- a/lms/static/sass/views/_shoppingcart.scss
+++ b/lms/static/sass/views/_shoppingcart.scss
@@ -12,14 +12,20 @@
   border: 1px solid $red;
 
 }
-
+.cart-errors{
+  background: #FFEEF5;color:#B72667;text-align: center;padding: 10px 0px;
+  font-family: "Open Sans",Verdana,Geneva,sans-serif,sans-serif;font-size: 15px;
+  border-bottom: 1px solid #B72667;
+  margin-bottom: 20px;
+  display: none;
+}
 .cart-list {
   padding: 30px;
   margin-top: 40px;
   border-radius: 3px;
   border: 1px solid $border-color-1;
   background-color: $action-primary-fg;
-  
+
   > h2 {
     font-size: 1.5em;
     color: $base-font-color;
@@ -27,13 +33,42 @@
   
   .cart-table {
     width: 100%;
-    
+    tr:nth-child(even){
+        background-color: #f8f8f8;
+        border-bottom: 1px solid #f3f3f3;
+      }
+    tr.always-gray{
+      background: #eee !important;
+      border-top: 2px solid #FFFFFF;
+    }
+    tr.always-white{
+      background: #fff !important;
+      td{
+        padding: 30px 0px 10px;
+      }
+    }
+    tr{
+        td.cart-total{
+            padding: 10px 0;
+            span{
+                display: inline-block;
+                margin-right: 15px;
+                margin-left: 15px;
+                font-weight: bold;
+              }
+        }
+
+    }
     .cart-headings {
       height: 35px;
-      
+      border-bottom: 1px solid #BEBEBE;
+
+      th:nth-child(5),th:first-child{
+        text-align: center;
+        width: 120px;
+      }
       th {
         text-align: left;
-        padding-left: 5px;
         border-bottom: 1px solid $border-color-1;
         
         &.qty {
@@ -48,12 +83,35 @@
         &.cur {
           width: 100px;
         }
+        &.dsc{
+          width: 640px;
+          padding-right: 50px;
+        }
       }
     }
     
     .cart-items {
       td {
-        padding: 10px 25px;
+        padding: 10px 0px;
+        position: relative;
+        line-height: normal;
+        span.old-price{
+          left: -75px;
+          position: relative;
+          text-decoration: line-through;
+          color: red;
+          font-size: 12px;
+          top: -1px;
+        }
+      }
+      td:nth-child(5),td:first-child{
+        text-align: center;
+
+
+      }
+      td:nth-child(2){
+         line-height: 22px;
+        padding-right: 50px;
       }
     }
     
@@ -64,6 +122,7 @@
           font-weight: bold;
           padding: 10px 25px;
         }
+
       }
     }
   }
@@ -80,11 +139,10 @@
     .items-ordered {
       padding-top: 50px;
     }
-    
+
     tr {
-      
     }
-    
+
     th {
       text-align: left;
       padding: 25px 0 15px 0;
@@ -105,6 +163,9 @@
     tr.order-item {
       td {
         padding-bottom: 10px;
+        span.old-price{
+          text-decoration: line-through !important;
+        }
       }
     }
   }
diff --git a/lms/templates/instructor/instructor_dashboard_2/add_coupon_modal.html b/lms/templates/instructor/instructor_dashboard_2/add_coupon_modal.html
new file mode 100644
index 00000000000..8befb90f109
--- /dev/null
+++ b/lms/templates/instructor/instructor_dashboard_2/add_coupon_modal.html
@@ -0,0 +1,62 @@
+<%! from django.utils.translation import ugettext as _ %>
+<%! from django.core.urlresolvers import reverse %>
+<%page args="section_data"/>
+<section id="add-coupon-modal" class="modal" role="dialog" tabindex="-1" aria-label="${_('Password Reset')}">
+  <div class="inner-wrapper">
+    <button class="close-modal">
+      <i class="icon-remove"></i>
+      <span class="sr">
+        ## Translators: this is a control to allow users to exit out of this modal interface (a menu or piece of UI that takes the full focus of the screen)
+        ${_('Close')}
+      </span>
+    </button>
+
+    <div id="coupon-content">
+      <header>
+        <h2>${_("Add Coupon")}</h2>
+      </header>
+
+      <div class="instructions">
+        <p>
+          ${_("Please enter Coupon detail below")}</p>
+      </div>
+
+      <form id="add_coupon_form" action="${section_data['ajax_add_coupon']}" method="post" data-remote="true">
+        <div id="coupon_form_error" class="modal-form-error"></div>
+        <fieldset class="group group-form group-form-requiredinformation">
+          <legend class="is-hidden">${_("Required Information")}</legend>
+
+          <ol class="list-input">
+            <li class="field required text" id="add-coupon-modal-field-code">
+              <label for="coupon_code" class="required">${_("Code")}</label>
+              <input class="" id="coupon_code" type="text" name="code" maxlength="16" value="" placeholder="example: A123DS"
+                    aria-required="true"/>
+            </li>
+            <li class="field required text" id="add-coupon-modal-field-discount">
+              <label for="coupon_discount" class="required text">${_("Percentage Discount")}</label>
+              <input class="field required" id="coupon_discount" type="text" name="discount" value="" maxlength="5"
+                     aria-required="true"/>
+            </li>
+
+            <li class="field" id="add-coupon-modal-field-description">
+              <label for="coupon_description">${_("Description")}</label>
+              <textarea class="field" id="coupon_description" type="text" name="description" value=""
+                     aria-describedby="pwd_reset_email-tip" aria-required="true"> </textarea>
+            </li>
+
+            <li class="field" id="add-coupon-modal-field-course_id">
+              <label for="coupon_course_id">${_("Course ID")}</label>
+              <input class="field readonly" id="coupon_course_id" type="text" name="course_id" value="${section_data['course_id']}"
+                     readonly aria-required="true"/>
+            </li>
+
+          </ol>
+        </fieldset>
+
+        <div class="submit">
+          <input name="submit" type="submit" id="add_coupon_button" value="${_('Add Coupon')}"/>
+        </div>
+      </form>
+    </div>
+  </div>
+</section>
diff --git a/lms/templates/instructor/instructor_dashboard_2/e-commerce.html b/lms/templates/instructor/instructor_dashboard_2/e-commerce.html
new file mode 100644
index 00000000000..90ec8548284
--- /dev/null
+++ b/lms/templates/instructor/instructor_dashboard_2/e-commerce.html
@@ -0,0 +1,256 @@
+<%! from django.utils.translation import ugettext as _ %>
+<%page args="section_data"/>
+<%include file="add_coupon_modal.html" args="section_data=section_data" />
+<%include file="edit_coupon_modal.html" args="section_data=section_data" />
+
+<div class="ecommerce-wrapper">
+  <h2>${_("Coupons List")}
+      %if section_data['total_amount'] is not None:
+           <span>${_("Total Amount: ")}<span>$${section_data['total_amount']}</span></span>
+      %endif
+  </h2>
+
+  <h3 class="coupon-errors" id="coupon-error"></h3>
+  <span class="tip">${_("Coupons Information")} <a id="add_coupon_link" href="#add-coupon-modal" rel="leanModal"
+                                                   class="add blue-button">${_("+ Add Coupon")}</a></span>
+</div>
+<div class="wrapper-content wrapper">
+  <section class="content">
+    %if len(section_data['coupons']):
+    <table class="coupons-table">
+      <thead>
+      <tr class="coupons-headings">
+        <th class="c_code">${_("Code")}</th>
+        <th class="c_dsc">${_("Description")}</th>
+        <th class="c_course_id">${_("Course_id")}</th>
+        <th class="c_discount">${_("Discount(%)")}</th>
+        <th class="c_count">${_("Count")}</th>
+        <th class="c_action">${_("Actions")}</th>
+      </tr>
+      </thead>
+
+      <tbody>
+      %for coupon in section_data['coupons']:
+      %if coupon.is_active == False:
+      <tr class="coupons-items inactive_coupon">
+      %else:
+      <tr class="coupons-items">
+      %endif
+        <td>${coupon.code}</td>
+
+        <td>${coupon.description}</td>
+        <td>${coupon.course_id.to_deprecated_string()}</td>
+        <td>${coupon.percentage_discount}</td>
+        <td>
+           ${ coupon.couponredemption_set.all().count() }
+        </td>
+        <!--<td>${coupon.is_active}</td>-->
+        <td><a data-item-id="${coupon.id}" class='remove_coupon' href='#'>[x]</a><a href="#edit-modal" data-item-id="${coupon.id}" class="edit-right">Edit</a></td>
+      </tr>
+      %endfor
+      </tbody>
+    </table>
+    <a id="edit-modal-trigger" href="#edit-coupon-modal" rel="leanModal"></a>
+    %endif
+  </section>
+</div>
+
+
+<script>
+  $(function () {
+    $('a[rel*=leanModal]').leanModal();
+    $.each($("a.edit-right"), function () {
+      if ($(this).parent().parent('tr').hasClass('inactive_coupon')) {
+        $(this).removeAttr('href')
+      }
+    });
+    $.each($("a.remove_coupon"), function () {
+      if ($(this).parent().parent('tr').hasClass('inactive_coupon')) {
+        $(this).removeAttr('href')
+      }
+    });
+    $('a.edit-right').click(function (event) {
+      $('#edit_coupon_form #coupon_form_error').attr('style', 'display: none');
+      $('#edit_coupon_form #coupon_form_error').text();
+      event.preventDefault();
+      event.stopPropagation();
+      var coupon_id = $(this).data('item-id');
+      $('#coupon_id').val(coupon_id);
+      if ($(this).parent().parent('tr').hasClass('inactive_coupon')) {
+        return false;
+      }
+      $.ajax({
+        type: "POST",
+        data: {id: coupon_id},
+        url: "${section_data['ajax_get_coupon_info']}",
+        success: function (data) {
+            $('#coupon-error').val('');
+            $('#coupon-error').attr('style', 'display: none');
+            $('input#edit_coupon_code').val(data.coupon_code);
+            $('input#edit_coupon_discount').val(data.coupon_discount);
+            $('textarea#edit_coupon_description').val(data.coupon_description);
+            $('input#edit_coupon_course_id').val(data.coupon_course_id);
+            $('#edit-modal-trigger').click();
+          },
+        error: function(jqXHR, textStatus, errorThrown) {
+          var data = $.parseJSON(jqXHR.responseText);
+          $('#coupon-error').html(data.message).show();
+        }
+      });
+    });
+    $('a.remove_coupon').click(function (event) {
+      var anchor = $(this);
+      if (anchor.data("disabled")) {
+        return false;
+      }
+      anchor.data("disabled", "disabled");
+      event.preventDefault();
+      if ($(this).parent().parent('tr').hasClass('inactive_coupon')) {
+        return false;
+      }
+      $.ajax({
+        type: "POST",
+        data: {id: $(this).data('item-id')},
+        url: "${section_data['ajax_remove_coupon_url']}",
+        success: function (data) {
+            anchor.removeData("disabled");
+            location.reload(true);
+          },
+        error: function(jqXHR, textStatus, errorThrown) {
+          var data = $.parseJSON(jqXHR.responseText);
+          $('#coupon-error').html(data.message).show();
+          anchor.removeData("disabled");
+        }
+      });
+    });
+    $('#edit_coupon_form').submit(function () {
+      $("#update_coupon_button").attr('disabled', true);
+      // Get the Code and Discount value and trim it
+      var code = $.trim($('#edit_coupon_code').val());
+      var coupon_discount = $.trim($('#edit_coupon_discount').val());
+
+      // Check if empty of not
+      if (code === '') {
+        $('#edit_coupon_form #coupon_form_error').attr('style', 'display: block !important');
+        $('#edit_coupon_form #coupon_form_error').text("${_('Please Enter the Coupon Code')}");
+        $("#update_coupon_button").removeAttr('disabled');
+        return false;
+      }
+      if (coupon_discount == '0') {
+        $('#edit_coupon_form #coupon_form_error').attr('style', 'display: block !important');
+        $('#edit_coupon_form #coupon_form_error').text("${_('Please Enter the Value Greater than 0')}");
+        $("#update_coupon_button").removeAttr('disabled');
+        return false;
+      }
+      if (!$.isNumeric(coupon_discount)) {
+        $('#edit_coupon_form #coupon_form_error').attr('style', 'display: block !important');
+        $('#edit_coupon_form #coupon_form_error').text("${_('Please Enter the Coupon Discount Value Greater than 0')}");
+        $("#update_coupon_button").removeAttr('disabled');
+        return false;
+      }
+    });
+    $('#add_coupon_link').click(function () {
+      reset_input_fields();
+    });
+    $('#add_coupon_form').submit(function () {
+      $("#add_coupon_button").attr('disabled', true);
+      // Get the Code and Discount value and trim it
+      var code = $.trim($('#coupon_code').val());
+      var coupon_discount = $.trim($('#coupon_discount').val());
+
+      // Check if empty of not
+      if (code === '') {
+        $("#add_coupon_button").removeAttr('disabled');
+        $('#add_coupon_form #coupon_form_error').attr('style', 'display: block !important');
+        $('#add_coupon_form #coupon_form_error').text("${_('Please Enter the Coupon Code')}");
+        return false;
+      }
+      if (coupon_discount == '0') {
+        $('#add_coupon_form #coupon_form_error').attr('style', 'display: block !important');
+        $('#add_coupon_form #coupon_form_error').text("${_('Please Enter the Coupon Discount Value Greater than 0')}");
+        $("#add_coupon_button").removeAttr('disabled');
+        return false;
+      }
+      if (!$.isNumeric(coupon_discount)) {
+        $("#add_coupon_button").removeAttr('disabled');
+        $('#add_coupon_form #coupon_form_error').attr('style', 'display: block !important');
+        $('#add_coupon_form #coupon_form_error').text("${_('Please Enter the Numeric value for Discount')}");
+        return false;
+      }
+    });
+
+    $('#add_coupon_form').on('ajax:complete', function (event, xhr) {
+      if (xhr.status == 200) {
+        location.reload(true);
+      } else {
+        $("#add_coupon_button").removeAttr('disabled');
+        $('#add_coupon_form #coupon_form_error').attr('style', 'display: block !important');
+        $('#add_coupon_form #coupon_form_error').text(xhr.responseText);
+      }
+    });
+
+    $('#edit_coupon_form').on('ajax:complete', function (event, xhr) {
+      if (xhr.status == 200) {
+        location.reload(true);
+      } else {
+        $("#update_coupon_button").removeAttr('disabled');
+        $('#edit_coupon_form #coupon_form_error').attr('style', 'display: block !important');
+        $('#edit_coupon_form #coupon_form_error').text(xhr.responseText);
+      }
+    });
+    // removing close link's default behavior
+    $('.close-modal').click(function (e) {
+      $("#update_coupon_button").removeAttr('disabled');
+      $("#add_coupon_button").removeAttr('disabled');
+      reset_input_fields();
+      e.preventDefault();
+    });
+
+    var onModalClose = function () {
+      $("#add-coupon-modal").attr("aria-hidden", "true");
+      $(".remove_coupon").focus();
+      $("#edit-coupon-modal").attr("aria-hidden", "true");
+      $(".edit-right").focus();
+      $("#add_coupon_button").removeAttr('disabled');
+      $("#update_coupon_button").removeAttr('disabled');
+      reset_input_fields();
+    };
+
+    var cycle_modal_tab = function (from_element_name, to_element_name) {
+      $(from_element_name).on('keydown', function (e) {
+        var keyCode = e.keyCode || e.which;
+        var TAB_KEY = 9;  // 9 corresponds to the tab key
+        if (keyCode === TAB_KEY) {
+          e.preventDefault();
+          $(to_element_name).focus();
+        }
+      });
+    };
+
+    $("#add-coupon-modal .close-modal").click(onModalClose);
+    $("#edit-coupon-modal .close-modal").click(onModalClose);
+    $("#add-coupon-modal .close-modal").click(reset_input_fields);
+
+
+    // Hitting the ESC key will exit the modal
+    $("#add-coupon-modal, #edit-coupon-modal").on("keydown", function (e) {
+      var keyCode = e.keyCode || e.which;
+      // 27 is the ESC key
+      if (keyCode === 27) {
+        e.preventDefault();
+        $("#add-coupon-modal .close-modal").click();
+        $("#edit-coupon-modal .close-modal").click();
+      }
+    });
+  });
+  var reset_input_fields = function () {
+    $('#coupon-error').val('');
+    $('#coupon-error').attr('style', 'display: none');
+    $('#add_coupon_form #coupon_form_error').attr('style', 'display: none');
+    $('#add_coupon_form #coupon_form_error').text();
+    $('input#coupon_code').val('');
+    $('input#coupon_discount').val('');
+    $('textarea#coupon_description').val('');
+
+  }
+</script>
diff --git a/lms/templates/instructor/instructor_dashboard_2/edit_coupon_modal.html b/lms/templates/instructor/instructor_dashboard_2/edit_coupon_modal.html
new file mode 100644
index 00000000000..2d292abf129
--- /dev/null
+++ b/lms/templates/instructor/instructor_dashboard_2/edit_coupon_modal.html
@@ -0,0 +1,63 @@
+<%! from django.utils.translation import ugettext as _ %>
+<%! from django.core.urlresolvers import reverse %>
+<%page args="section_data"/>
+<section id="edit-coupon-modal" class="modal" role="dialog" tabindex="-1" aria-label="${_('Edit Coupon')}">
+  <div class="inner-wrapper">
+    <button class="close-modal">
+      <i class="icon-remove"></i>
+      <span class="sr">
+        ## Translators: this is a control to allow users to exit out of this modal interface (a menu or piece of UI that takes the full focus of the screen)
+        ${_('Close')}
+      </span>
+    </button>
+
+    <div id="coupon-content">
+      <header>
+        <h2>${_("Update Coupon")}</h2>
+      </header>
+
+      <div class="instructions">
+        <p>
+          ${_("Update Coupon Information")}</p>
+      </div>
+
+      <form id="edit_coupon_form" action="${section_data['ajax_update_coupon']}" method="post" data-remote="true">
+        <div id="coupon_form_error" class="modal-form-error"></div>
+        <fieldset class="group group-form group-form-requiredinformation">
+          <legend class="is-hidden">${_("Required Information")}</legend>
+
+          <ol class="list-input">
+            <li class="field required text" id="edit-coupon-modal-field-code">
+              <label for="edit_coupon_code" class="required">${_("Code")}</label>
+              <input class="field" id="edit_coupon_code" type="text" name="code" maxlength="16" value="" placeholder="example: A123DS"
+                    aria-required="true"/>
+            </li>
+            <li class="field required text" id="edit-coupon-modal-field-discount">
+              <label for="edit_coupon_discount" class="required">${_("Percentage Discount")}</label>
+              <input class="field" id="edit_coupon_discount" type="text" name="discount" value="" maxlength="5"
+                     aria-required="true"/>
+            </li>
+
+            <li class="field" id="edit-coupon-modal-field-description">
+              <label for="edit_coupon_description">${_("Description")}</label>
+              <textarea class="field" id="edit_coupon_description" type="text" name="description" value=""
+                    aria-required="true"></textarea>
+            </li>
+
+            <li class="field" id="edit-coupon-modal-field-course_id">
+              <label for="edit_coupon_course_id">${_("Course ID")}</label>
+              <input class="field readonly" id="edit_coupon_course_id" type="text" name="course_id" value=""
+                     readonly aria-required="true"/>
+            </li>
+
+          </ol>
+        </fieldset>
+
+        <div class="submit">
+          <input type="hidden" name="coupon_id" id="coupon_id"/>
+          <input name="submit" type="submit" id="update_coupon_button" value="${_('Update Coupon')}"/>
+        </div>
+      </form>
+    </div>
+  </div>
+</section>
diff --git a/lms/templates/shoppingcart/cybersource_form.html b/lms/templates/shoppingcart/cybersource_form.html
index b29ea79aa16..b07af57ab7d 100644
--- a/lms/templates/shoppingcart/cybersource_form.html
+++ b/lms/templates/shoppingcart/cybersource_form.html
@@ -2,5 +2,6 @@
             % for pk, pv in params.iteritems():
                 <input type="hidden" name="${pk}" value="${pv}" />
             % endfor
+
                 <input type="submit" value="Check Out" />
-        </form>
+        </form> 
\ No newline at end of file
diff --git a/lms/templates/shoppingcart/list.html b/lms/templates/shoppingcart/list.html
index 89eeb0d1ccf..060a813c7ed 100644
--- a/lms/templates/shoppingcart/list.html
+++ b/lms/templates/shoppingcart/list.html
@@ -8,6 +8,7 @@
 
 <section class="container cart-list">
   <h2>${_("Your selected items:")}</h2>
+    <h3 class="cart-errors" id="cart-error">Error goes here.</h3>
   % if shoppingcart_items:
     <table class="cart-table">
       <thead>
@@ -24,24 +25,39 @@
         <tr class="cart-items">
           <td>${item.qty}</td>
           <td>${item.line_desc}</td>
-          <td>${"{0:0.2f}".format(item.unit_cost)}</td>
+          <td>
+              ${"{0:0.2f}".format(item.unit_cost)}
+              % if item.list_price != None:
+                <span class="old-price"> ${"{0:0.2f}".format(item.list_price)}</span>
+              % endif
+          </td>
           <td>${"{0:0.2f}".format(item.line_cost)}</td>
           <td>${item.currency.upper()}</td>
           <td><a data-item-id="${item.id}" class='remove_line_item' href='#'>[x]</a></td>
         </tr>
         % endfor
-        <tr class="cart-headings">
-          <td colspan="4"></td>
-          <th>${_("Total Amount")}</th>
-        </tr>
-        <tr class="cart-totals">
-          <td colspan="4"></td>
-          <td class="cart-total-cost">${"{0:0.2f}".format(amount)}</td>
+        <tr class="always-gray">
+          <td colspan="3"></td>
+            <td colspan="3" valign="middle" class="cart-total" align="right">
+                <b>${_("Total Amount")}: <span> ${"{0:0.2f}".format(amount)} </span> </b>
+            </td>
         </tr>
+
       </tbody>
+      <tfoot>
+        <tr class="always-white">
+            <td colspan="2">
+                 <input type="text" placeholder="Enter coupon code here" name="coupon_code" id="couponCode">
+                 <input type="button" value="Use Coupon" id="cart-coupon">
+            </td>
+            <td colspan="4" align="right">
+                ${form_html}
+            </td>
+        </tr>
+
+      </tfoot>
     </table>
     <!-- <input id="back_input" type="submit" value="Return" /> -->
-    ${form_html}
   % else:
     <p>${_("You have selected no items for purchase.")}</p>
   % endif
@@ -60,9 +76,39 @@
       });
     });
 
+    $('#cart-coupon').click(function(event){
+      event.preventDefault();
+      var post_url = "${reverse('shoppingcart.views.use_coupon')}";
+      $.post(post_url,{
+               "coupon_code" :  $('#couponCode').val(),
+                beforeSend: function(xhr, options){
+                 if($('#couponCode').val() == "") {
+                     showErrorMsgs('Must contain a valid coupon code')
+                     xhr.abort();
+                    }
+                }
+               }
+      )
+      .success(function(data) {
+                 location.reload(true);
+              })
+      .error(function(data,status) {
+                  if(status=="parsererror"){
+                       location.reload(true);
+                  }else{
+                        showErrorMsgs(data.responseText)
+                      }
+             })
+    });
+
     $('#back_input').click(function(){
       history.back();
     });
+
+    function showErrorMsgs(msg){
+        $(".cart-errors").css('display', 'block');
+        $("#cart-error").html(msg);
+    }
   });
 </script>
 
diff --git a/lms/templates/shoppingcart/receipt.html b/lms/templates/shoppingcart/receipt.html
index 4ff99a9f7e1..81da34c0725 100644
--- a/lms/templates/shoppingcart/receipt.html
+++ b/lms/templates/shoppingcart/receipt.html
@@ -3,6 +3,7 @@
 <%! from django.conf import settings %>
 
 <%inherit file="../main.html" />
+
 <%block name="bodyclass">purchase-receipt</%block>
 
 <%block name="pagetitle">${_("Register for [Course Name] | Receipt (Order")} ${order.id})</%block>
@@ -37,16 +38,25 @@
           <tr>
             <th class="qty">${_("Qty")}</th>
             <th class="desc">${_("Description")}</th>
+            <th class="url">${_("URL")}</th>
             <th class="u-pr">${_("Unit Price")}</th>
             <th class="pri">${_("Price")}</th>
             <th class="curr">${_("Currency")}</th>
           </tr>
           % for item in order_items:
+
+            <% course_id = reverse('info', args=[item.course_id.to_deprecated_string()]) %>
+
             <tr class="order-item">
               % if item.status == "purchased":
                 <td>${item.qty}</td>
                 <td>${item.line_desc}</td>
-                <td>${"{0:0.2f}".format(item.unit_cost)}</td>
+                <td><a href="${course_id}" class="enter-course">${_('View Course')}</a></td>
+                <td>${"{0:0.2f}".format(item.unit_cost)}
+                    % if item.list_price != None:
+                        <span class="old-price"> ${"{0:0.2f}".format(item.list_price)}</span>
+                    % endif
+                    </td>
                 <td>${"{0:0.2f}".format(item.line_cost)}</td>
                 <td>${item.currency.upper()}</td></tr>
               % elif item.status == "refunded":
diff --git a/lms/urls.py b/lms/urls.py
index a112daea5be..49465863163 100644
--- a/lms/urls.py
+++ b/lms/urls.py
@@ -283,6 +283,14 @@ if settings.COURSEWARE_ENABLED:
             'instructor.views.instructor_dashboard.instructor_dashboard_2', name="instructor_dashboard"),
         url(r'^courses/{}/instructor/api/'.format(settings.COURSE_ID_PATTERN),
             include('instructor.views.api_urls')),
+        url(r'^courses/{}/remove_coupon$'.format(settings.COURSE_ID_PATTERN),
+            'instructor.views.coupons.remove_coupon', name="remove_coupon"),
+        url(r'^courses/{}/add_coupon$'.format(settings.COURSE_ID_PATTERN),
+            'instructor.views.coupons.add_coupon', name="add_coupon"),
+        url(r'^courses/{}/update_coupon$'.format(settings.COURSE_ID_PATTERN),
+            'instructor.views.coupons.update_coupon', name="update_coupon"),
+        url(r'^courses/{}/get_coupon_info$'.format(settings.COURSE_ID_PATTERN),
+            'instructor.views.coupons.get_coupon_info', name="get_coupon_info"),
 
         # see ENABLE_INSTRUCTOR_LEGACY_DASHBOARD section for legacy dash urls
 
-- 
GitLab