I have two subscription plans in my Ruby on Rails application. I use stripe webhook to email to customer when subscription has been created. In the email I want to store data about subscription (and plan) details e.g. when trial_end and plan name or price.
def webhook
stripe_event = Stripe::Event.retrieve(params[:id]) #retrieving Event ID
if stripe_event.type == "customer.subscription.created" #checks if retrieved Event type subscription is created
stripe_customer_token = stripe_event.data.object.customer # Get Customer ID
customer = Stripe::Customer.retrieve(stripe_customer_token) #here I'm able to retrieve Customer data e.g. customer.email
subscription = customer.subscriptions.first.id #according to documentation I need to retrieve Subscription by supplying its ID. I can retrieve Subscription, but don't understand how to retrieve its data, like: subscription.trial_end
UserMailer.customer_subscription_created(customer.email).deliver #this works well
UserMailer.customer_subscription_created(subscription.trial_end).deliver #this does not work
end
end
I have retrieved Subscription of my Customer. When I retrieve customer I can access my customer data like: customer.email I assumed I would be able to do the same when I retrieve Subscription: subscription.trial_end, but this gives me an error. How can I access Subscription data?
Besides when I change plan of a Subscription I do it like so and it works:
def change_user_plan(customer_id, subscription_id)
customer = Stripe::Customer.retrieve("#{customer_id}")
subscription = customer.subscriptions.retrieve("#{subscription_id}")
subscription.plan = 2
subscription.save
end
Here is link to Stripe API to retrieve Subscription
You are correct, you are able to do what you are trying to do. Once you have your subscription, subscription.trial_end works. I just tested it:
2.1.6 :013 > customer = Stripe::Customer.retrieve("#{customer_id}")
=> #<Stripe::Customer:0x3fcd1ed0a630 id=...> JSON: { ... }
2.1.6 :014 > subscription = customer.subscriptions.retrieve("#{subscription_id}")
=> #<Stripe::Subscription:0x3fcd1ecae574 id=...> JSON: { ... }
2.1.6 :015 > subscription.trial_end
=> 1438387199
The problem is this line:
subscription = customer.subscriptions.first.id
You are saving the subscription id itself. You need to do:
subscription = customer.subscriptions.first
to save the whole subscription. Also, you can use subscriptions.retrieve to supply the id for retrieval (as you are doing in your second code example).
Related
So I have a rails app where I'm selling subscriptions to access content. For users in NJ, I have to charge them 7% sales tax. Everywhere else is 0. So when they register an account with Devise, I record their state.
Then in my SubscriptionsController.rb, I check to see if they live in NJ:
data = if current_user.home_state == "NJ"
subscription_params.merge(tax_percent: 7)
else
subscription_params
end
#subscription = ::Subscription.new(data)
When the subscription gets created, the subscription table gets updated properly. I also assumed that when the user makes a purchase this tax_percent would also hit Stripe's API, but it doesn't.
So I tried adding this to the subscription creation form:
<%= f.hidden_field :tax_percent %>
and it does hit Stripe's API, but hits as tax_percent: '', which is null.
So I can always perform a logic check like this:
<% if current_user.home_state == "NJ" %>
<%= hidden_field :tax_percent, :value => '7' %>
<% end %>
But then I'm exposing the value to the client, which can be manipulated.
What's the best 'rails' way to submit this value to Stripe's API?
It's a bit hard to tell what's going on based on the code-sample you've provided, but ultimately you want to set the tax-rate on individual users when you Create the Subscription [1].
If you're going to be doing one-off payments, you'll need to also store that information somewhere else on the Customer record. I might recommend you utilize the metadata-parameter [2] of the Customer record. You can set it by making an Update Customer [3] API Request. Then, when you're making a charge [4], you'll want to multiply it by whatever tax coefficient you've stored.
Another (probably better) approach, would be to create a pending invoice item [5] for the one-time purchase (without tax being included). Then create an Invoice [6] with the tax_percent-parameter being populated. And finally, pay the Invoice [7]. This will make a trackable record of purchases made with line-items for their tax percent kept separate.
Hope that helps!
[1] https://stripe.com/docs/api#create_subscription
[2] https://stripe.com/docs/api#metadata
[3] https://stripe.com/docs/api#update_customer
[4] https://stripe.com/docs/api#create_charge
[5] https://stripe.com/docs/api#create_invoiceitem
[6] https://stripe.com/docs/api#create_invoice
[7] https://stripe.com/docs/api#pay_invoice
I'm trying to test the scenario in my Rails app where a customer has allowed a subscription to go to 'unpaid' (usually because the card expired and was not updated during the two weeks while Stripe retried the charge) and is finally getting around to updating the card and reactivating the account. I believe I have the logic correct (update the card and pay each unpaid invoice) but I'd like to be able to test it, or better yet, write some RSpec tests (a feature test and possibly a controller test). The problem is, I can't figure out how to create a mock situation in which a subscription is 'unpaid'. (I suppose I could create a bunch of accounts with expired cards and wait two weeks to test, but that's not an acceptable solution. I can't even change the subscription retry settings for only the 'test' context to speed up the process.) I found stripe-ruby-mock but I can't manually set the status of a subscription.
Here's what I've tried:
plan = Stripe::Plan.create(id: 'test')
customer = Stripe::Customer.create(id: 'test_customer', card: 'tk', plan: 'test')
sub = customer.subscriptions.retrieve(customer.subscriptions.data.first.id)
sub.status = 'unpaid'
sub.save
sub = customer.subscriptions.retrieve(customer.subscriptions.data.first.id)
expect(sub.status).to eq 'unpaid'
This was the result with stripe-ruby-mock:
Failure/Error: expect(sub.status).to eq 'unpaid'
expected: "unpaid"
got: "active"
Stripe's recommended procedure is:
Setup the customer with the card 4000000000000341 ("Attaching this card to a Customer object will succeed, but attempts to charge the customer will fail.")
Give the customer a subscription but with a trial date ending today/tomorrow.
Wait
Step three is annoying, but it'll get the job done.
Taking #VoteyDisciple's suggestion, I've worked through something to have this reasonably automated in RSpec ('reasonably', given the circumstances). I'm using VCR to capture the API calls (against the test Stripe environment), which is essential, as it means the sleep call only happens when recording the test the first time.
Using comments to indicate behaviour done via Stripe's API, as my implementation is rather caught up in my project, and that's not so useful.
VCR.use_cassette('retry') do |cassette|
# Clear out existing Stripe data: customers, coupons, plans.
# This is so the test is reliably repeatable.
Stripe::Customer.all.each &:delete
Stripe::Coupon.all.each &:delete
Stripe::Plan.all.each &:delete
# Create a plan, in my case it has the id 'test'.
Stripe::Plan.create(
id: 'test',
amount: 100_00,
currency: 'AUD',
interval: 'month',
interval_count: 1,
name: 'RSpec Test'
)
# Create a customer
customer = Stripe::Customer.create email: 'test#test.test'
token = card_token cassette, '4000000000000341'
# Add the card 4000000000000341 to the customer
customer.sources.create token: 'TOKEN for 0341'
# Create a subscription with a trial ending in two seconds.
subscription = customer.subscriptions.create(
plan: 'test',
trial_end: 2.seconds.from_now.to_i
)
# Wait for Stripe to create a proper invoice. I wish this
# was unavoidable, but I don't think it is.
sleep 180 if cassette.recording?
# Grab the invoice that actually has a dollar value.
# There's an invoice for the trial, and we don't care about that.
invoice = customer.invoices.detect { |invoice| invoice.total > 0 }
# Force Stripe to attempt payment for the first time (instead
# of waiting for hours).
begin
invoice.pay
rescue Stripe::CardError
# Expecting this to fail.
end
invoice.refresh
expect(invoice.paid).to eq(false)
expect(invoice.attempted).to eq(true)
# Add a new (valid) card to the customer.
token = card_token cassette, '4242424242424242'
card = customer.sources.create token: token
# and set it as the default
customer.default_source = card.id
customer.save
# Run the code in your app that retries the payment, which
# essentially invokes invoice.pay again.
# THIS IS FOR YOU TO IMPLEMENT
# And now we can check that the invoice wass successfully paid
invoice.refresh
expect(invoice.paid).to eq(true)
end
Getting card tokens in an automated fashion is a whole new area of complexity. What I've got may not work for others, but here's the ruby method, calling out to phantomjs (you'll need to send the VCR cassette object through):
def card_token(cassette, card = '4242424242424242')
return 'tok_my_default_test_token' unless cassette.recording?
token = `phantomjs --ignore-ssl-errors=true --ssl-protocol=any ./spec/fixtures/stripe_tokens.js #{ENV['STRIPE_PUBLISH_KEY']} #{card}`.strip
raise "Unexpected token: #{token}" unless token[/^tok_/]
token
end
And the javascript file that phantomjs is running (stripe_tokens.js) contains:
var page = require('webpage').create(),
system = require('system');
var key = system.args[1],
card = system.args[2];
page.onCallback = function(data) {
console.log(data);
phantom.exit();
};
page.open('spec/fixtures/stripe_tokens.html', function(status) {
if (status == 'success') {
page.evaluate(function(key, card) {
Stripe.setPublishableKey(key);
Stripe.card.createToken(
{number: card, cvc: "123", exp_month: "12", exp_year: "2019"},
function(status, response) { window.callPhantom(response.id) }
);
}, key, card);
}
});
Finally, the HTML file involved (stripe_tokens.html) is pretty simple:
<html>
<head>
<script type="text/javascript" src="https://js.stripe.com/v2/"></script>
</head>
<body></body>
</html>
Put all of that together, and, well, it might work! It does for our app :)
Im trying to cancel a subscription which is already created. I did pass the correct customer id and plan id. Yet i get an error in my terminal saying
Stripe::InvalidRequestError (Customer cus_xxxxxxxxxxx does not have a subscription with ID Golden)
Here are my controller methods.
def create
sub_plan = params[:plan_id]
subscription_plan_id = SubscriptionPlan.where(plan_id:sub_plan).first.id
token = params[:stripeToken]
email = current_user.email
customer = Stripe::Customer.create(
:card => token,
:plan => sub_plan,
:email => email
)
Subscription.create(subscription_plan_id:subscription_plan_id,user_id:current_user.id,stripe_customer_token:customer[:id])
redirect_to '/membership'
end
def destroy
p "---------------------------"
subscription_plan_id = SubscriptionPlan.where(plan_id:params[:plan_id]).first.id
customer_id = Subscription.where(subscription_plan_id:subscription_plan_id,user_id:current_user.id).first.stripe_customer_token
customer = Stripe::Customer.retrieve(customer_id)
customer.subscriptions.retrieve(params[:plan_id]).delete()
Subscription.where(subscription_plan_id:subscription_plan_id,user_id:current_user.id).first.destroy
end
in my create method a user creates a subscription which happens successfuly, It appears on my stripe account too..
But on trying to destroy it this error occurs. Is there anything wrong. Pls help. Thanx in advance
A stripe subscription can't be retrieve with a plan id.
To retrieve a subscription : first you need a customer :
customer = Stripe::Customer.retrieve("cus_xxxxxxxxxx")
With the customer you can retrieve all his subscriptions :
subscriptions = customer.subscriptions.data
Or a specific subscription with retrieve method and stripe_id of subscription ( doc ref : https://stripe.com/docs/api/ruby#retrieve_subscription ) :
subscription = customer.subscriptions.retrieve("sub_xxxxxxxxxx")
Hope this helps
I am working on a small app that allows for users to add a product (or subscription) to their cart. Upon creating their account, the new user is sent to a "bundle" page where it asks if they would like to add a different subscription to a different product altogether for a bundled price.
Here is where I am stuck: Upon submitting the user's credit card info I get slightly "lost in translation" when trying to setup the bundle pricing to submit to Authorize.net (I understand how to authnet, not the question here).
Here is what I have so far:
current_order.products.includes(:client).each do |product|
transaction = current_order.submit_order_to_authnet(product)
if transaction.result_code == 'Ok'
new_group = Group.create!(:name => "#{current_user.full_name} #{product.title}", :type => 'school', :start_date => Time.now, :status => 'active', :site_id => 1)
primary = session[:primary_product_id].eql?(product.id) ? true : false
# Add subscription to Group
new_group.add_subscription(product, current_order, transaction.subscription_id, 'active', primary)
# Add Subscription to CurrentOrder
current_order.subscriptions << new_group.subscriptions.last
# Add user to NewGroup
current_user.groups << new_group
# Create New Group Admin
new_group.group_admins.create(:user_id => current_user.id)
# Send success email
OrderMailer.checkout_confirmation(current_user).deliver
else
errors << transaction.result_code
end
end
I am trying to figure out the best solution when it comes to looping through each product in the users current_order because the second subscription in the users cart is the subscription that gets the discount applied too. I know I can write something like this:
current_order.products.includes(:client).each do |product|
if current_order.products.many? and product == current_order.products.last
# run discount logic
else
# continue with authnet for single subscription
end
end
But I am just not sure if that is a best practice or not. Thoughts?
So the only subscription that doesn't get discounted is the first one? Why not write it like this:
current_order.products.includes(:client).each do |product|
if product == current_order.products.first
# continue with authnet for single subscription
else
# run discount logic
end
end
Has anyone had success in resubscribing an email address after being unsubscribed via the Campaign Monitor API.
I ask as i want to keep a list of Active User's email addresses in my CM Active List. When they are suspended they get removed, when they join or pay their fees before getting deleted they are (re)subscribed.
Looking at the Rails API docs:
# File lib/campaign_monitor.rb, line 241
def remove_subscriber(email)
response = #cm_client.Subscriber_Unsubscribe("ListID" => #id, "Email" => email)
Result.new(response["Message"], response["Code"].to_i)
end
# File lib/campaign_monitor.rb, line 445
def unsubscribe(list_id)
response = #cm_client.Subscriber_Unsubscribe("ListID" => list_id, "Email" => #email_address)
Result.new(response["Message"], response["Code"].to_i)
end
On the CM website to move an email in the subscriber list to the active list you need to confirm you have permission to resubscribe them, can anyone say for sure that this applies to the API too?
I've just found the Subscriber.AddAndResubscribe method, undocumented in http://campaignmonitor.rubyforge.org/