diff --git a/README.md b/README.md index 1b30201..fea2563 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,58 @@ Flutter package for Amazon PA-API 5.0. ## Getting Started +As this package is not available via pub you have to add it manually to your `pubspec.yaml`. +There are at least two ways to use it in your project. + +### Option 1 - Add git repo to `pubspec.yaml` +```yaml +flutter_amazon_pa_api: + git: + url: https://github.com/meganii/flutter_amazon_pa_api.git + ref: master +``` + +### Option 2 - Add local copy to `pubspec.yaml` +```yaml +flutter_amazon_pa_api: + path: ../flutter_amazon_pa_api +``` + +## Usage in your project +After adding the package to your `pubspec.yaml` you have to add the package to your code. +```dart +import 'package:flutter_amazon_pa_api/flutter_amazon_pa_api.dart'; +import 'package:flutter_amazon_pa_api/search_items_response.dart'; +import 'package:flutter_amazon_pa_api/paapi_resource.dart'; + +// list all resources you want to fetch +var resources = [ + PaAPIResource.BrowseNodeInfo_BrowseNodes.name, + PaAPIResource.Images_Primary_Small.name, + PaAPIResource.Images_Primary_Medium.name, + PaAPIResource.Images_Primary_Large.name, + PaAPIResource.ItemInfo_ByLineInfo.name, + PaAPIResource.ItemInfo_ContentInfo.name, + PaAPIResource.ItemInfo_Title.name, + PaAPIResource.Offers_Listings_Price.name, + PaAPIResource.Offers_Listings_SavingBasis.name, + PaAPIResource.Offers_Listings_Availability_MaxOrderQuantity.name, + PaAPIResource.Offers_Listings_DeliveryInfo_IsAmazonFulfilled.name, + ]; + +PaAPI paAPI = PaAPI( + accessKey: "", + secretKey: "", + partnerTag: "", + // marketplace is defaulting to US. Use the PaAPIMarketplace enum to select your marketplace + marketplace: PaAPIMarketplace.de); + +// this will get all Items for this ASIN +await paAPI.getItems(["B00S7N8G2Q"], resources); +// this will get all Items for these keywords +await paAPI.searchItems("urban classics damen ladies laces dress kleid", resources) +``` + This project is a starting point for a Dart [package](https://flutter.dev/developing-packages/), a library module containing code that can be shared easily across diff --git a/lib/flutter_amazon_pa_api.dart b/lib/flutter_amazon_pa_api.dart index 1f7cd2f..915853d 100644 --- a/lib/flutter_amazon_pa_api.dart +++ b/lib/flutter_amazon_pa_api.dart @@ -3,6 +3,9 @@ library flutter_amazon_pa_api; import 'dart:convert'; import 'package:flutter_amazon_pa_api/get_items_response.dart'; +import 'package:flutter_amazon_pa_api/search_items_response.dart'; +import 'package:flutter_amazon_pa_api/paapi_operation.dart'; +import 'package:flutter_amazon_pa_api/paapi_marketplace.dart'; import 'package:http/http.dart' as http; import 'package:crypto/crypto.dart'; import 'package:intl/intl.dart'; @@ -20,13 +23,14 @@ class PaAPI { /// Service name String service = 'ProductAdvertisingAPI'; - /// Host default=JP - String host = 'webservices.amazon.co.jp'; + /// Host + late String host; - /// Region default=JP - String region = 'us-west-2'; + /// Region + late String region; - String marketplace = 'www.amazon.co.jp'; + /// Markeptlace default=us + PaAPIMarketplace marketplace; /// API Request PATH late String path; @@ -34,31 +38,48 @@ class PaAPI { /// Amazon associate tag late String partnerTag; - PaAPI({required this.accessKey, required this.secretKey}); + PaAPI( + {required this.accessKey, + required this.secretKey, + required this.partnerTag, + this.marketplace = PaAPIMarketplace.us}) { + this.host = this.marketplace.host; + this.region = this.marketplace.region; + } - Future getItems(List items) async { + Future getItems( + List items, List resources) async { final body = { "ItemIds": items, - "Resources": [ - "BrowseNodeInfo.BrowseNodes", - "Images.Primary.Small", - "Images.Primary.Medium", - "Images.Primary.Large", - "ItemInfo.ByLineInfo", - "ItemInfo.ContentInfo", - "ItemInfo.Title" - ], + "Resources": resources, "PartnerTag": this.partnerTag, "PartnerType": "Associates", - "Marketplace": this.marketplace, - "Operation": "GetItems" + "Marketplace": this.marketplace.name, + "Operation": PaAPIOperation.GetItems.name }; - final response = await _post('/paapi5/getitems', body); + final response = + await _post('/paapi5/getitems', body, PaAPIOperation.GetItems); return GetItemsResponse.fromJson(response); } - Future _post(String path, Map body) async { - var headers = _createHeaders(path, 'GetItems', body); + Future searchItems( + String keywords, List resources) async { + final body = { + "Keywords": keywords, + "Resources": resources, + "PartnerTag": this.partnerTag, + "PartnerType": "Associates", + "Marketplace": this.marketplace.name, + "Operation": PaAPIOperation.SearchItems.name + }; + final response = + await _post('/paapi5/getitems', body, PaAPIOperation.SearchItems); + return SearchItemsResponse.fromJson(response); + } + + Future _post( + String path, Map body, PaAPIOperation operation) async { + var headers = _createHeaders(path, operation.name, body); var url = Uri.parse('https://$host$path'); var response = await http.post(url, headers: headers, body: json.encode(body)); diff --git a/lib/item.dart b/lib/item.dart index eda9601..c120a17 100644 --- a/lib/item.dart +++ b/lib/item.dart @@ -1,5 +1,6 @@ import 'package:flutter_amazon_pa_api/images.dart'; import 'package:flutter_amazon_pa_api/item_info.dart'; +import 'package:flutter_amazon_pa_api/offers.dart'; import 'package:json_annotation/json_annotation.dart'; part 'item.g.dart'; @@ -18,7 +19,10 @@ class Item { @JsonKey(name: 'ItemInfo') ItemInfo? itemInfo; - Item(this.asin, this.detailPageURL, this.images, this.itemInfo); + @JsonKey(name: 'Offers') + Offers? offers; + + Item(this.asin, this.detailPageURL, this.images, this.itemInfo, this.offers); factory Item.fromJson(Map json) => _$ItemFromJson(json); Map toJson() => _$ItemToJson(this); diff --git a/lib/item.g.dart b/lib/item.g.dart index 61c08c5..b41b7ff 100644 --- a/lib/item.g.dart +++ b/lib/item.g.dart @@ -15,6 +15,9 @@ Item _$ItemFromJson(Map json) => Item( json['ItemInfo'] == null ? null : ItemInfo.fromJson(json['ItemInfo'] as Map), + json['Offers'] == null + ? null + : Offers.fromJson(json['Offers'] as Map), ); Map _$ItemToJson(Item instance) => { @@ -22,4 +25,5 @@ Map _$ItemToJson(Item instance) => { 'DetailPageURL': instance.detailPageURL, 'Images': instance.images, 'ItemInfo': instance.itemInfo, + 'Offers': instance.offers, }; diff --git a/lib/listings.dart b/lib/listings.dart new file mode 100644 index 0000000..8ec099e --- /dev/null +++ b/lib/listings.dart @@ -0,0 +1,127 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'listings.g.dart'; + +@JsonSerializable() +class Listings { + @JsonKey(name: 'Availability') + Availability? availability; + + @JsonKey(name: 'DeliveryInfo') + DeliveryInfo? deliveryInfo; + + @JsonKey(name: 'Price') + Price? price; + + @JsonKey(name: 'SavingBasis') + SavingBasis? savingBasis; + + Listings(this.availability, this.deliveryInfo, this.price, this.savingBasis); + + factory Listings.fromJson(Map json) => + _$ListingsFromJson(json); + Map toJson() => _$ListingsToJson(this); +} + +@JsonSerializable() +class Availability { + @JsonKey(name: 'MaxOrderQuantity') + num? maxOrderQuantity; + + @JsonKey(name: 'Message') + String? message; + + @JsonKey(name: 'MinOrderQuantity') + num? minOrderQuantity; + + @JsonKey(name: 'Type') + String? type; + + Availability( + this.maxOrderQuantity, this.message, this.minOrderQuantity, this.type); + + factory Availability.fromJson(Map json) => + _$AvailabilityFromJson(json); + Map toJson() => _$AvailabilityToJson(this); +} + +@JsonSerializable() +class DeliveryInfo { + @JsonKey(name: 'IsAmazonFulfilled') + bool? isAmazonFulfilled; + + @JsonKey(name: 'IsFreeShippingEligible') + bool? isFreeShippingEligible; + + @JsonKey(name: 'IsPrimeEligible') + bool? isPrimeEligible; + + DeliveryInfo(this.isAmazonFulfilled, this.isFreeShippingEligible, + this.isPrimeEligible); + + factory DeliveryInfo.fromJson(Map json) => + _$DeliveryInfoFromJson(json); + Map toJson() => _$DeliveryInfoToJson(this); +} + +@JsonSerializable() +class Price { + @JsonKey(name: 'Amount') + num? amount; + + @JsonKey(name: 'Currency') + String? currency; + + @JsonKey(name: 'DisplayAmount') + String? displayAmount; + + @JsonKey(name: 'Savings') + Savings? savings; + + Price(this.amount, this.currency, this.displayAmount, this.savings); + + factory Price.fromJson(Map json) => _$PriceFromJson(json); + Map toJson() => _$PriceToJson(this); +} + +@JsonSerializable() +class Savings { + @JsonKey(name: 'Amount') + num? amount; + + @JsonKey(name: 'Currency') + String? currency; + + @JsonKey(name: 'DisplayAmount') + String? displayAmount; + + @JsonKey(name: 'Percentage') + num? percentage; + + Savings(this.amount, this.currency, this.displayAmount, this.percentage); + + factory Savings.fromJson(Map json) => + _$SavingsFromJson(json); + Map toJson() => _$SavingsToJson(this); +} + +@JsonSerializable() +class SavingBasis { + @JsonKey(name: 'Amount') + num? amount; + + @JsonKey(name: 'Currency') + String? currency; + + @JsonKey(name: 'DisplayAmount') + String? displayAmount; + + @JsonKey(name: 'PriceType') + String? priceType; + + SavingBasis(this.amount, this.currency, this.displayAmount, this.priceType); + + factory SavingBasis.fromJson(Map json) => + _$SavingBasisFromJson(json); + Map toJson() => _$SavingBasisToJson(this); +} diff --git a/lib/listings.g.dart b/lib/listings.g.dart new file mode 100644 index 0000000..d2b040c --- /dev/null +++ b/lib/listings.g.dart @@ -0,0 +1,102 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'listings.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +Listings _$ListingsFromJson(Map json) => Listings( + json['Availability'] == null + ? null + : Availability.fromJson(json['Availability'] as Map), + json['DeliveryInfo'] == null + ? null + : DeliveryInfo.fromJson(json['DeliveryInfo'] as Map), + json['Price'] == null + ? null + : Price.fromJson(json['Price'] as Map), + json['SavingBasis'] == null + ? null + : SavingBasis.fromJson(json['SavingBasis'] as Map), + ); + +Map _$ListingsToJson(Listings instance) => { + 'Availability': instance.availability, + 'DeliveryInfo': instance.deliveryInfo, + 'Price': instance.price, + 'SavingBasis': instance.savingBasis, + }; + +Availability _$AvailabilityFromJson(Map json) => Availability( + json['MaxOrderQuantity'] as num?, + json['Message'] as String?, + json['MinOrderQuantity'] as num?, + json['Type'] as String?, + ); + +Map _$AvailabilityToJson(Availability instance) => + { + 'MaxOrderQuantity': instance.maxOrderQuantity, + 'Message': instance.message, + 'MinOrderQuantity': instance.minOrderQuantity, + 'Type': instance.type, + }; + +DeliveryInfo _$DeliveryInfoFromJson(Map json) => DeliveryInfo( + json['IsAmazonFulfilled'] as bool?, + json['IsFreeShippingEligible'] as bool?, + json['IsPrimeEligible'] as bool?, + ); + +Map _$DeliveryInfoToJson(DeliveryInfo instance) => + { + 'IsAmazonFulfilled': instance.isAmazonFulfilled, + 'IsFreeShippingEligible': instance.isFreeShippingEligible, + 'IsPrimeEligible': instance.isPrimeEligible, + }; + +Price _$PriceFromJson(Map json) => Price( + json['Amount'] as num?, + json['Currency'] as String?, + json['DisplayAmount'] as String?, + json['Savings'] == null + ? null + : Savings.fromJson(json['Savings'] as Map), + ); + +Map _$PriceToJson(Price instance) => { + 'Amount': instance.amount, + 'Currency': instance.currency, + 'DisplayAmount': instance.displayAmount, + 'Savings': instance.savings, + }; + +Savings _$SavingsFromJson(Map json) => Savings( + json['Amount'] as num?, + json['Currency'] as String?, + json['DisplayAmount'] as String?, + json['Percentage'] as num?, + ); + +Map _$SavingsToJson(Savings instance) => { + 'Amount': instance.amount, + 'Currency': instance.currency, + 'DisplayAmount': instance.displayAmount, + 'Percentage': instance.percentage, + }; + +SavingBasis _$SavingBasisFromJson(Map json) => SavingBasis( + json['Amount'] as num?, + json['Currency'] as String?, + json['DisplayAmount'] as String?, + json['PriceType'] as String?, + ); + +Map _$SavingBasisToJson(SavingBasis instance) => + { + 'Amount': instance.amount, + 'Currency': instance.currency, + 'DisplayAmount': instance.displayAmount, + 'PriceType': instance.priceType, + }; diff --git a/lib/offers.dart b/lib/offers.dart new file mode 100644 index 0000000..ee2249c --- /dev/null +++ b/lib/offers.dart @@ -0,0 +1,15 @@ +import 'package:json_annotation/json_annotation.dart'; +import 'package:flutter_amazon_pa_api/listings.dart'; + +part 'offers.g.dart'; + +@JsonSerializable() +class Offers { + @JsonKey(name: 'Listings') + List? listings; + + Offers(this.listings); + + factory Offers.fromJson(Map json) => _$OffersFromJson(json); + Map toJson() => _$OffersToJson(this); +} diff --git a/lib/offers.g.dart b/lib/offers.g.dart new file mode 100644 index 0000000..66c4998 --- /dev/null +++ b/lib/offers.g.dart @@ -0,0 +1,17 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'offers.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +Offers _$OffersFromJson(Map json) => Offers( + (json['Listings'] as List?) + ?.map((e) => Listings.fromJson(e as Map)) + .toList(), + ); + +Map _$OffersToJson(Offers instance) => { + 'Listings': instance.listings, + }; diff --git a/lib/paapi_marketplace.dart b/lib/paapi_marketplace.dart new file mode 100644 index 0000000..13a8228 --- /dev/null +++ b/lib/paapi_marketplace.dart @@ -0,0 +1,69 @@ +enum PaAPIMarketplace { + /// USA - Marketplace: "www.amazon.com", Host: "webservices.amazon.com", Region: "us-east-1" + us("www.amazon.com", "webservices.amazon.com", "us-east-1"), + + /// Canada - Marketplace: "www.amazon.ca", Host: "webservices.amazon.ca", Region: "us-east-1" + ca("www.amazon.ca", "webservices.amazon.ca", "us-east-1"), + + /// Mexico - Marketplace: "www.amazon.mx", Host: "webservices.amazon.mx", Region: "us-east-1" + mx("www.amazon.com.mx", "webservices.amazon.com.mx", "us-east-1"), + + /// Brazil - Marketplace: "www.amazon.com.br", Host: "webservices.amazon.com.br", Region: "us-east-1" + br("www.amazon.com.br", "webservices.amazon.com.br", "us-east-1"), + + /// UK - Marketplace: "www.amazon.co.uk", Host: "webservices.amazon.co.uk", Region: "eu-west-1" + uk("www.amazon.co.uk", "webservices.amazon.co.uk", "eu-west-1"), + + /// France - Marketplace: "www.amazon.fr", Host: "webservices.amazon.fr", Region: "eu-west-1" + fr("www.amazon.fr", "webservices.amazon.fr", "eu-west-1"), + + /// Germany - Marketplace: "www.amazon.de", Host: "webservices.amazon.de", Region: "eu-west-1" + de("www.amazon.de", "webservices.amazon.de", "eu-west-1"), + + /// Spain - Marketplace: "www.amazon.es", Host: "webservices.amazon.es", Region: "eu-west-1" + es("www.amazon.es", "webservices.amazon.es", "eu-west-1"), + + /// India - Marketplace: "www.amazon.in", Host: "webservices.amazon.in", Region: "eu-west-1" + india("www.amazon.in", "webservices.amazon.in", "eu-west-1"), + + /// Italy - Marketplace: "www.amazon.it", Host: "webservices.amazon.it", Region: "eu-west-1" + it("www.amazon.it", "webservices.amazon.it", "eu-west-1"), + + /// Arab Emirates - Marketplace: "www.amazon.ae", Host: "webservices.amazon.ae", Region: "eu-west-1" + ae("www.amazon.ae", "webservices.amazon.ae", "eu-west-1"), + + /// Saudi Arabia - Marketplace: "www.amazon.sa", Host: "webservices.amazon.sa", Region: "eu-west-1" + sa("www.amazon.sa", "webservices.amazon.sa", "eu-west-1"), + + /// Turkey - Marketplace: "www.amazon.com.tr", Host: "webservices.amazon.com.tr", Region: "eu-west-1" + tr("www.amazon.com.tr", "webservices.amazon.com.tr", "eu-west-1"), + + /// Netherlands - Marketplace: "www.amazon.nl", Host: "webservices.amazon.nl", Region: "eu-west-1" + nl("www.amazon.nl", "webservices.amazon.nl", "eu-west-1"), + + /// Sweden - Marketplace: "www.amazon.se", Host: "webservices.amazon.se", Region: "eu-west-1" + se("www.amazon.se", "webservices.amazon.se", "eu-west-1"), + + /// Poland - Marketplace: "www.amazon.pl", Host: "webservices.amazon.pl", Region: "eu-west-1" + pl("www.amazon.pl", "webservices.amazon.pl", "eu-west-1"), + + /// Egypt - Marketplace: "www.amazon.eg", Host: "webservices.amazon.eg", Region: "eu-west-1" + eg("www.amazon.eg", "webservices.amazon.eg", "eu-west-1"), + + /// Belgium - Marketplace: "www.amazon.com.be", Host: "webservices.amazon.com.be", Region: "eu-west-1" + be("www.amazon.com.be", "webservices.amazon.com.be", "eu-west-1"), + + /// Japan - Marketplace: "www.amazon.co.jp", Host: "webservices.amazon.co.jp", Region: "eu-west-1" + jp("www.amazon.co.jp", "webservices.amazon.co.jp", "us-west-2"), + + /// Australia - Marketplace: "www.amazon.com.au", Host: "webservices.amazon.com.au", Region: "us-west-2" + au("www.amazon.com.au", "webservices.amazon.com.au", "us-west-2"), + + /// Singapore - Marketplace: "www.amazon.sg", Host: "webservices.amazon.sg", Region: "us-west-2" + sg("www.amazon.sg", "webservices.amazon.sg", "us-west-2"); + + const PaAPIMarketplace(this.name, this.host, this.region); + final String name; + final String host; + final String region; +} diff --git a/lib/paapi_operation.dart b/lib/paapi_operation.dart new file mode 100644 index 0000000..1657f32 --- /dev/null +++ b/lib/paapi_operation.dart @@ -0,0 +1,10 @@ +enum PaAPIOperation { + /// GetItems + GetItems("GetItems"), + + /// SearchItems + SearchItems("SearchItems"); + + const PaAPIOperation(this.name); + final String name; +} diff --git a/lib/paapi_resource.dart b/lib/paapi_resource.dart new file mode 100644 index 0000000..cd372c4 --- /dev/null +++ b/lib/paapi_resource.dart @@ -0,0 +1,58 @@ +/// List of implemented handled responses +enum PaAPIResource { + /// BrowseNodeInfo.BrowseNodes + BrowseNodeInfo_BrowseNodes("BrowseNodeInfo.BrowseNodes"), + + /// Images.Primary.Small + Images_Primary_Small("Images.Primary.Small"), + + /// Images.Primary.Medium + Images_Primary_Medium("Images.Primary.Medium"), + + /// Images.Primary.Large + Images_Primary_Large("Images.Primary.Large"), + + /// ItemInfo.ByLineInfo + ItemInfo_ByLineInfo("ItemInfo.ByLineInfo"), + + /// ItemInfo.ContentInfo + ItemInfo_ContentInfo("ItemInfo.ContentInfo"), + + /// ItemInfo.Title + ItemInfo_Title("ItemInfo.Title"), + + /// Offers.Listings.Availability.MaxOrderQuantity + Offers_Listings_Availability_MaxOrderQuantity( + "Offers.Listings.Availability.MaxOrderQuantity"), + + /// Offers.Listings.Availability.Message + Offers_Listings_Availability_Message("Offers.Listings.Availability.Message"), + + /// Offers.Listings.Availability.MinOrderQuantity + Offers_Listings_Availability_MinOrderQuantity( + "Offers.Listings.Availability.MinOrderQuantity"), + + /// Offers.Listings.Availability.Type + Offers_Listings_Availability_Type("Offers.Listings.Availability.Type"), + + /// Offers.Listings.DeliveryInfo.IsAmazonFulfilled + Offers_Listings_DeliveryInfo_IsAmazonFulfilled( + "Offers.Listings.DeliveryInfo.IsAmazonFulfilled"), + + /// Offers.Listings.DeliveryInfo.IsFreeShippingEligible + Offers_Listings_DeliveryInfo_IsFreeShippingEligible( + "Offers.Listings.DeliveryInfo.IsFreeShippingEligible"), + + /// Offers.Listings.DeliveryInfo.IsPrimeEligible + Offers_Listings_DeliveryInfo_IsPrimeEligible( + "Offers.Listings.DeliveryInfo.IsPrimeEligible"), + + /// Offers.Listings.Price + Offers_Listings_Price("Offers.Listings.Price"), + + /// Offers.Listings.SavingBasis + Offers_Listings_SavingBasis("Offers.Listings.SavingBasis"); + + const PaAPIResource(this.name); + final String name; +} diff --git a/lib/search_items_response.dart b/lib/search_items_response.dart new file mode 100644 index 0000000..868a471 --- /dev/null +++ b/lib/search_items_response.dart @@ -0,0 +1,16 @@ +import 'package:json_annotation/json_annotation.dart'; +import 'package:flutter_amazon_pa_api/items_result.dart'; + +part 'search_items_response.g.dart'; + +@JsonSerializable() +class SearchItemsResponse { + @JsonKey(name: 'SearchResult') + ItemsResult? searchResult; + + SearchItemsResponse(this.searchResult); + + factory SearchItemsResponse.fromJson(Map json) => + _$SearchItemsResponseFromJson(json); + Map toJson() => _$SearchItemsResponseToJson(this); +} diff --git a/lib/search_items_response.g.dart b/lib/search_items_response.g.dart new file mode 100644 index 0000000..4b4c551 --- /dev/null +++ b/lib/search_items_response.g.dart @@ -0,0 +1,20 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'search_items_response.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +SearchItemsResponse _$SearchItemsResponseFromJson(Map json) => + SearchItemsResponse( + json['SearchResult'] == null + ? null + : ItemsResult.fromJson(json['SearchResult'] as Map), + ); + +Map _$SearchItemsResponseToJson( + SearchItemsResponse instance) => + { + 'SearchResult': instance.searchResult, + }; diff --git a/pubspec.yaml b/pubspec.yaml index 8c67b0a..3cd8922 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -4,7 +4,7 @@ version: 0.0.8 homepage: https://github.com/meganii/flutter_amazon_pa_api environment: - sdk: ">=2.16.0 <3.0.0" + sdk: ">=2.17.0 <3.0.0" dependencies: crypto: ^3.0.1