diff --git a/README.md b/README.md index 39bc6571f..f1c7129fa 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,27 @@ require 'fulfillment-outbound-api-model' This is a handy way to see all the API model class names and corresponding files you need to require for them, e.g. require 'finances-api-model' to use https://www.rubydoc.info/gems/amz_sp_api/AmzSpApi/FinancesApiModel/DefaultApi +## Feeds + +This gem also offers encrypt/decrypt helper methods for feeds, but actually using that API as per https://github.com/amzn/selling-partner-api-docs/blob/main/guides/en-US/use-case-guides/feeds-api-use-case-guide requires the following calls: + +```ruby +feeds = AmzSpApi::FeedsApiModel::FeedsApi.new(AmzSpApi::SpApiClient.new) +response = feeds.create_feed_document({"contentType" => content_type}) +feed_document_id = response&.payload&.dig(:feedDocumentId) +url = response.payload[:url] +encrypted = AmzSpApi.encrypt_feed(feed_content, response.payload) +# PUT to url with lowercase "content-type" header, it's already pre-signed +response = feeds.create_feed({"feedType" => feed_type, "marketplaceIds" => marketplace_ids, "inputFeedDocumentId" => feed_document_id}) +feed_id = response&.payload&.dig(:feedId) +response = feeds.get_feed(feed_id) +result_feed_document_id = response&.payload&.dig(:resultFeedDocumentId) # present once it is successful +response = feeds.get_feed_document(result_feed_document_id) +url = response&.payload&.dig(:url) +# GET response&.payload&.dig(:url) into ciphertext, again it's pre-signed so no authorization needed +AmzSpApi.decrypt_and_inflate_feed(ciphertext, response.payload) +``` + ## Thanks to https://github.com/patterninc/muffin_man as the basis for [sp_api_client.rb](lib/sp_api_client.rb) diff --git a/lib/amz_sp_api.rb b/lib/amz_sp_api.rb index 78356077f..cba26978e 100644 --- a/lib/amz_sp_api.rb +++ b/lib/amz_sp_api.rb @@ -15,5 +15,38 @@ def configure SpConfiguration.default end end + + def encrypt_feed(feed_content, feed_document_response_payload) + cipher = feed_cipher(feed_document_response_payload, encrypt: true) + cipher.update(feed_content) + cipher.final + end + + def decrypt_and_inflate_feed(ciphertext, feed_document_response_payload) + cipher = feed_cipher(feed_document_response_payload, encrypt: false) + + compression = feed_document_response_payload[:compressionAlgorithm] + raise "unknown compressionAlgorithm #{compression}" if compression && compression != "GZIP" + + result = cipher.update(ciphertext) + cipher.final + result = Zlib::Inflate.inflate(result) if compression + result + end + + # from https://github.com/amzn/selling-partner-api-models/blob/main/clients/sellingpartner-api-documents-helper-java/src/main/java/com/amazon/spapi/documents/impl/AESCryptoStreamFactory.java + def feed_cipher(response, encrypt:) + key = Base64.decode64(response.dig(:encryptionDetails, :key)) + + cipher = case response.dig(:encryptionDetails, :standard) + when "AES" + OpenSSL::Cipher.new("AES-#{key.size * 8}-CBC") + else + raise "unknown encryption standard #{response.inspect}" + end + + encrypt ? cipher.encrypt : cipher.decrypt + cipher.key = key + cipher.iv = Base64.decode64(response.dig(:encryptionDetails, :initializationVector)) + cipher + end end end diff --git a/lib/amz_sp_api_version.rb b/lib/amz_sp_api_version.rb index 4b54a3c0a..46f296eb4 100644 --- a/lib/amz_sp_api_version.rb +++ b/lib/amz_sp_api_version.rb @@ -1,3 +1,3 @@ module AmzSpApi - VERSION = '0.2.0' + VERSION = '0.2.1' end