pay_with_amazon 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1352bad3d051550f618f54fb20deb352c04bcb1b
4
- data.tar.gz: 078b3a6429e4add06edfd3efeb2e5d3bde52cf16
3
+ metadata.gz: 540fd0fdf13854727e8cf3ee539ea88010295308
4
+ data.tar.gz: 46c573769d9f978f9c597af943d60d96cccb39c8
5
5
  SHA512:
6
- metadata.gz: bc9551b058cd013c64647824499ba465dfc8d3af3a53a1953403fa1a641e71920e40d49a4eb2d6283e034323e11add5370897718c0b0c0da14262a06a6bba2e5
7
- data.tar.gz: 828e07bb06698e7291098a2998eaa20782628c38d4309cd0a8455134f0b10278628467885fa21df99650d4cdedbdb32870267ef0a08b26b76be5071169265dcb
6
+ metadata.gz: cc1788370ce2e5a3c9733ce0dec4cdead496af0617cb9fc1766f5944a49522743ae6d04236ce13dae47ffc0cccbc997ab1aa697b7af4104f3017b2bba6ebec4b
7
+ data.tar.gz: 4ca87d9bc5a23f623d349203c9dcbb01e1e9b2c45f6ef36172401a14176d4fa60656175fa451d6f4bac5da891416f12275c04921fc877c6b7cc293530329a074
data/README.md CHANGED
@@ -12,7 +12,7 @@ or add the following in your Gemfile:
12
12
  gem 'pay_with_amazon'
13
13
  ```
14
14
  ```
15
- Bundle Install
15
+ bundle install
16
16
  ```
17
17
 
18
18
  ## Requirements
@@ -149,7 +149,7 @@ response.success
149
149
  ### Instant Payment Notification Verification and Parsing
150
150
 
151
151
  ```ruby
152
- # This can be placed in your controller for a method
152
+ # This can be placed in your rails controller for a method
153
153
  # that is configured to receive a "POST" IPN from Amazon.
154
154
  headers = request.headers
155
155
  body = request.body.read
@@ -179,3 +179,246 @@ ipn.notification_data
179
179
  ipn.message_timestamp
180
180
 
181
181
  ```
182
+
183
+ ### One Time Transaction API Flow
184
+
185
+ ```ruby
186
+ require 'pay_with_amazon'
187
+
188
+ # Your Login and Pay with Amazon keys are
189
+ # available in your Seller Central account
190
+ merchant_id = 'YOUR_MERCHANT_ID'
191
+ access_key = 'YOUR_ACCESS_KEY'
192
+ secret_key = 'YOUR_SECRET_KEY'
193
+
194
+ client = PayWithAmazon::Client.new(
195
+ merchant_id,
196
+ access_key,
197
+ secret_key,
198
+ sandbox: true
199
+ )
200
+
201
+ # These values are grabbed from the Login and Pay
202
+ # with Amazon Address and Wallet widgets
203
+ amazon_order_reference_id = 'AMAZON_ORDER_REFERENCE_ID'
204
+ address_consent_token = 'ADDRESS_CONSENT_TOKEN'
205
+
206
+ # To get the buyers full address if shipping/tax
207
+ # calculations are needed you can use the following
208
+ # API call to obtain the order reference details.
209
+ client.get_order_reference_details(
210
+ amazon_order_reference_id,
211
+ address_consent_token: address_consent_token
212
+ )
213
+
214
+ # Set the amount for the transaction.
215
+ amount = '10.00'
216
+
217
+ # Make the SetOrderReferenceDetails API call to
218
+ # configure the Amazon Order Reference Id.
219
+ # There are additional optional parameters that
220
+ # are not used below.
221
+ client.set_order_reference_details(
222
+ amazon_order_reference_id,
223
+ amount,
224
+ currency_code: 'USD', # Default: USD
225
+ seller_note: 'Your Seller Note',
226
+ seller_order_id: 'Your Seller Order Id',
227
+ store_name: 'Your Store Name'
228
+ )
229
+
230
+ # Make the ConfirmOrderReference API call to
231
+ # confirm the details set in the API call
232
+ # above.
233
+ client.confirm_order_reference(amazon_order_reference_id)
234
+
235
+ # Set a unique id for your current authorization
236
+ # of this payment.
237
+ authorization_reference_id = 'Your Unique Id'
238
+
239
+ # Make the Authorize API call to authorize the
240
+ # transaction. You can also capture the amount
241
+ # in this API call or make the Capture API call
242
+ # separately. There are additional optional
243
+ # parameters not used below.
244
+ response = client.authorize(
245
+ amazon_order_reference_id,
246
+ authorization_reference_id,
247
+ amount,
248
+ currency_code: 'USD', # Default: USD
249
+ seller_authorization_note: 'Your Authorization Note',
250
+ transaction_timeout: 0, # Set to 0 for synchronous mode
251
+ capture_now: true # Set this to true if you want to capture the amount in the same API call
252
+ )
253
+
254
+ # You will need the Amazon Authorization Id from the
255
+ # Authorize API response if you decide to make the
256
+ # Capture API call separately.
257
+ amazon_authorization_id = response.get_element('AuthorizeResponse/AuthorizeResult/AuthorizationDetails','AmazonAuthorizationId')
258
+
259
+ # Set a unique id for your current capture of
260
+ # this payment.
261
+ capture_reference_id = 'Your Unique Id'
262
+
263
+ # Make the Capture API call if you did not set the
264
+ # 'capture_now' parameter to 'true'. There are
265
+ # additional optional parameters that are not used
266
+ # below.
267
+ client.capture(
268
+ amazon_authorization_id,
269
+ capture_reference_id,
270
+ amount,
271
+ currency_code: 'USD', # Default: USD
272
+ seller_capture_note: 'Your Capture Note'
273
+ )
274
+
275
+ # Close the order reference once your one time
276
+ # transaction is complete.
277
+ client.close_order_reference(amazon_order_reference_id)
278
+
279
+ ```
280
+
281
+ ### Subscriptions API Flow
282
+
283
+ ```ruby
284
+ require 'pay_with_amazon'
285
+
286
+ # Your Login and Pay with Amazon keys are
287
+ # available in your Seller Central account
288
+ merchant_id = 'YOUR_MERCHANT_ID'
289
+ access_key = 'YOUR_ACCESS_KEY'
290
+ secret_key = 'YOUR_SECRET_KEY'
291
+
292
+ client = PayWithAmazon::Client.new(
293
+ merchant_id,
294
+ access_key,
295
+ secret_key,
296
+ sandbox: true
297
+ )
298
+
299
+ # These values are grabbed from the Login and Pay
300
+ # with Amazon Address and Wallet widgets
301
+ amazon_billing_agreement_id = 'AMAZON_BILLING_AGREEMENT_ID'
302
+ address_consent_token = 'ADDRESS_CONSENT_TOKEN'
303
+
304
+ # To get the buyers full address, if shipping/tax
305
+ # calculations are needed, you can use the following
306
+ # API call to obtain the billing agreement details.
307
+ client.get_billing_agreement_details(
308
+ amazon_billing_agreement_id,
309
+ address_consent_token: address_consent_token
310
+ )
311
+
312
+ # Next you will need to set the various details
313
+ # for this subscription with the following API call.
314
+ # There are additional optional parameters that
315
+ # are not used below.
316
+ client.set_billing_agreement_details(
317
+ amazon_billing_agreement_id,
318
+ seller_note: 'Your Seller Note',
319
+ seller_billing_agreement_id: 'Your Transaction Id',
320
+ store_name: 'Your Store Name',
321
+ custom_information: 'Additional Information'
322
+ )
323
+
324
+ # Make the ConfirmBillingAgreement API call to confirm
325
+ # the Amazon Billing Agreement Id with the details set above.
326
+ # Be sure that everything is set correctly above before
327
+ # confirming.
328
+ client.confirm_billing_agreement(amazon_billing_agreement_id)
329
+
330
+ # The following API call is not needed at this point, but
331
+ # can be used in the future when you need to validate that
332
+ # the payment method is still valid with the associated billing
333
+ # agreement id.
334
+ client.validate_billing_agreement(amazon_billing_agreement_id)
335
+
336
+ # Set the amount for your first authorization.
337
+ amount = '10.00'
338
+
339
+ # Set a unique authorization reference id for your
340
+ # first transaction on the billing agreement.
341
+ authorization_reference_id = 'Your Unique Id'
342
+
343
+ # Now you can authorize your first transaction on the
344
+ # billing agreement id. Every month you can make the
345
+ # same API call to continue charging your buyer
346
+ # with the 'capture_now' parameter set to true. You can
347
+ # also make the Capture API call separately. There are
348
+ # additional optional parameters that are not used
349
+ # below.
350
+ response = client.authorize_on_billing_agreement(
351
+ amazon_billing_agreement_id,
352
+ authorization_reference_id,
353
+ amount,
354
+ currency_code: 'USD', # Default: USD
355
+ seller_authorization_note: 'Your Authorization Note',
356
+ transaction_timeout: 0, # Set to 0 for synchronous mode
357
+ capture_now: true, # Set this to true if you want to capture the amount in the same API call
358
+ seller_note: 'Your Seller Note',
359
+ seller_order_id: 'Your Order Id',
360
+ store_name: 'Your Store Name',
361
+ custom_information: 'Additional Information'
362
+ )
363
+
364
+ # You will need the Amazon Authorization Id from the
365
+ # AuthorizeOnBillingAgreement API response if you decide
366
+ # to make the Capture API call separately.
367
+ amazon_authorization_id = response.get_element('AuthorizeOnBillingAgreementResponse/AuthorizeOnBillingAgreementResult/AuthorizationDetails','AmazonAuthorizationId')
368
+
369
+ # Set a unique id for your current capture of
370
+ # this transaction.
371
+ capture_reference_id = 'Your Unique Id'
372
+
373
+ # Make the Capture API call if you did not set the
374
+ # 'capture_now' parameter to 'true'. There are
375
+ # additional optional parameters that are not used
376
+ # below.
377
+ client.capture(
378
+ amazon_authorization_id,
379
+ capture_reference_id,
380
+ amount,
381
+ currency_code: 'USD', # Default: USD
382
+ seller_capture_note: 'Your Capture Note'
383
+ )
384
+
385
+ # The following API call should not be made until you
386
+ # are ready to terminate the billing agreement.
387
+ client.close_billing_agreement(
388
+ amazon_billing_agreement_id,
389
+ closure_reason: 'Reason For Closing'
390
+ )
391
+
392
+ ```
393
+
394
+ ### Get Login Profile API
395
+
396
+ This API call allows you to obtain user profile information
397
+ once a user has logged into your application using
398
+ their Amazon credentials.
399
+
400
+ ```ruby
401
+ require 'pay_with_amazon'
402
+
403
+ # Your client id is located in your Seller
404
+ # Central account.
405
+ client_id = 'Your Client Id'
406
+
407
+ login = PayWithAmazon::Login.new(
408
+ client_id,
409
+ region: :na, # Default: :na
410
+ sandbox: true # Default: false
411
+ )
412
+
413
+ # The access token is available in the return URL
414
+ # parameters after a user has logged in.
415
+ access_token = 'User Access Token'
416
+
417
+ # Make the 'get_login_profile' api call.
418
+ profile = login.get_login_profile(access_token)
419
+
420
+ name = profile['name']
421
+ email = profile['email']
422
+ user_id = profile['user_id']
423
+
424
+ ```
@@ -1,4 +1,7 @@
1
1
  require 'pay_with_amazon/client'
2
+ require 'pay_with_amazon/client_helper'
2
3
  require 'pay_with_amazon/ipn_handler'
4
+ require 'pay_with_amazon/login'
5
+ require 'pay_with_amazon/request'
3
6
  require 'pay_with_amazon/response'
4
7
  require 'pay_with_amazon/version'
@@ -1,8 +1,3 @@
1
- require 'uri'
2
- require 'net/http'
3
- require 'net/https'
4
- require 'base64'
5
- require 'openssl'
6
1
  require 'time'
7
2
 
8
3
  module PayWithAmazon
@@ -14,8 +9,6 @@ module PayWithAmazon
14
9
  # uses the standard Ruby library and is not dependant on Rails.
15
10
  class Client
16
11
 
17
- MAX_RETRIES = 4
18
-
19
12
  attr_reader(
20
13
  :merchant_id,
21
14
  :access_key,
@@ -36,7 +29,7 @@ module PayWithAmazon
36
29
  :proxy_pass)
37
30
 
38
31
  # API keys are located at:
39
- # @see htps://sellercentral.amazon.com
32
+ # @see https://sellercentral.amazon.com
40
33
  # @param merchant_id [String]
41
34
  # @param access_key [String]
42
35
  # @param secret_key [String]
@@ -44,7 +37,7 @@ module PayWithAmazon
44
37
  # @optional currency_code [Symbol] Default: :usd
45
38
  # @optional region [Symbol] Default: :na
46
39
  # @optional platform_id [String] Default: nil
47
- # @optional throttle [Boolean]
40
+ # @optional throttle [Boolean] Default: true
48
41
  # @optional application_name [String]
49
42
  # @optional application_version [String]
50
43
  # @optional proxy_addr [String]
@@ -95,8 +88,7 @@ module PayWithAmazon
95
88
  @default_hash['PlatformId'] = @platform_id if @platform_id
96
89
  end
97
90
 
98
- # Returns the operational status of the Off-Amazon Payments API section
99
- # The GetServiceStatus operation returns the operational status of the Off-Amazon Payments API
91
+ # The GetServiceStatus operation returns the operational status of the Amazon Payments API
100
92
  # section of Amazon Marketplace Web Service (Amazon MWS). Status values are GREEN, GREEN_I, YELLOW, and RED.
101
93
  # @see http://docs.developer.amazonservices.com/en_US/off_amazon_payments/OffAmazonPayments_GetServiceStatus.html
102
94
  def get_service_status
@@ -306,7 +298,7 @@ module PayWithAmazon
306
298
  custom_information: nil,
307
299
  seller_order_id: nil,
308
300
  store_name: nil,
309
- inherit_shipping_adderess: nil,
301
+ inherit_shipping_address: nil,
310
302
  merchant_id: @merchant_id,
311
303
  mws_auth_token: nil)
312
304
 
@@ -329,7 +321,7 @@ module PayWithAmazon
329
321
  'SellerOrderAttributes.CustomInformation' => custom_information,
330
322
  'SellerOrderAttributes.SellerOrderId' => seller_order_id,
331
323
  'SellerOrderAttributes.StoreName' => store_name,
332
- 'InheritShippingAddress' => inherit_shipping_adderess,
324
+ 'InheritShippingAddress' => inherit_shipping_address,
333
325
  'MWSAuthToken' => mws_auth_token
334
326
  }
335
327
 
@@ -496,6 +488,7 @@ module PayWithAmazon
496
488
  # @optional transaction_timeout [Integer]
497
489
  # @optional capture_now [Boolean]
498
490
  # @optional soft_descriptor [String]
491
+ # @optional provider_credit_details [Array of Hash]
499
492
  # @optional merchant_id [String]
500
493
  # @optional mws_auth_token [String]
501
494
  def authorize(
@@ -507,6 +500,7 @@ module PayWithAmazon
507
500
  transaction_timeout: nil,
508
501
  capture_now: nil,
509
502
  soft_descriptor: nil,
503
+ provider_credit_details: nil,
510
504
  merchant_id: @merchant_id,
511
505
  mws_auth_token: nil)
512
506
 
@@ -527,6 +521,8 @@ module PayWithAmazon
527
521
  'MWSAuthToken' => mws_auth_token
528
522
  }
529
523
 
524
+ optional.merge!(set_provider_credit_details(provider_credit_details)) if provider_credit_details
525
+
530
526
  operation(parameters, optional)
531
527
  end
532
528
 
@@ -562,6 +558,7 @@ module PayWithAmazon
562
558
  # @optional currency_code [String]
563
559
  # @optional seller_capture_note [String]
564
560
  # @optional soft_descriptor [String]
561
+ # @optional provider_credit_details [Array of Hash]
565
562
  # @optional merchant_id [String]
566
563
  # @optional mws_auth_token [String]
567
564
  def capture(
@@ -571,6 +568,7 @@ module PayWithAmazon
571
568
  currency_code: @currency_code,
572
569
  seller_capture_note: nil,
573
570
  soft_descriptor: nil,
571
+ provider_credit_details: nil,
574
572
  merchant_id: @merchant_id,
575
573
  mws_auth_token: nil)
576
574
 
@@ -589,6 +587,8 @@ module PayWithAmazon
589
587
  'MWSAuthToken' => mws_auth_token
590
588
  }
591
589
 
590
+ optional.merge!(set_provider_credit_details(provider_credit_details)) if provider_credit_details
591
+
592
592
  operation(parameters, optional)
593
593
  end
594
594
 
@@ -624,6 +624,7 @@ module PayWithAmazon
624
624
  # @optional currency_code [String]
625
625
  # @optional seller_refund_note [String]
626
626
  # @optional soft_descriptor [String]
627
+ # @optional provider_credit_reversal_details [Array of Hash]
627
628
  # @optional merchant_id [String]
628
629
  # @optional mws_auth_token [String]
629
630
  def refund(
@@ -633,6 +634,7 @@ module PayWithAmazon
633
634
  currency_code: @currency_code,
634
635
  seller_refund_note: nil,
635
636
  soft_descriptor: nil,
637
+ provider_credit_reversal_details: nil,
636
638
  merchant_id: @merchant_id,
637
639
  mws_auth_token: nil)
638
640
 
@@ -651,6 +653,8 @@ module PayWithAmazon
651
653
  'MWSAuthToken' => mws_auth_token
652
654
  }
653
655
 
656
+ optional.merge!(set_provider_credit_reversal_details(provider_credit_reversal_details)) if provider_credit_reversal_details
657
+
654
658
  operation(parameters, optional)
655
659
  end
656
660
 
@@ -731,6 +735,81 @@ module PayWithAmazon
731
735
  operation(parameters, optional)
732
736
  end
733
737
 
738
+ # @param amazon_provider_credit_id [String]
739
+ # @optional merchant_id [String]
740
+ # @optional mws_auth_token [String]
741
+ def get_provider_credit_details(
742
+ amazon_provider_credit_id,
743
+ merchant_id: @merchant_id,
744
+ mws_auth_token: nil)
745
+
746
+ parameters = {
747
+ 'Action' => 'GetProviderCreditDetails',
748
+ 'SellerId' => merchant_id,
749
+ 'AmazonProviderCreditId' => amazon_provider_credit_id
750
+ }
751
+
752
+ optional = {
753
+ 'MWSAuthToken' => mws_auth_token
754
+ }
755
+
756
+ operation(parameters, optional)
757
+ end
758
+
759
+ # @param amazon_provider_credit_reversal_id [String]
760
+ # @optional merchant_id [String]
761
+ # @optional mws_auth_token [String]
762
+ def get_provider_credit_reversal_details(
763
+ amazon_provider_credit_reversal_id,
764
+ merchant_id: @merchant_id,
765
+ mws_auth_token: nil)
766
+
767
+ parameters = {
768
+ 'Action' => 'GetProviderCreditReversalDetails',
769
+ 'SellerId' => merchant_id,
770
+ 'AmazonProviderCreditReversalId' => amazon_provider_credit_reversal_id
771
+ }
772
+
773
+ optional = {
774
+ 'MWSAuthToken' => mws_auth_token
775
+ }
776
+
777
+ operation(parameters, optional)
778
+ end
779
+
780
+ # @param amazon_provider_credit_id [String]
781
+ # @param credit_reversal_reference_id [String]
782
+ # @param amount [String]
783
+ # @optional currency_code [String]
784
+ # @optional credit_reversal_note [String]
785
+ # @optional merchant_id [String]
786
+ # @optional mws_auth_token [String]
787
+ def reverse_provider_credit(
788
+ amazon_provider_credit_id,
789
+ credit_reversal_reference_id,
790
+ amount,
791
+ currency_code: @currency_code,
792
+ credit_reversal_note: nil,
793
+ merchant_id: @merchant_id,
794
+ mws_auth_token: nil)
795
+
796
+ parameters = {
797
+ 'Action' => 'ReverseProviderCredit',
798
+ 'SellerId' => merchant_id,
799
+ 'AmazonProviderCreditId' => amazon_provider_credit_id,
800
+ 'CreditReversalReferenceId' => credit_reversal_reference_id,
801
+ 'CreditReversalAmount.Amount' => amount,
802
+ 'CreditReversalAmount.CurrencyCode' => currency_code
803
+ }
804
+
805
+ optional = {
806
+ 'CreditReversalNote' => credit_reversal_note,
807
+ 'MWSAuthToken' => mws_auth_token
808
+ }
809
+
810
+ operation(parameters, optional)
811
+ end
812
+
734
813
  private
735
814
 
736
815
  def region_hash
@@ -744,61 +823,51 @@ module PayWithAmazon
744
823
  }
745
824
  end
746
825
 
747
- # This method combines the required and optional
748
- # parameters to generate the post url, sign the post body,
749
- # and send the post request.
750
- def operation(parameters, optional)
751
- optional.map { |k, v| parameters[k] = v unless v.nil? }
752
- parameters = @default_hash.merge(parameters)
753
- post_url = parameters.sort.map { |k, v| "#{k}=#{ custom_escape(v) }" }.join("&")
754
- post_body = ["POST", "#{@mws_endpoint}", "/#{@sandbox_str}/#{PayWithAmazon::API_VERSION}", post_url].join("\n")
755
- post_url += "&Signature=" + sign(post_body)
756
- send_request(@mws_endpoint, @sandbox_str, post_url)
757
- end
826
+ # This method builds the provider credit details hash
827
+ # that will be combined with either the authorize or capture
828
+ # API call.
829
+ def set_provider_credit_details(provider_credit_details)
830
+ member_details = {}
831
+ provider_credit_details.each_with_index { |val, index|
832
+ member = index + 1
833
+ member_details["ProviderCreditList.member.#{member}.ProviderId"] = val[:provider_id]
834
+ member_details["ProviderCreditList.member.#{member}.CreditAmount.Amount"] = val[:amount]
835
+ member_details["ProviderCreditList.member.#{member}.CreditAmount.CurrencyCode"] = val[:currency_code]
836
+ }
758
837
 
759
- # This method signs the post body request that is being sent
760
- # using the secret key provided.
761
- def sign(post_body)
762
- custom_escape(Base64.strict_encode64(OpenSSL::HMAC.digest(OpenSSL::Digest::SHA256.new, @secret_key, post_body)))
838
+ return member_details
763
839
  end
764
840
 
765
- # This method performs the post to the MWS end point.
766
- # It will retry three times after the initial post if
767
- # the status code comes back as either 500 or 503.
768
- def send_request(mws_endpoint, sandbox_str, post_url)
769
- uri = URI("https://#{mws_endpoint}/#{sandbox_str}/#{PayWithAmazon::API_VERSION}")
770
- https = Net::HTTP.new(uri.host, uri.port, @proxy_addr, @proxy_port, @proxy_user, @proxy_pass)
771
- https.use_ssl = true
772
- https.verify_mode = OpenSSL::SSL::VERIFY_PEER
773
- user_agent = {"User-Agent" => "Language=Ruby; ApplicationLibraryVersion=#{PayWithAmazon::VERSION}; Platform=#{RUBY_PLATFORM}; MWSClientVersion=#{PayWithAmazon::API_VERSION}; ApplicationName=#{@application_name}; ApplicationVersion=#{@application_version}"}
774
- tries = 0
775
- begin
776
- response = https.post(uri.path, post_url, user_agent)
777
- if @throttle.eql?(true)
778
- if response.code.eql?('500')
779
- raise 'InternalServerError'
780
- elsif response.code.eql?('503')
781
- raise 'ServiceUnavailable or RequestThrottled'
782
- end
783
- end
784
- PayWithAmazon::Response.new(response)
785
- rescue => error
786
- tries += 1
787
- sleep(get_seconds_for_try_count(tries))
788
- retry if tries < MAX_RETRIES
789
- raise error.message
790
- end
791
- end
841
+ # This method builds the provider credit reversal
842
+ # details hash that will be combined with the refund
843
+ # API call.
844
+ def set_provider_credit_reversal_details(provider_credit_reversal_details)
845
+ member_details = {}
846
+ provider_credit_reversal_details.each_with_index { |val, index|
847
+ member = index + 1
848
+ member_details["ProviderCreditReversalList.member.#{member}.ProviderId"] = val[:provider_id]
849
+ member_details["ProviderCreditReversalList.member.#{member}.CreditReversalAmount.Amount"] = val[:amount]
850
+ member_details["ProviderCreditReversalList.member.#{member}.CreditReversalAmount.CurrencyCode"] = val[:currency_code]
851
+ }
792
852
 
793
- def get_seconds_for_try_count(try_count)
794
- seconds = { 1=>1, 2=>4, 3=>10, 4=>0 }
795
- seconds[try_count]
853
+ return member_details
796
854
  end
797
855
 
798
- def custom_escape(val)
799
- val.to_s.gsub(/([^\w.~-]+)/) do
800
- "%" + $1.unpack("H2" * $1.bytesize).join("%").upcase
801
- end
856
+ def operation(parameters, optional)
857
+ PayWithAmazon::Request.new(
858
+ parameters,
859
+ optional,
860
+ @default_hash,
861
+ @mws_endpoint,
862
+ @sandbox_str,
863
+ @secret_key,
864
+ @proxy_addr,
865
+ @proxy_port,
866
+ @proxy_user,
867
+ @proxy_pass,
868
+ @throttle,
869
+ @application_name,
870
+ @application_version).send_post
802
871
  end
803
872
 
804
873
  end
@@ -0,0 +1,193 @@
1
+ module PayWithAmazon
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
@@ -31,10 +31,7 @@ module PayWithAmazon
31
31
  attr_reader(:headers, :body)
32
32
  attr_accessor(:proxy_addr, :proxy_port, :proxy_user, :proxy_pass)
33
33
 
34
- # To initialize this class you will be required to
35
- # pass in the request header and body. The body can be
36
- # passed in parsed by json or without.
37
- # @param header [request.headers]
34
+ # @param headers [request.headers]
38
35
  # @param body [request.body.read]
39
36
  # @optional proxy_addr [String]
40
37
  # @optional proxy_port [String]
@@ -58,7 +55,7 @@ module PayWithAmazon
58
55
  end
59
56
 
60
57
  # This method will authenticate the ipn message sent from Amazon.
61
- # It will return true if everything is verified. It will display an
58
+ # It will return true if everything is verified. It will raise an
62
59
  # error message if verification fails.
63
60
  def authentic?
64
61
  begin
@@ -0,0 +1,75 @@
1
+ require 'uri'
2
+ require 'net/http'
3
+ require 'net/https'
4
+ require 'json'
5
+ require 'openssl'
6
+
7
+ module PayWithAmazon
8
+
9
+ # Login with Amazon 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,112 @@
1
+ require 'uri'
2
+ require 'net/http'
3
+ require 'net/https'
4
+ require 'base64'
5
+ require 'openssl'
6
+
7
+ module PayWithAmazon
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}/#{PayWithAmazon::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}/#{PayWithAmazon::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
+ user_agent = {"User-Agent" => "Language=Ruby; ApplicationLibraryVersion=#{PayWithAmazon::VERSION}; Platform=#{RUBY_PLATFORM}; MWSClientVersion=#{PayWithAmazon::API_VERSION}; ApplicationName=#{@application_name}; ApplicationVersion=#{@application_version}"}
80
+ tries = 0
81
+ begin
82
+ response = https.post(uri.path, post_url, user_agent)
83
+ if @throttle.eql?(true)
84
+ if response.code.eql?('500')
85
+ raise 'InternalServerError'
86
+ elsif response.code.eql?('503')
87
+ raise 'ServiceUnavailable or RequestThrottled'
88
+ end
89
+ end
90
+ PayWithAmazon::Response.new(response)
91
+ rescue => error
92
+ tries += 1
93
+ sleep(get_seconds_for_try_count(tries))
94
+ retry if tries <= MAX_RETRIES
95
+ raise error.message
96
+ end
97
+ end
98
+
99
+ def get_seconds_for_try_count(try_count)
100
+ seconds = { 1=>1, 2=>4, 3=>10, 4=>0 }
101
+ seconds[try_count]
102
+ end
103
+
104
+ def custom_escape(val)
105
+ val.to_s.gsub(/([^\w.~-]+)/) do
106
+ "%" + $1.unpack("H2" * $1.bytesize).join("%").upcase
107
+ end
108
+ end
109
+
110
+ end
111
+
112
+ end
@@ -5,7 +5,6 @@ module PayWithAmazon
5
5
  # This class provides helpers to parse the response
6
6
  class Response
7
7
 
8
- # @param response [String]
9
8
  def initialize(response)
10
9
  @response = response
11
10
  end
@@ -1,4 +1,4 @@
1
1
  module PayWithAmazon
2
- VERSION = "1.0.0"
2
+ VERSION = "1.1.0"
3
3
  API_VERSION = "2013-01-01"
4
4
  end
metadata CHANGED
@@ -1,17 +1,17 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pay_with_amazon
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Amazon Payments
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-04-07 00:00:00.000000000 Z
11
+ date: 2015-05-11 00:00:00.000000000 Z
12
12
  dependencies: []
13
- description: Login and Pay with Amazon Ruby SDK
14
- email:
13
+ description: Amazon Payments - Login and Pay with Amazon Ruby SDK
14
+ email: pay-with-amazon-sdk@amazon.com
15
15
  executables: []
16
16
  extensions: []
17
17
  extra_rdoc_files: []
@@ -21,7 +21,10 @@ files:
21
21
  - README.md
22
22
  - lib/pay_with_amazon.rb
23
23
  - lib/pay_with_amazon/client.rb
24
+ - lib/pay_with_amazon/client_helper.rb
24
25
  - lib/pay_with_amazon/ipn_handler.rb
26
+ - lib/pay_with_amazon/login.rb
27
+ - lib/pay_with_amazon/request.rb
25
28
  - lib/pay_with_amazon/response.rb
26
29
  - lib/pay_with_amazon/version.rb
27
30
  homepage: https://github.com/amzn/login-and-pay-with-amazon-sdk-ruby
@@ -47,6 +50,6 @@ rubyforge_project:
47
50
  rubygems_version: 2.4.5
48
51
  signing_key:
49
52
  specification_version: 4
50
- summary: Login and Pay with Amazon Ruby SDK
53
+ summary: Amazon Payments - Login and Pay with Amazon Ruby SDK
51
54
  test_files: []
52
55
  has_rdoc: