I finally figured out how to implement Stripes Monthly Billing using this tutorial. http://railscasts.com/episodes/288-billing-with-stripe
So far, A User can Create & Delete their Subscription with Stripe.
But how can a User change his Credit Card Information once they have created a subscription
This is my Code with Comments and Questions. Please help new to rails. :)
CONTROLLER
class SubscriptionsController < ApplicationController def new plan = Plan.find(params[:plan_id]) @subscription = plan.subscriptions.build @subscription.user_id = current_user.id end def create @subscription = Subscription.new(params[:subscription]) if @subscription.save_with_payment redirect_to @subscription, :notice => "Thank you for subscribing!" else render :new end end def update @subscription = current_user.subscription if @subscription.save redirect_to edit_subscription_path, :success => 'Updated Card.' else flash.alert = 'Unable to update card.' render :edit end end end
MODELS
class Subscription < ActiveRecord::Base attr_accessible :plan_id, :user_id, :email, :stripe_customer_token, :last_4_digits, :card_token, :card_name, :exp_month, :exp_year, :stripe_card_token attr_accessor :stripe_card_token belongs_to :plan belongs_to :user def save_with_payment if valid? save_with_stripe_payment end end def save_with_stripe_payment customer = Stripe::Customer.create(card: stripe_card_token, email: email, plan: plan_id, description: "Unlimited Comics") self.stripe_customer_token = customer.id self.card_token = customer.cards.data.first["id"] self.card_name = customer.cards.data.first["type"] self.exp_month = customer.cards.data.first["exp_month"] self.exp_year = customer.cards.data.first["exp_year"] self.last_4_digits = customer.cards.data.first["last4"] save! rescue Stripe::InvalidRequestError => e logger.error "Stripe error while creating customer: #{e.message}" errors.add :base, "There was a problem with your credit card." false end def update_card customer = Stripe::Customer.retrieve(stripe_customer_token) card = customer.cards.retrieve(card_token) *** This Update works, but how do I pass a new Credit Card Number, Expiration Date etc. card.name = "My new name" customer.save rescue Stripe::StripeError => e logger.error "Stripe Error: " + e.message errors.add :base, "#{e.message}." false end end
VIEWS
<%= form_for @subscription do |f| %> <%= render 'shared/error_messages', object: f.object %> <%= f.hidden_field :plan_id %> <%= f.hidden_field :user_id %> <%= f.hidden_field :stripe_card_token %> <h4>Change Credit Card</h4> <div class="field"> <%= label_tag :card_number, "Credit Card Number" %> <%= text_field_tag :card_number, nil, name: nil %> </div> <div class="field"> <%= label_tag :card_code, "Security Code on Card (CVV)" %> <%= text_field_tag :card_code, nil, name: nil %> </div> <div class="field"> <%= label_tag :card_month, "Card Expiration" %> <%= select_month nil, {add_month_numbers: true}, {name: nil, id: "card_month"} %> <%= select_year nil, {start_year: Date.today.year, end_year: Date.today.year+15}, {name: nil, id: "card_year"} %> </div> <%= f.submit "Change Credit Card", :class => "btn btn-primary" %> <% end %>
ROUTES
App::Application.routes.draw do resources :subscriptions end
SCHEMA
create_table "subscriptions", :force => true do |t| t.integer "plan_id" t.integer "user_id" t.string "email" t.string "card_name" t.string "exp_month" t.string "exp_year" t.string "card_token" t.string "stripe_customer_token" t.string "last_4_digits" t.datetime "created_at", :null => false t.datetime "updated_at", :null => false end
Stripe works with card networks and automatically attempts to update saved card details whenever a customer receives a new card (for example, replacing an expired card or one that was reported lost or stolen).
Visit the Manage payment methods for your connected accounts page in your Dashboard to configure which payment methods your connected accounts accept. Changes to default settings apply to all new and existing connected accounts. Your connected accounts accept this payment method during checkout.
If you have access to the standard Stripe dashboard, you can disconnect your financial accounts at any time by visiting your linked accounts settings page and unlinking any of the linked accounts by clicking “Manage” and selecting “Remove account".
This answer has been updated thanks to the note from @TimSullivan
I had to do the same thing in my app. Stripe does not permit updating the card number. This leaves you with two options: 1) Create a new card and delete the original card; and 2) Create a new card, set it as the default_card and leave the original card. I chose the second route.
Here's how I did it:
models/subscriber.rb
def update_card(subscriber, stripe_card_token) customer = Stripe::Customer.retrieve(subscriber.stripe_customer_token) card = customer.sources.create(card: stripe_card_token) card.save customer.default_source = card.id customer.save rescue Stripe::InvalidRequestError => e logger.error "Stripe error while updating card info: #{e.message}" errors.add :base, "#{e.message}" false end
controllers/subscribers_controller.rb
def edit_card @subscriber = current_subscriber end def update_card @subscriber = current_subscriber if @subscriber.update_card(@subscriber, params[:stripe_card_token]) flash[:success] = 'Saved. Your card information has been updated.' redirect_to @subscriber else flash[:warning] = 'Stripe reported an error while updating your card. Please try again.' redirect_to @subscriber end end
views/subscribers/edit_card.html.erb
<%= form_for @subscriber, url: update_card_path, html: { class: 'update_subscriber' } do |f| %> <div class='form-group'> <%= label_tag :number, 'Card Number', class: 'col-sm-3 control-label' %> <div class='col-sm-9'> <%= text_field_tag :number, nil, class: 'form-control', placeholder: 'We accept Visa, MasterCard, AMEX and Discover' %> </div> </div> <div class='form-group'> <%= label_tag :cvc, 'Security Code', class: 'col-sm-3 control-label' %> <div class='col-sm-9'> <%= text_field_tag :cvc, nil, class: 'form-control', placeholder: 'The code on the back of your card (CVC)' %> </div> </div> <div class='form-group'> <%= label_tag :exp_month, 'Expiration Date', class: 'col-sm-3 control-label' %> <div class='col-sm-5'> <%= select_month nil, { add_month_numbers: true }, { id: 'exp_month', class: 'form-control btm-space' } %> </div> <div class='col-sm-4'> <%= select_year nil, { start_year: Date.today.year, end_year: Date.today.year+15 }, { id: 'exp_year', class: 'form-control btm-space' } %> </div> </div> <div class='form-group'> <div class='col-sm-4 col-sm-offset-8'> <%= submit_tag 'Update', class: 'btn btn-success btn-block' %> </div> </div> <%= f.hidden_field :stripe_card_token %> <% end %>
config/routes.rb
match '/edit_card', to: 'subscribers#edit_card', via: 'get' match '/update_card', to: 'subscribers#update_card', via: 'post'
app/js/update_card.js
var subscription; jQuery(function() { Stripe.setPublishableKey($('meta[name="stripe-key"]').attr('content')); return subscription.setUpForm(); }); subscription = { setUpForm: function() { return $('.update_subscriber').submit(function() { $('input[type="submit"]').attr('disabled', true); if ($('#card_number').length) { subscription.updateCard(); return false; } else { return true; } }); }, updateCard: function() { var card; card = { number: $('#card_number').val(), cvc: $('#card_code').val(), expMonth: $('#card_month').val(), expYear: $('#card_year').val() }; return Stripe.createToken(card, subscription.handleStripeResponse); }, handleStripeResponse: function(status, response) { if (status === 200) { $('#subscriber_stripe_card_token').val(response.id); return $('.update_subscriber')[0].submit(); } else { $('#stripe_error').text(response.error.message); return $('input[type="submit"]').attr('disabled', false); } } };
Hope this helps.
The accepted answer here is dangerously unsafe to use. It does an end-run around the security that Stripe encourages. Your own server should never get the credit card details.
You should instead be using Stripe.js to pass the card to Stripe directly (similar to how it is already done in the Railscast you link to) and retrieving a token, then creating the card based on the token.
def update_card(subscriber, stripe_token) customer = Stripe::Customer.retrieve(subscriber.stripe_customer_token) card = customer.cards.create(card: stripe_token) card.save customer.default_card = card.id customer.save rescue Stripe::InvalidRequestError => e logger.error "Stripe error while updating card info: #{e.message}" errors.add :base, "#{e.message}" false end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With