amazon_pay 2.0.0
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.
- checksums.yaml +7 -0
- data/LICENSE +202 -0
- data/NOTICE +9 -0
- data/README.md +428 -0
- data/lib/amazon_pay/client.rb +875 -0
- data/lib/amazon_pay/client_helper.rb +193 -0
- data/lib/amazon_pay/ipn_handler.rb +222 -0
- data/lib/amazon_pay/login.rb +75 -0
- data/lib/amazon_pay/request.rb +114 -0
- data/lib/amazon_pay/response.rb +42 -0
- data/lib/amazon_pay/version.rb +5 -0
- data/lib/amazon_pay.rb +7 -0
- metadata +54 -0
@@ -0,0 +1,193 @@
|
|
1
|
+
module AmazonPay
|
2
|
+
|
3
|
+
# This will extend the client class to add additional
|
4
|
+
# helper methods that combine core API calls.
|
5
|
+
class Client
|
6
|
+
|
7
|
+
# This method combines multiple API calls to perform
|
8
|
+
# a complete transaction with minimum requirements.
|
9
|
+
# @param amazon_reference_id [String]
|
10
|
+
# @param authorization_reference_id [String]
|
11
|
+
# @param charge_amount [String]
|
12
|
+
# @optional charge_currency_code [String]
|
13
|
+
# @optional charge_note [String]
|
14
|
+
# @optional charge_order [String]
|
15
|
+
# @optional store_name [String]
|
16
|
+
# @optional custom_information [String]
|
17
|
+
# @optional soft_descriptor [String]
|
18
|
+
# @optional platform_id [String]
|
19
|
+
# @optional merchant_id [String]
|
20
|
+
# @optional mws_auth_token [String]
|
21
|
+
def charge(
|
22
|
+
amazon_reference_id,
|
23
|
+
authorization_reference_id,
|
24
|
+
charge_amount,
|
25
|
+
charge_currency_code: @currency_code,
|
26
|
+
charge_note: nil,
|
27
|
+
charge_order_id: nil,
|
28
|
+
store_name: nil,
|
29
|
+
custom_information: nil,
|
30
|
+
soft_descriptor: nil,
|
31
|
+
platform_id: nil,
|
32
|
+
merchant_id: @merchant_id,
|
33
|
+
mws_auth_token: nil)
|
34
|
+
|
35
|
+
if is_order_reference?(amazon_reference_id)
|
36
|
+
response = call_order_reference_api(
|
37
|
+
amazon_reference_id,
|
38
|
+
authorization_reference_id,
|
39
|
+
charge_amount,
|
40
|
+
charge_currency_code,
|
41
|
+
charge_note,
|
42
|
+
charge_order_id,
|
43
|
+
store_name,
|
44
|
+
custom_information,
|
45
|
+
soft_descriptor,
|
46
|
+
platform_id,
|
47
|
+
merchant_id,
|
48
|
+
mws_auth_token)
|
49
|
+
return response
|
50
|
+
end
|
51
|
+
|
52
|
+
if is_billing_agreement?(amazon_reference_id)
|
53
|
+
response = call_billing_agreement_api(
|
54
|
+
amazon_reference_id,
|
55
|
+
authorization_reference_id,
|
56
|
+
charge_amount,
|
57
|
+
charge_currency_code,
|
58
|
+
charge_note,
|
59
|
+
charge_order_id,
|
60
|
+
store_name,
|
61
|
+
custom_information,
|
62
|
+
soft_descriptor,
|
63
|
+
platform_id,
|
64
|
+
merchant_id,
|
65
|
+
mws_auth_token)
|
66
|
+
return response
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def call_order_reference_api(
|
73
|
+
amazon_reference_id,
|
74
|
+
authorization_reference_id,
|
75
|
+
charge_amount,
|
76
|
+
charge_currency_code,
|
77
|
+
charge_note,
|
78
|
+
charge_order_id,
|
79
|
+
store_name,
|
80
|
+
custom_information,
|
81
|
+
soft_descriptor,
|
82
|
+
platform_id,
|
83
|
+
merchant_id,
|
84
|
+
mws_auth_token)
|
85
|
+
|
86
|
+
response = set_order_reference_details(
|
87
|
+
amazon_reference_id,
|
88
|
+
charge_amount,
|
89
|
+
currency_code: charge_currency_code,
|
90
|
+
platform_id: platform_id,
|
91
|
+
seller_note: charge_note,
|
92
|
+
seller_order_id: charge_order_id,
|
93
|
+
store_name: store_name,
|
94
|
+
custom_information: custom_information,
|
95
|
+
merchant_id: merchant_id,
|
96
|
+
mws_auth_token: mws_auth_token)
|
97
|
+
if response.success
|
98
|
+
response = confirm_order_reference(
|
99
|
+
amazon_reference_id,
|
100
|
+
merchant_id: merchant_id,
|
101
|
+
mws_auth_token: mws_auth_token)
|
102
|
+
if response.success
|
103
|
+
response = authorize(
|
104
|
+
amazon_reference_id,
|
105
|
+
authorization_reference_id,
|
106
|
+
charge_amount,
|
107
|
+
currency_code: charge_currency_code,
|
108
|
+
seller_authorization_note: charge_note,
|
109
|
+
transaction_timeout: 0,
|
110
|
+
capture_now: true,
|
111
|
+
soft_descriptor: soft_descriptor,
|
112
|
+
merchant_id: merchant_id,
|
113
|
+
mws_auth_token: mws_auth_token)
|
114
|
+
return response
|
115
|
+
else
|
116
|
+
return response
|
117
|
+
end
|
118
|
+
else
|
119
|
+
return response
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def call_billing_agreement_api(
|
124
|
+
amazon_reference_id,
|
125
|
+
authorization_reference_id,
|
126
|
+
charge_amount,
|
127
|
+
charge_currency_code,
|
128
|
+
charge_note,
|
129
|
+
charge_order_id,
|
130
|
+
store_name,
|
131
|
+
custom_information,
|
132
|
+
soft_descriptor,
|
133
|
+
platform_id,
|
134
|
+
merchant_id,
|
135
|
+
mws_auth_token)
|
136
|
+
|
137
|
+
response = get_billing_agreement_details(
|
138
|
+
amazon_reference_id,
|
139
|
+
merchant_id: merchant_id,
|
140
|
+
mws_auth_token: mws_auth_token)
|
141
|
+
if response.get_element('GetBillingAgreementDetailsResponse/GetBillingAgreementDetailsResult/BillingAgreementDetails/BillingAgreementStatus','State').eql?('Draft')
|
142
|
+
response = set_billing_agreement_details(
|
143
|
+
amazon_reference_id,
|
144
|
+
platform_id: platform_id,
|
145
|
+
seller_note: charge_note,
|
146
|
+
seller_billing_agreement_id: charge_order_id,
|
147
|
+
store_name: store_name,
|
148
|
+
custom_information: custom_information,
|
149
|
+
merchant_id: merchant_id,
|
150
|
+
mws_auth_token: mws_auth_token)
|
151
|
+
if response.success
|
152
|
+
response = confirm_billing_agreement(
|
153
|
+
amazon_reference_id,
|
154
|
+
merchant_id: merchant_id,
|
155
|
+
mws_auth_token: mws_auth_token)
|
156
|
+
if response.success.eql?(false)
|
157
|
+
return response
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
response = authorize_on_billing_agreement(
|
163
|
+
amazon_reference_id,
|
164
|
+
authorization_reference_id,
|
165
|
+
charge_amount,
|
166
|
+
currency_code: charge_currency_code,
|
167
|
+
seller_authorization_note: charge_note,
|
168
|
+
transaction_timeout: 0,
|
169
|
+
capture_now: true,
|
170
|
+
soft_descriptor: soft_descriptor,
|
171
|
+
seller_note: charge_note,
|
172
|
+
platform_id: platform_id,
|
173
|
+
seller_order_id: charge_order_id,
|
174
|
+
store_name: store_name,
|
175
|
+
custom_information: custom_information,
|
176
|
+
inherit_shipping_address: true,
|
177
|
+
merchant_id: merchant_id,
|
178
|
+
mws_auth_token: mws_auth_token)
|
179
|
+
return response
|
180
|
+
end
|
181
|
+
|
182
|
+
|
183
|
+
def is_order_reference?(amazon_reference_id)
|
184
|
+
amazon_reference_id.start_with?('S','P')
|
185
|
+
end
|
186
|
+
|
187
|
+
def is_billing_agreement?(amazon_reference_id)
|
188
|
+
amazon_reference_id.start_with?('C','B')
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
192
|
+
|
193
|
+
end
|
@@ -0,0 +1,222 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'json'
|
3
|
+
require 'net/http'
|
4
|
+
require 'net/https'
|
5
|
+
require 'openssl'
|
6
|
+
require 'uri'
|
7
|
+
|
8
|
+
module AmazonPay
|
9
|
+
|
10
|
+
class IpnWasNotAuthenticError < StandardError
|
11
|
+
end
|
12
|
+
|
13
|
+
# AmazonPay Ipn Handler
|
14
|
+
#
|
15
|
+
# This class authenticates an sns message sent from Amazon. It
|
16
|
+
# will validate the header, subject, and certificate. After validation
|
17
|
+
# there are many helper methods in place to extract information received
|
18
|
+
# from the ipn notification.
|
19
|
+
class IpnHandler
|
20
|
+
|
21
|
+
SIGNABLE_KEYS = [
|
22
|
+
'Message',
|
23
|
+
'MessageId',
|
24
|
+
'Timestamp',
|
25
|
+
'TopicArn',
|
26
|
+
'Type',
|
27
|
+
].freeze
|
28
|
+
|
29
|
+
COMMON_NAME = 'sns.amazonaws.com'
|
30
|
+
|
31
|
+
attr_reader(:headers, :body)
|
32
|
+
attr_accessor(:proxy_addr, :proxy_port, :proxy_user, :proxy_pass)
|
33
|
+
|
34
|
+
# @param headers [request.headers]
|
35
|
+
# @param body [request.body.read]
|
36
|
+
# @optional proxy_addr [String]
|
37
|
+
# @optional proxy_port [String]
|
38
|
+
# @optional proxy_user [String]
|
39
|
+
# @optional proxy_pass [String]
|
40
|
+
def initialize(
|
41
|
+
headers,
|
42
|
+
body,
|
43
|
+
proxy_addr: :ENV,
|
44
|
+
proxy_port: nil,
|
45
|
+
proxy_user: nil,
|
46
|
+
proxy_pass: nil)
|
47
|
+
|
48
|
+
@body = body
|
49
|
+
@raw = parse_from(@body)
|
50
|
+
@headers = headers
|
51
|
+
@proxy_addr = proxy_addr
|
52
|
+
@proxy_port = proxy_port
|
53
|
+
@proxy_user = proxy_user
|
54
|
+
@proxy_pass = proxy_pass
|
55
|
+
end
|
56
|
+
|
57
|
+
# This method will authenticate the ipn message sent from Amazon.
|
58
|
+
# It will return true if everything is verified. It will raise an
|
59
|
+
# error message if verification fails.
|
60
|
+
def authentic?
|
61
|
+
begin
|
62
|
+
decoded_from_base64 = Base64.decode64(signature)
|
63
|
+
validate_header
|
64
|
+
validate_subject(get_certificate.subject)
|
65
|
+
public_key = get_public_key_from(get_certificate)
|
66
|
+
verify_public_key(public_key, decoded_from_base64, canonical_string)
|
67
|
+
|
68
|
+
return true
|
69
|
+
rescue IpnWasNotAuthenticError => e
|
70
|
+
raise e.message
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def type
|
75
|
+
@raw['Type']
|
76
|
+
end
|
77
|
+
|
78
|
+
def message_id
|
79
|
+
@raw['MessageId']
|
80
|
+
end
|
81
|
+
|
82
|
+
def topic_arn
|
83
|
+
@raw['TopicArn']
|
84
|
+
end
|
85
|
+
|
86
|
+
def message
|
87
|
+
@raw['Message']
|
88
|
+
end
|
89
|
+
|
90
|
+
def timestamp
|
91
|
+
@raw['Timestamp']
|
92
|
+
end
|
93
|
+
|
94
|
+
def signature
|
95
|
+
@raw['Signature']
|
96
|
+
end
|
97
|
+
|
98
|
+
def signature_version
|
99
|
+
@raw['SignatureVersion']
|
100
|
+
end
|
101
|
+
|
102
|
+
def signing_cert_url
|
103
|
+
@raw['SigningCertURL']
|
104
|
+
end
|
105
|
+
|
106
|
+
def unsubscribe_url
|
107
|
+
@raw['UnsubscribeURL']
|
108
|
+
end
|
109
|
+
|
110
|
+
def notification_type
|
111
|
+
parse_from(@raw['Message'])["NotificationType"]
|
112
|
+
end
|
113
|
+
|
114
|
+
def seller_id
|
115
|
+
parse_from(@raw['Message'])["SellerId"]
|
116
|
+
end
|
117
|
+
|
118
|
+
def environment
|
119
|
+
parse_from(@raw['Message'])["ReleaseEnvironment"]
|
120
|
+
end
|
121
|
+
|
122
|
+
def version
|
123
|
+
parse_from(@raw['Message'])["Version"]
|
124
|
+
end
|
125
|
+
|
126
|
+
def notification_data
|
127
|
+
parse_from(@raw['Message'])["NotificationData"]
|
128
|
+
end
|
129
|
+
|
130
|
+
def message_timestamp
|
131
|
+
parse_from(@raw['Message'])["Timestamp"]
|
132
|
+
end
|
133
|
+
|
134
|
+
def parse_from(json)
|
135
|
+
JSON.parse(json)
|
136
|
+
end
|
137
|
+
|
138
|
+
protected
|
139
|
+
|
140
|
+
def get_certificate
|
141
|
+
cert_pem = download_cert(signing_cert_url)
|
142
|
+
OpenSSL::X509::Certificate.new(cert_pem)
|
143
|
+
end
|
144
|
+
|
145
|
+
def get_public_key_from(certificate)
|
146
|
+
OpenSSL::PKey::RSA.new(certificate.public_key)
|
147
|
+
end
|
148
|
+
|
149
|
+
def canonical_string
|
150
|
+
text = ''
|
151
|
+
SIGNABLE_KEYS.each do |key|
|
152
|
+
value = @raw[key]
|
153
|
+
next if value.nil? or value.empty?
|
154
|
+
text << key << "\n"
|
155
|
+
text << value << "\n"
|
156
|
+
end
|
157
|
+
text
|
158
|
+
end
|
159
|
+
|
160
|
+
def download_cert(url)
|
161
|
+
uri = URI.parse(url)
|
162
|
+
unless
|
163
|
+
uri.scheme == 'https' &&
|
164
|
+
uri.host.match(/^sns\.[a-zA-Z0-9\-]{3,}\.amazonaws\.com(\.cn)?$/) &&
|
165
|
+
File.extname(uri.path) == '.pem'
|
166
|
+
then
|
167
|
+
msg = "Error - certificate is not hosted at AWS URL (https): #{url}"
|
168
|
+
raise IpnWasNotAuthenticError, msg
|
169
|
+
end
|
170
|
+
tries = 0
|
171
|
+
begin
|
172
|
+
resp = https_get(url)
|
173
|
+
resp.body
|
174
|
+
rescue => error
|
175
|
+
tries += 1
|
176
|
+
retry if tries < 3
|
177
|
+
raise error
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def https_get(url)
|
182
|
+
uri = URI.parse(url)
|
183
|
+
http = Net::HTTP.new(uri.host, uri.port, @proxy_addr, @proxy_port, @proxy_user, @proxy_pass)
|
184
|
+
http.use_ssl = true
|
185
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
186
|
+
http.start
|
187
|
+
resp = http.request(Net::HTTP::Get.new(uri.request_uri))
|
188
|
+
http.finish
|
189
|
+
resp
|
190
|
+
end
|
191
|
+
|
192
|
+
def validate_header
|
193
|
+
unless
|
194
|
+
@headers['x-amz-sns-message-type'] == 'Notification'
|
195
|
+
then
|
196
|
+
msg = "Error - Header does not contain x-amz-sns-message-type header"
|
197
|
+
raise IpnWasNotAuthenticError, msg
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def validate_subject(certificate_subject)
|
202
|
+
subject = certificate_subject.to_a
|
203
|
+
unless
|
204
|
+
subject[4][1] == COMMON_NAME
|
205
|
+
then
|
206
|
+
msg = "Error - Unable to verify certificate subject issued by Amazon"
|
207
|
+
raise IpnWasNotAuthenticError, msg
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
def verify_public_key(public_key, decoded_signature, signed_string)
|
212
|
+
unless
|
213
|
+
public_key.verify(OpenSSL::Digest::SHA1.new, decoded_signature, signed_string)
|
214
|
+
then
|
215
|
+
msg = "Error - Unable to verify public key with signature and signed string"
|
216
|
+
raise IpnWasNotAuthenticError, msg
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
end
|
221
|
+
|
222
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'net/http'
|
3
|
+
require 'net/https'
|
4
|
+
require 'json'
|
5
|
+
require 'openssl'
|
6
|
+
|
7
|
+
module AmazonPay
|
8
|
+
|
9
|
+
# AmazonPay API
|
10
|
+
#
|
11
|
+
# This class allows you to obtain user profile
|
12
|
+
# information once a user has logged into your
|
13
|
+
# application using their Amazon credentials.
|
14
|
+
class Login
|
15
|
+
|
16
|
+
attr_reader(:region)
|
17
|
+
|
18
|
+
attr_accessor(:client_id, :sandbox)
|
19
|
+
|
20
|
+
# @param client_id [String]
|
21
|
+
# @optional region [Symbol] Default: :na
|
22
|
+
# @optional sandbox [Boolean] Default: false
|
23
|
+
def initialize(client_id, region: :na, sandbox: false)
|
24
|
+
@client_id = client_id
|
25
|
+
@region = region
|
26
|
+
@endpoint = region_hash[@region]
|
27
|
+
@sandbox = sandbox
|
28
|
+
@sandbox_str = @sandbox ? "api.sandbox" : "api"
|
29
|
+
end
|
30
|
+
|
31
|
+
# This method will validate the access token and
|
32
|
+
# return the user's profile information.
|
33
|
+
# @param access_token [String]
|
34
|
+
def get_login_profile(access_token)
|
35
|
+
decoded_access_token = URI.decode(access_token)
|
36
|
+
encoded_access_token = URI.encode(decoded_access_token)
|
37
|
+
uri = URI("https://#{@sandbox_str}.#{@endpoint}/auth/o2/tokeninfo?access_token=#{encoded_access_token}")
|
38
|
+
req = Net::HTTP::Get.new(uri.request_uri)
|
39
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
40
|
+
http.use_ssl = true
|
41
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
42
|
+
response = http.request(req)
|
43
|
+
decode = JSON.parse(response.body)
|
44
|
+
|
45
|
+
if decode['aud'] != @client_id
|
46
|
+
raise "Invalid Access Token"
|
47
|
+
end
|
48
|
+
|
49
|
+
uri = URI.parse("https://#{@sandbox_str}.#{@endpoint}/user/profile")
|
50
|
+
req = Net::HTTP::Get.new(uri.request_uri)
|
51
|
+
req['Authorization'] = "bearer " + decoded_access_token
|
52
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
53
|
+
http.use_ssl = true
|
54
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
55
|
+
response = http.request(req)
|
56
|
+
decoded_login_profile = JSON.parse(response.body)
|
57
|
+
return decoded_login_profile
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def region_hash
|
63
|
+
{
|
64
|
+
:jp => 'amazon.co.jp',
|
65
|
+
:uk => 'amazon.co.uk',
|
66
|
+
:de => 'amazon.de',
|
67
|
+
:eu => 'amazon.co.uk',
|
68
|
+
:us => 'amazon.com',
|
69
|
+
:na => 'amazon.com'
|
70
|
+
}
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'net/http'
|
3
|
+
require 'net/https'
|
4
|
+
require 'base64'
|
5
|
+
require 'openssl'
|
6
|
+
|
7
|
+
module AmazonPay
|
8
|
+
|
9
|
+
# This class creates the request to send to the
|
10
|
+
# specified MWS endpoint.
|
11
|
+
class Request
|
12
|
+
|
13
|
+
MAX_RETRIES = 3
|
14
|
+
|
15
|
+
def initialize(
|
16
|
+
parameters,
|
17
|
+
optional,
|
18
|
+
default_hash,
|
19
|
+
mws_endpoint,
|
20
|
+
sandbox_str,
|
21
|
+
secret_key,
|
22
|
+
proxy_addr,
|
23
|
+
proxy_port,
|
24
|
+
proxy_user,
|
25
|
+
proxy_pass,
|
26
|
+
throttle,
|
27
|
+
application_name,
|
28
|
+
application_version)
|
29
|
+
|
30
|
+
@parameters = parameters
|
31
|
+
@optional = optional
|
32
|
+
@default_hash = default_hash
|
33
|
+
@mws_endpoint = mws_endpoint
|
34
|
+
@sandbox_str = sandbox_str
|
35
|
+
@secret_key = secret_key
|
36
|
+
@proxy_addr = proxy_addr
|
37
|
+
@proxy_port = proxy_port
|
38
|
+
@proxy_user = proxy_user
|
39
|
+
@proxy_pass = proxy_pass
|
40
|
+
@throttle = throttle
|
41
|
+
@application_name = application_name
|
42
|
+
@application_version = application_version
|
43
|
+
end
|
44
|
+
|
45
|
+
# This method sends the post request.
|
46
|
+
def send_post
|
47
|
+
post_url = build_post_url
|
48
|
+
post(@mws_endpoint, @sandbox_str, post_url)
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
# This method combines the required and optional
|
54
|
+
# parameters to sign the post body and generate
|
55
|
+
# the post url.
|
56
|
+
def build_post_url
|
57
|
+
@optional.map { |k, v| @parameters[k] = v unless v.nil? }
|
58
|
+
@parameters = @default_hash.merge(@parameters)
|
59
|
+
post_url = @parameters.sort.map { |k, v| "#{k}=#{ custom_escape(v) }" }.join("&")
|
60
|
+
post_body = ["POST", "#{@mws_endpoint}", "/#{@sandbox_str}/#{AmazonPay::API_VERSION}", post_url].join("\n")
|
61
|
+
post_url += "&Signature=" + sign(post_body)
|
62
|
+
return post_url
|
63
|
+
end
|
64
|
+
|
65
|
+
# This method signs the post body that is being sent
|
66
|
+
# using the secret key provided.
|
67
|
+
def sign(post_body)
|
68
|
+
custom_escape(Base64.strict_encode64(OpenSSL::HMAC.digest(OpenSSL::Digest::SHA256.new, @secret_key, post_body)))
|
69
|
+
end
|
70
|
+
|
71
|
+
# This method performs the post to the MWS endpoint.
|
72
|
+
# It will retry three times after the initial post if
|
73
|
+
# the status code comes back as either 500 or 503.
|
74
|
+
def post(mws_endpoint, sandbox_str, post_url)
|
75
|
+
uri = URI("https://#{mws_endpoint}/#{sandbox_str}/#{AmazonPay::API_VERSION}")
|
76
|
+
https = Net::HTTP.new(uri.host, uri.port, @proxy_addr, @proxy_port, @proxy_user, @proxy_pass)
|
77
|
+
https.use_ssl = true
|
78
|
+
https.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
79
|
+
|
80
|
+
user_agent = {"User-Agent" => "#{AmazonPay::SDK_NAME}/#{AmazonPay::VERSION}; (#{@application_name + '/' if @application_name }#{@application_version.to_s + ';' if @application_version} #{RUBY_VERSION}; #{RUBY_PLATFORM})"}
|
81
|
+
|
82
|
+
tries = 0
|
83
|
+
begin
|
84
|
+
response = https.post(uri.path, post_url, user_agent)
|
85
|
+
if @throttle.eql?(true)
|
86
|
+
if response.code.eql?('500')
|
87
|
+
raise 'InternalServerError'
|
88
|
+
elsif response.code.eql?('503')
|
89
|
+
raise 'ServiceUnavailable or RequestThrottled'
|
90
|
+
end
|
91
|
+
end
|
92
|
+
AmazonPay::Response.new(response)
|
93
|
+
rescue => error
|
94
|
+
tries += 1
|
95
|
+
sleep(get_seconds_for_try_count(tries))
|
96
|
+
retry if tries <= MAX_RETRIES
|
97
|
+
raise error.message
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def get_seconds_for_try_count(try_count)
|
102
|
+
seconds = { 1=>1, 2=>4, 3=>10, 4=>0 }
|
103
|
+
seconds[try_count]
|
104
|
+
end
|
105
|
+
|
106
|
+
def custom_escape(val)
|
107
|
+
val.to_s.gsub(/([^\w.~-]+)/) do
|
108
|
+
"%" + $1.unpack("H2" * $1.bytesize).join("%").upcase
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'rexml/document'
|
2
|
+
|
3
|
+
module AmazonPay
|
4
|
+
|
5
|
+
# This class provides helpers to parse the response
|
6
|
+
class Response
|
7
|
+
|
8
|
+
def initialize(response)
|
9
|
+
@response = response
|
10
|
+
end
|
11
|
+
|
12
|
+
def body
|
13
|
+
@response.body
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_xml
|
17
|
+
REXML::Document.new(body)
|
18
|
+
end
|
19
|
+
|
20
|
+
def get_element(xpath, xml_element)
|
21
|
+
xml = self.to_xml
|
22
|
+
xml.elements.each(xpath) do |element|
|
23
|
+
@value = element.elements[xml_element].text
|
24
|
+
end
|
25
|
+
return @value
|
26
|
+
end
|
27
|
+
|
28
|
+
def code
|
29
|
+
@response.code
|
30
|
+
end
|
31
|
+
|
32
|
+
def success
|
33
|
+
if @response.code.eql? '200'
|
34
|
+
return true
|
35
|
+
else
|
36
|
+
return false
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|