Build Robust & Production Quality Applications - Lesson 8: Fully Integrated API Tests

The are generally two options when testing third party API wrappers. We could only go to the where the TPA is and call the API and create fake response data - so what we are testing is given the generated response, can your application interpret the response and generate the desired result. On the other hand, we can go all the way to the extent to hit the TPA server and hit their server, getting the real response and intergrate our wrapper code with that data to test it.

In this instance, we are writing fully intergrated API specs require 'spec_helper'

describe StripeWrapper::Charge do

before do StripeWrapper.setapikey end

let(:token) do Stripe::Token.create( :card => { :number => cardnumber, :expmonth => 7, :exp_year => 2016, :cvc => 390 } ).id end

context "with a valid card" do let(:card_number) { '4242424242424242' }

it "charges the card successfully" do
response = StripeWapper::Charge.create(amount: 300, card: token)
expect(response).to be_successful
end

end

context "with invalid card" do let(:card_number) { '4000000000000002' } let(:response) {StripeWrapper::Charge.create(amount: 300, card: token) }

it "does not charge the card successfully" do expect(response).notto besuccessful end

it "contains an error message" do expect(response).to eq('Your card was declined') end end

Build Robust & Production Quality Applications - Lesson 8: How to Build API Wrappers

Since we are going to be using Stripe alot, it may be smart to put the API in something called an API Wrapper, which becomed a centralized place for the API, rather than spread through-out your application.

Create a Wrapper called Stripe Wrapper

models/stripe_wrapper.rb

module StripeWrapper class Charge def self.create(options={}) Stripe::Charge.create(amount: options[:amount], currency: 'usd', card: options[:card]) end end end

Then we want to create a seperate method within the Charge class for the Stripe API Key because there may be other processes that need rely on the key. We want want to write a conditional that would show the write key according to right env we are in, production, dev, test. etc.

module StripeWrapper class Charge def self.create(options={}) StripeWrapper.api_key Stripe::Charge.create(amount: options[:amount], currency: 'usd', card: options[:card]) end

def self.set_stripe_api_key
  Stripe.api_key = Rails.env.production? ? ENV['STRIPE_PUBLISHABLE_KEY'] : "pk_test_Xxzh18R0rGLpYys1rVT4KHLH"
end

end end

This handles the first part of the API request, but we also need to address the response:

Now we can update the PaymentsController to: class PaymentsController < ApplicationController def create token = params[:stripeToken]

charge = StripeWrapper::Charge.create9:smount => 3000, :card => token)

if charge.successful?
  flash[:success] = "Thank you for your generous support!"
  redirect_to new_payments_path
else
  flash[:error] = charge.error_message
  redirect_to new_payment_path
end

end end

Update the StripeWrapper

module StripeWrapper class Charge attr_reader :response, :status

def initialize(response, status)
  @response = response
  @status = status
end

def self.create(options={})
  StripeWrapper.api_key
  begin
    response = Stripe::Charge.create(amount: options[:amount], currency: 'usd', card: options[:card])
    new(response, :success)
  resuce Stripe::CaredError => e
    new(e, :error)
  end
end

def successful?
  status == :success
end

def error_message
  response.error_message
end

def self.set_stripe_api_key
  Stripe.api_key = Rails.env.production? ? ENV['STRIPE_PUBLISHABLE_KEY'] : "pk_test_Xxzh18R0rGLpYys1rVT4KHLH"
end

end end

Build Robust & Production Quality Applications - Lesson 7: Processing Credit Card Payments Online with Stripe

Processing Credit Card Payments Online

Back in the day, processing credit cards online was intense as told by 37Signals in their blogpost back in 2007. If merchants didn't want to connect their customers to Paypal to checkout, they had create a Merchant Account, followed by having an account with Authorize.net, and then an engine called ActiveMarchant with is Ruby gem for reoccuring billing - which required alot of custom code and an intense daily business process. One thing that was'nt mentioned was security - Payment Card Industry Security Standard (PCI Compliance). This means that if you do take credit card information via your website, your site must be sercured, for example. And if you store credit card data, it must be encrypted, etc.

Stripe

Stripe, luckily has changed all that as one of the first pioneers in this line of payment processing. You don't need a merchant account or gateway - which is a huge advantage.

Stripe also has the advantage of having a test mode that will allow you to test the payment pipeline while in the prototyping phase. Stripe will connect to your bank account and submit funds recieved on a 7 day rolling basis.

Stripe.js

If you incorporate Stripe.js in your payment processing, the clients credit card info is never touches your website, it goes straight to Stripe and token is returned. This also allows us to bypass the PCI Compliance standards. The cost is also pretty negligible to have a robust, reliable, safe payment processing system for your online business.

How to create a payment with Stripe

Select Test mode, once signing up

Include Stripe gem and bundle install

Specify your API key, use your Test Secret Key and set it a local variable in your rails console.

"Stripe.api_key = '4uhsvjfudjpsdf0u fvpdapo' "

Grab Fake Charge Data from Stripe API docks: Copy code block and enter in Rails console The Charge object, along with attributes will be returned once process.

Remember: currency is always in cents and you don't have to collect address information from customers when using Stripe

Refresh and you should see the charge that was just submitted in your console.

Accept Payments with Checkout

How to get the credit card information from customers to the web application

Using Stripe Checkout Buttom

is simply done with a button and you can add text, as well.

#views/payments/new.html.haml
%p Thank you for you support.
= form_tag payments_path do
  %script.stripe-button{"data-amount" => "2000", "data-description" => "2 widgets ($20.00)", "data-image" => "/128x128.png", "data-key" => "pk_test_------------------------", "data-name" => "Demo Site", :src => "https://checkout.stripe.com/checkout.js"}

Update the following fields: Data amount, Data-name, Description, and remove logo

Create routes

Create '/payements', new and create routes and grab code from API docs

If you run binding.pry in the payments#create action

You will see that one of the params submitted to your server is the stripe token - which is the only indication that credit card info has been submitted.

class PaymentsController < ApplicationController

def create
  Stripe.api_key = "sk_test_FKBLHWUFHPWHFO29793772"

  token = paramns[:stripeToken]

  begin
    charge = Stripe::Charge.create(
      :amount => 3000,
      :currency => "usd"
      :card => token
    )
    flash[:success] = "Thank you for your payment"
    redirect_to new_payment_path

  rescue Stripe::CardError => e
    flash[:error] = e.message
    redirect_to new_payment_path
    end
  end
end

Accept Payments with Custom Form using Stripe.js

Sometimes using the Stripe Checkout button is not seamless enough and you want submit payments through a custom form, where the payment form is apart of the UI:

1. Add Stripe at the top of the views template and set publishable key

<script type="text/javascript" src="https://js.stripe.com/v2/"></script>

<script type="text/javascript">Stripe.setPublishableKey("#{ENV['STRIPE_PUBLISHABLE_KEY']}");
</script>

= javascript_include_tag 'payment'

The above code also includes a turns off the submit button once its clicked to disable multiple submissions and then submit the custom form data to stripe for the stripe token and name attributes are set to nil, which means the data being submit will not be posted to the database. A hidden field for the StripeToken is generated and then submited to our server.

You should also create a file config/initializers/assets.rb with the following content (assuming your javascript file is called payments.js):

Rails.application.config.assets.precompile += ['payments.js'] This file will help to make sure that you add the payments.js file in the asset pipeline for all environments (dev, test and production), although in the development environment it doesn't really compress the assets for you.

Note: If you are using CircleCI at this point, your build may fail and you may need to set your stripe key environment variable on the CircleCI server as well, so it'll have access to it. (Project Settings -> Environment Variables)

Sandi Metz - The Rules

  1. Class can have no more than 100 lines

  2. A method can contain no more than 5 lines

  3. Can be passed no more than 4, more like 3 parameters

  4. A controller action can only pass one instance variable

  5. No more than 2 class names per controller action

Make smaller things and they should know as little about each other as possible

When looking at large chucks of code, its okay to do the "squint method" **1. Changes in shape means you have nested conditionals and it will be very hard to reason about

**2. Changes in color signify different levels of abstraction means that it be hard to follow

Duplication is far cheaper than the wrong abstraction

When code can know less, we can do more. Inheritance is okay when the subclasses - shallow, narrow hiearchry - subclasses to be at the leaf nodes of the object graph - subclasses use all the behavior of the superclass

Case Statements are made for business logic and not configuration - extract configuration into a hash.

Refactoring - Dealing wihth Generalization

1. Pull Up Method

You have methods with identical results on subclasses. Move them to the superclass.

2.Push Down Method

Behavior on a superclass is relevant only for some of its subclasses. Move it to those subclasses

3. Extract Module

You have duplicated behavior in two or more classes. Create a new module and move the relevant behavior from the old class into the module, and include the module in the class.

4. Inline Module

The resultant indirection of the included module is no longer worth the duplication it is preventing. Merge the module into the including class.

5. Extract Subclass

A class has features that are used only in some instances. Create a subclass for that subset of features.

6. Introduce Inheritance

You have two classes with similar features. Make one of the classes a superclass and move the common features to the superclass.

7. Collapse Hierarchy

A superclass and subclass (or module and the class that includes the module) are not very different. Merge them together

8. Form Template Method

You have two methods in subclasses that perform similar steps in the same order, yet the steps are different. Get the steps into methods with the same signature, so that the original methods become the same. Then you can pull them up.

9. Replace Inheritance with Delegation

A subclass uses only part of a superclass interface or does not want to inherit data. Create a field for the superclass, adjust methods to delegate to the superclass, and remove the subclassing. Replace Inheritance with Delegation

10. Replace Abstract Superclass with Module

You have an inheritance hierarchy, but never intend to explicitly instantiate an instance of the superclass. Replace the superclass with a module to better communicate your intention.