Giter VIP home page Giter VIP logo

koudoku's Introduction

Koudoku

Gem Version Code Climate Build Status

Robust subscription support for Ruby on Rails apps using Stripe, including out-of-the-box pricing pages, payment pages, and subscription management for your customers. Also makes it easy to manage logic related to new subscriptions, upgrades, downgrades, cancellations, payment failures, and streamlines hooking up notifications, metrics logging, etc.

To see an example of Koudoku in action, please visit Koudoku.org.

Churn Buster
Koudoku is authored and maintained by Andrew Culver and Christoph Engelhardt. If you find it useful, consider checking out Churn Buster. It's designed to help you handle the pain points you'll run into when doing payments at scale.

Installation

Include the following in your Gemfile:

    gem 'koudoku'

After running bundle install, you can run a Rails generator to do the rest. Before installing, the model you'd like to have own subscriptions must already exist. (In many cases this will be user. It may also be something like company, etc.)

    rails g koudoku:install user
    rake db:migrate

Add the following to app/views/layouts/application.html.erb before your <head> tag closes:

    <%= yield :koudoku %>

(This allows us to inject a Stripe <script> tag in the correct place. If you don't, the payment form will not work.)

After installing, you'll need to add some subscription plans. (You can see an explanation of each of the attributes in the table below.)

Note: You need to create the plans in your Stripe Dashboard separately.

    Plan.create({
      name: 'Personal',
      price: 10.00,
      interval: 'month',
      stripe_id: '1',
      features: ['1 Project', '1 Page', '1 User', '1 Organization'].join("\n\n"),
      display_order: 1
    })

    Plan.create({
      name: 'Team',
      highlight: true, # This highlights the plan on the pricing page.
      price: 30.00,
      interval: 'month',
      stripe_id: '2',
      features: ['3 Projects', '3 Pages', '3 Users', '3 Organizations'].join("\n\n"),
      display_order: 2
    })
    
    Plan.create({
      name: 'Enterprise',
      price: 100.00, 
      interval: 'month',
      stripe_id: '3', 
      features: ['10 Projects', '10 Pages', '10 Users', '10 Organizations'].join("\n\n"), 
      display_order: 3
    })

To help you understand the attributes:

Attribute Type Function
name string Name for the plan to be presented to customers.
price float Price per billing cycle.
interval string Optional. What is the billing cycle? Valid options are month, year, week, 3-month, 6-month. Defaults to month.
stripe_id string The Plan ID in Stripe.
features string A list of features. Supports Markdown syntax.
display_order integer Order in which to display plans.
highlight boolean Optional. Whether to highlight the plan on the pricing page.

The only view installed locally into your app by default is the koudoku/subscriptions/_social_proof.html.erb partial which is displayed alongside the pricing table. It's designed as a placeholder where you can provide quotes about your product from customers that could positively influence your visitors.

Configuring Stripe API Keys

You can supply your publishable and secret API keys in config/initializers/koudoku.rb. However, by default it will use the STRIPE_PUBLISHABLE_KEY and STRIPE_SECRET_KEY shell environment variables. This encourages people to keep these API keys out of version control. You may want to rename these environment variables to be more application specific.

In a bash shell, you can set them in ~/.bash_profile like so:

    export STRIPE_PUBLISHABLE_KEY=pk_0CJwDH9sdh98f79FDHDOjdiOxQob0
    export STRIPE_SECRET_KEY=sk_0CJwFDIUshdfh97JDJOjZ5OIDjOCH

(Reload your terminal for these settings to take effect.)

On Heroku you accomplish this same effect with Config Vars:

    heroku config:add STRIPE_PUBLISHABLE_KEY=pk_0CJwDH9sdh98f79FDHDOjdiOxQob0
    heroku config:add STRIPE_SECRET_KEY=sk_0CJwFDIUshdfh97JDJOjZ5OIDjOCH

User-Facing Subscription Management

By default a pricing_path route is defined which you can link to in order to show visitors a pricing table. If a user is signed in, this pricing table will take into account their current plan. For example, you can link to this page like so:

    <%= link_to 'Pricing', main_app.pricing_path %>

(Note: Koudoku uses the application layout, so it's important that application paths referenced in that layout are prefixed with "main_app." like you see above or Rails will try to look the paths up in the Koudoku engine instead of your application.)

Existing users can view available plans, select a plan, enter credit card details, review their subscription, change plans, and cancel at the following route:

    koudoku.owner_subscriptions_path(@user)

In these paths, owner refers to User by default, or whatever model has been configured to be the owner of the Subscription model.

A number of views are provided by default. To customize the views, use the following generator:

    rails g koudoku:views

Pricing Table

Koudoku ships with a stock pricing table. By default it depends on Twitter Bootstrap, but also has some additional styles required. In order to import these styles, add the following to your app/assets/stylesheets/application.css:

    *= require 'koudoku/pricing-table'

Or, if you've replaced your application.css with an application.scss (like I always do):

    @import "koudoku/pricing-table"

Using Coupons

While more robust coupon support is expected in the future, the simple way to use a coupon is to first create it:

    coupon = Coupon.create(code: '30-days-free', free_trial_length: 30)

Then assign it to a new subscription before saving:

    subscription = Subscription.new(...)
    subscription.coupon = coupon
    subscription.save

It should be noted that these coupons are different from the coupons provided natively by Stripe.

Implementing Logging, Notifications, etc.

The included module defines the following empty "template methods" which you're able to provide an implementation for in Subscription:

  • prepare_for_plan_change
  • prepare_for_new_subscription
  • prepare_for_upgrade
  • prepare_for_downgrade
  • prepare_for_cancelation
  • prepare_for_card_update
  • finalize_plan_change!
  • finalize_new_subscription!
  • finalize_upgrade!
  • finalize_downgrade!
  • finalize_cancelation!
  • finalize_card_update!
  • card_was_declined

Be sure to include a call to super in each of your implementations, especially if you're using multiple concerns to break all this logic into smaller pieces.

Between prepare_for_* and finalize_*, so far I've used finalize_* almost exclusively. The difference is that prepare_for_* runs before we settle things with Stripe, and finalize_* runs after everything is settled in Stripe. For that reason, please be sure not to implement anything in finalize_* implementations that might cause issues with ActiveRecord saving the updated state of the subscription.

Webhooks

We use stripe_event under the hood to support webhooks. The default webhooks URL is /koudoku/webhooks.

You can add your own webhooks using the (reduced) stripe_event syntax in the config/initializers/koudoku.rb file:

# /config/initializers/koudoku.rb
Koudoku.setup do |config|
  config.subscriptions_owned_by = :user
  config.stripe_publishable_key = ENV['STRIPE_PUBLISHABLE_KEY']
  config.stripe_secret_key = ENV['STRIPE_SECRET_KEY']
  
  # add webhooks
  config.subscribe 'charge.failed', YourChargeFailed
end

koudoku's People

Contributors

alainpilon avatar andrewculver avatar bf4 avatar boomshadow avatar cjavdev avatar confact avatar g8d3 avatar george-carlin avatar gregmolnar avatar hanchang avatar hates avatar laktek avatar laurobecker avatar lgs avatar marckohlbrugge avatar mattyb avatar mediavrog avatar pcboy avatar petergoldstein avatar razvanh avatar samdunne avatar strika avatar todddickerson avatar yas4891 avatar zirconcode 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

koudoku's Issues

multiple products?

Can Koudoku serve as the subscription management system for multiple products/applications?

Stripe::InvalidRequestError after migrating from Test to Live mode

Now I have everything up and running in test mode, I want to migrate to Live mode on Stripe in my production environment. I have changed my publishable and secret keys in my initialiser and pushed up to Heroku. Now when I try to post a transaction using a real card I get the following error returned from Stripe:

Stripe::InvalidRequestError (We couldn't charge this card because its Stripe token was created with an API key in live mode and now a charge is being attempted with an API key in test mode.
You can manage your API keys at https://manage.stripe.com/account/apikeys. If you have any questions, we can help at https://support.stripe.com/
If you'd like to charge this card, you should use a live API key to charge the card. If you just want to test, you should use a test API key to store the card.

For good measure I also set the publishable and secret keys directly in Heroku using config:set, in case it wasn't picking up my initialiser. No luck there either.

Any idea why my new config isn't being picked up? Or at least is only being half-picked up?

Handling of dispute webhooks is broken.

The handling of a charge.dispute.created webhook currently looks like this:

  elsif data_json['type'] == "charge.dispute.created"

    stripe_id = data_json['data']['object']['customer']

    subscription = ::Subscription.find_by_stripe_id(stripe_id)
    subscription.charge_disputed

  end

However, charge.dispute.created doesn't actually include any customer data, so the look-up fails and the call to subscription.charge_disputed throws an error.

Doesn't play well with Turbolinks

I'm running into an issue where the JavaScript that is required to run on the payment page isn't running as expected. As a result, when the user submits the payment form, it's not submitting the card information to Stripe and is instead submitting an incomplete form to the server, which results in the following error:

NoMethodError in Koudoku::SubscriptionsController#create
undefined method `encoding' for nil:NilClass

Is stripeResponseHandler out of date?

In app/views/koudoku/subscriptions/_card.html.erb we have the JS function stripeResponseHandler which contains the following lines:

      form$.append("<input type='hidden' name='subscription[credit_card_token]' value='" + response['id'] + "'/>");
      form$.append("<input type='hidden' name='subscription[last_four]' value='" + response['last4'] + "'/>");
      form$.append("<input type='hidden' name='subscription[card_type]' value='" + response['card_type'] + "'/>");

Note that it's looking for keys called id, last4, and card_type in the response object.

Problem is, the response object I'm getting back from the Stripe server looks like this:

{
    id: "...",
    livemode: false,
    created: 1415803250,
    used: false,
    type: "card",
    object: "token"โ€ฆ}

    card: {
        address_city: ...,
        address_country: ...,
        address_line1: ...,
        address_line2: ...,
        address_state: ...,
        address_zip: ...,
        brand: "Visa",
        country: "US",
        customer: ...,
        dynamic_last4: ...,
        exp_month: 4,
        exp_year: 2015,
        fingerprint: ...,
        funding: "credit"
        id: "card_xxxxxxxxxxxxxxxxxxxx"
        last4: "XXXX",
        name: ...,
        object: "card",
    }
}

Note that last4 and card_type aren't in there, although we do have card.last4 and type.

Is this due to a breaking change in the Stripe API?

If I'm going to fix this, should subscription[credit_card_token]' be set to response.id or response.card.id? And what should subscription[card_type] be set to? response.type? response.card.brand?

Disabling plans?

Is there any way to disable a plan so that it persists for users who are already subscribed, but is no longer available for new users? Ideally, it would still appear on the pricing_table but would be grayed out and not selectable. Any way to do this? Thanks!

Why does Koudoku generate routes pointing to a non-existent controller?

When I set up an app with Koudoku using the default settings, I see routes like this in rake routes:

owner_index_path  GET     /users(.:format)          koudoku/users#index
                  POST    /users(.:format)          koudoku/users#create
new_owner_path    GET     /users/new(.:format)      koudoku/users#new
edit_owner_path   GET     /users/:id/edit(.:format) koudoku/users#edit
owner_path        GET     /users/:id(.:format)      koudoku/users#show
                  PATCH   /users/:id(.:format)      koudoku/users#update
                  PUT     /users/:id(.:format)      koudoku/users#update
                  DELETE  /users/:id(.:format)      koudoku/users#destroy

The problem is, koudoku/users maps to Koudoku::UsersController, and that controller doesn't exist. Why are those routes there? Are they doing something I haven't spotted? I can't see any reference to them anywhere in the codebase.

The reason I discovered this is because I had this in my routes file:

mount Koudoku::Engine, at: '/'
scope module: 'koudoku' do
  get 'pricing' => 'subscriptions#index', as: 'pricing'
end

devise_for :users

and because mount Koudoku::Engine, at: '/' comes before devise_for :users, when I try to visit something like /users/sign_in it resolves to koudoku/users#show instead of devise/sessions#new... hence the error.

I can easily fix this by swapping the order of the lines, but still, it's a unintuitive error message and I'm curious what the routes are for.

(And is it a bad idea to mount the Koudoku engine at /? I don't like having to make my users visit a url like koudoku/subscriptions/new, but I'm thinking it may be necessary to avoid bugs like this.)

Where do I put the subscription code?

Would anyone mind letting me know what I do with the code provided?

Plan.create({
name: 'Personal',
price: 10.00,
interval: 'month',
stripe_id: '1',
features: ['1 Project', '1 Page', '1 User', '1 Organization'].join("\n\n"),
display_order: 1
})

Filter chain halted as :load_subscription rendered or redirected

I can successfully charge a card and record a subscription in the database, but when I go to the user's subscription page (at the end of <%= link_to 'Subscription', koudoku.owner_subscriptions_path(@current_user) %>) I get a 401 Unauthorised. Looking in the logs I have:

Filter chain halted as :load_subscription rendered or redirected
Completed 401 Unauthorized in 42ms (Views: 32.8ms | ActiveRecord: 6.2ms)

I am using v0.0.11 on Rails 3.2, I'm not using Devise for authentication and I'm overriding the subscriptions controller to use "organisation" rather than "user".

Support Rails 4

The gemspec requires Rails 3.2.x. I'd love to see this relaxed to support Rails 4.

Conflict when generator encounters existing models with same name.

I ran the generator:
$ rails g koudoku:install user

and it encountered a conflict with an existing model in my app called 'Plan'. The existing plan model represents a 'business plan' within the domain of the app.

I attempted to rename the parts of the generator that were being created to be PaymentPlan but this was not enough as the rest of the koudoku is expecting to hit the Plan model not PaymentPlan.

Please enhance koudoku so that we can specify custom model names or at least namespace koudoku models separately from any existing models with duplicate names.

Any ideas how I can handle the conflict in the mean time?

generators use deprecated rspec syntax

We used the generators in our Rails project that is using rspec 2.99, which no longer allows 'pending' as a keyword -- 'skip' is now used. It would be nice if the generator could detect if 'pending' is allowed by the current rspec version, and use 'skip' if the current rspec version doesn't allow 'pending'

routes.rb remains same during Install

When running rails g koudoku:install user, somehow the engine isn't being mounted. All paths fail, and the routes.rb file hasn't changed. The gsub is run during the installation process however, as I do get the line:

gsub config/routes.rb

Could it be that the regex in install_generator.rb is wrongly capitalized?

/Application.routes.draw do/ -> /application.routes.draw do/

I am on rails 4.1.

subscription redirect for to devise user sign in - NoMethodError 'router_name'

I receive this error when selecting a plan to sign up for:

Started GET "/koudoku/subscriptions/new?plan=2" for 127.0.0.1 at 2014-11-12 14:16:41 -0500
Processing by Koudoku::SubscriptionsController#new as HTML
Parameters: {"plan"=>"2"}
Completed 500 Internal Server Error in 2ms

NoMethodError (undefined method `router_name' for nil:NilClass):
  devise (3.3.0) lib/devise/controllers/url_helpers.rb:50:in `new_registration_path'
  koudoku (0.0.11) app/controllers/koudoku/subscriptions_controller.rb:46:in `redirect_to_sign_up'
  koudoku (0.0.11) app/controllers/koudoku/subscriptions_controller.rb:78:in `new'
  actionpack (4.0.8) lib/action_controller/metal/implicit_render.rb:4:in `send_action'
  actionpack (4.0.8) lib/abstract_controller/base.rb:189:in `process_action'
  actionpack (4.0.8) lib/action_controller/metal/rendering.rb:10:in `process_action'
  actionpack (4.0.8) lib/abstract_controller/callbacks.rb:18:in `block in process_action'
  activesupport (4.0.8) lib/active_support/callbacks.rb:443:in `_run__1101018572451401219__process_action__callbacks'
  activesupport (4.0.8) lib/active_support/callbacks.rb:80:in `run_callbacks'
  actionpack (4.0.8) lib/abstract_controller/callbacks.rb:17:in `process_action'
  actionpack (4.0.8) lib/action_controller/metal/rescue.rb:29:in `process_action'
  actionpack (4.0.8) lib/action_controller/metal/instrumentation.rb:31:in `block in process_action'
  activesupport (4.0.8) lib/active_support/notifications.rb:159:in `block in instrument'
  activesupport (4.0.8) lib/active_support/notifications/instrumenter.rb:20:in `instrument'
  activesupport (4.0.8) lib/active_support/notifications.rb:159:in `instrument'
  actionpack (4.0.8) lib/action_controller/metal/instrumentation.rb:30:in `process_action'
  actionpack (4.0.8) lib/action_controller/metal/params_wrapper.rb:250:in `process_action'
  activerecord (4.0.8) lib/active_record/railties/controller_runtime.rb:18:in `process_action'
  actionpack (4.0.8) lib/abstract_controller/base.rb:136:in `process'
  actionpack (4.0.8) lib/abstract_controller/rendering.rb:44:in `process'
  actionpack (4.0.8) lib/action_controller/metal.rb:195:in `dispatch'
  actionpack (4.0.8) lib/action_controller/metal/rack_delegation.rb:13:in `dispatch'
  actionpack (4.0.8) lib/action_controller/metal.rb:231:in `block in action'
  actionpack (4.0.8) lib/action_dispatch/routing/route_set.rb:82:in `call'
  actionpack (4.0.8) lib/action_dispatch/routing/route_set.rb:82:in `dispatch'
  actionpack (4.0.8) lib/action_dispatch/routing/route_set.rb:50:in `call'
  actionpack (4.0.8) lib/action_dispatch/journey/router.rb:71:in `block in call'
  actionpack (4.0.8) lib/action_dispatch/journey/router.rb:59:in `each'
  actionpack (4.0.8) lib/action_dispatch/journey/router.rb:59:in `call'
  actionpack (4.0.8) lib/action_dispatch/routing/route_set.rb:676:in `call'
  railties (4.0.8) lib/rails/engine.rb:511:in `call'
  railties (4.0.8) lib/rails/railtie/configurable.rb:30:in `method_missing'
  actionpack (4.0.8) lib/action_dispatch/journey/router.rb:71:in `block in call'
  actionpack (4.0.8) lib/action_dispatch/journey/router.rb:59:in `each'
  actionpack (4.0.8) lib/action_dispatch/journey/router.rb:59:in `call'
  actionpack (4.0.8) lib/action_dispatch/routing/route_set.rb:676:in `call'
  warden (1.2.3) lib/warden/manager.rb:35:in `block in call'
  warden (1.2.3) lib/warden/manager.rb:34:in `catch'
  warden (1.2.3) lib/warden/manager.rb:34:in `call'
  rack (1.5.2) lib/rack/etag.rb:23:in `call'
  rack (1.5.2) lib/rack/conditionalget.rb:25:in `call'
  rack (1.5.2) lib/rack/head.rb:11:in `call'
  actionpack (4.0.8) lib/action_dispatch/middleware/params_parser.rb:27:in `call'
  actionpack (4.0.8) lib/action_dispatch/middleware/flash.rb:241:in `call'
  rack (1.5.2) lib/rack/session/abstract/id.rb:225:in `context'
  rack (1.5.2) lib/rack/session/abstract/id.rb:220:in `call'
  actionpack (4.0.8) lib/action_dispatch/middleware/cookies.rb:486:in `call'
  activerecord (4.0.8) lib/active_record/query_cache.rb:36:in `call'
  activerecord (4.0.8) lib/active_record/connection_adapters/abstract/connection_pool.rb:626:in `call'
  activerecord (4.0.8) lib/active_record/migration.rb:373:in `call'
  actionpack (4.0.8) lib/action_dispatch/middleware/callbacks.rb:29:in `block in call'
  activesupport (4.0.8) lib/active_support/callbacks.rb:373:in `_run__4022916891687647533__call__callbacks'
  activesupport (4.0.8) lib/active_support/callbacks.rb:80:in `run_callbacks'
  actionpack (4.0.8) lib/action_dispatch/middleware/callbacks.rb:27:in `call'
  actionpack (4.0.8) lib/action_dispatch/middleware/reloader.rb:64:in `call'
  actionpack (4.0.8) lib/action_dispatch/middleware/remote_ip.rb:76:in `call'
  actionpack (4.0.8) lib/action_dispatch/middleware/debug_exceptions.rb:17:in `call'
  actionpack (4.0.8) lib/action_dispatch/middleware/show_exceptions.rb:30:in `call'
  railties (4.0.8) lib/rails/rack/logger.rb:38:in `call_app'
  railties (4.0.8) lib/rails/rack/logger.rb:20:in `block in call'
  activesupport (4.0.8) lib/active_support/tagged_logging.rb:68:in `block in tagged'
  activesupport (4.0.8) lib/active_support/tagged_logging.rb:26:in `tagged'
  activesupport (4.0.8) lib/active_support/tagged_logging.rb:68:in `tagged'
  railties (4.0.8) lib/rails/rack/logger.rb:20:in `call'
  actionpack (4.0.8) lib/action_dispatch/middleware/request_id.rb:21:in `call'
  rack (1.5.2) lib/rack/methodoverride.rb:21:in `call'
  rack (1.5.2) lib/rack/runtime.rb:17:in `call'
  activesupport (4.0.8) lib/active_support/cache/strategy/local_cache.rb:83:in `call'
  rack (1.5.2) lib/rack/lock.rb:17:in `call'
  actionpack (4.0.8) lib/action_dispatch/middleware/static.rb:64:in `call'
  rack (1.5.2) lib/rack/sendfile.rb:112:in `call'
  railties (4.0.8) lib/rails/engine.rb:511:in `call'
  railties (4.0.8) lib/rails/application.rb:97:in `call'
  rack (1.5.2) lib/rack/lock.rb:17:in `call'
  rack (1.5.2) lib/rack/content_length.rb:14:in `call'
  rack (1.5.2) lib/rack/handler/webrick.rb:60:in `service'
  /Users/cj/.rvm/rubies/ruby-2.0.0-p451/lib/ruby/2.0.0/webrick/httpserver.rb:138:in `service'
  /Users/cj/.rvm/rubies/ruby-2.0.0-p451/lib/ruby/2.0.0/webrick/httpserver.rb:94:in `run'
  /Users/cj/.rvm/rubies/ruby-2.0.0-p451/lib/ruby/2.0.0/webrick/server.rb:295:in `block in start_thread'

My I believe I have my routes setup properly.

MyApp::Application.routes.draw do

  resources :posts

  devise_for :admin_users, ActiveAdmin::Devise.config
  ActiveAdmin.routes(self)

  devise_for :users

  # Added by Koudoku.
  mount Koudoku::Engine, at: 'koudoku'
  scope module: 'koudoku' do
    get 'pricing' => 'subscriptions#index', as: 'pricing'
  end

  root to: 'front_page#index'
  get 'about', to: 'front_page#about'
  get 'tour', to: 'front_page#tour'
end 

In the example app at https://koudoku.herokuapp.com, selecting a plan while not logged in redirects to https://koudoku.herokuapp.com/users/sign_up, which is what I want it to do. How can I get the redirection to work properly?

undefined method `gsub' for nil:NilClass

I am using v0.0.9 as I am still on Rails 3.2. But the Koudoku code looks the same for v0.0.10 as well. I have been able to successfully sign up as a new customer with a test Stripe plan, but then after the Stripe processing we are redirected back to the _pricing_table-html.erb, which gives me the following error:

NoMethodError in Koudoku/subscriptions#index

Showing /Users/matthewcumberlidge/.rvm/gems/ruby-1.9.3-p429/gems/koudoku-0.0.9/app/views/koudoku/subscriptions/_pricing_table.html.erb where line #30 raised:

undefined method `gsub' for nil:NilClass
Extracted source (around line #30):

27: <% if Koudoku.free_trial? %>
28:

  • <%= Koudoku.free_trial_length %>-day Free Trial

  • 29: <% end %>
    30: <%= BlueCloth.new(plan.features.gsub(/\n/, "\n\n")).to_html.gsub(/<(/?)p>/, '<\1li>').html_safe %>
    31:
    32:
    33:

    This partial wasn't giving me any issues before I signed up for with Stripe, so must be something to do with the behaviour of the partial once sign up is completed. I also note that there is nothing in my Subscriptions table, so perhaps the sign up didn't complete properly? How would I debug?

    Koudoku Javascript

    Hi,

    I'm to deconstruct the gem. I know that Stripe requires JS, but I can't find the JS files used for Koudoku. Can you direct me to the file(s)?

    undefined method `__sort_option__' for :price:Symbol

    After installing like the README, I get the following error

    NoMethodError at /pricing
    
    undefined method `__sort_option__' for :price:Symbol
    

    here it is my env:

    $ ruby -v
    ruby 2.1.2p95 (2014-05-08 revision 45877) [x86_64-linux]
    $ gem list koudoku
    
    *** LOCAL GEMS ***
    
    koudoku (0.0.11)
    $ gem list mongoid
    
    *** LOCAL GEMS ***
    
    mongoid (4.0.0)
    

    Invoices page

    Provide a page with all past invoices for a subscription owner. It's common for services to provide a page where you can see all billing history.

    Change from subscriptions from User to Company

    I have the standard install having run..

    rails g koudoku:install user

    If I would like to change it from "User" to "Company", what do I need to change?

    Already changed:

    create_table "subscriptions", force: true do |t|
    t.string "stripe_id"
    t.integer "plan_id"
    t.string "last_four"
    t.integer "coupon_id"
    t.string "card_type"
    t.float "current_price"
    t.integer "user_id"
    t.datetime "created_at"
    t.datetime "updated_at"
    end

    to

    create_table "subscriptions", force: true do |t|
    t.string "stripe_id"
    t.integer "plan_id"
    t.string "last_four"
    t.integer "coupon_id"
    t.string "card_type"
    t.float "current_price"
    t.integer "company_id"
    t.datetime "created_at"
    t.datetime "updated_at"
    end

    and this

    Koudoku.setup do |config|
    config.webhooks_api_key = "e41d"
    config.subscriptions_owned_by =:user
    config.stripe_publishable_key = ENV['STRIPE_PUBLISHABLE_KEY']
    config.stripe_secret_key = ENV['STRIPE_SECRET_KEY']

    config.free_trial_length = 30

    end

    to

    Koudoku.setup do |config|
    config.webhooks_api_key = "e41d"
    config.subscriptions_owned_by = :company
    config.stripe_publishable_key = ENV['STRIPE_PUBLISHABLE_KEY']
    config.stripe_secret_key = ENV['STRIPE_SECRET_KEY']

    config.free_trial_length = 30

    end

    AND this...

    class Subscription < ActiveRecord::Base
    include Koudoku::Subscription

    belongs_to :user
    belongs_to :coupon
    end

    to this..

    class Subscription < ActiveRecord::Base
    include Koudoku::Subscription

    belongs_to :company
    belongs_to :coupon
    end

    AND THIS...

    class Plan < ActiveRecord::Base
    include Koudoku::Plan
    belongs_to :user
    belongs_to :coupon
    has_many :subscriptions
    end

    TO THIS..

    class Plan < ActiveRecord::Base
    include Koudoku::Plan
    belongs_to :company
    belongs_to :coupon
    has_many :subscriptions
    end

    What else am I missing? Thank you

    undefined method `encoding' for nil:NilClass

    I am getting this error when I submit a card with test info from the _card partial. The error is being generated within CGI.escape, here's the code:

    URL-encode a string.

    url_encoded_string = CGI::escape("'Stop!' said Fred")

    # => "%27Stop%21%27+said+Fred"

    def CGI::escape(string)
    encoding = string.encoding
    string.dup.force_encoding('ASCII-8BIT').gsub(/([^ a-zA-Z0-9_.-]+)/) do
    '%' + $1.unpack('H2' * $1.bytesize).join('%').upcase
    end.tr(' ', '+').force_encoding(encoding)
    end

    I've traced it back a few steps, and I'm pretty sure the error is stemming from the fact that the 'string' being passed in from here:

    def retrieve(id, api_key=nil)
    api_key ||= @api_key
    response, api_key = Stripe.request(:get,"#{url}/#{CGI.escape(id)}", api_key)
    Util.convert_to_stripe_object(response, api_key)
    end

    is nil. 'retrieve' is called from Koudoku::Subscription#processing:

    store the customer id.

            self.stripe_id = customer.id
            self.last_four = customer.cards.retrieve(customer.default_card).last4
            finalize_new_subscription!
            finalize_upgrade!
    

    It looks like customer.default_card is nil, which is leading to the error. What should this be? Where is it getting set from? Thanks!

    Stripe::AuthenticationError - No API key provided

    I am getting this error:

    Stripe::AuthenticationError - No API key provided.  (HINT: set your API key using "Stripe.api_key = <API-KEY>".  You can generate API keys from the Stripe web interface.  See https://stripe.com/api for details, or email [email protected] if you have any questions.):
    

    Yet, I defined both stripe_publishable_key and stripe_secret_key in koudoku.rb

    Koudoku stopped working with Devise 3.3.0

    I just tested and Koudoku stopped working with Devise 3.3.0 because it hands a string to new_registration_path in SubscriptionController - instead of the expected symbol

    Response body error: Stripe::InvalidRequestError

    I get Response body error: Stripe::InvalidRequestError

    Stripe::InvalidRequestError at /users/1/subscriptions
    This customer has no attached card
    

    env:

    lsoave@basenode:~/test/koudoku-omniauth$ grep koudoku Gemfile.lock 
      remote: https://github.com/andrewculver/koudoku.git
        koudoku (0.0.11)
      koudoku!
    

    stacktrace:

    lsoave@basenode:~/test/koudoku-omniauth$ rails s 
    => Booting WEBrick
    => Rails 4.1.6 application starting in development on http://0.0.0.0:3000
    => Run `rails server -h` for more startup options
    => Notice: server is listening on all interfaces (0.0.0.0). Consider using 127.0.0.1 (--binding option)
    => Ctrl-C to shutdown server
    [2014-09-28 18:02:57] INFO  WEBrick 1.3.1
    [2014-09-28 18:02:57] INFO  ruby 2.1.2 (2014-05-08) [x86_64-linux]
    [2014-09-28 18:02:57] INFO  WEBrick::HTTPServer#start: pid=26155 port=3000
    
    
    Started GET "/" for 127.0.0.1 at 2014-09-28 18:06:06 +0200
      ActiveRecord::SchemaMigration Load (0.1ms)  SELECT "schema_migrations".* FROM "schema_migrations"
    Processing by Rails::WelcomeController#index as HTML
      Rendered /home/lsoave/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/railties-4.1.6/lib/rails/templates/rails/welcome/index.html.erb (1.2ms)
    Completed 200 OK in 12ms (Views: 5.2ms | ActiveRecord: 0.0ms)
    
    
    Started GET "/pricing" for 127.0.0.1 at 2014-09-28 18:06:10 +0200
    Processing by Koudoku::SubscriptionsController#index as HTML
      User Load (0.2ms)  SELECT  "users".* FROM "users"  WHERE "users"."id" = ? LIMIT 1  [["id", 1]]
      Subscription Load (0.1ms)  SELECT  "subscriptions".* FROM "subscriptions"  WHERE "subscriptions"."user_id" = ? LIMIT 1  [["user_id", 1]]
      Plan Load (0.2ms)  SELECT "plans".* FROM "plans"   ORDER BY "plans"."display_order" ASC
      Rendered koudoku/subscriptions/_social_proof.html.erb (0.3ms)
      Rendered koudoku/subscriptions/_pricing_table.html.erb (17.9ms)
      Rendered koudoku/subscriptions/index.html.erb within layouts/application (19.9ms)
    Completed 200 OK in 119ms (Views: 92.9ms | ActiveRecord: 1.2ms)
    
    Started GET "/koudoku/subscriptions/new?plan=1" for 127.0.0.1 at 2014-09-28 18:06:12 +0200
    Processing by Koudoku::SubscriptionsController#new as HTML
      Parameters: {"plan"=>"1"}
      User Load (0.1ms)  SELECT  "users".* FROM "users"  WHERE "users"."id" = ? LIMIT 1  [["id", 1]]
    Redirected to http://localhost:3000/koudoku/users/1/subscriptions/new?plan=1
    Completed 302 Found in 2ms (ActiveRecord: 0.1ms)
    
    
    Started GET "/koudoku/users/1/subscriptions/new?plan=1" for 127.0.0.1 at 2014-09-28 18:06:12 +0200
    Processing by Koudoku::SubscriptionsController#new as HTML
      Parameters: {"plan"=>"1", "owner_id"=>"1"}
      User Load (0.1ms)  SELECT  "users".* FROM "users"  WHERE "users"."id" = ? LIMIT 1  [["id", 1]]
      User Load (0.1ms)  SELECT  "users".* FROM "users"  WHERE "users"."id" = ? LIMIT 1  [["id", 1]]
      Subscription Load (0.1ms)  SELECT  "subscriptions".* FROM "subscriptions"  WHERE "subscriptions"."user_id" = ? LIMIT 1  [["user_id", 1]]
      Plan Load (0.1ms)  SELECT  "plans".* FROM "plans"  WHERE "plans"."id" = ? LIMIT 1  [["id", 1]]
      Rendered koudoku/subscriptions/_card.html.erb (12.4ms)
      Rendered koudoku/subscriptions/new.html.erb within layouts/application (13.5ms)
    Completed 200 OK in 63ms (Views: 57.2ms | ActiveRecord: 0.7ms)
    
    
    Started POST "/koudoku/users/1/subscriptions" for 127.0.0.1 at 2014-09-28 18:06:41 +0200
    Processing by Koudoku::SubscriptionsController#create as HTML
      Parameters: {"utf8"=>"โœ“", "authenticity_token"=>"HQiU89eI8ptbmex4joeAvXMVNKRBPOPFnxq2lEey0Jg=", "subscription"=>{"plan_id"=>"1"}, "owner_id"=>"1"}
      User Load (0.2ms)  SELECT  "users".* FROM "users"  WHERE "users"."id" = ? LIMIT 1  [["id", 1]]
      User Load (0.1ms)  SELECT  "users".* FROM "users"  WHERE "users"."id" = ? LIMIT 1  [["id", 1]]
      Subscription Load (0.1ms)  SELECT  "subscriptions".* FROM "subscriptions"  WHERE "subscriptions"."user_id" = ? LIMIT 1  [["user_id", 1]]
       (0.2ms)  begin transaction
      Plan Load (0.1ms)  SELECT  "plans".* FROM "plans"  WHERE "plans"."id" = ? LIMIT 1  [["id", 1]]
       (0.2ms)  rollback transaction
    Completed 500 Internal Server Error in 2310ms
    
    Stripe::InvalidRequestError - This customer has no attached card:
      stripe (1.15.0) lib/stripe.rb:232:in `handle_api_error'
      stripe (1.15.0) lib/stripe.rb:126:in `rescue in request'
      stripe (1.15.0) lib/stripe.rb:112:in `request'
      stripe (1.15.0) lib/stripe/customer.rb:39:in `update_subscription'
       () home/lsoave/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/bundler/gems/koudoku-4f3fb7250539/app/concerns/koudoku/subscription.rb:87:in `processing!'
      activesupport (4.1.6) lib/active_support/callbacks.rb:424:in `block in make_lambda'
      activesupport (4.1.6) lib/active_support/callbacks.rb:160:in `call'
      activesupport (4.1.6) lib/active_support/callbacks.rb:160:in `block in halting'
      activesupport (4.1.6) lib/active_support/callbacks.rb:166:in `call'
      activesupport (4.1.6) lib/active_support/callbacks.rb:166:in `block in halting'
      activesupport (4.1.6) lib/active_support/callbacks.rb:86:in `call'
      activesupport (4.1.6) lib/active_support/callbacks.rb:86:in `run_callbacks'
      activerecord (4.1.6) lib/active_record/callbacks.rb:302:in `create_or_update'
      activerecord (4.1.6) lib/active_record/persistence.rb:103:in `save'
      activerecord (4.1.6) lib/active_record/validations.rb:51:in `save'
      activerecord (4.1.6) lib/active_record/attribute_methods/dirty.rb:21:in `save'
      activerecord (4.1.6) lib/active_record/transactions.rb:268:in `block (2 levels) in save'
      activerecord (4.1.6) lib/active_record/transactions.rb:329:in `block in with_transaction_returning_status'
      activerecord (4.1.6) lib/active_record/connection_adapters/abstract/database_statements.rb:201:in `block in transaction'
      activerecord (4.1.6) lib/active_record/connection_adapters/abstract/database_statements.rb:209:in `within_new_transaction'
      activerecord (4.1.6) lib/active_record/connection_adapters/abstract/database_statements.rb:201:in `transaction'
      activerecord (4.1.6) lib/active_record/transactions.rb:208:in `transaction'
      activerecord (4.1.6) lib/active_record/transactions.rb:326:in `with_transaction_returning_status'
      activerecord (4.1.6) lib/active_record/transactions.rb:268:in `block in save'
      activerecord (4.1.6) lib/active_record/transactions.rb:283:in `rollback_active_record_state!'
      activerecord (4.1.6) lib/active_record/transactions.rb:267:in `save'
       () home/lsoave/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/bundler/gems/koudoku-4f3fb7250539/app/controllers/koudoku/subscriptions_controller.rb:119:in `create'
      actionpack (4.1.6) lib/action_controller/metal/implicit_render.rb:4:in `send_action'
      actionpack (4.1.6) lib/abstract_controller/base.rb:189:in `process_action'
      actionpack (4.1.6) lib/action_controller/metal/rendering.rb:10:in `process_action'
      actionpack (4.1.6) lib/abstract_controller/callbacks.rb:20:in `block in process_action'
      activesupport (4.1.6) lib/active_support/callbacks.rb:113:in `call'
      activesupport (4.1.6) lib/active_support/callbacks.rb:113:in `call'
      activesupport (4.1.6) lib/active_support/callbacks.rb:229:in `block in halting'
      activesupport (4.1.6) lib/active_support/callbacks.rb:166:in `call'
      activesupport (4.1.6) lib/active_support/callbacks.rb:166:in `block in halting'
      activesupport (4.1.6) lib/active_support/callbacks.rb:166:in `call'
      activesupport (4.1.6) lib/active_support/callbacks.rb:166:in `block in halting'
      activesupport (4.1.6) lib/active_support/callbacks.rb:149:in `call'
      activesupport (4.1.6) lib/active_support/callbacks.rb:149:in `block in halting_and_conditional'
      activesupport (4.1.6) lib/active_support/callbacks.rb:149:in `call'
      activesupport (4.1.6) lib/active_support/callbacks.rb:149:in `block in halting_and_conditional'
      activesupport (4.1.6) lib/active_support/callbacks.rb:149:in `call'
      activesupport (4.1.6) lib/active_support/callbacks.rb:149:in `block in halting_and_conditional'
      activesupport (4.1.6) lib/active_support/callbacks.rb:166:in `call'
      activesupport (4.1.6) lib/active_support/callbacks.rb:166:in `block in halting'
      activesupport (4.1.6) lib/active_support/callbacks.rb:229:in `call'
      activesupport (4.1.6) lib/active_support/callbacks.rb:229:in `block in halting'
      activesupport (4.1.6) lib/active_support/callbacks.rb:166:in `call'
      activesupport (4.1.6) lib/active_support/callbacks.rb:166:in `block in halting'
      activesupport (4.1.6) lib/active_support/callbacks.rb:86:in `call'
      activesupport (4.1.6) lib/active_support/callbacks.rb:86:in `run_callbacks'
      actionpack (4.1.6) lib/abstract_controller/callbacks.rb:19:in `process_action'
      actionpack (4.1.6) lib/action_controller/metal/rescue.rb:29:in `process_action'
      actionpack (4.1.6) lib/action_controller/metal/instrumentation.rb:31:in `block in process_action'
      activesupport (4.1.6) lib/active_support/notifications.rb:159:in `block in instrument'
      activesupport (4.1.6) lib/active_support/notifications/instrumenter.rb:20:in `instrument'
      activesupport (4.1.6) lib/active_support/notifications.rb:159:in `instrument'
      actionpack (4.1.6) lib/action_controller/metal/instrumentation.rb:30:in `process_action'
      actionpack (4.1.6) lib/action_controller/metal/params_wrapper.rb:250:in `process_action'
      activerecord (4.1.6) lib/active_record/railties/controller_runtime.rb:18:in `process_action'
      actionpack (4.1.6) lib/abstract_controller/base.rb:136:in `process'
      actionview (4.1.6) lib/action_view/rendering.rb:30:in `process'
      actionpack (4.1.6) lib/action_controller/metal.rb:196:in `dispatch'
      actionpack (4.1.6) lib/action_controller/metal/rack_delegation.rb:13:in `dispatch'
      actionpack (4.1.6) lib/action_controller/metal.rb:232:in `block in action'
      actionpack (4.1.6) lib/action_dispatch/routing/route_set.rb:82:in `call'
      actionpack (4.1.6) lib/action_dispatch/routing/route_set.rb:82:in `dispatch'
      actionpack (4.1.6) lib/action_dispatch/routing/route_set.rb:50:in `call'
      actionpack (4.1.6) lib/action_dispatch/journey/router.rb:73:in `block in call'
      actionpack (4.1.6) lib/action_dispatch/journey/router.rb:59:in `each'
      actionpack (4.1.6) lib/action_dispatch/journey/router.rb:59:in `call'
      actionpack (4.1.6) lib/action_dispatch/routing/route_set.rb:678:in `call'
      railties (4.1.6) lib/rails/engine.rb:514:in `call'
      railties (4.1.6) lib/rails/railtie.rb:194:in `public_send'
      railties (4.1.6) lib/rails/railtie.rb:194:in `method_missing'
      actionpack (4.1.6) lib/action_dispatch/journey/router.rb:73:in `block in call'
      actionpack (4.1.6) lib/action_dispatch/journey/router.rb:59:in `each'
      actionpack (4.1.6) lib/action_dispatch/journey/router.rb:59:in `call'
      actionpack (4.1.6) lib/action_dispatch/routing/route_set.rb:678:in `call'
       () home/lsoave/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/bundler/gems/omniauth-18f8fb8e7ecf/lib/omniauth/strategy.rb:186:in `call!'
       () home/lsoave/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/bundler/gems/omniauth-18f8fb8e7ecf/lib/omniauth/strategy.rb:164:in `call'
       () home/lsoave/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/bundler/gems/omniauth-18f8fb8e7ecf/lib/omniauth/builder.rb:59:in `call'
      rack (1.5.2) lib/rack/etag.rb:23:in `call'
      rack (1.5.2) lib/rack/conditionalget.rb:35:in `call'
      rack (1.5.2) lib/rack/head.rb:11:in `call'
      actionpack (4.1.6) lib/action_dispatch/middleware/params_parser.rb:27:in `call'
      actionpack (4.1.6) lib/action_dispatch/middleware/flash.rb:254:in `call'
      rack (1.5.2) lib/rack/session/abstract/id.rb:225:in `context'
      rack (1.5.2) lib/rack/session/abstract/id.rb:220:in `call'
      actionpack (4.1.6) lib/action_dispatch/middleware/cookies.rb:560:in `call'
      activerecord (4.1.6) lib/active_record/query_cache.rb:36:in `call'
      activerecord (4.1.6) lib/active_record/connection_adapters/abstract/connection_pool.rb:621:in `call'
      activerecord (4.1.6) lib/active_record/migration.rb:380:in `call'
      actionpack (4.1.6) lib/action_dispatch/middleware/callbacks.rb:29:in `block in call'
      activesupport (4.1.6) lib/active_support/callbacks.rb:82:in `run_callbacks'
      actionpack (4.1.6) lib/action_dispatch/middleware/callbacks.rb:27:in `call'
      actionpack (4.1.6) lib/action_dispatch/middleware/reloader.rb:73:in `call'
      actionpack (4.1.6) lib/action_dispatch/middleware/remote_ip.rb:76:in `call'
      better_errors (2.0.0) lib/better_errors/middleware.rb:84:in `protected_app_call'
      better_errors (2.0.0) lib/better_errors/middleware.rb:79:in `better_errors_call'
      better_errors (2.0.0) lib/better_errors/middleware.rb:57:in `call'
      actionpack (4.1.6) lib/action_dispatch/middleware/debug_exceptions.rb:17:in `call'
      actionpack (4.1.6) lib/action_dispatch/middleware/show_exceptions.rb:30:in `call'
      railties (4.1.6) lib/rails/rack/logger.rb:38:in `call_app'
      railties (4.1.6) lib/rails/rack/logger.rb:20:in `block in call'
      activesupport (4.1.6) lib/active_support/tagged_logging.rb:68:in `block in tagged'
      activesupport (4.1.6) lib/active_support/tagged_logging.rb:26:in `tagged'
      activesupport (4.1.6) lib/active_support/tagged_logging.rb:68:in `tagged'
      railties (4.1.6) lib/rails/rack/logger.rb:20:in `call'
      actionpack (4.1.6) lib/action_dispatch/middleware/request_id.rb:21:in `call'
      rack (1.5.2) lib/rack/methodoverride.rb:21:in `call'
      rack (1.5.2) lib/rack/runtime.rb:17:in `call'
      activesupport (4.1.6) lib/active_support/cache/strategy/local_cache_middleware.rb:26:in `call'
      rack (1.5.2) lib/rack/lock.rb:17:in `call'
      actionpack (4.1.6) lib/action_dispatch/middleware/static.rb:64:in `call'
      rack (1.5.2) lib/rack/sendfile.rb:112:in `call'
      railties (4.1.6) lib/rails/engine.rb:514:in `call'
      railties (4.1.6) lib/rails/application.rb:144:in `call'
      rack (1.5.2) lib/rack/lock.rb:17:in `call'
      rack (1.5.2) lib/rack/content_length.rb:14:in `call'
      rack (1.5.2) lib/rack/handler/webrick.rb:60:in `service'
      /home/lsoave/.rbenv/versions/2.1.2/lib/ruby/2.1.0/webrick/httpserver.rb:138:in `service'
      /home/lsoave/.rbenv/versions/2.1.2/lib/ruby/2.1.0/webrick/httpserver.rb:94:in `run'
      /home/lsoave/.rbenv/versions/2.1.2/lib/ruby/2.1.0/webrick/server.rb:295:in `block in start_thread'
    
    Started POST "/koudoku/__better_errors/bd61193907fc4acc/variables" for 127.0.0.1 at 2014-09-28 18:06:44 +0200
    

    custom URIs

    I'd like to setup custom routes for the engine paths with a purpose of removing the '/koudoku/' from the URI. Any suggestions on the best approach?

    License missing from gemspec

    RubyGems.org doesn't report a license for your gem. This is because it is not specified in the gemspec of your last release.

    via e.g.

    spec.license = 'MIT'
    # or
    spec.licenses = ['MIT', 'GPL-2']
    

    Including a license in your gemspec is an easy way for rubygems.org and other tools to check how your gem is licensed. As you can imagine, scanning your repository for a LICENSE file or parsing the README, and then attempting to identify the license or licenses is much more difficult and more error prone. So, even for projects that already specify a license, including a license in your gemspec is a good practice. See, for example, how rubygems.org uses the gemspec to display the rails gem license.

    There is even a License Finder gem to help companies/individuals ensure all gems they use meet their licensing needs. This tool depends on license information being available in the gemspec. This is an important enough issue that even Bundler now generates gems with a default 'MIT' license.

    I hope you'll consider specifying a license in your gemspec. If not, please just close the issue with a nice message. In either case, I'll follow up. Thanks for your time!

    Appendix:

    If you need help choosing a license (sorry, I haven't checked your readme or looked for a license file), GitHub has created a license picker tool. Code without a license specified defaults to 'All rights reserved'-- denying others all rights to use of the code.
    Here's a list of the license names I've found and their frequencies

    p.s. In case you're wondering how I found you and why I made this issue, it's because I'm collecting stats on gems (I was originally looking for download data) and decided to collect license metadata,too, and make issues for gemspecs not specifying a license as a public service :). See the previous link or my blog post about this project for more information.

    undefined local variable or method `root_path'

    Hi,

    I'm running Rails 4.1 and I've run into some problems after installing Koudoku.

    None of the routes seem to work except for /pricing and when I do access it I get a method error.

    undefined local variable or method `root_path' for #<#<Class:0x007fbdd5106bf8>:0x007fbdcca09038>
    

    I ran into some problems earlier with the install_generator not writing to my user and routes files.

    Any help would be appreciated.
    Thanks a lot

    Subscription quantity

    Do you see any issues adding support for the quantity parameter?

    In addition to having a config default quantity per plan, I'd like to add a method to set the quantity at a later point on a given subscription.

    Ideas on how to best implement this?

    Register and Subscription on same page

    Is there anyway that we can combine the devise registration with the credit card form? So the user clicks a plan and then is taken to a page that allows them to enter: name, email, and credit card info?

    Gem has worked wonders so far btw.

    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.