Class: Facebook::ApiClient

Inherits:
BaseService show all
Defined in:
app/services/facebook/api_client.rb

Overview

Service object: API client.

Constant Summary collapse

API_VERSION =

Marketing Graph API version. Bump in lockstep with
Facebook::AdvertiserApiClient::API_VERSION.

'v25.0'
BASE_URL =

URL for base.

"https://graph.facebook.com/#{API_VERSION}".freeze

Instance Attribute Summary

Attributes inherited from BaseService

#options

Instance Method Summary collapse

Methods inherited from BaseService

#initialize, #log_debug, #log_error, #log_info, #log_warning, #logger, #process, #tagged_logger

Constructor Details

This class inherits a constructor from BaseService

Instance Method Details

#send_events(pixel_id:, token:, events:, test_event_code: nil) ⇒ Hash

Send one or more conversion events to a Facebook Pixel.

Parameters:

  • pixel_id (String)

    the Facebook Pixel ID (path segment).

  • token (String)

    the Facebook CAPI System User access token.

  • events (Array<Hash>)

    event objects per Meta's spec
    (event_name, event_time, event_id, action_source,
    user_data, custom_data, etc.).

  • test_event_code (String, nil) (defaults to: nil)

    when set, Meta routes the event
    to the Test Events tab in Events Manager and does NOT count it
    toward attribution — used during canary rollout to verify schema
    acceptance before flipping to real sends.

Returns:

  • (Hash)

    { status: :reported|:test_event_ok|:rate_limited|:failed, http_status:, error: }



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'app/services/facebook/api_client.rb', line 40

def send_events(pixel_id:, token:, events:, test_event_code: nil)
  body = { data: events }
  body[:test_event_code] = test_event_code if test_event_code.present?

  response = connection(token).post("#{pixel_id}/events") do |req|
    req.body = body.to_json
  end

  case response.status
  when 200
    if test_event_code.present?
      Rails.logger.info "Facebook::ApiClient: test_event_code=#{test_event_code} payload accepted (HTTP 200)"
      { status: :test_event_ok, http_status: 200 }
    else
      Rails.logger.info 'Facebook::ApiClient: events accepted (HTTP 200)'
      { status: :reported, http_status: 200 }
    end
  when 429
    error_msg = 'Rate limited (HTTP 429)'
    Rails.logger.warn "Facebook::ApiClient: #{error_msg}"
    { status: :rate_limited, http_status: 429, error: error_msg }
  else
    body       = safe_parse(response.body)
    meta_error = body['error'].is_a?(Hash) ? body['error'] : {}
    error_msg  = meta_error['message'] || body['message'] || "HTTP #{response.status}"
    # Surface Meta's diagnostic fields. "An unknown error has occurred." is
    # Meta's OWN generic message (Graph error code 1/2 = transient, "retry
    # later"); without the code/subcode/fbtrace_id the report is undiagnosable
    # (AppSignal #5248). Pass them through so transient-vs-real is visible.
    Rails.logger.error(
      "Facebook::ApiClient: Failed -- #{error_msg} " \
      "(code=#{meta_error['code']} subcode=#{meta_error['error_subcode']} " \
      "fbtrace_id=#{meta_error['fbtrace_id']} http=#{response.status})"
    )
    {
      status:        :failed,
      http_status:   response.status,
      error:         error_msg,
      error_code:    meta_error['code'],
      error_subcode: meta_error['error_subcode'],
      fbtrace_id:    meta_error['fbtrace_id']
    }
  end
rescue Faraday::TimeoutError => e
  { status: :failed, http_status: nil, error: "Timeout: #{e.message}" }
rescue Faraday::Error => e
  { status: :failed, http_status: nil, error: e.message }
end