Code Monkey home page Code Monkey logo

stripe-rails's Introduction

Stripe::Rails: A Rails Engine for use with stripe.com

Gem Version Build Status Code Climate Test Coverage Tidelift

This gem can help your rails application integrate with Stripe in the following ways

  • manage stripe configurations in a single place.
  • makes stripe.js available from the asset pipeline.
  • manage product, prices, plans and coupons from within your app.
  • painlessly receive and validate webhooks from stripe.

Professionally supported stripe-rails is coming soon


Installation

Setup your payment configuration

Stripe Elements

Webhooks

Unit testing

Thanks

Code of Conduct

Installation

Add this line to your application's Gemfile:

gem 'stripe-rails'

If you are going to be using stripe.js to securely collect credit card information on the client, then you will need to add the stripe javascript tags into your template. stripe-rails provides a helper to make this easy:

<%= stripe_javascript_tag %>

or, you can render it as a partial:

<%= render :partial => 'stripe/js' %>

In both cases, stripe-rails will choose a version of stripe.js appropriate for your development environment and automatically configure it to use your publishable API key. By default it uses stripe-debug.js for your development environment and stripe.js for everything else, but you can manually configure it per environment.

config.stripe.debug_js = true  # use stripe-debug.js
config.stripe.debug_js = false # use stripe.js

By default the helper renders the v3 version of stripe.js. You can provide an alternate version to the helper to generate the appropriate tag:

<%= stripe_javascript_tag(:v2) %>

Setup your API keys.

You will need to configure your application to authenticate with stripe.com using your api key. There are two methods to do this, you can either set the environment variable STRIPE_SECRET_KEY:

export STRIPE_SECRET_KEY=sk_test_xxyyzz

or if you are on heroku:

heroku config:add STRIPE_SECRET_KEY=sk_test_xxyyzz

You can also set this value from inside ruby configuration code:

config.stripe.secret_key = "sk_test_xxyyzz"

In either case, it is recommended that you not check in this value into source control.

You can verify that your api is set up and functioning properly by running the following command:

rake stripe:verify

If you are going to be using stripe.js, then you will also need to set the value of your publishable key. A nice way to do it is to set your test publishable for all environments:

# config/application.rb
# ...
config.stripe.publishable_key = 'pk_test_XXXYYYZZZ'

And then override it to use your live key in production only

# config/environments/production.rb
# ...
config.stripe.publishable_key = 'pk_live_XXXYYYZZZ'

This key will be publicly visible on the internet, so it is ok to put in your source. If you prefer to environment variables, you can also set STRIPE_PUBLISHABLE_KEY:

export STRIPE_PUBLISHABLE_KEY=pk_test_XXXYYYZZZ

If no API key is provided, stripe-rails will show a warning: "No stripe.com API key was configured ...". You can silence this warning by setting the ignore_missing_secret_key option to true:

# config/environments/production.rb
# ...
config.stripe.ignore_missing_secret_key = true

Manually set your API version (optional)

If you need to test a new API version in development, you can override the version number manually.

# config/environments/development.rb
# ...
config.stripe.api_version = '2015-10-16'

Setup your payment configuration

If you're using subscriptions, then you'll need to set up your application's payment plans and discounts. Stripe::Rails lets you automate the management of these definitions from within the application itself. To get started:

rails generate stripe:install

this will generate the configuration files containing your plan and coupon definitions:

create  config/stripe/products.rb
create  config/stripe/plans.rb
create  config/stripe/prices.rb
create  config/stripe/coupons.rb

Configuring your plans and coupons

Use the plan builder to define as many plans as you want in config/stripe/plans.rb

Stripe.plan :silver do |plan|
  plan.name = 'ACME Silver'
  plan.amount = 699 # $6.99
  plan.interval = 'month'
end

Stripe.plan :gold do |plan|
  plan.name = 'ACME Gold'
  plan.amount = 999 # $9.99
  plan.interval = 'month'
end

Stripe.plan :bronze do |plan|
  # Use an existing product id to prevent a new plan from
  # getting created
  plan.product_id = 'prod_XXXXXXXXXXXXXX'
  plan.amount = 999 # $9.99
  plan.interval = 'month'

  # Use graduated pricing tiers
  # ref: https://stripe.com/docs/api/plans/object#plan_object-tiers
  plan.tiers = [
    {
      unit_amount: 1500,
      up_to: 10
    },
    {
      unit_amount: 1000,
      up_to: 'inf'
    }
  ]
  plan.tiers_mode = 'graduated'

  # set the usage type to 'metered'
  plan.usage_type = 'metered'
end

This will define constants for these plans in the Stripe::Plans module so that you can refer to them by reference as opposed to an id string.

Stripe::Plans::SILVER # => 'silver: ACME Silver'
Stripe::Plans::GOLD # => 'gold: ACME Gold'

If you have to support an existing plan with a Stripe plan id that can not be used as a Ruby constant, provide the plan id as a symbol when defining the plan, but provide the name for the constant to define with constant_name:

Stripe.plan "Silver-Plan".to_sym do |plan|
  plan.constant_name = 'SILVER_PLAN' # <---
  plan.name = 'ACME Silver'
  plan.amount = 699
  plan.interval = 'month'
end

Stripe::Plans::SILVER_PLAN # => will be defined
# Will map to plan :id => "Silver-Plan" on Stripe

Note - If you're planning on running rake stripe:prepare to create your subscription plans, Stripe will restrict plan ids to match this regexp (/\A[a-zA-Z0-9_\-]+\z/) when created via API but still allows creation of plan ids that don't follow this restriction when manually created on stripe.com.

Coupons are created in much the same way:

Stripe.coupon :super_elite_free_vip do |coupon|
  coupon.duration = 'forever'
  coupon.percent_off = 100
  coupon.max_redemptions = 5
end

As are Products:

