Understanding Shopify App Proxy - CORS Policy issue - javascript

I am struggling to understand how to use the Shopify App Proxy feature.
We have a store using Shopify Pay and multi currency, but have offsite landing pages on Unbounce and the CRO people want to use direct checkout links, ie
https://our-shopify-store.myshopify.com/cart/36485954240671:1
Which is a nice idea, but the product gets added to the cart and then redirected to checkout in the store default currency, and it needs added in an appropriate currency based on the user location.
There seems to be no way to pass currency as a parameter in the direct checkout links so I am trying to create an intermediary app running on an app proxy to intercept this request;
https://my-cunning-proxy.com/36485954240671:1
And use a 3rd party API to get the user geoip before trying to create a checkout in the user currency by posting the variant, quantity and currency to the checkout API and receiving back a checkout url to redirect the visitor to.
What I've tried
I've got an app working in development.
The app is on https://our-shopify-store.myshopify.com with an app proxy of https://my-cunning-proxy.com/
I can access the app on https://my-cunning-proxy.com?host=our-shopify-store.myshopify.com but if I try to post to the checkout API in an included .jsx file;
const dataBody = "{\"checkout\":{\"line_items\":[{\"variant_id\":7699511831234,\"quantity\":5,\"presentment_currency\": \"USD\"}]}}";
const authtoken = 'xxxxx';
const shopifyDomain = 'https://our-shopify-store.myshopify.com/admin/api/2022-10/checkouts.json';
const response = await axios({
url: shopifyDomain,
method: 'POST',
headers: {
"X-Shopify-Access-Token": authtoken,
"Content-Type": "application/json",
"Access-Control-Allow-Origin":"*"
},
data: {
dataBody
}
});
console.log(response.data);
It rejects the post based on CORS policy. I'm not sure where the Subpath prefix and Subpath even come into things. None of the following urls work, they all 404;
const shopifyDomain = '/admin/api/2022-10/checkouts.json';
const shopifyDomain = 'https://our-shopify-store.myshopify.com/apps/proxy-subpath/admin/api/2022-10/checkouts.json';
const shopifyDomain = 'https://our-shopify-store.myshopify.com/apps/admin/api/2022-10/checkouts.json';
const shopifyDomain = '/apps/proxy-path/admin/api/2022-10/checkouts.json';
Any pointers/suggestions/observations/criticisms much appreciated, it's my first public Shopify app and it has been something of a struggle to even get to this lowly position.

I will inform you that you are indeed using App Proxy in a manner that induces head-scratching. But nonetheless, carry on.
If you wanted to create a checkout with more correctness, you can leverage Storefront API. It is meant to provide you with access to both your products and checkout, without actually forcing your customers to use your Shopify store. You should pivot to trying that out. While it is not perfect, it is far superior to simply hitting a checkout with a variant ID, because as you have indicated, that does not work at all with multi-currency. Or does it?
Since Shopify hacked on languages and markets recently, have you looked into markets, where you can sell to your "market" in their currency?
Anyway, long story short, and the answer, you are getting CORS because you are doing App Proxy all wrong, and no, it cannot help you with your checkout issue!

