amazon_flex_pay 0.9.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. data/LICENSE +20 -0
  2. data/README.rdoc +41 -0
  3. data/Rakefile +23 -0
  4. data/lib/amazon_flex_pay/api/base.rb +86 -0
  5. data/lib/amazon_flex_pay/api/cancel.rb +11 -0
  6. data/lib/amazon_flex_pay/api/cancel_token.rb +9 -0
  7. data/lib/amazon_flex_pay/api/get_account_activity.rb +21 -0
  8. data/lib/amazon_flex_pay/api/get_account_balance.rb +8 -0
  9. data/lib/amazon_flex_pay/api/get_recipient_verification_status.rb +9 -0
  10. data/lib/amazon_flex_pay/api/get_token_by_caller.rb +10 -0
  11. data/lib/amazon_flex_pay/api/get_token_usage.rb +9 -0
  12. data/lib/amazon_flex_pay/api/get_tokens.rb +13 -0
  13. data/lib/amazon_flex_pay/api/get_transaction.rb +9 -0
  14. data/lib/amazon_flex_pay/api/get_transaction_status.rb +13 -0
  15. data/lib/amazon_flex_pay/api/pay.rb +20 -0
  16. data/lib/amazon_flex_pay/api/refund.rb +14 -0
  17. data/lib/amazon_flex_pay/api/reserve.rb +20 -0
  18. data/lib/amazon_flex_pay/api/settle.rb +11 -0
  19. data/lib/amazon_flex_pay/api/verify_signature.rb +14 -0
  20. data/lib/amazon_flex_pay/api.rb +162 -0
  21. data/lib/amazon_flex_pay/data_types.rb +138 -0
  22. data/lib/amazon_flex_pay/enumerations.rb +23 -0
  23. data/lib/amazon_flex_pay/model.rb +130 -0
  24. data/lib/amazon_flex_pay/pipelines/base.rb +40 -0
  25. data/lib/amazon_flex_pay/pipelines/edit_token.rb +6 -0
  26. data/lib/amazon_flex_pay/pipelines/multi_use.rb +28 -0
  27. data/lib/amazon_flex_pay/pipelines/recipient.rb +10 -0
  28. data/lib/amazon_flex_pay/pipelines/single_use.rb +24 -0
  29. data/lib/amazon_flex_pay/pipelines.rb +41 -0
  30. data/lib/amazon_flex_pay/signing.rb +42 -0
  31. data/lib/amazon_flex_pay.rb +53 -0
  32. data/test/amazon_flex_pay_test.rb +117 -0
  33. data/test/api_test.rb +310 -0
  34. data/test/pipelines_test.rb +46 -0
  35. data/test/response_samples.rb +588 -0
  36. data/test/test_helper.rb +22 -0
  37. metadata +167 -0
