effective_orders 4.2.7 → 4.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f0bd383b484e3db946b0a7bb199f7afb8d7d8600019dcfad46b00bf6ddb3d798
4
- data.tar.gz: 16db4fd12e60d5995ec3dbc9003a720dacc8e9c95b761c622ac7c7db294ea77b
3
+ metadata.gz: 78fe7109ba11df3b289f3fe0180f8d0b2696115899428f38b7862d509e0f9f90
4
+ data.tar.gz: 5681ed2fce8ede2be574c640e04f782af4ddabaab054df96b67273011416acc0
5
5
  SHA512:
6
- metadata.gz: 19022b230c19a97bc5c1c9a203df1407c86be04b802f01b157929ba6204e8bf60ba9dd3967e32c992fa95a6bbf75aa30ac8781b79fb789f4b2dd37117b002541
7
- data.tar.gz: 2808d79b39ecae2b23bf2fd17cc9ed36085f11103d8d217eba454fa1508207d81331c4241a25e8a92c03ba604a18f93197028d9e7adad74d018be7a8d2fabd48
6
+ metadata.gz: b967a19a627fe54a542887483ca2868ff56cca6fb7fdf59a10efcb4979b1d3a9e6beaa055a655fca8a4af6480b111eee5b3fafa57af0b6ee9760903ab5e5b9ca
7
+ data.tar.gz: 125899ef092719eda6cda5f22b02a1d2eb4e56a31c6812b80eabb66a603ba1b918aa72ae7ebae10ce4f18f6cbbf19d986ac6ce755c1c531959befd477e2e212f
@@ -0,0 +1,97 @@
1
+ class StripeForm {
2
+ initialize() {
3
+ let $form = $('form[data-stripe-form]').first();
4
+ if ($form.length == 0) { return false; }
5
+
6
+ this.$form = $form;
7
+ this.$paymentIntentId = this.$form.find("input[name$='[payment_intent_id]']").first();
8
+
9
+ this.data = $form.data('stripe-form');
10
+ this.stripe = Stripe(this.data.key);
11
+ this.card = this.stripe.elements().create("card", { style: this.style() });
12
+
13
+ this.mount();
14
+ }
15
+
16
+ mount() {
17
+ this.card.mount("#stripe-card-element");
18
+
19
+ this.card.addEventListener('change', function ({ error }) {
20
+ if (error) {
21
+ $('#stripe-card-errors').text(error.message);
22
+ } else {
23
+ $('#stripe-card-errors').text('');
24
+ }
25
+ });
26
+
27
+ this.$form.on('click', '[type=submit]', (event) => { this.submit(event) });
28
+ }
29
+
30
+ shouldUseNewCard() {
31
+ let $newCardForm = this.$form.children('.collapse.show')
32
+
33
+ return (
34
+ this.$form.get(0).checkValidity() &&
35
+ (this.data.token_required || $newCardForm.length > 0) &&
36
+ this.$paymentIntentId.val().length == 0
37
+ )
38
+ }
39
+
40
+ submit(event) {
41
+ event.preventDefault();
42
+
43
+ let payment = this.shouldUseNewCard() ? this.useNewCard() : this.useExistingCard();
44
+
45
+ payment.then((result) => {
46
+ if (result.error) {
47
+ this.errorPayment(result.error)
48
+ } else if (result.paymentIntent.status == 'succeeded') {
49
+ this.submitPayment(result.paymentIntent);
50
+ }
51
+ });
52
+ }
53
+
54
+ useExistingCard() {
55
+ return this.stripe.confirmCardPayment(this.data.client_secret, {
56
+ payment_method: this.data.payment_method
57
+ });
58
+ }
59
+
60
+ useNewCard() {
61
+ return this.stripe.confirmCardPayment(this.data.client_secret, {
62
+ payment_method: {
63
+ card: this.card,
64
+ billing_details: {
65
+ email: this.data.email
66
+ }
67
+ },
68
+ setup_future_usage: 'off_session'
69
+ });
70
+ }
71
+
72
+ errorPayment(error) {
73
+ $('#stripe-card-errors').text(error.message);
74
+ EffectiveForm.invalidate(this.$form);
75
+ }
76
+
77
+ submitPayment(payment) {
78
+ this.$paymentIntentId.val('' + payment['id']);
79
+ this.$form.submit();
80
+ }
81
+
82
+ style() {
83
+ return {
84
+ base: {
85
+ color: "#32325d",
86
+ fontSize: "16px",
87
+ },
88
+ invalid: {
89
+ color: "#dc3545",
90
+ iconColor: "#dc3545"
91
+ }
92
+ };
93
+ }
94
+
95
+ }
96
+
97
+ $(document).ready(function () { new StripeForm().initialize(); });
@@ -12,11 +12,15 @@ module Effective
12
12
  payment: 'for pretend',
