FEATURE: Validate coupon codes on form submit (#74)

FEATURE: Validate coupon codes on form submit (#74)

diff --git a/app/controllers/discourse_subscriptions/subscribe_controller.rb b/app/controllers/discourse_subscriptions/subscribe_controller.rb
index 34a7467..6c3624e 100644
--- a/app/controllers/discourse_subscriptions/subscribe_controller.rb
+++ b/app/controllers/discourse_subscriptions/subscribe_controller.rb
@@ -65,8 +65,13 @@ module DiscourseSubscriptions
       begin
         customer = create_customer(params[:source])
         plan = ::Stripe::Price.retrieve(params[:plan])
-        promo_code = ::Stripe::PromotionCode.list({ code: params[:promo] }) if params[:promo].present?
-        promo_code = promo_code[:data][0] if promo_code && promo_code[:data] # we assume promo codes have a unique name
+
+        if params[:promo].present?
+          promo_code = ::Stripe::PromotionCode.list({ code: params[:promo] })
+          promo_code = promo_code[:data][0] # we assume promo codes have a unique name
+
+          return render_json_error I18n.t("js.discourse_subscriptions.subscribe.invalid_coupon") if promo_code.blank?
+        end
 
         recurring_plan = plan[:type] == 'recurring'
 
diff --git a/assets/javascripts/discourse/controllers/s-show.js.es6 b/assets/javascripts/discourse/controllers/s-show.js.es6
index 32cf5dc..45a91d5 100644
--- a/assets/javascripts/discourse/controllers/s-show.js.es6
+++ b/assets/javascripts/discourse/controllers/s-show.js.es6
@@ -121,7 +121,9 @@ export default Controller.extend({
           }
         })
         .catch((result) => {
-          bootbox.alert(result.errorThrown);
+          bootbox.alert(
+            result.jqXHR.responseJSON.errors[0] || result.errorThrown
+          );
           this.set("loading", false);
         });
     },
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index 0b23a45..a53eca3 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -100,6 +100,7 @@ en:
         view_past: View past purchases
         no_products: There are currently no products available.
         unauthenticated: Log in or create an account to subscribe.
+        invalid_coupon: You entered an invalid coupon code. Please try again.
         card:
           title: Payment
         customer:
diff --git a/spec/requests/subscribe_controller_spec.rb b/spec/requests/subscribe_controller_spec.rb
index ffd23aa..b06b5a2 100644
--- a/spec/requests/subscribe_controller_spec.rb
+++ b/spec/requests/subscribe_controller_spec.rb
@@ -219,59 +219,87 @@ module DiscourseSubscriptions
           end
 
           context "with promo code" do
-            before do
-              ::Stripe::PromotionCode.expects(:list).with({ code: '123' }).returns(
-                data: [{
-                  id: 'promo123',
-                  coupon: { id: 'c123' }
-                }]
-              )
-            end
-
-            it "applies promo code to recurring subscription" do
-              ::Stripe::Price.expects(:retrieve).returns(
-                type: 'recurring',
-                product: 'product_12345',
-                metadata: {
-                  group_name: 'awesome',
-                  trial_period_days: 0
-                }
-              )
-
-              ::Stripe::Subscription.expects(:create).with(
-                customer: 'cus_1234',
-                items: [ price: 'plan_1234' ],
-                metadata: { user_id: user.id, username: user.username_lower },
-                trial_period_days: 0,
-                promotion_code: 'promo123'
-              ).returns(status: 'active', customer: 'cus_1234')
-
-              post "/s/create.json", params: { plan: 'plan_1234', source: 'tok_1234', promo: '123' }
-
+            context "invalid code" do
+              it "prevents use of invalid coupon codes" do
+                ::Stripe::Price.expects(:retrieve).returns(
+                  type: 'recurring',
+                  product: 'product_12345',
+                  metadata: {
+                    group_name: 'awesome',
+                    trial_period_days: 0
+                  }
+                )
+
+                ::Stripe::PromotionCode.expects(:list).with({ code: 'invalid' }).returns(
+                  data: []
+                )
+
+                post "/s/create.json", params: { plan: 'plan_1234', source: 'tok_1234', promo: 'invalid' }
+
+                data = response.parsed_body
+                expect(data["errors"]).not_to be_blank
+              end
             end
 
