How to implement webhook code in Shopify CLI created app - webhooks

As a beginner, I need some very basic js help here I guess. I created a custom shopify app using Shopify CLI out of my terminal. I now want to implement code in the created Shopify App template, to receive webhooks from my Shopify, and I don't know how to do this.Should I create a "app.rb" file to put the code there, or a "app.jsx" file, and where?Do I need to "import" or "export" in order to connect my new webhook code to the rest of the App, and how? Just some basic advice, what to read, which tutorials to check, would be already awesome. The code I want to implement looks like this:
require 'rubygems'
require 'base64'
require 'openssl'
require 'sinatra'
require 'shopify_api'
require 'active_support/security_utils'
# The Shopify app's API secret key, viewable from the Partner Dashboard. In a production environment, set the API secret key as an environment variable to prevent exposing it in code.
API_SECRET_KEY = '******************'
helpers do
# Compare the computed HMAC digest based on the API secret key and the request contents to the reported HMAC in the headers
def verify_webhook(data, hmac_header)
calculated_hmac = Base64.strict_encode64(OpenSSL::HMAC.digest('sha256', API_SECRET_KEY, data))
ActiveSupport::SecurityUtils.secure_compare(calculated_hmac, hmac_header)
end
end
# Respond to HTTP POST requests sent to this web service
post '/webook/product_update' do
request.body.rewind
data = request.body.read
verified = verify_webhook(data, env["HTTP_X_SHOPIFY_HMAC_SHA256"])
halt 401 unless verified
# puts "Webhook verified: #{verified}"
# Process webhook payload
# ...
end
Maybe it helps to show my file structure (with open "web" folder):

Related

Create Shopify Custom App with webhook in 2022

