diff --git a/app/controllers/discourse_subscriptions/subscribe_controller.rb b/app/controllers/discourse_subscriptions/subscribe_controller.rb index 476d551..c5dd55f 100644 --- a/app/controllers/discourse_subscriptions/subscribe_controller.rb +++ b/app/controllers/discourse_subscriptions/subscribe_controller.rb @@ -53,24 +53,31 @@ 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 recurring_plan = plan[:type] == 'recurring' if recurring_plan trial_days = plan[:metadata][:trial_period_days] if plan[:metadata] && plan[:metadata][:trial_period_days] + promo_code_id = promo_code[:id] if promo_code + transaction = ::Stripe::Subscription.create( customer: customer[:id], items: [{ price: params[:plan] }], metadata: metadata_user, - trial_period_days: trial_days + trial_period_days: trial_days, + promotion_code: promo_code_id ) payment_intent = retrieve_payment_intent(transaction[:latest_invoice]) if transaction[:status] == 'incomplete' else + coupon_id = promo_code[:coupon][:id] if promo_code && promo_code[:coupon] && promo_code[:coupon][:id] invoice_item = ::Stripe::InvoiceItem.create( customer: customer[:id], - price: params[:plan] + price: params[:plan], + discounts: [{ coupon: coupon_id }] ) invoice = ::Stripe::Invoice.create( customer: customer[:id] diff --git a/assets/javascripts/discourse/controllers/s-show.js.es6 b/assets/javascripts/discourse/controllers/s-show.js.es6 index 7a999fe..c79bc0f 100644 --- a/assets/javascripts/discourse/controllers/s-show.js.es6 +++ b/assets/javascripts/discourse/controllers/s-show.js.es6 @@ -6,6 +6,7 @@ import { not } from "@ember/object/computed"; export default Controller.extend({ selectedPlan: null, + promoCode: null, isAnonymous: not("currentUser"), init() { @@ -32,6 +33,7 @@ export default Controller.extend({ const subscription = Subscription.create({ source: result.token.id, plan: plan.get("id"), + promo: this.promoCode, }); return subscription.save(); diff --git a/assets/javascripts/discourse/models/subscription.js.es6 b/assets/javascripts/discourse/models/subscription.js.es6 index 893ba61..f78ec93 100644 --- a/assets/javascripts/discourse/models/subscription.js.es6 +++ b/assets/javascripts/discourse/models/subscription.js.es6 @@ -12,6 +12,7 @@ const Subscription = EmberObject.extend({ const data = { source: this.source, plan: this.plan, + promo: this.promo, }; return ajax("/s/create", { method: "post", data }); diff --git a/assets/javascripts/discourse/models/user-subscription.js.es6 b/assets/javascripts/discourse/models/user-subscription.js.es6 index 15baf02..7709ae3 100644 --- a/assets/javascripts/discourse/models/user-subscription.js.es6 +++ b/assets/javascripts/discourse/models/user-subscription.js.es6 @@ -19,6 +19,22 @@ const UserSubscription = EmberObject.extend({ } }, + @discourseComputed("discount") + discounted(discount) { + if (discount) { + const amount_off = discount.coupon.amount_off; + const percent_off = discount.coupon.percent_off; + + if (amount_off) { + return `${parseFloat(amount_off * 0.01).toFixed(2)}`; + } else if (percent_off) { + return `${percent_off}%`; + } + } else { + return I18n.t("no_value"); + } + }, + destroy() { return ajax(`/s/user/subscriptions/${this.id}`, { method: "delete", diff --git a/assets/javascripts/discourse/templates/s/show.hbs b/assets/javascripts/discourse/templates/s/show.hbs index 41ae5a1..3d24d5d 100644 --- a/assets/javascripts/discourse/templates/s/show.hbs +++ b/assets/javascripts/discourse/templates/s/show.hbs @@ -33,6 +33,9 @@ {{else if isAnonymous}} {{login-required}} {{else}} +
+ {{input type="text" name="promo_code" placeholderKey="discourse_subscriptions.subscribe.promo_code" value=promoCode}} +
{{d-button disabled=loading diff --git a/assets/javascripts/discourse/templates/user/billing/subscriptions.hbs b/assets/javascripts/discourse/templates/user/billing/subscriptions.hbs index c0890e7..297d803 100644 --- a/assets/javascripts/discourse/templates/user/billing/subscriptions.hbs +++ b/assets/javascripts/discourse/templates/user/billing/subscriptions.hbs @@ -4,6 +4,7 @@ {{i18n 'discourse_subscriptions.user.subscriptions.id'}} {{i18n 'discourse_subscriptions.user.plans.product'}} {{i18n 'discourse_subscriptions.user.plans.rate'}} + {{i18n 'discourse_subscriptions.user.subscriptions.discounted'}} {{i18n 'discourse_subscriptions.user.subscriptions.status'}} {{i18n 'discourse_subscriptions.user.subscriptions.renews'}} {{i18n 'discourse_subscriptions.user.subscriptions.created_at'}} @@ -14,6 +15,7 @@ {{subscription.id}} {{subscription.product.name}} {{subscription.plan.subscriptionRate}} + {{subscription.discounted}} {{subscription.status}} {{subscription.endDate}} {{format-unix-date subscription.created}} diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index b712a78..2a7cfcb 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -53,6 +53,7 @@ en: subscriptions: id: Subscription ID status: Status + discounted: Discounted renews: Renews created_at: Created cancel: cancel @@ -68,6 +69,7 @@ en: title: Payment customer: title: Customer Details + promo_code: Coupon buttons: subscribe: Subscribe purchased: Purchased diff --git a/spec/requests/subscribe_controller_spec.rb b/spec/requests/subscribe_controller_spec.rb index e137353..0ad4287 100644 --- a/spec/requests/subscribe_controller_spec.rb +++ b/spec/requests/subscribe_controller_spec.rb @@ -128,7 +128,8 @@ module DiscourseSubscriptions customer: 'cus_1234', items: [ price: 'plan_1234' ], metadata: { user_id: user.id, username: user.username_lower }, - trial_period_days: 0 + trial_period_days: 0, + promotion_code: nil ).returns(status: 'active', customer: 'cus_1234') expect { @@ -170,6 +171,63 @@ module DiscourseSubscriptions post "/s/create.json", params: { plan: 'plan_1234', source: 'tok_1234' } }.to change { DiscourseSubscriptions::Customer.count } 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' } + + 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' } + end + end end describe "#finalize strong customer authenticated transaction" do