CheddarGetter Beginner

Paul A.'s Avatar

Paul A.

18 Jul, 2010 01:02 AM

Hi! I'm looking to implement CheddarGetter into a rails app I've built. I've decided to go with the "cheddargetter" gem since it's the only one that provided documentation. As a beginner developer, however, I am unsure where in my models/controllers I should put in what. Any suggestions?

  1. 1 Posted by Paul A. on 18 Jul, 2010 04:06 AM

    Paul A.'s Avatar

    better yet, can someone provide a good (working) example of Mousetrap or cheddargetter?
    Thanks!

  2. Support Staff 2 Posted by Marc Guyer on 18 Jul, 2010 09:43 PM

    Marc Guyer's Avatar

    Hi l4rk & Cameron! Would you guys mind pointing Paul in the right direction?

  3. 3 Posted by Charlie Zhao on 17 Oct, 2010 03:56 AM

    Charlie Zhao's Avatar

    I am interested in this too. An example with controller/model/view which is using cheddargetter gem will be appreciated.

  4. 4 Posted by Phil on 25 Oct, 2010 02:38 PM

    Phil's Avatar

    Using this library: http://github.com/ads/cheddargetter. You can do something like the following in you controller...

     @cheddar_getter = CheddarGetter.new(CHEDDAR_USER, CHEDDAR_PASS, CHEDDAR_PRODUCT)
    
     @cheddar_getter.create_customer("code" => params[:account][:name].upcase, "firstName" => params[:user][:first_name], "lastName" => params[:user][:last_name], "email" => params[:user][:email], "subscription[planCode]" => params[:account][:plan].upcase, "subscription[ccNumber]" => params[:cc_number], "subscription[ccExpiration]" => "#{params[:cc_expiration_month]}/#{params[:cc_expiration_year]}", "subscription[ccCardCode]" => params[:cc_card_code], "subscription[ccFirstName]" => params[:user][:first_name], "subscription[ccLastName]" => params[:user][:last_name], "subscription[ccZip]" => params[:cc_post_code], "subscription[ccCompany]" => params[:account][:name].capitalize)
  5. 5 Posted by Jon Larkowski on 26 Oct, 2010 03:23 PM

    Jon Larkowski's Avatar

    Here's an extract of some actual code I have that consumes my Mousetrap gem The concepts may be general enough to help with whatever wrapper you choose.

    A few notes:

    1. I deleted a bunch of code from the actual production code to remove noise and highlight the main concepts at play. So, don't expect this code to actually run.
    2. The main trick to note is that we validate locally in the controller, before we ever even attempt to talk to CheddarGetter API. This keeps us from unnecessarily sending invalid data to CheddarGetter.
    3. We also don't even save the subscription record locally unless CheddarGetter reports no errors. This keeps us from having a subscription locally with no actual subscription on CheddarGetter.
    4. We make use of nested attributes. The Business model accepts nested attributes for Subscription (the app used to save business information fields at the same time as credit card data, all in one form). Then we do a homemade version of nested attributes where the Subscription accepts credit card attributes. The credit card is validated by the ActiveMerchant gem, but we don't use ActiveMerchant for anything else.

    Controller

    class SubscriptionPlansController < ApplicationController
    
      def update
        @business = current_user.business
        old_subscription = @business.subscription
        @subscription = Subscription.new(params[:business][:subscription_attributes])
        @subscription.business_id = @business.id
    
        cheddar_getter_error_message = nil
    
        if @subscription.valid? && !(cheddar_getter_error_message = @subscription.save_on_cheddar_getter)
          @subscription.save!
          old_subscription.cancel! if old_subscription
          @business.reload
        else
          @business.build_subscription(@subscription.attributes)
          @subscription.errors.add_to_base cheddar_getter_error_message if cheddar_getter_error_message.present?
        end
      end
    
      def destroy
        @business = current_user.business
        @business.subscription.cancel!
        @business.reload
      end
    
    end

    Subscription model

    class Subscription < ActiveRecord::Base
      belongs_to :business
    
      attr_reader :credit_card
      attr_accessor :credit_card_number
    
      before_validation_on_create :assign_customer_code
    
      validates_format_of :billing_zip_code, :with => /^\d{5}$/, :message => "must be five digits"
      validates_presence_of :credit_card_last_digits
      validates_presence_of :customer_code
      validate :validate_credit_card
    
      def cancel!
        cancel_customer_on_cheddar_getter
        update_attribute :canceled_at, Time.now
      end
    
      def credit_card
        @credit_card || make_credit_card
      end
    
      def credit_card=(card)
        @credit_card = card
    
        self.credit_card_number           = @credit_card.try :number
        self.credit_card_expiration_month = @credit_card.try :month
        self.credit_card_expiration_year  = @credit_card.try :year
        self.credit_card_last_digits      = @credit_card.try :last_digits
        self.billing_first_name           = @credit_card.try :first_name
        self.billing_last_name            = @credit_card.try :last_name
      end
    
      def credit_card_attributes=(attributes)
        self.credit_card = make_credit_card(attributes)
      end
    
      def save_on_cheddar_getter
        new_record? ? create_on_cheddar_getter : update_on_cheddar_getter
      end
    
      def create_on_cheddar_getter
        customer_attributes = {
          :email      => business.user.email,
          :code       => customer_code,
          :first_name => business.user.first_name,
          :last_name  => business.user.last_name,
          :company    => business.subdomain,
          :subscription_attributes => {
            :plan_code                    => 'SOME_PLAN_CODE',
            :credit_card_number           => credit_card_number,
            :credit_card_expiration_month => credit_card_expiration_month,
            :credit_card_expiration_year  => credit_card_expiration_year,
            :billing_first_name           => billing_first_name,
            :billing_last_name            => billing_last_name,
            :billing_zip_code             => billing_zip_code,
          }
        }
    
        Mousetrap::Customer.create customer_attributes
        nil
      rescue => error
        Rails.logger.error error.message
        error.message
      end
    
      def update_on_cheddar_getter
        mousetrap_attributes = {}
    
        MOUSETRAP_SYNC_FIELDS.each do |f|
          field_symbol = f.to_sym
          mousetrap_attributes[field_symbol] = send(field_symbol)
        end
    
        Mousetrap::Subscription.update(customer_code, mousetrap_attributes)
        nil
      rescue => error
        Rails.logger.error error.message
        error.message
      end
    
    
      protected
    
      MOUSETRAP_SYNC_FIELDS = [
        :plan_code,
        :billing_first_name,
        :billing_last_name,
        :credit_card_number,
        :credit_card_expiration_month,
        :credit_card_expiration_year,
        :billing_zip_code
      ]
    
      def cancel_customer_on_cheddar_getter
        customer = Mousetrap::Customer[customer_code]
        customer.try(:cancel)
      end
    
      def credit_card_display_number
        "XXXX-XXXX-XXXX-#{credit_card_last_digits}"
      end
    
      def assign_customer_code
        self.customer_code ||= UUIDTools::UUID.random_create.to_s
      end
    
      def make_credit_card(attributes = {})
        ActiveMerchant::Billing::CreditCard.require_verification_value = false
    
        credit_card = ActiveMerchant::Billing::CreditCard.new(attributes)
    
        def credit_card.new_record?
          true
        end
    
        credit_card
      end
    
      def validate_credit_card
        unless credit_card.valid?
          credit_card.errors.each do |field, messages|
            messages.each do |message|
              errors.add("credit_card_#{field}".to_sym, message)
            end
          end
        end
      end
    end

    Portion of the view

    - form_for @business, :url => subscription_plan_path do |bf|
      - bf.fields_for :subscription do |sf|
        - sf.fields_for :credit_card do |fc|
          .element
            .label_card_type= fc.label :type, "Card Type"
            = fc.select :type, [['Visa', 'visa'], ['MasterCard', 'master']]
          .element
            .label= fc.label :number, "Card Number"
            .entry= fc.text_field :number
          .element
            .label= fc.label :year, "Expiration Date"
            #date_wrapper
              = fc.select :month, '01'..'12', :selected => @credit_card.month.to_s.rjust(2, "0")
              \/
              = fc.select :year, Time.now.year..6.years.from_now.year
          .element
            .label= fc.label :first_name, "Billing First Name"
            .entry= fc.text_field :first_name
          .element
            .label= fc.label :last_name, "Billing Last Name"
            .entry= fc.text_field :last_name
          .element
            .label= sf.label :billing_zip_code, "Billing Zip Code"
            .entry= f.text_field :billing_zip_code, :maxlength => 5
    
      = bf.submit
  6. 6 Posted by joshua on 25 Nov, 2010 04:30 PM

    joshua's Avatar

    Thanks for this detailed example Jon. Am I correct in assuming that you save the credit card number in your local subscription table? Is that a problem for PCI compliance? So far I am only storing the last 4 digits locally.

  7. 7 Posted by Jon Larkowski on 25 Nov, 2010 05:04 PM

    Jon Larkowski's Avatar

    @joshua You're welcome. No, I don't save the full credit card number. That would definitely be a PCI issue. Want to avoid as many of those as possible. ;) Like you, I just store the last four digits.

  8. 8 Posted by joshua on 25 Nov, 2010 07:53 PM

    joshua's Avatar

    Jon, thanks for the quick reply. Just one question then - where does the following variable live if the model has no credit card fields?

    self.credit_card_expiration_month = @credit_card.try :month
    
  9. 9 Posted by Jon Larkowski on 13 Dec, 2010 07:55 PM

    Jon Larkowski's Avatar

    @joshua Sorry for the delay. I just had to quit my job and move cross-country quick. ;)

    I do store the credit_card_expiration_month field in the database. Pretty much everything except the actual credit card number is stored. Here's the relevant portion of the schema.rb file.

    create_table "subscriptions", :force => true do |t|
      t.integer  "business_id"
      t.string   "plan_code"
      t.datetime "canceled_at"
      t.datetime "suspended_at"
      t.string   "billing_first_name"
      t.string   "billing_last_name"
      t.integer  "credit_card_expiration_month"
      t.integer  "credit_card_expiration_year"
      t.string   "credit_card_last_digits"
      t.string   "billing_zip_code"
      t.datetime "created_at"
      t.datetime "updated_at"
      t.string   "customer_code"
    end
    
  10. 10 Posted by joshua on 13 Dec, 2010 08:15 PM

    joshua's Avatar

    Thanks Jon - i figured that bit out. I was wondering if this is a PCI issue, storing some but not all CC info?

    Anyway, thanks for the assistance, this helped me move along with my setup.

  11. Dean closed this discussion on 16 Jan, 2013 03:41 PM.

Comments are currently closed for this discussion. You can start a new one.

Keyboard shortcuts

Generic

? Show this help
ESC Blurs the current field

Comment Form

r Focus the comment reply box
^ + ↩ Submit the comment

You can use Command ⌘ instead of Control ^ on Mac