13
13
  provider: 'pretend',
14
14
  card: 'none',
15
- purchased_url: params[:purchased_url],
16
- declined_url: params[:declined_url]
15
+ purchased_url: pretend_params[:purchased_url],
16
+ declined_url: pretend_params[:declined_url]
17
17
  )
18
18
  end
19
19
 
20
+ def pretend_params
21
+ params.require(:pretend).permit(:purchased_url, :declined_url)
22
+ end
23
+
20
24
  end
21
25
  end
22
26
  end
@@ -3,65 +3,68 @@ module Effective
3
3
  module Stripe
4
4
  extend ActiveSupport::Concern
5
5
 
6
- # TODO: Make stripe charge work with admin checkout workflow, purchased_url and declined_url
7
- # Make it save the customer and not require typing in a CC every time.
8
-
9
- def stripe_charge
10
- @order ||= Effective::Order.find(stripe_charge_params[:effective_order_id])
11
- @stripe_charge = Effective::Providers::StripeCharge.new(stripe_charge_params)
12
- @stripe_charge.order = @order
6
+ def stripe
7
+ @order = Order.find(params[:id])
8
+ @customer = Effective::Customer.for_user(@order.user)
13
9
 
14
10
  EffectiveOrders.authorize!(self, :update, @order)
15
11
 
16
- if @stripe_charge.valid? && (response = process_stripe_charge(@stripe_charge)) != false
17
- order_purchased(
18
- payment: response,
19
- provider: 'stripe',
20
- card: (response[:charge]['source']['brand'] rescue nil)
21
- )
22
- else
23
- @page_title = 'Checkout'
24
- flash.now[:danger] = @stripe_charge.errors.full_messages.to_sentence
25
- render :show
12
+ payment = validate_stripe_payment(stripe_params[:payment_intent_id])
13
+
14
+ if payment.blank? || !payment.kind_of?(Hash)
15
+ return order_declined(payment: payment, provider: 'stripe', declined_url: stripe_params[:declined_url])
16
+ end
17
+
18
+ # Update the customer payment fields
19
+ if payment[:payment_method_id].present?
20
+ @customer.update!(payment.slice(:payment_method_id, :active_card))
26
21
  end
22
+
23
+ order_purchased(
24
+ payment: payment,
25
+ provider: 'stripe',
26
+ card: payment[:card],
27
+ purchased_url: stripe_params[:purchased_url],
28
+ declined_url: stripe_params[:declined_url]
29
+ )
27
30
  end
28
31
 
29
32
  private
30
33
 
31
- def process_stripe_charge(charge)
32
- Effective::Order.transaction do
33
- begin
34
- subscripter = Effective::Subscripter.new(user: charge.order.user, stripe_token: charge.stripe_token)
35
- subscripter.save!
36
-
37
- return charge_with_stripe(charge, subscripter.customer)
38
- rescue => e
39
- charge.errors.add(:base, "Unable to process order with Stripe. Your credit card has not been charged. Message: \"#{e.message}\".")
40
- raise ActiveRecord::Rollback
41
- end
42
- end
43
-
44
- false
34
+ def stripe_params
35
+ params.require(:stripe).permit(:payment_intent_id, :purchased_url, :declined_url)
45
36
  end
