Skip to content

Instantly share code, notes, and snippets.

@jennli
Last active April 5, 2016 00:04
Show Gist options
  • Save jennli/9be0c8ea8c5e36e2b3ba to your computer and use it in GitHub Desktop.
Save jennli/9be0c8ea8c5e36e2b3ba to your computer and use it in GitHub Desktop.
  • stripe info should never be saved and sent through rails server

  • user fills out the form, send the credit card info to stripe server through ajax

  • stripe server sends a one time token to rails server

  • generate a migration

class AddStripeFieldsToDb < ActiveRecord::Migration
  def change
    add_column :pledges, :stripe_txn_id, :string

    add_column :users, :stripe_customer_id, :string
    add_column :users, :stripe_last_4, :string
    add_column :users, :stripe_card_type, :string
    add_column :users, :stripe_card_expiry_month, :integer
    add_column :users, :stripe_card_expiry_year, :integer
  end
end
  • bin/rake db:migrate

  • campaigns show view

<h2>Pledge</h2>

<%= simple_form_for [@campaign, @pledge] do |f| %>
  <%= f.input :amount %>
  <%= f.submit class: "btn btn-primary" %>
<% end %>
  • create a new controller
bin/rails g controller payments
  • routes
  resources :pledges, only: [] do
    resources :payments, only: [:new, :create]
  end
  • pledge controller create action
redirect_to new_pledge_payment_path(@pledge), notice:"thanks for pledging"

Stripe

  • layout application.erb
<script type="text/javascript" src="https://js.stripe.com/v2/"></script>
  • rename coffeescript to payments.js
$(document).ready(function(){
  var stripePublishableKey = $("meta[name='stripe-publishable-key']").attr("content");
  Stripe.setPublishableKey(stripePublishableKey);
});
  • put the stripe publishable key to layout application.erb
<meta name="stripe-publishable-key" content="<%=Rails.application.secrets.stripe_publishable_key %>"/>
  • finish the new view for payments:
<h1>Payment</h1>

<%= form_tag "", id:"payment-form" do %>
<div class="form-group">
  <%= label_tag :card_number%>
  <%= text_field_tag :card_number, "", class:"form-control", data:{stripe: "number"} %>
</div>

<div class="form-group">
  <%= label_tag :cvc%>
  <%= text_field_tag :cvc, "", class: "form-control", data:{stripe: "cvc"}%>
</div>

<div class="form-group">
  <%= label_tag :expiry_Month%>
  <%= select_month(Date.today, {add_month_numbers: true}, class:"form-control", data:{stripe: "exp-month"}) %>
  <%= select_year(Date.today, {start_year: Time.new.year, end_year: Time.now.year + 10}, class:"form-control", data:{stripe: "exp-year"}) %>
</div>

<%= submit_tag "Submit Payment", class: "btn btn-primary" %>

<% end %>

https://stripe.com/docs/custom-form

  • add a hidden form in the payment new view
<%= form_tag pledge_payments_path(@pledge), id:"server-form" do %>
  <%= hidden_field_tag :stripe_token %>
<% end %>
  • then add the following in payment.js
  $("#payment-form").on("submit", function(event){
    event.preventDefault();
    $("#stripe-error-message").addClass("hide");
    $("#payment-form").find("input:submit").attr("disabled", true);
    Stripe.card.createToken($("#payment-form"), stripeResponseHandler);
  });

  var stripeResponseHandler = function(status, data) {
    if(status === 200) {
      // successful => submit second form to the server
      var token = data.id;
      $("#stripe_token").val(token);
      $("#server-form").submit();
    } else {
      var errMessage = data.error.message;
      $("#stripe-error-message").html(errMessage);
      $("#stripe-error-message").removeClass("hide");
      $("#payment-form").find("input:submit").attr("disabled", false);
    }
  };

gem

 gem 'stripe'
  • create a file called stripe.rb in config/initializers
Stripe.api_key = Rails.application.secrets.stripe_secret_key
  • on payment controller, create a customer, make a payment
class PaymentsController < ApplicationController
  before_action :authenticate_user
  before_action :find_pledge

  def new
  end

  def create
    stripe_customer = Stripe::Customer.create(
    description: "Customer for #{current_user.email}",
    source: params[:stripe_token]
    )

    current_user.stripe_customer_id       = stripe_customer.id
    current_user.stripe_last_4            = stripe_customer.sources.data[0].last4
    current_user.stripe_card_type         = stripe_customer.sources.data[0].brand
    current_user.stripe_card_expiry_month = stripe_customer.sources.data[0].exp_month
    current_user.stripe_card_expiry_year  = stripe_customer.sources.data[0].exp_year

    current_user.save

    charge = Stripe::Charge.create(
    amount: @pledge.amount * 100,
    currency: "cad",
    customer: current_user.stripe_customer_id,
    description: "Charge for pledge id: #{@pledge.id}"
    )

    @pledge.stripe_txn_id = charge.id
    @pledge.save

    redirect_to @pledge.campaign, notice:"thanks for completing the payment"
  end

  private

  def find_pledge
    @pledge = current_user.pledges.find params[:pledge_id]
  end

end

Use service objects

  • create a file: services/stripe/create_customer.rb
class Payments::Stripe::CreateCustomer

  include Virtus.model

  attribute :user, User
  attribute :stripe_token, String

  # this `call` method will invoke `call_stripe_customer_create` method which
  # will return the created stripe customer or return false.
  # if it doesn't return false it will execute `save_user_data` which will
  # use the stripe customer information to store in the user record
  def call
    call_stripe_customer_create && save_user_data
  end

  private

  def call_stripe_customer_create
    begin
      stripe_customer
    rescue => e
      # You may want to email admin with the error `e`
      false
    end
  end

  def stripe_customer
    @stripe_customer ||=  Stripe::Customer.create(
    description: "Customer for #{user.email}",
    source:      stripe_token
    )
  end


  def save_user_data
    user.stripe_customer_id       = stripe_customer.id
    user.stripe_last_4            = stripe_customer.sources.data[0].last4
    user.stripe_card_type         = stripe_customer.sources.data[0].brand
    user.stripe_card_expiry_month = stripe_customer.sources.data[0].exp_month
    user.stripe_card_expiry_year  = stripe_customer.sources.data[0].exp_year
    user.save
  end

end
  • create a file: services/stripe/charge_customer.rb
class Payments::Stripe::ChargeCustomer

  include Virtus.model

  attribute :amount, Integer
  attribute :user, User
  attribute :description, String
  attribute :charge_id, String

  def call
    charge_customer
  end

  private

  def charge_customer
    begin
      charge = call_stripe_charge_customer
      @charge_id = charge.id
    rescue
      false
    end
  end

  def call_stripe_charge_customer
    Stripe::Charge.create(
    amount:      amount,
    currency:    "cad",
    customer:    user.stripe_customer_id,
    description: description
    )
  end

end
  • create a file in services/payments/handle_payment.rb
class Payments::HandlePayment

  include Virtus.model

  attribute :user, User
  attribute :stripe_token, String
  attribute :pledge, Pledge

  def call
    create_customer && charge_customer && update_pledge
  end

  private

  def create_customer
    customer_service = Payments::Stripe::CreateCustomer.new(user: user,
    stripe_token: stripe_token)
    customer_service.call
  end

  def charge_customer
    service = Payments::Stripe::ChargeCustomer.new(user:        user,
    description: description,
    amount:      amount)
    @charge_id = service.charge_id if service.call
  end

  def description
    "Charge for pledge id: #{pledge.id}"
  end

  def amount
    pledge.amount * 100
  end

  def update_pledge
    pledge.stripe_txn_id = @charge_id
    pledge.save
  end

end
  • controller
  def create
    service = Payments::HandlePayment.new(user:         current_user,
    stripe_token: params[:stripe_token],
    pledge:       @pledge)

    if service.call
      redirect_to @pledge.campaign, notice: "Thanks for completing the payment"
    else
      flash[:alert] = "Error happened! Please try again."
      render :new
    end
  end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment