├── README ├── README.rdoc ├── VERSION ├── .travis.yml ├── .DS_Store ├── .document ├── app ├── .DS_Store └── controllers │ └── wepay │ ├── application_controller.rb │ ├── ipn_controller.rb │ ├── authorize_controller.rb │ ├── checkout_controller.rb │ ├── charge_controller.rb │ └── preapproval_controller.rb ├── Gemfile ├── lib ├── api │ ├── account_methods.rb │ ├── preapproval_methods.rb │ ├── checkout_methods.rb │ └── charge_methods.rb ├── generators │ └── wepay_rails │ │ └── install │ │ ├── install_generator.rb │ │ └── templates │ │ ├── wepay_checkout_record.rb │ │ ├── create_wepay_checkout_records.rb │ │ └── wepay.yml ├── wepay-rails.rb └── helpers │ └── controller_helpers.rb ├── .gitignore ├── LICENSE.txt ├── test ├── test_wepay_rails_authorize.rb ├── test_wepay_rails_initialize.rb ├── test_wepay_rails_checkout_methods.rb ├── test_wepay_rails_preapproval_methods.rb ├── helper.rb └── test_wepay_rails_account_methods.rb ├── Rakefile ├── Gemfile.lock ├── wepay-rails.gemspec └── README.md /README: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /README.rdoc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 2.6.0 -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - 1.9.2 4 | - 1.9.3 5 | -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthedeveloper/wepay-rails/HEAD/.DS_Store -------------------------------------------------------------------------------- /.document: -------------------------------------------------------------------------------- 1 | lib/**/*.rb 2 | bin/* 3 | - 4 | features/**/*.feature 5 | LICENSE.txt 6 | -------------------------------------------------------------------------------- /app/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamthedeveloper/wepay-rails/HEAD/app/.DS_Store -------------------------------------------------------------------------------- /app/controllers/wepay/application_controller.rb: -------------------------------------------------------------------------------- 1 | class Wepay::ApplicationController < ApplicationController 2 | end -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | # Add dependencies required to use your gem here. 3 | # Example: 4 | # gem "activesupport", ">= 2.3.5" 5 | gem 'httparty' 6 | 7 | # Add dependencies to develop your gem here. 8 | # Include everything needed to run rake, tests, features, etc. 9 | group :development do 10 | gem "rails", "3.1.0" 11 | gem "rake" 12 | gem 'turn', '0.8.2', :require => false 13 | gem 'thor' 14 | gem "bundler" 15 | gem "jeweler", "~> 1.6.4" 16 | gem "rcov", ">= 0" 17 | gem "webmock" 18 | end 19 | -------------------------------------------------------------------------------- /lib/api/account_methods.rb: -------------------------------------------------------------------------------- 1 | module WepayRails 2 | module Api 3 | module AccountMethods 4 | def create_account(params) 5 | self.call_api("/account/create", params) 6 | end 7 | 8 | def get_account(account_id) 9 | self.call_api("/account", {:account_id => account_id}) 10 | end 11 | 12 | def find_account(params={}) 13 | self.call_api("/account/find", params) 14 | end 15 | 16 | def modify_account(account_id, params={}) 17 | self.call_api("/account/modify", params.merge({:account_id => account_id})) 18 | end 19 | 20 | def delete_account(account_id) 21 | self.call_api("/account/delete", {:account_id => account_id}) 22 | end 23 | 24 | def get_account_balance(account_id) 25 | self.call_api("/account/balance", {:account_id => account_id}) 26 | end 27 | end 28 | end 29 | end -------------------------------------------------------------------------------- /app/controllers/wepay/ipn_controller.rb: -------------------------------------------------------------------------------- 1 | class Wepay::IpnController < Wepay::ApplicationController 2 | include WepayRails::Payments 3 | def update 4 | record = WepayCheckoutRecord.find_by_checkout_id_and_security_token(params[:checkout_id],params[:security_token]) 5 | 6 | if record.present? 7 | wepay_gateway = WepayRails::Payments::Gateway.new 8 | 9 | if record.checkout_id.present? 10 | checkout = wepay_gateway.lookup_checkout(record.checkout_id) 11 | else 12 | checkout = wepay_gateway.lookup_preapproval(record.preapproval_id) 13 | end 14 | checkout.delete_if {|k,v| !record.attributes.include? k.to_s} 15 | record.update_attributes(checkout) 16 | render :text => "ok" 17 | else 18 | raise StandardError.new("Wepay IPN: No record found for checkout_id #{params[:checkout_id]} and security_token #{params[:security_token]}") 19 | end 20 | 21 | end 22 | end -------------------------------------------------------------------------------- /app/controllers/wepay/authorize_controller.rb: -------------------------------------------------------------------------------- 1 | class Wepay::AuthorizeController < Wepay::ApplicationController 2 | 3 | def index 4 | wepay_gateway = WepayRails::Payments::Gateway.new 5 | 6 | # For security purposes, stop people from hitting this page and resetting the access_token. 7 | if wepay_gateway.configuration[:access_token].present? 8 | render :text => "You have already specified an access token in wepay.yml. If you wish to change it, please delete the current one and try again." 9 | return 10 | end 11 | 12 | if params[:code].present? 13 | access_token = wepay_gateway.get_access_token(params[:code], redirect_uri) 14 | render :text => "Copy this access token, #{access_token} to the access_token directive in your wepay.yml and restart your app." 15 | else 16 | redirect_to wepay_gateway.auth_code_url redirect_uri 17 | end 18 | end 19 | 20 | private 21 | def redirect_uri 22 | "#{WepayRails::Configuration.settings[:root_callback_uri]}/wepay/authorize" 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # rcov generated 2 | coverage 3 | 4 | # rdoc generated 5 | rdoc 6 | 7 | # yard generated 8 | doc 9 | .yardoc 10 | 11 | # bundler 12 | .bundle 13 | 14 | # jeweler generated 15 | pkg 16 | 17 | # Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore: 18 | # 19 | # * Create a file at ~/.gitignore 20 | # * Include files you want ignored 21 | # * Run: git config --global core.excludesfile ~/.gitignore 22 | # 23 | # After doing this, these files will be ignored in all your git projects, 24 | # saving you from having to 'pollute' every project you touch with them 25 | # 26 | # Not sure what to needs to be ignored for particular editors/OSes? Here's some ideas to get you started. (Remember, remove the leading # of the line) 27 | # 28 | # For MacOS: 29 | # 30 | #.DS_Store 31 | .idea 32 | .rvmrc 33 | 34 | # For TextMate 35 | #*.tmproj 36 | #tmtags 37 | 38 | # For emacs: 39 | #*~ 40 | #\#* 41 | #.\#* 42 | 43 | # For vim: 44 | *.swp 45 | 46 | # For redcar: 47 | #.redcar 48 | 49 | # For rubinius: 50 | #*.rbc 51 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 Adam Medeiros 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /lib/generators/wepay_rails/install/install_generator.rb: -------------------------------------------------------------------------------- 1 | require 'rails/generators/migration' 2 | 3 | module WepayRails 4 | module Generators 5 | class InstallGenerator < ::Rails::Generators::Base 6 | include Rails::Generators::Migration 7 | source_root File.expand_path('../templates', __FILE__) 8 | desc "add a migration for the Wepay Rails - WepayCheckoutRecord Model - Used to capture your transactions from Wepay" 9 | def self.next_migration_number(path) 10 | unless @prev_migration_nr 11 | @prev_migration_nr = Time.now.utc.strftime("%Y%m%d%H%M%S").to_i 12 | else 13 | @prev_migration_nr += 1 14 | end 15 | @prev_migration_nr.to_s 16 | end 17 | 18 | def setup_routes 19 | route "WepayRails.routes(self)" 20 | end 21 | 22 | def copy_migrations 23 | migration_template "create_wepay_checkout_records.rb", "db/migrate/create_wepay_checkout_records.rb" 24 | copy_file "wepay_checkout_record.rb", "app/models/wepay_checkout_record.rb" 25 | copy_file "wepay.yml", "config/wepay.yml.example" 26 | end 27 | end 28 | end 29 | end -------------------------------------------------------------------------------- /lib/generators/wepay_rails/install/templates/wepay_checkout_record.rb: -------------------------------------------------------------------------------- 1 | class WepayCheckoutRecord < ActiveRecord::Base 2 | attr_accessible :amount, 3 | :short_description, 4 | :access_token, 5 | :checkout_id, 6 | :security_token, 7 | :checkout_uri, 8 | :account_id, 9 | :currency, 10 | :fee_payer, 11 | :state, 12 | :redirect_uri, 13 | :auto_capture, 14 | :app_fee, 15 | :gross, 16 | :fee, 17 | :callback_uri, 18 | :tax, 19 | :payer_email, 20 | :payer_name, 21 | :mode, 22 | :preapproval_id, 23 | :preapproval_uri, 24 | :reference_id, 25 | :period, 26 | :frequency, 27 | :start_time, 28 | :end_time, 29 | :auto_recur, 30 | :create_time, 31 | :manage_uri 32 | end -------------------------------------------------------------------------------- /app/controllers/wepay/checkout_controller.rb: -------------------------------------------------------------------------------- 1 | class Wepay::CheckoutController < Wepay::ApplicationController 2 | include WepayRails::Payments 3 | 4 | def index 5 | record = WepayCheckoutRecord.find_by_checkout_id_and_security_token(params[:checkout_id],params[:security_token]) 6 | 7 | if record.present? 8 | wepay_gateway = WepayRails::Payments::Gateway.new( record.access_token ) 9 | checkout = wepay_gateway.lookup_checkout(record.checkout_id) 10 | 11 | # remove unnecessary attributes 12 | checkout.delete_if {|k,v| !record.attributes.include? k.to_s} 13 | 14 | record.update_attributes(checkout) 15 | redirect_to "#{wepay_gateway.configuration[:after_checkout_redirect_uri]}?checkout_id=#{params[:checkout_id]}" 16 | else 17 | raise StandardError.new("Wepay IPN: No record found for checkout_id #{params[:checkout_id]} and security_token #{params[:security_token]}") 18 | end 19 | end 20 | 21 | def new 22 | # create the checkout - This is TEST info from Wepay only 23 | checkout_params = { 24 | :amount => '255.00', 25 | :short_description => 'A transaction for WePay Testing', 26 | :type => 'GOODS' 27 | } 28 | # Finally, send the user off to wepay so you can get paid! - CASH MONEY 29 | init_checkout_and_send_user_to_wepay(checkout_params) 30 | end 31 | 32 | end -------------------------------------------------------------------------------- /test/test_wepay_rails_authorize.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path(File.dirname(__FILE__) + '/helper') 2 | 3 | class TestWepayRailsAuthorize < ActiveSupport::TestCase 4 | def setup 5 | create_wepay_config_file(false, true) 6 | initialize_wepay_config 7 | end 8 | 9 | def teardown 10 | delete_wepay_config_file 11 | end 12 | 13 | test "WePay gateway should have correct authorize url" do 14 | @gateway = WepayRails::Payments::Gateway.new 15 | @url = @gateway.auth_code_url("http://www.example.com") 16 | assert @url.match("https://stage.wepay.com/v2/oauth2/authorize"), " expected but was #{@url}" 17 | end 18 | 19 | test "should raise errors when authorizing an invalid auth code" do 20 | @gateway = WepayRails::Payments::Gateway.new("notAnAccessToken") 21 | stub_request(:post, "https://stage.wepayapi.com/v2/oauth2/token"). 22 | with(:body => "client_id=168738&client_secret=8d701ad2ac&redirect_uri=http%3A%2F%2Fwww.example.com&code=authCode", 23 | :headers => {'Authorization'=>'Bearer: notAnAccessToken', 'User-Agent'=>'WepayRails'}). 24 | to_return(:status => 200, :body => {:error_description => 'invalid code parameter'}.to_json, :headers => {}) 25 | assert_raise WepayRails::Exceptions::AccessTokenError do 26 | @gateway.get_access_token("authCode", "http://www.example.com") 27 | end 28 | end 29 | 30 | # ToDo: Add test for successful authorization 31 | end -------------------------------------------------------------------------------- /test/test_wepay_rails_initialize.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path(File.dirname(__FILE__) + '/helper') 2 | 3 | class TestWepayRailsInitialize < ActiveSupport::TestCase 4 | def teardown 5 | delete_wepay_config_file 6 | end 7 | 8 | test "should initialize WepayRails with settings from wepay.yml" do 9 | create_wepay_config_file 10 | initialize_wepay_config 11 | @gateway = WepayRails::Payments::Gateway.new 12 | 13 | assert_not_nil @gateway.configuration 14 | assert_equal "http://www.example.com", @gateway.configuration[:root_callback_uri] 15 | end 16 | 17 | test "should initialize WepayRails with embedded Ruby in wepay.yml.erb" do 18 | create_wepay_config_file(true) 19 | initialize_wepay_config 20 | @gateway = WepayRails::Payments::Gateway.new 21 | 22 | assert_not_nil @gateway.configuration 23 | assert_equal "http://www.example.com", @gateway.configuration[:root_callback_uri] 24 | assert_equal 'abc' * 3, @gateway.access_token 25 | end 26 | 27 | test "should initialize WepayRails with an existing access_token" do 28 | @gateway = WepayRails::Payments::Gateway.new("myAccessToken") 29 | assert_equal "myAccessToken", @gateway.access_token 30 | end 31 | 32 | test "should raise error when WePay times out" do 33 | # In this test we simply pass 1 (ie 1 millisecond)) as our third (optional) value for timeout, 34 | # basically forcing the request to timeout 35 | assert_raise WepayRails::Exceptions::WepayApiError do 36 | wepay_gateway.call_api("account/find", {}, 1) 37 | end 38 | end 39 | end -------------------------------------------------------------------------------- /test/test_wepay_rails_checkout_methods.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path(File.dirname(__FILE__) + '/helper') 2 | 3 | class TestWepayRailsCheckoutMethods < ActiveSupport::TestCase 4 | include WepayRails::Helpers::ControllerHelpers 5 | 6 | def setup 7 | create_wepay_config_file(false, true) 8 | initialize_wepay_config 9 | @checkout_params = { 10 | :amount => 100, 11 | :short_description => "This is a checkout test!", 12 | :account_id => "12345" 13 | } 14 | end 15 | 16 | def teardown 17 | delete_wepay_config_file 18 | end 19 | 20 | test "should create a new WePay checkout object" do 21 | security_token = Digest::SHA2.hexdigest("1#{Time.now.to_i}") 22 | stub_request(:post, "https://stage.wepayapi.com/v2/checkout/create"). 23 | with(:body => "callback_uri=http%3A%2F%2Fwww.example.com%2Fwepay%2Fipn%3Fsecurity_token%3D#{security_token}&redirect_uri=http%3A%2F%2Fwww.example.com%2Fwepay%2Fcheckout%3Fsecurity_token%3D#{security_token}&fee_payer=Payee&type=GOODS&charge_tax=0&app_fee=0&auto_capture=1&require_shipping=0&shipping_fee=0&account_id=12345&amount=100&short_description=This%20is%20a%20checkout%20test!", 24 | :headers => wepay_gateway.wepay_auth_header). 25 | to_return(:status => 200, :body => sample_checkout_response, :headers => {}) 26 | 27 | @response = wepay_gateway.perform_checkout(@checkout_params) 28 | assert_equal "6789", @response[:checkout_id] 29 | assert_equal "http://stage.wepay.com/api/checkout/6789", @response[:checkout_uri] 30 | assert_not_nil @response[:security_token] 31 | end 32 | end 33 | 34 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'rubygems' 4 | require 'bundler' 5 | begin 6 | Bundler.setup(:default, :development) 7 | rescue Bundler::BundlerError => e 8 | $stderr.puts e.message 9 | $stderr.puts "Run `bundle install` to install missing gems" 10 | exit e.status_code 11 | end 12 | require 'rake' 13 | 14 | require 'jeweler' 15 | Jeweler::Tasks.new do |gem| 16 | # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options 17 | gem.name = "wepay-rails" 18 | gem.homepage = "http://github.com/adamthedeveloper/wepay-rails" 19 | gem.license = "MIT" 20 | gem.summary = "Rails gem that interfaces with the WePay API" 21 | gem.description = "Rails gem that interfaces with the WePay API" 22 | gem.email = "adammede@gmail.com" 23 | gem.authors = ["Adam Medeiros"] 24 | # dependencies defined in Gemfile 25 | end 26 | Jeweler::RubygemsDotOrgTasks.new 27 | 28 | require 'rake/testtask' 29 | Rake::TestTask.new(:test) do |test| 30 | test.libs << 'lib' << 'test' 31 | test.pattern = 'test/**/test_*.rb' 32 | test.verbose = true 33 | end 34 | 35 | require 'rcov/rcovtask' 36 | Rcov::RcovTask.new do |test| 37 | test.libs << 'test' 38 | test.pattern = 'test/**/test_*.rb' 39 | test.verbose = true 40 | test.rcov_opts << '--exclude "gems/*"' 41 | end 42 | 43 | task :default => :test 44 | 45 | require 'rdoc/task' 46 | RDoc::Task.new do |rdoc| 47 | version = File.exist?('VERSION') ? File.read('VERSION') : "" 48 | 49 | rdoc.rdoc_dir = 'rdoc' 50 | rdoc.title = "wepay-rails #{version}" 51 | rdoc.rdoc_files.include('README*') 52 | rdoc.rdoc_files.include('lib/**/*.rb') 53 | end 54 | -------------------------------------------------------------------------------- /test/test_wepay_rails_preapproval_methods.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path(File.dirname(__FILE__) + '/helper') 2 | 3 | class TestWepayRailsPreapprovalMethods < ActiveSupport::TestCase 4 | include WepayRails::Helpers::ControllerHelpers 5 | 6 | def setup 7 | create_wepay_config_file(false, true) 8 | initialize_wepay_config 9 | @checkout_params = { 10 | :amount => 300, 11 | :period => "once", 12 | :short_description => "This is a preapproval test!", 13 | :account_id => "12345" 14 | } 15 | end 16 | 17 | def teardown 18 | delete_wepay_config_file 19 | end 20 | 21 | test "should create a new WePay preapproval object" do 22 | 23 | security_token = Digest::SHA2.hexdigest("1#{Time.now.to_i}") 24 | stub_request(:post, "https://stage.wepayapi.com/v2/preapproval/create"). 25 | with(:body => "callback_uri=http%3A%2F%2Fwww.example.com%2Fwepay%2Fipn%3Fsecurity_token%3D#{security_token}&redirect_uri=http%3A%2F%2Fwww.example.com%2Fpreapproval%2Fsuccess%3Fsecurity_token%3D#{security_token}&fee_payer=Payee&charge_tax=0&app_fee=0&require_shipping=0&shipping_fee=0&account_id=12345&amount=300&period=once&short_description=This%20is%20a%20preapproval%20test!", 26 | :headers => {'Authorization'=>'Bearer: 1c69cebd40ababb0447700377dd7751bb645e874edac140f1ba0c35ad6e98c97', 'User-Agent'=>'WepayRails'}). 27 | to_return(:status => 200, :body => sample_preapproval_response, :headers => {}) 28 | 29 | @response = wepay_gateway.perform_preapproval(@checkout_params) 30 | assert_equal "6789", @response[:preapproval_id] 31 | assert_equal "http://stage.wepay.com/api/preapproval/6789", @response[:preapproval_uri] 32 | assert_not_nil @response[:security_token] 33 | end 34 | end -------------------------------------------------------------------------------- /lib/generators/wepay_rails/install/templates/create_wepay_checkout_records.rb: -------------------------------------------------------------------------------- 1 | class CreateWepayCheckoutRecords < ActiveRecord::Migration 2 | def self.up 3 | 4 | create_table :wepay_checkout_records do |t| 5 | t.integer :checkout_id 6 | t.integer :preapproval_id 7 | t.integer :account_id 8 | t.string :state 9 | t.string :short_description 10 | t.text :long_description 11 | t.string :currency 12 | t.decimal :amount 13 | t.decimal :app_fee 14 | t.string :fee_payer 15 | t.decimal :gross 16 | t.decimal :fee 17 | t.string :reference_id 18 | t.text :redirect_uri 19 | t.text :callback_uri 20 | t.text :checkout_uri 21 | t.text :preapproval_uri 22 | t.string :payer_email 23 | t.string :payer_name 24 | t.text :cancel_reason 25 | t.text :refund_reason 26 | t.boolean :auto_capture 27 | t.boolean :require_shipping 28 | t.text :shipping_address 29 | t.decimal :tax 30 | t.string :security_token 31 | t.string :access_token 32 | t.string :mode 33 | t.string :period 34 | t.integer :frequency 35 | t.datetime :start_time 36 | t.datetime :end_time 37 | t.string :manage_uri 38 | t.datetime :create_time 39 | t.boolean :auto_recur 40 | 41 | t.timestamps 42 | end 43 | 44 | add_index :wepay_checkout_records, :checkout_id 45 | add_index :wepay_checkout_records, :preapproval_id 46 | end 47 | 48 | def self.down 49 | drop_table :wepay_checkout_records 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /app/controllers/wepay/charge_controller.rb: -------------------------------------------------------------------------------- 1 | class Wepay::ChargeController < Wepay::ApplicationController 2 | include WepayRails::Payments 3 | 4 | def index 5 | record = WepayCheckoutRecord.find_by_checkout_id_and_security_token(params[:checkout_id],params[:security_token]) 6 | 7 | if record.present? 8 | wepay_gateway = WepayRails::Payments::Gateway.new( record.access_token ) 9 | charge = wepay_gateway.lookup_preapproval(record.preapproval_id) 10 | 11 | # remove unnecessary attributes 12 | charge.delete_if {|k,v| !record.attributes.include? k.to_s} 13 | 14 | record.update_attributes(charge) 15 | redirect_to "#{wepay_gateway.configuration[:after_charge_redirect_uri]}?checkout_id=#{params[:checkout_id]}" 16 | else 17 | raise StandardError.new("Wepay IPN: No record found for checkout_id #{params[:checkout_id]} and security_token #{params[:security_token]}") 18 | end 19 | end 20 | 21 | def success 22 | response = WepayCheckoutRecord.find(:last) 23 | wepay_gateway = WepayRails::Payments::Gateway.new( response.access_token ) 24 | charge = wepay_gateway.lookup_checkout(response.checkout_id) 25 | 26 | response.update_attributes(charge) 27 | logger.info params 28 | render :text => "Wepay charge OK from #{response.payer_email} with Checkout ID # #{response.checkout_id} from the Pre-approval ID # #{response.preapproval_id}." 29 | end 30 | 31 | def new 32 | # The following is from the Wepay API sample checkout call 33 | # create the checkout 34 | charge_params = { 35 | :amount => '50.00', 36 | :short_description => 'A Checkout on the Wepay PreApproval.', 37 | :type => 'GOODS', 38 | :preapproval_id => xxxxx # To test, be sure to manually add the preapproval_id from the preapproval response which will skip having to go to Wepay to add CC info. 39 | } 40 | # Finally, send user to charge on the preapproval. 41 | init_charge_and_return_success(charge_params) 42 | end 43 | end -------------------------------------------------------------------------------- /app/controllers/wepay/preapproval_controller.rb: -------------------------------------------------------------------------------- 1 | class Wepay::PreapprovalController < Wepay::ApplicationController 2 | include WepayRails::Payments 3 | 4 | def index 5 | record = WepayCheckoutRecord.find_by_preapproval_id_and_security_token(params[:preapproval_id],params[:security_token]) 6 | 7 | 8 | if record.present? 9 | wepay_gateway = WepayRails::Payments::Gateway.new ( record.access_token ) 10 | preapproval = wepay_gateway.lookup_preapproval(record.preapproval_id) 11 | 12 | #remove unneccesary attributes 13 | preapproval.delete_if {|k,v| !record.attributes.include? k.to_s} 14 | 15 | record.update_attributes(preapproval) 16 | redirect_to "#{wepay_gateway.configuration[:after_preapproval_redirect_uri]}?preapproval_id=#{params[:preapproval_id]}" 17 | else 18 | raise StandardError.new("Wepay IPN: No record found for preapproval_id #{params[:preapproval_id]} and security_token #{params[:security_token]}") 19 | end 20 | end 21 | 22 | def success 23 | response = WepayCheckoutRecord.find(:last) 24 | wepay_gateway = WepayRails::Payments::Gateway.new( response.access_token ) 25 | charge = wepay_gateway.lookup_preapproval(response.preapproval_id) 26 | 27 | response.update_attributes(charge) 28 | logger.info params 29 | render :text => "PRE-APPROVAL OK from #{response.payer_email} with Pre-approval ID # #{response.preapproval_id}. You can use this Pre-approval Id# to run a charge at a later time." 30 | end 31 | 32 | def new 33 | # create the preapproval - This is TEST info 34 | preapproval_params = { 35 | :period => 'once', 36 | :end_time => '2013-12-25', 37 | :amount => '50.00', 38 | :mode => 'regular', 39 | :short_description => 'A Preapproval for MyApp.', 40 | :app_fee => "10", 41 | :fee_payer => 'payee', 42 | :payer_email_message => "You just approved MyApp to charge you for a some money later. You have NOT been charged at this time!" 43 | } 44 | # Finally, send the user off to wepay for the preapproval 45 | init_preapproval_and_send_user_to_wepay(preapproval_params) 46 | end 47 | end -------------------------------------------------------------------------------- /lib/generators/wepay_rails/install/templates/wepay.yml: -------------------------------------------------------------------------------- 1 | production: 2 | client_id: 3 | client_secret: 4 | account_id: 5 | access_token: 6 | root_callback_uri: "http://www.example.com" 7 | after_checkout_redirect_uri: "http://www.example.com/purchase/finalize" 8 | after_preapproval_redirect_uri: "http://www.example.com/preapproval/success" 9 | after_charge_redirect_uri: "http://www.example.com/charge/success" 10 | scope: [manage_accounts,refund_payments,collect_payments,view_balance,view_user,preapprove_payments] 11 | wepay_ui_endpoint: "https://www.wepay.com/v2" 12 | wepay_api_endpoint: "https://wepayapi.com/v2" 13 | fee_payer: Payee 14 | checkout_type: GOODS 15 | charge_tax: false 16 | app_fee: 0 17 | auto_capture: true 18 | require_shipping: false 19 | shipping_fee: 0 20 | charge_tax: false 21 | development: 22 | client_id: 23 | client_secret: 24 | account_id: 25 | access_token: 26 | root_callback_uri: "http://www.example.com" 27 | after_checkout_redirect_uri: "http://www.example.com/purchase/finalize" 28 | after_preapproval_redirect_uri: "http://www.example.com/preapproval/success" 29 | after_charge_redirect_uri: "http://www.example.com/charge/success" 30 | scope: [manage_accounts,refund_payments,collect_payments,view_balance,view_user,preapprove_payments] 31 | wepay_ui_endpoint: "https://stage.wepay.com/v2" 32 | wepay_api_endpoint: "https://stage.wepayapi.com/v2" 33 | fee_payer: Payee 34 | checkout_type: GOODS 35 | charge_tax: false 36 | app_fee: 0 37 | auto_capture: true 38 | require_shipping: false 39 | shipping_fee: 0 40 | charge_tax: false 41 | test: 42 | client_id: 43 | client_secret: 44 | account_id: 45 | access_token: 46 | root_callback_uri: "http://www.example.com" 47 | after_checkout_redirect_uri: "http://www.example.com/purchase/finalize" 48 | after_preapproval_redirect_uri: "http://www.example.com/preapproval/success" 49 | after_charge_redirect_uri: "http://www.example.com/charge/success" 50 | scope: [manage_accounts,refund_payments,collect_payments,view_balance,view_user,preapprove_payments] 51 | wepay_ui_endpoint: "https://stage.wepay.com/v2" 52 | wepay_api_endpoint: "https://stage.wepayapi.com/v2" 53 | fee_payer: Payee 54 | checkout_type: GOODS 55 | charge_tax: false 56 | app_fee: 0 57 | auto_capture: true 58 | require_shipping: false 59 | shipping_fee: 0 60 | charge_tax: false -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: http://rubygems.org/ 3 | specs: 4 | actionmailer (3.1.0) 5 | actionpack (= 3.1.0) 6 | mail (~> 2.3.0) 7 | actionpack (3.1.0) 8 | activemodel (= 3.1.0) 9 | activesupport (= 3.1.0) 10 | builder (~> 3.0.0) 11 | erubis (~> 2.7.0) 12 | i18n (~> 0.6) 13 | rack (~> 1.3.2) 14 | rack-cache (~> 1.0.3) 15 | rack-mount (~> 0.8.2) 16 | rack-test (~> 0.6.1) 17 | sprockets (~> 2.0.0) 18 | activemodel (3.1.0) 19 | activesupport (= 3.1.0) 20 | bcrypt-ruby (~> 3.0.0) 21 | builder (~> 3.0.0) 22 | i18n (~> 0.6) 23 | activerecord (3.1.0) 24 | activemodel (= 3.1.0) 25 | activesupport (= 3.1.0) 26 | arel (~> 2.2.1) 27 | tzinfo (~> 0.3.29) 28 | activeresource (3.1.0) 29 | activemodel (= 3.1.0) 30 | activesupport (= 3.1.0) 31 | activesupport (3.1.0) 32 | multi_json (~> 1.0) 33 | addressable (2.3.2) 34 | ansi (1.4.2) 35 | arel (2.2.3) 36 | bcrypt-ruby (3.0.1) 37 | bcrypt-ruby (3.0.1-x86-mingw32) 38 | builder (3.0.0) 39 | crack (0.3.1) 40 | erubis (2.7.0) 41 | git (1.2.5) 42 | hike (1.2.1) 43 | httparty (0.9.0) 44 | multi_json (~> 1.0) 45 | multi_xml 46 | i18n (0.6.0) 47 | jeweler (1.6.4) 48 | bundler (~> 1.0) 49 | git (>= 1.2.5) 50 | rake 51 | json (1.7.3) 52 | mail (2.3.3) 53 | i18n (>= 0.4.0) 54 | mime-types (~> 1.16) 55 | treetop (~> 1.4.8) 56 | mime-types (1.18) 57 | multi_json (1.3.5) 58 | multi_xml (0.5.1) 59 | polyglot (0.3.3) 60 | rack (1.3.6) 61 | rack-cache (1.0.3) 62 | rack (>= 0.4) 63 | rack-mount (0.8.3) 64 | rack (>= 1.0.0) 65 | rack-ssl (1.3.2) 66 | rack 67 | rack-test (0.6.1) 68 | rack (>= 1.0) 69 | rails (3.1.0) 70 | actionmailer (= 3.1.0) 71 | actionpack (= 3.1.0) 72 | activerecord (= 3.1.0) 73 | activeresource (= 3.1.0) 74 | activesupport (= 3.1.0) 75 | bundler (~> 1.0) 76 | railties (= 3.1.0) 77 | railties (3.1.0) 78 | actionpack (= 3.1.0) 79 | activesupport (= 3.1.0) 80 | rack-ssl (~> 1.3.2) 81 | rake (>= 0.8.7) 82 | rdoc (~> 3.4) 83 | thor (~> 0.14.6) 84 | rake (0.9.2) 85 | rcov (0.9.9) 86 | rdoc (3.12) 87 | json (~> 1.4) 88 | sprockets (2.0.4) 89 | hike (~> 1.2) 90 | rack (~> 1.0) 91 | tilt (~> 1.1, != 1.3.0) 92 | thor (0.14.6) 93 | tilt (1.3.3) 94 | treetop (1.4.10) 95 | polyglot 96 | polyglot (>= 0.3.1) 97 | turn (0.8.2) 98 | ansi (>= 1.2.2) 99 | tzinfo (0.3.33) 100 | webmock (1.8.9) 101 | addressable (>= 2.2.7) 102 | crack (>= 0.1.7) 103 | 104 | PLATFORMS 105 | ruby 106 | x86-mingw32 107 | 108 | DEPENDENCIES 109 | bundler 110 | httparty 111 | jeweler (~> 1.6.4) 112 | rails (= 3.1.0) 113 | rake 114 | rcov 115 | thor 116 | turn (= 0.8.2) 117 | webmock 118 | -------------------------------------------------------------------------------- /wepay-rails.gemspec: -------------------------------------------------------------------------------- 1 | # Generated by jeweler 2 | # DO NOT EDIT THIS FILE DIRECTLY 3 | # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec' 4 | # -*- encoding: utf-8 -*- 5 | 6 | Gem::Specification.new do |s| 7 | s.name = "wepay-rails" 8 | s.version = "2.6.0" 9 | 10 | s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= 11 | s.authors = ["Adam Medeiros"] 12 | s.date = "2013-04-18" 13 | s.description = "Rails gem that interfaces with the WePay API" 14 | s.email = "adammede@gmail.com" 15 | s.extra_rdoc_files = [ 16 | "LICENSE.txt", 17 | "README", 18 | "README.md", 19 | "README.rdoc" 20 | ] 21 | s.files = [ 22 | ".DS_Store", 23 | ".document", 24 | ".travis.yml", 25 | "Gemfile", 26 | "Gemfile.lock", 27 | "LICENSE.txt", 28 | "README", 29 | "README.md", 30 | "README.rdoc", 31 | "Rakefile", 32 | "VERSION", 33 | "app/.DS_Store", 34 | "app/controllers/wepay/application_controller.rb", 35 | "app/controllers/wepay/authorize_controller.rb", 36 | "app/controllers/wepay/charge_controller.rb", 37 | "app/controllers/wepay/checkout_controller.rb", 38 | "app/controllers/wepay/ipn_controller.rb", 39 | "app/controllers/wepay/preapproval_controller.rb", 40 | "build", 41 | "lib/api/account_methods.rb", 42 | "lib/api/charge_methods.rb", 43 | "lib/api/checkout_methods.rb", 44 | "lib/api/preapproval_methods.rb", 45 | "lib/generators/wepay_rails/install/install_generator.rb", 46 | "lib/generators/wepay_rails/install/templates/create_wepay_checkout_records.rb", 47 | "lib/generators/wepay_rails/install/templates/wepay.yml", 48 | "lib/generators/wepay_rails/install/templates/wepay_checkout_record.rb", 49 | "lib/helpers/controller_helpers.rb", 50 | "lib/wepay-rails.rb", 51 | "test/helper.rb", 52 | "test/test_wepay_rails_account_methods.rb", 53 | "test/test_wepay_rails_authorize.rb", 54 | "test/test_wepay_rails_checkout_methods.rb", 55 | "test/test_wepay_rails_initialize.rb", 56 | "test/test_wepay_rails_preapproval_methods.rb", 57 | "wepay-rails.gemspec" 58 | ] 59 | s.homepage = "http://github.com/adamthedeveloper/wepay-rails" 60 | s.licenses = ["MIT"] 61 | s.require_paths = ["lib"] 62 | s.rubygems_version = "1.8.24" 63 | s.summary = "Rails gem that interfaces with the WePay API" 64 | 65 | if s.respond_to? :specification_version then 66 | s.specification_version = 3 67 | 68 | if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then 69 | s.add_runtime_dependency(%q, [">= 0"]) 70 | s.add_development_dependency(%q, ["= 3.1.0"]) 71 | s.add_development_dependency(%q, [">= 0"]) 72 | s.add_development_dependency(%q, ["= 0.8.2"]) 73 | s.add_development_dependency(%q, [">= 0"]) 74 | s.add_development_dependency(%q, [">= 0"]) 75 | s.add_development_dependency(%q, ["~> 1.6.4"]) 76 | s.add_development_dependency(%q, [">= 0"]) 77 | s.add_development_dependency(%q, [">= 0"]) 78 | else 79 | s.add_dependency(%q, [">= 0"]) 80 | s.add_dependency(%q, ["= 3.1.0"]) 81 | s.add_dependency(%q, [">= 0"]) 82 | s.add_dependency(%q, ["= 0.8.2"]) 83 | s.add_dependency(%q, [">= 0"]) 84 | s.add_dependency(%q, [">= 0"]) 85 | s.add_dependency(%q, ["~> 1.6.4"]) 86 | s.add_dependency(%q, [">= 0"]) 87 | s.add_dependency(%q, [">= 0"]) 88 | end 89 | else 90 | s.add_dependency(%q, [">= 0"]) 91 | s.add_dependency(%q, ["= 3.1.0"]) 92 | s.add_dependency(%q, [">= 0"]) 93 | s.add_dependency(%q, ["= 0.8.2"]) 94 | s.add_dependency(%q, [">= 0"]) 95 | s.add_dependency(%q, [">= 0"]) 96 | s.add_dependency(%q, ["~> 1.6.4"]) 97 | s.add_dependency(%q, [">= 0"]) 98 | s.add_dependency(%q, [">= 0"]) 99 | end 100 | end 101 | 102 | -------------------------------------------------------------------------------- /test/helper.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'bundler' 3 | begin 4 | Bundler.setup(:default, :development) 5 | rescue Bundler::BundlerError => e 6 | $stderr.puts e.message 7 | $stderr.puts "Run `bundle install` to install missing gems" 8 | exit e.status_code 9 | end 10 | require 'rails/all' 11 | require 'rails/test_help' 12 | require 'thor' 13 | require 'webmock/minitest' 14 | 15 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) 16 | $LOAD_PATH.unshift(File.dirname(__FILE__)) 17 | require 'wepay-rails' 18 | 19 | module WepayRails 20 | class TestsHelper < Thor 21 | include Thor::Actions 22 | source_root File.expand_path(File.dirname(__FILE__)) 23 | 24 | no_tasks do 25 | def create_wepay_config_file(erb=false, defaults=false) 26 | copy_file "../lib/generators/wepay_rails/install/templates/wepay.yml", "../config/wepay.yml#{'.erb' if erb}", verbose: false, force: true 27 | gsub_file "../config/wepay.yml.erb", "", "<%= 'abc' * 3 %>", verbose: false if erb 28 | add_config_defaults(erb) if defaults 29 | end 30 | 31 | def add_config_defaults(erb=false, client_id="168738", client_secret="8d701ad2ac") 32 | gsub_file "../config/wepay.yml#{'.erb' if erb}", "", client_id, verbose: false 33 | gsub_file "../config/wepay.yml#{'.erb' if erb}", "", client_secret, verbose: false 34 | end 35 | 36 | def delete_wepay_config_file 37 | remove_file "../config/wepay.yml", verbose: false 38 | remove_file "../config/wepay.yml.erb", verbose: false 39 | end 40 | end 41 | end 42 | end 43 | 44 | class ActiveSupport::TestCase 45 | TEST_ACCESS_TOKEN = "1c69cebd40ababb0447700377dd7751bb645e874edac140f1ba0c35ad6e98c97" 46 | 47 | def wepay_gateway(token=TEST_ACCESS_TOKEN) 48 | @wepay_gateway ||= WepayRails::Payments::Gateway.new(token) 49 | end 50 | 51 | def helper 52 | @helper ||= WepayRails::TestsHelper.new 53 | end 54 | 55 | def create_wepay_config_file(erb=false, defaults=false) 56 | helper.create_wepay_config_file(erb, defaults) 57 | end 58 | 59 | def delete_wepay_config_file 60 | helper.delete_wepay_config_file 61 | end 62 | 63 | def initialize_wepay_config 64 | yml = "../config/wepay.yml" 65 | if File.exists?(yml) 66 | settings = YAML.load_file(yml)[Rails.env].symbolize_keys 67 | elsif File.exists?(yml+".erb") 68 | settings = YAML::load(ERB.new(IO.read(yml+".erb")).result)[Rails.env].symbolize_keys 69 | end 70 | WepayRails::Configuration.init_conf(settings) 71 | end 72 | 73 | # Stubs for API calls 74 | # Uncomment the next line to allow live API calls 75 | # WebMock.allow_net_connect!(:net_http_connect_on_start => true) 76 | 77 | def sample_account_response(options={}) 78 | { "account_id" => "12345", 79 | "name" => "Example Account", 80 | "description" => "This is just an example WePay account.", 81 | "account_uri" => "https://stage.wepay.com/account/12345" }.merge(options).to_json 82 | end 83 | 84 | def sample_find_response(options={}) 85 | [{ "account_id" => "12345", 86 | "name" => "Custom Reference ID", 87 | "description" => "This is just an example WePay account.", 88 | "reference_id" => "wepayrailstestaccount123", 89 | "account_uri" => "https://stage.wepay.com/account/12345" }.merge(options)].to_json 90 | end 91 | 92 | def sample_balance_response(options={}) 93 | { "pending_balance" => "500", 94 | "available_balance" => "500", 95 | "currency" => "USD" }.merge(options).to_json 96 | end 97 | 98 | def sample_checkout_response(options={}) 99 | { "checkout_id" => "6789", 100 | "checkout_uri" => "http://stage.wepay.com/api/checkout/6789" }.merge(options).to_json 101 | end 102 | 103 | def sample_preapproval_response(options={}) 104 | { "preapproval_id" => "6789", 105 | "preapproval_uri" => "http://stage.wepay.com/api/preapproval/6789" }.merge(options).to_json 106 | end 107 | end -------------------------------------------------------------------------------- /test/test_wepay_rails_account_methods.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path(File.dirname(__FILE__) + '/helper') 2 | 3 | class TestWepayRailsAccountMethods < ActiveSupport::TestCase 4 | 5 | def setup 6 | create_wepay_config_file(false, true) 7 | initialize_wepay_config 8 | end 9 | 10 | def teardown 11 | delete_wepay_config_file 12 | end 13 | 14 | test "should raise error from WePay when using invalid access token" do 15 | stub_request(:post, "https://stage.wepayapi.com/v2/account/create"). 16 | with(:body => "name=Example%20Account&description=This%20is%20just%20an%20example%20WePay%20account.", 17 | :headers => wepay_gateway.wepay_auth_header.merge('Authorization' => "Bearer: notAnAccessToken")). 18 | to_return({:status => 401, :body => {:error_description => 'a valid access_token is required'}.to_json}) 19 | assert_raise WepayRails::Exceptions::ExpiredTokenError do 20 | wepay_gateway = WepayRails::Payments::Gateway.new("notAnAccessToken") 21 | wepay_gateway.create_account({ 22 | :name => "Example Account", 23 | :description => "This is just an example WePay account." 24 | }) 25 | end 26 | end 27 | 28 | test "should create new WePay account" do 29 | stub_request(:post, "https://stage.wepayapi.com/v2/account/create"). 30 | with(:body => "name=Example%20Account&description=This%20is%20just%20an%20example%20WePay%20account.", :headers => wepay_gateway.wepay_auth_header). 31 | to_return({:status => 200, :body => sample_account_response}) 32 | @response = wepay_gateway.create_account({ 33 | :name => "Example Account", 34 | :description => "This is just an example WePay account." 35 | }) 36 | 37 | assert_not_nil @response[:account_id] 38 | assert_not_nil @response[:account_uri] 39 | end 40 | 41 | test "should get WePay account" do 42 | stub_request(:post, "https://stage.wepayapi.com/v2/account"). 43 | with(:body => "account_id=12345", :headers => wepay_gateway.wepay_auth_header). 44 | to_return(:status => 200, :body => sample_account_response) 45 | @response = wepay_gateway.get_account(12345) 46 | 47 | assert_not_nil @response[:name] 48 | assert_equal "Example Account", @response[:name] 49 | assert_equal "This is just an example WePay account.", @response[:description] 50 | end 51 | 52 | test "should find WePay account by reference id or name" do 53 | stub_request(:post, "https://stage.wepayapi.com/v2/account/find"). 54 | with(:body => "reference_id=wepayrailstestaccount123", :headers => wepay_gateway.wepay_auth_header). 55 | to_return({:status => 200, :body => sample_find_response, :headers => {}}) 56 | @response = wepay_gateway.find_account(:reference_id => "wepayrailstestaccount123") 57 | 58 | assert @response.kind_of?(Array), " expected but was <#{@response.class}>" 59 | assert_equal 1, @response.length 60 | assert_equal "Custom Reference ID", @response.first[:name] 61 | end 62 | 63 | test "should find all WePay accounts for current authorized user" do 64 | stub_request(:post, "https://stage.wepayapi.com/v2/account/find"). 65 | with(:headers => wepay_gateway.wepay_auth_header). 66 | to_return({:status => 200, :body => sample_find_response, :headers => {}}) 67 | @response = wepay_gateway.find_account 68 | assert @response.kind_of?(Array), " expected but was <#{@response.class}>" 69 | assert_equal "Custom Reference ID", @response.last[:name] 70 | end 71 | 72 | test "should modify WePay account" do 73 | options = { :name => "This is a new Name!", 74 | :description => "This is a new description!" } 75 | stub_request(:post, "https://stage.wepayapi.com/v2/account/modify"). 76 | with(:headers => wepay_gateway.wepay_auth_header). 77 | to_return({:status => 200, :body => sample_account_response(options), :headers => {}}) 78 | @response = wepay_gateway.modify_account(12345, options) 79 | 80 | assert_not_nil @response[:account_id] 81 | assert_equal "This is a new Name!", @response[:name] 82 | assert_equal "This is a new description!", @response[:description] 83 | end 84 | 85 | test "should get current balance of WePay account" do 86 | stub_request(:post, "https://stage.wepayapi.com/v2/account/balance"). 87 | with(:headers => wepay_gateway.wepay_auth_header). 88 | to_return({:status => 200, :body => sample_balance_response, :headers => {}}) 89 | @response = wepay_gateway.get_account_balance(12345) 90 | 91 | assert_equal "500", @response[:pending_balance] 92 | assert_equal "500", @response[:available_balance] 93 | assert_equal "USD", @response[:currency] 94 | end 95 | 96 | test "should delete WePay account" do 97 | stub_request(:post, "https://stage.wepayapi.com/v2/account/delete"). 98 | with(:body => "account_id=12345", :headers => wepay_gateway.wepay_auth_header). 99 | to_return(:status => 200, :body => sample_account_response, :headers => {}) 100 | @response = wepay_gateway.delete_account(12345) 101 | 102 | assert_not_nil @response[:account_id] 103 | assert_equal "12345", @response[:account_id] 104 | end 105 | end 106 | -------------------------------------------------------------------------------- /lib/api/preapproval_methods.rb: -------------------------------------------------------------------------------- 1 | require 'digest/sha2' 2 | module WepayRails 3 | module Api 4 | module PreapprovalMethods 5 | # Many of the settings you pass in here are already factored in from 6 | # the wepay.yml file and only need to be overridden if you insist on doing 7 | # so when this method is called. The following list of key values are pulled 8 | # in for you from your wepay.yml file: 9 | # 10 | # Note: @wepay_config is your wepay.yml as a Hash 11 | # :callback_uri => @wepay_config[:ipn_callback_uri], 12 | # :redirect_uri => @wepay_config[:checkout_redirect_uri], 13 | # :fee_payer => @wepay_config[:fee_payer], 14 | # :type => @wepay_config[:checkout_type], 15 | # :charge_tax => @wepay_config[:charge_tax] ? 1 : 0, 16 | # :app_fee => @wepay_config[:app_fee], 17 | # :auto_capture => @wepay_config[:auto_capture] ? 1 : 0, 18 | # :require_shipping => @wepay_config[:require_shipping] ? 1 : 0, 19 | # :shipping_fee => @wepay_config[:shipping_fee], 20 | # :charge_tax => @wepay_config[:charge_tax], 21 | # :account_id => wepay_user['account_id'] # wepay-rails goes and gets this for you, but you can override it if you want to. 22 | # 23 | # 24 | # params hash key values possibilities are: 25 | # Parameter: Required: Description: 26 | # :account_id Yes The unique ID of the account you want to create a checkout for. 27 | # :short_description Yes A short description of what is being paid for. 28 | # :long_description No A long description of what is being paid for. 29 | # :type Yes The the checkout type (one of the following: GOODS, SERVICE, DONATION, or PERSONAL) 30 | # :reference_id No The unique reference id of the checkout (set by the application in /checkout/create 31 | # :amount Yes The amount that the payer will pay. 32 | # :app_fee No The amount that the application will receive in fees. 33 | # :fee_payer No Who will pay the fees (WePay's fees and any app fees). Set to "Payer" to charge fees to the person paying (Payer will pay amount + fees, payee will receive amount). Set to "Payee" to charge fees to the person receiving money (Payer will pay amount, Payee will receive amount - fees). Defaults to "Payer". 34 | # :redirect_uri No The uri the payer will be redirected to after paying. 35 | # :callback_uri No The uri that will receive any Instant Payment Notifications sent. Needs to be a full uri (ex https://www.wepay.com ) 36 | # :auto_capture No A boolean value (0 or 1). Default is 1. If set to 0 then the payment will not automatically be released to the account and will be held by WePay in payment state 'reserved'. To release funds to the account you must call /checkout/capture 37 | # :require_shipping No A boolean value (0 or 1). If set to 1 then the payer will be asked to enter a shipping address when they pay. After payment you can retrieve this shipping address by calling /checkout 38 | # :shipping_fee No The amount that you want to charge for shipping. 39 | # :charge_tax No A boolean value (0 or 1). If set to 1 and the account has a relevant tax entry (see /account/set_tax), then tax will be charged. 40 | def perform_preapproval(params) 41 | security_token = Digest::SHA2.hexdigest("#{Rails.env.production? ? rand(4) : 1}#{Time.now.to_i}") # Make less random during tests 42 | 43 | # add the security token to any urls that were passed in from the app 44 | if params[:callback_uri] 45 | params[:callback_uri] = apply_security_token( params[:callback_uri], security_token ) 46 | end 47 | 48 | if params[:redirect_uri] 49 | params[:redirect_uri] = apply_security_token( params[:redirect_uri], security_token ) 50 | end 51 | 52 | defaults = { 53 | :callback_uri => ipn_callback_uri(security_token), 54 | :redirect_uri => preapproval_redirect_uri(security_token), 55 | :fee_payer => @wepay_config[:fee_payer], 56 | :charge_tax => @wepay_config[:charge_tax] ? 1 : 0, 57 | :app_fee => @wepay_config[:app_fee], 58 | :require_shipping => @wepay_config[:require_shipping] ? 1 : 0, 59 | :shipping_fee => @wepay_config[:shipping_fee], 60 | :account_id => @wepay_config[:account_id] 61 | }.merge(params) 62 | 63 | begin 64 | resp = self.call_api("/preapproval/create", defaults) 65 | rescue => e 66 | puts e.message 67 | end 68 | resp.merge({:security_token => security_token}) 69 | end 70 | 71 | def lookup_preapproval(preapproval_id) 72 | co = self.call_api("/preapproval", {:preapproval_id => preapproval_id}) 73 | co.delete("type") 74 | co 75 | end 76 | 77 | def ipn_callback_uri(security_token) 78 | uri = if @wepay_config[:ipn_callback_uri].present? 79 | @wepay_config[:ipn_callback_uri] 80 | else 81 | "#{@wepay_config[:root_callback_uri]}/wepay/ipn" 82 | end 83 | apply_security_token(uri, security_token) 84 | end 85 | 86 | 87 | def preapproval_redirect_uri(security_token) 88 | uri = if @wepay_config[:ipn_callback_uri].present? 89 | @wepay_config[:preapproval_redirect_uri] 90 | else 91 | "#{@wepay_config[:root_callback_uri]}/preapproval/success" #redirect to success action with text - ALT: "#{@wepay_config[:root_callback_uri]}/wepay/preapproval" 92 | end 93 | apply_security_token(uri, security_token) 94 | end 95 | 96 | def apply_security_token(uri, security_token) 97 | uri += (uri =~ /\?/ ? '&' : '?') + "security_token=#{security_token}" 98 | end 99 | end 100 | end 101 | end -------------------------------------------------------------------------------- /lib/api/checkout_methods.rb: -------------------------------------------------------------------------------- 1 | require 'digest/sha2' 2 | module WepayRails 3 | module Api 4 | module CheckoutMethods 5 | # Many of the settings you pass in here are already factored in from 6 | # the wepay.yml file and only need to be overridden if you insist on doing 7 | # so when this method is called. The following list of key values are pulled 8 | # in for you from your wepay.yml file: 9 | # 10 | # Note: @wepay_config is your wepay.yml as a Hash 11 | # :callback_uri => @wepay_config[:ipn_callback_uri], 12 | # :redirect_uri => @wepay_config[:checkout_redirect_uri], 13 | # :fee_payer => @wepay_config[:fee_payer], 14 | # :type => @wepay_config[:checkout_type], 15 | # :charge_tax => @wepay_config[:charge_tax] ? 1 : 0, 16 | # :app_fee => @wepay_config[:app_fee], 17 | # :auto_capture => @wepay_config[:auto_capture] ? 1 : 0, 18 | # :require_shipping => @wepay_config[:require_shipping] ? 1 : 0, 19 | # :shipping_fee => @wepay_config[:shipping_fee], 20 | # :charge_tax => @wepay_config[:charge_tax], 21 | # :account_id => wepay_user['account_id'] # wepay-rails goes and gets this for you, but you can override it if you want to. 22 | # 23 | # 24 | # params hash key values possibilities are: 25 | # Parameter: Required: Description: 26 | # :account_id Yes The unique ID of the account you want to create a checkout for. 27 | # :short_description Yes A short description of what is being paid for. 28 | # :long_description No A long description of what is being paid for. 29 | # :type Yes The the checkout type (one of the following: GOODS, SERVICE, DONATION, or PERSONAL) 30 | # :reference_id No The unique reference id of the checkout (set by the application in /checkout/create 31 | # :amount Yes The amount that the payer will pay. 32 | # :app_fee No The amount that the application will receive in fees. 33 | # :fee_payer No Who will pay the fees (WePay's fees and any app fees). Set to "Payer" to charge fees to the person paying (Payer will pay amount + fees, payee will receive amount). Set to "Payee" to charge fees to the person receiving money (Payer will pay amount, Payee will receive amount - fees). Defaults to "Payer". 34 | # :redirect_uri No The uri the payer will be redirected to after paying. 35 | # :callback_uri No The uri that will receive any Instant Payment Notifications sent. Needs to be a full uri (ex https://www.wepay.com ) 36 | # :auto_capture No A boolean value (0 or 1). Default is 1. If set to 0 then the payment will not automatically be released to the account and will be held by WePay in payment state 'reserved'. To release funds to the account you must call /checkout/capture 37 | # :require_shipping No A boolean value (0 or 1). If set to 1 then the payer will be asked to enter a shipping address when they pay. After payment you can retrieve this shipping address by calling /checkout 38 | # :shipping_fee No The amount that you want to charge for shipping. 39 | # :charge_tax No A boolean value (0 or 1). If set to 1 and the account has a relevant tax entry (see /account/set_tax), then tax will be charged. 40 | def perform_checkout(params) 41 | security_token = Digest::SHA2.hexdigest("#{Rails.env.production? ? rand(4) : 1}#{Time.now.to_i}") # Make less random during tests 42 | 43 | # add the security token to any urls that were passed in from the app 44 | if params[:callback_uri] 45 | params[:callback_uri] = apply_security_token( params[:callback_uri], security_token ) 46 | end 47 | 48 | if params[:redirect_uri] 49 | params[:redirect_uri] = apply_security_token( params[:redirect_uri], security_token ) 50 | end 51 | 52 | defaults = { 53 | :callback_uri => ipn_callback_uri(security_token), 54 | :redirect_uri => checkout_redirect_uri(security_token), 55 | :fee_payer => @wepay_config[:fee_payer], 56 | :type => @wepay_config[:checkout_type], 57 | :charge_tax => @wepay_config[:charge_tax] ? 1 : 0, 58 | :app_fee => @wepay_config[:app_fee], 59 | :auto_capture => @wepay_config[:auto_capture] ? 1 : 0, 60 | :require_shipping => @wepay_config[:require_shipping] ? 1 : 0, 61 | :shipping_fee => @wepay_config[:shipping_fee], 62 | :account_id => @wepay_config[:account_id] 63 | }.merge(params) 64 | 65 | resp = self.call_api("/checkout/create", defaults) 66 | resp.merge({:security_token => security_token}) 67 | end 68 | 69 | def lookup_checkout(checkout_id) 70 | co = self.call_api("/checkout", {:checkout_id => checkout_id}) 71 | co.delete(:type) 72 | co 73 | end 74 | 75 | def lookup_preapproval(preapproval_id) 76 | co = self.call_api("/preapproval", {:preapproval_id => preapproval_id}) 77 | co.delete("type") 78 | co 79 | end 80 | 81 | def ipn_callback_uri(security_token) 82 | uri = if @wepay_config[:ipn_callback_uri].present? 83 | @wepay_config[:ipn_callback_uri] 84 | else 85 | "#{@wepay_config[:root_callback_uri]}/wepay/ipn" 86 | end 87 | apply_security_token(uri, security_token) 88 | end 89 | 90 | def checkout_redirect_uri(security_token) 91 | uri = if @wepay_config[:ipn_callback_uri].present? 92 | @wepay_config[:checkout_redirect_uri] 93 | else 94 | "#{@wepay_config[:root_callback_uri]}/wepay/checkout" 95 | end 96 | apply_security_token(uri, security_token) 97 | end 98 | 99 | def apply_security_token(uri, security_token) 100 | uri += (uri =~ /\?/ ? '&' : '?') + "security_token=#{security_token}" 101 | end 102 | end 103 | end 104 | end 105 | -------------------------------------------------------------------------------- /lib/api/charge_methods.rb: -------------------------------------------------------------------------------- 1 | require 'digest/sha2' 2 | module WepayRails 3 | module Api 4 | module ChargeMethods 5 | # Many of the settings you pass in here are already factored in from 6 | # the wepay.yml file and only need to be overridden if you insist on doing 7 | # so when this method is called. The following list of key values are pulled 8 | # in for you from your wepay.yml file: 9 | # 10 | # Note: @wepay_config is your wepay.yml as a Hash 11 | # :callback_uri => @wepay_config[:ipn_callback_uri], 12 | # :redirect_uri => @wepay_config[:checkout_redirect_uri], 13 | # :fee_payer => @wepay_config[:fee_payer], 14 | # :type => @wepay_config[:checkout_type], 15 | # :charge_tax => @wepay_config[:charge_tax] ? 1 : 0, 16 | # :app_fee => @wepay_config[:app_fee], 17 | # :auto_capture => @wepay_config[:auto_capture] ? 1 : 0, 18 | # :require_shipping => @wepay_config[:require_shipping] ? 1 : 0, 19 | # :shipping_fee => @wepay_config[:shipping_fee], 20 | # :charge_tax => @wepay_config[:charge_tax], 21 | # :account_id => wepay_user['account_id'] # wepay-rails goes and gets this for you, but you can override it if you want to. 22 | # 23 | # 24 | # params hash key values possibilities are: 25 | # Parameter: Required: Description: 26 | # :account_id Yes The unique ID of the account you want to create a checkout for. 27 | # :short_description Yes A short description of what is being paid for. 28 | # :long_description No A long description of what is being paid for. 29 | # :type Yes The the checkout type (one of the following: GOODS, SERVICE, DONATION, or PERSONAL) 30 | # :reference_id No The unique reference id of the checkout (set by the application in /checkout/create 31 | # :amount Yes The amount that the payer will pay. 32 | # :app_fee No The amount that the application will receive in fees. 33 | # :fee_payer No Who will pay the fees (WePay's fees and any app fees). Set to "Payer" to charge fees to the person paying (Payer will pay amount + fees, payee will receive amount). Set to "Payee" to charge fees to the person receiving money (Payer will pay amount, Payee will receive amount - fees). Defaults to "Payer". 34 | # :redirect_uri No The uri the payer will be redirected to after paying. 35 | # :callback_uri No The uri that will receive any Instant Payment Notifications sent. Needs to be a full uri (ex https://www.wepay.com ) 36 | # :auto_capture No A boolean value (0 or 1). Default is 1. If set to 0 then the payment will not automatically be released to the account and will be held by WePay in payment state 'reserved'. To release funds to the account you must call /checkout/capture 37 | # :require_shipping No A boolean value (0 or 1). If set to 1 then the payer will be asked to enter a shipping address when they pay. After payment you can retrieve this shipping address by calling /checkout 38 | # :shipping_fee No The amount that you want to charge for shipping. 39 | # :charge_tax No A boolean value (0 or 1). If set to 1 and the account has a relevant tax entry (see /account/set_tax), then tax will be charged. 40 | def perform_charge(params) 41 | security_token = Digest::SHA2.hexdigest("#{Rails.env.production? ? rand(4) : 1}#{Time.now.to_i}") # Make less random during tests 42 | 43 | # add the security token to any urls that were passed in from the app 44 | if params[:callback_uri] 45 | params[:callback_uri] = apply_security_token( params[:callback_uri], security_token ) 46 | end 47 | 48 | if params[:redirect_uri] 49 | params[:redirect_uri] = apply_security_token( params[:redirect_uri], security_token ) 50 | end 51 | 52 | defaults = { 53 | :callback_uri => ipn_callback_uri(security_token), 54 | :redirect_uri => checkout_redirect_uri(security_token), 55 | :fee_payer => @wepay_config[:fee_payer], 56 | :type => @wepay_config[:checkout_type], 57 | :charge_tax => @wepay_config[:charge_tax] ? 1 : 0, 58 | :app_fee => @wepay_config[:app_fee], 59 | :auto_capture => @wepay_config[:auto_capture] ? 1 : 0, 60 | :require_shipping => @wepay_config[:require_shipping] ? 1 : 0, 61 | :shipping_fee => @wepay_config[:shipping_fee], 62 | :account_id => @wepay_config[:account_id] 63 | }.merge(params) 64 | 65 | resp = self.call_api("/checkout/create", defaults) 66 | resp.merge({:security_token => security_token}) 67 | end 68 | 69 | def lookup_checkout(checkout_id) 70 | co = self.call_api("/checkout", {:checkout_id => checkout_id}) 71 | co.delete(:type) 72 | co 73 | end 74 | 75 | def lookup_preapproval(preapproval_id) 76 | co = self.call_api("/preapproval", {:preapproval_id => preapproval_id}) 77 | co.delete("type") 78 | co 79 | end 80 | 81 | def ipn_callback_uri(security_token) 82 | uri = if @wepay_config[:ipn_callback_uri].present? 83 | @wepay_config[:ipn_callback_uri] 84 | else 85 | "#{@wepay_config[:root_callback_uri]}/wepay/ipn" 86 | end 87 | apply_security_token(uri, security_token) 88 | end 89 | 90 | def charge_redirect_uri(security_token) 91 | uri = if @wepay_config[:ipn_callback_uri].present? 92 | @wepay_config[:charge_redirect_uri] 93 | else 94 | "#{@wepay_config[:root_callback_uri]}/wepay/charge" 95 | end 96 | apply_security_token(uri, security_token) 97 | end 98 | 99 | def checkout_redirect_uri(security_token) 100 | uri = if @wepay_config[:ipn_callback_uri].present? 101 | @wepay_config[:checkout_redirect_uri] 102 | else 103 | "#{@wepay_config[:root_callback_uri]}/wepay/checkout" 104 | end 105 | apply_security_token(uri, security_token) 106 | end 107 | 108 | def apply_security_token(uri, security_token) 109 | uri += (uri =~ /\?/ ? '&' : '?') + "security_token=#{security_token}" 110 | end 111 | end 112 | end 113 | end -------------------------------------------------------------------------------- /lib/wepay-rails.rb: -------------------------------------------------------------------------------- 1 | require 'active_record' 2 | require 'helpers/controller_helpers' 3 | require 'api/account_methods' 4 | require 'api/checkout_methods' 5 | require 'api/preapproval_methods' 6 | require 'api/charge_methods' 7 | require 'httparty' 8 | 9 | module WepayRails 10 | class Configuration 11 | @@settings = nil 12 | 13 | def self.init_conf(settings) 14 | @@settings = settings 15 | end 16 | 17 | def self.settings 18 | @@settings 19 | end 20 | end 21 | 22 | class Engine < Rails::Engine 23 | # Initializers 24 | initializer "WepayRails.initialize_wepay_rails" do |app| 25 | yml = Rails.root.join('config', 'wepay.yml').to_s 26 | if File.exists?(yml) 27 | settings = YAML.load_file(yml)[Rails.env].symbolize_keys 28 | elsif File.exists?(yml+".erb") 29 | settings = YAML::load(ERB.new(IO.read(yml+".erb")).result)[Rails.env].symbolize_keys 30 | end 31 | Configuration.init_conf(settings) 32 | end 33 | end 34 | 35 | class << self 36 | def routes(rails_router) 37 | rails_router.instance_exec do 38 | namespace :wepay do 39 | resources :ipn 40 | resources :authorize 41 | resources :checkout 42 | resources :finalize 43 | resources :preapproval 44 | resources :charge 45 | end 46 | match '/preapproval/success' => 'wepay/preapproval#success' 47 | match '/preapproval/charge' => 'wepay/charge#new' 48 | match '/charge/success' => 'wepay/charge#success' 49 | end 50 | end 51 | end 52 | 53 | module Exceptions 54 | class AccessTokenError < StandardError; end 55 | class ExpiredTokenError < StandardError; end 56 | class InitializeCheckoutError < StandardError; end 57 | class AuthorizationError < StandardError; end 58 | class WepayCheckoutError < StandardError; end 59 | class WepayApiError < StandardError; end 60 | class WepayPreapprovalError < StandardError; end 61 | class WepayChargeError < StandardError; end 62 | end 63 | 64 | module Payments 65 | class Gateway 66 | include HTTParty 67 | base_uri @base_uri 68 | default_timeout 30000 69 | 70 | attr_accessor :access_token 71 | attr_accessor :account_id 72 | 73 | # Pass in the wepay access token that we got after the oauth handshake 74 | # and use it for ongoing communique with Wepay. 75 | # This also relies heavily on there being a wepay.yml file in your 76 | # rails config directory - it must look like this: 77 | def initialize(*args) 78 | @wepay_config = WepayRails::Configuration.settings || {:scope => []} 79 | @access_token = args.first || @wepay_config[:access_token] 80 | @account_id = args.first || @wepay_config[:account_id] 81 | @ui_endpoint = @wepay_config[:wepay_ui_endpoint] || "https://www.wepay.com/v2" 82 | @api_endpoint = @wepay_config[:wepay_api_endpoint] || "https://wepayapi.com/v2" 83 | end 84 | 85 | # Get the auth code url that will be used to fetch the auth code for the customer 86 | # arguments are the redirect_uri and an array of permissions that your application needs 87 | # ex. ['manage_accounts','collect_payments','view_balance','view_user'] 88 | def auth_code_url(redirect_uri, params = {}) 89 | params[:client_id] ||= @wepay_config[:client_id] 90 | params[:scope] ||= @wepay_config[:scope].join(',') 91 | params[:redirect_uri] = redirect_uri 92 | query = params.map { |k, v| "#{k.to_s}=#{v}" }.join('&') 93 | 94 | "#{@ui_endpoint}/oauth2/authorize?#{query}" 95 | end 96 | 97 | def wepay_auth_header 98 | @access_token.blank? ? {'User-Agent' => "WepayRails"} 99 | : {'User-Agent' => "WepayRails", 'Authorization' => "Bearer: #{@access_token}"} 100 | end 101 | 102 | def configuration 103 | @wepay_config 104 | end 105 | 106 | def raise_if_response_error(json) 107 | if json.has_key?(:error) && json.has_key?(:error_description) 108 | if ['invalid code parameter','the code has expired','this access_token has been revoked', 'a valid access_token is required'].include?(json[:error_description]) 109 | raise WepayRails::Exceptions::ExpiredTokenError.new("Token either expired, revoked or invalid: #{json[:error_description]}") 110 | else 111 | raise WepayRails::Exceptions::WepayApiError.new(json[:error_description]) 112 | end 113 | end 114 | end 115 | 116 | def symbolize_response(response) 117 | json = JSON.parse(response) 118 | if json.kind_of? Hash 119 | json.symbolize_keys! and raise_if_response_error(json) 120 | elsif json.kind_of? Array 121 | json.each{|h| h.symbolize_keys!} 122 | end 123 | json 124 | end 125 | 126 | def call_api(api_path, params={}, timeout=30000) 127 | begin 128 | self.class.default_timeout(timeout) 129 | response = self.class.post("#{@api_endpoint}#{api_path}", {:headers => wepay_auth_header, :body => params}) 130 | json = symbolize_response(response.body) 131 | rescue Errno, JSON::ParserError => e 132 | raise WepayRails::Exceptions::WepayApiError.new("The request to WePay timed out. This might mean you sent an invalid request or WePay is having issues.") 133 | rescue => e 134 | raise e if e.class.to_s =~ /WepayRails/ 135 | raise WepayRails::Exceptions::WepayApiError.new("There was an error while trying to connect with WePay - #{e.inspect}") 136 | end 137 | if response.success? 138 | return json 139 | elsif response.code == 401 140 | raise WepayRails::Exceptions::ExpiredTokenError.new("Token either expired, revoked or invalid: #{json.inspect}.") 141 | else 142 | raise WepayRails::Exceptions::WepayApiError.new("The API request failed with error code ##{response.code}: #{json.inspect}.") 143 | end 144 | end 145 | 146 | # Fetch the access token from wepay for the auth code 147 | def get_access_token(auth_code, redirect_uri, callback_uri = nil) 148 | params = { 149 | :client_id => @wepay_config[:client_id], 150 | :client_secret => @wepay_config[:client_secret], 151 | :redirect_uri => redirect_uri, 152 | :code => auth_code, 153 | :callback_uri => callback_uri # Optional field in which you will receive IPNs with the user_id 154 | # when the user revokes an access_token or is deleted. 155 | } 156 | json = call_api("/oauth2/token", params) 157 | raise WepayRails::Exceptions::AccessTokenError.new("A problem occurred trying to get the access token: #{json.inspect}") unless json.has_key?(:access_token) 158 | @account_id = json[:user_id] 159 | @access_token = json[:access_token] 160 | end 161 | 162 | include WepayRails::Api::AccountMethods 163 | include WepayRails::Api::CheckoutMethods 164 | include WepayRails::Api::PreapprovalMethods 165 | include WepayRails::Api::ChargeMethods 166 | end 167 | 168 | include WepayRails::Exceptions 169 | include WepayRails::Helpers::ControllerHelpers 170 | end 171 | 172 | end 173 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wepay-rails 2 | 3 | Wepay-Rails allows your rails app to accept payments with [Wepay](http://www.wepay.com). 4 | 5 | [![build status][travis]][travis-link] 6 | [travis]: https://secure.travis-ci.org/adamthedeveloper/wepay-rails.png?branch=master 7 | [travis-link]: http://travis-ci.org/adamthedeveloper/wepay-rails 8 | 9 | ## Features 10 | 11 | * Added PREAPPROVAL and DELAYED CHARGE ability 09/2012 12 | * Built in IPN that listens to push notifications from wepay and updates saved checkout records for you 13 | * Allows you to fetch your access token easily through a web interface 14 | * Built in API tools to allow you to make all wepay-api calls easily 15 | * Built in ability to send your customers to wepay to make payments and handles their trip back for you OR use iFrame for checkouts (see Wiki https://github.com/adamthedeveloper/wepay-rails/wiki/Using-the-wepay-iframe) 16 | * Saves the current state of every checkout 17 | * Authorize many users to accept payments dynamically (see Wiki https://github.com/adamthedeveloper/wepay-rails/wiki/Authorize-Many-Users-Dynamically) 18 | * Configurable 19 | 20 | ## Installation 21 | 22 | To install it, add this to your Gemfile 23 | 24 | ```ruby 25 | gem 'wepay-rails' 26 | ``` 27 | 28 | To create your WepayCheckoutRecord model and migration: 29 | 30 | ```console 31 | script/rails g wepay_rails:install 32 | ``` 33 | 34 | This will create 3 files for you, a migration file for a table to hold the checkout results, the model and a wepay.yml.example file. Next run: 35 | 36 | ```console 37 | rake db:migrate 38 | ``` 39 | 40 | WepayCheckoutRecord will be updated by wepay's IPN system as changes to the checkout change - such as the status. 41 | Wepay-rails handles those IPN notifications for you. You can write observers watching the WepayCheckoutRecord model if you need to have 42 | something specific occur when the checkout changes. Also, since a model is created for you, you can also track changes to it's state 43 | through state machine or paper trail or some other gem of your liking. Hook up WepayCheckoutRecord how you like. 44 | 45 | Modify config/wepay.yml.example to your needs and copy it to config/wepay.yml. 46 | 47 | Assuming that you have: 48 | 49 | 1. created an account on wepay 50 | 2. created a user to accept the payments 51 | 3. created your application for your account 52 | 4. set your wepay.yml file with the info it needs to talk to the wepay api minus the access_token 53 | 54 | You can now get your access token. 55 | 56 | To fetch your access_token, open a browser and go to: 57 | 58 | ```console 59 | your.railsapp.com/wepay/authorize 60 | ``` 61 | 62 | Login at the prompt or register. You will be sent back to your app and you should have gotten an access_token. Copy it to your wepay.yml 63 | file and restart your app. 64 | 65 | ## Example 66 | 67 | I created a controller called finalize_controller and I use it for a landing page when the customer is finished paying 68 | their order. The other controller I created is a checkout_controller - I send my customers to it when they click checkout 69 | in the cart. Your app is surely different than mine. Do what makes sense to you. 70 | For now, here's a small example... 71 | 72 | ``` 73 | app 74 | |_ controllers 75 | |_ purchase 76 | |_ checkout_controller.rb 77 | |_ finalize_controller.rb 78 | ``` 79 | 80 | Tell wepay-rails where to send the customer after they come back from wepay with a complete payment. Open wepay.yml: 81 | 82 | ```ruby 83 | after_checkout_redirect_uri: "http://www.example.com/purchase/finalize" 84 | ``` 85 | 86 | Create a controller that will send the user to wepay - notice it includes WepayRails::Payments: 87 | 88 | ```ruby 89 | class Purchase::CheckoutController < ApplicationController 90 | include WepayRails::Payments 91 | 92 | def index 93 | cart = current_user.cart # EXAMPLE - get my shopping cart 94 | 95 | checkout_params = { 96 | :amount => cart.grand_total, 97 | :short_description => cart.short_description, 98 | :long_description => cart.long_description, 99 | } 100 | 101 | # Finally, send the user off to wepay so you can get paid! - CASH MONEY 102 | init_checkout_and_send_user_to_wepay(checkout_params) 103 | end 104 | end 105 | ``` 106 | 107 | Finally, the controller I use for finalizing the checkout - AKA - the controller the user is sent back to after his/her trip back from 108 | wepay. A checkout_id is passed in through params so you can access the WepayCheckoutRecord, make a call to 109 | wepay to get the checkout info - whatever you want to do (See the wiki for more info on API calls): 110 | 111 | ```ruby 112 | class Purchase::FinalizeController < ApplicationController 113 | def index 114 | # Fetch the WepayCheckoutRecord that was stored for the checkout 115 | wcr = WepayCheckoutRecord.find_by_checkout_id(params[:checkout_id]) 116 | 117 | # Example: Set the association of the wepay checkout record to my cart - then, on to order. 118 | cart = current_account.cart 119 | cart.wepay_checkout_record = wcr 120 | cart.save! 121 | 122 | # Convert cart to an order?? Move to observer of WepayCheckoutRecord?? 123 | cart.convert_cart_to_order if wcr.state == 'authorized' 124 | 125 | render :text => "Hooray - you bought some widgets!" 126 | end 127 | end 128 | ``` 129 | 130 | ## Example of WePay Oauth 131 | 132 | For reference, please refer to WePay's [documentation on Oauth](https://www.wepay.com/developer/reference/oauth2). 133 | 134 | ### Setup 135 | 136 | As an example, I have the User model, view, and controller. 137 | 138 | I have the following routes for WePay in my `config/routes.rb: 139 | 140 | ```ruby 141 | match 'wepay_connect', to: 'users#wepay_connect' 142 | match 'wepay_auth', to: 'users#wepay_auth' 143 | ``` 144 | 145 | ### Controllers 146 | 147 | The first method that we will hit in this example is `wepay_connect` in my `Users` controller: 148 | 149 | ```ruby 150 | def wepay_connect 151 | wepay_gateway = WepayRails::Payments::Gateway.new 152 | redirect_to wepay_gateway.auth_code_url( wepay_auth_path(current_user, only_path: false) ) 153 | end 154 | ``` 155 | 156 | This will send the user to WePay's `/oauth2/authorize` uri to start the Oauth flow. 157 | 158 | The response is an authorization code, which is used to get the user's access token. 159 | 160 | The next method we will hit is `wepay_auth`: 161 | 162 | ```ruby 163 | def wepay_auth 164 | wepay_gateway = WepayRails::Payments::Gateway.new 165 | access_token = wepay_gateway.get_access_token(params[:code], wepay_auth_path(current_user, :only_path => false) ) 166 | if current_user.update_attributes(wepay_access_token: access_token, wepay_user_id: wepay_gateway.account_id) 167 | flash[:success] = "Your WePay account is now connected!" 168 | redirect_to root_path 169 | end 170 | end 171 | ``` 172 | 173 | ### Start Oauth 174 | 175 | I have this in the view, assuming that a user is currently signed in: 176 | 177 | ```ruby 178 | link_to "Connect To WePay", wepay_connect_path 179 | ``` 180 | 181 | When the user clicks on this link, he will be prompted to start the WePay Oauth flow. 182 | 183 | ## Special Thanks to additional contributers of Wepay-Rails 184 | * lucisferre (Chris Nicola) https://github.com/lucisferre 185 | * mindeavor (Gilbert) https://github.com/mindeavor 186 | * ustorf (Bernd Ustorf) https://github.com/ustorf 187 | * dragonstarwebdesign (Steve Aquino) https://github.com/dragonstarwebdesign 188 | * jules27 (Julie Mao) https://github.com/jules27 189 | 190 | ## Contributing to wepay-rails 191 | 192 | * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet 193 | * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it 194 | * Fork the project 195 | * Start a feature/bugfix branch 196 | * Commit and push until you are happy with your contribution 197 | * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally. 198 | * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it. 199 | 200 | ## Copyright 201 | 202 | Copyright (c) 2011 Adam Medeiros. See LICENSE.txt for further details. 203 | -------------------------------------------------------------------------------- /lib/helpers/controller_helpers.rb: -------------------------------------------------------------------------------- 1 | module WepayRails 2 | module Helpers 3 | module ControllerHelpers 4 | 5 | # Many of the settings you pass in here are already factored in from 6 | # the wepay.yml file and only need to be overridden if you insist on doing 7 | # so when this method is called. The following list of key values are pulled 8 | # in for you from your wepay.yml file: 9 | # 10 | # Note: @wepay_config is your wepay.yml as a Hash 11 | # :callback_uri => @wepay_config[:ipn_callback_uri], 12 | # :redirect_uri => @wepay_config[:checkout_redirect_uri], 13 | # :fee_payer => @wepay_config[:fee_payer], 14 | # :type => @wepay_config[:checkout_type], 15 | # :charge_tax => @wepay_config[:charge_tax] ? 1 : 0, 16 | # :app_fee => @wepay_config[:app_fee], 17 | # :auto_capture => @wepay_config[:auto_capture] ? 1 : 0, 18 | # :require_shipping => @wepay_config[:require_shipping] ? 1 : 0, 19 | # :shipping_fee => @wepay_config[:shipping_fee], 20 | # :charge_tax => @wepay_config[:charge_tax], 21 | # :account_id => wepay_user['account_id'] # wepay-rails goes and gets this for you, but you can override it if you want to. 22 | # 23 | # 24 | # params hash key values possibilities are: 25 | # Parameter: Required: Description: 26 | # :account_id Yes The unique ID of the account you want to create a checkout for. 27 | # :short_description Yes A short description of what is being paid for. 28 | # :long_description No A long description of what is being paid for. 29 | # :type Yes The the checkout type (one of the following: GOODS, SERVICE, DONATION, or PERSONAL) 30 | # :reference_id No The unique reference id of the checkout (set by the application in /checkout/create 31 | # :amount Yes The amount that the payer will pay. 32 | # :app_fee No The amount that the application will receive in fees. 33 | # :fee_payer No Who will pay the fees (WePay's fees and any app fees). Set to "Payer" to charge fees to the person paying (Payer will pay amount + fees, payee will receive amount). Set to "Payee" to charge fees to the person receiving money (Payer will pay amount, Payee will receive amount - fees). Defaults to "Payer". 34 | # :redirect_uri No The uri the payer will be redirected to after paying. 35 | # :callback_uri No The uri that will receive any Instant Payment Notifications sent. Needs to be a full uri (ex https://www.wepay.com ) 36 | # :auto_capture No A boolean value (0 or 1). Default is 1. If set to 0 then the payment will not automatically be released to the account and will be held by WePay in payment state 'reserved'. To release funds to the account you must call /checkout/capture 37 | # :require_shipping No A boolean value (0 or 1). If set to 1 then the payer will be asked to enter a shipping address when they pay. After payment you can retrieve this shipping address by calling /checkout 38 | # :shipping_fee No The amount that you want to charge for shipping. 39 | # :charge_tax No A boolean value (0 or 1). If set to 1 and the account has a relevant tax entry (see /account/set_tax), then tax will be charged. 40 | def init_checkout(params, access_token=nil) 41 | wepay_gateway = WepayRails::Payments::Gateway.new(access_token) 42 | response = wepay_gateway.perform_checkout(params) 43 | 44 | if response[:checkout_uri].blank? 45 | raise WepayRails::Exceptions::WepayCheckoutError.new("An error occurred: #{response.inspect}") 46 | end 47 | 48 | params.merge!({ 49 | :access_token => wepay_gateway.access_token, 50 | :checkout_id => response[:checkout_id], 51 | :security_token => response[:security_token], 52 | :checkout_uri => response[:checkout_uri] 53 | }) 54 | 55 | params.delete_if {|k,v| !WepayCheckoutRecord.attribute_names.include? k.to_s} 56 | 57 | WepayCheckoutRecord.create(params) 58 | end 59 | 60 | def init_checkout_and_send_user_to_wepay(params, access_token=nil) 61 | record = init_checkout(params, access_token) 62 | redirect_to record.checkout_uri and return record 63 | end 64 | 65 | #Parameter Required Format Description 66 | #account_id Yes Number The WePay account where the money will go when you use this pre-approval to execute a payment. 67 | #amount No Number The amount for the pre-approval. The API application can charge up to this amount every period. 68 | #short_description Yes String A short description of what the payer is paying for. 69 | #period Yes String Can be: hourly, daily, weekly, biweekly, monthly, bimonthly, quarterly, yearly, or once. The API application can charge the payer every period. 70 | #reference_id No String The reference id of the pre-approval. Can be any string, but must be unique for the application/user pair. 71 | #app_fee No Number The application fee that will go to the API application's account. 72 | #fee_payer No String Who will pay the WePay fees and app fees (if set). Can be payee or payer. Defaults to payer. 73 | #redirect_uri No String The uri the payer will be redirected to after approving the pre-approval. 74 | #callback_uri No String The uri that any instant payment notifications will be sent to. Needs to be a full uri (ex https://www.wepay.com ) and must NOT be localhost or 127.0.0.1 or include wepay.com. Max 2083 chars. 75 | #require_shipping No Boolean Defaults to false. If set to true then the payer will be require to enter their shipping address when they approve the pre-approval. 76 | #shipping_fee No Number The dollar amount of shipping fees that will be charged. 77 | #charge_tax No Boolean Defaults to false. If set to true then any applicable taxes will be charged. 78 | #payer_email_message No String A short message that will be included in the payment confirmation email to the payer. 79 | #payee_email_message No String A short message that will be included in the payment confirmation email to the payee. 80 | #long_description No String An optional longer description of what the payer is paying for. 81 | #frequency No Number How often per period the API application can charge the payer. 82 | #start_time No Number or String When the API application can start charging the payer. Can be a unix_timestamp or a parse-able date-time. 83 | #end_time No Number or String The last time the API application can charge the payer. Can be a unix_timestamp or a parse-able date-time. The default value is five (5) years from the preapproval creation time. 84 | #auto_recur No Boolean Set to true if you want the payments to automatically execute every period. Useful for subscription use cases. Default value is false. Only the following periods are allowed if you set auto_recur to true: Weekly, Biweekly, Monthly, Quarterly, Yearly 85 | #mode No String What mode the pre-approval confirmation flow will be displayed in. The options are 'iframe' or 'regular'. Choose 'iframe' if this is an iframe pre-approval. Mode defaults to 'regular'. 86 | #prefill_info No Object A JSON object that lets you pre fill certain fields in the pre-approval flow. Allowed fields are 'name', 'email', 'phone_number', 'address', 'city', 'state', 'zip', Pass the prefill-info as a JSON object like so: {"name":"Bill Clerico","phone_number":"855-469-3729"} 87 | #funding_sources No String What funding sources you want to accept for this checkout. Options are: "bank,cc" to accept both bank and cc payments, "cc" to accept just credit card payments, and "bank" to accept just bank payments. 88 | 89 | 90 | def init_preapproval(params, access_token=nil) 91 | wepay_gateway = WepayRails::Payments::Gateway.new(access_token) 92 | response = wepay_gateway.perform_preapproval(params) 93 | 94 | if response[:preapproval_uri].blank? 95 | raise WepayRails::Exceptions::WepayPreapprovalError.new("An error occurred: #{response.inspect}") 96 | end 97 | 98 | params.merge!({ 99 | :access_token => wepay_gateway.access_token, 100 | :preapproval_id => response[:preapproval_id], 101 | :security_token => response[:security_token], 102 | :preapproval_uri => response[:preapproval_uri] 103 | }) 104 | 105 | params.delete_if {|k,v| !WepayCheckoutRecord.attribute_names.include? k.to_s} 106 | 107 | WepayCheckoutRecord.create(params) 108 | end 109 | 110 | def init_preapproval_and_send_user_to_wepay(params, access_token=nil) 111 | record = init_preapproval(params, access_token) 112 | redirect_to record.preapproval_uri and return record 113 | end 114 | 115 | def init_charge(params, access_token=nil) 116 | wepay_gateway = WepayRails::Payments::Gateway.new(access_token) 117 | response = wepay_gateway.perform_charge(params) 118 | 119 | params.merge!({ 120 | :access_token => wepay_gateway.access_token, 121 | :preapproval_id => response[:preapproval_id], 122 | :checkout_id => response[:checkout_id], 123 | :security_token => response[:security_token], 124 | }) 125 | 126 | params.delete_if {|k,v| !WepayCheckoutRecord.attribute_names.include? k.to_s} 127 | 128 | WepayCheckoutRecord.create(params) 129 | end 130 | 131 | def init_charge_and_return_success(params, access_token=nil) 132 | record = init_charge(params, access_token) 133 | redirect_to charge_success_url and return record 134 | end 135 | 136 | 137 | end 138 | end 139 | end 140 | --------------------------------------------------------------------------------