remit 0.0.1 → 0.0.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 CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2007-2008 Tyler Hunt
1
+ Copyright (c) 2007-2009 Tyler Hunt
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.markdown ADDED
@@ -0,0 +1,91 @@
1
+ Remit
2
+ =====
3
+
4
+ This API provides access to the Amazon Flexible Payment Service (FPS). After
5
+ trying to get the SOAP version of the API written, I began working on this REST
6
+ version to provide a cohesive means of access to all of the functionality of
7
+ the FPS without having to get dirty dealing with SOAP requests.
8
+
9
+ I hope you enjoy using it as much as I've enjoyed writing it. I'm interested to
10
+ hear what sort of uses you find for it. If you find any bugs, let me know (or
11
+ better yet, submit a patch).
12
+
13
+
14
+ Sandbox
15
+ -------
16
+
17
+ Amazon provides a testing environment for the FPS called a sandbox. You may
18
+ (and should) use the sandbox while testing your application. It can be enabled
19
+ by passing a value of true to the last argument of the API constructor.
20
+
21
+
22
+ Getting Started
23
+ ---------------
24
+
25
+ The following example shows how to load up the API, initialize the service, and
26
+ make a simple call to get the tokens stored on the account:
27
+
28
+ gem 'remit'
29
+ require 'remit'
30
+
31
+ ACCESS_KEY = '<your AWS access key>'
32
+ SECRET_KEY = '<your AWS secret key>'
33
+
34
+ # connect using the API's sandbox mode
35
+ remit = Remit::API.new(ACCESS_KEY, SECRET_KEY, true)
36
+
37
+ response = remit.get_tokens
38
+ puts response.tokens.first.token_id
39
+
40
+
41
+ Using with Rails
42
+ ----------------
43
+
44
+ To use Remit in a Rails application, you must first specify a dependency on the
45
+ Remit gem in your config/environment.rb file:
46
+
47
+ config.gem 'remit', :version => '~> 0.0.1'
48
+
49
+ Then you should create an initializer to configure your Amazon keys. Create the
50
+ file config/initializers/remit.rb with the following contents:
51
+
52
+ config_file = File.join(Rails.root, 'config', 'amazon_fps.yml')
53
+ config = YAML.load_file(config_file)[RAILS_ENV].symbolize_keys
54
+
55
+ FPS_ACCESS_KEY = config[:access_key]
56
+ FPS_SECRET_KEY = config[:secret_key]
57
+
58
+ Then create the YAML file config/amazon_fps.yml:
59
+
60
+ development: &sandbox
61
+ access_key: <your sandbox access key>
62
+ secret_key: <your sandbox secret key>
63
+
64
+ test:
65
+ <<: *sandbox
66
+
67
+ production:
68
+ access_key: <your access key>
69
+ secret_key: <your secret key>
70
+
71
+ To instantiate and use the Remit API in your application, you could define a
72
+ method in your ApplicationController like this:
73
+
74
+ def remit
75
+ @remit ||= begin
76
+ sandbox = !Rails.env.production?
77
+ Remit::API.new(FPS_ACCESS_KEY, FPS_SECRET_KEY, sandbox)
78
+ end
79
+ end
80
+
81
+
82
+ Sites Using Remit
83
+ -----------------
84
+
85
+ The following production sites are currently using Remit:
86
+
87
+ * http://www.storenvy.com/
88
+ * http://www.obsidianportal.com/
89
+
90
+
91
+ Copyright (c) 2007-2009 Tyler Hunt, released under the MIT license
data/lib/remit/common.rb CHANGED
@@ -11,30 +11,28 @@ module Remit
11
11
  parameter :action, :value => name
12
12
  end
13
13
 
14
- protected
15
-
16
14
  def convert_key(key)
17
15
  key.to_s.gsub(/(^|_)(.)/) { $2.upcase }.to_sym
18
16
  end
17
+ protected :convert_key
19
18
  end
20
19
 
21
20
  class BaseResponse < Relax::Response
22
- private
23
-
24
- def node_name(name)
25
- name.to_s.gsub(/(^|_)(.)/) { $2.upcase }
21
+ def node_name(name, namespace=nil)
22
+ super(name.to_s.gsub(/(^|_)(.)/) { $2.upcase }, namespace)
26
23
  end
27
24
  end
28
25
 
29
26
  class Response < BaseResponse
30
27
  parameter :request_id
28
+
31
29
  attr_accessor :status
32
30
  attr_accessor :errors
33
31
 
34
32
  def initialize(xml)
35
33
  super
36
34
 
37
- if is?(:Response) and has?(:Errors)
35
+ if is?(:Response) && has?(:Errors)
38
36
  @errors = elements(:Errors).collect do |error|
39
37
  Error.new(error)
40
38
  end
@@ -42,7 +40,7 @@ module Remit
42
40
  @status = text_value(element(:Status))
43
41
  @errors = elements('errors/errors').collect do |error|
44
42
  ServiceError.new(error)
45
- end if not successful?
43
+ end unless successful?
46
44
  end
47
45
  end
48
46
 
@@ -50,26 +48,26 @@ module Remit
50
48
  @status == ResponseStatus::SUCCESS
51
49
  end
52
50
 
53
- private
54
-
55
- def node_name(name)
56
- name.to_s.gsub(/(^|_)(.)/) { $2.upcase }
51
+ def node_name(name, namespace=nil)
52
+ super(name.to_s.split('/').collect{ |tag|
53
+ tag.gsub(/(^|_)(.)/) { $2.upcase }
54
+ }.join('/'), namespace)
57
55
  end
58
56
  end
59
57
 
60
58
  class SignedQuery < Relax::Query
61
- def initialize(uri, secret_key, query = {})
59
+ def initialize(uri, secret_key, query={})
62
60
  super(query)
63
61
  @uri = URI.parse(uri.to_s)
64
62
  @secret_key = secret_key
65
63
  end
66
64
 
67
65
  def sign
68
- delete_if { |key, value| key == :awsSignature }
66
+ delete(:awsSignature)
69
67
  store(:awsSignature, Base64.encode64(OpenSSL::HMAC.digest(OpenSSL::Digest::SHA1.new, @secret_key, "#{@uri.path}?#{to_s(false)}".gsub('%20', '+'))).strip)
70
68
  end
71
69
 
72
- def to_s(signed = true)
70
+ def to_s(signed=true)
73
71
  sign if signed
74
72
  super()
75
73
  end
@@ -77,10 +75,12 @@ module Remit
77
75
  class << self
78
76
  def parse(uri, secret_key, query_string)
79
77
  query = self.new(uri, secret_key)
78
+
80
79
  query_string.split('&').each do |parameter|
81
- key, value = parameter.split('=')
80
+ key, value = parameter.split('=', 2)
82
81
  query[key] = unescape_value(value)
83
82
  end
83
+
84
84
  query
85
85
  end
86
86
  end
@@ -9,6 +9,16 @@ module Remit
9
9
  parameter :amount, :type => :float
10
10
  end
11
11
 
12
+ class TemporaryDeclinePolicy < BaseResponse
13
+ parameter :temporary_decline_policy_type
14
+ parameter :implicit_retry_timeout_in_mins
15
+ end
16
+
17
+ class DescriptorPolicy < BaseResponse
18
+ parameter :soft_descriptor_type
19
+ parameter :CS_number_of
20
+ end
21
+
12
22
  class ChargeFeeTo
13
23
  CALLER = 'Caller'
14
24
  RECIPIENT = 'Recipient'
@@ -80,6 +90,21 @@ module Remit
80
90
  parameter :status
81
91
  parameter :status_detail
82
92
  parameter :new_sender_token_usage, :type => TokenUsageLimit
93
+
94
+ %w(reserved success failure initiated reinitiated temporary_decline).each do |status_name|
95
+ define_method("#{status_name}?") do
96
+ self.status == Remit::TransactionStatus.const_get(status_name.sub('_', '').upcase)
97
+ end
98
+ end
99
+ end
100
+
101
+ class TransactionStatus
102
+ RESERVED = 'Reserved'
103
+ SUCCESS = 'Success'
104
+ FAILURE = 'Failure'
105
+ INITIATED = 'Initiated'
106
+ REINITIATED = 'Reinitiated'
107
+ TEMPORARYDECLINE = 'TemporaryDecline'
83
108
  end
84
109
 
85
110
  class TokenType
@@ -88,7 +113,7 @@ module Remit
88
113
  RECURRING = 'Recurring'
89
114
  UNRESTRICTED = 'Unrestricted'
90
115
  end
91
-
116
+
92
117
  class PipelineName
93
118
  SINGLE_USE = 'SingleUse'
94
119
  MULTI_USE = 'MultiUse'
@@ -97,11 +122,43 @@ module Remit
97
122
  SETUP_PREPAID = 'SetupPrepaid'
98
123
  SETUP_POSTPAID = 'SetupPostpaid'
99
124
  end
100
-
125
+
126
+ class PipelineStatusCode
127
+ CALLER_EXCEPTION = 'CE' # problem with your code
128
+ SYSTEM_ERROR = 'SE' # system error, try again
129
+ SUCCESS_ABT = 'SA' # successful payment with Amazon balance
130
+ SUCCESS_ACH = 'SB' # successful payment with bank transfer
131
+ SUCCESS_CC = 'SC' # successful payment with credit card
132
+ ABORTED = 'A' # user aborted payment
133
+ PAYMENT_METHOD_MISMATCH = 'PE' # user does not have payment method requested
134
+ PAYMENT_METHOD_UNSUPPORTED = 'NP' # account doesn't support requested payment method
135
+ INVALID_CALLER = 'NM' # you are not a valid 3rd party caller to the transaction
136
+ SUCCESS_RECIPIENT_TOKEN_INSTALLED = 'SR'
137
+ end
138
+
101
139
  module RequestTypes
102
140
  class Amount < Remit::Request
103
141
  parameter :amount
104
142
  parameter :currency_code
105
143
  end
144
+
145
+ class TemporaryDeclinePolicy < Remit::Request
146
+ parameter :temporary_decline_policy_type
147
+ parameter :implicit_retry_timeout_in_mins
148
+ end
149
+
150
+ class DescriptorPolicy < Remit::Request
151
+ parameter :soft_descriptor_type
152
+ parameter :CS_number_of
153
+ end
154
+ end
155
+
156
+ class Operation
157
+ PAY = "Pay"
158
+ REFUND = "Refund"
159
+ SETTLE = "Settle"
160
+ SETTLE_DEBT = "SettleDebt"
161
+ WRITE_OFF_DEBT = "WriteOffDebt"
162
+ FUND_PREPAID = "FundPrepaid"
106
163
  end
107
164
  end
@@ -0,0 +1,118 @@
1
+ # Scraped and categorized from http://docs.amazonwebservices.com/AmazonFPS/\
2
+ # 2007-01-08/FPSDeveloperGuide/index.html?ErrorCodesTable.html. You can use
3
+ # these categories to specify default error handling in your application such
4
+ # as asking users to retry or sending an exception email.
5
+ module Remit::ErrorCodes
6
+ class << self
7
+ def sender_error?(code)
8
+ SENDER.include? code.to_sym
9
+ end
10
+
11
+ def recipient_error?(code)
12
+ RECIPIENT.include? code.to_sym
13
+ end
14
+
15
+ def caller_error?(code)
16
+ CALLER.include?(code.to_sym)
17
+ end
18
+
19
+ def amazon_error?(code)
20
+ AMAZON.include? code.to_sym
21
+ end
22
+
23
+ def api_error?(code)
24
+ API.include? code.to_sym
25
+ end
26
+
27
+ def unknown_error?(code)
28
+ UNKNOWN.include? code.to_sym
29
+ end
30
+ end
31
+
32
+ SENDER = [
33
+ :InactiveAccount_Sender, # The sender's account is in suspended or closed state.
34
+ :InactiveInstrument, # The payment instrument used for this transaction is no longer active.
35
+ :InstrumentExpired, # The prepaid or the postpaid instrument has expired.
36
+ :InstrumentNotActive, # The prepaid or postpaid instrument used in the transaction is not active.
37
+ :InvalidAccountState_Sender, # Sender account cannot participate in the transaction.
38
+ :InvalidInstrumentForAccountType, # The sender account can use only credit cards
39
+ :InvalidInstrumentState, # The prepaid or credit instrument should be active
40
+ :InvalidTokenId_Sender, # The send token specified is either invalid or canceled or the token is not active.
41
+ :PaymentInstrumentNotCC, # The payment method specified in the transaction is not a credit card. You can only use a credit card for this transaction.
42
+ :PaymentInstrumentMissing, # There needs to be a payment instrument defined in the token which defines the payment method.
43
+ :TokenNotActive_Sender, # The sender token is canceled.
44
+ :UnverifiedAccount_Sender, # The sender's account must have a verified U.S. credit card or a verified U.S bank account before this transaction can be initiated
45
+ :UnverifiedBankAccount, # A verified bank account should be used for this transaction
46
+ :UnverifiedEmailAddress_Sender, # The sender account must have a verified e-mail address for this payment
47
+ ]
48
+
49
+ RECIPIENT = [
50
+ :InactiveAccount_Recipient, # The recipient's account is in suspended or closed state.
51
+ :InvalidAccountState_Recipient, # Recipient account cannot participate in the transaction
52
+ :InvalidRecipientRoleForAccountType, # The recipient account is not allowed to receive payments
53
+ :InvalidRecipientForCCTransaction, # This account cannot receive credit card payments.
54
+ :InvalidTokenId_Recipient, # The recipient token specified is either invalid or canceled.
55
+ :TokenNotActive_Recipient, # The recipient token is canceled.
56
+ :UnverifiedAccount_Recipient, # The recipient's account must have a verified bank account or a credit card before this transaction can be initiated.
57
+ :UnverifiedEmailAddress_Recipient, # The recipient account must have a verified e-mail address for receiving payments.
58
+ ]
59
+
60
+ CALLER = [
61
+ :InactiveAccount_Caller, # The caller's account is in suspended or closed state.
62
+ :InvalidAccountState_Caller, # The caller account cannot participate in the transaction
63
+ :InvalidTokenId_Caller, # The caller token specified is either invalid or canceled or the specified token is not active.
64
+ :TokenNotActive_Caller, # The caller token is canceled.
65
+ :UnverifiedEmailAddress_Caller, # The caller account must have a verified e-mail address
66
+ ]
67
+
68
+ AMAZON = [
69
+ :InternalError # A retriable error that happens due to some transient problem in the system.
70
+ ]
71
+
72
+ # bad syntax or logic
73
+ API = [
74
+ :AmountOutOfRange, # The transaction amount is more than the allowed range.
75
+ :BadRule, # One of the GK constructs is not well defined
76
+ :CannotSpecifyUsageForSingleUseToken, # Token usages cannot be specified for a single use token.
77
+ :ConcurrentModification, # A retriable error can happen due to concurrent modification of data by two processes.
78
+ :DuplicateRequest, # A different request associated with this caller reference already exists.
79
+ :IncompatibleTokens, # The transaction could not be completed because the tokens have incompatible payment instructions.
80
+ :InstrumentAccessDenied, # The external calling application is not the recipient for this postpaid or prepaid instrument. The caller should be the liability holder
81
+ :InvalidCallerReference, # The CallerReferece does not have a token associated with it.
82
+ :InvalidDateRange, # The end date specified is before the start date or the start date is in the future.
83
+ :InvalidEvent, # The event specified was not subscribed using the SubscribeForCallerNotification operation.
84
+ :InvalidParams, # One or more parameters in the request is invalid.
85
+ :InvalidPaymentInstrument, # The payment method used in the transaction is invalid.
86
+ :InvalidPaymentMethod, # Payment method specified in the GK construct is invalid.
87
+ :InvalidSenderRoleForAccountType, # This token cannot be used for this operation.
88
+ :InvalidTokenId, # The token that you are trying to cancel was not installed by you.
89
+ :InvalidTokenType, # Invalid operation performed on the token. Example, getting the token usage information on a single use token.
90
+ :InvalidTransactionId, # The specified transaction could not be found or the caller did not execute the transaction or this is not a Pay or Reserve call.
91
+ :InvalidTransactionState, # The transaction is not completed or it has been temporarily failed.
92
+ :InvalidUsageDuration, # The duration cannot be less than one hour.
93
+ :InvalidUsageLimitCount, # The usage count is null or empty.
94
+ :InvalidUsageStartTime, # The start time specified for the token is not valid.
95
+ :InvalidUsageType, # The usage type specified is invalid.
96
+ :OriginalTransactionIncomplete, # The original transaction is still in progress.
97
+ :OriginalTransactionFailed, # The original transaction has failed
98
+ :PaymentMethodNotDefined, # Payment method is not defined in the transaction.
99
+ :RefundAmountExceeded, # The refund amount is more than the refundable amount.
100
+ :SameTokenIdUsedMultipleTimes, # This token is already used in earlier transactions.
101
+ :SenderNotOriginalRecipient, # The sender in the refund transaction is not the recipient of the original transaction.
102
+ :SettleAmountGreaterThanReserveAmount, # The amount being settled is greater than the reserved amount.
103
+ :TransactionDenied, # This transaction is not allowed.
104
+ :TransactionExpired, # Returned when the Caller attempts to explicitly retry a transaction that is temporarily declined and is in queue for implicit retry.
105
+ :TransactionFullyRefundedAlready, # The complete refund for this transaction is already completed
106
+ :TransactionTypeNotRefundable, # You cannot refund this transaction.
107
+ :TokenAccessDenied, # Permission is denied to cancel the token.
108
+ :TokenUsageError, # The token usage limit is exceeded.
109
+ :UsageNotDefined, # For a multi-use token or a recurring token the usage limits are not specified in the GateKeeper text.
110
+ ]
111
+
112
+ # these errors don't specify who is at fault
113
+ UNKNOWN = [
114
+ :InvalidAccountState, # The account is either suspended or closed. Payment instructions cannot be installed on this account.
115
+ :InsufficientBalance, # The sender, caller, or recipient's account balance has insufficient funds to complete the transaction.
116
+ :AccountLimitsExceeded, # The spending or the receiving limit on the account is exceeded
117
+ ]
118
+ end
@@ -33,12 +33,11 @@ module Remit
33
33
  attr_reader :api
34
34
 
35
35
  parameter :pipeline_name
36
- parameter :return_URL
36
+ parameter :return_url
37
37
  parameter :caller_key
38
38
 
39
- def initialize(api, pipeline, options)
39
+ def initialize(api, options)
40
40
  @api = api
41
- @pipeline = pipeline
42
41
 
43
42
  options.each do |k,v|
44
43
  self.send("#{k}=", v)
@@ -46,7 +45,7 @@ module Remit
46
45
  end
47
46
 
48
47
  def url
49
- uri = URI.parse(@pipeline)
48
+ uri = URI.parse(@api.pipeline_url)
50
49
 
51
50
  query = {}
52
51
  self.class.parameters.each do |p|
@@ -61,7 +60,7 @@ module Remit
61
60
  # Remove any unused optional parameters
62
61
  query.reject! { |key, value| value.nil? }
63
62
 
64
- uri.query = SignedQuery.new(@api.pipeline, @api.secret_key, query).to_s
63
+ uri.query = SignedQuery.new(@api.pipeline_url, @api.secret_key, query).to_s
65
64
  uri.to_s
66
65
  end
67
66
  end
@@ -72,6 +71,48 @@ module Remit
72
71
  parameter :payment_method
73
72
  parameter :transaction_amount
74
73
  parameter :recipient_token
74
+
75
+ def pipeline_name
76
+ Remit::PipelineName::SINGLE_USE
77
+ end
78
+ end
79
+
80
+ class MultiUsePipeline < Pipeline
81
+ parameter :caller_reference
82
+ parameter :payment_reason
83
+ parameter :recipient_token_list
84
+ parameter :amount_type
85
+ parameter :transaction_amount
86
+ parameter :validity_start
87
+ parameter :validity_expiry
88
+ parameter :payment_method
89
+ parameter :global_amount_limit
90
+ parameter :usage_limit_type_1
91
+ parameter :usage_limit_period_1
92
+ parameter :usage_limit_value_1
93
+ parameter :usage_limit_type_2
94
+ parameter :usage_limit_period_2
95
+ parameter :usage_limit_value_2
96
+ parameter :is_recipient_cobranding
97
+
98
+ def pipeline_name
99
+ Remit::PipelineName::MULTI_USE
100
+ end
101
+ end
102
+
103
+ class RecipientPipeline < Pipeline
104
+ parameter :caller_reference
105
+ parameter :validity_start # Time or seconds from Epoch
106
+ parameter :validity_expiry # Time or seconds from Epoch
107
+ parameter :payment_method
108
+ parameter :recipient_pays_fee
109
+ parameter :caller_reference_refund
110
+ parameter :max_variable_fee
111
+ parameter :max_fixed_fee
112
+
113
+ def pipeline_name
114
+ Remit::PipelineName::RECIPIENT
115
+ end
75
116
  end
