Bad Request in QuickBooks API production, works in sandbox - php

Today I tried to switch over development code to a limited-release production environment and immediately ran into issues.
The QuickBooks Accounting API works fine, however when it comes to payments I am able to accept them in sandbox mode but production returns "400 Bad Request"
I'm using my production API keys, I'm accessing the production API endpoints (api.intuit.com as opposed to sandbox.api.intuit.com), and I know the query I'm sending should be fine since it works in sandbox mode (unique Request-Id, amount, token, and currency provided in request body, proper content-type header, etc)
I also know my API key is fine since it works with the QuickBooks Accounting API. I am able to create a customer and create an invoice, but when I attempt to collect payment on the invoice it throws up on me.
What could be going wrong? I can post any code snippets or errors required. I'm using Laravel 5.2 with a phpOauthLib wrapper written by lusitanian.
The code I'm using:
$oauth = OAuth::consumer('QuickBooks');
$oauth_token = unserialize(file_get_contents(storage_path("tokens/oauth.token")));
$storage = $oauth->getStorage();
$storage->storeAccessToken('QuickBooks', $oauth_token['access_token']);
$member = Member::find(Request::get('id'));
$query = urlencode("select * from customer where displayname = '" . $member->first_name . " " . $member->last_name . "'");
$result = json_decode($oauth->request("/v3/company/" . $oauth_token['company_id'] . "/query?query=" . $query, "GET"));
$donor = null;
if( !property_exists( $result->QueryResponse, 'Customer' ) ) {
$donor = $oauth->request("/v3/company/" . $oauth_token['company_id'] . "/customer", "POST", json_encode([
'DisplayName' => $member->first_name . " " . $member->last_name
]), array('Content-Type' => 'application/json'));
} else {
$donor = $result->QueryResponse->Customer[0];
}
$pledge = $oauth->request("/v3/company/" . $oauth_token['company_id'] . "/invoice", "POST", json_encode([
'Line' => [
[
'Amount' => '25.00',
'DetailType' => 'SalesItemLineDetail',
'SalesItemLineDetail' => [
'ItemRef' => [
'value' => '21'
]
]
]
],
'CustomerRef' => [
'value' => $donor->Id
]
]), array('Content-Type' => 'application/json'));
$pledge = json_decode( $pledge )->Invoice;
$guid = GUID();
/************** THIS IS THE LINE THAT FAILS: ***************/
$capture = $oauth->request("https://api.intuit.com/quickbooks/v4/payments/charges", "POST", json_encode([
'amount' => $amount,
'token' => Request::get('token'),
'currency' => "USD"
]), ['Content-Type' => 'application/json', 'Request-Id' => $guid]);
$capture = json_decode( $capture );
$payment = $oauth->request("/v3/company/" . $oauth_token['company_id'] . "/payment", "POST", json_encode([
'CustomerRef' => [
'value' => $donor->Id,
'name' => $donor->DisplayName
],
'TotalAmt' => $capture->amount,
'Line' => [
[
'Amount' => $capture->amount,
'LinkedTxn' => [
[
'TxnId' => $pledge->Id,
'TxnType' => 'Invoice'
]
]
]
]
]), ['Content-Type' => 'application/json']);
Again, this code worked perfectly with the sandbox API, but now it fails. Tonight I'll see what I can do to get the HTTP transaction (request and response)

Related

Using Guzzle to retrieve API via (POST), unable to pass JSON parameters for query

I have created an API request, it connects to the server fine and passes basic authentication. The error I receive is
"Uncaught GuzzleHttp\Exception\ClientException: Client error: POST http://my.api.com/dest/addr resulted in a 400 Bad Request response:
{"code":400,"result":"Missing customer id"
I have tried many ways to pass the query parameters and none of them have worked. I checked to make sure the JSON being output was formatted correctly. I have followed the directions in the Guzzle documentation. I have also tried every solution I could find in the forums and none of them gave me a different response. I am not sure what I am missing, hoping someone can see what the problem might be... I have pasted what I feel is the code that best follows the Guzzle documentation below. Thanks.
<?php
require __DIR__.'/../vendor/autoload.php';
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
Use GuzzleHttp\Psr7;
try {
$client = new Client(['base_uri' => 'http://my.api.com/']);
$credentials = base64_encode('12345');
$request_param = [
'json' => [
'customer' => [
'name' => 'Mary',
'id' => 111
],
'products' => [
[
'product_id' => 2,
'qty' => 3
]
]
]
];
$request_data = json_encode($request_param, true);
$headers = ['headers' => [
'Content-Type' => 'application/json',
'Authorization' => 'Basic ' . $credentials
]];
$response = $client->request('POST', 'dest/addr', $headers, $request_data);
echo $response->getStatusCode();
echo "<br />";
echo $response->getHeader('content-type')[0];
echo "<br />";
echo "<pre />";
echo $response->getBody()->getContents();
}
catch(\GuzzleHttp\Exception\ClientException $e) {
echo $e->getCode(). '<hr />';
echo $e->getMessage();
}
//Server Exception
catch(\GuzzleHttp\Exception\ServerException $e) {
echo $e->getCode(). '<hr />';
echo $e->getMessage();
}
if your my.api.com can accept json request(not form params json=json_string&something=value in post body).you could try like this
$json_data = [
'customer' => [
'name' => 'Mary',
'id' => 111
],
'products' => [
[
'product_id' => 2,
'qty' => 3
]
]
];
$headers = [
'Authorization' => 'Basic ' . $credentials
];
$response = $client->request('POST', 'dest/addr', [
'json' => $json_data,
'headers' => $headers
]);
Have you tried using the json request option like you did the headers request option? The documentation shows an example like so:
An easy way to upload JSON data and set the appropriate header is using the json request option:
$r = $client->request('PUT', 'http://httpbin.org/put', [
'json' => ['foo' => 'bar']
]);
The syntax is very similar to the headers option. At the moment, your code is trying to send the raw json. Documentation for json request option found at https://docs.guzzlephp.org/en/stable/quickstart.html#uploading-data

How to verify paypal payment in omnipay

Please help me.
I'm trying to integrate omnipay/paypal to process payments. Everythings works great until i need to verify my payment. How i register payment:
$gateway = Omnipay::create('PayPal_Rest');
$gateway->initialize(array(
'clientId' => $config['paypal_client_id'],
'secret' => $config['paypal_secret'],
'testMode' => true, // Or false when you are ready for live transactions
));
$response = $gateway->purchase(array(
'amount' => $data['price_sum'],
'currency' => 'PLN',
'description' => 'NNŻ: ' . $description . $data['order_id'],
'transactionId' => $data['order_id'],
'returnUrl' => $app_url . 'basket/pay?token=' . $data['token'],
'cancelUrl' => $app_url . 'basket/pay?token=' . $data['token'],
'notifyUrl' => $app_url . 'payment/verify-basket-paypal'
))->send();
//print_r( get_class_methods( $response ) );
// Process response
if ($response->isSuccessful()) {
$transaction_id = $response->getTransactionReference();
Kernel::log('paypal.log' , $transaction_id);
return ['url' => $response->getRedirectUrl(), 'paypal' => true ];
} elseif ($response->isRedirect()) {
// Redirect to offsite payment gateway
$response->redirect();
} else {
// Payment failed
echo $response->getMessage();
}
On html page i've added button with url ($response->getRedirectUrl()) after that, i'm redirected to same page, where i want to verify, if payment is booked right.
Url has additional GET params:
/payment/verify-basket-paypal?paymentId=PAYID-L37QMOY42K85628E4550351P&token=EC-3SM080895T541133U&PayerID=Y3DZ53CGGL47N
I've try something like this:
$gateway = Omnipay::create('PayPal_Rest');
$gateway->initialize(array(
'clientId' => $config['paypal_client_id'],
'secret' => $config['paypal_secret'],
'testMode' => true, // Or false when you are ready for live transactions
));
$transaction = $gateway->fetchTransaction([
'paymentId' => $request->get['paymentId'],
'token' => $request->get['token'],
'PayerID' => $request->get['PayerID']
]);
but it isn't working. Please help, how can i verify payment. There are no additional documentation on their page. Thanks a lot.

Payment API Returns AuthenticationFailed Error

I am integrating payments to my app and using PHP-Payments-SDK from Intuit
When creating echeck debit by the following code.
public function echeck(Request $request){
$this->refreshToken();
$record = QuickbookModel::first();
$client = new PaymentClient([
'access_token' => $record->accessToken,
'environment' => "sandbox" // or 'environment' => "production"
]);
$array = [
"bankAccount" => [
"phone" => $request->phone,
"routingNumber" => $request->routingNumber,
"name" => $request->name,
"accountType" => $request->accountType,
"accountNumber" => $request->accountNumber
],
"description" => "Easyfi Testing",
"paymentMode" => "WEB",
"amount" => "5.55",
];
$bank = ECheckOperations::buildFrom($array);
$response = $client->debit($bank);
if($response->failed()){
$code = $response->getStatusCode();
$errorMessage = $response->getBody();
return json_encode(["status" => $code, "message" => $errorMessage]);
}else{
$responseCharge = $response->getBody();
//Get the Id of the charge request
$id = $responseCharge->id;
//Get the Status of the charge request
$status = $responseCharge->status;
return json_encode(["status" => $status, "message" => $responseCharge, "data" => $id]);
}
}
I always getting following error even everything is up to mark including Access Token
{status: 401, message: "{"code":"AuthenticationFailed","type":"INPUT","message":null,"detail":null,"moreInfo":null}"}

Can't get an unenrolled 3d-secure card authenticaited (secrure trading)

I am running a PHP 7.3, running on apache server. I used composer to get this library:
https://github.com/SecureTrading/PHP-API
For the code provided, I am now using the test site reference. I already managed to use for regular transitions. I now started managing 3D secure transactions, with the test MAESTRO card provided by secure trading here: https://docs.securetrading.com/document/testing/. the one designed not to demand 3D auth - that is 5000000000000421
The code provided next, will sum up the way I think thought this should work: I start by creating AUTH request, get error 30004, using CACHETOKENISE request to get a token, run THREEDQUERY to figure out if I need a full auth sceme on this card, get N as an answer, and run another AUTH request, this time with the transactionreference.
I am providing a version of the code I am testing (obviously, username, password and site reference name was removed to protect my privacy, but the code otherwise is the same)
<?php
$configData = array(
'username' => 'api#gigsberg.com',
'password' => 'xq!Kq$j4',
);
$site_refrance = 'test_gigsberg74319';
?>
<?php
$configData = array(
'username' => '*****',
'password' => '*****',
);
$site_refrance = '*****';
if (!($autoload = realpath(__DIR__ . '/vendor/autoload.php'))) {
throw new \Exception('Composer autoloader file could not be found.');
}
require_once($autoload);
$api = \Securetrading\api($configData);
$requestData = array(
'sitereference' => $site_refrance,
'requesttypedescription' => 'AUTH',
'accounttypedescription' => 'ECOM',
'currencyiso3a' => 'GBP',
'mainamount' => '1000',
'pan' => '5000000000000421',
'expirymonth' => '12',
'expiryyear' => '2030',
'securitycode' => '123',
);
echo '<pre>';
print_r($requestData);
$response = $api->process($requestData)->toArray();
print_r( $response['responses'] ); // $response['responses'][0]['errorcode'] == 30004
echo "\n--------------------------------------\n";
$transactionreference = $response['responses'][0]['transactionreference'];
$requestData = array(
'sitereference' => $site_refrance,
'expirymonth' => '12',
'expiryyear' => '2030',
'requesttypedescriptions' => array('CACHETOKENISE'),
'securitycode' => '123',
'orderreference' => $transactionreference,
'pan' => '5000000000000421'
);
print_r($requestData);
$response = $api->process($requestData)->toArray();
echo "\n--------------------------------------\n";
$cachetoken = $response['responses'][0]['cachetoken'];
$requestData = array(
'termurl' => 'https://termurl.com',
'accept' => 'text/html,*/*',
'currencyiso3a' => 'GBP',
'requesttypedescription' => 'THREEDQUERY',
'accounttypedescription' => 'ECOM',
'sitereference' => $site_refrance,
'baseamount' => '1000',
'pan' => '5000000000000421',
'expirymonth' => '12',
'expiryyear' => '2030',
'cachetoken' => $cachetoken,
);
print_r($requestData);
$response = $api->process($requestData)->toArray(); // $response['responses'][0]['enrolled'] == 'N'
/* Copying from the docs here: https://docs.securetrading.com/document/api/security/3-d-secure/
* If the enrolled value returned in the response is “Y”, the customer’s card is enrolled in 3-D secure. Please refer to the following table for enrolled values:
* .
* .
* N - The card is not enrolled in the card issuer’s 3-D Secure scheme. - Perform an AUTH Request, including the transactionreference returned in the THREEDQUERY response.
* .
* .
*/
print_r( $response['responses'] );
echo "\n--------------------------------------\n";
$transactionreference = $response['responses'][0]['transactionreference'];
$requestData = array(
'sitereference' => $site_refrance,
'requesttypedescription' => 'AUTH',
'accounttypedescription' => 'ECOM',
'currencyiso3a' => 'GBP',
'mainamount' => '1000',
'pan' => '5000000000000421',
'expirymonth' => '12',
'expiryyear' => '2030',
'securitycode' => '123',
'transactionreference' => $transactionreference
);
print_r($requestData);
$response = $api->process($requestData)->toArray();
print_r( $response['responses'] ); // Still get $response['responses'][0]['errorcode'] == 30004
I expected it to give me a note that all works well, but I still got error 30004, as if the transactionreference wasn't provided. Any idea what I can do, to fix this code, and prevent this error?
Thanks in advance
Yair
Well, I read the Api tests, and I found my error. On the last request data, instead of
$requestData = array(
.
.
'transactionreference' => $transactionreference
.
.
);
I should use
$requestData = array(
.
.
'parenttransactionreference' => $transactionreference
.
.
);
Anyway, home this helps somone

Google spreadsheet, how to protect Cell?

I create excel file and proteced some field in this file. In Office excel all fine my some cell locked for edit, begin I upload my excel file in google drive with convert - true (Whether to convert this file to the corresponding Google Docs format. (Default: false)) But in google spread sheet I can edit locked cell, how to locked in google spread sheet some cell ?
create excel file
$phpExcel = new \PHPExcel();
$ews = $phpExcel->getSheet(0);
$ews->setTitle(Reports::LIST_AOG);
$ews->getProtection()->setSheet(true);
$ews
->getStyle('E6:F36')
->getProtection()->setLocked(
\PHPExcel_Style_Protection::PROTECTION_UNPROTECTED
);
and now I can edit only E6:F36 cell in my file in computer, then upload in google drive
$insertArray = [
'mimeType' => $fileUpload->getMimeType(),
'uploadType' => 'media',
'data' => file_get_contents($fileUpload),
'convert' => true
];
$service = new \Google_Service_Drive($client->getGoogleClient());
$file = new \Google_Service_Drive_DriveFile();
$file->setTitle($nameFile);
$file->setMimeType($fileUpload->getMimeType());
try {
$createdFile = $service->files->insert($file, $insertArray);
$spreadsheetId = $createdFile->getId();
} catch (\Exception $e) {
$view = $this->view((array)GoogleDriveController::ERROR_OCCURRED . $e->getMessage(), 400);
return $this->handleView($view);
}
I fing for google spreadsheet api bundle asimlqt/php-google-spreadsheet-client but not find how to protected
$serviceRequest = new DefaultServiceRequest($arrayAccessTokenClient['access_token']);
ServiceRequestFactory::setInstance($serviceRequest);
$spreadsheetService = new SpreadsheetService();
$spreadsheet = $spreadsheetService->getSpreadsheetById($spreadsheetId);
$worksheetFeed = $spreadsheet->getWorksheets();
$cellFeed = $worksheet->getCellFeed();
$cellFeed->editCell(1, 1, 'developer');
$cellFeed->editCell(1, 2, 'hors');
$cellFeed->editCell(10, 2, 'sum');
or how to protected cell with asimlqt/php-google-spreadsheet-client ?
and In google spread sheet I can any edit any cell (((( Who knows hot to protected cell in google spreat sheet ?
UPDATE
I read Google Sheets API and try create request, this I have
$arrayAccessTokenClient = json_decode($client->getGoogleClient()->getAccessToken(), true);
$serviceRequest = new DefaultServiceRequest($arrayAccessTokenClient['access_token']);
ServiceRequestFactory::setInstance($serviceRequest);
$spreadsheetService = new SpreadsheetService();
$spreadsheet = $spreadsheetService->getSpreadsheetById($spreadsheetId);
$worksheetFeed = $spreadsheet->getWorksheets();
$worksheet = $worksheetFeed->getByTitle(Reports::LIST_AOG);
$addProtectedRange['addProtectedRange'] = [
'protectedRange' => [
'range' => [
'sheetId' => $worksheet->getGid(),
'startRowIndex' => 3,
'endRowIndex' => 4,
'startColumnIndex' => 0,
'endColumnIndex' => 5,
],
'description' => "Protecting total row",
'warningOnly' => true
]
];
$guzzle = new Client();
$putTeam = $guzzle
->post('https://sheets.googleapis.com/v4/spreadsheets/'.$spreadsheetId.':batchUpdate?key='.$arrayAccessTokenClient['access_token'],
[],
json_encode($addProtectedRange)
)
->send()
->getBody(true);
$answer = json_decode($putTeam, true);
But have
Client error response
[status code] 401
[reason phrase] Unauthorized
[url] https://sheets.googleapis.com/v4/spreadsheets/1M_NFvKMZ7Rzbj9ww86AJRMto1UesIy71840r2sxbD5Y:batchUpdate?key=myAccessToken
Early I have Google Api Clien with access token and I can change cell and update with google spread sheet and work fine but https://sheets.googleapis.com/v4/spreadsheets/'.$spreadsheetId.':batchUpdate?key='.$arrayAccessTokenClient['access_token'],
return 401 and I not understand why and how to correct. Help
I think google spreadsheets allow the editing of protected MSExcel sheets the moment they become Google docs. The rules for MSExcel doesn't always apply to Google Sheets. Reading from Adding Named or Protected Ranges:
The following spreadsheets.batchUpdate request contains two request
objects. The first gives the range A1:E3 the name "Counts". The second
provides a warning-level protection to the range A4:E4. This level
protection still allows cells within the range to be edited, but
prompts a warning prior to making the change.
This thread also shares the same view.
However, if it's a google spreadsheet file you want to protect, check this guide.
I read documentation and
$addProtectedRange['requests'] = [
'addProtectedRange' => [
'protectedRange' => [
'range' => [
'sheetId' => $worksheetGid,
'startRowIndex' => $startRowIndex,
'endRowIndex' => $endRowIndex,
'startColumnIndex' => $startColumnIndex,
'endColumnIndex' => $endColumnIndex,
],
'description' => "Protecting total row",
'warningOnly' => false,
'editors' => [
'users' => [
"shuba.ivan.vikt#gmail.com"
]
]
],
]
];
This file_get_contents variant, because Guzzle not returned json response from google, like this:
{
"protectedRangeId": number,
"range": {
object(GridRange)
},
"namedRangeId": string,
"description": string,
"warningOnly": boolean,
"requestingUserCanEdit": boolean,
"unprotectedRanges": [
{
object(GridRange)
}
],
"editors": {
object(Editors)
},
}
I create new question for this for Guzzle reasponse
This is variant for update protected edit cell
$addProtectedRangeJson = json_encode($addProtectedRange);
$url = 'https://sheets.googleapis.com/v4/spreadsheets/' . $spreadsheetId . ':batchUpdate';
$opts = array('http' =>
array(
'method' => 'POST',
'header' => "Content-type: application/json\r\n" .
"Authorization: Bearer $arrayAccessTokenClient\r\n",
'content' => $addProtectedRangeJson
)
);
$context = stream_context_create($opts);
$response = file_get_contents($url, FALSE, $context);
$answer = json_decode($response, TRUE);
$addProtectedRangeUpdate['requests'] = [
'updateProtectedRange' => [
'protectedRange' => [
'protectedRangeId' => $protectedRangeId,
'warningOnly' => false,
'editors' => [
'users' => [
"shuba.ivan.vikt#gmail.com"
]
]
],
'fields' => "namedRangeId,warningOnly,editors"
]
];
$addProtectedRangeUpdateJson = json_encode($addProtectedRangeUpdate);
$optsUpdate = array('http' =>
array(
'method' => 'POST',
'header' => "Content-type: application/json\r\n" .
"Authorization: Bearer $arrayAccessTokenClient\r\n",
'content' => $addProtectedRangeUpdateJson
)
);
$contextUpdate = stream_context_create($optsUpdate);
$responseUpdate = file_get_contents($url, FALSE, $contextUpdate);
$answerUpdate = json_decode($responseUpdate, TRUE);
and this is Guzzle, but Guzzle without google json response this is bad I wait maybe who know what is it problem, because standard php function file_get_contents work very well and I have json google response. But without response this variant work normal, cell protected in spread sheet
$guzzle = new Client();
$postCell = $guzzle
->post('https://sheets.googleapis.com/v4/spreadsheets/' . $spreadsheetId . ':batchUpdate',
[],
$addProtectedRangeJson
)
->addHeader('Authorization', 'Bearer ' . $arrayAccessTokenClient)
->addHeader('Content-type', 'application/json')
;
$postCell
->send()
->getBody(true)
;
$contents = (string) $postCell->getBody();// get body that I post -> my request, not response
$contents = $postCell->getBody()->getContents();// not find getContents, ask me did you mean to call getContentMd5
$answer = json_decode($postCell->getResponse()->getBody(), true);

Categories