As mentioned in the comments, I realised the easy solution to the problem (although not necessarily the answer to this question) was to do it using JS and not use an app at all.
Create a custom page template that includes a div <div id="direct-checkout"></div>. In this example it has the url /pages/direct-checkout
Assuming there is a currency switcher form template in the theme in this format;
{%- if localization.available_countries.size > 1 -%}
<div class="currency-switcher-form">
<localization-form>
{% form 'localization' %}
<div class="select-country" data-input-name="country2" data-selected-country="{{ localization.country.iso_code }}"></div>
<input type="hidden" name="country_code" value="{{ localization.country.iso_code }}">
{% endform %}
</localization-form>
</div>
{%- endif -%}
You would use the following JS for both general currency switching (rather than using the badly performing and impossible to tweak Shopify Geolocation App) and also to handle the direct-to-checkout links passing product variant and quantity parameters.
It uses a cookie to reduce the geoip API calls and would redirect users who had previously been on the site to the currency they had previously been assigned/selected.
/*-----------------------------------------------------------------------------------*/
/* GeoIP currency switching
/*-----------------------------------------------------------------------------------*/
function setCurrencyCookie(countryCode) {
var cookieArguments = {
expires: 30,
path: '/',
domain: 'your-domain.com'
}
Cookies.set('_your-domain-geoip-currency', countryCode, cookieArguments);
}
var cookieName = '_your-domain-geoip-currency';
if (!Cookies.get(cookieName)) {
autoCurrencySwitch();
} else {
instantBuy();
}
function autoCurrencySwitch() {
$.ajax( {
url: 'https://api.ipgeolocation.io/',
type: 'GET',
dataType: 'json',
success: function(location) {
var currencyCountry= location.country_code2;
$('input[name="country_code"]').val(currencyCountry);
$(".select-country").attr('data-selected-country', currencyCountry);
var form = $('form#localization_form');
var actionUrl = form.attr('action');
$.ajax({
type: "POST",
url: actionUrl,
data: form.serialize(),
success: function(data){
setCurrencyCookie(currencyCountry);
instantBuy();
},
error: function(errorData){
console.log('currency switch error', errorData);
}
});
}
});
}
/*-----------------------------------------------------------------------------------*/
/* Direct Checkout Redirect
/*-----------------------------------------------------------------------------------*/
function instantBuy( ){
var directCheckoutElement = document.getElementById("direct-checkout");
const params = new Proxy(new URLSearchParams(window.location.search), {
get: (searchParams, prop) => searchParams.get(prop),
});
// Example URL
// https://your-domain.com/pages/direct-checkout?variant=417239912345&quantity=1
let variantId = params.variant;
let qty = params.quantity;
if (directCheckoutElement && variantId && qty) {
$.ajax({
url: "/cart/add.js",
type: "POST",
data : { id: variantId, quantity: qty },
dataType: 'json',
success: function(data){
window.location.href = "/checkout";
},
error: function(errorData){
console.log('checkout redirect error', errorData);
}
});
} else {
return false;
}
}
Hopefully this can help someone with the same requirement I had.

Related

Stripe Payment element show saved card

I am using laravel with stripe payment element. I am trying to show the saved cards for the customers that we already have. I have followed the stripe docs and found how I can show it on checkout. But the problem is that I am not getting the saved cards for the customer. And instead I am facing an error on my console as:
When authenticating with an ephemeral key, you must set the Stripe-Version header to an explicit API version, such as 2020-08-27
I have checked and changed lot of versions from here:
$ephemeralKey = \Stripe\EphemeralKey::create(
['customer' => "$user->stripe_customer_id"],
['stripe_version' => '2019-11-05']
);
I changed the version to different version that I can see on my stripe dashboard:
This is my Js Initialize function:
// Fetches a payment intent and captures the client secret
async function initialize() {
// Customize the appearance of Elements using the Appearance API.
const appearance = { /* ... */ };
// Enable the skeleton loader UI for the optimal loading experience.
const loader = 'auto';
const { clientSecret, customerOptions } = await fetch("{{ route("user-create-stripe-element-payment") }}", {
method: "POST",
headers: {
"Content-Type" : "application/json",
"accept" : "application/json",
'X-CSRF-TOKEN': "{{ csrf_token() }}",
'stripe_version':"2019-11-05"
},
body: JSON.stringify({ totalCharge:total }),
}).then((r) => r.json());
elements = stripe.elements({
clientSecret,
appearance,
loader,
customerOptions
});
const paymentElement = elements.create("payment");
paymentElement.mount("#payment-element");
}
And I am also using the betas which is given in the documentation:
const stripe = Stripe("{{env('STRIPE_KEY')}}", {
betas: ['elements_customers_beta_1'],
});
But this error is not going away. And its not even populating the Payment element.
Please help me debug this or if someone has any suggestion to check what is going on here.
Thanks in advance.
You are not providing an API version in your JS here
const stripe = Stripe("{{env('STRIPE_KEY')}}", {
betas: ['elements_customers_beta_1'],
});
change the above code to
const stripe = Stripe("{{env('STRIPE_KEY')}}", {
betas: ['elements_customers_beta_1'],
apiVersion: 'Your Version Here'
});
In your case, it should be something like this
const stripe = Stripe("{{env('STRIPE_KEY')}}", {
betas: ['elements_customers_beta_1'],
apiVersion: '2019-11-05'
});
You can read more here. https://stripe.com/docs/api/versioning?lang=node
It is for nodejs but the API version override will work in the same way.

SPA Routing Vanilla JavaScript

I'm doing a SPA project using Vanilla JS. It is a kind of notice board platform where there are categories. Each category will have a list of posts that users have uploaded.
This is the URL format for each category: base_URL/{category}/{pageNum}.
This is the URL format for each post: base_URL/board/${board_id}.
I have to extract the board_id some how upon the user clicking it and use it in the URL (ex: http://example.com/board/31890adfa).
From searching I found out I'll be using the history API.
Then I'll extract the board_id from the URL and send a request to view that post:
const fetchBoardDetails = async (board_id) => {
await fetch(`http://${base_URL}/board/${board_id}`, {
method: "GET",
headers: { "Content-Type": "application/json" },
}).then(async (res) => {
const data = await res.json();
root.innerHTML = `
<h1>${data.title}</h1>
<ul>
<li>${data.category}</li>
<li>${data.date}</li>
<li>${data.place}</li>
<li>${data.fee}</li>
</ul>
`
}
My problem is, how do I get the board_id in step 1 to use in step 2? I think it has to do with URL routing and I've tried to figure it out but can't wrap my head around it.

How to pass JWT-TOKEN to localStorage using JavaScript (script for html)?

I am writing a Rest MVC application for an online coffee and tea store. The following technologies are used: Spring-Boot, Hibernate, PostgreSQL, Gradle, Thymeleaf, HTML, CSS. I have the whole backend ready, it remains to make a frontend. At the moment I'm making an authorization page. The page itself is ready with HTML and CSS, now you need to make the authorization logic itself. To do this, I need to write a script in javascript so that my jwt token is stored in localStorage. The point is that I don't know how to implement this, how to pass my token through the header using javascripte in localStorage.
Important: javascript must be clean, without using frameworks (angular, node ...). How should I do it?
P.S. Again, the whole backand is ready. Rest-authorization method works (that is, I enter my login and password - I get a jwt token).
Java - authorization method
public ResponseEntity<Map<String, String>> authorization(#RequestBody AuthenticationRequestDTO requestDto) {
try {
String login = requestDto.getLogin();
authenticationManager
.authenticate(new UsernamePasswordAuthenticationToken(login, requestDto.getPassword()));
User user = userRepository.getByLogin(login);
if (user == null) {
throw new AuthenticationServiceException("ddwd");
}
String token = jwtTokenProvider.createToken(login, user.getRole());
Map<String, String> response = new HashMap<>();
response.put("login", login);
response.put("token", token);
return ResponseEntity.ok(response);
} catch (AuthenticationServiceException e) {
log.error("Error: ", e);
throw new AuthenticationServiceException("Invalid login");
}
}
You can add the JWT token in localstorage and retrieve/read and pass the value with your API calls. below is the example for setting and reading value from localstorage.
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script type="text/javascript">
function createLocalstorageItem(txtJwtTokenValue) {
sessionStorage.setItem("jwtToken", txtJwtTokenValue);
}
function readValue() {
var jwtToken = sessionStorage.getItem("jwtToken");
console.log("jwtToken : "+jwtToken );
return jwtToken ;
}
function getDataFromAPiByPassingJwtTokenInHeader(){
var jwtToken=readValue();
$.ajax({
url: 'https://url.com',
type: 'post',
data: {},
headers: {
Bearer Token: "Bearer "+jwtToken, //key word **Bearer** should pass before the token string
},
dataType: 'json',
success: function (data) {
console.info(data);
}
});
}
</script>
please try this way.

Spotify api add track to playlist

I am using ReactJS and trying to make simple site using the Spotify api.
I am also using the js package spotify-web-api-js. I have succeeded to get the current song playing and able to show it in the browser.
getNowPlaying(){
spotifyApi.getMyCurrentPlaybackState()
.then((response) => {
this.setState({
nowPlaying: {
name: response.item.name,
albumArt: response.item.album.images[0].url
}
});
})
}
//this is from the github link above
Constr.prototype.getMyCurrentPlaybackState = function(options, callback) {
var requestData = {
url: _baseUri + '/me/player'
};
return _checkParamsAndPerformRequest(requestData, options, callback);
};
But the problem I am facing is to add a track to my playlist. I have tried to use the function replaceTracksInPlaylist from spotify-web-api-js but I cant manage to get it to work, I always get the 403 Forbidden error.
addToPlayList(){
spotifyApi.addTracksToPlaylist(listID, listOfSongID, callback);
}
//this is from the github link above
Constr.prototype.replaceTracksInPlaylist = function(playlistId, uris, callback) {
var requestData = {
url: _baseUri + '/playlists/' + playlistId + '/tracks',
type: 'PUT',
postData: { uris: uris }
};
return _checkParamsAndPerformRequest(requestData, {}, callback);
};
The first example also requires authentication as a access token but why does it not work when adding to a playlist?
I have also changed the scopes to the correct ones that the api documentation says i should use, playlist-modify-public and playlist-modify-private
var scope = 'playlist-modify-public playlist-modify-private user-read-private user-read-email user-read-playback-state';
I can test the api call from the api documentation site and insert my oauth token, track and the playlist id there, and that works fine, I also get this back from the call. I cant think of anything else to try and need some help figuring out the next step.

Laravel 4 can't get data from Angular Ajax

I am trying to develop my application in Laravel 4 and Angular JS, my application allows user to retrieve their Information through the system via Text Change.
Angular is used to pass data input from the user to Laravel which in turn retrieves the Information from the Database.
However Laravel is unable to retrieve the data passed from Angular.
View
<div data-ng-controller="ReservationController">
<input id='ERI' type='text' data-ng-model="scanRID" data-ng-change="queryRes()" name='exampleInput' maxlength='3' />
</div>
Angular Factory
app.factory('exampleFactory', function($http) {
var factory = {};
factory.getExample = function(scanRID) {
return $http({
method: 'GET',
url: LARAVEL_CONTROLLER + 'Example',
data: $.param(scanRID)
});
};
return factory;
});
Angular Controller
app.controller('exampleController', function($scope, $http, exampleFactory) {
$scope.queryRes = function() {
if($scope.scanRID.length == 3) {
exampleFactory.getExample($scope.scanRID)
.success(function (data) {
// Do Something Here
})
.error(function (data) {
console.log(data);
});
}
};
});
Laravel 4 Routes
Route::get('Example', 'ExampleController#show');
Laravel 4 ExampleController
class ExampleController extends \BaseController {
public function show()
{
$id = Input::get('scanRID'); // This here might be wrong. It's always empty!
$data = ExampleModel::find($id); // Able to query and retrieve.
return Response::JSON($data); // Returns empty.
}
}
Laravel 4 ExampleModel
class ExampleModel extends Eloquent {
// The id of this table is what I want, but I can't retrieve it.
protected $fillable = ['id', 'ExampleData1', 'ExampleData2'];
protected $table = 'exampleTable';
}
I have searched everywhere for a solution, it seems that everyone is able to successfully make the Ajax call. I think there is something that I am missing out that I am unaware about.
I have also tried setting CSRF Token, but however, I do not think that is an issue. So my last resort is to turn to the experts and hope someone is able to help me.
On a side note, I am fairly new to Laravel and Angular, so if you do post a solution, please explain to me the issue as I would like to learn more about Angular and Laravel.
Thank you for reviewing my issue.
You are not passing the value of scanRID by scanRID parameter instead pass only the value without parameter. So you are try to get the value from scanRID using Input::get('scanRID'); but without having scanRID parameter. that should be the case ur not getting the value :)
return $http({
method: 'GET',
url: LARAVEL_CONTROLLER + 'Example',
data: $.param({scanRID:scanRID}) //Change Here
});
OR
return $http({
method: "GET",
url: LARAVEL_CONTROLLER + 'Example',
params: {scanRID:scanRID} //Change Here
);
change like this

Categories

Resources