i am using the amazon API product description in order to get price lists for specific items in their database. however after getting a few thousand items i am getting:
System.ServiceModel.ServerTooBusyException was unhandled
Message="The HTTP service located at https://webservices.amazon.com/onca/soap?Service=AWSECommerceService is too busy. "
it looks like they do not want users to bother them too much with too many requests.
i will need to do probably around 1,000,000 requests per day.
i am wondering if there is any way to get over this limit?
here is how i am request my data:
// prepare the first ItemSearchRequest
// prepare a second ItemSearchRequest
ItemSearchRequest request1 = new ItemSearchRequest();
request1.SearchIndex = "All";
request1.Keywords = table.Rows[i].ItemArray[0].ToString();
request1.ItemPage = "1";
request1.ResponseGroup = new string[] { "OfferSummary" };
// batch the two requests together
ItemSearch itemSearch = new ItemSearch();
itemSearch.Request = new ItemSearchRequest[] { request1 };
itemSearch.AWSAccessKeyId = accessKeyId;
// issue the ItemSearch request
ItemSearchResponse response = client.ItemSearch(itemSearch);
You would contact Amazon and ask them. You don't try to "get around" it.
I suppose you could get a bunch of servers on different IPs. But just ask Amazon, it's the right thing to do.
Related
I have an application that uses a deep integration with Stripe. My platform is occasionally charging the connected accounts and I am storing information about these charges in the metadata of the charge itself.
I want to display this information back to the connected account so I am using the Charge Service to list charges. However I want to filter that list based on some metadata key/value pair so that I don't have to list all of the charges each time, for every connected account.
Is there a clever way of doing this?
Filtering based on metadata is not supported.
Since you are doing this across Connected accounts, a better approach would be to store the Charge ID and metadata on your end so that you don't have to list and paginate through charges, looking for particular metadata.
Stripe recently added Search API that allows search by metadata. Currently, it does not have .NET / C# integration but you can query using HTTP call directly:
Example from their docs:
curl https://api.stripe.com/v1/search/charges \
-u sk_test_daHanKyJOCiXCeBsa65biLML00wylimZ8S: \
--data-urlencode "query=metadata['key']:'value'" \
-H "Stripe-Version: 2020-08-27;search_api_beta=v1" \
-G
I was able to accomplish this with the "TransferGroup" property of the ChargeCreateOptions object. I basically set the TransferGroup property to a unique value for each connected account which I can then query later.
CREATE THE CHARGE
//THERE IS NO ID IN THE REQUEST OPTIONS BECAUSE
//WE ARE USING THE PLATFORM
var requestOptions = new RequestOptions()
{
ApiKey = {API Key}
};
//CHARGE OPTIONS
var chargeOptions = new ChargeCreateOptions
{
Amount = {AMOUNT},
Currency = "usd",
Description = $"Platform Charge",
SourceId = {StripeId},
Metadata = {MetaData},
TransferGroup = {Unique String For Each Connected Account}
};
//CREAT AND RETURN THE CHARGE
var chargeService = new ChargeService();
return chargeService.Create(chargeOptions, requestOptions);
QUERY THE PLATFORM CHARGES BASED ON TRANSFER GROUP
public List<Charge> StripePlatformCharges(string apiKey, int days, string transferGroup)
{
try
{
RequestOptions requestOptions = new RequestOptions()
{
ApiKey = apiKey
};
ChargeListOptions chargeListOptions = new ChargeListOptions()
{
CreatedRange = new DateRangeOptions()
{
GreaterThanOrEqual = DateTime.Now.AddDays(-days)
},
TransferGroup = transferGroup
};
ChargeService chargeService = new ChargeService();
return chargeService.List(chargeListOptions, requestOptions).ToList();
}
catch (Exception ex)
{
throw new Exception("Error getting stripe charges from the platform", ex);
}
}
Stripe recently released a Search API which offers filtering by metadata for certain resources including charges. The documentation is at https://stripe.com/docs/search
I'm using RestSharp to programmatically build/make/deserialize a call to the Azure Maps API for batch geocoding. Currently testing the process with a 5 address batch--something I expected to go quickly after all the posts about "10k addresses in minutes." But a request I make successfully yesterday is still not available, only showing the "Accepted 202" status that the documentation says means it's still processing...and there's no outage showing at the status page.
I've replicated these calls and results using Postman, so I'm not sure there's a code problem per se...but it wouldn't be the first time I got tunnel vision and overlooked something obvious.
My POST call is generated with the code below, and returns an OK status with the necessary Location header with what looks like a valid link.
public RestRequest CreateBatchRequest()
{
var request = new RestRequest($"{_batchAddressEndpoint}subscription-key={_apiToken}", Method.POST);
request.AddQueryParameter("api-version", _version);
var batchRequestBody = GenerateQueryBatch();
var requestBodyJson = JsonConvert.SerializeObject(batchRequestBody);
request.AddHeader("Content-Type", "application/json");
request.AddParameter("undefined", requestBodyJson, ParameterType.RequestBody);
return request;
}
protected AzureBatchRequest GenerateQueryBatch()
{
var requestBody = new AzureBatchRequest();
foreach (var address in Addresses)
{
var addressString = $"{address.Address}, {address.City}, {address.State}";
if (!string.IsNullOrEmpty(_country))
addressString = $"{address.Address}, {address.City}, {address.State}, {_country.ToUpper()}";
requestBody.Queries.Add($"?query={addressString}&limit={_resultLimit}");
}
return requestBody;
}
This gives me a body parameter for the request that appears to match the documentation (actual addresses hidden for privacy reason, but they've been successfully geocoded with other services)...
{
undefined={"queries":[
"?query=123 MAIN ST, LOS ANGELES, CA&limit=3",
"?query=123 MAIN ST, PLEASANTVILLE, CA&limit=3",
"?query=123 MAIN ST, STOCKTON, CA&limit=3",
"?query=123 MAIN ST, SAN DIEGO, CA&limit=3",
"?query=123 MAIN ST, REDDING, CA&limit=3"
]}
}
I get the Location header value and make the GET call with it using the code below...
public List<Coordinate> DeserializeBatchResponse(RestResponse response)
{
var batchLink = response.Headers.Where(header => header.Name.Equals("Location")).FirstOrDefault();
var request = new RestRequest(batchLink.Value.ToString(), Method.GET);
var batch = SendRequest(request);
if (batch.StatusCode == System.Net.HttpStatusCode.Accepted)
{
var isProcessing = true;
while (isProcessing)
{
Thread.Sleep(TimeSpan.FromSeconds(60));
request = new RestRequest(batchLink.Value.ToString(), Method.GET);
batch = SendRequest(request);
if (batch.StatusCode != System.Net.HttpStatusCode.Accepted)
isProcessing = false;
}
}
}
And it never leaves that loop. When I hardcode the URL returned from yesterday's POST request, it has the same behavior--as ditto when tried in Postman to isolate from the rest of my code.
Does anyone have any insight?
UPDATE
We discovered that after creating a new plan at a higher tier (the S1 rather than S0 tier) there was no noticeable delay on the batch calls. Still not a solution, per se, because that prices us out of the product for production purposes, but possibly a fix for others until the updates mentioned in the accepted answer come to fruition.
This is to be expected at the moment while in preview. This will be moving out of preview soon and will be significantly faster.
I'm attempting to capture a PayPal transaction that has been authorized using the PayPal button. I'm trying to use CyberSource Simple Order API to do this. I have the only 3 pieces of information that seem to come back from the PayPal button are: payerID, paymentID and paymentToken. I've tried a few ways of handing this off to the Simple Order API, but always get a 102 code with the DECLINE message in the response. Cybersource's logging system indicates this is because The following request field(s) is either invalid or missing: request_token.
Do I need to conduct the whole transaction - authorize and capture - via cybersource? Or what is the way I can take the paypal-generated button and authorize a transaction, then capture it via CyberSource?
Here's my code snippet for the CyberSource SOAPI request:
RequestMessage request = new RequestMessage
{
merchantID = WebConfigurationManager.AppSettings["cybs.merchantID"]
, payPalDoCaptureService = new PayPalDoCaptureService {
run = "true"
, invoiceNumber = orders
, paypalAuthorizationId = authId
, paypalAuthorizationRequestToken = requestToken
, completeType = "Complete" }
, clientApplication = "MyClient Application"
, clientApplicationVersion = "2.0"
, clientApplicationUser = userName
, clientEnvironment = WebConfigurationManager.AppSettings["Tier"]
, merchantReferenceCode = orders
, customerID = OrderConstants.CustomerNumber
, merchantDefinedData = new MerchantDefinedData { field1 = "Customer #: " + OrderConstants.CustomerNumber, field2 = orders }
, purchaseTotals = new PurchaseTotals { currency = "usd", grandTotalAmount = total, taxAmount = taxtotal }
, item = items.ToArray()
};
ReplyMessage reply = new ReplyMessage();
try
{
reply = SoapClient.RunTransaction(request);
}
catch (Exception ex)
{
reply.decision = "SYSTEM ERROR";
reply.additionalData = string.Format("Error processing request. Exception message: {0}", ex.Message);
}
Have you tried setting up your request like this? Pay no attention to the values I used, but I am looking at the example in the docs (Example 21 Request for payPalDoCaptureService). You'll need to ctrl+f to find it.
RequestMessage request = new RequestMessage
{
payPalDoCaptureService_run=true
, merchantID = WebConfigurationManager.AppSettings["cybs.merchantID"]
, merchantReferenceCode = HTNsubscription9647
, purchaseTotals = new PurchaseTotals { currency = "usd", grandTotalAmount = total, taxAmount = taxtotal }
, payPalDoCaptureService = new PayPalDoCaptureService
{
run = "true"
, invoiceNumber = orders
, paypalAuthorizationId = authId
, paypalAuthorizationRequestID = authReqId
, paypalAuthorizationRequestToken = requestToken
, completeType = "Complete"
}
}
I did end up solving this problem. My question involving whether I needed to employ CyberSource from end-to-end was the correct path. Eventually I figured out - after talking for days with CyberSource support - that I needed to use their "Set Service" in a WebApi method to generate the PayPal "token" and give that to the PayPal button via ajax within the javascript that generates the button. I also had to dig deep into their and PayPal's documentation and to figure out how to make that work because it wasn't super clear initially. At any rate, once I did the Set service, I gathered the information in the response from CyberSource, along with the PayPal token, and send that along to a Get Details service call (also CyberSource). Finally that information is given to a combined Do Sale and Do Capture service call and the funds are captured. The button only allowed a user to log into their PayPal account and verify they wanted to continue with the transaction.
CyberSource's documentation is a little lacking in clarity with regards to this, and they link to a deprecated set of PayPal documentation, which doesn't make anything easier.
As the error says you have git invalid data, please check the reply fields invalidField_0 through invalidField_N for
the invalid fields.
I hope these Requesting the Authorization Service samples will help
No matter what I do I'm always getting the same AuthorizationException for the following request on sandbox with valid keys:
REQUEST:
{"CreditCard":
{
"CVV":"123",
"ExpirationMonth":"10",
"ExpirationYear":"2016",
"Number":"4111 1111 1111 1111"
},
"Amount":195.000000,
"OrderId":"bb461ebb-b894-4716-9ea2-7317f9e8c8d9",
"MerchantAccountId":"xxxxx",
"TaxAmount":0,
"Type":{},
"CustomFields":
{
"correlation_id":"bb461ebb-b894-4716-9ea2-7317f9e8c8d9"
},
"Options":
{
"StoreInVault":true,
"SubmitForSettlement":true
},
"CustomerId":"2012f124-2f00-477f-85fb-f6bc3f5fe275"
}
Here's the code that I'm using to create this request:
var request = new TransactionRequest
{
OrderId = message.Id.ToString(),
Amount = message.Amount,
CustomerId = message.CustomerId.ToString(),
MerchantAccountId = message.MerchantAccountId,
Options = new TransactionOptionsRequest
{
SubmitForSettlement = message.SubmitForSettlement
}
};
string expirationMonth = message.ExpirationMonth.GetValueOrDefault().ToString(CultureInfo.InvariantCulture);
string expirationYear = message.ExpirationYear.GetValueOrDefault().ToString(CultureInfo.InvariantCulture);
request.CreditCard = new TransactionCreditCardRequest
{
Token = message.CreditCardId.ToString(),
CardholderName = message.CardholderName,
Number = message.CardNumber,
ExpirationMonth = expirationMonth,
ExpirationYear = expirationYear,
CVV = message.CVV
};
request.Options.StoreInVault = true;
var result = _gateway.Transaction.Sale(request);
What I'm missing?
I work at Braintree. If you have any other questions, feel free to contact our support team directly.
This error can happen when you're attempting to use a merchant account that your user doesn't have access to. In this case, the merchant account ID you're passing (which you X'd out above) doesn't exist.
Take a look at our support pages for more information on merchant account IDs:
Merchant Account ID
With Braintree, you can have multiple merchant accounts processing via the same single gateway account. You could have multiple locations, multiple businesses, and multiple currencies all setup and processing under one single account. This makes it easy to keep track of all of your processing with unified reporting and access and can even save you money.
You can find the values for all merchant accounts in your gateway account by following these steps:
Log in to the Control Panel
Navigate to Settings -> Processing
Scroll to the bottom of the page to find the section labeled Merchant Accounts
If I only have a single merchant account, do I still need to send this value with API requests?
No, this is an optional value with all API requests that support it. If you only have a single merchant account, there's no need to include this value. If you have more than one merchant account you can specify which merchant account should be used with each API request. If you omit this value, all requests will automatically be routed through your default account.
I can not find a working example of the new amazon service (or at least, within the last couple of years). The closest working example just comes back with a null item no matter what I put in the title. The code is:
// Amazon ProductAdvertisingAPI client
AWSECommerceServicePortTypeClient amazonClient = new AWSECommerceServicePortTypeClient();
// prepare an ItemSearch request
ItemSearchRequest request = new ItemSearchRequest();
request.SearchIndex = "Books";
request.Title = "C#";
request.Condition = Condition.All;
//request.ResponseGroup = new string[] { "Small" };
ItemSearch itemSearch = new ItemSearch();
itemSearch.Request = new ItemSearchRequest[] { request };
itemSearch.AWSAccessKeyId = ConfigurationManager.AppSettings["accessKeyId"];
// send the ItemSearch request
ItemSearchResponse response = amazonClient.ItemSearch(itemSearch);
// write out the results from the ItemSearch request
foreach (var itemLst in response.Items)
{
if (itemLst.Item != null)
{
foreach (var item in response.Items[0].Item)
{
Console.WriteLine(item.ItemAttributes.Title);
}
}
else
Console.WriteLine("No item info was found for this response list item.");
}
Console.WriteLine("<Done...press enter to continue>");
Console.ReadLine();
What am I doing wrong?
I'm assuming that you've downloaded the code from here. If this is correct then you need to replace this line:
AWSECommerceServicePortTypeClient amazonClient = new AWSECommerceServicePortTypeClient();
With these lines:
BasicHttpBinding binding = new BasicHttpBinding(BasicHttpSecurityMode.Transport);
binding.MaxReceivedMessageSize = int.MaxValue;
AWSECommerceServicePortTypeClient amazonClient = new AWSECommerceServicePortTypeClient(
binding,
new EndpointAddress("https://webservices.amazon.com/onca/soap?Service=AWSECommerceService"));
// add authentication to the ECS client
amazonClient.ChannelFactory.Endpoint.Behaviors.Add(new AmazonSigningEndpointBehavior(accessKeyId, secretKey));
The problem is two fold:
You are not binding the amazonClient to an HttpBinding
You are not signing the request
If my assumption is incorrect then you should download the code from the above link as it is a working example of how to call the Amazon Product API.
I believe your problem may be lack of an Associate Tag. As of November, 2011, this is required for all requests and I noticed early on in my testing that I got null responses back (with an error code) when I didn't include it. I'm not sure if that's still the behavior but I'd definitely assume that if you aren't adding it (which I don't see in your code) that's a likely suspect.
Look at top change note here
If you don't have an Associate ID you will need to apply for one.