Stripe.product :primo do |product|
  product.name = 'PRIMO as a service'
  product.type = 'service'
  product.statement_descriptor = 'PRIMO'
end

And Prices:

Stripe.price :bronze do |price|
  # Use an existing product id to prevent a new product from
  # getting created
  price.product_id = Stripe::Products::PRIMO.id
  price.billing_scheme = 'tiered'
  price.recurring = {
    interval: 'month',
    usage_type: 'metered'
  }

  # Use graduated pricing tiers
  # ref: https://stripe.com/docs/api/prices/object#price_object-tiers
  price.tiers = [
    {
      unit_amount: 1500,
      up_to: 10
    },
    {
      unit_amount: 1000,
      up_to: 'inf'
    }
  ]
  price.tiers_mode = 'graduated'
end

To upload your plans, products, prices and coupons onto stripe.com, run:

rake stripe:prepare

This will create any plans, products, prices and coupons that do not currently exist, and treat as a NOOP any objects that already exist, so you can run this command safely as many times as you wish. Now you can use any of these objects in your application.

NOTE: You must destroy plans and prices manually from your stripe dashboard.

Stripe Elements

Stripe::Rails allows you to easily include Stripe Elements in your application.

Stripe Elements are rich, pre-built UI components that help you create your own pixel-perfect checkout flows across desktop and mobile.

Simply include the stripe_elements_tag anywhere below the stripe_javascript_tag and pass it the path to the controller action which will handle the Stripe token once the form is submitted:

<%= stripe_javascript_tag %>
<%= stripe_elements_tag submit_path: billing_path %>

Additionally, you can pass a block containing custom form elements to stripe_elements_tag:

Custom Elements

Stripe::Rails allows you to easily include your own custom form elements within the Stripe form by including those form elements in a block passed to stripe_elements_tag:

<%= stripe_javascript_tag %>
<%= stripe_elements_tag(submit_path: billing_path) do %>
  <%= label_tag 'email', 'Email' %>
  <%= text_field :user, :email %>
<% end %>

Configuration options

Stripe::Rails comes bundled with default CSS and Javascript for Stripe elements, making it easy to drop in to your app. You can also specify your own assets paths:

<%= stripe_elements_tag submit_path: billing_path,
                        css_path: 'your/asset/path',
                        js_path: 'your/asset/path' %>

If you decide to use your own CSS and Javascript for Stripe Elements, please refer to the Stripe elements docs.

To change the form text you can add the following keys to your locale files

# config/locales/en.yml
en:
  stripe_rails:
    elements:
      label_text: Your label text
      submit_button_text: Your button text

Webhooks

Stripe::Rails automatically sets up your application to receive webhooks from stripe.com whenever a payment event is generated. To enable this, you will need to configure your stripe webhooks to point back to your application. By default, the webhook controller is mounted at '/stripe/events' so you would want to enter in http://myproductionapp.com/stripe/events as your url for live mode, and http://mystagingapp.com/stripe/events for your test mode.

If you want to mount the stripe engine somewhere else, you can do so by setting the stripe.endpoint parameter. E.g.

config.stripe.endpoint = '/payment/stripe-integration'

Your new webhook URL would then be http://myproductionapp/payment/stripe-integration/events

Signed Webhooks

Validation of your webhook's signature uses your webhook endpoint signing secret. Before you can verify signatures, you need to retrieve your endpoint’s secret from your Stripe Dashboard. Select an endpoint for which you want to obtain the secret, then select the Click to reveal button.

# config/application.rb
# ...
config.stripe.signing_secrets = ['whsec_XXXYYYZZZ']

Each secret is unique to the endpoint to which it corresponds. If you use multiple endpoint, you must obtain a secret for each one. After this setup, Stripe starts to sign each webhook it sends to the endpoint. Because of this, we recommend setting this variable with environment variables:

export STRIPE_SIGNING_SECRET=whsec_XXXYYYZZZ
export STRIPE_CONNECT_SIGNING_SECRET=whsec_AAABBBCCC
config.stripe.signing_secrets = [ENV.fetch('STRIPE_SIGNING_SECRET'), ENV.fetch('STRIPE_CONNECT_SIGNING_SECRET')]

The first secret that successfully matches for each incoming webhook will be used to verify the incoming events.

Testing Signed Webhooks Locally

In order to test signed webhooks, you'll need to trigger test webhooks from your Stripe dashboard, and configure your local environment to receive remote network requests. To do so, we recommend using ngrok to configure a secure tunnel to localhost.

Once configured and running, ngrok will give you a unique URL which can be used to set up a webhook endpoint. Webhook endpoints are configured in your Dashboard's Webhook settings section. Make sure you are in Test mode and click Add endpoint, and provide your ngrok URL along with the stripe.endpoint suffix.

An example webhook URL would then be https://bf2a5d21.ngrok.io/stripe/events.

Once your endpoint is configured, you can reveal the Signing secret. This will need to be set as documented above:

# config/application.rb
# ...
config.stripe.signing_secrets = ['whsec_XXXYYYZZZ']

And you'll need to restart your rails server with:

rails restart

Now you're ready to click Send test webhook, and trigger whichever events you'd like to test from Stripe itself.

Disabling auto mount

Sometimes, you don't want the stripe engine to be auto-mounted so that you control exactly what priority it will take in your routing table. This is especially important if you have a catch-all route which should appear after all other routes. In order to disable auto-mounting of the Stripe engine:

# in application.rb
config.stripe.auto_mount = false

Then, you will have to manually mount the engine in your main application.

# in your application's routes.rb:
mount Stripe::Engine => "/stripe"

Responding to webhooks

Once you have your webhook URL configured you can respond to a stripe webhook anywhere in your application just by including the Stripe::Callbacks module into your class and declaring a callback with one of the callback methods. For example, to update a customer's payment status:

class User < ActiveRecord::Base
  include Stripe::Callbacks

  after_customer_updated! do |customer, event|
    user = User.find_by_stripe_customer_id(customer.id)
    if customer.delinquent
      user.is_account_current = false
      user.save!
    end
  end
end

or to send an email with one of your customer's monthly invoices

class InvoiceMailer < ActionMailer::Base
  include Stripe::Callbacks

  after_invoice_created! do |invoice, event|
    user = User.find_by_stripe_customer(invoice.customer)
    new_invoice(user, invoice).deliver
  end

  def new_invoice(user, invoice)
    @user = user
    @invoice = invoice
    mail :to => user.email, :subject => '[Acme.com] Your new invoice'
  end
end

Note: Stripe::Callbacks won't get included until the including class has been loaded. This is usually not an issue in the production environment as eager loading is enabled by default (config.eager_load = true). You may run into an issue in your development environment where eager loading is disabled by default.

If you don't wish to enable eager loading in development, you can configure the classes to be eager loaded like so

# in your application's config/environments/development.rb
config.stripe.eager_load = 'account', 'module/some_class', 'etc'

This will ensure that callbacks will get loaded in those configured classes if eager loading is disabled.

The naming convention for the callback events is after__{callback_name}! where callback_name is name of the stripe event with all . characters substituted with underscores. So, for example, the stripe event customer.discount.created can be hooked by after_customer_discount_created! and so on...

Each web hook is passed an instance of the stripe object to which the event corresponds (Stripe::Customer, Stripe::Invoice, Stripe::Charge, etc...) as well as the Stripe::Event which contains metadata about the event being raised.

By default, the event is re-fetched securely from stripe.com to prevent damage to your system by a malicious system spoofing real stripe events.

Critical and non-critical hooks

So far, the examples have all used critical hooks, but in fact, each callback method comes in two flavors: "critical", specified with a trailing ! character, and "non-critical", which has no "bang" character at all. What distinguishes one from the other is that if an exception is raised in a critical callback, it will cause the entire webhook to fail.

This will indicate to stripe.com that you did not receive the webhook at all, and that it should retry it again later until it receives a successful response. On the other hand, there are some tasks that are more tangential to the payment work flow and aren't such a big deal if they get dropped on the floor. For example, A non-critical hook can be used to do things like have a bot notify your company's chatroom that something a credit card was successfully charged:

class AcmeBot
  include Stripe::Callbacks

  after_charge_succeeded do |charge|
    announce "Attention all Dudes and Dudettes. Ya'll are so PAID!!!"
  end
end

Chances are that if you experience a momentary failure in connectivity to your chatroom, you don't want the whole payment notification to fail.

Filtering Callbacks

Certain stripe events represent updates to existing data. You may want to only fire the event when certain attributes of that data are updated. You can pass an :only option to your callback to filter to specify which attribute updates you're interested in. For example, to warn users whenever their credit card has changed:

class StripeMailer
  include Stripe::Callbacks

  after_customer_updated! :only => :active_card do |customer, evt|
    your_credit_card_on_file_was_updated_are_you_sure_this_was_you(customer).deliver
  end
end

Filters can be specified as an array as well:

module Accounting
  include Stripe::Callbacks

  after_invoice_updated! :only => [:amount, :subtotal] do
    # update our records
  end
end

Alternatively, you can just pass a proc to filter the event manually. It will receive an instance of Stripe::Event as its parameter:

module StagingOnly
  include Stripe::Callbacks

  after_charge_succeeded! :only => proc {|charge, evt| unless evt.livemode} do |charge|
    puts "FAKE DATA, PLEASE IGNORE!"
  end
end

Catchall Callback

The special 'stripe.event' callback will be invoked for every single event received from stripe.com. This can be useful for things like logging and analytics:

class StripeFirehose
  include Stripe::Callbacks

  after_stripe_event do |target, event|
    # do something useful
  end
end

See the complete listing of all stripe events, and the webhook tutorial for more great information on this subject.

Unit testing

If you want to test your callbacks, you can use the Stripe::Rails::Testing module to send mocked Stripe events.

require 'stripe/rails/testing'
test "my callback handles new subscription" do
  Stripe::Rails::Testing.send_event "customer.subscription.created"
  # Assertions
end

You can also overwrite some event properties: (More info)

require 'stripe/rails/testing'
test "my callback handles new subscription" do
  Stripe::Rails::Testing.send_event "customer.subscription.created", {
    :email => "[email protected]",
    :account_balance => 40
  }
  # Assertions
end

The default fixtures come from the stripe-ruby-mock gem.

Thanks

Frontside

Stripe::Rails was originally developed with love and fondness by your friends at Frontside. They are available for your custom software development needs, including integration with stripe.com.

Evercondo

Stripe::Rails has also been supported by the fine folks at Evercondo, the next generation condo management software.

Code of Conduct

Please note that this project is released with a Contributor Code of Conduct. By participating in this project you agree to abide by its terms, which can be found in the CODE_OF_CONDUCT.md file in this repository.

stripe-rails's People

Contributors

alexagranov avatar cowboyd avatar danielwellman avatar gaffneyc avatar hopsoft avatar jacobcsmith avatar jamesprior avatar japestrale avatar jeanmartin avatar kiddrew avatar klapperkopp avatar lewispb avatar lloydpick avatar maksimabramchuk avatar martron avatar mattgoldman avatar michaelherold avatar millariel avatar ndbroadbent avatar nitrino avatar noahezekwugo avatar pyo25 avatar rubygeek avatar server-monitor avatar smtlaissezfaire avatar stevenelberger avatar tansengming avatar vfonic avatar wkirby avatar zilvinaskucinskas avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

stripe-rails's Issues

Stripe API update on 2018-02-05 breaks plan builder

Stripe's API update on 2018-02-05 makes a major change to the plan object causing the plan builder to break. More specifically, they moved the name and statement_descriptor out of the plan object into a product object. So an error is returned when running the plan builder (i.e. rake stripe:prepare) with the most recent API.

From Stripe's API changelog:

Each plan object is now linked to a product object with type=service. The plan object fields statement_descriptor and name attributes have been moved to product objects. Creating a plan now requires passing a product attribute to POST /v1/plans. This may be either an existing product ID or a dictionary of product fields, so that you may continue to create plans without separately creating products.

Stripe Connect webhooks

I think Stripe Connect hooks do not work properly. Here are my observations.

Connect Accounts with type "custom" (created via API) produce events under their own namespace. And general account has restricted access to those ones. Seems slightly reasonable, as far as "those are other people accounts". But they actually are accounts-in-account. Especially when your server receive event account.updated from those accounts with all details but a moment later https://github.com/tansengming/stripe-rails/blob/master/app/models/stripe/event_dispatch.rb#L20 gonna fail with Stripe::InvalidRequestError (No such event: evt_xxx): as per my words above (because of Stripe access restrictions).

I had a chat with Stripe support guys. Seems like it's proper behaviour (from their perspective) but they promise to submit my observations as "feature request" to dev team.

As a workaround general account owner can visit every new account and set their webhooks url to server or use API (i.e. #141) to set it from code. But imo this sounds a bit crazy. Especially when we talk about "custom" accounts, which means by design that only owner of general account has dashboard/API access to those ones.

I see #92 and yours decision to re-fetch event data by id. So this does not work with Stripe Connect hooks (currently). It just spams logs with Stripe::InvalidRequestError. It plays bad role here, just a note.

Please advise [from design perspective] which solution is better here.
For now I can just skip all this as far as that is not critical for me at the moment. As a temporary solution I can try some monkey-patch until Stripe guys do some access changes. But that sounds not so great to me.

Webhook callbacks not working

According to the documentation, defining a webhook callback like below ought to work.

class User < ActiveRecord::Base
  include Stripe::Callbacks

  after_customer_updated! do |customer, event|
    # do work
  end
end

However none of the callbacks (explicitly declared or catch-all) are working for me. I've copied and pasted the code verbatim into a new project and still am unable to trigger the webhook callback. I'm using the following curl command against my local server:

#!/bin/bash

curl \
  -H "Accept: application/json" -H "Content-type: application/json" \
  -X POST \
  -d '{"created": 1326853478, "livemode": false, "id": "evt_00000000000000", "type": "invoice.created", "object": "event", "data": {"object": {"date": 1377794951, "id": "in_00000000000000", "period_start": 1377794951, "period_end": 1377794951, "lines": {"data": [{"id": "su_2TVcgkbh3kLJ9M", "object": "line_item", "type": "subscription", "livemode": true, "amount": 16999, "currency": "cad", "proration": false, "period": {"start": 1380473351, "end": 1383065351 }, "quantity": 1, "plan": {"interval": "month", "name": "PetroFeed Professional", "amount": 16999, "currency": "cad", "id": "pro", "object": "plan", "livemode": false, "interval_count": 1, "trial_period_days": null }, "description": null } ], "count": 1, "object": "list", "url": "/v1/invoices/in_2TVcGZFvuE8opw/lines"}, "subtotal": 16999, "total": 16999, "customer": "cus_00000000000000", "object": "invoice", "attempted": false, "closed": true, "paid": true, "livemode": false, "attempt_count": 0, "amount_due": 16999, "currency": "cad", "starting_balance": 0, "ending_balance": 0, "next_payment_attempt": null, "charge": "ch_00000000000000", "discount": null } } }' \
  http://localhost:3000/stripe/events

Using debugger, I've verified that events_controller#create is being properly called, but it appears that the callbacks themselves aren't being created during model initialization in Rails.

Where do I catch webook?

class User < ActiveRecord::Base
  include Stripe::Callbacks

  after_customer_updated! do |customer, event|
    user = User.find_by_stripe_customer_id(customer.id)
    if customer.delinquent
      user.is_account_current = false
      user.save!
    end
  end
end

This example works, however if I move method to different class, it doesn't. I have Subscription model that stores related data. so

class Subscription < ActiveRecord::Base
  include Stripe::Callbacks
  belongs_to :user

  after_customer_updated! do |customer, event|
    binding.pry
    user = User.find_by_stripe_customer_id(customer.id)
    if customer.delinquent
      user.is_account_current = false
      user.save!
    end
  end
end

doesn't catch webhook. am I doing it wrong?

API version support

I'm upgrading to the newest version of the Stripe API, and I want to be able to test that in dev before pushing to production. Stripe doesn't let you upgrade your API version one environment at a time, and the workaround is to pass the version number in the request header.

Am I blind, or is that not currently a feature?

undefined method `respond_to' for Stripe::EventsController:Class Did you mean? respond_to? (NoMethodError)

I'm not sure if this is specifically a Stripe issue, but error message points that way. The problem is that I have everything running fine on the development machine. And as far as I can tell, the set-up (ruby version, rails version, gem versions) are the same on the production machine as well. But I keep getting this error in the log which crashes the website. The only difference is that my development machine uses Puma (as per Rails5 defaults) and production uses Unicorn (since that's what Digital Ocean droplet comes with).

Ability to store current plans while changing new customers to different plan?

Great gem! This is not a bug or issue, just a question...

I have been thinking about how to change the pricing of a new plan while keeping the name and not removing an existing customer from the old plan.

For example
Customer A is signed up with the silver plan at 9.99 a month
We want to change the silver plan to 12.99 a month but keep Customer A on the 9.99 price
We want new customers to still see "Silver Plan" but at the 12.99, and old customers to see "Silver Plan" but at 9.99

Any ideas on how to tweak the system for something like this?

Dynamic api key

how do i configure API key so it is taken from database depending on request?

Getting duplicate callbacks when running RSpec tests with spring

Hello,

I've been using spring to run my RSpec tests, which preloads the Rails environment. This makes things much faster. But I've noticed that sometimes I will get a lot of test failures, because the stripe-rails callback are being registered multiple times. e.g. after_customer_updated!.

I think this is something to do with spring or Rails autoloading. Whenever I change a file with some Stripe callback, a duplicate callback gets registered. This callback is fired multiple times, so it breaks my tests. I can fix the issue by running spring stop.

I was wondering about the best way to fix this. It's not really a problem in Spring, because that's just reloading the file and registering a new callback. But I wonder if the same problem happens in Rails development, when a model is reloaded? Do you already have some code to handle that?

I was thinking that the hooks could be tagged with a filename or model class. Then if the file is reloaded, stripe-rails would remove any hooks that were previously defined. I might look into this and see if I can get it to work.

Using Multiple Signing Secrets

How do you set up multiple signing secrets (for multiple webhook endpoints)? The documentation says "If you use multiple endpoints, you must obtain a secret for each one." but it doesn't say what to do with the extra secrets.

I tried using an array like StripeEvent does (i.e. config.stripe.signing_secret = ["whsec_XXX1", "whsec_XXX2"] but just got an array to string conversion error.

Thanks!

Does not work with ActionController::API

We are creating an API-Centric Web Application and we have ApiController in it, which is nested from ActionController::API. After adding stripe-rails, application began crashing on startup with error:

Uncaught exception: undefined method `helper' for ActionController::API:Class
Exiting
/Users/user/.rvm/gems/ruby-2.2.3@peakhero/gems/stripe-rails-0.3.1/lib/stripe/engine.rb:58:in `block (2 levels) in <class:Engine>'
/Users/user/.rvm/gems/ruby-2.2.3@peakhero/gems/activesupport-5.0.0.beta4/lib/active_support/lazy_load_hooks.rb:38:in `instance_eval'

I've found that initializer 'stripe.javascript_helper' in engine.rb tries to add Stripe::JavascriptHelper after ActiveSupport is loaded, but there is no helpers in API version.

You are probably need to add ability to skip adding of javascript helpers, which will solve the issue.

Turbolinks support

The call to Stripe.setPublishableKey doesn't work with Turbolinks, as it will be executed before the stripe.js file has finished loading.

I tried using the page:load turbolinks event, but strangely that didn't work for me.

Help installing gem

After following the installation steps and attempting to bundle the gem:

rails generate stripe:install
/Users/rs/.rvm/gems/ruby-2.3.0/gems/stripe-rails-0.4.1/lib/stripe/engine.rb:20:in block in <class:Engine>': undefined method secret_key' for #Hash:0x007fbd7f3e1400 (NoMethodError)

Any help? Thanks in advance!

config.stripe.api_key

Hi,

New to stripe and this gem. Initially confused by which API key was which. Don't really understand why you are calling the secret key the api_key? Thought I was looking for an another which was somewhere other than https://manage.stripe.com/account/apikeys :)

Would recommend that config.stripe.api_key becomes config.stripe.secret_key

Showing customer’s native currency based on country

Hello,

First of all, thanks for the awesome gem! stripe-rails has made it very easy to integrate with Stripe. I keep hearing that presenting prices in a customer’s native currency can improve sales, so I was wondering if this feature might be a good addition to the stripe-rails gem. I need to:

  • Geolocate the visitor's IP address to find their country
  • Find the default currency for that country
  • Find the USD exchange rate, and present this currency on my pricing page

Have you done this before? Would it be better to do this on the server or the client? And do you know if Stripe has a way to handle this automatically?

Not ThreadSafe

Rails 3.2

Given environment:

config.threadsafe!

Running rake environment resque:work

will result in

(See full trace by running task with --trace)
[DEPRECATION] to align with stripe nomenclature, stripe.api_key has been renamed to config.stripe.secret_key
Connecting to database specified by database.yml
rake aborted!
NameError: uninitialized constant Stripe::EventDispatch
/var/deploy/likeminds/web_head/shared/bundle/ruby/2.1.0/gems/stripe-rails-0.3.1/app/controllers/stripe/events_controller.rb:3:in `<class:EventsController>'
/var/deploy/likeminds/web_head/shared/bundle/ruby/2.1.0/gems/stripe-rails-0.3.1/app/controllers/stripe/events_controller.rb:2:in `<module:Stripe>'
/var/deploy/likeminds/web_head/shared/bundle/ruby/2.1.0/gems/stripe-rails-0.3.1/app/controllers/stripe/events_controller.rb:1:in `<top (required)>'
/var/deploy/likeminds/web_head/shared/bundle/ruby/2.1.0/gems/activesupport-3.2.19/lib/active_support/dependencies.rb:251:in `require'
/var/deploy/likeminds/web_head/shared/bundle/ruby/2.1.0/gems/activesupport-3.2.19/lib/active_support/dependencies.rb:251:in `block in require'
/var/deploy/likeminds/web_head/shared/bundle/ruby/2.1.0/gems/activesupport-3.2.19/lib/active_support/dependencies.rb:236:in `load_dependency'
/var/deploy/likeminds/web_head/shared/bundle/ruby/2.1.0/gems/activesupport-3.2.19/lib/active_support/dependencies.rb:251:in `require'
/var/deploy/likeminds/web_head/shared/bundle/ruby/2.1.0/gems/activesupport-3.2.19/lib/active_support/dependencies.rb:359:in `require_or_load'
/var/deploy/likeminds/web_head/shared/bundle/ruby/2.1.0/gems/activesupport-3.2.19/lib/active_support/dependencies.rb:313:in `depend_on'
/var/deploy/likeminds/web_head/shared/bundle/ruby/2.1.0/gems/activesupport-3.2.19/lib/active_support/dependencies.rb:225:in `require_dependency'
/var/deploy/likeminds/web_head/shared/bundle/ruby/2.1.0/gems/railties-3.2.19/lib/rails/engine.rb:444:in `block (2 levels) in eager_load!'
/var/deploy/likeminds/web_head/shared/bundle/ruby/2.1.0/gems/railties-3.2.19/lib/rails/engine.rb:443:in `each'
/var/deploy/likeminds/web_head/shared/bundle/ruby/2.1.0/gems/railties-3.2.19/lib/rails/engine.rb:443:in `block in eager_load!'
/var/deploy/likeminds/web_head/shared/bundle/ruby/2.1.0/gems/railties-3.2.19/lib/rails/engine.rb:441:in `each'
/var/deploy/likeminds/web_head/shared/bundle/ruby/2.1.0/gems/railties-3.2.19/lib/rails/engine.rb:441:in `eager_load!'
/var/deploy/likeminds/web_head/shared/bundle/ruby/2.1.0/gems/railties-3.2.19/lib/rails/application/railties.rb:8:in `each'
/var/deploy/likeminds/web_head/shared/bundle/ruby/2.1.0/gems/railties-3.2.19/lib/rails/application/railties.rb:8:in `all'
/var/deploy/likeminds/web_head/shared/bundle/ruby/2.1.0/gems/railties-3.2.19/lib/rails/engine.rb:439:in `eager_load!'
/var/deploy/likeminds/web_head/shared/bundle/ruby/2.1.0/gems/resque-1.22.0/lib/resque/tasks.rb:56:in `block (2 levels) in <top (required)>'
Tasks: TOP => resque:work => resque:preload

Turning off config.threadsafe! resolves the issue.

Do not create a new product for each plan

After #101, the plan builder now always specifies a product.name in the POST request, thus creating a new product for each plan, even if the product name is the same.

The API docs state that you can either pass a hash to in product, or an existing product ID as a string.

It would be great to either be possible to pass a product_id attribute to the builder. A builder for products would also be great, to not have to create them manually.

Also, new attributes has been added to plans, most notably nickname, a customer-hidden name for the plan (displayed in the GUI for example).

undefined method `constant_name=' for #<Stripe::Plans::Configuration...>

Trying this gem now, but I'm getting the error below. Initially I thought this could be a name collision as I have a Plan model in my application, however I have renamed both the model file and the DB table to LegacyPlan and I get the following:

config/stripe/plans.rb:

Stripe.plan "Silver-Plan".to_sym do |plan|
  plan.constant_name = 'SILVER_PLAN'
end

When running rails console:

/Users/me/projects/myproj/config/stripe/plans.rb:35:in `block in <top (required)>': undefined method `constant_name=' for #<Stripe::Plans::Configuration:0x00007f8dacd29b00> (NoMethodError)
        from /Users/me/.rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/stripe-rails-1.6.1/lib/stripe/configuration_builder.rb:19:in `block in configuration_for'
        from /Users/me/projects/myproj/config/stripe/plans.rb:34:in `<top (required)>'
        from /Users/me/.rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/activesupport-5.2.3/lib/active_support/dependencies.rb:285:in `load'
        from /Users/me/.rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/activesupport-5.2.3/lib/active_support/dependencies.rb:285:in `block in load'
        from /Users/me/.rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/activesupport-5.2.3/lib/active_support/dependencies.rb:257:in `load_dependency'
        from /Users/me/.rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/activesupport-5.2.3/lib/active_support/dependencies.rb:285:in `load'
        from /Users/me/.rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/stripe-rails-1.6.1/lib/stripe/engine.rb:79:in `block (2 levels) in <class:Engine>'
        from /Users/me/.rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/stripe-rails-1.6.1/lib/stripe/engine.rb:77:in `each'
        from /Users/me/.rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/stripe-rails-1.6.1/lib/stripe/engine.rb:77:in `block in <class:Engine>'
        from /Users/me/.rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/railties-5.2.3/lib/rails/initializable.rb:32:in `instance_exec'
        from /Users/me/.rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/railties-5.2.3/lib/rails/initializable.rb:32:in `run'
        from /Users/me/.rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/railties-5.2.3/lib/rails/initializable.rb:61:in `block in run_initializers'
        from /Users/me/.rbenv/versions/2.6.2/lib/ruby/2.6.0/tsort.rb:228:in `block in tsort_each'
        from /Users/me/.rbenv/versions/2.6.2/lib/ruby/2.6.0/tsort.rb:350:in `block (2 levels) in each_strongly_connected_component'
        from /Users/me/.rbenv/versions/2.6.2/lib/ruby/2.6.0/tsort.rb:431:in `each_strongly_connected_component_from'
        from /Users/me/.rbenv/versions/2.6.2/lib/ruby/2.6.0/tsort.rb:349:in `block in each_strongly_connected_component'
        from /Users/me/.rbenv/versions/2.6.2/lib/ruby/2.6.0/tsort.rb:347:in `each'
        from /Users/me/.rbenv/versions/2.6.2/lib/ruby/2.6.0/tsort.rb:347:in `call'
        from /Users/me/.rbenv/versions/2.6.2/lib/ruby/2.6.0/tsort.rb:347:in `each_strongly_connected_component'
        from /Users/me/.rbenv/versions/2.6.2/lib/ruby/2.6.0/tsort.rb:226:in `tsort_each'
        from /Users/me/.rbenv/versions/2.6.2/lib/ruby/2.6.0/tsort.rb:205:in `tsort_each'
        from /Users/me/.rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/railties-5.2.3/lib/rails/initializable.rb:60:in `run_initializers'
        from /Users/me/.rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/railties-5.2.3/lib/rails/application.rb:361:in `initialize!'
        from /Users/me/projects/myproj/config/environment.rb:5:in `<top (required)>'
        from /Users/me/.rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/activesupport-5.2.3/lib/active_support/dependencies.rb:291:in `require'
        from /Users/me/.rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/activesupport-5.2.3/lib/active_support/dependencies.rb:291:in `block in require'
        from /Users/me/.rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/activesupport-5.2.3/lib/active_support/dependencies.rb:257:in `load_dependency'
        from /Users/me/.rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/activesupport-5.2.3/lib/active_support/dependencies.rb:291:in `require'
        from /Users/me/.rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/spring-2.0.2/lib/spring/application.rb:102:in `preload'
        from /Users/me/.rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/spring-2.0.2/lib/spring/application.rb:153:in `serve'
        from /Users/me/.rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/spring-2.0.2/lib/spring/application.rb:141:in `block in run'
        from /Users/me/.rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/spring-2.0.2/lib/spring/application.rb:135:in `loop'
        from /Users/me/.rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/spring-2.0.2/lib/spring/application.rb:135:in `run'
        from /Users/me/.rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/spring-2.0.2/lib/spring/application/boot.rb:19:in `<top (required)>'
        from /Users/me/.rbenv/versions/2.6.2/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require'
        from /Users/me/.rbenv/versions/2.6.2/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require'
        from -e:1:in `<main>'

Looking for new ownership!

We're looking to hand this repo off to someone who wants to continue maintaining this gem.

If this sounds like something you want to do let us know!

Don't assume sprockets are loaded

In the initialise step here it is assuming that the sprockets are loaded. This would raise an error and will not allow to boot the app if sprockets are not loaded/used.

Adding a guard clause to make sure it's not invoked if sprockets are not used would be helpful.

# lib/stripe/engine.rb

...

initializer 'stripe.assets.precompile' do |app|
  if !::Rails.env.test? && app.config.respond_to?(:assets)
    app.config.assets.precompile += %w( stripe_elements.js stripe_elements.css )
  end
end

...

Gem ownership

@tansengming It looks like you're priming the pump for a new release, awesome! What is your username / email on Rubygems so that I can add you to the authors list?

Missing support for PaymentIntent, SetupIntent, and PaymentMethod events

Missing these new stripe events:

  • payment_method.attached
  • payment_method.card_automatically_updated
  • payment_method.detached
  • payment_method.updated
  • setup_intent.created
  • setup_intent.setup_failed
  • setup_intent.succeeded
  • payment_intent.amount_capturable_updated
  • payment_intent.created
  • payment_intent.payment_failed
  • payment_intent.succeeded

When I try to register a callback for them I get a NoMethodError.

See https://stripe.com/docs/api/events/types

Broken with Rails 5.2-rc1

FYI, the gem is currently broken when using it with Rails 5.2-rc1:

/Users/renchap/.rbenv/versions/2.4.3/lib/ruby/gems/2.4.0/gems/activerecord-5.2.0.rc1/lib/active_record/coders/yaml_column.rb:24:in `load': undefined method `new' for Representable::JSON:Module (NoMethodError)
	from /Users/renchap/.rbenv/versions/2.4.3/lib/ruby/gems/2.4.0/gems/activerecord-5.2.0.rc1/lib/active_record/coders/yaml_column.rb:44:in `check_arity_of_constructor'
	from /Users/renchap/.rbenv/versions/2.4.3/lib/ruby/gems/2.4.0/gems/activerecord-5.2.0.rc1/lib/active_record/coders/yaml_column.rb:13:in `initialize'
	from /Users/renchap/.rbenv/versions/2.4.3/lib/ruby/gems/2.4.0/gems/activerecord-5.2.0.rc1/lib/active_record/store.rb:187:in `new'
	from /Users/renchap/.rbenv/versions/2.4.3/lib/ruby/gems/2.4.0/gems/activerecord-5.2.0.rc1/lib/active_record/store.rb:187:in `initialize'
	from /Users/renchap/.rbenv/versions/2.4.3/lib/ruby/gems/2.4.0/gems/activerecord-5.2.0.rc1/lib/active_record/store.rb:83:in `new'
	from /Users/renchap/.rbenv/versions/2.4.3/lib/ruby/gems/2.4.0/gems/activerecord-5.2.0.rc1/lib/active_record/store.rb:83:in `store'
	from /Users/renchap/.rbenv/versions/2.4.3/lib/ruby/gems/2.4.0/gems/activestorage-5.2.0.rc1/app/models/active_storage/blob.rb:22:in `<class:Blob>'
	from /Users/renchap/.rbenv/versions/2.4.3/lib/ruby/gems/2.4.0/gems/activestorage-5.2.0.rc1/app/models/active_storage/blob.rb:16:in `<main>'

This looks like to be an issue with representable, investigation here: trailblazer/representable#224

I mainly opened this issue for people to know this is expected.

new plans are not updating

I have removed old plans from stripe and my plans.rb and coupons.rb file. Then, I created a new set of plans and ran

rake stripe:prepare

It's still loading and sending my old plans and coupons. I believe it should just be using my new plans and coupons right?

LoadError: cannot load such file -- stripe/rails/testing

I'm getting this error when I run

bundle exec rspec

I've added the following to my spec/rails_helper.rb file
require 'stripe/rails/testing'

I'm a little rusty with rails but shouldn't this be working? Or do I need to add this to each spec file?

Thanks!

Using multiple tiers in a plan

Could you give an example on how to write a plan that has several tiers?
Example:

From 0-5 units 9.99
From 5-50 6 per user
from 50 onwards: 4 per user

Thanks

Webhooks not being called outside of models

Thanks for the awesome work you did on the gem.

I seem to have issues getting webhooks to be fired outside of my models. Despite being able to see the Stripe events being processed in my Rails console. I've tried to first in app/lib/stripe_webhook.rb:

class StripeWebhook

  include Stripe::Callbacks

  after_stripe_event do |target, event|
    binding.pry
  end

end

Then a controller app/controllers/stripe_webhooks_controller.rb, but sill without success:

class StripeWebhooksController < ApplicationController

  include Stripe::Callbacks

  after_stripe_event do |target, event|
    binding.pry
  end

end

However, adding the same code in a model (user.rb) works as expected.

Any ideas of what might be wrong?

Using Rails (5.1.5) and Mongoid (6.0.3).

No autorefresh of webhooks

Hi guys,

First of. This gem is amazing! (such an elegant way to receive the webhooks).
Is it correct though, that the webhooks do not get auto-reloaded in development?
I need to restart my server every time.

kind regards,
jeljer

Check signature of webhook

It's straightforward, when setting up the controller manually, but I can not find how to check the signature of the webhook, when using the stripe-rails webhook implementation.

Clearing callbacks after unload doesn't play nice with eager_load settings in DEV

Changes introduced by #137 will cause callbacks registered via eager loading to be cleared upon first event sent from Stripe.

To recreate:

  • Include callbacks engine in a class that is provided to eager_load config:
    config.stripe.eager_load = ["PaymentProvider::StripeClient"]
  • Trigger a test event from Stripe.
  • No event will be handled.

PR with a fix coming up.

Long delay when using Turbolinks

Everything is working correctly, there is a long delay, 10+ seconds on localhost when submitting custom form.

If I refresh the page with the form on it works instantly upon submit. If I navigate from another page TO the form page, it has the 10 second delay after submitting.

I'm putting the <script type="text/javascript" src="https://js.stripe.com/v2/"></script> in the head, and I have tried all combinations of data-turbolinks-eval=always, data-turbolinks-eval=false just to see if anything makes a difference...

What could be causing this kind of a delay?

Make stripe-ruby-mock an optional dependency

The currently released version of stripe-ruby-mock has a very specific constraint on the stripe gem (stripe <= 1.58.0, >= 1.31.0) that is causing bundler to drop from 3.x to 1.58.0. While the new testing functionality looks interesting, we shouldn't be forced to use an old version of Stripe just to test our webhooks. By having it be a required dependency we are also forced to load the mocking library into our production application where it won't be used and could have other side effects that we're not aware of.

The easiest way forward would be to remove stripe-ruby-mock from the gemspec. Users of the gem will need to require "stripe/testing" which will then require stripe-ruby-mock and fail if the gem is not installed. We then have the ability to add stripe-ruby-mock to our bundler test group to get that functionality when needed.

On a side note, I would expect it to be require "stripe/rails/testing" since the stripe gem should own the require "stripe/*" namespace and stripe-rails owns the require "stripe/rails/*" namespace.

stripe-rails and rails 5/ruby 2.3 compatability

is stripe-rails work under rails5?

I'm getting the following error while testing webhooks

NoMethodError (undefined method `object' for #ActionController::Parameters:0x007fc0200563a0
Did you mean? object_id):

actionpack (5.0.0.rc1) lib/action_controller/metal/strong_parameters.rb:621:in method_missing' stripe-rails (0.3.1) app/models/stripe/event_dispatch.rb:6:inblock in dispatch_stripe_event'
stripe-rails (0.3.1) app/models/stripe/event_dispatch.rb:14:in retrieve_stripe_event' stripe-rails (0.3.1) app/models/stripe/event_dispatch.rb:5:indispatch_stripe_event'
stripe-rails (0.3.1) app/controllers/stripe/events_controller.rb:7:in `create'

Mentions on Stripe Rails Newsletter #1

Hi @georgecheng @renchap @jeanmartin @wkirby

I'm about to publish a newsletter for the gem and just wanted y'all to know that you get mentions on the newsletter for your contributions. The newsletter will be sent to a tiny group of subscribers and also be posted on Medium. In the newsletter I make a note of your contributions with a link to your Github profile. Please let me know at [email protected] if you do not want to be mentioned for any reason.

A draft of this is available at https://medium.com/@sengming/stripe-rails-newsletter-1-f894d0a27bd1

I'll keep this open for a week before publishing after which I’ll delete this issue.

Thanks for all your help!

Undefined local variable or method `stripe_js_version'

Just upgraded to Rails 4.2.9 and bumped into this issue when rendering stripe/js partial in view:

Failure/Error: = render :partial => 'stripe/js'

ActionView::Template::Error:undefined local variable or method `stripe_js_version' for 
#<#<Class:0x007fd91d97e1b0>:0x007fd91d3b41f8> Did you mean?  stripe_url

Anyone had this issue before?

Stripe Elements

@tansengming I saw in your newsletter post on Medium that you're looking to add Stripe Elements support soon. I'm in the middle of adding Elements in a product that I'm working on, so I'd be happy to look into it if you haven't already started.

Unit testing callback

Hi,

I'm wondering how I could properly unit test callbacks.

At the moment I define callbacks in my model like this:

  include Stripe::Callbacks
  after_customer_subscription_created! do |stripe_subscription, event|
     # some logic
  end

I'm thinking of moving the logic in a class method (like below) but it doesn't sound good.

  include Stripe::Callbacks
  after_customer_subscription_created! do |stripe_subscription, event|
     self.after_customer_subscription_created stripe_subscription, event
  end

  def self.after_customer_subscription_created
    # some logic
  end

Then my test would be something like:

  test "Stripe customer.subscription.created hook" do
    event = StripeMock.mock_webhook_event('customer.subscription.created')
    Subscription.after_customer_subscription_created event.data.object, event
    # assert some stuffs
  end

What would be a better approach? Could I use run_callbacks somehow to simulate the hook?

Thanks for your suggestions.

Plans with dash in ID

I have a bunch of existing plans that I'm integrating with, and there plan IDs contain dashes ("-").

Any idea how I can integrate dashes into the Stripe.plan stuff?

I've tried:

Stripe.plan :'EU-2015' do |plan|
...
end

Stripe.plan 'EU-2015' do |plan|
...
end

...but no luck :(

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.