Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Change Credit Card Information (Stripe)

Tags:

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 
like image 275
Serge Pedroza Avatar asked Nov 19 '13 07:11

Serge Pedroza


People also ask

Does Stripe automatically update expired cards?

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).

How do I add payment method to Stripe?

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.

How do I delete my bank account from Stripe?

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".


2 Answers

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.

like image 136
Hunter Avatar answered Sep 20 '22 08:09

Hunter


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 
like image 29
Tim Sullivan Avatar answered Sep 21 '22 08:09

Tim Sullivan