spree_klaviyo 1.0.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 +7 -0
- data/LICENSE.md +12 -0
- data/README.md +65 -0
- data/Rakefile +21 -0
- data/app/assets/config/spree_klaviyo_manifest.js +1 -0
- data/app/assets/images/integration_icons/klaviyo-logo.png +0 -0
- data/app/clients/spree_klaviyo/klaviyo/client.rb +77 -0
- data/app/controllers/spree_klaviyo/addresses_controller_decorator.rb +18 -0
- data/app/controllers/spree_klaviyo/profile_controller_decorator.rb +18 -0
- data/app/controllers/spree_klaviyo/user_registrations_controller_decorator.rb +22 -0
- data/app/jobs/spree_klaviyo/base_job.rb +5 -0
- data/app/jobs/spree_klaviyo/create_back_in_stock_subscription_job.rb +10 -0
- data/app/jobs/spree_klaviyo/create_event_job.rb +10 -0
- data/app/jobs/spree_klaviyo/create_or_update_profile_job.rb +10 -0
- data/app/jobs/spree_klaviyo/fetch_profile_job.rb +15 -0
- data/app/jobs/spree_klaviyo/subscribe_job.rb +10 -0
- data/app/jobs/spree_klaviyo/unsubscribe_job.rb +10 -0
- data/app/models/concerns/spree_klaviyo/user_methods.rb +24 -0
- data/app/models/spree/integrations/klaviyo.rb +122 -0
- data/app/models/spree_klaviyo/analytics_event_handler.rb +61 -0
- data/app/models/spree_klaviyo/order_decorator.rb +34 -0
- data/app/models/spree_klaviyo/shipment_handler_decorator.rb +25 -0
- data/app/models/spree_klaviyo/user_decorator.rb +9 -0
- data/app/presenters/spree_klaviyo/address_presenter.rb +27 -0
- data/app/presenters/spree_klaviyo/back_in_stock_subscription_presenter.rb +39 -0
- data/app/presenters/spree_klaviyo/event_presenter.rb +97 -0
- data/app/presenters/spree_klaviyo/line_item_presenter.rb +28 -0
- data/app/presenters/spree_klaviyo/order_attributes_presenter.rb +28 -0
- data/app/presenters/spree_klaviyo/order_presenter.rb +95 -0
- data/app/presenters/spree_klaviyo/product_presenter.rb +32 -0
- data/app/presenters/spree_klaviyo/shipment_presenter.rb +53 -0
- data/app/presenters/spree_klaviyo/subscribe_presenter.rb +48 -0
- data/app/presenters/spree_klaviyo/taxon_presenter.rb +19 -0
- data/app/presenters/spree_klaviyo/user_presenter.rb +39 -0
- data/app/services/spree_klaviyo/base.rb +5 -0
- data/app/services/spree_klaviyo/create_event.rb +16 -0
- data/app/services/spree_klaviyo/create_or_update_profile.rb +25 -0
- data/app/services/spree_klaviyo/fetch_profile.rb +15 -0
- data/app/services/spree_klaviyo/subscribe.rb +13 -0
- data/app/services/spree_klaviyo/unsubscribe.rb +13 -0
- data/app/views/spree/admin/integrations/forms/_klaviyo.html.erb +48 -0
- data/config/i18n-tasks.yml +173 -0
- data/config/initializers/spree.rb +4 -0
- data/config/locales/en.yml +11 -0
- data/config/routes.rb +3 -0
- data/lib/generators/spree_klaviyo/install/install_generator.rb +20 -0
- data/lib/spree_klaviyo/configuration.rb +8 -0
- data/lib/spree_klaviyo/engine.rb +28 -0
- data/lib/spree_klaviyo/factories.rb +3 -0
- data/lib/spree_klaviyo/testing_support/factories/klaviyo_integration.rb +9 -0
- data/lib/spree_klaviyo/version.rb +7 -0
- data/lib/spree_klaviyo.rb +11 -0
- metadata +234 -0
@@ -0,0 +1,39 @@
|
|
1
|
+
module SpreeKlaviyo
|
2
|
+
class BackInStockSubscriptionPresenter
|
3
|
+
def initialize(email:, variant_id:)
|
4
|
+
@email = email
|
5
|
+
@variant_id = variant_id
|
6
|
+
end
|
7
|
+
|
8
|
+
def call
|
9
|
+
{
|
10
|
+
data: {
|
11
|
+
type: 'back-in-stock-subscription',
|
12
|
+
attributes: {
|
13
|
+
channels: %w[EMAIL],
|
14
|
+
profile: {
|
15
|
+
data: {
|
16
|
+
type: 'profile',
|
17
|
+
attributes: {
|
18
|
+
email: email
|
19
|
+
}
|
20
|
+
}
|
21
|
+
}
|
22
|
+
},
|
23
|
+
relationships: {
|
24
|
+
variant: {
|
25
|
+
data: {
|
26
|
+
type: 'catalog-variant',
|
27
|
+
id: "$custom:::$default:::#{variant_id}"
|
28
|
+
}
|
29
|
+
}
|
30
|
+
}
|
31
|
+
}
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
attr_reader :email, :variant_id
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
module SpreeKlaviyo
|
2
|
+
class EventPresenter
|
3
|
+
def initialize(integration:, event:, resource:, email:, guest_id: nil)
|
4
|
+
@integration = integration
|
5
|
+
@store = integration.store
|
6
|
+
@resource = resource
|
7
|
+
@event = event
|
8
|
+
@email = email
|
9
|
+
@guest_id = guest_id
|
10
|
+
end
|
11
|
+
|
12
|
+
def call
|
13
|
+
{
|
14
|
+
data: {
|
15
|
+
type: 'event',
|
16
|
+
attributes: {
|
17
|
+
properties: properties,
|
18
|
+
metric: {
|
19
|
+
data: {
|
20
|
+
type: 'metric',
|
21
|
+
attributes: {
|
22
|
+
name: @event
|
23
|
+
}
|
24
|
+
}
|
25
|
+
},
|
26
|
+
profile: @email.present? ? profile : guest_profile,
|
27
|
+
**top_level_attributes
|
28
|
+
}
|
29
|
+
}
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def top_level_attributes
|
36
|
+
if @resource.is_a?(::Spree::Order)
|
37
|
+
OrderAttributesPresenter.new(event_name: @event, order: @resource).call
|
38
|
+
else
|
39
|
+
{}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def properties
|
44
|
+
if @resource.is_a? ::Spree::Order
|
45
|
+
OrderPresenter.new(order: @resource).call
|
46
|
+
elsif @resource.is_a? ::Spree::Shipment
|
47
|
+
ShipmentPresenter.new(shipment: @resource, store: @store).call
|
48
|
+
elsif @resource.is_a? ::Spree::Product
|
49
|
+
ProductPresenter.new(product: @resource, store: @store).call
|
50
|
+
elsif @resource.is_a? ::Spree::Taxon
|
51
|
+
TaxonPresenter.new(taxon: @resource).call
|
52
|
+
elsif @resource.is_a? String
|
53
|
+
{
|
54
|
+
query: @resource
|
55
|
+
}
|
56
|
+
else
|
57
|
+
{}
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def profile
|
62
|
+
if @resource.is_a?(::Spree::Order) && events_that_update_profile.include?(@event)
|
63
|
+
UserPresenter.new(
|
64
|
+
email: @email,
|
65
|
+
address: @resource&.bill_address || @resource&.ship_address,
|
66
|
+
user: @resource&.user,
|
67
|
+
guest_id: @guest_id
|
68
|
+
).call
|
69
|
+
else
|
70
|
+
{
|
71
|
+
data: {
|
72
|
+
type: 'profile',
|
73
|
+
attributes: {
|
74
|
+
anonymous_id: @guest_id,
|
75
|
+
email: @email
|
76
|
+
}
|
77
|
+
}
|
78
|
+
}
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def guest_profile
|
83
|
+
{
|
84
|
+
data: {
|
85
|
+
type: 'profile',
|
86
|
+
attributes: {
|
87
|
+
anonymous_id: @guest_id
|
88
|
+
}
|
89
|
+
}
|
90
|
+
}
|
91
|
+
end
|
92
|
+
|
93
|
+
def events_that_update_profile
|
94
|
+
@events_that_update_profile ||= [::Spree::Analytics.events[:checkout_email_entered], ::Spree::Analytics.events[:order_completed]]
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module SpreeKlaviyo
|
2
|
+
class LineItemPresenter < ProductPresenter
|
3
|
+
include ::Spree::BaseHelper
|
4
|
+
include ::Spree::ProductsHelper
|
5
|
+
|
6
|
+
def initialize(resource:, quantity:, total_price:, currency:, store:, position: nil)
|
7
|
+
@resource = resource
|
8
|
+
@position = position
|
9
|
+
@quantity = quantity
|
10
|
+
@total_price = total_price
|
11
|
+
super(product: resource&.product, store: store)
|
12
|
+
end
|
13
|
+
|
14
|
+
def call
|
15
|
+
return {} if @resource.nil?
|
16
|
+
|
17
|
+
super.merge({
|
18
|
+
quantity: @quantity,
|
19
|
+
item_price: @resource.price,
|
20
|
+
row_total: @total_price
|
21
|
+
})
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
attr_reader :resource, :quantity, :total_price, :currency, :position, :current_store, :feeable_item_ids
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module SpreeKlaviyo
|
2
|
+
class OrderAttributesPresenter
|
3
|
+
def initialize(order:, event_name:)
|
4
|
+
@order = order
|
5
|
+
@event_name = event_name
|
6
|
+
end
|
7
|
+
|
8
|
+
def call
|
9
|
+
{
|
10
|
+
value: @order.total&.to_f,
|
11
|
+
time: time(@order)&.iso8601
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def time(order)
|
18
|
+
case @event_name
|
19
|
+
when ::Spree::Analytics.events[:order_completed]
|
20
|
+
order.completed_at
|
21
|
+
when 'Order Cancelled'
|
22
|
+
order.canceled_at
|
23
|
+
else
|
24
|
+
order.updated_at
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module SpreeKlaviyo
|
2
|
+
class OrderPresenter
|
3
|
+
include ::Spree::BaseHelper
|
4
|
+
include ::Spree::ImagesHelper
|
5
|
+
|
6
|
+
def initialize(order:)
|
7
|
+
@order = order
|
8
|
+
@current_store = order.store
|
9
|
+
@products = products(order)
|
10
|
+
end
|
11
|
+
|
12
|
+
def call
|
13
|
+
{
|
14
|
+
email: @order&.user&.email || @order.email,
|
15
|
+
customer_name: @order.name || ::Spree.t('customer'),
|
16
|
+
store_name: @current_store.name,
|
17
|
+
order_id: @order.number,
|
18
|
+
order_number: @order.number,
|
19
|
+
affiliation: @order.store.name,
|
20
|
+
value: subtotal,
|
21
|
+
subtotal: subtotal,
|
22
|
+
item_total: @order.item_total&.to_f,
|
23
|
+
total: @order.total.to_f,
|
24
|
+
revenue: @order.total&.to_f,
|
25
|
+
shipping: @order.shipments.sum(&:cost).to_f,
|
26
|
+
shipping_method: shipping_method_names,
|
27
|
+
tax: @order.additional_tax_total&.to_f,
|
28
|
+
included_tax: @order.included_tax_total&.to_f,
|
29
|
+
discount: @order.promo_total&.to_f,
|
30
|
+
items: products(@order),
|
31
|
+
coupon: @order.coupon_code.to_s,
|
32
|
+
currency: @order.currency,
|
33
|
+
completed_at: @order.completed_at&.iso8601.to_s,
|
34
|
+
checkout_url: ::Spree::Core::Engine.routes.url_helpers.checkout_url(host: @current_store.url_or_custom_domain, token: @order.token),
|
35
|
+
all_adjustments: all_adjustments,
|
36
|
+
bill_address: AddressPresenter.new(address: @order.bill_address).call,
|
37
|
+
ship_address: AddressPresenter.new(address: @order.ship_address).call
|
38
|
+
}.merge(try_shipped_shipments)
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
attr_reader :order, :current_store
|
44
|
+
|
45
|
+
def products(order)
|
46
|
+
@products ||= order.line_items.includes(variant: { product: :taxons }).map.with_index do |line_item, index|
|
47
|
+
LineItemPresenter.new(
|
48
|
+
resource: line_item,
|
49
|
+
quantity: line_item.quantity,
|
50
|
+
total_price: line_item.amount,
|
51
|
+
currency: order.currency,
|
52
|
+
position: index + 1,
|
53
|
+
store: order.store
|
54
|
+
).call
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def all_adjustments
|
59
|
+
@order.all_adjustments.promotion.eligible.group_by(&:label).map do |label, adjustments|
|
60
|
+
{
|
61
|
+
label: label,
|
62
|
+
amount: adjustments.sum(&:amount).to_f
|
63
|
+
}
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def shipping_method_names
|
68
|
+
@shipping_method_names ||= @order.shipments.map { |shipment| shipping_method_name(shipment) }.compact.uniq.join(',')
|
69
|
+
end
|
70
|
+
|
71
|
+
def shipping_method_name(shipment)
|
72
|
+
shipment.shipping_method&.name.to_s
|
73
|
+
end
|
74
|
+
|
75
|
+
def try_shipped_shipments
|
76
|
+
return {} unless @order.fully_shipped? && @order.shipments.shipped.any?
|
77
|
+
|
78
|
+
{
|
79
|
+
shipped_shipments: @order.shipments.shipped.map do |shipment|
|
80
|
+
ShipmentPresenter.new(order: @order, shipment: shipment).call
|
81
|
+
end
|
82
|
+
}
|
83
|
+
end
|
84
|
+
|
85
|
+
def subtotal
|
86
|
+
@order.analytics_subtotal
|
87
|
+
end
|
88
|
+
|
89
|
+
def try_variants(variant)
|
90
|
+
{
|
91
|
+
variant_dict: ::Spree::Variants::OptionsPresenter.new(variant).to_hash
|
92
|
+
}
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module SpreeKlaviyo
|
2
|
+
class ProductPresenter
|
3
|
+
include ::Spree::BaseHelper
|
4
|
+
include ::Spree::ImagesHelper
|
5
|
+
include Rails.application.routes.mounted_helpers
|
6
|
+
|
7
|
+
def initialize(product:, store:)
|
8
|
+
@product = product
|
9
|
+
@current_store = store
|
10
|
+
@current_currency = store.default_currency
|
11
|
+
end
|
12
|
+
|
13
|
+
def call
|
14
|
+
return {} if @product.nil?
|
15
|
+
|
16
|
+
{
|
17
|
+
name: @product.name,
|
18
|
+
price: @product.amount_in(current_currency)&.to_f,
|
19
|
+
brand: @product&.brand_name,
|
20
|
+
category: @product.main_taxon&.pretty_name,
|
21
|
+
currency: current_currency,
|
22
|
+
url: spree_storefront_resource_url(@product, store: @store),
|
23
|
+
image_url: @product.default_image.present? ? spree_image_url(@product.default_image, width: 1200, height: 1200) : '',
|
24
|
+
sku: @product.sku
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
attr_reader :current_store, :current_currency
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module SpreeKlaviyo
|
2
|
+
class ShipmentPresenter < OrderPresenter
|
3
|
+
include ::Spree::BaseHelper
|
4
|
+
include ::Spree::ProductsHelper
|
5
|
+
include ::Spree::ImagesHelper
|
6
|
+
include Rails.application.routes.mounted_helpers
|
7
|
+
|
8
|
+
def initialize(shipment:, order: nil, store: nil)
|
9
|
+
@shipment = shipment
|
10
|
+
@order = order || shipment.order
|
11
|
+
@current_store = store || order.store || shipment.store
|
12
|
+
end
|
13
|
+
|
14
|
+
def call
|
15
|
+
{
|
16
|
+
customer_name: @order.name || ::Spree.t('customer'),
|
17
|
+
email: @order&.user&.email || @order.email,
|
18
|
+
order_number: @order.number,
|
19
|
+
shipping_method: shipping_method_name(@shipment),
|
20
|
+
tracking: @shipment&.tracking.to_s,
|
21
|
+
tracking_url: @shipment&.tracking_url,
|
22
|
+
store_name: @current_store.name,
|
23
|
+
cost: @shipment.final_price.to_f,
|
24
|
+
completed_at: @order.completed_at&.iso8601.to_s,
|
25
|
+
shipped_items: shipped_items,
|
26
|
+
bill_address: AddressPresenter.new(address: @order.bill_address).call,
|
27
|
+
ship_address: AddressPresenter.new(address: @order.ship_address).call
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
attr_reader :order, :shipment, :current_store
|
34
|
+
|
35
|
+
def shipped_items
|
36
|
+
@shipment.manifest.map do |shipped_item|
|
37
|
+
shipped_items_quantity = shipped_item.line_item.quantity
|
38
|
+
{
|
39
|
+
url: spree_storefront_resource_url(shipped_item.variant.product),
|
40
|
+
image_url: shipped_item.variant.default_image.present? ? spree_image_url(shipped_item.variant.default_image, width: 1200, height: 1200) : '',
|
41
|
+
name: shipped_item.variant.name,
|
42
|
+
variant: shipped_item.variant.options_text,
|
43
|
+
sku: shipped_item.variant.sku,
|
44
|
+
shipped_quantity: shipped_item.quantity,
|
45
|
+
total_quantity: shipped_items_quantity,
|
46
|
+
price: shipped_item.line_item.price.to_f,
|
47
|
+
total_price: shipped_item.line_item.amount.to_f,
|
48
|
+
brand: brand_name(shipped_item.variant.product)
|
49
|
+
}.merge(try_variants(shipped_item.variant))
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module SpreeKlaviyo
|
2
|
+
class SubscribePresenter
|
3
|
+
SUBSCRIBED = 'SUBSCRIBED'.freeze
|
4
|
+
UNSUBSCRIBED = 'UNSUBSCRIBED'.freeze
|
5
|
+
|
6
|
+
def initialize(email:, list_id:, type: 'profile-subscription-bulk-create-job', subscribed: true)
|
7
|
+
@email = email
|
8
|
+
@list_id = list_id
|
9
|
+
@type = type
|
10
|
+
@subscribed = subscribed
|
11
|
+
end
|
12
|
+
|
13
|
+
def call
|
14
|
+
{
|
15
|
+
data: {
|
16
|
+
type: @type,
|
17
|
+
attributes: {
|
18
|
+
profiles: {
|
19
|
+
data: [
|
20
|
+
{
|
21
|
+
"type": 'profile',
|
22
|
+
"attributes": {
|
23
|
+
email: @email,
|
24
|
+
"subscriptions": {
|
25
|
+
"email": {
|
26
|
+
"marketing": {
|
27
|
+
"consent": @subscribed ? SUBSCRIBED : UNSUBSCRIBED
|
28
|
+
}
|
29
|
+
}
|
30
|
+
}
|
31
|
+
}
|
32
|
+
}
|
33
|
+
]
|
34
|
+
}
|
35
|
+
},
|
36
|
+
relationships: {
|
37
|
+
list: {
|
38
|
+
data: {
|
39
|
+
type: 'list',
|
40
|
+
id: @list_id
|
41
|
+
}
|
42
|
+
}
|
43
|
+
}
|
44
|
+
}
|
45
|
+
}
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module SpreeKlaviyo
|
2
|
+
class TaxonPresenter
|
3
|
+
include ::Spree::BaseHelper
|
4
|
+
include ::Spree::ImagesHelper
|
5
|
+
include Rails.application.routes.mounted_helpers
|
6
|
+
|
7
|
+
def initialize(taxon:)
|
8
|
+
@taxon = taxon
|
9
|
+
end
|
10
|
+
|
11
|
+
def call
|
12
|
+
{
|
13
|
+
name: @taxon.pretty_name,
|
14
|
+
image_url: @taxon.image.present? ? spree_image_url(@taxon.image, width: 1200, height: 1200) : '',
|
15
|
+
url: spree_storefront_resource_url(@taxon, store: @taxon.store)
|
16
|
+
}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module SpreeKlaviyo
|
2
|
+
class UserPresenter
|
3
|
+
def initialize(email:, address: nil, user: nil, guest_id: nil)
|
4
|
+
@email = email
|
5
|
+
@address = address
|
6
|
+
@user = user
|
7
|
+
@guest_id = guest_id
|
8
|
+
end
|
9
|
+
|
10
|
+
def call
|
11
|
+
{
|
12
|
+
data: {
|
13
|
+
type: 'profile',
|
14
|
+
attributes: {
|
15
|
+
anonymous_id: @guest_id,
|
16
|
+
email: @user.present? ? @user.email : @email,
|
17
|
+
first_name: @user&.first_name || @address&.first_name,
|
18
|
+
last_name: @user&.last_name || @address&.last_name,
|
19
|
+
external_id: @user&.id,
|
20
|
+
location: {
|
21
|
+
address1: @address&.address1,
|
22
|
+
address2: @address&.address2,
|
23
|
+
city: @address&.city,
|
24
|
+
country: @address&.country_name,
|
25
|
+
region: @address&.state_text,
|
26
|
+
zip: @address&.zipcode
|
27
|
+
}
|
28
|
+
}
|
29
|
+
}.merge(try_klaviyo_id)
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def try_klaviyo_id
|
36
|
+
@user&.klaviyo_id.present? ? { id: @user.klaviyo_id } : {}
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module SpreeKlaviyo
|
2
|
+
class CreateEvent < Base
|
3
|
+
prepend ::Spree::ServiceModule::Base
|
4
|
+
|
5
|
+
def call(klaviyo_integration:, event:, resource:, email:, guest_id: nil)
|
6
|
+
return failure(false, ::Spree.t('admin.integrations.klaviyo.not_found')) unless klaviyo_integration
|
7
|
+
|
8
|
+
klaviyo_integration.create_event(
|
9
|
+
event: event,
|
10
|
+
resource: resource,
|
11
|
+
email: email,
|
12
|
+
guest_id: guest_id
|
13
|
+
)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module SpreeKlaviyo
|
2
|
+
class CreateOrUpdateProfile < Base
|
3
|
+
prepend ::Spree::ServiceModule::Base
|
4
|
+
|
5
|
+
def call(klaviyo_integration:, user:, guest_id: nil)
|
6
|
+
return failure(false, ::Spree.t('admin.integrations.klaviyo.not_found')) unless klaviyo_integration
|
7
|
+
|
8
|
+
if user.klaviyo_id.blank?
|
9
|
+
fetch_profile_result = FetchProfile.call(klaviyo_integration: klaviyo_integration, user: user)
|
10
|
+
|
11
|
+
if fetch_profile_result.success?
|
12
|
+
return klaviyo_integration.update_profile(user, guest_id) if guest_id.present?
|
13
|
+
|
14
|
+
return fetch_profile_result
|
15
|
+
end
|
16
|
+
|
17
|
+
klaviyo_integration.create_profile(user).tap do |result|
|
18
|
+
user.update!(klaviyo_id: JSON.parse(result.value).dig('data', 'id')) if result.success?
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
klaviyo_integration.update_profile(user)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module SpreeKlaviyo
|
2
|
+
class FetchProfile < Base
|
3
|
+
prepend ::Spree::ServiceModule::Base
|
4
|
+
|
5
|
+
def call(klaviyo_integration:, user:)
|
6
|
+
return failure(false, ::Spree.t('admin.integrations.klaviyo.not_found')) unless klaviyo_integration
|
7
|
+
|
8
|
+
return success(user.klaviyo_id) if user.klaviyo_id.present?
|
9
|
+
|
10
|
+
klaviyo_integration.fetch_profile(email: user.email).tap do |result|
|
11
|
+
user.update!(klaviyo_id: JSON.parse(result.value)['data'].first['id']) if result.success?
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module SpreeKlaviyo
|
2
|
+
class Subscribe < Base
|
3
|
+
prepend ::Spree::ServiceModule::Base
|
4
|
+
|
5
|
+
def call(klaviyo_integration:, email:, user: nil)
|
6
|
+
return failure(false, ::Spree.t('admin.integrations.klaviyo.not_found')) unless klaviyo_integration
|
7
|
+
|
8
|
+
klaviyo_integration.subscribe_user(email).tap do |result|
|
9
|
+
user.update(klaviyo_subscribed: true) if result.success? && user && !user.klaviyo_subscribed?
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module SpreeKlaviyo
|
2
|
+
class Unsubscribe < Base
|
3
|
+
prepend ::Spree::ServiceModule::Base
|
4
|
+
|
5
|
+
def call(klaviyo_integration:, email:, user: nil)
|
6
|
+
return failure(false, ::Spree.t('admin.integrations.klaviyo.not_found')) unless klaviyo_integration
|
7
|
+
|
8
|
+
klaviyo_integration.unsubscribe_user(email).tap do |result|
|
9
|
+
user.update(klaviyo_subscribed: false) if result.success? && user && user.klaviyo_subscribed?
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
<div class="alert alert-info">
|
2
|
+
<div>
|
3
|
+
Having trouble? Follow <%= external_link_to " our setup guide", "https://spreecommerce.org/docs/integrations/marketing/klaviyo" %> for more information.
|
4
|
+
</div>
|
5
|
+
</div>
|
6
|
+
|
7
|
+
<div class="col-12 col-md-6 p-0">
|
8
|
+
<div class="card mb-4">
|
9
|
+
<div class="card-body">
|
10
|
+
<%= preference_field(
|
11
|
+
@integration,
|
12
|
+
form,
|
13
|
+
"klaviyo_public_api_key",
|
14
|
+
i18n_scope: "admin.integrations.klaviyo",
|
15
|
+
) %>
|
16
|
+
<p class="mb-4 mt-n2">
|
17
|
+
<%= external_link_to "Where to find my API Key?",
|
18
|
+
"https://help.klaviyo.com/hc/en-us/articles/115005062267-How-to-Manage-Your-Account-s-API-Keys" %>
|
19
|
+
</p>
|
20
|
+
|
21
|
+
<%= preference_field(
|
22
|
+
@integration,
|
23
|
+
form,
|
24
|
+
"klaviyo_private_api_key",
|
25
|
+
i18n_scope: "admin.integrations.klaviyo",
|
26
|
+
) %>
|
27
|
+
<p class="mb-4 mt-n2">
|
28
|
+
<%= external_link_to "Where to find my Private API key?",
|
29
|
+
"https://help.klaviyo.com/hc/en-us/articles/115005062267-How-to-Manage-Your-Account-s-API-Keys" %>
|
30
|
+
</p>
|
31
|
+
|
32
|
+
<%= preference_field(
|
33
|
+
@integration,
|
34
|
+
form,
|
35
|
+
"default_newsletter_list_id",
|
36
|
+
i18n_scope: "admin.integrations.klaviyo",
|
37
|
+
) %>
|
38
|
+
<p class="mt-n2">
|
39
|
+
<%= external_link_to "Where to find list ID?",
|
40
|
+
"https://help.klaviyo.com/hc/en-us/articles/115005078647-How-to-find-a-list-ID" %>
|
41
|
+
</p>
|
42
|
+
<div class="alert alert-info mt-n2 mb-0">
|
43
|
+
This list will be used as the default newsletter list for your
|
44
|
+
customers signing up from storefront and checkout.
|
45
|
+
</div>
|
46
|
+
</div>
|
47
|
+
</div>
|
48
|
+
</div>
|