I have successfully added rapidoreach ios app monetization SDK into my app. i am not very much aware about server to server callbacks..Any idea how it can be done with nodejs and express?
You can use request method which comes by default with node.js or by using axios library for server to server callback, but a quick check at rapidoreach site (https://www.rapidoreach.com/docs#/callbacks) leads to this doc for callback setup and looks like you want to receive callbacks, in that case you have to setup a api at your end to receive their server call, this is one example done in Nodejs
Example:
function rapidoreachPostback(req: Request, res: Response) {
var IP = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
var Input = req.query;
if (Input.status == 'C' || Input.status == "P" || Input.status == "F") {
if(Input.status === "C"){
//record your completion here
}
//if rapidoreach allows disaqualification points
if(Input.status === "P"){
//record your disqualification transaction over here
}
if(Input.status === "F"){
//record your survey offer failure transaction over here
}
}
} catch (error) {
// send "1" in response to server call from rapidoreach.
res.send("1");
return;
}
res.send("1");
return;
})
return;
}
res.send("1");
return;
}
Rapidoreach will send server to server callback using get request to the callback URL maintained in App setings.
Assuming Callback URL in app settings is set as
https://local.callback.com/callback.php
Below is an example of server to server callback. Details of parameters available in callback can be found in their official docs
https://www.rapidoreach.com/docs#/callbacks
https://local.callback.com/callback.php?cmd=P&userId=SYKUser-CvkBPlfibeV-aba04fe2fc72219fdbb9a9353a68713d&amt=0.01&offerInvitationId=trans0002&status=P&offerHash=4fdcc6461ec225ba8af3bc66ccf4017c¤cyAmt=0.80&transactionId=trans0002&endUserId=SYKUser&txnHash=d5fecc45d5be250ff757f8694a6d65a1&useragent=Rapidoreach¤cyName=Local Coins&offerType=1&deviceType=Desktop&intergrationMethod=IFRAME
Here is an example how you can handle callback using PHP.
File callback.php
<?php
/**
* ApplicationKey can be found in credentials tab of app created in Rapidoreach
*/
$ApplicationKey = "<Replace this>"; //<- replace this with real application key
/**
* $endUserId is unique endUserId in publisher's system.
*/
$EndUserId = $_REQUEST['endUserId'];
/**
* Unique offer Id
*/
$OfferInvitationId = $_REQUEST['offerInvitationId'];
/**
* TransactionId
*/
$TransactionId = $_REQUEST['transactionId'];
/**
* Status
* C: Completed - User has successfully completed an offer and should be rewarded with currencyAmt
* P: Attempted - User has attempted an offer and attempt is valid will be rewarded with currencyAmt
* currencyAmt will not be full in this case and will be equal to screenout reward maintained in App Setting of Rapidoreach Publisher portal
* F: Failed - User has failed to complete an offer and hence terminated. No rewards should be awarded
*/
$Status = $_REQUEST['status'];
/**
* Validate offerHash
* You should verify oidHash upon receipt of the callback by recomputing it with the callback offerInvitationId and your ApplicationKey.
* This will secure against users faking their own id and passing it in if by some chance they come across the script.
*/
$offerHash = $_REQUEST['offerHash'];
$CalculatedOfferHash = md5($OfferInvitationId . $ApplicationKey);
if ($offerHash != $CalculatedOfferHash) {
/**
* TODO: User is trying to manupulate offerId to get credits. Flag this user within this condition if required (optional).
* Do not credit this user and send success response to Rapidoreach
* Do not continue further
*/
echo "1";
die;
}
/**
* Validate txnHash
*/
$txnHash = $_REQUEST['txnHash'];
$CalculatedTxnHash = md5($TransactionId . $ApplicationKey);
if ($txnHash != $CalculatedTxnHash) {
/**
* TODO: User is trying to manupulate transaction id to get credits. Flag this user within this condition if required (optional).
* Do not credit this user and send success response to Rapidoreach
* Do not continue further
*/
echo "1";
die;
}
/**
* Credit the user based on offer status
*/
switch ($Status) {
case 'C':
# TODO: Credit the $EndUserId with $currencyAmt use $TransactionId to avoid duplicates
echo "1";
die;
break;
case 'P':
# TODO: Credit the $EndUserId with $currencyAmt use $TransactionId to avoid duplicates
echo "1";
die;
break;
case 'F':
# TODO: User has failed to complete an offer
echo "1";
die;
break;
default:
# code...
break;
}
Related
I have been trying to get the list of subscriptions of my channel but unfortunately I get errors every time I run my code, I am describing each step below:
Step 1: I created this channel: My YouTube Channel
Step 2: I enabled the YouTube Data API V3 in Google Developer Console
Step 3: I created API Key and Google OAuth 2.0 Client ID, you can see the following screenshot:
Step 4: I checked the YouTube API Reference and checked some parameters here and got a successful response with all the subscriptions of my channel: YouTube API Reference for my Channel
Step 5: I copied the following code from the YouTube API Reference and placed my own API Key and Google OAuth 2.0 Client ID after I got a successful response for my channel:
<script src="https://apis.google.com/js/api.js"></script>
<script>
/**
* Sample JavaScript code for youtube.subscriptions.list
* See instructions for running APIs Explorer code samples locally:
* https://developers.google.com/explorer-help/code-samples#javascript
*/
function authenticate() {
return gapi.auth2.getAuthInstance()
.signIn({scope: "https://www.googleapis.com/auth/youtube.readonly"})
.then(function() { console.log("Sign-in successful"); },
function(err) { console.error("Error signing in", err); });
}
function loadClient() {
gapi.client.setApiKey("YOUR_API_KEY");
return gapi.client.load("https://www.googleapis.com/discovery/v1/apis/youtube/v3/rest")
.then(function() { console.log("GAPI client loaded for API"); },
function(err) { console.error("Error loading GAPI client for API", err); });
}
// Make sure the client is loaded and sign-in is complete before calling this method.
function execute() {
return gapi.client.youtube.subscriptions.list({
"part": [
"subscriberSnippet,contentDetails"
],
"channelId": "UCLlE_JEV7I0pQ7fhY4BIrrQ"
})
.then(function(response) {
// Handle the results here (response.result has the parsed body).
console.log("Response", response);
},
function(err) { console.error("Execute error", err); });
}
gapi.load("client:auth2", function() {
gapi.auth2.init({client_id: "YOUR_CLIENT_ID"});
});
Step 6: Then I added the following buttons in my HTML code:
<button onclick="authenticate().then(loadClient)">authorize and load</button>
<button onclick="execute()">execute</button>
Step 7: When I execute the code, I get the following error messages:
Error 1:
"You have created a new client application that uses libraries for user authentication or
authorization that will soon be deprecated. New clients must use the new libraries instead;
existing clients must also migrate before these libraries are deprecated. See the [Migration
Guide](https://developers.google.com/identity/gsi/web/guides/gis-migration) for more
information."
Step 8: Then I click the authorize and load button and sign in to my channel and allow any requested rights. After that, when I click the execute button, then I get the following error:
"The requester is not allowed to access the requested subscriptions."
This is worth mentioning that this is my own channel, I login when required and I allow any rights that are requested. I also use my own Google Developer Console account, my own API Key and my own OAuth 2.0 Client ID. I have enabled the YouTube Data API V3 and I have set up everything properly. I can get the proper result from the YouTube API reference but I can't get it using JS.
Any help is appreciated in advance.
error one.
You have created a new client application that uses libraries for user authentication or
authorization that will soon be deprecated. New clients must use the new libraries instead;
existing clients must also migrate before these libraries are deprecated. See the Migration
Guide for more
information.
The code you are using is using the old google sign-in. You need to change this and use the new Google authorization library Authorizing for Web
Error two:
The requester is not allowed to access the requested subscriptions.
This error is a little harder to understand. First off when the authorization request pops up it should be asking you to pick a user and then a channel Make sure you select the channel that maps to the channel id you are selecting UCLlE_JEV7I0pQ7fhY4BIrrQ The YouTube data api is channel based you only have access to the single channel.
You are also using the "https://www.googleapis.com/auth/youtube.readonly" scope and the subscriptions list method documentation says it needs https://www.googleapis.com/youtube/v3/subscriptions which doesnt exist so I used https://www.googleapis.com/auth/youtube, but i would expect a different error message if this was the issue.
Notes:
I login when required and I allow any rights that are requested.
Make sure its to the correct channel.
I also use my own Google Developer Console account, my own API Key and my own OAuth 2.0 Client ID. I have enabled the YouTube Data API V3 and I have set up everything properly.
This has nothing to do with your access the client id, and api key just identify your application to Google they dont grant it access to anything. Thats what authorization is doing.
YouTube data api QuickStart for Authorizing for Web
Here is my QuickStart for this api.
<!DOCTYPE html>
<html>
<head>
<title>YouTube Data API Quickstart</title>
<meta charset="utf-8" />
</head>
<body>
<p>YouTube Data API Quickstart</p>
<!--Add buttons to initiate auth sequence and sign out-->
<button id="authorize_button" onclick="handleAuthClick()">Authorize</button>
<button id="signout_button" onclick="handleSignoutClick()">Sign Out</button>
<pre id="content" style="white-space: pre-wrap;"></pre>
<script type="text/javascript">
/* exported gapiLoaded */
/* exported gisLoaded */
/* exported handleAuthClick */
/* exported handleSignoutClick */
// TODO(developer): Set to client ID and API key from the Developer Console
const CLIENT_ID = '[Redacted]';
const API_KEY = '[Redacted]';
// Discovery doc URL for APIs used by the quickstart
const DISCOVERY_DOC = 'https://www.googleapis.com/discovery/v1/apis/youtube/v3/rest';
// Authorization scopes required by the API; multiple scopes can be
// included, separated by spaces.
const SCOPES = 'https://www.googleapis.com/auth/youtube';
let tokenClient;
let gapiInited = false;
let gisInited = false;
document.getElementById('authorize_button').style.visibility = 'hidden';
document.getElementById('signout_button').style.visibility = 'hidden';
/**
* Callback after api.js is loaded.
*/
function gapiLoaded() {
gapi.load('client', initializeGapiClient);
}
/**
* Callback after the API client is loaded. Loads the
* discovery doc to initialize the API.
*/
async function initializeGapiClient() {
await gapi.client.init({
apiKey: API_KEY,
discoveryDocs: [DISCOVERY_DOC],
});
gapiInited = true;
maybeEnableButtons();
}
/**
* Callback after Google Identity Services are loaded.
*/
function gisLoaded() {
tokenClient = google.accounts.oauth2.initTokenClient({
client_id: CLIENT_ID,
scope: SCOPES,
callback: '', // defined later
});
gisInited = true;
maybeEnableButtons();
}
/**
* Enables user interaction after all libraries are loaded.
*/
function maybeEnableButtons() {
if (gapiInited && gisInited) {
document.getElementById('authorize_button').style.visibility = 'visible';
}
}
/**
* Sign in the user upon button click.
*/
function handleAuthClick() {
tokenClient.callback = async (resp) => {
if (resp.error !== undefined) {
throw (resp);
}
document.getElementById('signout_button').style.visibility = 'visible';
document.getElementById('authorize_button').innerText = 'Refresh';
await listSubscriptions();
};
if (gapi.client.getToken() === null) {
// Prompt the user to select a Google Account and ask for consent to share their data
// when establishing a new session.
tokenClient.requestAccessToken({prompt: 'consent'});
} else {
// Skip display of account chooser and consent dialog for an existing session.
tokenClient.requestAccessToken({prompt: ''});
}
}
/**
* Sign out the user upon button click.
*/
function handleSignoutClick() {
const token = gapi.client.getToken();
if (token !== null) {
google.accounts.oauth2.revoke(token.access_token);
gapi.client.setToken('');
document.getElementById('content').innerText = '';
document.getElementById('authorize_button').innerText = 'Authorize';
document.getElementById('signout_button').style.visibility = 'hidden';
}
}
/**
* Print metadata for first 10 Albums.
*/
async function listSubscriptions() {
let response;
try {
response = await gapi.client.youtube.subscriptions.list({
'pageSize': 10,
'part' :[ "snippet,subscriberSnippet,contentDetails" ],
"channelId": "UCyqzvMN8newXIxyYIkFzPvA",
'fields': 'items(id,snippet(title))',
});
} catch (err) {
document.getElementById('content').innerText = err.message;
return;
}
const subscriptions = response.result.items;
if (!subscriptions || subscriptions.length == 0) {
document.getElementById('content').innerText = 'No subscriptions found.';
return;
}
// Flatten to string to display
const output = subscriptions.reduce(
(str, subscription) => `${str}${subscription.snippet.title} (${subscription.id}\n`,
'albums:\n');
document.getElementById('content').innerText = output;
}
</script>
<script async defer src="https://apis.google.com/js/api.js" onload="gapiLoaded()"></script>
<script async defer src="https://accounts.google.com/gsi/client" onload="gisLoaded()"></script>
</body>
</html>
I'm implementing apple-authentication in react native using expo-apple-authentication package.
Below is the code which I'm calling on button's onPress.
async handleSocialLogin() {
const { mutate, BB, onSuccess, navigation } = this.props;
try {
const result = await AppleAuthentication.signInAsync({
requestedScopes: [
AppleAuthentication.AppleAuthenticationScope.FULL_NAME,
AppleAuthentication.AppleAuthenticationScope.EMAIL,
],
});
Alert.alert(JSON.stringify(result))
// signed in
} catch (e) {
Alert.alert(e)
if (e.code === 'ERR_CANCELED') {
// handle that the user canceled the sign-in flow
} else {
// handle other errors
}
}
}
It should return me authentication-token, Full_Name and Email which I requested in scope but It is giving me null for Full_Name and Email.
As per the documentation:
requestedScopes (AppleAuthenticationScope[]) (optional) - Array of user information scopes to which your app is requesting access. Note that the user can choose to deny your app access to any scope at the time of logging in. You will still need to handle null values for any scopes you request. Additionally, note that the requested scopes will only be provided to you the first time each user signs into your app; in subsequent requests they will be null.
You have probably already logged in once and didn't catch the logs. Subsequent log in will result in this data being null
I am new in google sheets v4 and I just want to know how can I update my google sheet in v4. I am using Nodejs and following is the code sample link which I am using Method: spreadsheets.values.update
You can use the sample script of the link. In you case, combining Quickstart and the sample script may be useful for you. The sample script is as follows.
In this sample script, the text of sample text is imported to the cell a1 of Sheet1.
Sample script :
var fs = require('fs');
var readline = require('readline');
var google = require('googleapis');
var googleAuth = require('google-auth-library');
// If modifying these scopes, delete your previously saved credentials
// at ~/.credentials/sheets.googleapis.com-nodejs-quickstart.json
var SCOPES = ['https://www.googleapis.com/auth/spreadsheets', 'https://www.googleapis.com/auth/drive'];
var TOKEN_DIR = (process.env.HOME || process.env.HOMEPATH ||
process.env.USERPROFILE) + '/.credentials/';
var TOKEN_PATH = TOKEN_DIR + 'sheets.googleapis.com-nodejs-quickstart.json';
// Load client secrets from a local file.
fs.readFile('client_secret.json', function processClientSecrets(err, content) {
if (err) {
console.log('Error loading client secret file: ' + err);
return;
}
// Authorize a client with the loaded credentials, then call the
// Google Sheets API.
authorize(JSON.parse(content), valuesUpdate);
});
/**
* Create an OAuth2 client with the given credentials, and then execute the
* given callback function.
*
* #param {Object} credentials The authorization client credentials.
* #param {function} callback The callback to call with the authorized client.
*/
function authorize(credentials, callback) {
var clientSecret = credentials.installed.client_secret;
var clientId = credentials.installed.client_id;
var redirectUrl = credentials.installed.redirect_uris[0];
var auth = new googleAuth();
var oauth2Client = new auth.OAuth2(clientId, clientSecret, redirectUrl);
// Check if we have previously stored a token.
fs.readFile(TOKEN_PATH, function(err, token) {
if (err) {
getNewToken(oauth2Client, callback);
} else {
oauth2Client.credentials = JSON.parse(token);
callback(oauth2Client);
}
});
}
/**
* Get and store new token after prompting for user authorization, and then
* execute the given callback with the authorized OAuth2 client.
*
* #param {google.auth.OAuth2} oauth2Client The OAuth2 client to get token for.
* #param {getEventsCallback} callback The callback to call with the authorized
* client.
*/
function getNewToken(oauth2Client, callback) {
var authUrl = oauth2Client.generateAuthUrl({
access_type: 'offline',
scope: SCOPES
});
console.log('Authorize this app by visiting this url: ', authUrl);
var rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.question('Enter the code from that page here: ', function(code) {
rl.close();
oauth2Client.getToken(code, function(err, token) {
if (err) {
console.log('Error while trying to retrieve access token', err);
return;
}
oauth2Client.credentials = token;
storeToken(token);
callback(oauth2Client);
});
});
}
/**
* Store token to disk be used in later program executions.
*
* #param {Object} token The token to store to disk.
*/
function storeToken(token) {
try {
fs.mkdirSync(TOKEN_DIR);
} catch (err) {
if (err.code != 'EEXIST') {
throw err;
}
}
fs.writeFile(TOKEN_PATH, JSON.stringify(token));
console.log('Token stored to ' + TOKEN_PATH);
}
function valuesUpdate(auth) {
var sheets = google.sheets('v4');
var request = {
// The ID of the spreadsheet to update.
spreadsheetId: 'my-spreadsheet-id', // TODO: Update placeholder value.
// The A1 notation of the values to update.
range: 'Sheet1!a1:a1', // TODO: Update placeholder value.
// How the input data should be interpreted.
valueInputOption: 'RAW', // TODO: Update placeholder value.
resource: {'values': [['sample text']]},
auth: auth,
};
sheets.spreadsheets.values.update(request, function(err, response) {
if (err) {
console.error(err);
return;
}
// TODO: Change code below to process the `response` object:
console.log(JSON.stringify(response, null, 2));
});
}
IMPORTANT :
Please modify my-spreadsheet-id to yours in above script.
This sample script supposes that the script of Quickstart works fine.
After run the script of Quickstart, please remove sheets.googleapis.com-nodejs-quickstart.json once, before run the above sample script. After remove the file, please run the above script. By this, the refresh token with the new scopes is retrieved and it is used for updating values.
If you want to use this script, please use googleapis v24 or less. Because the latest version doesn't work. Because the following error occurs, even if valueInputOption is set.
Error: 'valueInputOption' is required but not specified
I believe that this error will be modified in the near future.
If I misunderstand your question, I'm sorry.
I am following GitHub code on how to implement push notification based on realtime database triggers.
Here is the code and the link:
https://github.com/firebase/functions-samples/blob/master/fcm-notifications/functions/index.js
/**
* Copyright 2016 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
/**
* Triggers when a user gets a new follower and sends a notification.
*
* Followers add a flag to `/followers/{followedUid}/{followerUid}`.
* Users save their device notification tokens to `/users/{followedUid}/notificationTokens/{notificationToken}`.
*/
exports.sendFollowerNotification = functions.database.ref('/followers/{followedUid}/{followerUid}').onWrite(event => {
const followerUid = event.params.followerUid;
const followedUid = event.params.followedUid;
// If un-follow we exit the function.
if (!event.data.val()) {
return console.log('User ', followerUid, 'un-followed user', followedUid);
}
console.log('We have a new follower UID:', followerUid, 'for user:', followerUid);
// Get the list of device notification tokens.
const getDeviceTokensPromise = admin.database().ref(`/users/${followedUid}/notificationTokens`).once('value');
// Get the follower profile.
const getFollowerProfilePromise = admin.auth().getUser(followerUid);
return Promise.all([getDeviceTokensPromise, getFollowerProfilePromise]).then(results => {
const tokensSnapshot = results[0];
const follower = results[1];
// Check if there are any device tokens.
if (!tokensSnapshot.hasChildren()) {
return console.log('There are no notification tokens to send to.');
}
console.log('There are', tokensSnapshot.numChildren(), 'tokens to send notifications to.');
console.log('Fetched follower profile', follower);
// Notification details.
const payload = {
notification: {
title: 'You have a new follower!',
body: `${follower.displayName} is now following you.`,
icon: follower.photoURL
}
};
// Listing all tokens.
const tokens = Object.keys(tokensSnapshot.val());
// Send notifications to all tokens.
return admin.messaging().sendToDevice(tokens, payload).then(response => {
// For each message check if there was an error.
const tokensToRemove = [];
response.results.forEach((result, index) => {
const error = result.error;
if (error) {
console.error('Failure sending notification to', tokens[index], error);
// Cleanup the tokens who are not registered anymore.
if (error.code === 'messaging/invalid-registration-token' ||
error.code === 'messaging/registration-token-not-registered') {
tokensToRemove.push(tokensSnapshot.ref.child(tokens[index]).remove());
}
}
});
return Promise.all(tokensToRemove);
});
});
});
My silly question, new to Functions and Node, is in this code notifications are sent to all users who tokens are saved, is that correct? and if it is how can I let's say send just to one particular person instead all?
I was thinking of saving token of each user in different nodes (children) so I can pick the one I want to send notification to. Does it work?
Thanks All
This code will send notification to just one user (follower in this example). This user can have multiple tokens, representing multiple devices, and hence the variable name: tokensSnapshot.
What you intend to do is very doable with Cloud Functions. You just have to be careful with paths of your nodes where you save users, or tokens, for instance. Also as Frank van Puffelen suggested, having some acquaintance with Admin SDK (Realtime Database and FCM) will really help you out.
I'm creating a Swift app using Backendless. The app automatically creates the user's username attribute, though this may be changed in the future. Either way, how can I get Backendless to prevent a user from registering if they have the same username as another?
I have added an event handler in Backendless (Business Logic section) to run before allowing a user to register:
/* global Backendless */
/**
* #param {Object} req The request object contains information about the request
* #param {Object} req.context The execution context contains an information about application, current user and event
* #param {Object} req.user
*/
Backendless.ServerCode.User.beforeRegister(function(req) {
//add your code here
}, true);
How can I set it up to check if a username value is the same as an existing one and, if so, throw an error?
Here is the query to know whether user exists or not
let query = BackendlessDataQuery()
let userEmail = "foo#foo.com"
query.whereClause = "email = '\(userEmail)'"
let users = backendless.data.of(BackendlessUser.ofClass()).find(query)
if( users.data.count > 0 )
{
// user exists
}
referenced from Here
You may set a UNIQUE constraint on the username column. This way you'll receive an error in your app, which you'll we able to handle as you wish.