amazon_flex_pay 0.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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