46
37
 
47
- def charge_with_stripe(charge, customer)
48
- results = { charge: nil }
38
+ def validate_stripe_payment(payment_intent_id)
39
+ begin
40
+ intent = ::Stripe::PaymentIntent.retrieve(payment_intent_id)
41
+ raise('status is not succeeded') unless intent.status == 'succeeded'
42
+ raise('charges are not present') unless intent.charges.present?
49
43
 
50
- if charge.order.total > 0
51
- results[:charge] = JSON.parse(::Stripe::Charge.create(
52
- amount: charge.order.total,
53
- currency: EffectiveOrders.stripe[:currency],
54
- customer: customer.stripe_customer.id,
55
- description: "Charge for Order ##{charge.order.to_param}"
56
- ).to_json)
57
- end
44
+ charge = intent.charges.data.first
45
+ raise('charge not succeeded') unless charge.status == 'succeeded'
58
46
 
59
- results
60
- end
47
+ card = charge.payment_method_details.try(:card) || {}
48
+ active_card = "**** **** **** #{card['last4']} #{card['brand']} #{card['exp_month']}/#{card['exp_year']}" if card.present?
49
+
50
+ {
51
+ charge_id: charge.id,
52
+ payment_method_id: charge.payment_method,
53
+ payment_intent_id: intent.id,
61
54
 
62
- # StrongParameters
63
- def stripe_charge_params
64
- params.require(:effective_providers_stripe_charge).permit(:stripe_token, :effective_order_id)
55
+ active_card: active_card,
56
+ card: card['brand'],
57
+
58
+ amount: charge.amount,
59
+ created: charge.created,
60
+ currency: charge.currency,
61
+ customer: charge.customer,
62
+ status: charge.status
63
+ }.compact
64
+
65
+ rescue => e
66
+ e.message
67
+ end
65
68
  end
66
69
 
67
70
  end
@@ -45,17 +45,41 @@ module EffectiveStripeHelper
45
45
  "#{order.num_items} items (#{price_to_currency(order.total)})"
46
46
  end
47
47
 
