I am trying to request shopify graphql-admin-api from my api. I am doing it according to the documentation given by graphql-admin-api, but it still gives me authorization errors.
PHP users can follow this function to make request to Shopify Admin API using GraphQL
I am using GuzzleHttp ( PHP HTTP client ) to create request
public function graph($query , $variables = []){
$domain = 'xxx.myshopify.com';
$url = 'https://'.$domain.'/admin/api/2019-10/graphql.json';
$request = ['query' => $query];
if(count($variables) > 0) { $request['variables'] = $variables; }
$req = json_encode($request);
$parameters['body'] = $req;
$stack = HandlerStack::create();
$client = new \GuzzleHttp\Client([
'handler' => $stack,
'headers' => [
'Accept' => 'application/json',
'Content-Type' => 'application/json',
'X-Shopify-Access-Token'=>$this->token // shopify app accessToken
],
]);
$response = $client->request('post',$url,$parameters);
return $body = json_decode($response->getBody(),true);
}
$query = "{ shop { name email } }"; // this is example graphQL query
$response = graph($query) // call this function
Below code can help you to check how much cost this graphQL query
$calls = $response->extensions->cost;
$apiCallLimitGraph = [
'left' => (int) $calls->throttleStatus->currentlyAvailable,
'made' => (int) ($calls->throttleStatus->maximumAvailable - $calls->throttleStatus->currentlyAvailable),
'limit' => (int) $calls->throttleStatus->maximumAvailable,
'restoreRate' => (int) $calls->throttleStatus->restoreRate,
'requestedCost' => (int) $calls->requestedQueryCost,
'actualCost' => (int) $calls->actualQueryCost,
];
Go to Apps -> Manage Apps at the bottom and then :
Create a private app in Shopify, which will connect to your application. Make sure you manage permission for what you want to query
After creating the private app you will get the password which you can use as the token for your HTTP requests with header 'X-Shopify-Access-Token' value: password
curl -X POST \
https://{shop}.myshopify.com/admin/api/2021-04/graphql.json \
-H 'Content-Type: application/graphql' \
-H 'X-Shopify-Access-Token: {password}' \
-d '
{
products(first: 5) {
edges {
node {
id
handle
}
}
pageInfo {
hasNextPage
}
}
}
'
For more visit: https://shopify.dev/docs/admin-api/getting-started#authentication
The way I use in NodeJS is by using package "graphql-request" to make requests and
const mutation = gql`
mutation createProduct(
$input: ProductInput!
$media: [CreateMediaInput!]
) {
productCreate(input: $input, media: $media) {
userErrors {
field
message
}
product {
id
metafields(first: 1) {
edges {
node {
id
}
}
}
}
}
}
`;
//const input = form your own input
const res = await graphQLClient.rawRequest(mutation, input);
Related
I am doing Paypal integration in Laravel. I have used composer require srmklive/paypal to install the srmklive/paypal package in this project.
When I press the PayPal button, I get this error:
Here is my code:
code from blade file:
paypal.Buttons({
createOrder: function(data, actions) {
return fetch('api/paypal/order/create/', {
method: 'post',
body:JSON.stringify({
"value":100
})
}).then(function(res) {
return res.json();
}).then(function(orderData) {
return orderData.id;
});
},
onApprove: function(data, actions) {
return fetch('/api/paypal/order/capture/', {
method: 'post',
body: JSON.stringify({
orderID: data.orderID
})
}).then(function(res) {
return res.json();
}).then(function(orderData) {
var errorDetail = Array.isArray(orderData.details) && orderData.details[0];
if (errorDetail && errorDetail.issue === 'INSTRUMENT_DECLINED') {
return actions.restart();
}
if (errorDetail) {
var msg = 'Sorry, your transaction could not be processed.';
return alert(msg); // Show a failure message (try to avoid alerts in production environments)
}
});
}
}).render('#paypal-button-container');
code from paymentController:
class PaymentController extends Controller
{
public function create(Request $request){
$data = json_decode($request->getContent(), true);
$provider = \PayPal::setProvider();
$provider->setApiCredentials(config('paypal'));
$token = $provider->getAccessToken();
$provider->setAccessToken($token);
$price = Plan::getSubscriptionPrice($data['value']);
$description = Plan::getSubscriptionDescription($data['value']);
$order = $provider->createOrder([
"intent" => "CAPTURE",
"purchase_units" => [
[
"amount" => [
"currency_code" => "USD",
"value" => $price
],
"description" => $description
]
]
]);
return response()->json($order);
}
public function capture(Request $request) {
$data = json_decode($request->getContent(), true);
$orderId = $data['orderID'];
$provider = \PayPal::setProvider();
$provider->setApiCredentials(config('paypal'));
$token = $provider->getAccessToken();
$provider->setAccessToken($token);
$result = $provider->capturePaymentOrder($orderId);
return response()->json($result);
}
}
How can I solve this error?
The route api/paypal/order/create/ is returning/outputting text that is not JSON, such as an HTML error page or something else that begins with an HTML tag.
The route must only output JSON, and must contain a valid id from the PayPal API.
I want to do paypal integration in Laravel. I have use composer require srmklive/paypal to install the srmklive/paypal package for my project. I get 404 error when I want to press the PayPal button. The popup paypal login tab will missing. Then I inspect the network I get the error like image given.
Here is my code:
class PaymentController extends Controller
{
public function create(Request $request){
$data = json_decode($request->getContent(), true);
$provider = \PayPal::setProvider();
$provider->setApiCredentials(config('paypal'));
$token = $provider->getAccessToken();
$provider->setAccessToken($token);
$plan = $provider->createOrder([
"intent" => "CAPTURE",
"purchase_units" => [
[
"amount" => [
"currency_code" => "USD",
"value" => "30"
],
"description" => "Item 1"
]
]
]);
return response()->json($plan);
}
public function capture(Request $request) {
$data = json_decode($request->getContent(), true);
$orderId = $data['orderID'];
$provider = \PayPal::setProvider();
$provider->setApiCredentials(config('paypal'));
$token = $provider->getAccessToken();
$provider->setAccessToken($token);
$result = $provider->capturePaymentOrder($orderId);
return response()->json($result);
}
}
Here is the code from blade file
paypal.Buttons({
createOrder: function(data, actions) {
return fetch('api/paypal/order/create/', {
method: 'post',
body:JSON.stringify({
"value":30
})
}).then(function(res) {
return res.json();
}).then(function(orderData) {
return orderData.id;
});
},
onApprove: function(data, actions) {
return fetch('/api/paypal/order/capture/', {
method: 'post',
body: JSON.stringify({
orderID: data.orderID
})
}).then(function(res) {
return res.json();
}).then(function(orderData) {
var errorDetail = Array.isArray(orderData.details) && orderData.details[0];
if (errorDetail && errorDetail.issue === 'INSTRUMENT_DECLINED') {
return actions.restart(); // Recoverable state, per:
}
if (errorDetail) {
var msg = 'Sorry, your transaction could not be processed.';
return alert(msg);
}
});
}
}).render('#paypal-button-container');
The error show like image given.
Does anyone know how to solve it?
Does the route api/paypal/order/create/ exist on your server? From the error message, it's returning a 404.
The route must exist (no 404) and successfully output a JSON response with an id obtained from the PayPal API.
I'm having some issues with using Shopify's GraphQL API. I've already made a bunch of REST calls, but for this one I would need GraphQL.
I'm trying to add videos to certain products and this is what I have so far:
mutation productCreateMedia($productId: ID!, $media: [CreateMediaInput!]!) {
productCreateMedia(productId: $productId, media: $media) {
media {
alt
}
mediaUserErrors {
code
field
message
}
product {
id
}
}
}
and for variables, I have an array of:
$gid = "gid://shopify/Product/".row('shopifyID');
$videoLink = "https://www.youtube.com/watch?v=".row('youtubeID');
$media = array('originalSource'=>$videoLink,'mediaContentType'=>'EXTERNAL_VIDEO');
$variables = array ('productId'=>$gid,'media'=>$media);
I use the next function for the call:
function graph($query , $variables = []){
$domain = 'domain.myshopify.com';
$url = 'https://'.$domain.'/admin/api/2020-01/graphql.json';
$request = ['query' => $query];
if(count($variables) > 0) { $request['variables'] = $variables; }
$req = json_encode($request);
$parameters['body'] = $req;
$stack = HandlerStack::create();
$client = new \GuzzleHttp\Client([
'handler' => $stack,
'headers' => [
'Accept' => 'application/json',
'Content-Type' => 'application/json',
'X-Shopify-Access-Token'=>'myAPIpass' // shopify app accessToken
],
]);
$response = $client->request('post',$url,$parameters);
return $body = json_decode($response->getBody(),true);
}
But what I'm getting back is:
Variable productId of type ID! was provided invalid value
I used php-shopify SDK for REST API, but couldn't figure out how it works for GraphQL, so went with the usual way of just calling the JSON endpoint.
Any help in what I'm doing wrong here?
So...to answer my own question...the shopify ID string has to be base 64 encoded.
I added just this line and it works now:
$gid = base64_encode($gid);
Looking to add tags to my mailing list members via the api. But I don't see where to pass in tags in the documentation. Can someone point to an example of how to update the tags associated with a member via the api?
If you want to create a member AND add a tag while doing so you may specify the tag attribute the following way:
$data = array(
'apikey' => $api_key,
'email_address' => $email,
'status' => $status,
'tags' => array('your-tag-name'),
'merge_fields' => array(
'FNAME' => $fname,
'LNAME' => $lname
)
);
Even though MC API some places will tell you to fill out both a name and a status, it helped me to define tags as an array but ONLY pasting in the name of the tag.
Seefan's answer in this thread helped me out and I figured i wanted to help a person who spend days (like me) to figure out how the "tags" is specified: add tags to mailchimp subscriber created via api php
Tags replaced static segments. So, the endpoints used to create tags and add and remove tags from members are the same endpoints that were previously used to manage segments. Here is the documentation on the endpoints to use to manage your tags via the API that includes the request and response body parameters as well as example requests and responses:
http://developer.mailchimp.com/documentation/mailchimp/reference/lists/segments/
In order to add tags to your members, you need to include their email addresses in the 'static_segment' array parameter.
I hope that helps.
This is the official way to add tags:
https://developer.mailchimp.com/documentation/mailchimp/reference/lists/members/tags/
It works, except that in my testing, the response message is empty, even though the tag is added.
Here's sample code in Google Apps Script:
payload = '{\
"tags": [\
{\
"name":"' + tagName + '",\
"status":"' + tagStatus + '"\
}\
]\
}'
;
params = {
"method": "POST",
"headers":MC_headers,
"payload": payload,
"muteHttpExceptions": true
};
url = MC_url + 'lists/' + MC_IDs.listId + '/members/' + sub_hash + '/tags';
response = UrlFetchApp.fetch(url, params);
Apparently Mailchimp "tags" are "segments".
I coded a couple functions that allow me to add tags by name (rather than by ID) to a member (i.e. subscriber) by email address.
/**
*
* #param string $emailAddress
* #param array $tags
* #return void
*/
public function addTagsToContact($emailAddress, $tags) {
$list_id = $this->getDefaultListId();
foreach ($tags as $tag) {
$this->addMemberToSegment($emailAddress, $list_id, $tag);
}
}
/**
* Add a tag to a subscriber (tags replaced segments https://stackoverflow.com/a/52315577/470749)
*
* #param string $emailAddress
* #param string $list_id
* #param string $segment_name
* #return array
*/
public function addMemberToSegment($emailAddress, $list_id, $segment_name) {
$api = Newsletter::getApi();
$segmentsByName = $this->getSegments($list_id);
$segment_id = $segmentsByName[$segment_name]['id'];
$response = $api->post("lists/$list_id/segments/$segment_id", [
'members_to_add' => [$emailAddress]
]); //https://developer.mailchimp.com/documentation/mailchimp/reference/lists/segments/#create-post_lists_list_id_segments_segment_id
return $response;
}
/**
*
* #param string $list_id
* #return array
*/
public function getSegments($list_id) {//https://developer.mailchimp.com/documentation/mailchimp/reference/lists/segments/#%20
$segmentsByName = [];
$api = Newsletter::getApi();
$count = 50; //default is 10
$offset = 0;
do {
$url = "lists/$list_id/segments/?" . http_build_query(['count' => $count, 'offset' => $offset]);
Log::debug($url);
$response = $api->get($url);
$total_items = $response['total_items'];
foreach ($response['segments'] as $segment) {
$segmentsByName[$segment['name']] = $segment;
}
$offset += $count;
} while (count($segmentsByName) < $total_items);
//Log::debug(json_encode($segmentsByName));
return $segmentsByName;
}
/**
*
* #return string
*/
public function getDefaultListId() {
return config('newsletter.lists.subscribers.id');
}
This relies on the https://github.com/spatie/laravel-newsletter library.
P.S. Thanks so much to #Jelan, whose answer got me on the right track!
It took me a while to also figure this one out. Their documentation isn't clear and it seems there are 2 ways to add tags, either via the tags endpoint using POST or via the update user via a PATCH. Here's an example of the POST in PHP:
function tagUser($email){
global $api_key;
global $listId;
$hashedEmail = md5(strtolower($email));
$args = array(
'method' => 'POST',
'headers' => array(
'Authorization' => 'Basic ' . base64_encode( 'user:'. $api_key )
),
'body' => json_encode(array(
'tags' => array(['name'=>'healthy','status'=>'active'])
))
);
$response = wp_remote_post( 'https://usxx.api.mailchimp.com/3.0/lists/'.$listId.'/members/'.$hashedEmail.'/tags', $args );
$body = json_decode( $response['body'] );
}
This is code for WordPress, but should help. I did get most of this from another answer but could not find it working anywhere else.
Note this only work if the subscriber already exists on the list and then you can tag or untag them.
$api_key = XXXXXXXXXXXXXXXXXXX-us20';
$email = 'tom#gmail.com';
$list_id = 'XXXXXXXX'; //This is the list /Audience id
$tag_name_text = 'XXXXX'; //This can be whatever you want it to be. Mail chimp will add it to the tags if not already on the system
//TAGS
$args = array(
'method' => 'POST',
'headers' => array(
'Authorization' => 'Basic ' . base64_encode( 'user:'. $api_key )
),
'body' => json_encode(array(
'tags' => array(
['name' => $tag_name_text,
'status' => 'active']
)
))
);
$response = wp_remote_post( 'https://' . substr($api_key,strpos($api_key,'-')+1) . '.api.mailchimp.com/3.0/lists/' . $list_id . '/members/'.md5(strtolower($email)).'/tags', $args );
if ($response['response']['code'] == 200 && $body->status == $status || $response['resp`enter code here`onse']['code'] == 204) {
//echo 'The you have been successfully ' . $status . '. Please check your emails';
} else {
echo '<b>' . $response['response']['code'] . $body->title . ':</b> ' . $body->detail;
}
FULL EXAMPLE USING GRAILS TO ADD LIST OF TAGS BY NAME TO A LIST OF USERS BY EMAIL
Note you may want to setup some error checking to see if the MailChimp audience member exists.
BusinessLogicController.groovy
/*
* Add list of tags by name to list of members
*/
def addTagsByNameToUser(){
List<string> tagNamesToAdd = ['foo', 'bar']
def addResult = mailChimpService.addTagsToContactsByName(["foo#example.com", "bar#example.com"], tagNamesToAdd)
}
MailChimpService.groovy
import grails.util.Holders
import groovyx.net.http.Method
class MailChimpService {
def grailsApplication
ApiConsumerService apiConsumerService
final String AUTH = Holders.config.grails.mailChimp.auth
final String BASEURL = "https://us19.api.mailchimp.com/3.0/"
final String LISTID = "abc123"
//Add list of tags by name to list of subscribers by email
def addTagsToContactsByName(List emailAddresses, List tags = []) {
tags.each { tagName ->
addMembersToSegment(emailAddresses, tagName);
}
}
//Add a tag to a subscriber by name
def addMembersToSegment(List emailAddresses, String segmentName) {
def segmentsByName = getAllSegmentsInList()
String segmentId = segmentsByName["$segmentName"] as String
return addMailChimpTagToUsers(emailAddresses, segmentId)
}
//Get information about all available segments for a specific list.
def getAllSegmentsInList(Map query = [:]) {
String path = "lists/"+LISTID+"/segments/"
Map segments = [:]
def segmentResults = apiConsumerService.getRequest(BASEURL, path, AUTH, query, Method.GET)
segmentResults.segments.each { segment ->
segments.put(segment.name, segment.id)
}
return segments
}
//Add list of tags to a list members.
def addMailChimpTagToUsers(List emailAddresses = [], String segmentId) {
String path = "lists/LISTID/segments/" + segmentId
apiConsumerService.postRequest(BASEURL, path, AUTH, ['members_to_add': emailAddresses], Method.POST)
}
}
ApiConsumerService.groovy
import grails.transaction.Transactional
import groovyx.net.http.ContentType
import groovyx.net.http.HTTPBuilder
import groovyx.net.http.Method
#Transactional
class ApiConsumerService {
//POST REQUEST
def postRequest(String baseUrl, String path, String auth, Map query = [:], Method method = Method.POST) {
try {
HTTPBuilder http = new HTTPBuilder(baseUrl)
http.headers['Authorization'] = 'Basic ' + "${auth}".getBytes('iso-8859-1').encodeBase64()
http.request(method, ContentType.JSON) { req ->
uri.path = path
if (method == Method.POST) {
body = query
} else {
uri.query = query
}
headers.'Accept' = 'application/json'
headers.'User-Agent' = "MyPetCerts/US19"
response.success = { resp, json ->
return json
}
response.failure = { resp, json ->
println "POST response status: ${resp.statusLine}"
}
}
} catch (groovyx.net.http.HttpResponseException ex) {
ex.printStackTrace()
return null
} catch (java.net.ConnectException ex) {
ex.printStackTrace()
return null
}
}
//GET Request
def getRequest(String baseUrl, String path, String auth, Map query = [:], Method method = Method.GET) {
return postRequest(baseUrl, path, auth, query, method)
}
}
I am trying to implement social login with angular 5 as my front-end and lumen 5.6 as my backend.
I am using JWT Authentication system with Lumen.
So, I am confused here how I should implement social login system in this situation.
Through my research I have come to these libraries which can do that work, but I am not sure how the process is handled from the front-end (angular) to back-end (lumen).
For Angular 5 -
angularx-social-login OR Satellizer
For Lumen -
laravel/socialite
But as I have not found any documentation on Satellizer working with Angular 5, so I choosed to use other one.
I have read this article, but still the process from front-end to back-end is not clear to me.
https://medium.com/#barryvdh/oauth-in-javascript-apps-with-angular-and-lumen-using-satellizer-and-laravel-socialite-bb05661c0d5c
Any explanatory help ?
So the way I used social login with Angular 5 is with a package called
"angular5-social-login": "^1.0.9",
So add that to your package.json file.
Import it in app.module.ts
import { SocialLoginModule, AuthServiceConfig, GoogleLoginProvider, FacebookLoginProvider } from 'angular5-social-login';
Set up a function in app.module.ts
export function getAuthServiceConfigs() {
const config = new AuthServiceConfig(
[
{
id: FacebookLoginProvider.PROVIDER_ID,
provider: new FacebookLoginProvider('') // Left as i dont use it
},
{
id: GoogleLoginProvider.PROVIDER_ID,
provider: new GoogleLoginProvider('YOUR-API-TOKEN.apps.googleusercontent.com')
},
]
);
return config;
}
Add it to your Imports in app.module.ts
imports: [
HttpClientModule,
AppRoutingModule,
...
SocialLoginModule, // One we need to add
],
Then at the add it to your providers in app.module.ts
providers: [
YourServices,
...
ApiAuthService,
{
provide: AuthServiceConfig,
useFactory: getAuthServiceConfigs
},
LoggedInGuard,
],
As you can see i have a LoggedInGuard and a ApiAuthService these are these with the auth and checking your logged in.
So That's the package installed and set up...
Now inside of api-auth.service.ts add this function
socialSignIn(userData) {
const formData = new FormData();
formData.append('email', userData.email);
formData.append('name', userData.name);
formData.append('provider', userData.provider);
formData.append('id', userData.id);
formData.append('idToken', userData.idToken);
formData.append('token', userData.token);
formData.append('image', userData.image);
return this._http.post(
environment.apiUrl + '/auth/social-signin/',
formData,
{
headers: new Headers({
'Authorization': 'Bearer ' + userData.idToken
})
}
);
}
Now in your sign in component add this to the HTML
<div (click)="socialSignIn('google')" class="c2a_btn large google">
Log in with google
</div>
In your sign in component .ts file add this function
import { AuthService, FacebookLoginProvider, GoogleLoginProvider, LinkedinLoginProvider } from 'angular5-social-login';
import { ApiAuthService } from '../../../../services/api-auth.service';
import { TokenService } from '../../../../services/token.service';
public socialSignIn(socialPlatform: string) {
this.loading = true;
let socialPlatformProvider;
if (socialPlatform === 'facebook') {
socialPlatformProvider = FacebookLoginProvider.PROVIDER_ID;
} else if (socialPlatform === 'google') {
socialPlatformProvider = GoogleLoginProvider.PROVIDER_ID;
} else if (socialPlatform === 'linkedin') {
socialPlatformProvider = LinkedinLoginProvider.PROVIDER_ID;
}
this.socialAuthService.signIn(socialPlatformProvider).then(
(userData) => {
this._apiAuthService.socialSignIn(userData)
.map( data => {
return data.json();
})
.subscribe(
token => {
this._tokenService.setAccessToken(token.access_token);
},
error => {
this.invalidLogin = true;
this.loading = false;
},
() => {
this.loading = false;
this.closeSignIn.emit('out');
// this._router.navigate(['/profile']);
}
);
}
);
}
This is just the front end now for the back end. I'm using Laravel 5.6
But I made a function like this
public function socialSignIn(Request $request, Response $response) {
$date = date('Y-m-d h:i:s');
$provider = $request->input('provider');
if ($provider == 'google') {
$id_token = $request->header('Authorization');
$id_token = str_replace("Bearer ","",$id_token);
$CLIENT_ID = Config::get('google.client_id');
$email = $request->input('email');
$names = $request->input('name');
$name = explode(' ', $names);
$client = new \Google_Client();
$client->setDeveloperKey($CLIENT_ID);
$payload = $client->verifyIdToken($id_token);
if ($payload) {
if (User::where('email', '=', $email)->exists()) {
$user = User::Where('email', '=', $email)->first();
if(!Auth::loginUsingId($user->id)){
return response()->json([
'failed'
], 403);
}
$updateLastLoginDate = User::where('id', Auth::user()->id)-first();
$updateLastLoginDate->last_login_date = $date;
$updateLastLoginDate->save();
$activeAccount = Auth::user();
$activeAccount->active = '1';
$activeAccount->save();
} else {
$recordUser = New User;
$recordUser->email = $request->input('email');
$recordUser->last_login_date = $date;
$recordUser->save();
$recordLinkedSocialAcounts = new LSA;
$recordLinkedSocialAcounts->user_id = $recordUser->id;
$recordLinkedSocialAcounts->provider_name = $provider;
$recordLinkedSocialAcounts->provider_id = $request->input('id');
$recordLinkedSocialAcounts->save();
$recordUserInformation = new UPI;
$recordUserInformation->user_id = $recordUser->id;
$recordUserInformation->first_name = $name[0];
$recordUserInformation->last_name = $name[1];
$recordUserInformation->last_login_date = $date;
$recordUserInformation->image = $request->input('image');
$recordUserInformation->save();
if(!Auth::loginUsingId($recordUser->id)){
return response()->json([
'failed'
], 403);
}
}
return response()->json([
'access_token' => Auth::user()->createToken('access_token')->accessToken,
'role_id' => Auth::user()->role_id
], 200);
} else {
return response()->json([
'failed'
], 403);
}
}
}
I will most probably make a video on this soon. Any questions just ask