-            it "applies promo code to one time purchase" do
-              ::Stripe::Price.expects(:retrieve).returns(
-                type: 'one_time',
-                product: 'product_12345',
-                metadata: {
-                  group_name: 'awesome'
-                }
-              )
-
-              ::Stripe::InvoiceItem.expects(:create).with(customer: 'cus_1234', price: 'plan_1234', discounts: [{ coupon: 'c123' }])
-
-              ::Stripe::Invoice.expects(:create).returns(status: 'open', id: 'in_123')
-
-              ::Stripe::Invoice.expects(:finalize_invoice).returns(id: 'in_123', status: 'open', payment_intent: 'pi_123')
-
-              ::Stripe::Invoice.expects(:retrieve).returns(id: 'in_123', status: 'open', payment_intent: 'pi_123')
-
-              ::Stripe::PaymentIntent.expects(:retrieve).returns(status: 'successful')
-
-              ::Stripe::Invoice.expects(:pay).returns(status: 'paid', customer: 'cus_1234')
-
-              post '/s/create.json', params: { plan: 'plan_1234', source: 'tok_1234', promo: '123' }
+            context "valid code" do
+              before do
+                ::Stripe::PromotionCode.expects(:list).with({ code: '123' }).returns(
+                  data: [{
+                    id: 'promo123',
+                    coupon: { id: 'c123' }
+                  }]
+                )
+              end
+
+              it "applies promo code to recurring subscription" do
+                ::Stripe::Price.expects(:retrieve).returns(
+                  type: 'recurring',
+                  product: 'product_12345',
+                  metadata: {
+                    group_name: 'awesome',
+                    trial_period_days: 0
+                  }
+                )
+
+                ::Stripe::Subscription.expects(:create).with(
+                  customer: 'cus_1234',
+                  items: [ price: 'plan_1234' ],
+                  metadata: { user_id: user.id, username: user.username_lower },
+                  trial_period_days: 0,
+                  promotion_code: 'promo123'
+                ).returns(status: 'active', customer: 'cus_1234')
+
+                expect {
+                  post "/s/create.json", params: { plan: 'plan_1234', source: 'tok_1234', promo: '123' }
+                }.to change { DiscourseSubscriptions::Customer.count }
+
+              end
+
+              it "applies promo code to one time purchase" do
+                ::Stripe::Price.expects(:retrieve).returns(
+                  type: 'one_time',
+                  product: 'product_12345',
+                  metadata: {
+                    group_name: 'awesome'
+                  }
+                )
+
+                ::Stripe::InvoiceItem.expects(:create).with(customer: 'cus_1234', price: 'plan_1234', discounts: [{ coupon: 'c123' }])
+
+                ::Stripe::Invoice.expects(:create).returns(status: 'open', id: 'in_123')
+
+                ::Stripe::Invoice.expects(:finalize_invoice).returns(id: 'in_123', status: 'open', payment_intent: 'pi_123')
+
+                ::Stripe::Invoice.expects(:retrieve).returns(id: 'in_123', status: 'open', payment_intent: 'pi_123')
+
+                ::Stripe::PaymentIntent.expects(:retrieve).returns(status: 'successful')
+
+                ::Stripe::Invoice.expects(:pay).returns(status: 'paid', customer: 'cus_1234')
+
+                expect {
+                  post '/s/create.json', params: { plan: 'plan_1234', source: 'tok_1234', promo: '123' }

[... diff too long, it was truncated ...]

GitHub sha: 8ab7a63167f9c6beb8832b655055e519a2e3b478

This commit appears in #74 which was approved by romanrizzi. It was merged by justindirose.