48
- def stripe_charge_data(order)
49
- {
50
- stripe: {
51
- key: EffectiveOrders.stripe[:publishable_key],
52
- name: EffectiveOrders.stripe[:site_title],
53
- image: stripe_site_image_url,
54
- email: order.user.email,
55
- amount: order.total,
56
- description: stripe_order_description(order)
57
- }.to_json
48
+ def stripe_payment_intent(order)
49
+ customer = Effective::Customer.for_user(order.user)
50
+ customer.create_stripe_customer! # Only creates if customer not already present
51
+
52
+ payment = {
53
+ amount: order.total,
54
+ currency: EffectiveOrders.stripe[:currency],
55
+ customer: customer.stripe_customer_id,
56
+ payment_method: customer.payment_method_id.presence,
57
+ description: stripe_order_description(order),
58
+ metadata: { order_id: order.id },
59
+ }
60
+
61
+ token_required = customer.token_required?
62
+
63
+ intent = begin
64
+ Rails.logger.info "[STRIPE] create payment intent : #{payment}"
65
+ Stripe::PaymentIntent.create(payment)
66
+ rescue Stripe::CardError => e
67
+ token_required = true
68
+ Rails.logger.info "[STRIPE] (error) get payment intent : #{e.error.payment_intent.id}"
69
+ Stripe::PaymentIntent.retrieve(e.error.payment_intent.id)
70
+ end
71
+
72
+ payload = {
73
+ key: EffectiveOrders.stripe[:publishable_key],
74
+ client_secret: intent.client_secret,
75
+ payment_method: intent.payment_method,
76
+
77
+ active_card: customer.active_card,
78
+ email: customer.email,
79
+ token_required: token_required
58
80
  }
81
+
82
+ payload
59
83
  end
60
84
 
61
85
  end
@@ -10,6 +10,7 @@ module Effective
10
10
 
11
11
  # Attributes
12
12
  # stripe_customer_id :string # cus_xja7acoa03
13
+ # payment_method_id :string # Last payment method used
13
14
  # active_card :string # **** **** **** 4242 Visa 05/12
14
15
 
15
16
  # timestamps
@@ -31,6 +32,18 @@ module Effective
31
32
  user.email if user
32
33
  end
33
34
 
35
+ def create_stripe_customer!
36
+ return if stripe_customer.present?
37
+ raise('expected a user') unless user.present?
38
+
39
+ Rails.logger.info "[STRIPE] create customer: #{user.email}"
40
+
41
+ self.stripe_customer = Stripe::Customer.create(email: user.email, description: user.to_s, metadata: { user_id: user.id })
42
+ self.stripe_customer_id = stripe_customer.id
43
+
44
+ save!
45
+ end
46
+
34
47
  def stripe_customer
35
48
  @stripe_customer ||= if stripe_customer_id.present?
36
49
  Rails.logger.info "[STRIPE] get customer: #{stripe_customer_id}"
@@ -79,12 +79,7 @@ module Effective
79
79
  protected
80
80
 
81
81
  def create_customer!
82
- return if customer.stripe_customer.present?
83
-
84
- Rails.logger.info "[STRIPE] create customer: #{user.email}"
85
- customer.stripe_customer = Stripe::Customer.create(email: user.email, description: user.to_s, metadata: { user_id: user.id })
86
- customer.stripe_customer_id = customer.stripe_customer.id
87
- customer.save!
82
+ customer.create_stripe_customer!
88
83
  end
89
84
 
90
85
  # Update stripe customer card
@@ -5,33 +5,32 @@
5
5
  = link_to 'Change Addresses', effective_orders.edit_order_path(order), rel: :nofollow, class: 'btn btn-secondary'
6
6
 
7
7
  .effective-order-purchase-actions
8
- .form-actions.form-actions-bordered.justify-content-end
9
- - provider_locals = { order: order, purchased_url: purchased_url, declined_url: declined_url }
8
+ - provider_locals = { order: order, purchased_url: purchased_url, declined_url: declined_url }
10
9
 
11
- - if EffectiveOrders.free? && order.free?
12
- = render partial: '/effective/orders/free/form', locals: provider_locals
10
+ - if EffectiveOrders.free? && order.free?
11
+ = render partial: '/effective/orders/free/form', locals: provider_locals
13
12
 
14
- - elsif EffectiveOrders.refunds? && order.refund?
15
- -# Nothing
13
+ - elsif EffectiveOrders.refunds? && order.refund?
14
+ -# Nothing
16
15
 
17
- - else
18
- - if EffectiveOrders.pretend?
19
- = render partial: '/effective/orders/pretend/form', locals: provider_locals
16
+ - else
17
+ - if EffectiveOrders.pretend? && EffectiveOrders.pretend_message.present?
18
+ %p.text-right= EffectiveOrders.pretend_message
20
19
 
21
- - if EffectiveOrders.moneris?
22
- = render partial: '/effective/orders/moneris/form', locals: provider_locals
20
+ - if EffectiveOrders.pretend?
21
+ = render partial: '/effective/orders/pretend/form', locals: provider_locals
23
22
 
24
- - if EffectiveOrders.paypal?
25
- = render partial: '/effective/orders/paypal/form', locals: provider_locals
23
+ - if EffectiveOrders.moneris?
24
+ = render partial: '/effective/orders/moneris/form', locals: provider_locals
26
25
 
27
- - if EffectiveOrders.stripe?
28
- = render partial: '/effective/orders/stripe/form', locals: provider_locals
26
+ - if EffectiveOrders.paypal?
27
+ = render partial: '/effective/orders/paypal/form', locals: provider_locals
29
28
 
30
- - if EffectiveOrders.cheque? && order.user == current_user
31
- = render partial: '/effective/orders/cheque/form', locals: provider_locals
29
+ - if EffectiveOrders.stripe?
30
+ = render partial: '/effective/orders/stripe/form', locals: provider_locals
32
31
 
33
- - if EffectiveOrders.pretend? && EffectiveOrders.pretend_message.present?
34
- %p.text-right= EffectiveOrders.pretend_message
32
+ - if EffectiveOrders.cheque? && order.user == current_user
33
+ = render partial: '/effective/orders/cheque/form', locals: provider_locals
35
34
 
36
35
  - if EffectiveOrders.authorized?(controller, :admin, :effective_orders) && order.user != current_user
37
36
  - if EffectiveOrders.refunds? && order.refund?
@@ -1,2 +1,5 @@
1
- = effective_form_with(scope: :pretend, url: effective_orders.pretend_order_path(order, purchased_url: purchased_url, declined_url: declined_url), method: :post) do |f|
1
+ = effective_form_with(scope: :pretend, url: effective_orders.pretend_order_path(order), method: :post) do |f|
2
+ = f.hidden_field :purchased_url, value: purchased_url
3
+ = f.hidden_field :declined_url, value: declined_url
4
+
2
5
  = f.submit order_checkout_label(:pretend), border: false
@@ -0,0 +1,8 @@
1
+ .row
2
+ .col
3
+ %label Card
4
+ .col.text-right.mx-2
5
+ %label Details
6
+
7
+ #stripe-card-element
8
+ #stripe-card-errors
@@ -1,8 +1,31 @@
1
- = javascript_include_tag 'https://checkout.stripe.com/checkout.js'
1
+ = javascript_include_tag 'https://js.stripe.com/v3/'
2
2
 
3
- #effective-orders-new-charge-form{data: stripe_charge_data(order) }
4
- = effective_form_with(model: @stripe_charge || Effective::Providers::StripeCharge.new(order: order), url: effective_orders.stripe_charge_orders_path) do |f|
5
- = f.hidden_field :stripe_token
6
- = f.hidden_field :effective_order_id
3
+ - stripe = stripe_payment_intent(order)
7
4
 
8
- = f.submit order_checkout_label(:stripe), border: false
5
+ .card
6
+ .card-body
7
+ %h3= order_checkout_label(:stripe)
8
+ %p
9
+ %em This checkout is powered by stripe.
10
+
11
+ .my-4.text-center
12
+ = link_to(image_tag('effective_orders/stripe.png'), 'https://www.stripe.com', target: '_blank')
13
+
14
+ = effective_form_with(scope: :stripe, url: effective_orders.stripe_order_path(order), data: { 'stripe-form': stripe.to_json }) do |f|
15
+ = f.hidden_field :purchased_url, value: purchased_url
16
+ = f.hidden_field :declined_url, value: declined_url
17
+
18
+ -# This is set by the stripe.js javascript
19
+ = f.hidden_field :payment_intent_id
20
+
21
+ - if stripe[:token_required]
22
+ %p Please enter your credit card information.
23
+ = render('effective/orders/stripe/element')
24
+ - else
25
+ %p Your existing card <strong>#{stripe[:active_card]}</strong> will be charged.
26
+
27
+ = collapse('Use this card instead...', class: 'update-stripe-payment-method') do
28
+ = render('effective/orders/stripe/element')
29
+
30
+ .mt-4
31
+ = f.submit 'Pay Now', center: true, border: false
data/config/routes.rb CHANGED
@@ -11,6 +11,7 @@ EffectiveOrders::Engine.routes.draw do
11
11
  post :pay_by_cheque if EffectiveOrders.cheque?
12
12
  post :pretend if EffectiveOrders.pretend?
13
13
  post :refund if EffectiveOrders.refunds?
14
+ post :stripe if EffectiveOrders.stripe?
14
15
  end
15
16
 
16
17
  collection do
@@ -18,7 +19,6 @@ EffectiveOrders::Engine.routes.draw do
18
19
 
19
20
  post :moneris_postback if EffectiveOrders.moneris?
20
21
  post :paypal_postback if EffectiveOrders.paypal?
21
- post :stripe_charge if EffectiveOrders.stripe?
22
22
  end
23
23
  end
24
24
 
@@ -72,6 +72,7 @@ class CreateEffectiveOrders < ActiveRecord::Migration[4.2]
72
72
  t.integer :user_id
73
73
 
74
74
  t.string :stripe_customer_id
75
+ t.string :payment_method_id
75
76
  t.string :active_card
76
77
  t.string :status
77
78
 
@@ -23,6 +23,10 @@ module EffectiveOrders
23
23
  )
