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.
- 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
|