effective_orders 4.4.6 → 4.5.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 +4 -4
- data/app/assets/javascripts/effective_orders/providers/stripe.js.coffee +77 -0
- data/app/models/concerns/acts_as_purchasable.rb +9 -9
- data/app/models/concerns/acts_as_subscribable.rb +10 -5
- data/app/models/effective/customer.rb +7 -0
- data/app/models/effective/order.rb +6 -2
- data/app/models/effective/subscription.rb +4 -0
- data/app/views/effective/customers/_customer.html.haml +2 -2
- data/db/migrate/01_create_effective_orders.rb.erb +4 -0
- data/lib/effective_orders.rb +7 -4
- data/lib/effective_orders/version.rb +1 -1
- metadata +7 -7
- data/app/assets/javascripts/effective_orders/providers/stripe.js +0 -97
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f0bcae4f96f01459844e667c3c9c426a2eac78084e2cfcccb3dc630874554bf8
|
4
|
+
data.tar.gz: ca5cc298e9277e80c080d743045f6ff469b50d0ba54ed6c0183d5905f7b10b0e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 315a63864050c7dacfe06ac17dfd098bfb4d698490cb214a9293b7446b6f35061dfa1e5919b63ce1684588977ecb1258ad07d8e7d4c056b61cb457de0ac13ec2
|
7
|
+
data.tar.gz: 910d703ac1b4c71a4f0ae00666ea16511578c7a39f31b56348bdd34069aa82d0c07978db26d7586c3b3a545f0aa2fa242bedd8ee25d7a02ca4baff2756537162
|
@@ -0,0 +1,77 @@
|
|
1
|
+
this.StripeForm ||= class StripeForm
|
2
|
+
constructor: ->
|
3
|
+
@form = null
|
4
|
+
@paymentIntent = null
|
5
|
+
|
6
|
+
@data = null
|
7
|
+
@stripe = null
|
8
|
+
@card = null
|
9
|
+
|
10
|
+
initialize: ->
|
11
|
+
@form = $('form[data-stripe-form]').first()
|
12
|
+
return false unless @form.length > 0
|
13
|
+
|
14
|
+
@paymentIntent = @form.find("input[name$='[payment_intent_id]']").first()
|
15
|
+
@data = @form.data('stripe-form')
|
16
|
+
@stripe = Stripe(@data.key)
|
17
|
+
@card = @stripe.elements().create('card', @style())
|
18
|
+
|
19
|
+
@mount()
|
20
|
+
|
21
|
+
style: ->
|
22
|
+
style: {
|
23
|
+
base: { color: '#32325d', fontSize: '16px', },
|
24
|
+
invalid: { color: '#dc3545', iconColor: '#dc3545' }
|
25
|
+
}
|
26
|
+
|
27
|
+
mount: ->
|
28
|
+
@card.mount('#stripe-card-element')
|
29
|
+
|
30
|
+
@card.addEventListener('change', (error) ->
|
31
|
+
$('#stripe-card-errors').text(if error.message then error.message else '')
|
32
|
+
)
|
33
|
+
|
34
|
+
@form.on('click', '[type=submit]', (event) => @submit(event))
|
35
|
+
|
36
|
+
submit: (event) ->
|
37
|
+
event.preventDefault()
|
38
|
+
|
39
|
+
payment = if @shouldUseNewCard() then @useNewCard() else @useExistingCard()
|
40
|
+
|
41
|
+
$.active = $.active + 1
|
42
|
+
|
43
|
+
payment.then((result) =>
|
44
|
+
if result.error
|
45
|
+
@errorPayment(result.error)
|
46
|
+
else if result.paymentIntent.status == 'succeeded'
|
47
|
+
@submitPayment(result.paymentIntent)
|
48
|
+
).then((result) =>
|
49
|
+
$.active = $.active - 1
|
50
|
+
)
|
51
|
+
|
52
|
+
shouldUseNewCard: ->
|
53
|
+
@form.get(0).checkValidity() &&
|
54
|
+
@paymentIntent.val().length == 0 &&
|
55
|
+
(@data.token_required || @form.children('.collapse.show').length > 0)
|
56
|
+
|
57
|
+
useNewCard: ->
|
58
|
+
@stripe.confirmCardPayment(@data.client_secret, {
|
59
|
+
payment_method: {
|
60
|
+
card: @card,
|
61
|
+
billing_details: { email: @data.email }
|
62
|
+
},
|
63
|
+
setup_future_usage: 'off_session'
|
64
|
+
})
|
65
|
+
|
66
|
+
useExistingCard: ->
|
67
|
+
@stripe.confirmCardPayment(@data.client_secret, { payment_method: @data.payment_method })
|
68
|
+
|
69
|
+
submitPayment: (payment) ->
|
70
|
+
@paymentIntent.val(payment['id'])
|
71
|
+
@form.submit()
|
72
|
+
|
73
|
+
errorPayment: (error) ->
|
74
|
+
$('#stripe-card-errors').text(error.message)
|
75
|
+
EffectiveForm.invalidate(@form);
|
76
|
+
|
77
|
+
$ -> (new StripeForm()).initialize()
|
@@ -5,15 +5,15 @@ module ActsAsPurchasable
|
|
5
5
|
def acts_as_purchasable(*options)
|
6
6
|
@acts_as_purchasable = options || []
|
7
7
|
|
8
|
-
if table_exists?
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
end
|
8
|
+
# if table_exists?
|
9
|
+
# instance = new()
|
10
|
+
# raise 'must respond_to price' unless instance.respond_to?(:price)
|
11
|
+
# raise 'must respond_to purchased_order_id' unless instance.respond_to?(:purchased_order_id)
|
12
|
+
|
13
|
+
# if defined?(EffectiveQbSync)
|
14
|
+
# raise 'must respond to qb_item_name' unless instance.respond_to?(:qb_item_name)
|
15
|
+
# end
|
16
|
+
# end
|
17
17
|
|
18
18
|
include ::ActsAsPurchasable
|
19
19
|
end
|
@@ -7,9 +7,11 @@ module ActsAsSubscribable
|
|
7
7
|
def acts_as_subscribable(*options)
|
8
8
|
@acts_as_subscribable = options || []
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
raise 'must respond to
|
10
|
+
# if table_exists?
|
11
|
+
# instance = new()
|
12
|
+
# raise 'must respond to trialing_until' unless instance.respond_to?(:trialing_until) || !EffectiveOrders.trial?
|
13
|
+
# raise 'must respond to subscription_status' unless instance.respond_to?(:subscription_status)
|
14
|
+
# end
|
13
15
|
|
14
16
|
include ::ActsAsSubscribable
|
15
17
|
(ActsAsSubscribable.descendants ||= []) << self
|
@@ -20,7 +22,7 @@ module ActsAsSubscribable
|
|
20
22
|
has_one :subscription, as: :subscribable, class_name: 'Effective::Subscription', inverse_of: :subscribable
|
21
23
|
has_one :customer, through: :subscription, class_name: 'Effective::Customer'
|
22
24
|
|
23
|
-
before_validation(if: -> {
|
25
|
+
before_validation(if: -> { EffectiveOrders.trial? && trialing_until.blank? }) do
|
24
26
|
self.trialing_until = (Time.zone.now + EffectiveOrders.trial.fetch(:length)).beginning_of_day
|
25
27
|
end
|
26
28
|
|
@@ -28,7 +30,10 @@ module ActsAsSubscribable
|
|
28
30
|
raise :abort unless (subscripter.destroy! rescue false)
|
29
31
|
end
|
30
32
|
|
31
|
-
|
33
|
+
if EffectiveOrders.trial?
|
34
|
+
validates :trialing_until, presence: true
|
35
|
+
end
|
36
|
+
|
32
37
|
validates :subscription_status, inclusion: { allow_nil: true, in: EffectiveOrders::STATUSES.keys }
|
33
38
|
|
34
39
|
scope :trialing, -> { where(subscription_status: nil).where('trialing_until > ?', Time.zone.now) }
|
@@ -51,6 +51,13 @@ module Effective
|
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
|
+
def invoices
|
55
|
+
@invoices ||= if stripe_customer_id.present?
|
56
|
+
Rails.logger.info "[STRIPE] list invoices: #{stripe_customer_id}"
|
57
|
+
::Stripe::Invoice.list(customer: stripe_customer_id) rescue nil
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
54
61
|
def upcoming_invoice
|
55
62
|
@upcoming_invoice ||= if stripe_customer_id.present?
|
56
63
|
Rails.logger.info "[STRIPE] get upcoming invoice: #{stripe_customer_id}"
|
@@ -28,6 +28,9 @@ module Effective
|
|
28
28
|
attr_accessor :send_mark_as_paid_email_to_buyer # Set by Admin::Orders#mark_as_paid
|
29
29
|
attr_accessor :skip_buyer_validations # Set by Admin::Orders#create
|
30
30
|
|
31
|
+
# If we want to use orders in a has_many way
|
32
|
+
belongs_to :parent, polymorphic: true, optional: true
|
33
|
+
|
31
34
|
belongs_to :user, validate: false # This is the buyer/user of the order. We validate it below.
|
32
35
|
has_many :order_items, -> { order(:id) }, inverse_of: :order, class_name: 'Effective::OrderItem', dependent: :delete_all
|
33
36
|
|
@@ -337,13 +340,14 @@ module Effective
|
|
337
340
|
|
338
341
|
assign_attributes(
|
339
342
|
state: EffectiveOrders::PURCHASED,
|
340
|
-
purchased_at: Time.zone.now,
|
341
343
|
payment: payment_to_h(payment),
|
342
344
|
payment_provider: provider,
|
343
345
|
payment_card: (card.presence || 'none'),
|
344
346
|
skip_buyer_validations: skip_buyer_validations
|
345
347
|
)
|
346
348
|
|
349
|
+
self.purchased_at ||= Time.zone.now
|
350
|
+
|
347
351
|
Effective::Order.transaction do
|
348
352
|
begin
|
349
353
|
run_purchasable_callbacks(:before_purchase)
|
@@ -504,7 +508,7 @@ module Effective
|
|
504
508
|
end
|
505
509
|
|
506
510
|
def update_purchasables_purchased_order!
|
507
|
-
order_items.each { |oi| oi.purchasable
|
511
|
+
order_items.each { |oi| oi.purchasable&.update_column(:purchased_order_id, self.id) }
|
508
512
|
end
|
509
513
|
|
510
514
|
def run_purchasable_callbacks(name)
|
@@ -56,6 +56,10 @@ module Effective
|
|
56
56
|
|
57
57
|
validates :status, inclusion: { in: EffectiveOrders::STATUSES.keys }
|
58
58
|
|
59
|
+
before_validation do
|
60
|
+
self.name ||= EffectiveOrders.stripe_plans.find { |plan| plan[:id] == stripe_plan_id }&[:name] || 'Subscribed Plan'
|
61
|
+
end
|
62
|
+
|
59
63
|
def to_s
|
60
64
|
name || 'New Subscription'
|
61
65
|
end
|
@@ -15,7 +15,7 @@
|
|
15
15
|
%td= sub.quantity
|
16
16
|
%td= sub.interval
|
17
17
|
|
18
|
-
- if customer.
|
18
|
+
- if customer.invoices.present?
|
19
19
|
.card.my-4
|
20
20
|
.card-header Invoices
|
21
21
|
.card-body
|
@@ -26,7 +26,7 @@
|
|
26
26
|
%th Invoice
|
27
27
|
%th Total
|
28
28
|
%tbody
|
29
|
-
- customer.
|
29
|
+
- customer.invoices.each do |invoice|
|
30
30
|
- date_method = [(:date if invoice.respond_to?(:date)), (:period_start if invoice.respond_to?(:period_start)), :created].compact.first
|
31
31
|
%tr
|
32
32
|
%td= link_to Time.zone.at(invoice.public_send(date_method)).strftime('%F'), invoice.invoice_pdf
|
data/lib/effective_orders.rb
CHANGED
@@ -183,7 +183,7 @@ module EffectiveOrders
|
|
183
183
|
Rails.logger.info '[STRIPE] index plans'
|
184
184
|
|
185
185
|
plans = begin
|
186
|
-
Stripe::Plan.all
|
186
|
+
Stripe::Plan.respond_to?(:all) ? Stripe::Plan.all : Stripe::Plan.list
|
187
187
|
rescue => e
|
188
188
|
raise e if Rails.env.production?
|
189
189
|
Rails.logger.info "[STRIPE ERROR]: #{e.message}"
|
@@ -192,15 +192,18 @@ module EffectiveOrders
|
|
192
192
|
end
|
193
193
|
|
194
194
|
plans = plans.map do |plan|
|
195
|
+
description = ("$#{'%0.2f' % (plan.amount / 100.0)}" + ' ' + plan.currency.upcase + '/' + plan.interval.to_s)
|
196
|
+
|
195
197
|
{
|
196
198
|
id: plan.id,
|
197
199
|
product_id: plan.product,
|
198
|
-
name: plan.nickname,
|
200
|
+
name: plan.nickname || description,
|
201
|
+
description: description,
|
199
202
|
amount: plan.amount,
|
200
203
|
currency: plan.currency,
|
201
|
-
description: ("$#{'%0.2f' % (plan.amount / 100.0)}" + ' ' + plan.currency.upcase + '/' + plan.interval.to_s),
|
202
204
|
interval: plan.interval,
|
203
|
-
interval_count: plan.interval_count
|
205
|
+
interval_count: plan.interval_count,
|
206
|
+
trial_period_days: plan.trial_period_days
|
204
207
|
}
|
205
208
|
end.sort do |x, y|
|
206
209
|
val ||= (x[:interval] <=> y[:interval])
|
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.
|
4
|
+
version: 4.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Code and Effect
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-07-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -121,7 +121,7 @@ files:
|
|
121
121
|
- app/assets/images/effective_orders/stripe.png
|
122
122
|
- app/assets/javascripts/effective_orders.js
|
123
123
|
- app/assets/javascripts/effective_orders/customers.js.coffee
|
124
|
-
- app/assets/javascripts/effective_orders/providers/stripe.js
|
124
|
+
- app/assets/javascripts/effective_orders/providers/stripe.js.coffee
|
125
125
|
- app/assets/javascripts/effective_orders/subscriptions.js.coffee
|
126
126
|
- app/assets/stylesheets/effective_orders.scss
|
127
127
|
- app/assets/stylesheets/effective_orders/_cart.scss
|
@@ -253,7 +253,7 @@ homepage: https://github.com/code-and-effect/effective_orders
|
|
253
253
|
licenses:
|
254
254
|
- MIT
|
255
255
|
metadata: {}
|
256
|
-
post_install_message:
|
256
|
+
post_install_message:
|
257
257
|
rdoc_options: []
|
258
258
|
require_paths:
|
259
259
|
- lib
|
@@ -268,8 +268,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
268
268
|
- !ruby/object:Gem::Version
|
269
269
|
version: '0'
|
270
270
|
requirements: []
|
271
|
-
rubygems_version: 3.
|
272
|
-
signing_key:
|
271
|
+
rubygems_version: 3.1.2
|
272
|
+
signing_key:
|
273
273
|
specification_version: 4
|
274
274
|
summary: Quickly build an online store with carts, orders, automatic email receipts
|
275
275
|
and payment collection via Stripe, StripeConnect, PayPal and Moneris.
|
@@ -1,97 +0,0 @@
|
|
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(); });
|