Skip to content

Instantly share code, notes, and snippets.

@matschaffer
Created January 13, 2011 21:53
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save matschaffer/778691 to your computer and use it in GitHub Desktop.
Save matschaffer/778691 to your computer and use it in GitHub Desktop.
Given /^I expect to pay \$(\d+) via amazon$/ do |arg1|
price = arg1.to_f
mock_pipeline_request = mock('pipeline_request',
:url => sekret_payment_path(:transaction_amount => price))
Remit::API.any_instance.
expects(:get_recurring_use_pipeline).
with{ |opts| opts[:transaction_amount] == price }.
returns(mock_pipeline_request)
Remit::PipelineResponse.any_instance.expects(:valid?).returns(true)
Remit::PipelineResponse.any_instance.expects(:successful?).returns(true)
Remit::PipelineResponse.any_instance.expects(:tokenID).returns("1234")
mock_pay_result = mock('pay_result',
:transaction_id => "1234",
:transaction_status => "Pending",
:pending? => true)
mock_pay_response = mock('pay_response',
:pay_result => mock_pay_result,
:request_id => "5678-901",
:successful? => false)
Remit::API.any_instance.expects(:pay).returns(mock_pay_response)
end

Sign up at https://payments.amazon.com

Click "Enter the sandbox" on the bottom left at http://aws.amazon.com/fps/. This will enable your AWS account to create sandbox accounts. Use the "click here" links to create sandbox business and personal accounts.

screenshot

Once you have those accounts you can log into them at: https://payments-sandbox.amazon.com

Use this to check payment logs, etc.

Instant Payment Notification

Under "Edit my account settings" > "Developer and Seller preferences" in business account.

IPN has to be on port 80 or 8080, can't use a different port even for testing

Once you see a payment success, ignore anything that says "PENDING". IPN can come out of order.

class PaymentReceipt < ActiveRecord::Base
belongs_to :user
serialize :pipeline_response_params
default_value_for :amount, 5.00
def caller_reference
[user.id, (created_at || Time.now).to_s(:number)].join(':')
end
def status=(status)
write_attribute(:status, status.to_s.downcase)
end
def successful?
status == "success"
end
def self.remit
@remit ||= Remit::API.new(FPS_ACCESS_KEY, FPS_SECRET_KEY, FPS_SANDBOX)
end
def remit
self.class.remit
end
# Returns a URI object with localhost replaced with 127.0.0.1
# For whatever reason FPS can't validate localhost urls.
# Also the URI object is useful for remit calls that require
# the host/path to get passed separate from the query.
def self.sanitize(url)
uri = URI.parse(url)
uri.host = "127.0.0.1" if uri.host == "localhost"
uri
end
def sanitize(url)
self.class.sanitize(url)
end
def payment_url(return_url)
remit.get_recurring_use_pipeline(
:caller_reference => caller_reference,
:payment_reason => "CallMe - Monthly Subscription",
:recurring_period => "1 Month",
:return_url => sanitize(return_url).to_s,
:transaction_amount => amount
).url
end
def process_pipeline_response(url)
uri = sanitize(url)
# Note that you can't use controller params since they might
# also contain an :id from the URI path.
query = uri.query
uri.query = nil
update_attribute(:pipeline_response_params, query)
pipeline_response = Remit::PipelineResponse.new(uri.to_s, query, remit)
raise "Invalid pipeline response" unless pipeline_response.valid?
raise "Unsuccessful pipeline" unless pipeline_response.successful?
update_attribute(:token_id, pipeline_response.tokenID)
end
def pay
return if transaction_id
pay_request = Remit::Pay::Request.new(
:caller_reference => caller_reference,
:charge_fee_to => "Caller",
:sender_description => "CALLME",
:sender_token_id => token_id,
:transaction_amount => Remit::RequestTypes::Amount.new(
:value => amount,
:currency_code => "USD"))
pay_response = remit.pay(pay_request)
pay_result = pay_response.pay_result
update_attributes(:request_id => pay_response.request_id,
:transaction_id => pay_result.transaction_id,
:status => pay_result.transaction_status)
raise "Payment error" unless pay_result.pending? || pay_response.successful?
end
def pay_from_pipeline(url)
process_pipeline_response(url)
pay
end
def self.update_from_ipn(url, params)
uri = sanitize(url)
ipn_request = Remit::IpnRequest.new(uri.to_s, params, remit)
raise "Invalid IPN signature" unless ipn_request.valid?
transaction_id = ipn_request.transactionId
status = ipn_request.transactionStatus
receipt = PaymentReceipt.find_by_transaction_id(transaction_id)
raise "Receipt for #{transaction_id} was not found" unless receipt
receipt.update_attribute(:status, status) unless receipt.successful?
receipt
end
end
require 'test_helper'
class PaymentReceiptTest < ActiveSupport::TestCase
setup do
@user = users(:mashion)
@receipt = @user.payment_receipts.build
end
test "builds a unique caller reference" do
assert_match /#{@user.id}:/, @receipt.caller_reference
end
test "always represents status as lower case" do
@receipt.status = "PENDING"
assert_equal "pending", @receipt.status
end
test "sanitizes localhost from urls for Amazon's sake" do
assert_equal "http://127.0.0.1:3000/some/path", @receipt.sanitize("http://localhost:3000/some/path").to_s
end
test "generates a payment url" do
return_url = "http://mysite.com/return_url"
mock_pipeline = mock('pipeline', :url => 'fps.blah.blah')
@receipt.remit.expects(:get_recurring_use_pipeline).
with { |opts|
opts[:return_url] == return_url &&
opts[:transaction_amount] == @receipt.amount
}.
returns(mock_pipeline)
url = @receipt.payment_url(return_url)
assert_equal 'fps.blah.blah', url
end
test "processes pipeline responses" do
url = "http://mysite.com/pipeline?tokenID=MSAA...&status=SC"
Remit::PipelineResponse.any_instance.expects(:valid?).returns(true)
@receipt.process_pipeline_response(url)
assert_equal "MSAA...", @receipt.token_id
assert_match /tokenID/, @receipt.pipeline_response_params
end
test "issues pay requests" do
@receipt.token_id = "MSA..."
mock_pay_result = mock('pay_result', :transaction_id => "1234",
:transaction_status => "Pending",
:pending? => true)
mock_pay_response = mock('pay_response', :pay_result => mock_pay_result,
:request_id => "5678")
@receipt.remit.expects(:pay).returns(mock_pay_response)
@receipt.pay
assert_equal "5678", @receipt.request_id
assert_equal "1234", @receipt.transaction_id
assert_equal "pending", @receipt.status
end
test "pay shortcircuits if we already have a transaction id" do
@receipt.transaction_id = "1234"
@receipt.remit.expects(:pay).never
@receipt.pay
end
test "finds and updates on ipn requests" do
url = "http://mysite.com/ipn"
@receipt.update_attribute(:transaction_id, "15KNUM...")
Remit::IpnRequest.any_instance.expects(:valid?).returns(true)
PaymentReceipt.update_from_ipn(url, "transactionId=15KNUM...&transactionStatus=SUCCESS")
@receipt.reload
assert_equal "success", @receipt.status
end
end
class PaymentReceiptsController < ApplicationController
skip_before_filter :verify_authenticity_token, :only => [:ipn]
def ipn
@receipt = PaymentReceipt.update_from_ipn(request.url, params)
@receipt.process_activation(twilio_callback)
render :text => "ok"
end
def show
@receipt = PaymentReceipt.find(params[:id])
@receipt.pay_from_pipeline(request.url)
@receipt.process_activation(twilio_callback)
redirect_to user_path(@receipt.user)
end
end
class SekretController < ApplicationController
def pay
render :inline => <<-ERB
Payment with Amazon would happen here. Amount is <%= params[:transaction_amount] %>.
<%= link_to "Pay", payment_receipt_url(PaymentReceipt.last) %>
ERB
end
end
Feature: User Registration
As a user
I want to provide my information
In order to register for Call You
Background:
Given I am on the new user registration page
Then I should see "Email"
@javascript
Scenario: Forward to Amazon payments page
Given I expect to pay $5 via amazon
When I fill in "Zip code" with "19128"
And I follow "Search"
And I select "(484) 580-9974" from "Incoming phone number"
And I fill in "user_phone_numbers_attributes_0_number" with "555-555-5555"
And I chill a sec
And I fill in "user_email" with "test@example.com"
And I chill a sec
And I fill in "Password" with "password"
And I fill in "One more time" with "password"
And I press "Subscribe"
Then I should see "Amazon"
And I should see "5"
When I follow "Pay"
Then I should see "You Are Done!"
class Users::RegistrationsController < Devise::RegistrationsController
def new
build_resource({})
resource.build_extra_phone_numbers
render_with_scope :new
end
def create
# Copied and pasted from devise/app/controllers/devise/registrations_controller.rb
build_resource
if resource.save
if resource.active?
sign_in resource
redirect_to payment_url(resource)
else
set_flash_message :notice, :inactive_signed_up, :reason => resource.inactive_message.to_s
redirect_to after_inactive_sign_up_path_for(resource)
end
else
resource.build_extra_phone_numbers
clean_up_passwords(resource)
render_with_scope :new
end
end
def payment_url(user)
receipt = user.payment_receipts.create
receipt.payment_url(payment_receipt_url(receipt))
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment