Authorize.Net API limit 1000 results. How to overcome it? - php

On my website we keep transactions as pending and capture them when we ship (often we need to change amounts/cancel and it makes it easier accounting wise to work like that).
When we are ready to ship, we match all specified orders (with specified order status) with the invoice#/order id in authorize.
the issue is that the authorize.net API only allows for 1000 transaction limit so when using their GetUnsettledTransactionListRequest function it is missing transactions that are unsettled passed that amount.
I am able to set the paging limit to 1000 and I can also set the offset to 1000 (or 999 not sure which yet) so I think what I need to do is something like check if the array storing the results size is 1000 and if so get the next 1000 results to store in the array. But about about the next loop do I have to manually say 3000, 4000, 5000 (we don't have that many transactions but still).
here is my current code:
function getUnsettledTransactionList()
{
//get orders that are in the exp status
$orders_pending_query = tep_db_query("select orders_id as invoice_number from " . TABLE_ORDERS . " where orders_status = '15' order by invoice_number");
$orders_pending = array();
while ($row = mysqli_fetch_array($orders_pending_query, MYSQLI_ASSOC)) {
$orders_pending[] = $row;
}
/* Create a merchantAuthenticationType object with authentication details
retrieved from the constants file */
$merchantAuthentication = new AnetAPI\MerchantAuthenticationType();
$merchantAuthentication->setName(\SampleCodeConstants::MERCHANT_LOGIN_ID);
$merchantAuthentication->setTransactionKey(\SampleCodeConstants::MERCHANT_TRANSACTION_KEY);
// Set the transaction's refId
$refId = 'ref' . time();
$request = new AnetAPI\GetUnsettledTransactionListRequest();
$request->setMerchantAuthentication($merchantAuthentication);
$controller = new AnetController\GetUnsettledTransactionListController($request);
$response = $controller->executeWithApiResponse(\net\authorize\api\constants\ANetEnvironment::PRODUCTION);
$transactionArray = array();
$resulttrans = array();
if (($response != null) && ($response->getMessages()->getResultCode() == "Ok")) {
if (null != $response->getTransactions()) {
foreach ($response->getTransactions() as $tx) {
$transactionArray[] = array(
'transaction_id' => $tx->getTransId(),
'invoice_number' => $tx->getInvoiceNumber()
);
// echo "TransactionID: " . $tx->getTransId() . "order ID:" . $tx->getInvoiceNumber() . "Amount:" . $tx->getSettleAmount() . "<br/>";
}
//match the array column by invoice mumber to find all the transaction ids for cards to capture
$invoiceNumbers = array_column($orders_pending, "invoice_number");
$result = array_filter($transactionArray, function ($x) use ($invoiceNumbers) {
return in_array($x["invoice_number"], $invoiceNumbers);
});
$resulttrans = array_column($result, "transaction_id");
print_r($resulttrans);
} else {
echo "No unsettled transactions for the merchant." . "\n";
}
} else {
echo "ERROR : Invalid response\n";
$errorMessages = $response->getMessages()->getMessage();
echo "Response : " . $errorMessages[0]->getCode() . " " . $errorMessages[0]->getText() . "\n";
}
return $resulttrans;
}

GetUnsettledTransactionListRequest offers the ability to page the results. So you after you process the first 1,000 results you can request the next 1,000 results.
I don't use the Authnet SDK but it looks like you can use AnetAPI\PagingType() to handle the paging for you:
$pagenum = 1;
$transactionArray = array();
do {
// ...code truncated...
$request = new AnetAPI\GetUnsettledTransactionListRequest();
$request->setMerchantAuthentication($merchantAuthentication);
// Paging code
$paging = new AnetAPI\PagingType();
$paging->setLimit("1000");
$paging->setOffset($pagenum);
$request->setPaging($paging);
$controller = new AnetController\GetUnsettledTransactionListController($request);
// ...code truncated...
$numResults = (int) $response->getTotalNumInResultSet();
// ...code truncated...
$pagenum++;
} while ($numResults === 1000);
The JSON would resemble this:
{
"getUnsettledTransactionListRequest": {
"merchantAuthentication": {
"name": "",
"transactionKey": ""
},
"paging": {
"limit": "1000",
"offset": "1"
}
}
}

Related

Why is do while loop creating/overwriting 2 separate arrays

I have the following code that is overwriting my array on the second pass through of the while loop.
Here is my code:
<?php
require '../vendor/autoload.php';
require_once 'constants/constants.php';
use net\authorize\api\contract\v1 as AnetAPI;
use net\authorize\api\controller as AnetController;
require('includes/application_top.php');
define("AUTHORIZENET_LOG_FILE", "phplog");
function getUnsettledTransactionList()
{
//get orders that are in the exp status
$orders_pending_query = tep_db_query("select orders_id as invoice_number from " . TABLE_ORDERS . " where orders_status = '14' order by invoice_number");
$orders_pending = array();
while ($row = mysqli_fetch_array($orders_pending_query, MYSQLI_ASSOC)) {
$orders_pending[] = $row;
}
/* Create a merchantAuthenticationType object with authentication details
retrieved from the constants file */
$merchantAuthentication = new AnetAPI\MerchantAuthenticationType();
$merchantAuthentication->setName(\SampleCodeConstants::MERCHANT_LOGIN_ID);
$merchantAuthentication->setTransactionKey(\SampleCodeConstants::MERCHANT_TRANSACTION_KEY);
// Set the transaction's refId
$refId = 'ref' . time();
$pagenum = 1;
do {
$request = new AnetAPI\GetUnsettledTransactionListRequest();
$request->setMerchantAuthentication($merchantAuthentication);
$paging = new AnetAPI\PagingType;
$paging->setLimit("1000");
$paging->setOffset($pagenum);
$request->setPaging($paging);
$controller = new AnetController\GetUnsettledTransactionListController($request);
$response = $controller->executeWithApiResponse(\net\authorize\api\constants\ANetEnvironment::PRODUCTION);
$transactionArray = array();
$resulttrans = array();
if (($response != null) && ($response->getMessages()->getResultCode() == "Ok")) {
if (null != $response->getTransactions()) {
foreach ($response->getTransactions() as $tx) {
$transactionArray[] = array(
'transaction_id' => $tx->getTransId(),
'invoice_number' => $tx->getInvoiceNumber()
);
// echo "TransactionID: " . $tx->getTransId() . "order ID:" . $tx->getInvoiceNumber() . "Amount:" . $tx->getSettleAmount() . "<br/>";
}
$invoiceNumbers = array_column($orders_pending, "invoice_number");
$result = array_filter($transactionArray, function ($x) use ($invoiceNumbers) {
return in_array($x["invoice_number"], $invoiceNumbers);
});
$resulttrans = array_column($result, "transaction_id");
} else {
echo "No unsettled transactions for the merchant." . "\n";
}
} else {
echo "ERROR : Invalid response\n";
$errorMessages = $response->getMessages()->getMessage();
echo "Response : " . $errorMessages[0]->getCode() . " " . $errorMessages[0]->getText() . "\n";
}
$numResults = (int) $response->getTotalNumInResultSet();
$pagenum++;
print_r($resulttrans);
} while ($numResults === 1000);
return $resulttrans;
}
getUnsettledTransactionList();
?>
the print_r($resulttrans); is actually printing 2 separate arrays, instead of my desired 1 array.
If I move the print_r($resulttrans) to after the while loop, I am only seeing the second array, meaning the first array was overwritten. I am not seeing where this is happening though as to me it seems like all results should be added onto the array.
Your code is supposed to work as you described because you are reassigning the array variable in your loop like this
$resulttrans = array_column($result, "transaction_id");
If you need to get all the resulting values in the same array you need to append it to the array. you can do that by merging the new result into your array variable like this
$resulttrans = array_merge($resulttrans, array_column($result, "transaction_id"));

Pagination with google ads api in php

I am trying to do the pagination with google-ads-php at the bottom of my page in php, so I get through my ads, like PREVIOUS 1,2,3 NEXT
So this is my code:
public function runExample(GoogleAdsClient $googleAdsClient, int $customerId)
{
$googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient();
// Creates a query that retrieves all ads.
$query = "SELECT campaign.name, ad_group.name, "
. "ad_group_ad.ad.responsive_display_ad.marketing_images, "
. "ad_group_ad.ad.app_ad.images, ad_group_ad.ad.app_ad.youtube_videos, "
. "ad_group_ad.ad.responsive_display_ad.youtube_videos, ad_group_ad.ad.local_ad.videos, "
. "ad_group_ad.ad.video_responsive_ad.videos, ad_group_ad.ad.video_ad.media_file, "
. "ad_group_ad.ad.app_engagement_ad.images, ad_group_ad.ad.app_engagement_ad.videos, "
. "ad_group_ad.ad.display_upload_ad.media_bundle, ad_group_ad.ad.gmail_ad.product_images, "
. "ad_group_ad.ad.gmail_ad.product_videos, ad_group_ad.ad.gmail_ad.teaser.logo_image, "
. "ad_group_ad.ad.image_ad.image_url, ad_group_ad.ad.legacy_responsive_display_ad.square_marketing_image, "
. "ad_group_ad.ad.local_ad.marketing_images, ad_group_ad.ad.responsive_display_ad.logo_images, "
. "ad_group_ad.ad.responsive_display_ad.square_logo_images, "
. "ad_group_ad.ad.responsive_display_ad.square_marketing_images, "
. "ad_group_ad.ad.responsive_display_ad.youtube_videos, "
. "metrics.impressions, campaign.campaign_budget, campaign.status, "
. "campaign.start_date, campaign.end_date, metrics.all_conversions, "
. "metrics.average_cost, ad_group_ad.ad.type, ad_group_ad.ad.id, "
. "campaign.campaign_budget, metrics.cost_micros, ad_group_ad.status, metrics.impressions "
. "FROM ad_group_ad "
. "WHERE segments.date >= '{$this->from}' AND segments.date <= '{$this->to}' "
. "ORDER BY campaign.name ASC";
// Issues a search stream request.
/** #var GoogleAdsServerStreamDecorator $stream */
$stream = $googleAdsServiceClient->search($customerId, $query, ['pageSize' => 10]);
$ads = [];
foreach ($stream->iterateAllElements() as $googleAdsRow) {
dump($googleAdsRow->serializeToJsonString());
/** #var GoogleAdsRow $googleAdsRow */
$ads[] = json_decode($googleAdsRow->serializeToJsonString(), true);
}
As you see the pageSize is set to 10, so it will be 23 pages, because I have 230 ads.
How can I do the pagination, now the $stream returns all ads in one response. How can return only 10 ads, and then when user click for example second page button, it will return the next 10 ads, and so on?
Thanks in advance!
here is how it can be done in Python though -
import sys
from google.ads.googleads.client import GoogleAdsClient
from google.ads.googleads.errors import GoogleAdsException
client = GoogleAdsClient.load_from_storage()
ga_service = client.get_service("GoogleAdsService")
search_request = client.get_type("SearchGoogleAdsRequest")
try:
search_request.query = QUERY_STRING
search_request.customer_id = CUSOMER_ID
search_request.page_size = PAGE_SIZE
df = pd.DataFrame()
while True:
response = ga_service.search(search_request)
dictobj = MessageToDict(response._pb)
df = df.append(pd.json_normalize(dictobj,record_path=['results']))
if response.next_page_token == '':
break
else:
search_request.page_token = response.next_page_token
except GoogleAdsException as ex:
print(ex)
To achieve pagination, you may slightly change the display.
Say, if you want to display 10 records per page, so for the 2nd page, it will be
records 11 to 20.
So , to display page 2, you may revising the part :
foreach ($stream->iterateAllElements() as $googleAdsRow) {
dump($googleAdsRow->serializeToJsonString());
/** #var GoogleAdsRow $googleAdsRow */
$ads[] = json_decode($googleAdsRow->serializeToJsonString(), true);
}
to
$index=0;
foreach ($stream->iterateAllElements() as $googleAdsRow) {
$index++;
if ($index >=11 && $index <=20) {
dump($googleAdsRow->serializeToJsonString());
/** #var GoogleAdsRow $googleAdsRow */
$ads[] = json_decode($googleAdsRow->serializeToJsonString(), true);
}
}
Please see whether the above works, and if so, you may amend the codes to show the data according to page number
Google Ads API provides native pagination that as of now is not very well documented. However, you can only paginate by "next" and "previous" pages.
Here is a working example on keywords since that's the use case most people probably get stuck upon.
$googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient();
$query =
'SELECT ad_group.id, '
. 'ad_group_criterion.type, '
. 'ad_group_criterion.criterion_id, '
. 'ad_group_criterion.keyword.text, '
. 'ad_group_criterion.keyword.match_type '
. 'FROM ad_group_criterion '
. 'WHERE ad_group_criterion.type = KEYWORD';
// Get the stream
$stream = $googleAdsServiceClient->search($customerId, $query, ['pageSize' => 1000]);
// Get the first page
$page = $stream->getPage();
foreach ($page->getIterator() as $googleAdsRow) {
// Iterating over the first page of 1000 keywords.
}
// Get the next page token
$nextPageToken = $page->getNextPageToken();
// Get the second page
$page = $stream->getPage();
$stream = $googleAdsServiceClient->search($customerId, $query, ['pageSize' => 1000, 'pageToken' => $nextPageToken]);
foreach ($page->getIterator() as $googleAdsRow) {
// Iterating over the second page of 1000 keywords.
}
Note that you have to use search() and not searchStream() and iterator of the actual page instead of iterateAllElements()

Getting full list of contacts from Hubspot API

Hubspot's API allows you retrieve a list of contacts, however it only allows a max of 100 per call.
I do that with this call:
$contacts_batch1 = $contacts->get_all_contacts(array( 'count' => '100'));
And then if I want to get the next 100 I do this:
$offset1 = $contacts_batch1->{'vid-offset'};
$contacts_batch2 = $contacts->get_all_contacts(array('count' => '100', 'vidOffset'=>$offset1));
I am trying to get all the contacts without having to create a new variable each time I want the next 100. My first question would be how would I go about getting the vid-offset of the last set, and then how would I put that as a parameter into the next variable automatically.
Here's an example of getting all contacts into one array using HubSpot's API.
<?php
require "haPiHP/class.contacts.php";
require "haPiHP/class.exception.php";
define("HUBSPOT_API_KEY", "<YOUR API KEY HERE>");
$contacts = new HubSpot_Contacts(HUBSPOT_API_KEY);
$all_contacts = array();
do
{
$params = array("count" => 100);
if (isset($vidOffset))
{
$params["vidOffset"] = $vidOffset;
}
echo "count=" . $params["count"] . (isset($params["vidOffset"]) ? ", vidOffset=" . $params["vidOffset"] : "") . "\n";
$some_contacts = $contacts->get_all_contacts($params);
if ($some_contacts !== NULL)
{
$all_contacts = array_merge($all_contacts, $some_contacts->contacts);
}
else
{
break;
}
$vidOffset = $some_contacts->{'vid-offset'};
} while ($some_contacts->{'has-more'});
echo "Received " . count($all_contacts) . " contacts.\n";
?>

How to get attribute values of an object stored in $_SESSION?

I have this code that store a "student" object in $_SESSION:
if(isset($_POST["name"]) && isset($_POST["note"]) && isset($_POST["year"]))
{
$nom = $_POST["name"];
$note = $_POST["note"];
$session = $_POST["year"];
$vec = array("name" => $name, "note" => $note, "year" => $year);
$_SESSION["students"][] = $vec;
echo "The student has been added.<br><br>";
}
Then I have this code in another page:
function calculateAverage()
{
$av = 0;
$count = 0;
foreach($_SESSION['students'] as $student)
{
$av = $av + $student["note"];
$count = $count + 1;
}
return $av / $count;
}
function bestNote()
{
//$best = array_search(max())
return $best;
}
function worstNote()
{
$worst = min(array_search(["note"], $_SESSION['students']));
return $worst;
}
if(isset($_SESSION['students']))
{
echo "The average note of the group is = " . calculateAverage() . "\n";
echo "The one with the best note is " . bestNote()["name"] . " is " . hauteNote()["note"] . " points.\n";
echo "The one with the worst note is " . worstNote()["name"] . " with " . basseNote()["note"] . " points.\n";
}
As you can see, it is not finished. What I want to do is to be able to get the note of a student that is stored in $_SESSION["students"]. How can I do this?
Thanks for answers.
you can access the stored values within a nested array like so:
$studentNote = $_SESSION["students"][YourActiveStudent]["note"];
However, you are currently not adding but overwriting data. Use array_push() to add data to an array (your students).
And when adding a student to the array, make sure to give it a name to make it associative so you can simply "call a student":
$_SESSION["students"][$nom] = $vec;
this way, if the $nom was "Max", you could say
$_SESSION["students"]["Max"]["note"]
to get Max's note (BTW, I assume you are talking about grades or marks, rather than notes?)

PHP while loop within a while loop works once

I have two queries sent to a database bring back posts (op_ideas 16 cols) followed by another which holds the votes per post (op_idea_vote cols 4) with matching idea_id's
Example of Data:
Query: op_ideas:
[{"idea_id":"2211","author_id":"100000", "date":"2012-09-06
10:02:28","idea_title":"Another test","4" etc etc
Query: op_idea_votes:
idea_id = 2211, agree=3, disagree=1, abstain=0
The code below ought to look at op_ideas, and then cycle over op_ideas_vote until it finds a match under 'idea_id'. Then it goes to the next record under op_ideas, and again using that idea_id search for it within the op_idea_vote list, find a match, and add it to the array.
This works for only the first record, not for the other three. I am testing, so I have 3 rows in each that match idea_id with different results in the op_idea_vote.
$votes = mysql_query($commentVotes);
$result = mysql_query($gl_query);
while ($gce_result = mysql_fetch_array($result)) {
$voteid = $gce_result['idea_id'];
while($allvotes= mysql_fetch_array($votes)) {
if($voteid = $allvotes['idea_id'])
{
//echo $voteid . " main idea and the votes: " . $allvotes;
$gce_result["agree"] = $allvotes['agree'];
$gce_result["disagree"] = $allvotes['disagree'];
$gce_result["abstain"] = $allvotes['obstain'];
}
else
{
$gce_result["agree"] = 0;
$gce_result["disagree"] = 0;
$gce_result["abstain"] = 0;
}
//print_r($gce_result);
}
$data_result[] = $gce_result;
}
echo json_encode($data_result);
If I use print_f(&gce_result) it works fine in phpfiddle. But when i use the code above, it works for the first record, but it's complete missing the second two. It seems to be missing the second while, as it does not even give me the 0 0 0 results.
Query for op_ideas:
$gl_query = "SELECT DISTINCT * FROM heroku_056eb661631f253.op_ideas INNER JOIN op_organs ORDER BY date ASC;";
if (!mysql_query($gl_query)) {
die('Error: ' . $gl_query . " " . mysql_error());
}
$result = mysql_query($gl_query);
Query For op_idea_vote :
$commentVotes = "SELECT v.idea_id, COUNT(v.agree = 1 or null) as agree, COUNT(v.disagree = 1 or null) as disagree, COUNT(v.obstain = 1 or null) as obstain FROM op_idea_vote v GROUP BY v.idea_id";
if (!mysql_query($commentVotes)) {
die('Error: ' . $commentVotes . " " . mysql_error());
}
$votes = mysql_query($commentVotes);
You can scan a resource only once.
So the inner while will be run only one time.
use == instead of = for checking condition of if & while
in the while loop ,you have to assign the value of $allvotes ,but you never assigned,
while ($gce_result == mysql_fetch_array($result)) {
$voteid = $gce_result['idea_id'];
while($allvotes== mysql_fetch_array($votes)) {
if($voteid == $allvotes['idea_id'])
{
//echo $voteid . " main idea and the votes: " . $allvotes;
$gce_result["agree"] = $allvotes['agree'];
$gce_result["disagree"] = $allvotes['disagree'];
$gce_result["abstain"] = $allvotes['obstain'];
}
else
{
$gce_result["agree"] = 0;
$gce_result["disagree"] = 0;
$gce_result["abstain"] = 0;
}
$data_result[] = $gce_result;
}
}
Your problem is trying to scan over the $votes result more than once.
You should store the result of that query first.
Eg.
while ($vote = mysql_fetch_array($votes)) {
$allvotes['idea_id'] = $vote;
}
while ($gce_result = mysql_fetch_array($result)) {
$voteid = $gce_result['idea_id'];
if (array_key_exists[$voteid, $allvotes]) {
//assign results
} else {
//default
}
}
Another option would be to do the query with a join, so you can do everything in one query. Then just loop over that result.

Categories