24
24
  end
25
25
 
26
+ initializer "effective_orders.append_precompiled_assets" do |app|
27
+ Rails.application.config.assets.precompile += ['effective_orders/*']
28
+ end
29
+
26
30
  initializer 'effective_orders.stripe', after: :load_config_initializers do
27
31
  if EffectiveOrders.stripe?
28
32
  begin
@@ -1,3 +1,3 @@
1
1
  module EffectiveOrders
2
- VERSION = '4.2.7'.freeze
2
+ VERSION = '4.3.0'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: effective_orders
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.2.7
4
+ version: 4.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Code and Effect
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-04-03 00:00:00.000000000 Z
11
+ date: 2020-04-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -118,9 +118,10 @@ extra_rdoc_files: []
118
118
  files:
119
119
  - MIT-LICENSE
120
120
  - README.md
121
+ - app/assets/images/effective_orders/stripe.png
121
122
  - app/assets/javascripts/effective_orders.js
122
123
  - app/assets/javascripts/effective_orders/customers.js.coffee
123
- - app/assets/javascripts/effective_orders/providers/stripe.js.coffee
124
+ - app/assets/javascripts/effective_orders/providers/stripe.js
124
125
  - app/assets/javascripts/effective_orders/subscriptions.js.coffee
125
126
  - app/assets/stylesheets/effective_orders.scss
