Solidus integration

Tariffs API for Solidus.

A Rails-native way to add US duty calculation to a Solidus store. One HTTP call, drop the result into the order as a calculator or adjustment.

Solidus is built on Rails and so are we, which means the integration is short and predictable. This page shows how to wire Tariffs API into Solidus as a calculator on a shipping rate, as an order adjustment, or as a line-item modifier — whichever fits your store's accounting needs.

Why this works

Built for Solidus developers.

Rails to Rails

No SDK, no GraphQL client. Net::HTTP or Faraday calls the resolve endpoint and you parse JSON. The whole integration fits in one initializer plus one PORO.

Calculator-compatible

Solidus inherits the Spree::Calculator interface. Tariffs API maps cleanly into a calculator subclass that returns the duty as a money value.

Public, flat pricing

$199/month, 100,000 calls included. Same price for a side project and a Series B Solidus store. No per-shipment metering.

Audit-ready response

Every response includes the Chapter 99 codes applied, the rate components, and a confidence score. Easy to log alongside the order for compliance review.

In your code

Drop it in.

Two snippets: a thin client PORO, then a calculator subclass you register with Solidus.

1. Client PORO (app/services/tariffs_api.rb)

ruby
require "net/http"
require "json"

class TariffsApi
  BASE = "https://tariffsapi.com/api/v1"

  def self.resolve(hts:, origin:)
    uri = URI("#{BASE}/tariffs/resolve?hts=#{hts}&origin=#{origin}")
    req = Net::HTTP::Get.new(uri)
    req["Authorization"] = "Bearer #{ENV.fetch('TARIFFSAPI_KEY')}"
    res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) }
    JSON.parse(res.body)
  end
end

2. Solidus calculator (app/models/spree/calculator/tariffs_api_duty.rb)

ruby
module Spree
  class Calculator::TariffsApiDuty < Calculator
    def self.description
      "US duty via Tariffs API"
    end

    def compute_line_item(line_item)
      hts = line_item.variant.product.hts_code
      origin = line_item.variant.product.country_of_origin
      return 0 if hts.blank? || origin.blank?

      data = TariffsApi.resolve(hts: hts, origin: origin)
      rate = data.dig("summary", "total_resolved_ad_valorem_rate").to_f / 100
      (line_item.amount * rate).round(2)
    end
  end
end

3. Register the calculator (config/initializers/spree.rb)

ruby
Rails.application.config.to_prepare do
  Rails.application.config.spree.calculators.shipping_methods << Spree::Calculator::TariffsApiDuty
end
What you get back

One endpoint. Deterministic JSON.

GET /api/v1/tariffs/resolve returns the base duty, every applicable Chapter 99 measure, and a confidence score. The response is stable JSON, easy to memoize per (hts, origin) pair in Rails.cache to keep your call count down.

GET /api/v1/tariffs/resolve?hts=8541.10.00.80&origin=CN

{
  "summary": {
    "applicable_ad_valorem_rate": 0.0,
    "resolved_additional_ad_valorem_rate": 25.0,
    "total_resolved_ad_valorem_rate": 25.0
  },
  "base_tariff": { "percentage_component": 0.0 },
  "additional_measures": [
    {
      "program": "section_301",
      "chapter_99_code": "9903.91.05",
      "resolved_rate": { "percentage_component": 0.25 }
    }
  ]
}
FAQ

Solidus + Tariffs API

Is there an official tariffs_api gem?

Not yet. A Solidus-friendly gem is on the roadmap. For now the integration is the 30 lines of Ruby above, which most teams want to own anyway.

Where should the HTS code live on a Solidus product?

Add an hts_code string column to Spree::Product via a migration, or store it in product properties. Country of origin can live on the variant if it varies by SKU.

How do I avoid hitting the API on every cart update?

Memoize the resolve call in Rails.cache keyed by [hts, origin]. Duty rates change rarely (when a Chapter 99 measure is published) and our response is safe to cache for an hour or more. The /api/v1/tariffs/resolve_batch endpoint is also there for cart-level batching.

Does this work with Spree?

Yes. Solidus is Spree-API compatible, so the same calculator pattern works on Spree. See the dedicated Spree page for Spree-specific notes.

Sources

Add US duty to your Solidus checkout in an afternoon.