Ruby Scripting
As a rails developer, I spend a majority of my time in rails. This is fine, because I love rails, but it's nice to write something outside of rails from time to time...in particular, a ruby script.
Stripe Transfer
My client was recently acquired and needed to migrate their stripe account to another stripe account. Stripe will handle transferring customers and cards, but it's up to you to transfer the rest. The client's app was a SaaS app and heavily made use of subscriptions. The account had thousands of subscriptions that needed to be ported over to the new account.
Using the Stripe gem and the api, I was able to transfer them over without much trouble.
require 'stripe'
require 'pry-debugger'
MODE = :live
def old_stripe_key
# live
# key = "xxx"
# test
key = "xxx"
key
end
def new_stripe_key
# live
# key = "xxx"
# test
key = "xxx"
key
end
def old_customers
Stripe::Customer.all({limit: 10}, old_stripe_key)
end
def new_customers
Stripe::Customer.all({ limit: 100 }, new_stripe_key)
end
def migrate(customers)
puts "The End" && return if customers.data.empty?
puts customers.data.count
sync_customers(customers.data)
migrate(Stripe::Customer.all({ limit: 100, starting_after: customers.data.last.id }, old_stripe_key))
end
def sync_customers(customers)
customers.each do |customer|
File.open('sync.log', 'a') { |file| file.write("\n#{customer.id} ") }
puts customer.id
subscriptions = customer.subscriptions
if subscriptions.data.any?
subscriptions.data.each do |subscription|
File.open('sync.log', 'a') { |file| file.write("#{subscription.id}") }
puts subscription.id
plan_id = subscription.plan.id
current_period_end = subscription.current_period_end
if MODE == :test
new_customer = Stripe::Customer.create(
{ :description => "clone #{customer.id}" },
new_stripe_key
)
else
new_customer = Stripe::Customer.retrieve(customer.id, new_stripe_key)
end
unless new_customer.subscriptions.data.map(&:id).include?(subscription.id)
new_customer.subscriptions.create(:plan => plan_id, :billing_cycle_anchor => current_period_end, :prorate => false)
subscription.delete unless MODE == :test
end
end
end
end
end
def print_active_subscriptions(customers)
puts "The End" && return if customers.data.empty?
puts customers.data.count
active_subscriptions_for(customers.data)
print_active_subscriptions(Stripe::Customer.all({ limit: 100, starting_after: customers.data.last.id }, customers.api_key))
end
def active_subscriptions_for(customers)
customers.each do |customer|
subscriptions = customer.subscriptions
if subscriptions.data.any?
subscriptions.data.each do |subscription|
puts "#{customer.id} #{subscription.id}"
end
end
end
end
As you can see it's a crude but straightforward script. We didn't have a lot of time to implement it so I just hashed it out rather procedurally.
A couple of the challenges were:
1. How to jump back and forth from the old stripe account and new account
Typically you see api gems do something like
connection = API::Connection.new(:api_key => 'xxx')
# Then
connection.customers.all
But I didn't see how to get this type of instance with the stripe api.
However, Stripe has great support and Brian Collins helped me out with
how to pass the api_key
. It's the last argument in the constructor,
which means if you're passing options like in the all
method, you have
to wrap those options in {}
.
2. How to run a test on non-live data
Stripe gives you a great testing environment, but for our scenario (we would have customers in both accounts with identical ID's) we couldn't mimic the live setup(you cannot specify ID's when creating customers). So while in test mode, what I chose to do was just clone the customer instead of finding them in the new account. And by passing in the old customer id to the description, I could easily locate the new customer to compare subscriptions.
if MODE == :test
new_customer = Stripe::Customer.create(
{ :description => "clone #{customer.id}" },
new_stripe_key
)
else
new_customer = Stripe::Customer.retrieve(customer.id, new_stripe_key)
end
3. Setting up the subscription
First off, we had to recreate our plans with the same id in the new account. Typically this won't be a problem to manually do, because you'll likely only have a handful of plans. Just make sure to get the exact id and price (unless you're changing prices). Next we want to make sure the customer doesn't see a change in their billing. If they were billed on the 7th of last month, then their next billing should be the 7th. Also Stripe will automatically prorate a subscription, so you don't want that to happen (because their already paid for the whole period on their last billing). The solution was pretty easy:
new_customer.subscriptions.create(
:plan => plan_id,
:billing_cycle_anchor => current_period_end,
:prorate => false)
Set prorate to false and the billing_cycle_anchor
to the date of
current_period_end
of the old subscription, and the plan to the plan_id
of the old subscription.
Summary
It seemed to work well. When we first ran the script on the live data, we only did a few:
pry> customers = old_customers.data[13..14]
pry> sync_customers customers
That's when we discovered we had a setting in Stripe of notify the customer if a subscription was canceled. Oops! Glad we caught that before running it on thousands of customers.
Also you can see that there are two methods at the end to print out all of the active subscriptions. We used this in a before and after snapshot to make sure our numbers added up.
The script could probably be written a little more ruby like but for a quick job, it worked out OK.
What do you think? How would you have done it?