126
127
  - app/assets/stylesheets/effective_orders/_cart.scss
@@ -217,6 +218,7 @@ files:
217
218
  - app/views/effective/orders/purchased.html.haml
218
219
  - app/views/effective/orders/refund/_form.html.haml
219
220
  - app/views/effective/orders/show.html.haml
221
+ - app/views/effective/orders/stripe/_element.html.haml
220
222
  - app/views/effective/orders/stripe/_form.html.haml
221
223
  - app/views/effective/orders_mailer/order_error.html.haml
222
224
  - app/views/effective/orders_mailer/order_receipt_to_admin.html.haml
@@ -1,26 +0,0 @@
1
- stripeCheckoutHandler = (key, form) ->
2
- StripeCheckout.configure
3
- key: key
4
- closed: -> EffectiveBootstrap.reset(form) unless form.hasClass('stripe-success')
5
- token: (token, args) ->
6
- if token.error
7
- alert("An error ocurred when contacting Stripe. Your card has not been charged. Please refresh the page and try again. #{token.error.message}")
8
- else
9
- form.find("input[name$='[stripe_token]']").val('' + token['id'])
10
- form.addClass('stripe-success').submit()
11
-
12
- $(document).on 'click', "#effective-orders-new-charge-form form [type='submit']", (event) ->
13
- event.preventDefault()
14
-
15
- obj = $('#effective-orders-new-charge-form')
16
- $form = obj.find('form').first()
17
- stripe = obj.data('stripe')
18
-
19
- EffectiveForm.submitting($form)
20
-
21
- stripeCheckoutHandler(stripe.key, $form).open
22
- image: stripe.image
23
- name: stripe.name
24
- description: stripe.description
25
- email: stripe.email
26
- amount: stripe.amount