I want to create a quite simple Shopify Custom App for my shop. This App should run when an order is created, so with a webhook product/change.
After creating the webhook, I receive errors in my terminal as soon as I update a product and the webhook is sent. I just have no idea how to correctly implement the webhook code and where. So this question here is a very basic question, how can I implement a Shopify webhook (webhook code from official Shopify dev page for Ruby) in my freshly Shopify CLI created app. Unfortunately, I can't find any help, it seems that there are so many different ways of creating webhooks. None of them is working for me so far, so I try to stick to the most common one described by shopify dev tutorials.
Steps so far:
I installed a custom app with Shopify CLI, using a Node template with the command "npm init #shopify/app#latest". After this, with "npm run dev" I created a tunnel with ngrok. With this, I receive a url to the app, I can access the app in my Shopify Partners and install it.There is a video tutorial of a shopify dev that I used as help, if someone is more interested Shopify dev Webhook tuorial (of 2021, so I guess a bit outdated unfortunately).
I put the following "webhook code", which uses Ruby, in a app.rb file, which I saved in my root folder of my Shopify CLI created app. The code is available in Shopify dev tutorials (Shopify dev tuorial) for Ruby (I made just very few adaptions like adding a path and adding the Shopify Secret Key:
require 'rubygems'
require 'base64'
require 'openssl'
require 'sinatra'
require 'active_support/security_utils'
# The Shopify app's API secret key, viewable from the Partner Dashboard. In a production environment, set the API secret key as an environment variable to prevent exposing it in code.
API_SECRET_KEY = 'my_api_secret_key'
helpers do
# Compare the computed HMAC digest based on the API secret key and the request contents to the reported HMAC in the headers
def verify_webhook(data, hmac_header)
calculated_hmac = Base64.strict_encode64(OpenSSL::HMAC.digest('sha256', API_SECRET_KEY, data))
ActiveSupport::SecurityUtils.secure_compare(calculated_hmac, hmac_header)
end
end
# Respond to HTTP POST requests sent to this web service
post '/webook/product_update' do
request.body.rewind
data = request.body.read
verified = verify_webhook(data, env["HTTP_X_SHOPIFY_HMAC_SHA256"])
halt 401 unless verified
# Process webhook payload
# ...
end
Next to the "app.rb" in the root folder of my App, I created a "Gemfile":
source 'https://rubygems.org'
gem 'shopify_api'
gem 'sinatra'
gem 'activesupport'
With bundle install, a Gemfile.lock is created, everything seems fine here. I created a webhook for product_change in my Shopify Partners Admin under "Notifications". As URL, I entered the ngrok URL from terminal window (from npm run dev command, the tunnel is still open), but with adding /webook/product_update, as this I used in the function path of the webhook.The webhook is sending sth to my app when I update a product, which is good at least. Unfortunately, as I update a product, my app shows errors in the terminal and crashes. The same errors are appearing even if I delete the app.rb file and the Gemlock files completely.The terminal shows these errors:
C:\ShopifyApps\test2\web\node_modules\#shopify\shopify-api\dist\error.js:13
var _this = _super.apply(this, tslib_1.__spreadArray([], tslib_1.__read(args), false)) || this;
**InvalidRequestError: Request does not contain a host query parameter**
at InvalidRequestError.ShopifyError [as constructor] (C:\ShopifyApps\test2\web\node_modules\#shopify\shopify-api\dist\error.js:13:28)
at new InvalidRequestError (C:\ShopifyApps\test2\web\node_modules\#shopify\shopify-api\dist\error.js:230:42)
at Object.getEmbeddedAppUrl (C:\ShopifyApps\test2\web\node_modules\#shopify\shopify-api\dist\utils\get-embedded-app-url.js:22:15)
at file:///C:/ShopifyApps/test2/web/index.js:187:41
I'd be very happy about any hint how to find out what is wrong. How can I connect the abb.rb file with my index.js file of my root app? I guess I need to do that. But I have no real idea unfortunately.

passport-apple inexplainable invalid_client on nodejs backend -- using clean example repository with fresh set of credentials

I've cloned https://github.com/ananay/passport-apple-example and replaced the config with this:
clientID: "com.myname.web",
teamID: "myteamid",
callbackURL: "https://myurldev.com/auth/apple/redirect",
keyID: "mykeyid",
privateKeyLocation: path.join(__dirname, "../apple-key.p8")
I've also added SSL certificate on my machine and starting the server with https, all works fine & is recognized by my browser. I'm also starting the app on port 443 and proxying using my hosts file myurl.dev.com -> 127.0.0.1.
I have the same auth setup for facebook, google & microsoft and everything works fine.
I have:
Created a new APP identifier and enabled Sign in with Apple for it, named it: com.myname.dev
Created a new SERVICE identifier and enabled Sign in with apple, called it: com.myname.web
Added "https://myurldev.com/auth/apple/redirect" to the "Reply URLS" on the service identifier com.myname.web
Set my app identifier com.myname.dev as the main app identifier my service to be grouped with.
Created a private key and enabled sign in with apple, interface confirmed the presence of grouped ID com.myname.web bundled with com.myname.dev for which the key was created.
I have confirmed using console.log that the private key is indeed at the path being passed as parameter.
converted the .p8 file to base64 & then back to UTF-8 in an attempt to use the string for privateKeyString
successfully implemented Apple Oauth several times in the past using passport-apple
This time around, for some reason, auth simply doesn't work.
If I set the clientID as the APP identifier, not the service, I'm getting
invalid_request
Invalid web redirect url.
instead of invalid_client
Any advice on debugging this is highly appreciated. Thank you.
EDIT #1:
I have dug a bit deeper into the passport-apple package to figure out if anything goes against apple's docs around token generation, but the flow never reaches that part, indicating things go wrong on the actual configuration in Apple's console & what I'm trying to use for my project.
EDIT #2
2 of the app Ids I have created always throw "wrong redirect uri" because they're not service IDs so I can't configure redirect_uri, this will change if to "required" if I pass undefined as a redirect_uri.
One of the app ids throws only invalid client_id instead, regardless if I pass undefined or good value for redirect_uri.
EDIT #3
Went full vanilla through the OAuth code flow process and just created a url & redirected the user it, failing with this method is consistent with what is happening when using the passport-apple module.
const url = new URL("https://appleid.apple.com/auth/authorize");
url.searchParams.append("state", "fdbd287b1f");
url.searchParams.append("response_type", "code");
url.searchParams.append("scope", "name email");
url.searchParams.append("response_mode", "form_post");
url.searchParams.append(
"redirect_uri",
"https://raiseitupdev.com/auth/apple/redirect",
);
url.searchParams.append("client_id", "com.myname.web");
return res.redirect(url.toString());
[Creator of the library here.]
Did it stop working in development too? I feel this is a configuration error because the actual thing is working live on my website:
https://passport-apple.ananay.dev
Please follow up on this Github issue. Thanks!
https://github.com/ananay/passport-apple/issues/23

How to prevent Postman like tool to access the Django rest api ? and able to access only from Native APP

I have a API Code like this
class TEST(APIView):
def post(self, request):
try:
taxdtl = views
jsondata = json.loads(request.body.decode('utf-8'))
request.session['Entity_gid'] = 1
lj_sales_fav_pdct = taxdtl.sales_fav_product(request)
test = lj_sales_fav_pdct.content
return Response(json.loads(test))
except:
return Response({"MESSAGE": "ERROR_OCCURED"})
and i have a authendicate token like this
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
),
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated', )
}
But when user can in debugger mode F12 can see all data's and able to see all data including Token.
Suggest me how to prevent this.
The API must be access only from the Python views of the Native website, and not form ARC or POST Man like tool.
I think its not really possible to do exactly what You ask.
When You run server with backend application, and this app can be external access, 'everybody' can send request and if its valid get response (POST Man tool, custom scripts etc.). Also all data stored in headers, local storage etc. will be visible for anybody who gonna hit f12 :/.
What You can do:
use one time auth token
use auth token with very short use time
encrypt data inside auth token
run everything local
Cheers
fenrir

Send emails through gmail using flask-mail

I have a simple CRUD webapp set up in Python/Flask, when one particular function is activated (approving a request) I'd like to send an email notification to the user, but for all I've tried I can't get the email to send through my code.
Here is my config file with all the relevant environment variables set (inside of a Config object):
MAIL_SERVER = 'smtp.gmail.com'
MAIL_PORT=465
MAIL_USE_SSL=True
MAIL_USERNAME = '**#gmail.com'
MAIL_PASSWORD = '**'
I have also tried calling app.config.update(those values) in my app/init.py file. Here is the current code to do so
mail = Mail()
def create_app(config_name):
app = Flask(__name__, instance_relative_config=True)
app.config.from_object(app_config[config_name])
app.config.from_pyfile('./config.py')
app.config.update(
MAIL_SERVER='smtp.gmail.com',
MAIL_PORT=465,
MAIL_USE_SSL=True,
MAIL_USE_TLS=False,
MAIL_USERNAME = '**#gmail.com',
MAIL_PASSWORD = '**')
mail.init_app(app)
And finally here is the code where I actually attempt to send the email:
msg = Message(html=html, sender='**#gmail.com', subject='Your Reservation for %s' % reservation.item.name, recipients=['**'])
mail.send(msg)
Additionally, it currently fails silently and I don't know how to even view what error is happening. Any help is much appreciated!
My suggestion in the comments was indeed the answer to the question.
Enabling "Less Secure Apps" in the Google Account settings was the necessary step to fix the hangup the OP was experiencing. This link from Google's support page walks you through how to enable this option.
I think, you should switch your sending protocol to TLS
this is sample from my project
MAIL_SERVER='smtp.gmail.com',
MAIL_PORT=587,
MAIL_USE_TLS=True,
MAIL_USERNAME = '**#gmail.com',
MAIL_PASSWORD = '**'
for me this works very well.
Now that Google is removing the less-secure app access feature due to security reasons, the best way to get around this is to use Sendgrid. They provide 100 free emails per day forever. You can register your same Gmail address as a single sender in SendGrid. Generate an API key and use it in your flask app to send emails.
For reference: Sending Emails from Python Flask Applications With Twilio SendGrid

Getting customized message from GCM using Web push notifications

I'm using Web push notifications with Chrome, and they work great. But now I want to deliver a custom message in my notifications. I can have my Service Worker call out to my site to get content, as is done at https://simple-push-demo.appspot.com/—which is fine if I want every recipient to see the same message.
Is there any way to get either the recipient’s registration_id or the message_id that GCM returns? If I could get either of these and include them in the callback to the service, I could customize the response.
Also, any info on when we might be able to include a payload in the call to GCM?
The registration_id and message_id fields aren't exposed, but if the user is previously authenticated to your app, any fetch() to the server from your Service Worker will include credentials (and session information) which you can use to identify them.
If that doesn't work for your case, you can store user/session information in IndexedDB.
Payloads are coming soon—likely Chrome 50 or 51—based on the Web Push protocol. It's a bit of extra overhead and work to configure the (required) encryption.
It's possible, but I wouldn't do it since it's specific to GCM, while other browsers use other services.
You can either create a unique ID for each user (like we're doing in Mercurius) and store it in IndexedDB, or you can use the entire endpoint URL as an ID.
Here's the snippet to get the registration_id:
self.registration.pushManager.getSubscription()
.then(function(subscription) {
if (subscription) {
var endpoint = subscription.endpoint;
var endpointParts = endpoint.split('/');
var gcmRegistrationID = endpointParts[endpointParts.length - 1];
console.log(gcmRegistrationID);
}
});
P.S.: It returns a promise, so make sure your service worker waits for the promise to be resolved.

Resources