effective_orders 4.2.7 → 4.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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