solidus_meta 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/README.md +57 -0
- data/app/controllers/concerns/solidus_meta/controller_helpers.rb +15 -0
- data/app/jobs/solidus_meta/base_job.rb +5 -0
- data/app/jobs/solidus_meta/event_job.rb +7 -0
- data/app/models/solidus_meta/client.rb +29 -0
- data/app/models/solidus_meta/event.rb +147 -0
- data/lib/solidus_meta/configuration.rb +22 -0
- data/lib/solidus_meta/engine.rb +14 -0
- data/lib/solidus_meta/version.rb +5 -0
- data/lib/solidus_meta.rb +18 -0
- metadata +97 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: b9df7c2815f8fcf61690d5f9728822ed9d2e38c2395bfd3a903e7765b8e629fc
|
|
4
|
+
data.tar.gz: 79881489f2cd955db11445883dd359fa415392525b33728d734f5ebcb94be54c
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 27f7e2c7c2c6301b40f1053d8dec9a531acb5cf34081b2ae5bceddccb8e8a9f022fca3dddfd6ca543690cda4542723652bf586fa389e93015e3a38326723a6dc
|
|
7
|
+
data.tar.gz: 9dfbf78a4f965421222ee53d00051727a5600c6e3f3b216a7329ad5063d199da37bd21094bffd130f916c493572cc7df7f13e19bd8611f38afeaf149c0538e2d
|
data/README.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# SolidusMeta
|
|
2
|
+
|
|
3
|
+
Envia eventos da [Conversions API da Meta](https://developers.facebook.com/docs/marketing-api/conversions-api) a partir de pedidos do Solidus: `AddToCart`, `InitiateCheckout`, `AddPaymentInfo` e `Purchase`.
|
|
4
|
+
|
|
5
|
+
## Instalação
|
|
6
|
+
|
|
7
|
+
```ruby
|
|
8
|
+
gem "solidus_meta"
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Configuração
|
|
12
|
+
|
|
13
|
+
Por padrão lê `META_PIXEL_ID`, `META_ACCESS_TOKEN` e `META_STORE_URL` do ambiente. Para sobrescrever:
|
|
14
|
+
|
|
15
|
+
```ruby
|
|
16
|
+
# config/initializers/solidus_meta.rb
|
|
17
|
+
SolidusMeta.configure do |config|
|
|
18
|
+
config.pixel_id = ENV["META_PIXEL_ID"]
|
|
19
|
+
config.access_token = ENV["META_ACCESS_TOKEN"]
|
|
20
|
+
config.store_url = "https://minhaloja.com.br"
|
|
21
|
+
end
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
`store_url` é usado para montar o `event_source_url` de cada evento. Se não for configurado, usa a url da loja do pedido (`order.store.url`). Quando a url vem sem protocolo, a gem adiciona `https://`.
|
|
25
|
+
|
|
26
|
+
## Pixel
|
|
27
|
+
|
|
28
|
+
A gem envia os eventos pelo servidor, mas o pixel da Meta também precisa estar instalado no `<head>` do `app/views/layouts/application.html.erb`. É ele que cria os cookies `_fbp` e `_fbc`, que a gem envia junto de cada evento para a Meta conseguir atribuir a conversão ao clique no anúncio.
|
|
29
|
+
|
|
30
|
+
Obs.: O id do pixel precisa estar dessa forma: `<%= j ENV["NOME_DA_ENV"] %>`. Escrever `ENV["NOME_DA_ENV"]` direto no `<script>` não funciona, porque o navegador não tem acesso ao `ENV`.
|
|
31
|
+
|
|
32
|
+
## Uso
|
|
33
|
+
|
|
34
|
+
Inclua o helper no controller:
|
|
35
|
+
|
|
36
|
+
```ruby
|
|
37
|
+
class ApplicationController < ActionController::Base
|
|
38
|
+
include SolidusMeta::ControllerHelpers
|
|
39
|
+
end
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
E dispare o `EventJob` nos pontos do fluxo da loja. O `meta_tracking_data` lê os cookies `_fbp` e `_fbc` e, se o app usa [Ahoy](https://github.com/ankane/ahoy), a visita atual, para enviar IP e user agent do cliente:
|
|
43
|
+
|
|
44
|
+
```ruby
|
|
45
|
+
SolidusMeta::EventJob.perform_later("AddToCart", line_item_id: line_item.id, quantity:, **meta_tracking_data)
|
|
46
|
+
SolidusMeta::EventJob.perform_later("InitiateCheckout", order_id: order.id, **meta_tracking_data)
|
|
47
|
+
SolidusMeta::EventJob.perform_later("AddPaymentInfo", payment_id: payment.id, **meta_tracking_data)
|
|
48
|
+
SolidusMeta::EventJob.perform_later("Purchase", order_id: order.id, **meta_tracking_data)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Fora de um controller (por exemplo, num job que confirma um pagamento PIX), passe `cookies_meta:` e `ahoy_visit_id:` direto, com os valores salvos durante o checkout.
|
|
52
|
+
|
|
53
|
+
O `event_time` enviado à Meta é o momento em que o job foi enfileirado.
|
|
54
|
+
|
|
55
|
+
### Adicionando um evento
|
|
56
|
+
|
|
57
|
+
Inclua o nome em `SolidusMeta::Event::EVENTS` e crie um método privado em `SolidusMeta::Event` com o nome em snake_case (`ViewContent` → `view_content`). Ele recebe as opções passadas ao job, define `@order` e retorna `event_id`, `path` e, opcionalmente, `fallback` e `custom_data` (que sobrescreve os campos padrão).
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module SolidusMeta
|
|
2
|
+
module ControllerHelpers
|
|
3
|
+
extend ActiveSupport::Concern
|
|
4
|
+
|
|
5
|
+
private
|
|
6
|
+
|
|
7
|
+
def cookies_meta
|
|
8
|
+
{fbp: cookies[:_fbp], fbc: cookies[:_fbc]}
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def meta_tracking_data
|
|
12
|
+
{cookies_meta:, ahoy_visit_id: (current_visit&.id if respond_to?(:current_visit, true))}
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module SolidusMeta
|
|
2
|
+
class Client < Ac::Base
|
|
3
|
+
BASE_URL = "https://graph.facebook.com/v24.0/"
|
|
4
|
+
SAVE_RESPONSES = true
|
|
5
|
+
|
|
6
|
+
def initialize(pixel_id: SolidusMeta.config.pixel_id, access_token: SolidusMeta.config.access_token)
|
|
7
|
+
@pixel_id = pixel_id
|
|
8
|
+
@meta_access_token = access_token
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def send_event(event_name, event_id:, event_source_url:, event_time: Time.current, user_data: {}, custom_data: {})
|
|
12
|
+
params = {access_token: @meta_access_token}
|
|
13
|
+
body = {
|
|
14
|
+
data: [
|
|
15
|
+
{
|
|
16
|
+
event_name:,
|
|
17
|
+
event_time: event_time.to_time.to_i,
|
|
18
|
+
action_source: "website",
|
|
19
|
+
event_source_url:,
|
|
20
|
+
event_id:,
|
|
21
|
+
user_data:,
|
|
22
|
+
custom_data:
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
post("#{@pixel_id}/events", body:, params:)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
module SolidusMeta
|
|
2
|
+
class Event
|
|
3
|
+
EVENTS = %w[AddToCart InitiateCheckout AddPaymentInfo Purchase].freeze
|
|
4
|
+
|
|
5
|
+
attr_reader :order
|
|
6
|
+
|
|
7
|
+
def initialize(client: Client.new)
|
|
8
|
+
@client = client
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def track(event_name, cookies_meta:, ahoy_visit_id: nil, event_time: Time.current, **options)
|
|
12
|
+
raise Error, "evento não suportado: #{event_name}" unless EVENTS.include?(event_name)
|
|
13
|
+
event = send(event_name.underscore, **options)
|
|
14
|
+
|
|
15
|
+
@client.send_event(
|
|
16
|
+
event_name,
|
|
17
|
+
event_time:,
|
|
18
|
+
event_id: event[:event_id],
|
|
19
|
+
event_source_url: File.join(SolidusMeta.config.store_url(order.store), event[:path]),
|
|
20
|
+
user_data: user_data(cookies_meta, ahoy_visit_id, fallback: event[:fallback]),
|
|
21
|
+
custom_data: {
|
|
22
|
+
currency: order.currency,
|
|
23
|
+
value: order.total.to_f,
|
|
24
|
+
order_id: order.number,
|
|
25
|
+
content_type: "product",
|
|
26
|
+
contents: content_line_items,
|
|
27
|
+
**address_data,
|
|
28
|
+
**event.fetch(:custom_data, {})
|
|
29
|
+
}.compact
|
|
30
|
+
)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def add_to_cart(line_item_id:, quantity:)
|
|
36
|
+
line_item = Spree::LineItem.find(line_item_id)
|
|
37
|
+
@order = line_item.order
|
|
38
|
+
|
|
39
|
+
{
|
|
40
|
+
event_id: "#{line_item.id}#{line_item.updated_at.to_i}",
|
|
41
|
+
path: "carrinho",
|
|
42
|
+
fallback: "sem usuário",
|
|
43
|
+
custom_data: {
|
|
44
|
+
currency: line_item.currency,
|
|
45
|
+
value: line_item.total.to_f,
|
|
46
|
+
contents: [content_line_item(line_item, quantity)]
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def initiate_checkout(order_id:)
|
|
52
|
+
@order = Spree::Order.find(order_id)
|
|
53
|
+
|
|
54
|
+
{
|
|
55
|
+
event_id: order.number,
|
|
56
|
+
path: "checkout/address/edit",
|
|
57
|
+
custom_data: {num_items: order.line_items.count}
|
|
58
|
+
}
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def add_payment_info(payment_id:)
|
|
62
|
+
payment = Spree::Payment.find(payment_id)
|
|
63
|
+
@order = payment.order
|
|
64
|
+
|
|
65
|
+
{
|
|
66
|
+
event_id: order.number,
|
|
67
|
+
path: "checkout/payment",
|
|
68
|
+
custom_data: {
|
|
69
|
+
currency: payment.currency,
|
|
70
|
+
value: payment.amount.to_f,
|
|
71
|
+
content_id: payment.number
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def purchase(order_id:)
|
|
77
|
+
@order = Spree::Order.find(order_id)
|
|
78
|
+
|
|
79
|
+
{event_id: order.number, path: "pedidos"}
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def user_data(cookies_meta, ahoy_visit_id, fallback: nil)
|
|
83
|
+
cookies_meta = cookies_meta.to_h.with_indifferent_access
|
|
84
|
+
visit = Ahoy::Visit.find_by(id: ahoy_visit_id) if ahoy_visit_id && defined?(Ahoy::Visit)
|
|
85
|
+
|
|
86
|
+
{
|
|
87
|
+
em: sha256(email || fallback),
|
|
88
|
+
fn: sha256(name || fallback),
|
|
89
|
+
ph: sha256(phone),
|
|
90
|
+
ct: sha256(address&.city&.downcase),
|
|
91
|
+
st: sha256(address&.state&.name&.downcase),
|
|
92
|
+
zp: sha256(address&.zipcode),
|
|
93
|
+
country: sha256(country),
|
|
94
|
+
external_id: address&.id&.to_s,
|
|
95
|
+
client_user_agent: visit&.user_agent,
|
|
96
|
+
client_ip_address: visit&.ip,
|
|
97
|
+
fbp: cookies_meta[:fbp],
|
|
98
|
+
fbc: cookies_meta[:fbc]
|
|
99
|
+
}.compact
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def address_data
|
|
103
|
+
{
|
|
104
|
+
postal_code: address&.zipcode,
|
|
105
|
+
region: state,
|
|
106
|
+
country:
|
|
107
|
+
}
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def content_line_items
|
|
111
|
+
order.line_items.map { |line_item| content_line_item(line_item, line_item.quantity) }
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def content_line_item(line_item, quantity)
|
|
115
|
+
{id: line_item.sku, quantity:, item_price: line_item.price.to_f}
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def address
|
|
119
|
+
order.ship_address || order.user&.address
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def state
|
|
123
|
+
address&.state&.name&.downcase
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def country
|
|
127
|
+
address&.country&.name&.downcase
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def name
|
|
131
|
+
(order.name || order.user&.name)&.downcase
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def email
|
|
135
|
+
(order.email || order.user&.email)&.downcase
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def phone
|
|
139
|
+
return unless address&.phone.present?
|
|
140
|
+
"55" + address.phone.remove(/\D/).remove(/^55/)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def sha256(value)
|
|
144
|
+
Digest::SHA256.hexdigest(value.strip) if value.present?
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SolidusMeta
|
|
4
|
+
class Configuration
|
|
5
|
+
attr_writer :pixel_id, :access_token, :store_url
|
|
6
|
+
|
|
7
|
+
def pixel_id
|
|
8
|
+
@pixel_id || ENV["META_PIXEL_ID"]
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def access_token
|
|
12
|
+
@access_token || ENV["META_ACCESS_TOKEN"]
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def store_url(store = nil)
|
|
16
|
+
url = @store_url.presence || ENV["META_STORE_URL"].presence || store&.url.presence
|
|
17
|
+
|
|
18
|
+
raise Error, "store_url não configurado: defina SolidusMeta.config.store_url, META_STORE_URL ou a url da loja" unless url
|
|
19
|
+
url.start_with?("http") ? url : "https://#{url}"
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "solidus_core"
|
|
4
|
+
require "solidus_support"
|
|
5
|
+
|
|
6
|
+
module SolidusMeta
|
|
7
|
+
class Engine < Rails::Engine
|
|
8
|
+
include SolidusSupport::EngineExtensions
|
|
9
|
+
|
|
10
|
+
isolate_namespace SolidusMeta
|
|
11
|
+
|
|
12
|
+
engine_name "solidus_meta"
|
|
13
|
+
end
|
|
14
|
+
end
|
data/lib/solidus_meta.rb
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "ac"
|
|
4
|
+
require "solidus_meta/version"
|
|
5
|
+
require "solidus_meta/configuration"
|
|
6
|
+
require "solidus_meta/engine"
|
|
7
|
+
|
|
8
|
+
module SolidusMeta
|
|
9
|
+
class Error < StandardError; end
|
|
10
|
+
|
|
11
|
+
def self.config
|
|
12
|
+
@config ||= Configuration.new
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.configure
|
|
16
|
+
yield config
|
|
17
|
+
end
|
|
18
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: solidus_meta
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- marialuiza1324
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-10-29 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: ac
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: 1.1.0
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: 1.1.0
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: solidus_core
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '0'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: solidus_support
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ">="
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '0'
|
|
48
|
+
type: :runtime
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ">="
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0'
|
|
55
|
+
description: Envia eventos para a Conversions API da Meta a partir de pedidos do Solidus.
|
|
56
|
+
email:
|
|
57
|
+
- marialuiza.nerypinto@yahoo.com.br
|
|
58
|
+
executables: []
|
|
59
|
+
extensions: []
|
|
60
|
+
extra_rdoc_files: []
|
|
61
|
+
files:
|
|
62
|
+
- README.md
|
|
63
|
+
- app/controllers/concerns/solidus_meta/controller_helpers.rb
|
|
64
|
+
- app/jobs/solidus_meta/base_job.rb
|
|
65
|
+
- app/jobs/solidus_meta/event_job.rb
|
|
66
|
+
- app/models/solidus_meta/client.rb
|
|
67
|
+
- app/models/solidus_meta/event.rb
|
|
68
|
+
- lib/solidus_meta.rb
|
|
69
|
+
- lib/solidus_meta/configuration.rb
|
|
70
|
+
- lib/solidus_meta/engine.rb
|
|
71
|
+
- lib/solidus_meta/version.rb
|
|
72
|
+
homepage: https://github.com/todasessascoisas/solidus_meta
|
|
73
|
+
licenses: []
|
|
74
|
+
metadata: {}
|
|
75
|
+
post_install_message:
|
|
76
|
+
rdoc_options: []
|
|
77
|
+
require_paths:
|
|
78
|
+
- lib
|
|
79
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
80
|
+
requirements:
|
|
81
|
+
- - ">="
|
|
82
|
+
- !ruby/object:Gem::Version
|
|
83
|
+
version: '3.1'
|
|
84
|
+
- - "<"
|
|
85
|
+
- !ruby/object:Gem::Version
|
|
86
|
+
version: '4'
|
|
87
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
88
|
+
requirements:
|
|
89
|
+
- - ">="
|
|
90
|
+
- !ruby/object:Gem::Version
|
|
91
|
+
version: '0'
|
|
92
|
+
requirements: []
|
|
93
|
+
rubygems_version: 3.5.3
|
|
94
|
+
signing_key:
|
|
95
|
+
specification_version: 4
|
|
96
|
+
summary: Eventos da Conversions API da Meta para lojas Solidus
|
|
97
|
+
test_files: []
|