StripeInvalidRequestError: No such setupintent: 'seti_...' - node.js

After a user registers on my app I want them to add a payment method. Their stripe customer account is created as soon as they register and from there they are transferred to the 'AddPaymentMethod' screen. As soon as the ' AddPaymentMethod' screen appears, I send a request to my server to create a setupIntent.
Creating Setup Intent:
exports.createSetupIntent = functions.https.onCall(async (data, context) => {
const userId = data.userId;
const snapshot = await db
.collection("development")
.doc("development")
.collection("users")
.doc(userId).get();
const customerId = snapshot.data().customer_id;
const setupIntent = await stripe.setupIntents.create({
customer: customerId,
});
const clientSecret = setupIntent.client_secret;
return {
clientsecret: clientSecret,
};
});
Calling the function when the screen appears on my client (This successfully creates the client secret key and stores it in a variable in the frontend):
FirebaseReferenceManager.functions.httpsCallable("createSetupIntent").call(["userId": Auth.auth().currentUser?.uid]) { (response, error) in
if let error = error {
print(error.localizedDescription)
}
if let response = (response?.data as? [String: Any]) {
let clientSecretKey = response["clientsecret"] as! String?
self.clientSecret = clientSecretKey ?? "-"
print("created client secret key: \(clientSecretKey!)")
}
}
Next, the user enters their credit card information and creates a payment method. Here is the function on my server:
exports.createPaymentMethod = functions.https.onCall(async (data, context) => {
const number = data.number;
const expMonth = data.expMonth;
const expYear = data.expYear;
const cvc = data.cvc;
const paymentMethod = await stripe.paymentMethods.create({
type: "card",
card: {
number: number,
exp_month: expMonth,
exp_year: expYear,
cvc: cvc,
},
});
const pmId = paymentMethod.id;
return {
paymentMethodId: pmId,
};
});
I call this function from the frontend when the user presses the "Save payment method" button. This successfully creates a payment method and returns the payment method id which is stored in a variable on the front end.
Lastly, using the client secret id and payment method id that was returned from the previous functions, I call the last function to confirm the setupIntent.
This function is called when a payment method is created successfully:
exports.confirmSetupIntent = functions.https.onCall(async (data, context) => {
const clientSecretKey = data.clientSecretKey;
const paymentMethodId = data.paymentMethodId;
const setupIntent = await stripe.setupIntents.confirm(
clientSecretKey,
{payment_method: paymentMethodId}
);
});
This is how the createPaymentMethod and confirmSetupIntent functions are called from the frontend:
FirebaseReferenceManager.functions.httpsCallable("createPaymentMethod").call(["number": self.cardNumber, "expMonth": self.expMonth, "expYear": "20\(self.expYear)", "cvc": self.cvvCode]) { (response, error) in
if let error = error {
print("error occured when creating payment method: \(error.localizedDescription)")
}
if let response = response?.data as? [String: Any] {
let paymentMethodId = response["paymentMethodId"] as! String?
self.paymentMethodID = paymentMethodId ?? "-"
print(paymentMethodId!)
FirebaseReferenceManager.functions.httpsCallable("confirmSetupIntent").call(["clientSecretKey": self.clientSecret, "paymentMethodId": self.paymentMethodID]) { (response, error) in
if let error = error {
print("error occured when confirming setup intent: \(error.localizedDescription)")
}
print("setup intent confirmed")
}
}
}
In the debug console on the frontend it says that the error from confirming the setupIntent was INTERNAL. When I check the logs on my server I it says:
StripeInvalidRequestError: No such setupintent: 'seti_...'
Note that I am using SwiftUI and custom screens/textfields for the stripe integration.
Any help is appreciated!

The No such setupintent error indicates you have a mismatch in your API keys, and you should double check that your server secret key and client publishable are a matched pair for the same account and both for test mode, eg.
Of greater concern is that you appear to be passing payment details to your server to create the payment method. This is not recommended, and has significant PCI Compliance implications. Instead of creating the payment method like this on your server, you should use Elements and provide a reference to the Card Element when you use confirmCardSetup (docs):
stripe.confirmCardSetup(
clientSecret,
{
payment_method: {
card: cardElement,
},
}
)

Related

Unable to use 'array-contains' where clause in cloud function

I am working on a job bidding app.
Each user has a field "User job notifications preferences".
The array field stores the data to which type of job they would like to receive notifications for.
for example:
Person A has the setting to receive a notification when a job of type 'plumming' is created.
Person B has the setting to receive a notification when a job of type 'electrical' is created.
Person C creates a plumming job,
Peron A should receive a notification to let them know a new job of type 'plumming' has been created.
here is the code snip
// when a job is updated from new to open
// send notifications to the users that signed up for that jobtype notification
exports.onJobUpdateFromNewToOpen= functions.firestore
.document('job/{docId}')
.onUpdate(async (change, eventContext) => {
const beforeSnapData = change.before.data();
const afterSnapData = change.after.data();
const jobType = afterSnapData['Job type'];
const afterJobState = afterSnapData["Job state"];
const beforeJobState = beforeSnapData["Job state"];
console.log('job updated');
// only consider jobs switching from new to open
if (beforeJobState=="New" && afterJobState == "Open") {
console.log('job updated from new to open');
console.log('jobType: '+jobType);
console.log('job id: '+change.after.id )
// get users that contain the matching job type
const usersWithJobTypePreferenceList = await admin.firestore().collection("user").where("User job notifications preferences", "array-contains-any", jobType).get();
// get their userIds
const userIdsList = [];
usersWithJobTypePreferenceList.forEach((doc) => {
const userId = doc.data()["User id"];
userIdsList.push(userId);
})
// get their user tokens
const userTokenList = [];
for (var user in userIdsList) {
const userId = userIdsList[user];
const userToken = await (await admin.firestore().collection("user token").doc(userId).get()).data()["token"];
userTokenList.push(userToken);
};
// send message
const messageTitle = "new " + jobType + " has been created";
for (var token in userTokenList) {
var userToken = userTokenList[token];
const payload = {
notification: {
title: messageTitle,
body: messageTitle,
sound: "default",
},
data: {
click_action: "FLUTTER_NOTIFICATION_CLICK",
message: "Sample Push Message",
},
};
return await admin.messaging().sendToDevice(receiverToken, payload);
}
}
});
I think the issue is at the following line because I am getting the error 'Error: 3 INVALID_ARGUMENT: 'ARRAY_CONTAINS_ANY' requires an ArrayValue' (see image)
const usersWithJobTypePreferenceList = await admin.firestore().collection("user").where("User job notifications preferences", "array-contains-any", jobType).get();
below is the full error:
Error: 3 INVALID_ARGUMENT: 'ARRAY_CONTAINS_ANY' requires an ArrayValue.
at Object.callErrorFromStatus (/workspace/node_modules/#grpc/grpc-js/build/src/call.js:31:19)
at Object.onReceiveStatus (/workspace/node_modules/#grpc/grpc-js/build/src/client.js:352:49)
at Object.onReceiveStatus (/workspace/node_modules/#grpc/grpc-js/build/src/client-interceptors.js:328:181)
at /workspace/node_modules/#grpc/grpc-js/build/src/call-stream.js:188:78
at processTicksAndRejections (node:internal/process/task_queues:78:11)
I interpret the error as the following: there is no value being passed to 'jobType'.but that cant be right because I am printing the value ( see screenshot )
I found the following related questions but I dont think I am having the same issue:
Getting firestore data from a Google Cloud Function with array-contains-any
Firestore: Multiple 'array-contains'
So I am not sure what the issue is here, any ideas?
here is how the data looks in firebase:
I looked at similar questions and I printed the values being passed to the function that was creating the error
I updated the line that was giving me an issue now everything works :) ::
'''
const usersWithJobTypePreferenceList = await admin.firestore().collection("user").where("User job notifications preferences", "array-contains", jobType).get();
'''

How to send already minted NFT using alchemy

I have minted some NFTs on opensea. These are on Polygon Mumbai network. Now I want to transfer these to token to other addresses using alchemy web3. Here is the code I am using.
Note: This is supposed to run in nodejs restful API, so there is no wallet available that why I am manually signing the transaction.
async function main() {
require('dotenv').config();
const { API_URL,API_URL_TEST, PRIVATE_KEY } = process.env;
const { createAlchemyWeb3 } = require("#alch/alchemy-web3");
const web3 = createAlchemyWeb3(API_URL_TEST);
const myAddress = '*************************'
const nonce = await web3.eth.getTransactionCount(myAddress, 'latest');
const transaction = { //I believe transaction object is not correct, and I dont know what to put here
'asset': {
'tokenId': '******************************',//NFT token id in opensea
},
'gas': 53000,
'to': '***********************', //metamask address of the user which I want to send the NFT
'quantity': 1,
'nonce': nonce,
}
const signedTx = await web3.eth.accounts.signTransaction(transaction, PRIVATE_KEY);
web3.eth.sendSignedTransaction(signedTx.rawTransaction, function(error, hash) {
if (!error) {
console.log("🎉 The hash of your transaction is: ", hash, "\n Check Alchemy's Mempool to view the status of your transaction!");
} else {
console.log("âť—Something went wrong while submitting your transaction:", error)
}
});
}
main();
Assumed that you have Metamask installed in your browser, and that the NFT smart contract follows ERC721 Standard
const { API_URL,API_URL_TEST, PRIVATE_KEY } = process.env;
const { createAlchemyWeb3 } = require("#alch/alchemy-web3");
const {abi} = YOUR_CONTRACT_ABI
const contract_address = CONTRACT ADDRESS
require('dotenv').config();
async function main() {
const web3 = createAlchemyWeb3(API_URL_TEST);
web3.eth.getAccounts().then(accounts => {
const account = account[0]
const nameContract = web3.eth.Contract(abi, contract_address);
nameContract.methods.transfer(account, ADDRESS_OF_WALLET_YOU_WANT_TO_SEND_TO, TOKEN_ID).send();
})
.catch(e => console.log(e));
}
main();
Had the same problem, because there is no example on transferring NFT token.
There is a well explained 3-parts-example on ethereum website to mint an NFT.
In the first part, step 10, it explains how to write a contract and also mentions the existing methods in the contract object extended:
After our import statements, we have our custom NFT smart contract, which is surprisingly short — it only contains a counter, a constructor, and single function! This is thanks to our inherited OpenZeppelin contracts, which implement most of the methods we need to create an NFT, such as ownerOf which returns the owner of the NFT, and transferFrom, which transfers ownership of the NFT from one account to another.
So, with these informations, I made an NFT transfer transaction between two addresses with my metamask mobile app. Then I searched the JSON of this transaction through etherscan API.
In this way, I was able to transfer tokens to other addresses using alchemy web3 with this script:
require("dotenv").config()
const API_URL = process.env.API_URL; //the alchemy app url
const PUBLIC_KEY = process.env.PUBLIC_KEY; //my metamask public key
const PRIVATE_KEY = process.env.PRIVATE_KEY;//my metamask private key
const {createAlchemyWeb3} = require("#alch/alchemy-web3")
const web3 = createAlchemyWeb3(API_URL)
const contract = require("../artifacts/contracts/MyNFT.sol/MyNFT.json")//this is the contract created from ethereum example site
const contractAddress = "" // put here the contract address
const nftContract = new web3.eth.Contract(contract.abi, contractAddress)
/**
*
* #param tokenID the token id we want to exchange
* #param to the metamask address will own the NFT
* #returns {Promise<void>}
*/
async function exchange(tokenID, to) {
const nonce = await web3.eth.getTransactionCount(PUBLIC_KEY, 'latest');
//the transaction
const tx = {
'from': PUBLIC_KEY,
'to': contractAddress,
'nonce': nonce,
'gas': 500000,
'input': nftContract.methods.safeTransferFrom(PUBLIC_KEY, to, tokenID).encodeABI() //I could use also transferFrom
};
const signPromise = web3.eth.accounts.signTransaction(tx, PRIVATE_KEY)
signPromise
.then((signedTx) => {
web3.eth.sendSignedTransaction(
signedTx.rawTransaction,
function (err, hash) {
if (!err) {
console.log(
"The hash of your transaction is: ",
hash,
"\nCheck Alchemy's Mempool to view the status of your transaction!"
)
} else {
console.log(
"Something went wrong when submitting your transaction:",
err
)
}
}
)
})
.catch((err) => {
console.log(" Promise failed:", err)
})
}
I Had the same problem. I need to transfer NFT in node.js back-end.
I use network provider to use Moralis NetworkWeb3Connector.
here's my repository for example:
https://github.com/HanJaeJoon/Web3API/blob/2e30e89e38b7b1f947f4977a0fe613c882099fbc/views/index.ejs#L259-L275
await Moralis.start({
serverUrl,
appId,
masterKey,
});
await Moralis.enableWeb3({
// rinkeby
chainId: 0x4,
privateKey: process.env.PRIVATE_KEY,
provider: 'network',
speedyNodeApiKey: process.env.MORALIS_SPEEDY_NODE_API_KEY,
});
const options = {
type,
receiver,
contractAddress,
tokenId,
amount: 1,
};
try {
await Moralis.transfer(options);
} catch (error) {
console.log(error);
}
you can get speed node api key in
Moralis dashboad > Networks > Eth Rinkeby(in my case) > Settings
screenshot

How do I trigger LUIS in all dialogs retaining the previous context?

I am developing a bot with Microsoft Bot Framework, LUIS and node.js .This bot is based on Microsoft Teams and I am using TeamsBotSsoPrompt and Microsoft-Graph for authentication of the user.
The authentication step in the AUTH_DIALOG is as follows:
async showUserInfo(stepContext) {
const tokenResponse = stepContext.result;
if (tokenResponse) {
try {
// Call Microsoft Graph on behalf of user
const oboCredential = new OnBehalfOfUserCredential(tokenResponse.ssoToken);
//console.log("oboCredential> " + JSON.stringify(oboCredential));
const graphClient = createMicrosoftGraphClient(oboCredential, this.requiredScopes);
const me = await graphClient.api("/me").get();
const lanID = await graphClient.api("/me/onPremisesSamAccountName").get();
if (me) {
var authHeaders = {
"user": me.userPrincipalName,
"token": tokenResponse.token,
header1: null,
header2: null,
header3: null,
}
userName = me.givenName;
authHeaders['name'] = userName;
await stepContext.context.sendActivity({ type: ActivityTypes.Typing });
await stepContext.context.sendActivity(`Hello! <b>` + userName + `</b>`);
await stepContext.context.sendActivity({ type: ActivityTypes.Typing });
await stepContext.context.sendActivity("Let's get started.How can I help you?");
return await stepContext.beginDialog(FLOW_DIALOG, authHeaders);
} else {
await stepContext.context.sendActivity({ type: ActivityTypes.Typing });
await stepContext.context.sendActivity("Getting profile from Microsoft Graph failed! ");
return await stepContext.endDialog();
}
}
catch (error) {
// error logged here
}
}
else {
await stepContext.context.sendActivity({ type: ActivityTypes.Typing });
await stepContext.context.sendActivity("Login was not successful please try again.");
return await stepContext.endDialog();
}
}
The FLOW_DIALOG is where I have LUIS enabled.From the FLOW_DIALOG, I redirect the bot to other dialogs based on the Intent.But when I am in that other dialog and if I enter something in the bot, the current dialog ends and returns the flow to the FLOW_DIALOG. I want all the dialogs to trigger LUIS and move the flow to the correct Intent Dialog.
The FLOW_DIALOG is as follows:
async introStep(stepContext) {
const ActionCard = utility.createSuggestedActions(suggestionList, suggestionTitle);
await stepContext.context.sendActivity({ type: ActivityTypes.Typing });
await stepContext.context.sendActivity({ attachments: [ActionCard] });
return { status: DialogTurnStatus.waiting };
}
async actStep(stepContext) {
const luisRecognizer = new LuisRecogniser(luisConfig, stepContext.dialogs.telemetryClient);
let authHeaders = stepContext.options;
if (!luisRecognizer.isConfigured) {
return await stepContext.context.sendActivity("Something went wrong. Please Contact Your Administrator");
}
const luisResult = await luisRecognizer.executeLuisQuery(stepContext.result ? stepContext.result : stepContext.context);
topIntent = LuisRecognizer.topIntent(luisResult);
switch (topIntent) {
case "Intent1": {
authHeaders['header1'] = luisResult.entities;
return await stepContext.beginDialog(DIALOG_A, authHeaders);
}
case "Intent2": {
authHeaders['header2'] = luisResult.entities;
return await stepContext.beginDialog(DIALOG_B, authHeaders);
}
// ... and other intents in similar way
}
}
Now, I did try adding the LUIS to the onMessage call but I couldn't access the context.options to set or reset the authHeaders that I have. I know the context there is DialogContext and the one we get access to is TurnContext.
So is it possible to alter the headers values and pass it along the dialog calls and also trigger LUIS for every message the user enters from whichever Dialog the user currently has access to.
Having LUIS integrated within Waterfall dialogs seems not supported. It worked fine in case of TextPrompt but I find no way to do that when we have 'ChoicePrompt' or 'ConfirmPrompt'.
We can have LUIS integrated to decide which dialog to start and for the same sample is also available - Core bot.
You can also refer this link to have idea on how to use dialogs in bot - https://learn.microsoft.com/en-us/azure/bot-service/bot-builder-concept-waterfall-dialogs?view=azure-bot-service-4.0#using-dialogs

Cloud functions for Firebase FCM notifications to multiple users

I am using nodeJS with firebase for my flutter/firebase mobile app
I would like to send notifications to all users that have a certain query met. ie all users who have radiology as their specialty. So that they will be notified when a new article is added to the database
However I am unsure why my code (below) doesn't work to get notification tokens for all users with this query.
My database structure is users/notificationTokens/Ids of all tokens for that user stored in field 'token'
exports.sendToDevice5 = functions.firestore
.document('Articles/{paper}')
.onCreate(async (snapshot, context) => {
const paper = context.params.paper;
const item = snapshot.data();
if (item.subspecialty == "RadMSK" || item.subspecialty == "RadMS") {
const tokens = await admin.firestore().collection('users').where("specialty", "==", "RADIOLOGY").get().then(
snapshot.forEach((doc) => {
const docs = admin.firestore().collection('users').doc(doc.id).collection('notificationTokens').get();
return docs.data().token;
}));
const payload = {
notification: {
title: `${item.title}!`,
body: `New Journal`,
sound: "default",
},
data: {click_action: 'FLUTTER_NOTIFICATION_CLICK'},
};
return admin.messaging().sendToDevice(tokens, payload);
}
});

Unable to update an item in CosmosDB using the replace method with JavaScript

I am trying to create a basic REST API using Azure functions and the cosmosDB client for JavaScript. I have been successful with all the actions except the UPDATE. The cosmosDB client uses conainter.item(id,category).replace(newObject) I am unable to get the container.item().replace method to work. When I test the function in the portal or using Postman, I get a 500 error and in the portal, I get the error: Result: Failure Exception: Error: invalid input: input is not string Stack: Error: invalid input: input is not string at trimSlashFromLeftAndRight.
Example of my basic document/item properties
{
id:002,
project:"Skip rope",
category:"task",
completed: false
}
const config = require("../sharedCode/config");
const { CosmosClient } = require("#azure/cosmos");
module.exports = async function (context, req) {
const endpoint = config.endpoint;
const key = config.key;
const client = new CosmosClient({ endpoint, key });
const database = client.database(config.databaseId);
const container = database.container(config.containerId);
const theId = req.params.id;
// I am retrieving the document/item that I want to update
const { resource: docToUpdate } = await container.item(theId).read();
// I am pulling the id and category properties from the retrieved document/item
// they are used as part of the replace method
const { id, category } = docToUpdate;
// I am updating the project property of the docToUpdate document/item
docToUpdate.project = "Go fly a kite";
// I am replacing the item referred to with the ID with the updated docToUpdate object
const { resource: updatedItem } = await container
.item(id, category)
.replace(docToUpdate);
const responseMessage = {
status: 200,
message: res.message,
data: updatedItem,
};
context.res = {
// status: 200, /* Defaults to 200 */
body: responseMessage,
};
};
I Googled the heck out of this and been through the Microsoft Azure CosmosDB documents from top-to-bottom, but I can't figure out how to get this to work. I can get the other CRUD operations to work based on the examples Microsoft docs provide, but not this. Any help would be greatly appreciated.
I believe the reason you’re getting this error is because the data type of your “id” field is numeric. The data type of “id” field should be string.
UPDATE
So I tried your code and was able to run it successfully. There was one issue I noticed in your code though:
const { resource: docToUpdate } = await container.item(theId).read();
In the above line of code, you are not specifying the partition key value. If you don't specify the value, then your docToUpdate would come as undefined. In my code I used task as partition key value (I created a container with /category as the partition key).
This is the code I wrote:
const { CosmosClient } = require("#azure/cosmos");
const endpoint = 'https://account.documents.azure.com:443/';
const key = 'accountkey==';
const databaseId = 'database-name';
const containerId = 'container-name';
// const docToUpdate = {
// 'id':'e067cbae-1700-4016-bc56-eb609fa8189f',
// 'project':"Skip rope",
// 'category':"task",
// 'completed': false
// };
async function readAndUpdateDocument() {
const client = new CosmosClient({ endpoint, key });
const database = client.database(databaseId);
const container = database.container(containerId);
const theId = 'e067cbae-1700-4016-bc56-eb609fa8189f';
const { resource: docToUpdate } = await container.item(theId, 'task').read();
console.log(docToUpdate);
console.log('==============================');
const { id, category } = docToUpdate;
docToUpdate.project = "Go fly a kite";
console.log(docToUpdate);
console.log('==============================');
const { resource: updatedItem } = await container
.item(id, category)
.replace(docToUpdate);
console.log(updatedItem);
console.log('==============================');
}
readAndUpdateDocument();
Can you try by using this code?

Resources