@@ -0,0 +1,138 @@
1
+ # Complex Data Types pulled from the documentation, with some obvious corrections.
2
+ #
3
+ # See http://docs.amazonwebservices.com/AmazonFPS/latest/FPSAdvancedGuide/ComplexDataTypes.html
4
+ module AmazonFlexPay::DataTypes
5
+ # moved from Enumerations
6
+ class AccountBalance < AmazonFlexPay::Model #:nodoc:
7
+ attribute :available_balances, :type => :available_balances
8
+ attribute :pending_in_balance, :type => :amount
9
+ attribute :pending_out_balance, :type => :amount
10
+ attribute :total_balance, :type => :amount
11
+ end
12
+
13
+ class Amount < AmazonFlexPay::Model #:nodoc:
14
+ attribute :currency_code, :enumeration => :currency_code
15
+ attribute :value
16
+ end
17
+
18
+ class AvailableBalances < AmazonFlexPay::Model #:nodoc:
19
+ attribute :disburse_balance, :type => :amount
20
+ attribute :refund_balance, :type => :amount
21
+ end
22
+
23
+ class DebtBalance < AmazonFlexPay::Model #:nodoc:
24
+ attribute :available_balance, :type => :amount
25
+ attribute :pending_out_balance, :type => :amount
26
+ end
27
+
28
+ class DescriptorPolicy < AmazonFlexPay::Model #:nodoc:
29
+ attribute :'CSOwner', :enumeration => :cs_owner
30
+ attribute :soft_descriptor_type, :enumeration => :soft_descriptor_type
31
+ end
32
+
33
+ class OutstandingDebtBalance < AmazonFlexPay::Model #:nodoc:
34
+ attribute :outstanding_balance, :type => :amount
35
+ attribute :pending_out_balance, :type => :amount
36
+ end
37
+
38
+ class OutstandingPrepaidLiability < AmazonFlexPay::Model #:nodoc:
39
+ attribute :outstanding_balance, :type => :amount
40
+ attribute :pending_in_balance, :type => :amount
41
+ end
42
+
43
+ class PrepaidBalance < AmazonFlexPay::Model #:nodoc:
44
+ attribute :available_balance, :type => :amount
45
+ attribute :pending_in_balance, :type => :amount
46
+ end
47
+
48
+ class RelatedTransaction < AmazonFlexPay::Model #:nodoc:
49
+ attribute :relation_type, :enumeration => :relation_type
50
+ attribute :transaction_id
51
+ end
52
+
53
+ class StatusHistory < AmazonFlexPay::Model #:nodoc:
54
+ attribute :amount, :type => :amount
55
+ attribute :date
56
+ attribute :status_code
57
+ attribute :transaction_status, :enumeration => :transaction_status
58
+ end
59
+
60
+ class Token < AmazonFlexPay::Model #:nodoc:
61
+ attribute :caller_reference
62
+ attribute :date_installed
63
+ attribute :friendly_name
64
+ attribute :old_token_id
65
+ attribute :payment_reason
66
+ attribute :token_id
67
+ attribute :token_status, :enumeration => :token_status
68
+ attribute :token_type, :enumeration => :token_type
69
+ end
70
+
71
+ class TokenUsageLimit < AmazonFlexPay::Model #:nodoc:
72
+ attribute :amount, :type => :amount
73
+ attribute :last_reset_amount, :type => :amount
74
+ attribute :count
75
+ attribute :last_reset_count
76
+ attribute :last_reset_timestamp
77
+ end
78
+
79
+ class Transaction < AmazonFlexPay::Model #:nodoc:
80
+ attribute :balance, :type => :amount
81
+ attribute :caller_name
82
+ attribute :caller_transaction_date
83
+ attribute :date_completed
84
+ attribute :date_received
85
+ attribute :'FPSFees', :type => :amount
86
+ attribute :'FPSOperation', :enumeration => :fps_operation
87
+ attribute :original_transaction_id
88
+ attribute :payment_method, :enumeration => :payment_method
89
+ attribute :recipient_name
90
+ attribute :recipient_token_id
91
+ attribute :sender_name
92
+ attribute :sender_token_id
93
+ attribute :status_code
94
+ attribute :status_message
95
+ attribute :transaction_amount, :type => :amount
96
+ attribute :transaction_id
97
+ attribute :transaction_part, :collection => :transaction_part
98
+ attribute :transaction_status, :enumeration => :transaction_status
99
+ end
100
+
101
+ class TransactionDetail < AmazonFlexPay::Model #:nodoc:
102
+ attribute :caller_name
103
+ attribute :caller_description
104
+ attribute :caller_reference
105
+ attribute :credit_instrument_id
106
+ attribute :date_received
107
+ attribute :date_completed
108
+ attribute :'FPSFees', :type => :amount
109
+ attribute :'FPSFeesPaidBy', :enumeration => :transactional_role
110
+ attribute :'FPSOperation', :enumeration => :fps_operation
111
+ attribute :marketplace_fees, :type => :amount
112
+ attribute :payment_method, :enumeration => :payment_method
113
+ attribute :prepaid_instrument_id
114
+ attribute :recipient_email
115
+ attribute :recipient_name
116
+ attribute :recipient_token_id
117
+ attribute :related_transaction, :type => :related_transaction
118
+ attribute :sender_description
119
+ attribute :sender_email
120
+ attribute :sender_name
121
+ attribute :sender_token_id
122
+ attribute :status_code
123
+ attribute :status_history, :collection => :status_history
124
+ attribute :status_message
125
+ attribute :transaction_amount, :type => :amount
126
+ attribute :transaction_id
127
+ attribute :transaction_status, :enumeration => :transaction_status
128
+ end
129
+
130
+ class TransactionPart < AmazonFlexPay::Model #:nodoc:
131
+ attribute :description
132
+ attribute :fees_paid, :type => :amount
133
+ attribute :instrument_id
134
+ attribute :name
135
+ attribute :reference
136
+ attribute :role, :enumeration => :transactional_role
137
+ end
138
+ end
@@ -0,0 +1,23 @@
1
+ # Enumerated sets pulled from documentation, with some obvious corrections.
2
+ #
3
+ # See http://docs.amazonwebservices.com/AmazonFPS/latest/FPSAdvancedGuide/EnumeratedDataTypes.html
4
+ module AmazonFlexPay::Enumerations
5
+ AmountType = %w(Exact Maximum Minimum) # undocumented
6
+ ChargeFeeTo = %w(Caller Recipient)
7
+ CsOwner = %w(Caller Recipient) # undocumented
8
+ CurrencyCode = %w(USD)
9
+ FpsOperation = %w(Pay Refund Settle SettleDebt WriteOffDebt FundPrepaid Reserve)
10
+ InstrumentId = %w(InstrumentId) # whaaaaaaat? should go in DataTypes probably, but it actually seems like a simple field.
11
+ InstrumentStatus = %w(Active All Cancelled)
12
+ MarketplaceRefundPolicy = %w(MarketplaceTxnOnly MasterAndMarketplaceTxn MasterTxnOnly) # moved from DataTypes
13
+ PaymentMethod = %w(ABT ACH CC Debt Prepaid)
14
+ RecipientVerificationStatus = %w(VerificationComplete VerificationPending VerificationCompleteNoLimits) # moved from DataTypes
15
+ RelationType = %w(MarketplaceFee Parent Refund RefundReversal Reserve Settle)
16
+ SoftDescriptorType = %w(Static Dynamic) # undocumented
17
+ SortOrderByDate = %w(Ascending Descending)
18
+ TokenStatus = %w(Active Inactive)
19
+ TokenType = %w(MultiUse Recurring SingleUse Unrestricted)
20
+ TransactionalRole = %w(Caller Recipient Sender)
21
+ TransactionStatus = %w(Cancelled Failure Pending Reserved Success)
22
+ UsageLimitType = %w(Amount Count) # undocumented
23
+ end
@@ -0,0 +1,130 @@
1
+ module AmazonFlexPay
2
+
3
+ class Model
4
+ class << self
5
+ # Creates an attribute by defining reader and writer methods. These attributes will
6
+ # also be returned by AmazonFlexPay::Model#to_hash for processing into query strings, etc.
7
+ #
8
+ # A few different attribute types are supported.
9
+ #
10
+ # === Enumerated Attributes
11
+ # If Amazon only supports certain values, this will enforce a whitelist. Name one of
12
+ # the existing enumerations from AmazonFlexPay::Enumerations like this:
13
+ #
14
+ # attribute :status, :enumeration => :token_status
15
+ #
16
+ # === Complex Attributes
17
+ # When the attribute itself has attributes, name one of the existing complex data types
18
+ # from AmazonFlexPay::DataTypes like this:
19
+ #
20
+ # attribute :final_amount, :type => :amount
21
+ #
22
+ # === Arrays of Complex Attributes
23
+ # When Amazon's XML returns (or has the potential to return) multiples of a complex data
24
+ # type, declare it like this:
25
+ #
26
+ # attribute :transaction, :collection => :transaction_detail
27
+ #
28
+ # And then for convenience and readability, alias a plural method:
29
+ #
30
+ # alias_method :transactions, :transaction
31
+ def attribute(attr, options = {})
32
+ attribute_names << attr.to_s.camelcase
33
+ name = attr.to_s.underscore
34
+
35
+ # reader
36
+ attr_reader name
37
+
38
+ # writer
39
+ if options[:enumeration]
40
+ enumerated_attribute(name, options[:enumeration])
41
+ elsif options[:type]
42
+ complex_attribute(name, options[:type])
43
+ elsif options[:collection]
44
+ collection_attribute(name, options[:collection])
45
+ else
46
+ attr_writer name
47
+ end
48
+ end
49
+
50
+ # The names of all of the attributes of this model, in CamelCase.
51
+ def attribute_names
52
+ @attributes ||= (self == Model) ? [] : superclass.attribute_names.dup
53
+ end
54
+
55
+ def enumerated_attribute(attr, source = nil) #:nodoc:
56
+ source ||= attr
57
+ class_eval <<-END
58
+ def #{attr}=(val)
59
+ options = AmazonFlexPay::Enumerations::#{source.to_s.camelcase}
60
+ unless options.include?(val)
61
+ raise ArgumentError.new("\#{val} is not an allowed option (\#{options.join(', ')})")
62
+ end
63
+ @#{attr} = val
64
+ end
65
+ END
66
+ end
67
+
68
+ def complex_attribute(attr, data_type = nil) #:nodoc:
69
+ data_type ||= attr
70
+ class_eval <<-END
71
+ def #{attr}=(hash)
72
+ @#{attr} = AmazonFlexPay::DataTypes::#{data_type.to_s.camelcase}.new(hash)
73
+ end
74
+ END
75
+ end
76
+
77
+ def collection_attribute(attr, data_type = nil) #:nodoc:
78
+ class_eval <<-END
79
+ def #{attr}=(array)
80
+ @#{attr} = [array].flatten.map{|hash| AmazonFlexPay::DataTypes::#{data_type.to_s.camelcase}.new(hash)}
81
+ end
82
+ END
83
+ end
84
+ end
85
+
86
+ def initialize(hash = {}) #:nodoc:
87
+ assign(hash)
88
+ end
89
+
90
+ protected
91
+
92
+ # Formats all attributes into a hash of parameters.
93
+ def to_hash
94
+ self.class.attribute_names.inject({}) do |hash, name|
95
+ val = send(name.underscore)
96
+ if val.nil? or val == ''
97
+ hash
98
+ else
99
+ hash.merge(format_key(name) => val.is_a?(AmazonFlexPay::Model) ? val.to_hash : format_value(val))
100
+ end
101
+ end
102
+ end
103
+
104
+ # By default all parameter keys are CamelCase.
105
+ def format_key(key)
106
+ key.camelcase
107
+ end
108
+
109
+ # Formats times and booleans as Amazon desires them.
110
+ def format_value(val)
111
+ case val
112
+ when Time
113
+ val.utc.strftime('%Y-%m-%dT%H:%M:%SZ')
114
+
115
+ when TrueClass, FalseClass
116
+ val.to_s.capitalize
117
+
118
+ else
119
+ val.to_s
120
+ end
121
+ end
122
+
123
+ # Allows easy initialization for a model by assigning attributes from a hash.
124
+ def assign(hash)
125
+ hash.each do |k, v|
126
+ send("#{k.to_s.underscore}=", v.respond_to?(:strip) ? v.strip : v)
127
+ end
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,40 @@
1
+ module AmazonFlexPay::Pipelines #:nodoc:
2
+ class Base < AmazonFlexPay::Model
3
+ attribute :caller_reference # required
4
+ attribute :cobranding_style
5
+ attribute :cobranding_url
6
+ attribute :website_description
7
+
8
+ # Returns a full redirectable URL for this pipeline.
9
+ def url(return_url)
10
+ AmazonFlexPay.pipeline_endpoint + '?' + AmazonFlexPay.query_string(to_params(return_url))
11
+ end
12
+
13
+ # Converts the Pipeline object into parameters and signs them.
14
+ def to_params(return_url)
15
+ params = self.to_hash.merge(
16
+ 'pipelineName' => pipeline_name,
17
+ 'callerKey' => AmazonFlexPay.access_key,
18
+ 'version' => AmazonFlexPay::PIPELINE_VERSION,
19
+ 'returnURL' => return_url
20
+ )
21
+
22
+ params['signatureVersion'] = 2
23
+ params['signatureMethod'] = 'HmacSHA256'
24
+ params['signature'] = AmazonFlexPay.signature(AmazonFlexPay.pipeline_endpoint, params)
25
+
26
+ params
27
+ end
28
+
29
+ protected
30
+
31
+ # For pipelines, parameter keys are camelCase instead of CamelCase.
32
+ def format_key(key)
33
+ key.camelcase(:lower)
34
+ end
35
+
36
+ def pipeline_name #:nodoc:
37
+ self.class.to_s.split('::').last
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,6 @@
1
+ module AmazonFlexPay::Pipelines #:nodoc:
2
+ class EditToken < Base #:nodoc:
3
+ attribute :token_id # required
4
+ attribute :payment_method, :enumeration => :payment_method
5
+ end
6
+ end
@@ -0,0 +1,28 @@
1
+ module AmazonFlexPay::Pipelines #:nodoc:
2
+ class MultiUse < Base #:nodoc:
3
+ attribute :global_amount_limit # required
4
+ attribute :amount_type, :enumeration => :amount_type
5
+ attribute :transaction_amount
6
+ attribute :currency_code, :enumeration => :currency_code
7
+ attribute :payment_method, :enumeration => :payment_method
8
+ attribute :payment_reason
9
+ attribute :is_recipient_cobranding
10
+ attribute :recipient_token_list
11
+ attribute :usage_limit_type1, :enumeration => :usage_limit_type
12
+ attribute :usage_limit_period1
13
+ attribute :usage_limit_value1
14
+ attribute :usage_limit_type2, :enumeration => :usage_limit_type
15
+ attribute :usage_limit_period2
16
+ attribute :usage_limit_value2
17
+ attribute :validity_expiry
18
+ attribute :validity_start
19
+ attribute :collect_shipping_address
20
+ attribute :address_name
21
+ attribute :address_line1
22
+ attribute :address_line2
23
+ attribute :city
24
+ attribute :state
25
+ attribute :zip
26
+ attribute :phone_number
27
+ end
28
+ end
@@ -0,0 +1,10 @@
1
+ module AmazonFlexPay::Pipelines #:nodoc:
2
+ class Recipient < Base #:nodoc:
3
+ attribute :recipient_pays_fee # required
4
+ attribute :payment_method, :enumeration => :payment_method
5
+ attribute :max_fixed_fee
6
+ attribute :max_variable_fee
7
+ attribute :validity_expiry
8
+ attribute :validity_start
9
+ end
10
+ end
@@ -0,0 +1,24 @@
1
+ module AmazonFlexPay::Pipelines #:nodoc:
2
+ class SingleUse < Base #:nodoc:
3
+ attribute :recipient_token # required
4
+ attribute :transaction_amount # required
5
+ attribute :currency_code, :enumeration => :currency_code
6
+ attribute :item_total
7
+ attribute :shipping
8
+ attribute :handling
9
+ attribute :discount
10
+ attribute :tax
11
+ attribute :address_name
12
+ attribute :address_line1
13
+ attribute :address_line2
14
+ attribute :city
15
+ attribute :state
16
+ attribute :zip
17
+ attribute :phone_number
18
+ attribute :collect_shipping_address
19
+ attribute :gift_wrapping
20
+ attribute :payment_method, :enumeration => :payment_method
21
+ attribute :payment_reason
22
+ attribute :reserve
23
+ end
24
+ end
@@ -0,0 +1,41 @@
1
+ require 'amazon_flex_pay/pipelines/base'
2
+ Dir[File.dirname(__FILE__) + '/pipelines/*'].each do |p| require "amazon_flex_pay/pipelines/#{File.basename(p)}" end
3
+
4
+ module AmazonFlexPay
5
+ class << self
6
+ # Creates a pipeline that may be used to change the payment method of a token.
7
+ #
8
+ # Note that this does not allow changing a token's limits or recipients or really anything but the method.
9
+ #
10
+ # See http://docs.amazonwebservices.com/AmazonFPS/latest/FPSAdvancedGuide/EditTokenPipeline.html
11
+ def edit_token_pipeline(caller_reference, options = {})
12
+ AmazonFlexPay::Pipelines::EditToken.new(options.merge(:caller_reference => caller_reference))
13
+ end
14
+
15
+ # Creates a pipeline that will authorize you to send money _from_ the user multiple times.
16
+ #
17
+ # This is also necessary to create sender tokens that are valid for a long period of time, even if
18
+ # you only plan to collect from the token once.
19
+ #
20
+ # See http://docs.amazonwebservices.com/AmazonFPS/latest/FPSAdvancedGuide/MultiUsePipeline.html
21
+ def multi_use_pipeline(caller_reference, options = {})
22
+ AmazonFlexPay::Pipelines::MultiUse.new(options.merge(:caller_reference => caller_reference))
23
+ end
24
+
25
+ # Creates a pipeline that will authorize you to send money _to_ the user.
26
+ #
27
+ # See http://docs.amazonwebservices.com/AmazonFPS/latest/FPSAdvancedGuide/CBUIapiMerchant.html
28
+ def recipient_pipeline(caller_reference, options = {})
29
+ AmazonFlexPay::Pipelines::Recipient.new(options.merge(:caller_reference => caller_reference))
30
+ end
31
+
32
+ # Creates a pipeline that will authorize you to send money _from_ the user one time.
33
+ #
34
+ # Note that if this payment fails, you must create another pipeline to get another token.
35
+ #
36
+ # See http://docs.amazonwebservices.com/AmazonFPS/2010-08-28/FPSBasicGuide/SingleUsePipeline.html
37
+ def single_use_pipeline(caller_reference, options = {})
38
+ AmazonFlexPay::Pipelines::SingleUse.new(options.merge(:caller_reference => caller_reference))
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,42 @@
1
+ module AmazonFlexPay
2
+ class << self
3
+ # Returns a signature for the given URL and parameters.
4
+ def signature(endpoint, params)
5
+ uri = URI.parse(endpoint)
6
+
7
+ signable_string = [
8
+ 'GET',
9
+ uri.host,
10
+ uri.path,
11
+ query_string(params)
12
+ ].join("\n")
13
+
14
+ Base64.encode64(OpenSSL::HMAC.digest(OpenSSL::Digest::SHA256.new, AmazonFlexPay.secret_key, signable_string)).strip
15
+ end
16
+
17
+ # Flattens a possibly-nested hash into a query string for Amazon.
18
+ # With Amazon, nested hashes are flattened with a period, as follows:
19
+ #
20
+ # AmazonFlexPay.query_string(:foo => {:hello => 'world'})
21
+ # => "foo.hello=world"
22
+ #
23
+ def query_string(params, prefix = nil) #:nodoc:
24
+ prefix = "#{prefix}." if prefix
25
+ params.keys.sort { |a, b| a.to_s <=> b.to_s }.collect do |key|
26
+ case val = params[key]
27
+ when Hash
28
+ query_string(val, key)
29
+ else
30
+ "#{prefix}#{key}=#{escape val}"
31
+ end
32
+ end.join('&')
33
+ end
34
+
35
+ UNSAFE = /[^A-Za-z0-9_.~-]/ #:nodoc:
36
+ # Amazon is very specific about which chars should be escaped, and which should not.
37
+ def escape(value) #:nodoc:
38
+ # note that URI.escape(' ') => '%20', and CGI.escape(' ') => '+'
39
+ URI.escape(value.to_s, UNSAFE)
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,53 @@
1
+ require 'uri'
2
+ require 'base64'
3
+ require 'openssl'
4
+
5
+ require 'rubygems'
6
+ require 'rest_client'
7
+ require 'multi_xml'
8
+ require 'active_support/core_ext/string/inflections' # camelcase, underscore
9
+
10
+ require 'amazon_flex_pay/signing'
11
+ require 'amazon_flex_pay/model'
12
+ require 'amazon_flex_pay/data_types'
13
+ require 'amazon_flex_pay/enumerations'
14
+
15
+ require 'amazon_flex_pay/api'
16
+ require 'amazon_flex_pay/pipelines'
17
+
18
+ module AmazonFlexPay
19
+ VERSION = '0.9.2'
20
+ API_VERSION = '2010-08-28'
21
+ PIPELINE_VERSION = '2009-01-09'
22
+
23
+ class << self
24
+ attr_accessor :access_key
25
+ attr_accessor :secret_key
26
+
27
+ # The URL used for API calls.
28
+ #
29
+ # Defaults to the sandbox unless you set it explicitly or call <tt>go_live!</tt>.
30
+ def api_endpoint
31
+ @api_endpoint ||= 'https://fps.sandbox.amazonaws.com/'
32
+ end
33
+ attr_writer :api_endpoint
34
+
35
+ # The URL used for pipeline redirects.
36
+ #
37
+ # Defaults to the sandbox unless you set it explicitly or call <tt>go_live!</tt>.
38
+ def pipeline_endpoint
39
+ @pipeline_endpoint ||= 'https://authorize.payments-sandbox.amazon.com/cobranded-ui/actions/start'
40
+ end
41
+ attr_writer :pipeline_endpoint
42
+
43
+ # By default all API calls and pipeline redirects are in the Amazon Payments sandbox.
44
+ #
45
+ # Call <tt>AmazonFlexPay.go_live!</tt> to enable live transactions and real money in this environment.
46
+ def go_live!
47
+ self.api_endpoint = 'https://fps.amazonaws.com/'
48
+ self.pipeline_endpoint = 'https://authorize.payments.amazon.com/cobranded-ui/actions/start'
49
+ end
50
+ end
51
+
52
+ end
53
+
@@ -0,0 +1,117 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ class AmazonFlexPayTest < AmazonFlexPay::Test
4
+ ## signing
5
+
6
+ should "generate a valid v2 signature" do
7
+ # NOTE: I'm not sure of a supplied signature example that I can copy, so
8
+ # I set this one up by making sure signatures were being accepted by
9
+ # Amazon and then generating and saving my own example. Kinda backwards
10
+ # but good enough for regression testing.
11
+ assert_equal "Ro7iH0M+1hIR/SXGvT1kmF6Tg5uUKRSUd1AWaJHOcpE=", AmazonFlexPay.signature('http://example.com/api', {:hello => 'world'})
12
+ end
13
+
14
+ ## query strings
15
+
16
+ should "create a sorted query string" do
17
+ assert_equal "a=1&b=2&c=3&d=4&e=5", AmazonFlexPay.query_string(:a => 1, :b => 2, :c => 3, :d => 4, :e => 5)
18
+ end
19
+
20
+ should "flatten nested hashes into a query string using periods" do
21
+ assert_equal "a.a=1&a.b=2&b=3", AmazonFlexPay.query_string(:b => 3, :a => {:a => 1, :b => 2})
22
+ end
23
+
24
+ should "percent-encode spaces and other characters for a query string" do
25
+ assert_equal 'a=hello%20world%21', AmazonFlexPay.query_string(:a => 'hello world!')
26
+ end
27
+
28
+ ## verifying a request
29
+
30
+ should "verify a GET request" do
31
+ request = stub(:get? => true, :protocol => 'http://', :host_with_port => 'example.com', :path => '/foo/bar', :query_string => 'a=1&b=2')
32
+ AmazonFlexPay.expects(:verify_signature).with('http://example.com/foo/bar', 'a=1&b=2').returns(true)
33
+ assert AmazonFlexPay.verify_request(request)
34
+ end
35
+
36
+ should "verify a POST request" do
37
+ request = stub(:get? => false, :protocol => 'http://', :host_with_port => 'example.com', :path => '/foo/bar', :raw_post => 'a=1&b=2')
38
+ AmazonFlexPay.expects(:verify_signature).with('http://example.com/foo/bar', 'a=1&b=2').returns(true)
39
+ assert AmazonFlexPay.verify_request(request)
40
+ end
41
+
42
+ # api basics
43
+
44
+ class TestRequest < AmazonFlexPay::API::Base
45
+ attribute :foo
46
+ attribute :amount, :type => :amount
47
+
48
+ class Response < AmazonFlexPay::API::Base::BaseResponse; end
49
+ end
50
+
51
+ should "add necessary fields and sign api requests" do
52
+ Time.stubs(:now).returns(Time.parse('Jan 1 2011')) # so the signature remains constant
53
+
54
+ request = TestRequest.new(:foo => 'bar', :amount => {:value => '3.14', :currency_code => 'USD'})
55
+ params = request.to_params
56
+
57
+ # simple attributes
58
+ assert_equal 'bar', params['Foo']
59
+
60
+ # complex attributes
61
+ assert_equal '3.14', params['Amount']['Value']
62
+ assert_equal 'USD', params['Amount']['CurrencyCode']
63
+
64
+ # standard additions
65
+ assert_equal 'foo', params['AWSAccessKeyId']
66
+ assert_equal 'TestRequest', params['Action']
67
+ assert_equal '2010-08-28', params['Version']
68
+
69
+ # the signature is backwards-calculated for regression testing
70
+ assert_equal 'kVNr+W7L3Z/A6sBrcz1FHdshQqPFU0YOPZJpMglofNk=', params['Signature']
71
+ assert_equal 'HmacSHA256', params['SignatureMethod']
72
+ assert_equal 2, params['SignatureVersion']
73
+ end
74
+
75
+ should "store the request in the response" do
76
+ RestClient.expects(:get).returns(stub(:body => cancel_token_response))
77
+ response = TestRequest.new(:foo => 'bar').submit
78
+ assert_equal 'bar', response.request.foo
79
+ end
80
+
81
+ should "catch and parse errors" do
82
+ http_response = RestClient::Response.create(error_response, nil, nil)
83
+ RestClient.expects(:get).raises(RestClient::BadRequest.new(http_response))
84
+
85
+ response = TestRequest.new(:foo => 'bar').submit
86
+ assert response.request_id
87
+ assert response.error?
88
+ assert response.errors.first.code
89
+ assert response.errors.first.message
90
+ end
91
+
92
+ should "not allow unknown values for enumerated attributes" do
93
+ assert_raises ArgumentError do TestRequest.new(:amount => {:currency_code => 'UNKOWN'}) end
94
+ end
95
+
96
+ # pipeline basics
97
+
98
+ class TestPipeline < AmazonFlexPay::Pipelines::Base
99
+ attribute :foo
100
+ end
101
+
102
+ should "add necessary fields and sign pipeline urls" do
103
+ Time.stubs(:now).returns(Time.parse('Jan 1 2011')) # so the signature remains constant
104
+
105
+ pipeline = TestPipeline.new(:foo => 'bar')
106
+ params = pipeline.to_params('http://example.com/return')
107
+
108
+ assert_equal 'TestPipeline', params['pipelineName']
109
+ assert_equal 'foo', params['callerKey']
110
+ assert_equal '2009-01-09', params['version']
111
+ assert_equal 'http://example.com/return', params['returnURL']
112
+
113
+ assert_equal 2, params['signatureVersion']
114
+ assert_equal 'HmacSHA256', params['signatureMethod']
115
+ assert_equal 'OuUJQqFBJhezmcWOAhDGcsD/6OXpOLVlcbF3XMIZO3U=', params['signature']
116
+ end
117
+ end