pay_with_amazon 1.0.0 → 1.1.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 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: