Skip to content

Instantly share code, notes, and snippets.

@drewish
Last active August 20, 2019 16:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save drewish/b1a562e2c99060dc2ab6fad9d093b7dc to your computer and use it in GitHub Desktop.
Save drewish/b1a562e2c99060dc2ab6fad9d093b7dc to your computer and use it in GitHub Desktop.
# This is a script to demonstrate some features of Recurly's V3 API. It shows
# how to use multiple clients for separate sites to copy plan and add-on data
# between them.
require 'bundler/inline'
gemfile do
source 'https://rubygems.org'
gem 'recurly', '3.0.0.beta.6'
gem 'pry'
end
SOURCE_API_KEY = '?'
SOURCE_SITE_ID = '?'
DESTINATION_API_KEY = '?'
DESTINATION_SITE_ID = '?'
PLAN_ATTRIBUTES = %i(
code name description interval_unit interval_length trial_unit trial_length
total_billing_cycles auto_renew accounting_code setup_fee_accounting_code
tax_code tax_exempt
)
ADD_ON_ATTRIBUTES = %i(
code name display_quantity default_quantity accounting_code tax_code
)
source_client = Recurly::Client.new(api_key: SOURCE_API_KEY, site_id: SOURCE_SITE_ID)
destination_client = Recurly::Client.new(api_key: DESTINATION_API_KEY, site_id: DESTINATION_SITE_ID)
puts "Copying plans and add-ons from '#{source_client.get_site.subdomain}' to '#{destination_client.get_site.subdomain}'"
# We can track the mappings here... might not be necessary.
plan_map = {}
source_client.list_plans(state: :active).each do |source_plan|
# Some of the attributes are read-only and cannot be assigned during creation
# this is all we'll copy over. We need the `compact` call to get rid of any
# `nil` values.
attributes = source_plan.attributes.slice(*PLAN_ATTRIBUTES).compact
# The pricing in the currencies structure is more complicated and needs a
# litte extra attention.
attributes[:currencies] = source_plan.currencies.map(&:attributes)
# A plan might already exist in the destination site and we'd need to update
# it rather than create a new one. We could either check if it exists and then
# do a create or update... or we could just try the create and if it says it
# already exists do an update. We'll demonstrate the former for plans and the
# later for the add-ons
begin
# We don't actually need the response we're just checking if it exists.
destination_client.get_plan(plan_id: "code-#{source_plan.code}")
# Sadly, we can't change the interval_unit or interval_length of an existing
# plan so we'll have to exclude those from updates. The only way to change
# them is to delete the plan and recreate it.
attributes.delete(:interval_unit)
attributes.delete(:interval_length)
destination_plan = destination_client.update_plan(
plan_id: "code-#{source_plan.code}",
body: attributes
)
puts "Updated plan '#{destination_plan.code}'"
plan_map[source_plan.id] = destination_plan.id
rescue Recurly::Errors::NotFoundError => ex
destination_plan = destination_client.create_plan(body: attributes)
puts "Created plan '#{destination_plan.code}'"
plan_map[source_plan.id] = destination_plan.id
end
end
source_client.list_add_ons(state: :active).each do |source_add_on|
# Now we need to use the plan_map we built that tracks how the source and
# destination plans are related. If a plan isn't in the map then it wasn't
# copied over due to an error and we should skip its add-ons.
destination_plan_id = plan_map[source_add_on.plan_id]
next unless destination_plan_id
attributes = source_add_on.attributes.slice(*ADD_ON_ATTRIBUTES).compact
attributes[:currencies] = source_add_on.currencies.map(&:attributes)
begin
destination_add_on = destination_client.create_plan_add_on(
plan_id: destination_plan_id,
body: attributes
)
puts "Created add-on '#{destination_add_on.code}'"
rescue Recurly::Errors::ValidationError => ex
if ex.recurly_error.params.any? { |p| p['param'] == 'code' && p['message'] == 'has already been taken' }
destination_add_on = destination_client.update_plan_add_on(
plan_id: destination_plan_id,
add_on_id: "code-#{source_add_on.code}",
body: attributes
)
puts "Updated add-on '#{destination_add_on.code}'"
else
puts "Error creating add-on: #{ex}"
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment