diff --git a/.gitignore b/.gitignore index 9b53a18..a8e13a3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ pkg/ Gemfile.lock *.gem +.project +.idea +.bundle diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..3623d20 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "examples/marketplace/fe"] + path = examples/marketplace/fe + url = git@github.com:PoundPay/simplefe.git diff --git a/README.rdoc b/README.rdoc index 3b3510a..9d299ce 100644 --- a/README.rdoc +++ b/README.rdoc @@ -7,7 +7,7 @@ Poundpay is a payments platform for marketplaces 1. Add the following to your Gemfile - gem 'poundpay', '~> 0.2.0' + gem 'poundpay', '~> 0.3.1' 2. At the command prompt, install the gem with bundler @@ -35,6 +35,13 @@ Poundpay is a payments platform for marketplaces before_filter :verify_poundpay_callback +== Creating a user + + @user = Poundpay::User.create( + :first_name => "Dart", + :last_name => "Master", + :email_address => "dart-master@example.com") + == Creating a payment @@ -47,7 +54,7 @@ Poundpay is a payments platform for marketplaces :description => "Beats by Dr. Dre") -== Serving IFRAME +== Serving the payment IFRAME @@ -79,6 +86,122 @@ Poundpay is a payments platform for marketplaces == Payment methods payment = Poundpay::Payment.find(payment_sid) - payment.escrow # AUTHORIZED -> ESCROWED. Credit card is charged - payment.release # ESCROWED -> RELEASED. Recipient receives money - payment.cancel # ESCROWED -> CANCELED. Payer receives refund \ No newline at end of file + payment.escrow # AUTHORIZED -> ESCROWED. Credit card is charged + payment.release # ESCROWED or PARTIALLY_RELEASED -> RELEASED. Recipient receives money + payment.cancel # ESCROWED or PARTIALLY_RELEASED -> CANCELED. Payer receives refund + Poundpay::Payment.batch_update # Batch update a list of payments. + + +== Creating a charge permission + + @charge_permission = Poundpay::ChargePermission.create( + :email_address => "fred@example.com") + + +== Serving the charge permission IFRAME + + + +
+ + + + +== Charge permission methods + + charge_permission = Poundpay::ChargePermission.find(charge_permission_sid) + charge_permission.deactivate # CREATED or ACTIVE -> INACTIVE. Charge permission is deactivated and can no longer be used to authorize payments for the associated payer. + + +== Batching + +In some cases you may wish to batch authorize and escrow a collection of +payments. By doing so there will be only *one* payer charge for that collection +of payments. Note that if you do batch authorize a collection of payments that +it must *also* be batch escrowed. + +Batching is designed for shopping carts where you want a collection of payments +to appear to appear as a single charge. + +In order to use batching you simply need to pass `sids` for *all* payments in +the collection you want to batch to the IFrame + + + +
+ + + +Alternatively if you are directly authorizing the payments using a charge +permission + + Poundpay::Payment.batch_update( + :sid => [payment1.sid, payment2.sid, payment3.sid], + :status => 'AUTHORIZED') + +Finally you'll need to batch escrow the payments + + Poundpay::Payment.batch_update( + :sid => [payment1.sid, payment2.sid, payment3.sid], + :status => 'ESCROWED') + +Notice that if you did the following instead an error would be triggered since +batched payments *must* be authorized and escrowed collectively + + payment = Poundpay::Payment.find(payment1_sid) + payment.escrow # fails + +However if you cancel some of the payments prior to batch escrow you should +exclude them from the batch call + + payment1 = Poundpay::Payment.find(payment1_sid) + payment1.cancel # ok + + Poundpay::Payment.batch_update( + :sid => [payment2.sid, payment3.sid], + :status => 'ESCROWED') diff --git a/examples/simple_application/.gems b/examples/marketplace/.gems similarity index 100% rename from examples/simple_application/.gems rename to examples/marketplace/.gems diff --git a/examples/simple_application/.rvmrc b/examples/marketplace/.rvmrc similarity index 54% rename from examples/simple_application/.rvmrc rename to examples/marketplace/.rvmrc index eb68572..34b575b 100644 --- a/examples/simple_application/.rvmrc +++ b/examples/marketplace/.rvmrc @@ -1,3 +1,3 @@ # echo 'Switching rvm gemset ...' -rvm use --create --install 1.9.1@poundpay-simple_application >& /dev/null +rvm use --create --install 1.9.2@poundpay-simple_application >& /dev/null # rvm gemset import >& /dev/null diff --git a/examples/simple_application/README b/examples/marketplace/README similarity index 100% rename from examples/simple_application/README rename to examples/marketplace/README diff --git a/examples/marketplace/application.rb b/examples/marketplace/application.rb new file mode 100644 index 0000000..553b185 --- /dev/null +++ b/examples/marketplace/application.rb @@ -0,0 +1,181 @@ +require 'pp' +require 'poundpay' + +require './config' + + +class SimpleController + attr_reader :poundpay_client + + def initialize + if Poundpay.configured? + return + end + config = SimpleApplication::CONFIG[:poundpay] + puts config + Poundpay.configure_from_hash(config) + end + + def return_404 + response = Rack::Response.new(["Page Not Found"], 404, {"Content-Type" => "text/plain"}) + response.finish + end + +end + +class Index < SimpleController + + def call env + request = Rack::Request.new(env) + unless request.path == '/' and request.get? + return return_404 + end + # Create payment request + payment = SimpleApplication::CONFIG[:default_payment] + # Render and return page + www_poundpay_url = Poundpay.www_url + template = ERB.new(open("index.html.erb").read) + page = template.result(binding) + response = Rack::Response.new([page], 200, {"Content-Type" => "text/html"}) + response.finish + end + +end + +class Payment < SimpleController + + def call env + request = Rack::Request.new(env) + return_value, mime_type = case request.path.gsub(/\/$/, '') # trim trailing / + when '/payment' then request.post? ? create(request) : [nil, nil] + when '/payment/release' then request.post? ? release(request) : [nil, nil] + when '/payment/authorize' then request.post? ? authorize(request) : [nil, nil] + when '/payment/cancel' then request.post? ? cancel(request) : [nil, nil] + when '/payment/escrow' then request.post? ? escrow(request) : [nil, nil] + else nil + end + + if return_value + response = Rack::Response.new([return_value], 201, {"Content-Type" => mime_type}) + response.finish + else + return_404 + end + end + + def create request + payment = Poundpay::Payment.create(request.POST) + payment.include_root_in_json = false + return payment.to_json(), "application/json" + end + + def authorize request + if request.POST['sid'].kind_of?(Array) + payments = Poundpay::Payment.batch_update(:sid => request.POST['sid'], :state => 'authorized') + payments = payments.collect! {|p| p.schema } + return PP.pp(payments, ''), "text/html" + else + payment = Poundpay::Payment.find(request.POST['sid']) + payment.authorize + payment.save + return PP.pp(payment.schema, ''), "text/html" + end + end + + def release request + payment = Poundpay::Payment.find(request.POST['sid']) + payment.release + return PP.pp(payment.schema, ''), "text/html" + end + + def cancel request + payment = Poundpay::Payment.find(request.POST['sid']) + payment.cancel + return PP.pp(payment.schema, ''), "text/html" + end + + def escrow request + if request.POST['sid'].kind_of?(Array) + payments = Poundpay::Payment.batch_update(:sid => request.POST['sid'], :state => 'escrowed') + payments = payments.collect! {|p| p.schema } + return PP.pp(payments, ''), "text/html" + else + payment = Poundpay::Payment.find(request.POST['sid']) + payment.escrow + return PP.pp(payment.schema, ''), "text/html" + end + end + +end + + +class User < SimpleController + + def call env + request = Rack::Request.new(env) + return_value, mime_type = case request.path.gsub(/\/$/, '') # trim trailing / + when '/user' then request.post? ? create(request) : [nil, nil] + else [nil, nil] + end + + if return_value + response = Rack::Response.new([return_value], 201, {"Content-Type" => mime_type}) + response.finish + else + return_404 + end + end + + def create request + user = Poundpay::User.create({ + :first_name => request.POST['user_first_name'], + :last_name => request.POST['user_last_name'], + :email_address => request.POST['user_email_address'] + }) + return PP.pp(user.schema, ''), "text/html" + end + +end + + +class ChargePermission < SimpleController + + def call env + request = Rack::Request.new(env) + return_value, mime_type = case request.path.gsub(/\/$/, '') # trim trailing / + when '/charge_permission' then request.post? ? create(request) : [nil, nil] + when '/charge_permission/find' then request.post? ? show(request) : [nil, nil] + when '/charge_permission/deactivate' then request.post? ? deactivate(request) : [nil, nil] + else [nil, nil] + end + + if return_value + response = Rack::Response.new([return_value], 201, {"Content-Type" => mime_type}) + response.finish + else + return_404 + end + end + + def create request + charge_permission = Poundpay::ChargePermission.create(request.POST) + charge_permission.include_root_in_json = false + return charge_permission.to_json(), "application/json" + end + + def show request + charge_permissions = Poundpay::ChargePermission.find(:all, :params => { :email_address => request.POST['email_address'] }) + if charge_permissions + return PP.pp(charge_permissions.map {|cp| cp.schema}, ''), 'text/plain' + else + return [nil, nil] + end + end + + def deactivate request + charge_permission = Poundpay::ChargePermission.find(request.POST['sid']) + charge_permission.deactivate + return PP.pp(charge_permission.schema, ''), 'text/plain' + end + +end diff --git a/examples/marketplace/config.rb b/examples/marketplace/config.rb new file mode 100644 index 0000000..3ee5206 --- /dev/null +++ b/examples/marketplace/config.rb @@ -0,0 +1,20 @@ +class SimpleApplication + CONFIG = { + poundpay: { + "api_url" => "https://api-sandbox.poundpay.com", + "www_url" => "https://www-sandbox.poundpay.com", + "version" => "silver", + "developer_sid" => "DVxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "auth_token" => "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "callback_url" => '', + }, + default_payment: { + "amount" => "67890", + "payer_fee_amount" => "100", + "recipient_fee_amount" => "200", + "payer_email_address" => "sam@example.com", + "recipient_email_address" => "jacob@example.net", + "description" => "this is a simple description that just loves developers, developers", + } + } +end \ No newline at end of file diff --git a/examples/marketplace/config.ru b/examples/marketplace/config.ru new file mode 100644 index 0000000..1be61f2 --- /dev/null +++ b/examples/marketplace/config.ru @@ -0,0 +1,20 @@ +# http://stackoverflow.com/questions/2900370/ +# why-does-ruby-1-9-2-remove-from-load-path-and-whats-the-alternative + +require 'rack' +require 'rack/urlmap' + +use Rack::ShowExceptions +use Rack::Lint +use Rack::Static, :urls => ["/static"], :root => "fe" + +require './application' + +app = Rack::URLMap.new( + '/' => Index.new, + '/payment' => Payment.new, + '/charge_permission'=> ChargePermission.new, + '/user' => User.new, +) + +run app \ No newline at end of file diff --git a/examples/marketplace/fe b/examples/marketplace/fe new file mode 160000 index 0000000..6637c1c --- /dev/null +++ b/examples/marketplace/fe @@ -0,0 +1 @@ +Subproject commit 6637c1c181355a53821cdac621090199ab756b8e diff --git a/examples/marketplace/index.html.erb b/examples/marketplace/index.html.erb new file mode 100644 index 0000000..e52f20a --- /dev/null +++ b/examples/marketplace/index.html.erb @@ -0,0 +1,129 @@ + + + + + + Simple MarketPlace + + + + + + + + +

Create charge permission

+ + + +
Email + +
+ Create Charge Permission
+ Find Charge Permission
+

+
+  

Display charge permission iframe

+ + + + + + + + +
Charge permission id + +
Card holder name +
server + +
+ Deactivate Charge Permission
+ Start Charge Permission IFrame
+
+ +

Create payment

+ + + + + + + + + + + + + + + + + + +
Payment Amount + +
Payer Fee +
Recipient Fee +
Payer Email +
Recipient Email +
Description +
+ + Create Payment + +

Display payment iframe

+ + + + + + + + + + +
Payment request id +
Card holder name +
server +
+ Start Payment IFrame +
+

+ Payment Complete +

+

Create User

+ + + + + + + + + + +
First Name +
Last Name +
Email Address +
+ Create User + +

+
+
+  

Payment Operations

+ + + +
Payment SID + +
+ Authorize Payment
+ Escrow Payment
+ Release Payment
+ Cancel Payment
+ +

+  
+
diff --git a/examples/simple_application/application.rb b/examples/simple_application/application.rb
deleted file mode 100644
index a9ea561..0000000
--- a/examples/simple_application/application.rb
+++ /dev/null
@@ -1,65 +0,0 @@
-require 'uri'
-require 'net/http'
-require 'net/https'
-require 'json'
-require 'erb'
-
-require 'config'
-
-
-class PoundPay
-  attr_reader :api_url, :version, :sid, :token
-
-  def initialize(api_url, version, sid, token)
-    @api_url = api_url
-    @version = version
-    @sid = sid
-    @token = token
-  end
-
-  def post(endpoint, params)
-    request_url = "#{@api_url}/#{@version}/#{endpoint}"
-    puts request_url, params
-    uri = URI.parse request_url
-    http = Net::HTTP.new uri.host, uri.port
-    http.use_ssl = true
-    req = Net::HTTP::Post.new uri.request_uri
-    req.set_form_data params
-    req.basic_auth @sid, @token
-    response = http.request req
-    JSON.load response.body
-  end
-end
-
-
-class SimpleApplication
-  attr_reader :poundpay_client
-
-  def initialize
-    config = SimpleApplication::CONFIG[:poundpay]
-    @poundpay_client = PoundPay.new(config[:api_url], config[:version], config[:sid], config[:token])
-  end
-
-  def call(env)
-    unless env['REQUEST_PATH'] == '/' and env['REQUEST_METHOD'] == 'GET'
-      return [404, {"Content-Type" => "text/plain"}, ["Page Not Found"]]
-    end
-    # Create payment request
-    params = {
-      'amount'                  => 20000,  # In USD cents
-      'payer_fee_amount'        => 0,
-      'payer_email_address'     => 'goliath@example.com',
-      'recipient_fee_amount'    => 500,
-      'recipient_email_address' => 'david@example.com',
-      'description'             => 'Beats by Dr. Dre',
-    }
-    payment = @poundpay_client.post 'payments', params
-    puts payment
-
-    # Render and return page
-    www_poundpay_url = SimpleApplication::CONFIG[:poundpay][:www_url]
-    template = ERB.new(open("index.html.erb").read)
-    page = template.result(binding)
-    [200, {"Content-Type" => "text/html"}, [page]]
-  end
-end
\ No newline at end of file
diff --git a/examples/simple_application/config.rb b/examples/simple_application/config.rb
deleted file mode 100644
index cb8aaee..0000000
--- a/examples/simple_application/config.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-class SimpleApplication
-  CONFIG = {
-    poundpay: {
-      api_url: "https://api-sandbox.poundpay.com",
-      www_url: "https://www-sandbox.poundpay.com",
-      version: "silver",
-      sid: "DVxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
-      token: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
-    }
-  }
-end
\ No newline at end of file
diff --git a/examples/simple_application/config.ru b/examples/simple_application/config.ru
deleted file mode 100644
index ebdd53c..0000000
--- a/examples/simple_application/config.ru
+++ /dev/null
@@ -1,6 +0,0 @@
-require 'application'
-
-use Rack::ShowExceptions
-use Rack::Lint
-
-run SimpleApplication.new
\ No newline at end of file
diff --git a/examples/simple_application/index.html.erb b/examples/simple_application/index.html.erb
deleted file mode 100644
index e078f4b..0000000
--- a/examples/simple_application/index.html.erb
+++ /dev/null
@@ -1,18 +0,0 @@
-
-  Simple Marketplace
-  
-  
-    

Simple Marketplace

-

<%= payment['description'] %>

-
- - - diff --git a/lib/poundpay.rb b/lib/poundpay.rb index 837f98d..b4cf4ec 100644 --- a/lib/poundpay.rb +++ b/lib/poundpay.rb @@ -1,6 +1,7 @@ require 'poundpay/resource' require 'poundpay/elements' require 'poundpay/callback' +require 'poundpay/exceptions' require 'poundpay/rails' @@ -24,7 +25,7 @@ def configure(developer_sid, auth_token) end yield self if block_given? - + api_url.sub! /(\/)$/, "" # Remove trailing backslash www_url.sub /(\/)$/, "" Resource.site = "#{api_url}/#{api_version}/" @@ -34,9 +35,8 @@ def configure(developer_sid, auth_token) # Set callback_url if defined in configuration if callback_url - @me = Developer.me + @me = Developer.new @me.callback_url = callback_url - @me.save! end end @@ -84,4 +84,4 @@ def api_version @api_version || API_VERSION end end -end \ No newline at end of file +end diff --git a/lib/poundpay/elements.rb b/lib/poundpay/elements.rb index 18f019d..4666a7d 100644 --- a/lib/poundpay/elements.rb +++ b/lib/poundpay/elements.rb @@ -5,6 +5,9 @@ class PaymentException < Exception end +class PaymentAuthorizeException < PaymentException +end + class PaymentEscrowException < PaymentException end @@ -14,6 +17,12 @@ class PaymentReleaseException < PaymentException class PaymentCancelException < PaymentException end +class ChargePermissionException < Exception +end + +class ChargePermissionDeactivateException < ChargePermissionException +end + module Poundpay class Developer < Resource def self.me @@ -22,6 +31,7 @@ def self.me def save validate_url callback_url + validate_url charge_permission_callback_url super end @@ -29,6 +39,11 @@ def callback_url=(url) validate_url url attributes['callback_url'] = url end + + def charge_permission_callback_url=(url) + validate_url url + attributes['charge_permission_callback_url'] = url + end protected def validate_url(url) @@ -40,32 +55,64 @@ def validate_url(url) end end + class User < Resource + end + + class ChargePermission < Resource + def deactivate + states = ['CREATED', 'ACTIVE'] + unless states.include?(state) + raise ChargePermissionDeactivateException.new "Charge permission state is #{state}. Only CREATED or ACTIVE charge permissions may be deactivated." + end + attributes['state'] = 'INACTIVE' + save + end + end + class Payment < Resource + + def self.batch_update(params) + body = self.put('', {}, self.urlencode(params)).body + collection = self.format.decode(body) + return self.instantiate_collection(collection) + end + + def authorize + unless state == 'CREATED' + raise PaymentAuthorizeException.new "Payment state is #{state}. Only CREATED payments may be AUTHORIZED." + end + attributes['state'] = 'AUTHORIZED' + save + end + def escrow - unless status == 'AUTHORIZED' - raise PaymentEscrowException.new "Payment status is #{status}. Only AUTHORIZED payments may be released" + unless state == 'AUTHORIZED' + raise PaymentEscrowException.new "Payment state is #{state}. Only AUTHORIZED payments may be ESCROWED." end - attributes['status'] = 'ESCROWED' + attributes['state'] = 'ESCROWED' save end def release - unless status == 'ESCROWED' - raise PaymentReleaseException.new "Payment status is #{status}. Only ESCROWED payments may be released" + states = ['ESCROWED'] + unless states.include?(state) + raise PaymentReleaseException.new "Payment state is #{state}. Only ESCROWED payments may be RELEASED." end - # Tried setting status with status=, but save still had status == 'ESCROWED'. - # Setting the status through the attributes, however, does work. - attributes['status'] = 'RELEASED' + # Tried setting state with state=, but save still had state == 'ESCROWED'. + # Setting the state through the attributes, however, does work. + attributes['state'] = 'RELEASED' save end def cancel - unless status == 'ESCROWED' - raise PaymentCancelException.new "Payment status is #{status}. Only ESCROWED payments may be canceled" + states = ['CREATED', 'AUTHORIZED', 'ESCROWED'] + unless states.include?(state) + raise PaymentCancelException.new "Payment state is #{state}. Only payments with states " \ + "#{states.join(', ')} may be canceled" end - attributes['status'] = 'CANCELED' + attributes['state'] = 'CANCELED' save end end -end \ No newline at end of file +end diff --git a/lib/poundpay/exceptions.rb b/lib/poundpay/exceptions.rb new file mode 100644 index 0000000..d767bdd --- /dev/null +++ b/lib/poundpay/exceptions.rb @@ -0,0 +1,10 @@ +require 'active_resource/exceptions' + +# monkey patch the base AR exception class to provide access to the response body in a structured format. +module ActiveResource + class ConnectionError + def data + @data ||= ActiveSupport::JSON.decode(self.response.body).symbolize_keys rescue {} + end + end +end diff --git a/lib/poundpay/rails.rb b/lib/poundpay/rails.rb index ae43a90..b31a9ad 100644 --- a/lib/poundpay/rails.rb +++ b/lib/poundpay/rails.rb @@ -3,8 +3,8 @@ module Poundpay def self.configure_from_yaml(path) pathname = Pathname.new Rails.root.join(path) raise ArgumentError.new "File does not exist: #{pathname.to_s}" unless pathname.exist? - config = YAML::load_file(pathname)[Rails.env] + config = YAML::load(ERB.new(File.read(pathname)).result)[Rails.env] Poundpay.configure_from_hash(config) end end -end \ No newline at end of file +end diff --git a/lib/poundpay/resource.rb b/lib/poundpay/resource.rb index 3849dea..557e219 100644 --- a/lib/poundpay/resource.rb +++ b/lib/poundpay/resource.rb @@ -7,7 +7,11 @@ class Resource < ActiveResource::Base self.format = Formats::UrlencodedJsonFormat class << self - attr_accessor_with_default(:primary_key, 'sid') + attr_accessor :primary_key + + def primary_key + @primary_key ||= 'sid' + end # Modified default to not use an extension def element_path(id, prefix_options = {}, query_options = nil) @@ -27,6 +31,12 @@ def collection_path(prefix_options = {}, query_options = nil) remove_extension(path) end + # Modified default to not use an extension + def custom_method_collection_url(method_name, options = {}) + path = super(method_name, options) + remove_extension(path) + end + # Handle paginated collections def instantiate_collection(collection, prefix_options = {}) # TODO: Consume pages @@ -36,14 +46,14 @@ def instantiate_collection(collection, prefix_options = {}) protected def remove_extension(path) - path.sub /(\.#{format.extension})$/, "" + path.sub /(\.#{format.extension})/, "" end end # Poundpay accepts urlencoded form parameters # Ideally we should override this functionality in the format, but it's not very straightforward to do so def encode - urlencode(@attributes) + Resource.urlencode(@attributes) end def collection_name @@ -51,8 +61,14 @@ def collection_name end protected - def urlencode(params) - params.to_a.collect! { |k, v| "#{k}=#{CGI.escape(v.to_s)}" }.join("&") - end + def self.urlencode(params) + params.to_a.collect! { |k, v| + if v.kind_of?(Array) + v.collect! { |x| "#{k}=#{CGI.escape(x.to_s)}"}.join("&") + else + "#{k}=#{CGI.escape(v.to_s)}" + end + }.join("&") + end end end \ No newline at end of file diff --git a/lib/poundpay/version.rb b/lib/poundpay/version.rb index 634b36c..da123cc 100644 --- a/lib/poundpay/version.rb +++ b/lib/poundpay/version.rb @@ -1,3 +1,3 @@ module Poundpay - VERSION = "0.2.8" + VERSION = "0.4.0" end diff --git a/poundpay.gemspec b/poundpay.gemspec index 1eed260..a48680c 100644 --- a/poundpay.gemspec +++ b/poundpay.gemspec @@ -14,7 +14,7 @@ Gem::Specification.new do |s| s.rubyforge_project = "poundpay" - s.add_dependency("activeresource", ">= 3.0") + s.add_dependency("activeresource", "~> 3.1.0") s.add_development_dependency("rspec", ">= 2.0") s.add_development_dependency("wirble") diff --git a/spec/fixtures/charge_permissions.rb b/spec/fixtures/charge_permissions.rb new file mode 100644 index 0000000..76794af --- /dev/null +++ b/spec/fixtures/charge_permissions.rb @@ -0,0 +1,24 @@ +module Poundpay + module ChargePermissionFixture + def created_attributes + { + "email_address" => "john@example.com", + "state" => "CREATED", + "updated_at" => "2011-02-11T19:07:05.332356Z", + "created_at" => "2011-02-11T19:07:05.332356Z", + } + end + + def active_attributes + @attributes = created_attributes + @attributes["state"] = "ACTIVE" + @attributes + end + + def inactive_attributes + @attributes = created_attributes + @attributes["state"] = "INACTIVE" + @attributes + end + end +end diff --git a/spec/fixtures/payments.rb b/spec/fixtures/payments.rb index ca1d6ad..f6b8661 100644 --- a/spec/fixtures/payments.rb +++ b/spec/fixtures/payments.rb @@ -1,6 +1,6 @@ module Poundpay module PaymentFixture - def staged_payment_attributes + def created_payment_attributes { "amount" => 20000, "payer_fee_amount" => 0, @@ -9,7 +9,7 @@ def staged_payment_attributes "recipient_email_address" => "david@example.com", "description" => "Beats by Dr. Dre", "sid" => "PY1d82752a361211e0bce31231400042c7", - "status" => "STAGED", + "state" => "CREATED", "amount_to_credit_developer" => 550, "updated_at" => "2011-02-11T19:07:05.332356Z", "recipient_sid" => nil, @@ -23,27 +23,27 @@ def staged_payment_attributes end def authorized_payment_attributes - @attributes = staged_payment_attributes - @attributes["status"] = "AUTHORIZED" + @attributes = created_payment_attributes + @attributes["state"] = "AUTHORIZED" @attributes end def escrowed_payment_attributes - @attributes = staged_payment_attributes - @attributes["status"] = "ESCROWED" + @attributes = created_payment_attributes + @attributes["state"] = "ESCROWED" @attributes end def released_payment_attributes - @attributes = staged_payment_attributes - @attributes["status"] = "RELEASED" + @attributes = created_payment_attributes + @attributes["state"] = "RELEASED" @attributes end def canceled_payment_attributes - @attributes = staged_payment_attributes - @attributes["status"] = "CANCELED" + @attributes = created_payment_attributes + @attributes["state"] = "CANCELED" @attributes end end -end \ No newline at end of file +end diff --git a/spec/fixtures/poundpay.yml b/spec/fixtures/poundpay.yml index dbe8e43..705374f 100644 --- a/spec/fixtures/poundpay.yml +++ b/spec/fixtures/poundpay.yml @@ -1,6 +1,6 @@ development: developer_sid: DV0383d447360511e0bbac00264a09ff3c - auth_token: development_auth_token + auth_token: <%= "development_auth_token" %> www_url: https://www-sandbox.poundpay.com api_url: https://api-sandbox.poundpay.com diff --git a/spec/poundpay/elements_spec.rb b/spec/poundpay/elements_spec.rb index 4c1623e..d5cacf1 100644 --- a/spec/poundpay/elements_spec.rb +++ b/spec/poundpay/elements_spec.rb @@ -2,6 +2,7 @@ require 'poundpay/elements' require 'fixtures/payments' require 'fixtures/developers' +require 'fixtures/charge_permissions' include Poundpay @@ -66,10 +67,25 @@ Poundpay.clear_config! end + describe "#authorize" do + it "should not be able to authorize a non CREATED payment" do + @non_created_payment = Payment.new authorized_payment_attributes + expect {@non_created_payment.authorize}.to raise_error(PaymentAuthorizeException) + end + + it "should authorize a CREATED payment" do + @created_payment = Payment.new created_payment_attributes + @created_payment.should_receive(:save).and_return(Payment.new authorized_payment_attributes) + + @created_payment.authorize + @created_payment.state.should == 'AUTHORIZED' + end + end + describe "#escrow" do - it "should not be able to escrow a STAGED payment" do - @staged_payment = Payment.new staged_payment_attributes - expect {@staged_payment.escrow}.to raise_error(PaymentEscrowException) + it "should not be able to escrow a CREATED payment" do + @created_payment = Payment.new created_payment_attributes + expect {@created_payment.escrow}.to raise_error(PaymentEscrowException) end it "should escrow an AUTHORIZED payment" do @@ -77,14 +93,14 @@ @authorized_payment.should_receive(:save).and_return(Payment.new escrowed_payment_attributes) @authorized_payment.escrow - @authorized_payment.status.should == 'ESCROWED' + @authorized_payment.state.should == 'ESCROWED' end end describe "#release" do - it "should not be able to release a STAGED payment" do - @staged_payment = Payment.new staged_payment_attributes - expect {@staged_payment.release}.to raise_error(PaymentReleaseException) + it "should not be able to release a CREATED payment" do + @created_payment = Payment.new created_payment_attributes + expect {@created_payment.release}.to raise_error(PaymentReleaseException) end it "should release an ESCROWED payment" do @@ -92,22 +108,65 @@ @escrowed_payment.should_receive(:save).and_return(Payment.new released_payment_attributes) @escrowed_payment.release - @escrowed_payment.status.should == 'RELEASED' + @escrowed_payment.state.should == 'RELEASED' end - end + end describe "#cancel" do - it "should not be able to cancel a STAGED payment" do - @staged_payment = Payment.new staged_payment_attributes - expect {@staged_payment.cancel}.to raise_error(PaymentCancelException) + it "should not be able to cancel a RELEASED payment" do + @released_payment = Payment.new released_payment_attributes + expect {@released_payment.cancel}.to raise_error(PaymentCancelException) end - it "should release an ESCROWED payment" do + it "should cancel an ESCROWED payment" do @escrowed_payment = Payment.new escrowed_payment_attributes @escrowed_payment.should_receive(:save).and_return(Payment.new canceled_payment_attributes) @escrowed_payment.cancel - @escrowed_payment.status.should == 'CANCELED' + @escrowed_payment.state.should == 'CANCELED' + end + + it "should cancel a CREATED payment" do + @created_payment = Payment.new created_payment_attributes + @created_payment.should_receive(:save).and_return(Payment.new canceled_payment_attributes) + + @created_payment.cancel + @created_payment.state.should == 'CANCELED' + end + end +end + +describe ChargePermission do + include ChargePermissionFixture + + before (:all) do + Poundpay.configure( + "DV0383d447360511e0bbac00264a09ff3c", + "c31155b9f944d7aed204bdb2a253fef13b4fdcc6ae1540200449cc4526b2381a") + end + + after (:all) do + Poundpay.clear_config! + end + + describe "#deactivate" do + it "should not be able to deactivate an INACTIVE charge permission" do + @inactive_charge_permission = ChargePermission.new inactive_attributes + expect {@inactive_charge_permission.deactivate}.to raise_error(ChargePermissionDeactivateException) + end + + it "should deactivate a CREATED charge permission" do + @created_charge_permission = ChargePermission.new created_attributes + @created_charge_permission.should_receive(:save).and_return(ChargePermission.new created_attributes) + @created_charge_permission.deactivate + @created_charge_permission.state.should == 'INACTIVE' + end + + it "should deactivate an ACTIVE charge permission" do + @active_charge_permission = ChargePermission.new active_attributes + @active_charge_permission.should_receive(:save).and_return(ChargePermission.new active_attributes) + @active_charge_permission.deactivate + @active_charge_permission.state.should == 'INACTIVE' end end -end \ No newline at end of file +end diff --git a/spec/poundpay/exceptions_spec.rb b/spec/poundpay/exceptions_spec.rb new file mode 100644 index 0000000..6a54fdc --- /dev/null +++ b/spec/poundpay/exceptions_spec.rb @@ -0,0 +1,11 @@ +require 'poundpay' + +describe ActiveResource::ConnectionError do + describe ".data" do + it "should return the parsed response body" do + response = double('response') + response.stub(:body).and_return('{"error_message":"You screwed up!"}') + ActiveResource::ConnectionError.new(response).data.should == {error_message: "You screwed up!"} + end + end +end diff --git a/spec/poundpay/resource_spec.rb b/spec/poundpay/resource_spec.rb index 3309387..ba08e31 100644 --- a/spec/poundpay/resource_spec.rb +++ b/spec/poundpay/resource_spec.rb @@ -29,6 +29,12 @@ class FakeElement < Resource FakeElement.collection_path.should == "/version/fake_elements" end end + + describe "#self.custom_method_collection_url" do + it "should not add a extension to the custom_method_collection_url" do + FakeElement.custom_method_collection_url("").should == "/version/fake_elements/" + end + end describe "#self.instantiate_collection" do it "should handle paginated responses" do @@ -43,6 +49,10 @@ class FakeElement < Resource fake_element = FakeElement.new(:foo => "bar baz") fake_element.encode.should == "foo=bar+baz" end + it "should urlencode the attributes wuihtout brackets" do + fake_element = FakeElement.new(:foo => ["bar", "baz"]) + fake_element.encode.should == "foo=bar&foo=baz" + end end describe "#collection_name" do diff --git a/spec/poundpay_spec.rb b/spec/poundpay_spec.rb index b141d80..d7a7f91 100644 --- a/spec/poundpay_spec.rb +++ b/spec/poundpay_spec.rb @@ -60,8 +60,7 @@ it "should configure callback_url" do callback_url = "http://awesomemarketplace.com/payments/callback" @developer = Poundpay::Developer.new - @developer.should_receive(:save!) - Poundpay::Developer.should_receive(:me).and_return(@developer) + Poundpay::Developer.should_receive(:new).and_return(@developer) Poundpay.configure("DV0383d447360511e0bbac00264a09ff3c", "c31155b9f944d7aed204bdb2a253fef13b4fdcc6ae1540200449cc4526b2381a") do |c| c.callback_url = callback_url end @@ -120,8 +119,7 @@ config = @config["production"] config["callback_url"] = "http://awesomemarketplace.com/payments/callback" @developer = Poundpay::Developer.new - @developer.should_receive(:save!) - Poundpay::Developer.should_receive(:me).and_return(@developer) + Poundpay::Developer.should_receive(:new).and_return(@developer) Poundpay.configure_from_hash config @developer.callback_url.should == config["callback_url"] end @@ -143,4 +141,4 @@ Poundpay::Resource.password.should == nil end end -end \ No newline at end of file +end