laravel verify shopify webhook - laravel

How can I verify my shopify webhooks in laravel?
Currently I'm doing the following:
//Validate secret
if ( Request::header( 'X-Shopify-Hmac-Sha256' ) ) {
$hmac_header = Request::header( 'X-Shopify-Hmac-Sha256' );
$data = Request::json();
$calculated_hmac = base64_encode( hash_hmac( 'sha256', $data, Config::get( 'constants.SHOPIFY_APP_SECRET' ), true ) );
if ( $hmac_header != $calculated_hmac ) {
return Response::json( array(
'error' => true,
'message' => "invalid secret" ),
403 );
}
}else {
return Response::json( array(
'error' => true,
'message' => "no secret" ),
403 );
}
But it fails with the following message:
#0 [internal function]: Illuminate\Exception\Handler->handleError(2, 'hash_hmac() exp...', '/Users/JS/Sites...', 58, Array)
#1 /Users/JS/Sites/xxx/api/app/controllers/CustomerController.php(58): hash_hmac('sha256', Object(Symfony\Component\HttpFoundation\ParameterBag), 'xxxxxxxxxx...', true)
I suspect it has sth to do with the way I get the request data:
$data = Request::json();
Does anyone have a solution? Thx!

Follow the example given in the Shopify docs: https://docs.shopify.com/api/webhooks/using-webhooks#verify-webhook
Replace
$data = Request::json();
with
$data = file_get_contents('php://input');
You can still use Request::json() elsewhere to get a ParameterBag for processing data from the webhook.

Here's my handler, works good:
public function handle($request, Closure $next)
{
$data = file_get_contents('php://input');
$calculated_hmac = base64_encode(hash_hmac('sha256', $data, [SECRET], true));
if (!$hmac_header = $request->header('X-Shopify-Hmac-Sha256') or
$hmac_header != $calculated_hmac or $request->email == 'jon#doe.ca') {
return Response::json(['error' => true], 403);
}
return $next($request);
}
note that:
$request->email == 'jon#doe.ca' for case if there is not received test hooks for some reason
[SECRET] is the code from the store notifications settings under the webhook callback URL (All your webhooks will be signed with [SECRET] so you can verify their integrity.)

Related

Amazon payment integeration in Laravel and Vue.js

I am facing issue in Amazon payment gateway while completing checkoutsession.
Error which I am facing:
status=422; response={"reasonCode":"InvalidCheckoutSessionStatus","message":"You tried to call an operation on a Checkout Session that is in a state where that operation is not allowed"}
My code:
public function completeCheckout(Request $request){
$amazonpay_config = array(
'public_key_id' => config('amazonkey.public_key_id'),
'private_key' => config('amazonkey.private_key'),
'region' => 'UK',
'sandbox' => true
);
$payload = array(
"chargeAmount" => array(
"amount" => "100",
"currencyCode"=> "USD"
),
);
$payload = json_encode($payload);
try {
$checkoutSessionId = $request->checkoutSessionId;
// dd($checkoutSessionId);
$client = new ApayAPI($amazonpay_config);
$result = $client->completeCheckoutSession($checkoutSessionId, $payload, $headers = null);
if ($result['status'] === 200) {
// dd($result);
$response = json_decode($result['response'], true);
return $response;
// $response = json_decode($result['response'], true);
// $amazonPayRedirectUrl = $response['webCheckoutDetails']['amazonPayRedirectUrl'];
// echo "amazonPayRedirectUrl=$amazonPayRedirectUrl\n";
} else {
// check the error
echo 'status=' . $result['status'] . '; response=' . $result['response'] . "\n";
}
} catch (\Exception $e) {
// handle the exception
echo $e . "\n";
}
}
Make sure that prior to sending a request to complete checkout session that 1/the buyer has been redirected to checkoutResultReturnUrl (returned in response to update checkout session) and 2/ there are no constraints in the constraint object returned from update checkout session. If both of these conditions have been satisfied, know that complete checkout session must be requested within 24 hours of a buyer's redirect to checkoutResultReturnUrl or the checkout session will automatically cancelled.

Expected response code "250/251/252" but got code "451", with message "451 Temporary local problem - please try later"

I am new in Laravel development. I am sending an email to agencies which is approved and email is not null and if agencies email is null then get customer email for send email, But I got this error https://flareapp.io/share/DPygyxQ5#F57
I have tried to figure out where is the issue using echo pre in every steps. I am using smtp for sending email.
If you think that is there issue in smtp so if user register itself then email is successfully sent to user.
Here is code
// Get agency details
$agencies = Agency::where('status', 2)->get();
$property = Property::where('property_id', $id)->first();
if($agencies->isNotEmpty())
{
foreach($agencies as $agency)
{
if($agency->email != null)
{
$agency_name = $agency->name;
$agency_email = $agency->email;
$property_slug = $property->slug;
$property_link = route('property.detail', $property_slug);
Mail::send('emails.user.agency.mail_to_agency_after_property_approve',
[
'agency_email' => $agency_email,
'agency_name' => $agency_name,
'property' => $property,
'property_link' => $property_link,
],
function($message) use ($agency_email)
{
$message->to($agency_email);
$message->subject('Fresh Property Listing Update');
});
}
else
{
$customer = Customer::select('customer_id', 'first_name', 'last_name', 'customer_email')->where('customer_id', $agency->customer_id)->first();
$agency_name = $customer->first_name.' '.$customer->last_name;
$agency_email = $customer->customer_email;
$property_slug = $property->slug;
$property_link = route('property.detail', $property_slug);
Mail::send('emails.user.agency.mail_to_agency_after_property_approve',
[
'agency_email' => $agency_email,
'agency_name' => $agency_name,
'property' => $property,
'property_link' => $property_link,
],
function($message) use ($agency_email)
{
$message->to($agency_email);
$message->subject('Fresh Property Listing Update');
});
}
}
}

Guzzle POST request: required body when execute request

I have a POST request with Guzzle like this:
// Return a collection
$cart = $this->getCart('2019-10-08 07:08:39');
//Return first entry of the collection with first()
$template = $this->getTemplate($config->key);
$isDetail = null;
foreach ($cart as $item) {
try {
$client = $this->getClient();
$headers = ['Content-Type' => 'application/json'];
$body = [
'user_id' => $item->mystore_user_id,
'title' => $template->title,
'message' => $template->message,
'avatar' => $template->avatar,
'detail_id' => $isDetail,
'schedule' => null
];
print_r($body);
$response = $client->post('push-noti/unicast', $headers, $body);
print_r(response()->json(json_decode($response->getBody(), true)));
} catch (QueryException | \Exception $ex) {
echo "Error!";
}
}
My body variable value is exist in each loop when it printed. But when I use it in $client->post, my request return error with user_id, title, message is required. I really don't know why is it?
Can you tell me what's wrong in my code?
Thank you!
Try
$response = $client->post('push-noti/unicast', ['body' => $body , 'headers' => $headers]);
If you are calling a third party API, replace push-noti/unicast with complete URL.

REST User registration and Login through Api Key

I am not new to codeignitor , but still i didn't used they feature called 'API keys' authentication.
What i want to do ?
Register a user in my_users table and there is apikey as column that should store and generate unique api while user registration.
Login User , user give username , password and it get response as logged in if correct information is given , with it's respective apikey in my_users table.
After login i want to use apikey for all other user related calls like bookATask with header as X-API-KEY , which will allow me to control user session at client side and secure login and sign system.
Problem in Setup:
I am being asked each time i make a request to pass X-API-KEY which is wrong because , maybe there is call register_post in User controller which register users and Api key cannot be passed in headers unless it is generated in this method right?
I am just getting status:FALSE and error:Invalid Api key on each call i make.
What am i doing?
(Only changed code)
application/config/rest.php
defined('BASEPATH') OR exit('No direct script access allowed');
$config['force_https'] = FALSE;
$config['rest_default_format'] = 'json';
$config['rest_supported_formats'] = [
'json',
'array',
'csv',
'html',
'jsonp',
'php',
'serialized',
'xml',
];
$config['rest_status_field_name'] = 'status';
$config['rest_message_field_name'] = 'error';
$config['enable_emulate_request'] = TRUE;
$config['rest_realm'] = 'REST API';
$config['rest_auth'] = 'basic';
$config['auth_source'] = '';
$config['allow_auth_and_keys'] = TRUE;
$config['auth_library_class'] = '';
$config['auth_library_function'] = '';
$config['auth_override_class_method_http']['user']['register']['post'] = 'none';
$config['auth_override_class_method_http']['user']['login']['post'] = 'none';
$config['rest_valid_logins'] = ['user1' => '12345'];
$config['rest_ip_whitelist_enabled'] = FALSE;
$config['rest_ip_whitelist'] = '';
$config['rest_ip_blacklist_enabled'] = FALSE;
$config['rest_ip_blacklist'] = '';
$config['rest_database_group'] = 'default';
$config['rest_keys_table'] = 'my_users';
$config['rest_enable_keys'] = TRUE;
$config['rest_key_column'] = 'apikey';
$config['rest_limits_method'] = 'ROUTED_URL';
$config['rest_key_length'] = 40;
$config['rest_key_name'] = 'X-API-KEY';
$config['rest_enable_logging'] = TRUE;
$config['rest_logs_table'] = 'app_logs';
$config['rest_enable_access'] = FALSE;
$config['rest_access_table'] = 'access';
$config['rest_logs_json_params'] = FALSE;
$config['rest_enable_limits'] = TRUE;
$config['rest_limits_table'] = 'app_limits';
$config['rest_ignore_http_accept'] = FALSE;
$config['rest_ajax_only'] = FALSE;
$config['rest_language'] = 'english';
$config['check_cors'] = FALSE;
$config['allowed_cors_headers'] = [
'Origin',
'X-Requested-With',
'Content-Type',
'Accept',
'Access-Control-Request-Method'
];
$config['allowed_cors_methods'] = [
'GET',
'POST',
'OPTIONS',
'PUT',
'PATCH',
'DELETE'
];
$config['allow_any_cors_domain'] = FALSE;
$config['allowed_cors_origins'] = [];
application/config/autoload.php
$autoload['libraries'] = array('database');
controllers/api/Key.php
defined('BASEPATH') OR exit('No direct script access allowed');
require APPPATH . '/libraries/REST_Controller.php';
class Key extends REST_Controller {
protected $methods = [
'index_put' => ['level' => 10, 'limit' => 10],
'index_delete' => ['level' => 10],
'level_post' => ['level' => 10],
'regenerate_post' => ['level' => 10],
];
public function index_put()
{
$key = $this->_generate_key();
$level = $this->put('level') ? $this->put('level') : 1;
$ignore_limits = ctype_digit($this->put('ignore_limits')) ? (int) $this->put('ignore_limits') : 1;
if ($this->_insert_key($key, ['level' => $level, 'ignore_limits' => $ignore_limits]))
{
$this->response([
'status' => TRUE,
'key' => $key
], REST_Controller::HTTP_CREATED); // CREATED (201) being the HTTP response code
}
else
{
$this->response([
'status' => FALSE,
'message' => 'Could not save the key'
], REST_Controller::HTTP_INTERNAL_SERVER_ERROR); // INTERNAL_SERVER_ERROR (500) being the HTTP response code
}
}
public function index_delete()
{
$key = $this->delete('key');
if (!$this->_key_exists($key))
{
$this->response([
'status' => FALSE,
'message' => 'Invalid API key'
], REST_Controller::HTTP_BAD_REQUEST); // BAD_REQUEST (400) being the HTTP response code
}
$this->_delete_key($key);
$this->response([
'status' => TRUE,
'message' => 'API key was deleted'
], REST_Controller::HTTP_NO_CONTENT); // NO_CONTENT (204) being the HTTP response code
}
public function level_post()
{
$key = $this->post('key');
$new_level = $this->post('level');
if (!$this->_key_exists($key))
{
$this->response([
'status' => FALSE,
'message' => 'Invalid API key'
], REST_Controller::HTTP_BAD_REQUEST); // BAD_REQUEST (400) being the HTTP response code
}
if ($this->_update_key($key, ['level' => $new_level]))
{
$this->response([
'status' => TRUE,
'message' => 'API key was updated'
], REST_Controller::HTTP_OK); // OK (200) being the HTTP response code
}
else
{
$this->response([
'status' => FALSE,
'message' => 'Could not update the key level'
], REST_Controller::HTTP_INTERNAL_SERVER_ERROR); // INTERNAL_SERVER_ERROR (500) being the HTTP response code
}
}
public function suspend_post()
{
$key = $this->post('key');
if (!$this->_key_exists($key))
{
$this->response([
'status' => FALSE,
'message' => 'Invalid API key'
], REST_Controller::HTTP_BAD_REQUEST); // BAD_REQUEST (400) being the HTTP response code
}
if ($this->_update_key($key, ['level' => 0]))
{
$this->response([
'status' => TRUE,
'message' => 'Key was suspended'
], REST_Controller::HTTP_OK); // OK (200) being the HTTP response code
}
else
{
$this->response([
'status' => FALSE,
'message' => 'Could not suspend the user'
], REST_Controller::HTTP_INTERNAL_SERVER_ERROR); // INTERNAL_SERVER_ERROR (500) being the HTTP response code
}
}
public function regenerate_post()
{
$old_key = $this->post('key');
$key_details = $this->_get_key($old_key);
if (!$key_details)
{
$this->response([
'status' => FALSE,
'message' => 'Invalid API key'
], REST_Controller::HTTP_BAD_REQUEST); // BAD_REQUEST (400) being the HTTP response code
}
$new_key = $this->_generate_key();
if ($this->_insert_key($new_key, ['level' => $key_details->level, 'ignore_limits' => $key_details->ignore_limits]))
{
$this->_update_key($old_key, ['level' => 0]);
$this->response([
'status' => TRUE,
'key' => $new_key
], REST_Controller::HTTP_CREATED); // CREATED (201) being the HTTP response code
}
else
{
$this->response([
'status' => FALSE,
'message' => 'Could not save the key'
], REST_Controller::HTTP_INTERNAL_SERVER_ERROR); // INTERNAL_SERVER_ERROR (500) being the HTTP response code
}
}
private function _generate_key()
{
do
{
$salt = base_convert(bin2hex($this->security->get_random_bytes(64)), 16, 36);
if ($salt === FALSE)
{
$salt = hash('sha256', time() . mt_rand());
}
$new_key = substr($salt, 0, config_item('rest_key_length'));
}
while ($this->_key_exists($new_key));
return $new_key;
}
private function _get_key($key)
{
return $this->db
->where(config_item('rest_key_column'), $key)
->get(config_item('rest_keys_table'))
->row();
}
private function _key_exists($key)
{
return $this->db
->where(config_item('rest_key_column'), $key)
->count_all_results(config_item('rest_keys_table')) > 0;
}
private function _insert_key($key, $data)
{
$data[config_item('rest_key_column')] = $key;
$data['date_created'] = function_exists('now') ? now() : time();
return $this->db
->set($data)
->insert(config_item('rest_keys_table'));
}
private function _update_key($key, $data)
{
return $this->db
->where(config_item('rest_key_column'), $key)
->update(config_item('rest_keys_table'), $data);
}
private function _delete_key($key)
{
return $this->db
->where(config_item('rest_key_column'), $key)
->delete(config_item('rest_keys_table'));
}
}
(Method used in controller to generate Api key)
private function _generate_key()
{
do
{
// Generate a random salt
$salt = base_convert(bin2hex($this->security->get_random_bytes(64)), 16, 36);
// If an error occurred, then fall back to the previous method
if ($salt === FALSE)
{
$salt = hash('sha256', time() . mt_rand());
}
$new_key = substr($salt, 0, config_item('rest_key_length'));
}
while ($this->_key_exists($new_key));
return $new_key;
}
private function _key_exists($key)
{
return $this->db
->where(config_item('rest_key_column'), $key)
->count_all_results(config_item('rest_keys_table')) > 0;
}
On Database i have two extra tables app_limits , app_logs , and apikey column with varchar(40) in my_users table that's all , can anyone help me solving my issue , what am i doing wrong ?
I have found the answer myself , i just modifying these variables helped me :
$config['auth_override_class_method_http']['user']['customer_register']['post'] = 'none';
$config['auth_override_class_method_http']['user']['customer_login']['post'] = 'none';
these are basically exception methods for Api key which can be register and login , thanks myself

HybridAuth send tweet with image

I'm using the HybridAuth library.
I'd like to be able to post message to my authenticated users twitter profile with images.
The setUserStatus method works well to automatically send a tweet.
I wrote the following method :
function setUserStatus( $status, $image )
{
//$parameters = array( 'status' => $status, 'media[]' => "#{$image}" );
$parameters = array( 'status' => $status, 'media[]' => file_get_contents($image) );
$response = $this->api->post( 'statuses/update_with_media.json', $parameters );
// check the last HTTP status code returned
if ( $this->api->http_code != 200 ){
throw new Exception( "Update user status failed! {$this->providerId} returned an error. " . $this->errorMessageByStatus( $this->api->http_code ) );
}
}
The message I get from twitter is :
Ooophs, we got an error: Update user status failed! Twitter returned an error. 403 Forbidden: The request is understood, but it has been refused.
How Can I get more precise info about error ?
Does anybody allready success in sending a picture attached to a tweet ?
Thanks !
Hugo
Thanks #Heena for making myself wake up on this question, I MADE IT ;)
function setUserStatus( $status )
{
if(is_array($status))
{
$message = $status["message"];
$image_path = $status["image_path"];
}
else
{
$message = $status;
$image_path = null;
}
$media_id = null;
# https://dev.twitter.com/rest/reference/get/help/configuration
$twitter_photo_size_limit = 3145728;
if($image_path!==null)
{
if(file_exists($image_path))
{
if(filesize($image_path) < $twitter_photo_size_limit)
{
# Backup base_url
$original_base_url = $this->api->api_base_url;
# Need to change base_url for uploading media
$this->api->api_base_url = "https://upload.twitter.com/1.1/";
# Call Twitter API media/upload.json
$parameters = array('media' => base64_encode(file_get_contents($image_path)) );
$response = $this->api->post( 'media/upload.json', $parameters );
error_log("Twitter upload response : ".print_r($response, true));
# Restore base_url
$this->api->api_base_url = $original_base_url;
# Retrieve media_id from response
if(isset($response->media_id))
{
$media_id = $response->media_id;
error_log("Twitter media_id : ".$media_id);
}
}
else
{
error_log("Twitter does not accept files larger than ".$twitter_photo_size_limit.". Check ".$image_path);
}
}
else
{
error_log("Can't send file ".$image_path." to Twitter cause does not exist ... ");
}
}
if($media_id!==null)
{
$parameters = array( 'status' => $message, 'media_ids' => $media_id );
}
else
{
$parameters = array( 'status' => $message);
}
$response = $this->api->post( 'statuses/update.json', $parameters );
// check the last HTTP status code returned
if ( $this->api->http_code != 200 ){
throw new Exception( "Update user status failed! {$this->providerId} returned an error. " . $this->errorMessageByStatus( $this->api->http_code ) );
}
}
To make it work you have to do like this :
$config = "/path_to_hybridauth_config.php";
$hybridauth = new Hybrid_Auth( $config );
$adapter = $hybridauth->authenticate( "Twitter" );
$twitter_status = array(
"message" => "Hi there! this is just a random update to test some stuff",
"image_path" => "/path_to_your_image.jpg"
);
$res = $adapter->setUserStatus( $twitter_status );
Enjoy !
I did not understand it for hybridauth then I used this library
https://github.com/J7mbo/twitter-api-php/archive/master.zip
Then I was successful using code below: (appears elsewhere in stack)
<?php
require_once('TwitterAPIExchange.php');
$settings= array(
'oauth_access_token' => '';
'oauth_access_secret' => '';
'consumer_key' => '';
'consumer_secret' => '';
// paste your keys above properly
)
$url_media = "https://api.twitter.com/1.1/statuses/update_with_media.json";
$requestMethod = "POST";
$tweetmsg = $_POST['post_description']; //POST data from upload form
$twimg = $_FILES['pictureFile']['tmp_name']; // POST data of file upload
$postfields = array(
'status' => $tweetmsg,
'media[]' => '#' . $twimg
);
try {
$twitter = new TwitterAPIExchange($settings);
$twitter->buildOauth($url_media, $requestMethod)
->setPostfields($postfields)
->performRequest();
echo "You just tweeted with an image";
} catch (Exception $ex) {
echo $ex->getMessage();
}
?>

Resources