Stripe Payments Add Payment Source / Card / Method using NodeJS / Vue - javascript

I am building a simple SaaS application with recurring payments using NodeJS with Express for the API and Vue for the UI. I have code written to add a customer and link a subscription and plan as well as a few other routines. We allow users to sign up without entering a payment method so now, I need to add a way for a user to add a payment method. I have been through so much documentation that my head is spinning and Stripe support (or lack thereof) has been no help.
I have tried everything from createSource, createToken, and createPaymentMethod in the UI and then submitted that to the API where I have tried using everything from stripeapi.customers.createSource to stripe.paymentMethods.create and nothing works. Everything returns an error about either something missing in the object or the object being incorrect. I have attempted to look at the payment intents API however, this seems like overkill to just simply add a card to a customer.
Here is my latest code.
UI : Create Element
this.stripe = await loadStripe('pk_test_');
let stripeElem = this.stripe.elements();
this.card = stripeElem.create('card', { hideIcon: true, hidePostalCode: false, style: { base: { color: '#363636', fontSize: '22px', fontSmoothing: 'antialiased' }}});
this.card.mount(this.$refs.card);
UI: Submit to API
await this.stripe.createSource(this.card, { type: 'card' } ).then((source) => {
this.$http.post(`/api/route`, source).then((response) => {
if (response.status === 200) {
} else {
}
}).catch(() => {
});
API
await stripeapi.customers.createSource(customer_id, { source: card });
This code produces this object:
{ source:
{ id: 'src_1HLFsEDfvqoM1TxYXmFvlcK9',
object: 'source',
amount: null,
card:
{ exp_month: 1,
exp_year: 2022,
last4: '4242',
country: 'US',
brand: 'Visa',
address_zip_check: 'unchecked',
cvc_check: 'unchecked',
funding: 'credit',
three_d_secure: 'optional',
name: null,
address_line1_check: null,
tokenization_method: null,
dynamic_last4: null },
client_secret: 'src_client_secret_VILuqM6ZikLzp9nMq4gizfN8',
created: 1598653002,
currency: null,
flow: 'none',
livemode: false,
metadata: {},
owner:
{ address: [Object],
email: null,
name: null,
phone: null,
verified_address: null,
verified_email: null,
verified_name: null,
verified_phone: null },
statement_descriptor: null,
status: 'chargeable',
type: 'card',
usage: 'reusable' } }
This code and object produce this error:
(node:352976) UnhandledPromiseRejectionWarning: Error: The source hash must include an 'object' key indicating what type of source to create.
at Function.generate (/data/api/node_modules/stripe/lib/Error.js:39:16)
at IncomingMessage.res.once (/data/api/docroot/node_modules/stripe/lib/StripeResource.js:190:33)
at Object.onceWrapper (events.js:286:20)
at IncomingMessage.emit (events.js:203:15)
at IncomingMessage.EventEmitter.emit (domain.js:448:20)
at endReadableNT (_stream_readable.js:1145:12)
at process._tickCallback (internal/process/next_tick.js:63:19)
All I want to do is take an element, create a payment source/method (whatever it's called) and then associate that with a customer. Any help is appreciated. I have look at so many examples but nothing has worked for me. Everything seems to produce an error about the object or what not.

After more hours of development I finally figured it out! The API reference is severely lacking but this article here explains what to do: https://stripe.com/docs/payments/save-card-without-authentication
Essentially, you create and mount the element. Then, you use the createPaymentMethod in the UI and pass the card element to it. From there, you submit the paymentMethod.id string to your API and then use strip.paymentMethods.attach to attach it to a customer by passing the paymentMethod.id and the Stripe customer ID.
Front End HTML
<div ref="card" class="credit-card"></div>
Front End Create and Mount
this.stripe = await loadStripe('pk_test_YOURKEY');
let stripeElem = this.stripe.elements();
this.card = stripeElem.create('card', { hideIcon: true, hidePostalCode: false, style: { base: { color: '#363636', fontSize: '22px', fontSmoothing: 'antialiased' }}});
this.card.mount(this.$refs.card);
Front End Create Payment Method and Submit to Back End
await this.stripe.createPaymentMethod({ type: 'card', card: this.card }).then((method) => {
this.$http.post(`/users/billing/cards`, { id: method.paymentMethod.id }).then((response) => {
}).catch(() => {
});
}).catch(() => {
});
Please note: this code is NOT complete, it's just meant to give you an example for those that have struggled like I have.

The NodeJS error message reads:
The source hash must include an 'object' key indicating what type of source to create.
It can also be found here, but I'm not certain, if not this is a bogus error message. If this should indeed apply, it would be object: 'card' instead of object: 'source'; but I don't think so.
With Stripe there sometimes is more than one way to get something done:
The source should definitely be a client-side generated card token,
but your client-side doesn't have any code that would token-ize the card.
For reference, these would have to be combined:
https://stripe.com/docs/js/tokens_sources/create_token?type=cardElement
https://stripe.com/docs/api/cards/create

Related

Error: 3 INVALID_ARGUMENT: com.google.apps.framework.request.BadRequestException: Agent does not support language: 'pl'

I try to fix error. and research in two hours. does anyone found error like this. Thank you.
I try to connect backend node js to dialogflow to make conversation session
'batchUpdate()' problem dialogflow
} else {
event.contents.forEach((content: IDialogContent) => {
const intent: IIntent = {
name: content.intent.intentId,
displayName: content.intent.intentName,
webhookState: 0,
inputContextNames: [
`projects/${projectId}/agent/sessions/${sessionId}/contexts/disable`,
],
outputContexts: [],
};
intents.push(intent);
});
}
});
const finalResult = await batchUpdate(projectId, intents)
^^^^^^^^^^^^^^^^^^^^^^problem this line
return finalResult;
};
error like this
Error: 3 INVALID_ARGUMENT: com.google.apps.framework.request.BadRequestException: Agent does not support language: 'pl'.
at Object.callErrorFromStatus (/app/node_modules/#grpc/grpc-js/src/call.ts:81:24)
at Object.onReceiveStatus (/app/node_modules/#grpc/grpc-js/src/client.ts:338:36)
at Object.onReceiveStatus (/app/node_modules/#grpc/grpc-js/src/client-interceptors.ts:426:34)
at Object.onReceiveStatus (/app/node_modules/#grpc/grpc-js/src/client-interceptors.ts:389:48)
at /app/node_modules/#grpc/grpc-js/src/call-stream.ts:276:24
at processTicksAndRejections (internal/process/task_queues.js:75:11) {
code: 3,
details: "com.google.apps.framework.request.BadRequestException: Agent does not support language: 'pl'.",
metadata: Metadata {
internalRepr: Map(1) { 'grpc-server-stats-bin' => [Array] },
options: {}
},
note: 'Exception occurred in retry method that was not classified as transient'
}
looks like you are sending an intent detection request for a language that the agent is not configured to use.
take a look at https://dialogflow.cloud.google.com/#/editAgent/<your agent name>/ and check if Polish is included as a supported language. If it is not and you need it, just click on "Select Additional Language", select Polish from the list and click "Save".

MongoDB Atlas with "Full Document" enabled returns only the changed item, not the full document

I am trying to use Triggers with MongoDB Atlas to notify on changes to a document in my collection. I want to receive the full document that had any data in it change, and use that full document upon receipt of the change notification. In the triggers configuration, there is a slider to enable/disable "Full Document" which has the following description:
By turning on Full Document, you will receive the document created or
modified in your change event. For Delete operations, the full
document will not exist.
However, with or without that slider enabled, I get the same results.
Here is my change listener code:
const client = new MongoClient(uri, { useNewUrlParser: true, useUnifiedTopology: true })
await client.connect().then(db => {
const collection = client.db("database_name").collection("collection_name")
const changeStream = collection.watch()
changeStream.on("change", async data => {
console.log("Detected database change on", Date())
console.log(data) // only returns changed data
})
})
Here is the example output, which as stated doesn't include the full document despite the trigger configuration:
{
_id: {
_data: '82606C926E000000012B022C0100296E5A10049008F3458DF14719A9225DF7AB403CEC46645F69640064606C76124670930F9A1F657C0004'
},
operationType: 'update',
clusterTime: Timestamp { _bsontype: 'Timestamp', low_: 1, high_: 1617728110 },
ns: { db: 'database_name', coll: 'collection_name' },
documentKey: { _id: 606c76124670930f9a1f657c },
updateDescription: {
updatedFields: {
my_data: 'my changed data',
created: 1617728109618
},
removedFields: []
}
}
The full document should look like this:
{
_id: 606c76124670930f9a1f657c,
myKey: 'something that never changed',
my_data: 'my changed data',
created: 1617728109618,
expires: 'some time in seconds that never changed',
other_info: 'something that never changed'
}
Any help would be appreciated... e.g. do I need to add a function in the trigger configuration to return the full document? If so, what would such a function look like?
I typically don't like answering my own questions, but I figured I'll post the answer here anyway in the event someone else needs it.
After a bit more digging, it looks like collection.watch() can accept options. With Full Document enabled in Atlas, my code needed this:
const changeStream = collection.watch([], { fullDocument: 'updateLookup' })
Now the full document is returned as expected. The documentation I found comes from here, and here.

MS Teams adaptive card "herocard" not work on mobile

I`m creating bot for MS Teams and using JS Microsoft Bot Framework V4 SDK.
In my work, I use search message extension and to work with it, I implemented the onSelectItem method that returns a adaptive card. I will give an example of the code below.
return Promise.resolve({
type: "result",
attachmentLayout: "list",
attachments: [CardFactory.heroCard(
`${file.name}`,
`${text}`,
undefined,
CardFactory.actions([
{
type: "openUrl",
title: "Open",
value: `${openLink}`
},
{
type: "openUrl",
title: "Download",
value: `${downloadLink}`
},
]),
)]
});
Where I pass undefined, this should be the path to the picture, but in my implementation I don't need it. So and this code works great in the browser and on the desktop version here is a screenshot
however, on the mobile version, I get the following result
this is absolutely not the right card, it has no content or buttons
I think I found the answer myself. When the message extension search is triggered, the onQuery method is called and suppose you made a query and received an array of values ​​that you want to display. And here, in the same method, iterating over the array, you must draw two cards at once. For example
files.forEach((file: IDocumentInfo): void => {
const card: any = CardFactory.heroCard(
cutString(file.name, LIMIT),
text,
undefined,
[
{
type: "openUrl",
title: "Open",
value: "", // some value
},
{
type: "openUrl",
title: "Download",
value: "" //some value,
},
]
);
const preview: any = {
contentType: "application/vnd.microsoft.card.thumbnail",
content: {
title: `${cutString(file.name, LIMIT)}`,
text: "", // some text
}
};
And here the variable preview in my case will respond to a small view of information after the search and the variable card will be responsible for the view after selection. And after the card is selected, the onSelectItem method is triggered, which I need to get more information about the document
It turns out that the adaptive heroCard is not to blame here, the onSelectItem method is not called in the mobile application, or I am doing something wrong

TypeORM findOne with nested relations

I am having some issues performing a nested find query with TypeORM. Here's the basic code:
const { completionId } = req?.params;
const user = req.user;
const retrievedCompletion = await getRepository(
CompletionGoogleSearch
).findOne({
relations: ['run', 'run.user'],
where: {
id: completionId,
// run: { user: { id: user.id } }, // This is the code that breaks the function
},
});
console.log(retrievedCompletion?.run.user.id);
console.log(user.id);
It looks to me like there's nothing out of order, and that the query should run. Any idea on what I am doing wrong? I know I can get around this issue by writing a querybuilder query or using raw SQL–I am just curious to understand if there's a flaw in my code.
typeorm added the ability to use nested object
userRepository.find({
relations: {
profile: true,
photos: true,
videos: {
videoAttributes: true,
},
},
});
on this way, you can fetch the data without using eager.
You can find more information here
The feature you're asking about doesn't supported on typeorm yet (Feb 2021).
Checkout this issue that was opened on 2018.
the Solution is use eager:true in run.user entity :
#OneToOne(() => User, User=> User.run, {
eager:true
})
user: User;
and next time you search in CompletionGoogleSearch do just relations: ['run'] and user will come with it.

Firebase nested queries slow - sendRequest call when we're not connected not allowed

I want to implement a follow system between users.
For that, I want to display all of the 250 users of my app, then add a checkmark button next to the ones I already follow, and an empty button next to the ones I do not follow.
var usersRef = firebase.database().ref(‘/users’);
var followingRef = firebase.database().ref(‘/followingByUser’);
var displayedUsers = [];
// I loop through all users of my app
usersRef.once('value', users => {
users.forEach(user => {
// For each user, I check if I already follow him or not
followingRef.child(myUid).child(user.key).once('value', follow => {
if (follow.val()) {
// I do follow this user, follow button is on
displayedUsers.push({
name: user.val().name,
following: true
});
} else {
// I do not follow this user, follow button is off
displayedUsers.push({
name: user.val().name,
following: false
});
}
})
})
})
When doing that, I often (not always) get the following error: "Error: Firebase Database (4.1.3) INTERNAL ASSERT FAILED: sendRequest call when we're not connected not allowed."

Eventually, all the data is fetched, but after 10 seconds instead of 1 (without the error).
I do not believe it is an internet connection issue, as I have a very fast and stable wifi.
Is it a bad practice to nest queries like that?
If not, why do I get this error?
My data is structured as below:
users: {
userId1: {
name: User 1,
email: email#exemple.com,
avatar: url.com
},
userId2: {
name: User 2,
email: email#exemple.com,
avatar: url.com
},
...
}
followByUser: {
userId1: {
userId2: true,
userId10: true,
userId223: true
},
userId2: {
userId23: true,
userId100: true,
userId203: true
},
...
}
Your current database structure allows you to efficiently look up who each user is following. As you've found out it does not allow you to look who a user is follow by. If you also want to allow an efficient lookup of the latter, you should add additional data to your model:
followedByUser: {
userId2: {
userId1: true,
}
userId10: {
userId1: true,
},
userId223: {
userId1: true,
},
...
}
This is a quite common pattern in Firebase and other NoSQL databases: you often expand your data model to allow the use-cases that your app needs.
Also see my explanation on modeling many-to-many relations and the AskFirebase video on the same topic.

Categories