76
117
 
77
118
  class RecurringUsePipeline < Pipeline
@@ -82,7 +123,11 @@ module Remit
82
123
  parameter :validity_start # Time or seconds from Epoch
83
124
  parameter :validity_expiry # Time or seconds from Epoch
84
125
  parameter :payment_method
85
- parameter :recurring_period
126
+ parameter :recurring_period
127
+
128
+ def pipeline_name
129
+ Remit::PipelineName::RECURRING
130
+ end
86
131
  end
87
132
 
88
133
  class PostpaidPipeline < Pipeline
@@ -100,11 +145,23 @@ module Remit
100
145
  parameter :usage_limit_type2
101
146
  parameter :usage_limit_period2
102
147
  parameter :usage_limit_value2
148
+
149
+ def pipeline_name
150
+ Remit::PipelineName::SETUP_POSTPAID
151
+ end
103
152
  end
104
153
 
105
154
  def get_single_use_pipeline(options)
106
155
  self.get_pipeline(SingleUsePipeline, options)
107
156
  end
157
+
158
+ def get_multi_use_pipeline(options)
159
+ self.get_pipeline(MultiUsePipeline, options)
160
+ end
161
+
162
+ def get_recipient_pipeline(options)
163
+ self.get_pipeline(RecipientPipeline, options)
164
+ end
108
165
 
109
166
  def get_recurring_use_pipeline(options)
110
167
  self.get_pipeline(RecurringUsePipeline, options)
@@ -115,7 +172,7 @@ module Remit
115
172
  end
116
173
 
117
174
  def get_pipeline(pipeline_subclass, options)
118
- pipeline = pipeline_subclass.new(self, @pipeline, {
175
+ pipeline = pipeline_subclass.new(self, {
119
176
  :caller_key => @access_key
120
177
  }.merge(options))
121
178
  end
@@ -11,12 +11,13 @@ module Remit
11
11
  class Response < Remit::Response
12
12
  class TransactionResults < Remit::BaseResponse
13
13
  parameter :transaction_id
14
- parameter :operation_type
14
+ parameter :operation_type, :element => :operation
15
15
  parameter :caller_reference
16
- parameter :transaction_status
16
+ parameter :transaction_status, :element => :status
17
17
  end
18
18
 
19
- parameter :transaction_results, :type => TransactionResults
19
+ parameter :transaction_results, :collection => TransactionResults
20
+ parameter :number_pending, :type => :integer
20
21
  end
21
22
 
22
23
  def get_results(request = Request.new)
@@ -0,0 +1,56 @@
1
+ require 'base64'
2
+ require 'openssl/digest'
3
+
4
+ module Remit
5
+
6
+ ##
7
+ # Encapsulates the logic for IPN request validation and attribute retrieval.
8
+ #
9
+ class IpnRequest
10
+
11
+ # Signature key name used by AmazonFPS IPNs
12
+ SIGNATURE_KEY = 'signature'
13
+
14
+ ##
15
+ # +params+ should be your controllers request parameters.
16
+ #
17
+ def initialize(params, secret_key)
18
+ raise ArgumentError, "Expected the request params hash, received: #{params.inspect}" unless params.kind_of?(Hash)
19
+ @params = strip_keys_from(params, 'action', 'controller')
20
+ @supplied_signature = @params.delete(SIGNATURE_KEY)
21
+ @secret_key = secret_key
22
+ end
23
+
24
+ def valid?
25
+ return false unless @supplied_signature
26
+ generate_signature_for(@params) == @supplied_signature
27
+ end
28
+
29
+ def method_missing(method, *args) #:nodoc:
30
+ if @params.has_key?(method.to_s)
31
+ @params[method.to_s]
32
+ else
33
+ super(method, *args)
34
+ end
35
+ end
36
+
37
+
38
+ private
39
+
40
+
41
+ def generate_signature_for(params)
42
+ query = params.sort_by { |k,v| k.downcase }
43
+ digest = OpenSSL::Digest::Digest.new('sha1')
44
+ hmac = OpenSSL::HMAC.digest(digest, @secret_key, query.to_s)
45
+ encoded = Base64.encode64(hmac).chomp
46
+ end
47
+
48
+ def strip_keys_from(params, *ignore_keys)
49
+ parsed = params.dup
50
+ ignore_keys.each { |key| parsed.delete(key) }
51
+ parsed
52
+ end
53
+
54
+ end
55
+
56
+ end
data/lib/remit/pay.rb CHANGED
@@ -8,6 +8,9 @@ module Remit
8
8
  parameter :caller_reference
9
9
  parameter :caller_token_id
10
10
  parameter :charge_fee_to
11
+ parameter :descriptor_policy, :type => Remit::RequestTypes::DescriptorPolicy
12
+ parameter :marketplace_fixed_fee, :type => Remit::RequestTypes::Amount
13
+ parameter :marketplace_variable_fee
11
14
  parameter :meta_data
12
15
  parameter :recipient_description
13
16
  parameter :recipient_reference
@@ -15,16 +18,14 @@ module Remit
15
18
  parameter :sender_description
16
19
  parameter :sender_reference
17
20
  parameter :sender_token_id
21
+ parameter :temporary_decline_policy, :type => Remit::RequestTypes::TemporaryDeclinePolicy
18
22
  parameter :transaction_amount, :type => Remit::RequestTypes::Amount
19
23
  parameter :transaction_date
20
24
  end
21
25
 
22
26
  class Response < Remit::Response
23
- # FIXME: Due to an issue with Hpricot, Relax-0.0.4, and namespaces, the
24
- # transaction_response parameter is not parsed correctly and will always
25
- # be nil (http://groups.google.com/group/remit/t/1e0af072200d1bb3).
26
- # The suggested course of action is to operate on the raw XML.
27
- parameter :transaction_response, :type => TransactionResponse
27
+ parser :rexml
28
+ parameter :transaction_response, :namespace => 'ns3', :type => TransactionResponse
28
29
  end
29
30
 
30
31
  def pay(request = Request.new)
@@ -0,0 +1,62 @@
1
+ module Remit
2
+
3
+ class PipelineResponse
4
+
5
+ def initialize(uri, secret_key)
6
+ @uri = URI.parse(uri)
7
+ @secret_key = secret_key
8
+ end
9
+
10
+ ##
11
+ # Returns +true+ if the response is correctly signed (awsSignature).
12
+ #
13
+ #--
14
+ # The unescape_value method is used here because the awsSignature value
15
+ # pulled from the request is filtered through the same method.
16
+ #++
17
+ #
18
+ def valid?
19
+ return false unless given_signature
20
+ Relax::Query.unescape_value(correct_signature) == given_signature
21
+ end
22
+
23
+ ##
24
+ # Returns +true+ if the response returns a successful state.
25
+ #
26
+ def successful?
27
+ [
28
+ Remit::PipelineStatusCode::SUCCESS_ABT,
29
+ Remit::PipelineStatusCode::SUCCESS_ACH,
30
+ Remit::PipelineStatusCode::SUCCESS_CC,
31
+ Remit::PipelineStatusCode::SUCCESS_RECIPIENT_TOKEN_INSTALLED
32
+ ].include?(request_query[:status])
33
+ end
34
+
35
+
36
+ def method_missing(method, *args) #:nodoc:
37
+ if request_query.has_key?(method.to_sym)
38
+ request_query[method.to_sym]
39
+ else
40
+ super
41
+ end
42
+ end
43
+
44
+
45
+ private
46
+
47
+
48
+ def request_query(reload = false)
49
+ @query ||= Remit::SignedQuery.parse(@uri, @secret_key, @uri.query || '')
50
+ end
51
+
52
+ def given_signature
53
+ request_query[:awsSignature]
54
+ end
55
+
56
+ def correct_signature
57
+ Remit::SignedQuery.new(@uri.path, @secret_key, request_query).sign
58
+ end
59
+
60
+ end
61
+
62
+ end
data/lib/remit.rb CHANGED
@@ -8,6 +8,8 @@ require 'base64'
8
8
  require 'erb'
9
9
 
10
10
  require 'rubygems'
11
+
12
+ gem 'relax', '0.0.6'
11
13
  require 'relax'
12
14
 
13
15
  require 'remit/common'
@@ -15,6 +17,7 @@ require 'remit/data_types'
15
17
 
16
18
  require 'remit/cancel_token'
17
19
  require 'remit/discard_results'
20
+ require 'remit/error_codes'
18
21
  require 'remit/fund_prepaid'
19
22
  require 'remit/get_account_activity'
20
23
  require 'remit/get_account_balance'
@@ -32,7 +35,9 @@ require 'remit/get_token_by_caller'
32
35
  require 'remit/get_total_prepaid_liability'
33
36
  require 'remit/get_transaction'
34
37
  require 'remit/install_payment_instruction'
38
+ require 'remit/ipn_request'
35
39
  require 'remit/pay'
40
+ require 'remit/pipeline_response'
36
41
  require 'remit/refund'
37
42
  require 'remit/reserve'
38
43
  require 'remit/retry_transaction'
@@ -73,29 +78,29 @@ module Remit
73
78
  include UnsubscribeForCallerNotification
74
79
  include WriteOffDebt
75
80
 
76
- API_ENDPOINT = 'https://fps.amazonaws.com/'
77
- API_SANDBOX = 'https://fps.sandbox.amazonaws.com/'
78
- PIPELINE_ENDPOINT = 'https://authorize.payments.amazon.com/cobranded-ui/actions/start'
79
- PIPELINE_SANDBOX = 'https://authorize.payments-sandbox.amazon.com/cobranded-ui/actions/start'
80
- API_VERSION = Date.new(2007, 1, 8).to_s
81
- SIGNATURE_VERSION = 1
81
+ API_ENDPOINT = 'https://fps.amazonaws.com/'.freeze
82
+ API_SANDBOX_ENDPOINT = 'https://fps.sandbox.amazonaws.com/'.freeze
83
+ PIPELINE_URL = 'https://authorize.payments.amazon.com/cobranded-ui/actions/start'.freeze
84
+ PIPELINE_SANDBOX_URL = 'https://authorize.payments-sandbox.amazon.com/cobranded-ui/actions/start'.freeze
85
+ API_VERSION = Date.new(2007, 1, 8).to_s.freeze
86
+ SIGNATURE_VERSION = 1.freeze
82
87
 
83
- attr_reader :pipeline
84
88
  attr_reader :access_key
85
89
  attr_reader :secret_key
90
+ attr_reader :pipeline_url
86
91
 
87
- def initialize(access_key, secret_key, sandbox = false)
88
- super((not sandbox) ? API_ENDPOINT : API_SANDBOX)
89
- @pipeline = ((not sandbox) ? PIPELINE_ENDPOINT : PIPELINE_SANDBOX)
92
+ def initialize(access_key, secret_key, sandbox=false)
90
93
  @access_key = access_key
91
94
  @secret_key = secret_key
92
- end
95
+ @pipeline_url = sandbox ? PIPELINE_SANDBOX_URL : PIPELINE_URL
93
96
 
94
- private
97
+ super(sandbox ? API_SANDBOX_ENDPOINT : API_ENDPOINT)
98
+ end
95
99
 
96
- def new_query(query = {})
100
+ def new_query(query={})
97
101
  SignedQuery.new(@endpoint, @secret_key, query)
98
102
  end
103
+ private :new_query
99
104
 
100
105
  def default_query
101
106
  new_query({
@@ -105,19 +110,24 @@ module Remit
105
110
  :Timestamp => Time.now.utc.strftime('%Y-%m-%dT%H:%M:%SZ')
106
111
  })
107
112
  end
113
+ private :default_query
108
114
 
109
115
  def query(request)
110
116
  query = super
111
117
  query[:Signature] = sign(query)
112
118
  query
113
119
  end
120
+ private :query
114
121
 
115
122
  def sign(values)
116
123
  keys = values.keys.sort { |a, b| a.to_s.downcase <=> b.to_s.downcase }
124
+
117
125
  signature = keys.inject('') do |signature, key|
118
126
  signature += key.to_s + values[key].to_s
119
127
  end
128
+
120
129
  Base64.encode64(OpenSSL::HMAC.digest(OpenSSL::Digest::SHA1.new, @secret_key, signature)).strip
121
130
  end
131
+ private :sign
122
132
  end
123
133
  end
@@ -1,12 +1,12 @@
1
- require File.dirname(__FILE__) + '/spec_helper'
1
+ require File.dirname(__FILE__) + '/integrations_helper'
2
2
 
3
3
  describe 'a GetAccountActivity call' do
4
- it_should_behave_like 'a successful request'
4
+ it_should_behave_like 'a successful response'
5
5
 
6
6
  before(:all) do
7
- request = Remit::API::GetAccountActivityRequest.new
7
+ request = Remit::GetAccountActivity::Request.new
8
8
  request.start_date = Date.today - 7
9
- @response = @remit.get_account_activity(request)
9
+ @response = remit.get_account_activity(request)
10
10
  end
11
11
 
12
12
  it 'should have a collection of transactions' do
@@ -1,10 +1,10 @@
1
- require File.dirname(__FILE__) + '/spec_helper'
1
+ require File.dirname(__FILE__) + '/integrations_helper'
2
2
 
3
3
  describe 'a GetTokens call' do
4
- it_should_behave_like 'a successful request'
4
+ it_should_behave_like 'a successful response'
5
5
 
6
6
  before(:all) do
7
- @response = @remit.get_tokens
7
+ @response = remit.get_tokens
8
8
  end
9
9
 
10
10
  it 'should have a collection of tokens' do
@@ -0,0 +1,8 @@
1
+ ACCESS_KEY = ENV['AWS_ACCESS_KEY'] || ENV['AMAZON_ACCESS_KEY_ID']
2
+ SECRET_KEY = ENV['AWS_SECRET_KEY'] || ENV['AMAZON_SECRET_ACCESS_KEY']
3
+
4
+ unless ACCESS_KEY and SECRET_KEY
5
+ raise RuntimeError, "You must set your AWS_ACCESS_KEY and AWS_SECRET_KEY environment variables to run integration tests"
6
+ end
7
+
8
+ require File.dirname(__FILE__) + '/../spec_helper'
data/spec/spec_helper.rb CHANGED
@@ -1,19 +1,15 @@
1
+ require 'rubygems'
1
2
  require 'spec'
2
3
 
3
4
  require File.dirname(__FILE__) + '/../lib/remit'
4
5
 
5
- fail unless ENV.include?('AWS_ACCESS_KEY') and ENV.include?('AWS_SECRET_KEY')
6
-
7
- ACCESS_KEY = ENV['AWS_ACCESS_KEY'] unless defined?(ACCESS_KEY)
8
- SECRET_KEY = ENV['AWS_SECRET_KEY'] unless defined?(SECRET_KEY)
9
-
10
- describe 'a successful request', :shared => true do
11
- before(:all) do
12
- @remit = Remit::API.new(ACCESS_KEY, SECRET_KEY, true)
13
- end
6
+ def remit
7
+ @remit ||= Remit::API.new(ACCESS_KEY, SECRET_KEY, true)
8
+ end
14
9
 
10
+ describe 'a successful response', :shared => true do
15
11
  it 'should return success' do
16
- @response.status.should eql('Success')
12
+ @response.status.should == 'Success'
17
13
  end
18
14
 
19
15
  it 'should not have any errors' do
@@ -24,3 +20,17 @@ describe 'a successful request', :shared => true do
24
20
  @response.request_id.should_not be_nil
25
21
  end
26
22
  end
23
+
24
+ describe 'a failed response', :shared => true do
25
+ it "is not successful" do
26
+ @response.should_not be_successful
27
+ end
28
+
29
+ it "has a request id" do
30
+ @response.request_id.should_not be_empty
31
+ end
32
+
33
+ it "has errors" do
34
+ @response.errors.should_not be_empty
35
+ end
36
+ end
@@ -1,17 +1,15 @@
1
- require File.dirname(__FILE__) + '/spec_helper'
1
+ require File.dirname(__FILE__) + '/units_helper'
2
2
 
3
3
  describe 'A pipeline', :shared => true do
4
4
  before do
5
- @remit = Remit::API.new(ACCESS_KEY, SECRET_KEY, true)
6
-
7
5
  @pipeline_options = {
8
- :return_URL => 'http://example.com/'
6
+ :return_url => 'http://example.com/'
9
7
  }
10
8
  end
11
9
 
12
10
  it 'should sign its URL' do
13
11
  uri = URI.parse(@pipeline.url)
14
- pipeline = Remit::SignedQuery.parse(uri, @remit.secret_key, uri.query)
12
+ pipeline = Remit::SignedQuery.parse(uri, remit.secret_key, uri.query)
15
13
  query = Relax::Query.parse(uri)
16
14
 
17
15
  pipeline[:awsSignature].should == query[:awsSignature]
@@ -25,19 +23,74 @@ describe 'A single-use pipeline' do
25
23
  @pipeline_options.merge!({
26
24
  :transaction_amount => 10,
27
25
  :caller_reference => 'N2PCBEIA5864E27EL7C86PJL1FGUGPBL61QTJJM5GQK265SPEN8ZKIJPMQARDVJK',
28
- :recipient_token => 'N5PCME5A5Q6FE2QEB7CD64JLGFTUGXBE61HTCJMGGAK2R5IPEQ8EKIVP3QAVD7JP',
29
- :pipeline_name => Remit::PipelineName::SINGLE_USE
26
+ :recipient_token => 'N5PCME5A5Q6FE2QEB7CD64JLGFTUGXBE61HTCJMGGAK2R5IPEQ8EKIVP3QAVD7JP'
27
+ })
28
+
29
+ @pipeline = remit.get_single_use_pipeline(@pipeline_options)
30
+ end
31
+
32
+ it 'should ignore unused parameters' do
33
+ uri = URI.parse(@pipeline.url)
34
+ query = Relax::Query.parse(uri)
35
+
36
+ query[:paymentReason].should be_nil
37
+ end
38
+
39
+ it 'should have the right name' do
40
+ @pipeline.pipeline_name.should == Remit::PipelineName::SINGLE_USE
41
+ end
42
+ end
43
+
44
+ describe 'A multi-use pipeline' do
45
+ it_should_behave_like 'A pipeline'
46
+
47
+ before do
48
+ @pipeline_options.merge!({
49
+ :transaction_amount => 10,
50
+ :caller_reference => 'N2PCBEIA5864E27EL7C86PJL1FGUGPBL61QTJJM5GQK265SPEN8ZKIJPMQARDVJK',
51
+ :recipient_token_list => 'N5PCME5A5Q6FE2QEB7CD64JLGFTUGXBE61HTCJMGGAK2R5IPEQ8EKIVP3QAVD7JP'
30
52
  })
31
53
 
32
- @pipeline = @remit.get_single_use_pipeline(@pipeline_options)
54
+ @pipeline = remit.get_multi_use_pipeline(@pipeline_options)
33
55
  end
34
56
 
35
57
  it 'should ignore unused parameters' do
36
58
  uri = URI.parse(@pipeline.url)
37
59
  query = Relax::Query.parse(uri)
38
-
60
+
39
61
  query[:paymentReason].should be_nil
40
62
  end
63
+
64
+ it 'should have the right name' do
65
+ @pipeline.pipeline_name.should == Remit::PipelineName::MULTI_USE
66
+ end
67
+ end
68
+
69
+ describe 'A recipient pipeline' do
70
+ it_should_behave_like 'A pipeline'
71
+
72
+ before do
73
+ @validity_start = Time.now + (3600 * 24) # 1 day from now
74
+ @validity_expiry = Time.now + (2600 * 24 * 180) # ~6 months from now
75
+
76
+ @pipeline_options.merge!({
77
+ :validity_start => @validity_start,
78
+ :validity_expiry => @validity_expiry,
79
+ :caller_reference => 'N2PCBEIA5864E27EL7C86PJL1FGUGPBL61QTJJM5GQK265SPEN8ZKIJPMQARDVJK',
80
+ :max_variable_fee => '0.25',
81
+ :recipient_pays_fee => true
82
+ })
83
+
84
+ @pipeline = remit.get_recipient_pipeline(@pipeline_options)
85
+ end
86
+
87
+ it 'should have the recipient pay marketplace fees' do
88
+ @pipeline.url.should match(/recipientPaysFee=true/)
89
+ end
90
+
91
+ it 'should have the right name' do
92
+ @pipeline.pipeline_name.should == Remit::PipelineName::RECIPIENT
93
+ end
41
94
  end
42
95
 
43
96
  describe 'A recurring-use pipeline' do
@@ -47,9 +100,8 @@ describe 'A recurring-use pipeline' do
47
100
  @validity_start = Time.now + (3600 * 24) # 1 day from now
48
101
  @validity_expiry = Time.now + (3600 * 24 * 180) # ~6 months from now
49
102
  @recurring_period = '1 Month'
50
-
103
+
51
104
  @pipeline_options.merge!({
52
- :pipeline_name => Remit::PipelineName::RECURRING,
53
105
  :validity_start => @validity_start,
54
106
  :validity_expiry => @validity_expiry,
55
107
  :recurring_period => @recurring_period,
@@ -57,50 +109,57 @@ describe 'A recurring-use pipeline' do
57
109
  :caller_reference => 'N2PCBEIA5864E27EL7C86PJL1FGUGPBL61QTJJM5GQK265SPEN8ZKIJPMQARDVJK',
58
110
  :recipient_token => 'N5PCME5A5Q6FE2QEB7CD64JLGFTUGXBE61HTCJMGGAK2R5IPEQ8EKIVP3QAVD7JP'
59
111
  })
60
-
61
- @pipeline = @remit.get_recurring_use_pipeline(@pipeline_options)
112
+
113
+ @pipeline = remit.get_recurring_use_pipeline(@pipeline_options)
62
114
  end
63
115
 
64
116
  it 'should convert times to seconds from epoch' do
65
117
  uri = URI.parse(@pipeline.url)
66
118
  query = Relax::Query.parse(uri)
67
-
119
+
68
120
  @validity_start.to_i.to_s.should == query[:validityStart]
69
121
  @validity_expiry.to_i.to_s.should == query[:validityExpiry]
70
122
  end
71
-
123
+
72
124
  it 'should allow time in seconds' do
73
125
  options = @pipeline_options.merge({
74
126
  :validity_start => @validity_start.to_i,
75
127
  :validity_expiry => @validity_expiry.to_i
76
128
  })
77
- @pipeline = @remit.get_recurring_use_pipeline(options)
78
-
129
+ @pipeline = remit.get_recurring_use_pipeline(options)
130
+
79
131
  uri = URI.parse(@pipeline.url)
80
132
  query = Relax::Query.parse(uri)
81
-
133
+
82
134
  @validity_start.to_i.to_s.should == query[:validityStart]
83
135
  @validity_expiry.to_i.to_s.should == query[:validityExpiry]
84
136
  end
137
+
138
+ it 'should have the right name' do
139
+ @pipeline.pipeline_name.should == Remit::PipelineName::RECURRING
140
+ end
85
141
  end
86
142
 
87
143
  describe 'A postpaid pipeline' do
88
144
  it_should_behave_like 'A pipeline'
89
-
145
+
90
146
  before do
91
147
  @credit_limit = 100
92
148
  @global_amount_limit = 100
93
-
149
+
94
150
  @pipeline_options.merge!({
95
- :pipeline_name => Remit::PipelineName::SETUP_POSTPAID,
96
151
  :credit_limit => @credit_limit,
97
152
  :global_amount_limit => @global_amount_limit
98
153
  })
99
-
100
- @pipeline = @remit.get_postpaid_pipeline(@pipeline_options)
154
+
155
+ @pipeline = remit.get_postpaid_pipeline(@pipeline_options)
101
156
  end
102
-
157
+
103
158
  it 'should create a PostpaidPipeline' do
104
159
  @pipeline.class.should == Remit::GetPipeline::PostpaidPipeline
105
160
  end
161
+
162
+ it 'should have the right name' do
163
+ @pipeline.pipeline_name.should == Remit::PipelineName::SETUP_POSTPAID
164
+ end
106
165
  end
@@ -0,0 +1,49 @@
1
+ require File.dirname(__FILE__) + '/units_helper'
2
+
3
+ describe "the GetResults API" do
4
+ describe "a successful response" do
5
+ it_should_behave_like 'a successful response'
6
+
7
+ before do
8
+ doc = <<-XML
9
+ <?xml version=\"1.0\"?>
10
+ <ns3:GetResultsResponse xmlns:ns3=\"http://fps.amazonaws.com/doc/2007-01-08/\">
11
+ <TransactionResults>
12
+ <TransactionId>abc123</TransactionId>
13
+ <Operation>Pay</Operation>
14
+ <CallerReference>1827</CallerReference>
15
+ <Status>Success</Status>
16
+ </TransactionResults>
17
+ <NumberPending>1</NumberPending>
18
+ <Status>Success</Status>
19
+ <RequestId>f89727ba-9ff6-4ca8-87a3-0fd6c9de6b95:0</RequestId>
20
+ </ns3:GetResultsResponse>
21
+ XML
22
+
23
+ @response = Remit::GetResults::Response.new(doc)
24
+ end
25
+
26
+ it "has one result" do
27
+ @response.number_pending.should == 1
28
+ @response.transaction_results.size == "1"
29
+ end
30
+
31
+ describe "the result" do
32
+ before do
33
+ @result = @response.transaction_results.first
34
+ end
35
+
36
+ it "references a previous transaction" do
37
+ @result.transaction_id.should == "abc123"
38
+ end
39
+
40
+ it "references a pay transaction" do
41
+ @result.operation_type.should == 'Pay'
42
+ end
43
+
44
+ it "reports the transaction's new status" do
45
+ @result.transaction_status.should == 'Success'
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,32 @@
1
+ require File.dirname(__FILE__) + '/units_helper'
2
+
3
+ describe 'an IPN request' do
4
+ before(:each) do
5
+ @request_params = {
6
+ "action" => "notice",
7
+ "buyerName" => "Fps Buyer",
8
+ "callerReference" => "4-8-1-3.5",
9
+ "controller" => "amazon_fps/ipn",
10
+ "operation" => "PAY",
11
+ "paymentMethod" => "CC",
12
+ "recipientEmail" => "recipient@email.url",
13
+ "recipientName" => "Fps Business",
14
+ "signature" => "DA7ZbuQaBDt2/+Mty9XweJyqI1E=",
15
+ "status" => "SUCCESS",
16
+ "transactionAmount" => "USD 3.50",
17
+ "transactionDate" => "1224687134",
18
+ "transactionId" => "13KIGL9RC25853BGPPOS2VSKBKF2JERR3HO"
19
+ }
20
+ @request = Remit::IpnRequest.new(@request_params, 'THISISMYTESTKEY')
21
+ end
22
+
23
+ it 'should be a valid request' do
24
+ @request.should be_valid
25
+ end
26
+
27
+ it 'should pass through access to given parameters' do
28
+ @request.status.should == 'SUCCESS'
29
+ @request.operation.should == 'PAY'
30
+ @request.transactionId.should == '13KIGL9RC25853BGPPOS2VSKBKF2JERR3HO'
31
+ end
32
+ end
@@ -0,0 +1,70 @@
1
+ require File.dirname(__FILE__) + '/units_helper'
2
+
3
+ describe "the Pay API" do
4
+ describe "a successful response" do
5
+ it_should_behave_like 'a successful response'
6
+
7
+ before do
8
+ doc = <<-XML
9
+ <ns3:PayResponse xmlns:ns3="http://fps.amazonaws.com/doc/2007-01-08/">
10
+ <ns3:TransactionResponse>
11
+ <TransactionId>abc123</TransactionId>
12
+ <Status>Initiated</Status>
13
+ </ns3:TransactionResponse>
14
+ <Status>Success</Status>
15
+ <RequestId>foo</RequestId>
16
+ </ns3:PayResponse>
17
+ XML
18
+
19
+ @response = Remit::Pay::Response.new(doc)
20
+ end
21
+
22
+ it "has a transaction response" do
23
+ @response.transaction_response.should_not be_nil
24
+ end
25
+
26
+ it "has a transaction id" do
27
+ @response.transaction_response.transaction_id.should == 'abc123'
28
+ end
29
+
30
+ it "has a transaction status" do
31
+ @response.transaction_response.status.should == 'Initiated'
32
+ end
33
+
34
+ it "has status shortcuts" do
35
+ @response.transaction_response.should be_initiated
36
+ end
37
+ end
38
+
39
+ describe "for a failed request" do
40
+ it_should_behave_like 'a failed response'
41
+
42
+ before do
43
+ doc = <<-XML
44
+ <ns3:PayResponse xmlns:ns3=\"http://fps.amazonaws.com/doc/2007-01-08/\">
45
+ <Status>Failure</Status>
46
+ <Errors>
47
+ <Errors>
48
+ <ErrorType>Business</ErrorType>
49
+ <IsRetriable>false</IsRetriable>
50
+ <ErrorCode>InvalidParams</ErrorCode>
51
+ <ReasonText>callerTokenId can not be empty</ReasonText>
52
+ </Errors>
53
+ </Errors>
54
+ <RequestId>7966a2d9-5ce9-4902-aefc-b01d254c931a:0</RequestId>
55
+ </ns3:PayResponse>
56
+ XML
57
+
58
+ @response = Remit::Pay::Response.new(doc)
59
+ end
60
+
61
+ it "has error details" do
62
+ error = @response.errors.first
63
+ error.should be_kind_of(Remit::ServiceError)
64
+ error.error_type.should == 'Business'
65
+ error.is_retriable.should == 'false'
66
+ error.error_code.should == 'InvalidParams'
67
+ error.reason_text.should == 'callerTokenId can not be empty'
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,4 @@
1
+ ACCESS_KEY = 'foo'
2
+ SECRET_KEY = 'bar'
3
+
4
+ require File.dirname(__FILE__) + '/../spec_helper'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: remit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tyler Hunt
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-01-28 00:00:00 -05:00
12
+ date: 2009-02-03 00:00:00 -05:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -29,7 +29,7 @@ executables: []
29
29
  extensions: []
30
30
 
31
31
  extra_rdoc_files:
32
- - README
32
+ - README.markdown
33
33
  - LICENSE
34
34
  files:
35
35
  - lib/remit
@@ -37,6 +37,7 @@ files:
37
37
  - lib/remit/common.rb
38
38
  - lib/remit/data_types.rb
39
39
  - lib/remit/discard_results.rb
40
+ - lib/remit/error_codes.rb
40
41
  - lib/remit/fund_prepaid.rb
41
42
  - lib/remit/get_account_activity.rb
42
43
  - lib/remit/get_account_balance.rb
@@ -54,7 +55,9 @@ files:
54
55
  - lib/remit/get_total_prepaid_liability.rb
55
56
  - lib/remit/get_transaction.rb
56
57
  - lib/remit/install_payment_instruction.rb
58
+ - lib/remit/ipn_request.rb
57
59
  - lib/remit/pay.rb
60
+ - lib/remit/pipeline_response.rb
58
61
  - lib/remit/refund.rb
59
62
  - lib/remit/reserve.rb
60
63
  - lib/remit/retry_transaction.rb
@@ -64,7 +67,7 @@ files:
64
67
  - lib/remit/unsubscribe_for_caller_notification.rb
65
68
  - lib/remit/write_off_debt.rb
66
69
  - lib/remit.rb
67
- - README
70
+ - README.markdown
68
71
  - LICENSE
69
72
  has_rdoc: true
70
73
  homepage: http://tylerhunt.com/
@@ -93,7 +96,12 @@ signing_key:
93
96
  specification_version: 2
94
97
  summary: An API for using the Amazon Flexible Payment Service (FPS).
95
98
  test_files:
96
- - spec/get_account_activity_spec.rb
97
- - spec/get_pipeline_spec.rb
98
- - spec/get_tokens_spec.rb
99
+ - spec/integrations/get_account_activity_spec.rb
100
+ - spec/integrations/get_tokens_spec.rb
101
+ - spec/units/get_pipeline_spec.rb
102
+ - spec/units/get_results_spec.rb
103
+ - spec/units/ipn_request_spec.rb
104
+ - spec/units/pay_spec.rb
105
+ - spec/integrations/integrations_helper.rb
99
106
  - spec/spec_helper.rb
107
+ - spec/units/units_helper.rb
data/README DELETED
@@ -1,47 +0,0 @@
1
- Remit
2
- =====
3
-
4
- This API provides access to the Amazon Flexible Payment Service (FPS). After
5
- trying to get the SOAP version of the API written, I began working on this REST
6
- version to provide a cohesive means of access to all of the functionality of
7
- the FPS without having to get dirty dealing with SOAP requests.
8
-
9
- I hope you enjoy using it as much as I've enjoyed writing it. I'm interested to
10
- hear what sort of uses you find for it. If you find any bugs, let me know (or
11
- better yet, submit a patch).
12
-
13
-
14
- Users
15
- -----
16
-
17
- The following sites are using Remit:
18
-
19
- * http://www.storenvy.com/
20
- * http://www.obsidianportal.com/
21
-
22
-
23
- Sandbox
24
- -------
25
- Amazon provides a testing environment for the FPS called a sandbox. You may
26
- (and should) use the sandbox while testing your application. It can be enabled
27
- by passing a value of true to the last argument of the API constructor.
28
-
29
-
30
- Example
31
- -------
32
- The following example shows how to load up the API, initialize the service, and
33
- make a simple call to get the tokens stored on the account:
34
-
35
- require 'remit'
36
-
37
- ACCESS_KEY = '<your AWS access key>'
38
- SECRET_KEY = '<your AWS secret key>'
39
-
40
- # connect using the API's sandbox mode
41
- remit = Remit::API.new(ACCESS_KEY, SECRET_KEY, true)
42
-
43
- response = remit.get_tokens
44
- puts response.tokens.first.token_id
45
-
46
-
47
- Copyright (c) 2007-2008 Tyler Hunt, released under the MIT license