I've building out a small app that connects to a Quickbooks API via an SDK. The SDK provides batch operations to help reduce the number of API requests needed.
However, I'm hoping to make a large amount of requests (ie: bulk deletes, uploads in the 100s/1000s). I've gotten the deletes to work, however, now I'm hoping to integrate Laravel's Queue system so that any items in the $batch that fail (due to these business-rules or other reasons) are sent to a worker who will reattempt them after waiting a minute .
Below is an example of a delete request.
class QuickBooksAPIController extends Controller
{
public function batchDelete(Request $request, $category)
{
$chunks = array_chunk($request->data, 30);
foreach ($chunks as $key => $value) {
$batch[$key] = $this->dataService()->CreateNewBatch();
foreach ($value as $id) {
$item = $this->dataService()->FindById($category, $id);
$batch[$key]->AddEntity($item, $id, "delete");
}
$batch[$key]->Execute();
}
return response()->json(['message' => 'Items Deleted'], 200);
}
}
The documentations are a bit sparse for my scenario though. How can I get the failed batch items on order to try again?
Is using batches even the right choice here? Because I have to hit the API anyway to get the $item... which doesn't make sense to me (I think I'm doing something wrong there).
EDIT:
I intentionally sent out a request with more then 30 items and this is the failure message. Which doesn't have the values that didn't make the cut.
EDIT#2:
Ended up using array_chunk to separate the payload into 30 items (which is the limit of the API). Doing so helps process many requests. I've adjusted my code above to represent my current code.
How can I get the failed batch items on order to try again?
If you look at Intuit's documentation, you can see that the HTTP response the API returns contains this information. Here's the example request they show:
{
"BatchItemRequest": [
{
"bId": "bid1",
"Vendor": {
"DisplayName": "Smith Family Store"
},
"operation": "create"
},
{
"bId": "bid2",
"operation": "delete",
"Invoice": {
"SyncToken": "0",
"Id": "129"
}
},
{
"SalesReceipt": {
"PrivateNote": "A private note.",
"SyncToken": "0",
"domain": "QBO",
"Id": "11",
"sparse": true
},
"bId": "bid3",
"operation": "update"
},
{
"Query": "select * from SalesReceipt where TotalAmt > '300.00'",
"bId": "bid4"
}
]
}
And the corresponding response:
{
"BatchItemResponse": [
{
"Fault": {
"type": "ValidationFault",
"Error": [
{
"Message": "Duplicate Name Exists Error",
"code": "6240",
"Detail": "The name supplied already exists. : Another customer, vendor or employee is already using this \nname. Please use a different name.",
"element": ""
}
]
},
"bId": "bid1"
},
{
"Fault": {
"type": "ValidationFault",
"Error": [
{
"Message": "Object Not Found",
"code": "610",
"Detail": "Object Not Found : Something you're trying to use has been made inactive. Check the fields with accounts, customers, items, vendors or employees.",
"element": ""
}
]
},
"bId": "bid2"
},
{
"Fault": {
"type": "ValidationFault",
"Error": [
{
"Message": "Stale Object Error",
"code": "5010",
"Detail": "Stale Object Error : You and root were working on this at the same time. root finished before you did, so your work was not saved.",
"element": ""
}
]
},
"bId": "bid3"
},
{
"bId": "bid4",
"QueryResponse": {
"SalesReceipt": [
{
"TxnDate": "2015-08-25",
"domain": "QBO",
"CurrencyRef": {
"name": "United States Dollar",
"value": "USD"
},
"PrintStatus": "NotSet",
"PaymentRefNum": "10264",
"TotalAmt": 337.5,
"Line": [
{
"Description": "Custom Design",
"DetailType": "SalesItemLineDetail",
"SalesItemLineDetail": {
"TaxCodeRef": {
"value": "NON"
},
"Qty": 4.5,
"UnitPrice": 75,
"ItemRef": {
"name": "Design",
"value": "4"
}
},
"LineNum": 1,
"Amount": 337.5,
"Id": "1"
},
{
"DetailType": "SubTotalLineDetail",
"Amount": 337.5,
"SubTotalLineDetail": {}
}
],
"ApplyTaxAfterDiscount": false,
"DocNumber": "1003",
"PrivateNote": "A private note.",
"sparse": false,
"DepositToAccountRef": {
"name": "Checking",
"value": "35"
},
"CustomerMemo": {
"value": "Thank you for your business and have a great day!"
},
"Balance": 0,
"CustomerRef": {
"name": "Dylan Sollfrank",
"value": "6"
},
"TxnTaxDetail": {
"TotalTax": 0
},
"SyncToken": "1",
"PaymentMethodRef": {
"name": "Check",
"value": "2"
},
"EmailStatus": "NotSet",
"BillAddr": {
"Lat": "INVALID",
"Long": "INVALID",
"Id": "49",
"Line1": "Dylan Sollfrank"
},
"MetaData": {
"CreateTime": "2015-08-27T14:59:48-07:00",
"LastUpdatedTime": "2016-04-15T09:01:10-07:00"
},
"CustomField": [
{
"DefinitionId": "1",
"Type": "StringType",
"Name": "Crew #"
}
],
"Id": "11"
}
],
"startPosition": 1,
"maxResults": 1
}
}
],
"time": "2016-04-15T09:01:18.141-07:00"
}
Notice the separate response object for each request.
The bId value is a unique value you send in the request, which is then echo'd back to you in the response, so you can match up the requests you send with the responses you get back.
Here's the docs:
https://developer.intuit.com/app/developer/qbo/docs/api/accounting/all-entities/batch#sample-batch-request
Is using batches even the right choice here?
Batches make a lot of sense when you are doing a lot of things all at once.
The way you're trying to use them is... weird. What you should probably be doing is:
Batch 1
- go find all your items
Batch 2
- delete all the items
Your existing code doesn't make sense because you're trying to both find the item and delete the item in the exact same batch HTTP request, which isn't possible via the API.
I intentionally sent out a request with more then 30 items and this is the failure message.
No, it's not. That's a PHP error message - you have an error in your code.
You need to fix the PHP error, and then look at the actual response you're getting back from the API.
Related
I try to implement a paypal express checkout flow to a website.
The user must be able to adjust the order after they have logged in with paypal and choosed their payment option.
To do so, I create the order with paypal with the intent "AUTHORIZE" and user action "CONTINUE". It is created, the user is sent back to our website and I can fetch order and payer information with the (order) id created.
But then, when the payer is done checking and adjusting some final parameters and clicks the "buy now" button, I want to send the update order call as referenced here:
https://developer.paypal.com/api/orders/v2/#orders_patch
I create the payload in exactly the same way as for order creation, but this time with intent "CAPTURE" and user_action="PAY_NOW". I PATCH it to the correct path, but I keep getting the failure message "MALFORMED_REQUEST_JSON" .
Hoewever, the JSON itself is a valid JSON, no errors thrown in creation. Here is a sample:
{
"intent": "CAPTURE",
"application_context": {
"landing_page": "NO_PREFERENCE",
"shipping_preference": "SET_PROVIDED_ADDRESS",
"user_action": "PAY_NOW"
},
"purchase_units": [{
"reference_id": "2289256",
"description": "Your order at site",
"custom_id": "order id 2289256",
"soft_descriptor": "site name",
"invoice_id": "2289256",
"amount": {
"currency_code": "EUR",
"value": 59.98,
"breakdown": {
"item_total": {
"currency_code": "EUR",
"value": 50.41
},
"shipping": {
"currency_code": "EUR",
"value": 0
},
"discount": {
"currency_code": "EUR",
"value": 0
},
"tax_total": {
"currency_code": "EUR",
"value": 9.57
}
}
},
"items": [{
"name": "Product 1",
"description": "Product 1 Description",
"sku": "1019879",
"unit_amount": {
"currency_code": "EUR",
"value": 16.8
},
"tax": {
"currency_code": "EUR",
"value": 3.19
},
"quantity": "1",
"category": "PHYSICAL_GOODS"
}, {
"name": "Product 2",
"description": "Product 2 Description",
"sku": "1024593",
"unit_amount": {
"currency_code": "EUR",
"value": 33.61
},
"tax": {
"currency_code": "EUR",
"value": 6.38
},
"quantity": "1",
"category": "PHYSICAL_GOODS"
}],
"shipping": {
"name": {
"full_name": "John Doe"
},
"address": {
"address_line_1": "Badensche Str. 24",
"address_line_2": "",
"admin_area_2": "Berlin(Berlin)",
"postal_code": "10715",
"country_code": "DE"
}
}
}]
}
Here is paypal´s response
{
name: "INVALID_REQUEST",
message: "Request is not well-formed, syntactically incorrect, or violates schema.",
debug_id: "c315ce9eb90b4",
details: [{
field: "/",
location: "body",
issue: "MALFORMED_REQUEST_JSON",
description: "The request JSON is not well formed.",
}],
links: [{
href: "https://developer.paypal.com/docs/api/orders/v2/#error-MALFORMED_REQUEST_JSON",
rel: "information_link",
encType: "application/json",
}],
}
I just cannot figure out the problem. I tried to remove the whole application_context, purchase_units, intent and see if there is a problem within any of these parameters. Nothing changed. What exactly is wrong with this call?
That's not how a patch operation works. A patch operation's JSON payload must look something like the example in the API reference:
'[
{
"op": "replace",
"path": "/purchase_units/#reference_id==\'default\'/shipping/address",
"value": {
"address_line_1": "123 Townsend St",
"address_line_2": "Floor 6",
"admin_area_2": "San Francisco",
"admin_area_1": "CA",
"postal_code": "94107",
"country_code": "US"
}
}
]'
However, based on the information provided it's unclear why you are attempting to use a PATCH, since you do not list any relevant fields for a patch.
Intent authorize and intent capture are for creating an order. Use one or the other, never both for the same transaction. To capture an order that was created with intent capture, use a capture API call. To authorize an order that was created with intent authorize, use an authorize API call. The relevant API endpoints for either will be in the API response when you create the order, or you can read the documentation.
Since it appears your intended result is a captured payment (rather than an authorization), intent authorize is not relevant to your use case.
I am using the Google Fit REST API for our current project. We can add some of the data sources but there are some we cannot add. We already followed the documentations. Some of the request are returning this kind of error message (we even tried in their OAuth playground)
"error": {
"status": "INVALID_ARGUMENT",
"message": "Data type does not match well-known data type with the same name",
"code": 400,
"errors": [
{
"reason": "invalidArgument",
"message": "Data type does not match well-known data type with the same name",
"domain": "global"
}
]
}
What we are trying to add is this data source which we already allow the permission scopes of
https://www.googleapis.com/auth/fitness.activity.read
https://www.googleapis.com/auth/fitness.activity.write
{
"application": {
"name": "PersonalPulse",
"version": "1.0.0"
},
"dataType": {
"name": "com.google.activity.exercise",
"field": [
{
"name": "exercise",
"format": "integer"
},
{
"name": "repetitions",
"format": "integer"
},
{
"name": "resistance type",
"format": "integer"
},
{
"name": "resistance",
"format": "floatPoint"
},
{
"name": "duration",
"format": "integer"
}
]
},
"device": {
"manufacturer": "browser",
"model": "browser",
"type": "unknown",
"uid": "2",
"version": "10"
},
"type": "derived"
}
We followed the instructions based on the documentations here
https://developers.google.com/fit/datatypes/activity#workout
We are using PHP/Laravel framework for the fitness api and cURL for the http requests. If someone got an idea why we got the error please help. Thanks.
You can also try in the google playground here and paste the scopes https://developers.google.com/oauthplayground/
The first problem with your request is that you're passing in the exercise is an integer field, but the documentation says that you need to pass in one of an enumerated set of strings (I don't know why it says int there, but if you click the "Accepted values", you can see those definitely aren't integers...).
However, it would be easier if you didn't specify the fields at all. Simply specify the data type name:
// ...
"dataType": {
"name": "com.google.activity.exercise",
}
// ...
and the correct fields will be filled in on the backend and returned to you.
My bot is registered in several groups and receives data from them.
when receive the the json code.
{
"update_id": 753984481,
"message": {
"message_id": 158011,
"from": {
"id": 212105015,
"first_name": "\u0634\u0631\u06a9\u062a \u0635\u0628\u0627 \u0645\u0647\u0631 \u0633\u06cc\u0631\u0627\u0641"
},
"chat": {
"id": -196924840,
"title": "\u067e\u0631 \u067e\u0631\u0648\u0627\u0632 \u0635\u0628\u0627 \u0645\u0647\u0631 \u0633\u06cc\u0631\u0627\u0641",
"type": "group",
"all_members_are_administrators": true
},
"date": 1500091212,
"photo": [
{
"file_id": "AgADBAAD9qkxG98UMFNewex76YKoYAr-vBkABEvcu9cjuXx1WCQDAAEC",
"file_size": 1168,
"width": 67,
"height": 90
}
]
}
}
How do I know which message is sent from (Groups)?
as you can see, in your json message.chat.type is equal to "group", when your bot receives a message from a channel, its type value will be equal to "channel".
other difference between channels and groups is that the message.chat.id of channels are bigger (13 digit numbers)
I listed the messages in My App successfully. Now I need to paginate the messages
(about 8 in every page). I followed the doc of google but it only gives a pageToken but how to make it work?
If you list messages, and there still are more results to fetch, the response will contain a nextPageToken.
Request
userId = me
maxResults = 8
GET https://www.googleapis.com/gmail/v1/users/me/messages?maxResults=8&access_token={YOUR_API_KEY}
Response
{
"messages": [
{
"id": "151596a5055b9412",
"threadId": "151596a5055b9412"
},
{
"id": "1515915d0fcfd685",
"threadId": "1515915d0fcfd685"
},
{
"id": "15158e6826ed7587",
"threadId": "15158e6826ed7587"
},
{
"id": "15158e0e37572671",
"threadId": "15158e0e37572671"
},
{
"id": "151586443b5b309a",
"threadId": "151586443b5b309a"
},
{
"id": "15157c4b11732c5c",
"threadId": "1510f004b81a9de2"
},
{
"id": "151576512d37c9ec",
"threadId": "1515765122918d37"
},
{
"id": "1515765122918d37",
"threadId": "1515765122918d37"
}
],
"nextPageToken": "01770178536732383613", // Here it is!
"resultSizeEstimate": 26
}
Just include this value as the pageToken in the next request.
Request
userId = me
maxResults = 8
pageToken = 01770178536732383613
GET https://www.googleapis.com/gmail/v1/users/me/messages?maxResults=8&pageToken=01770178536732383613&access_token={YOUR_API_KEY}
Response
{
"messages": [
{
"id": "1515762549119366",
"threadId": "1513f096ab90fdab"
},
{
"id": "15157616d03a66a1",
"threadId": "1513f096ab90fdab"
},
{
"id": "151575f958ac69e8",
"threadId": "1513338849950602"
},
{
"id": "1515756737710843",
"threadId": "1515756737710843"
},
{
"id": "1515756735412b45",
"threadId": "1515756735412b45"
},
{
"id": "1515756710eed602",
"threadId": "1515756710eed602"
},
{
"id": "15157567089a24b0",
"threadId": "15157567089a24b0"
},
{
"id": "151574a87fefe71d",
"threadId": "151336e890f46a2c"
}
],
"nextPageToken": "13534757816909071635",
"resultSizeEstimate": 27
}
When there is no nextPageToken in the response you have fetched every result there is.
The solution that I found is storing in an array of the front all the tokens that it brings you to be able to go forward and backward between pages, being the index of the array the pages, if it does not bring you the nextPageToken it is because it is the last one, then you pass the current token (of the page where you are) as parameter, and it brings you the list of emails of that page.
Have built an video app that publish user actions towards Facebook.
In this app i have implemented an "Favorite" function that i have hooked up towards a basic open graph action "og.like"
I want to be able to display video's that user liked and apply my own styling to that.
Basically i want to display "Title" "Url" & "Image"
So i use the PHP-SDK towards authored user with active access token and execute
$response = $facebook->api(
'me/og.likes',
'GET'
);
// handle the response
How do i now sort out my correct fields and display them ?
Am not hardcore at either php or javascript but will be able to sort this out if i just can get a little push in the right direction. Like just showing the raw data
Update
Finally a little progress, adding
print_r ($response);
Will write out the raw data, Now i know that am on the right way.
Array returned
{
"data": [
{
"id": "123",
"from": {
"name": "Mathias",
"id": "APP_ID"
},
"start_time": "X",
"end_time": "X",
"publish_time": "X",
"application": {
"name": "APP_Name",
"namespace": "",
"id": "321"
},
"data": {
"object": {
"id": "139",
"url": "Url to like",
"type": "video.tv_show",
"title": "title"
}
},
"type": "og.likes",
"no_feed_story": false,
"likes": {
"count": 0,
"can_like": true,
"user_likes": false
},
"comments": {
"count": 0,
"can_comment": true,
"comment_order": "chronological"
}
},
And then the next..
From every app "like" i would like to display Url ,Title & Image
From what i understand so far my main problem is that this is nested arrays, Did try with single level arrays and there i did manage to display correct data just by
echo $response[name];
So how do i digg in and loop this around, All tips are welcome,
{
"id": "139",
"url": "url",
"type": "video.tv_show",
"title": "titke",
"image": [
{
"url": "image_URL",
"secure_url": "image_URL",
"type": "image/jpg",
"width": 1024,
"height": 576
}
Here's an example:
<?php foreach ( $response['data'] as $data ): ?>
<?php $Object = $data['data']['object']; ?>
<?php echo $Object['title']; ?><br />
<?php endforeach; ?>