amazon_flex_pay 0.9.2
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README.rdoc +41 -0
- data/Rakefile +23 -0
- data/lib/amazon_flex_pay/api/base.rb +86 -0
- data/lib/amazon_flex_pay/api/cancel.rb +11 -0
- data/lib/amazon_flex_pay/api/cancel_token.rb +9 -0
- data/lib/amazon_flex_pay/api/get_account_activity.rb +21 -0
- data/lib/amazon_flex_pay/api/get_account_balance.rb +8 -0
- data/lib/amazon_flex_pay/api/get_recipient_verification_status.rb +9 -0
- data/lib/amazon_flex_pay/api/get_token_by_caller.rb +10 -0
- data/lib/amazon_flex_pay/api/get_token_usage.rb +9 -0
- data/lib/amazon_flex_pay/api/get_tokens.rb +13 -0
- data/lib/amazon_flex_pay/api/get_transaction.rb +9 -0
- data/lib/amazon_flex_pay/api/get_transaction_status.rb +13 -0
- data/lib/amazon_flex_pay/api/pay.rb +20 -0
- data/lib/amazon_flex_pay/api/refund.rb +14 -0
- data/lib/amazon_flex_pay/api/reserve.rb +20 -0
- data/lib/amazon_flex_pay/api/settle.rb +11 -0
- data/lib/amazon_flex_pay/api/verify_signature.rb +14 -0
- data/lib/amazon_flex_pay/api.rb +162 -0
- data/lib/amazon_flex_pay/data_types.rb +138 -0
- data/lib/amazon_flex_pay/enumerations.rb +23 -0
- data/lib/amazon_flex_pay/model.rb +130 -0
- data/lib/amazon_flex_pay/pipelines/base.rb +40 -0
- data/lib/amazon_flex_pay/pipelines/edit_token.rb +6 -0
- data/lib/amazon_flex_pay/pipelines/multi_use.rb +28 -0
- data/lib/amazon_flex_pay/pipelines/recipient.rb +10 -0
- data/lib/amazon_flex_pay/pipelines/single_use.rb +24 -0
- data/lib/amazon_flex_pay/pipelines.rb +41 -0
- data/lib/amazon_flex_pay/signing.rb +42 -0
- data/lib/amazon_flex_pay.rb +53 -0
- data/test/amazon_flex_pay_test.rb +117 -0
- data/test/api_test.rb +310 -0
- data/test/pipelines_test.rb +46 -0
- data/test/response_samples.rb +588 -0
- data/test/test_helper.rb +22 -0
- 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,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
|