I'm using Parse to run a background job which will query a parse class and be given a list of Stripe tokens to charge. I've got the background job to easily make one charge however there's issues when multiple payments are present within the one job because of the calling of success/error. I require each stripe charge to give a success or error return so that I can then set the parse object as being paid or unpaid. I'm pretty inexperienced with JavaScript.
I'm able to make two charges successfully however can't get the success/error methods to return on the second stripe charge. Eventually I will want to run a 'for' method to iterate through various customer tokens to make bulk charges.
Parse.Cloud.job("makeCharge", function(request, status) {
Stripe.Charges.create({
amount: 1000 * 10,
currency: "usd",
customer: "cus_customer1" client
},{
success: function() {
status.success("Purchase made!");
},
error: function() {
status.error("Uh oh, something went wrong");
}
});
Stripe.Charges.create({
amount: 100000 * 10,
currency: "usd",
customer: "cus_customer2"
},{
success: function() {
status.success("Purchase made!");
},
error: function() {
status.error("Uh oh, something went wrong");
}
});
});
Related
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
I am totally a beginner in payment gateway no idea how to approach it. But I went through Razorpay side and all the other stackoverflow questions and from 2 days I am struggling to integrate the Razorpay gateway in my project. Now I am approaching the payment gateway by using JavaScript.
My project is a online shopping website which is based on java but I used JavaScript for giving the functionality in the website.
I don't have any idea about Reactjs and I am trying to integrate the gateway using basic Js.
directly into the jsp .
<script type="text/javascript" src="https://checkout.razorpay.com/v1/razorpay.js"></script>
</head>
<input type="button" id="razorGateway" name="submit" class="submit action-button"
value="Pay" />
<script type="text/javascript">
var options = {
"key": "rzp_test_1234567UHGSssj", // Enter the Key ID generated from the Dashboard
"amount": "50000", // Amount is in currency subunits. Default currency is INR. Hence, 50000 refers to 50000 paise or INR 500.
"currency": "INR",
"name": "Acme Corp",
"description": "Ecommerce",
"image": "image",
"order_id": "order_9A33XWu170gUtm",//This is a sample Order ID. Create an Order using Orders API. (https://razorpay.com/docs/payment-gateway/orders/integration/#step-1-create-an-order). Refer the Checkout form table given below
"handler": function (response){
alert(response.razorpay_payment_id);
},
"prefill": {
"name": "Gaurav Kumar",
"email": "gaurav.kumar#example.com",
"contact": "9999999999"
},
"notes": {
"address": "note value"
},
"theme": {
"color": "#EA5B29"
}
};
var rzp1 = new window.Razorpay(options);
document.getElementById('razorGateway').onclick = function(e){
rzp1.open();
e.preventDefault();
}
</script>
After debugging I am getting an error message. rzp1.open().
Screenshot
When I am not able to integrate with above method I went with another way.
<script>
// Single instance on page.
var razorpay = new Razorpay({
key: 'rzp_test_1234567UHGSssj',
// logo, displayed in the payment processing popup
image: 'https://i.imgur.com/n5tjHFD.png',
});
//Fetching the payment.
razorpay.once('ready', function(response) {
console.log(response.methods);
})
//Submitting the data.
var data = {
amount: 1000, // in currency subunits. Here 1000 = 1000 paise, which equals to ₹10
currency: "INR",// Default is INR. We support more than 90 currencies.
email: 'test.appmomos#gmail.com',
contact: '9123456780',
notes: {
address: 'Ground Floor, SJR Cyber, Laskar Hosur Road, Bengaluru',
},
// order_id: '123',
method: 'netbanking',
// method specific fields
bank: 'HDFC'
};
$("#razorGateway").click (function(){
alert("payment clicked");
// has to be placed within user initiated context, such as click, in order for popup to open.
razorpay.createPayment(data);
razorpay.on('payment.success', function(resp) {
alert("payment checking.");
alert(resp.razorpay_payment_id),
alert(resp.razorpay_order_id),
alert(resp.razorpay_signature)}); // will pass payment ID, order ID, and Razorpay signature to success handler.
razorpay.on('payment.error', function(resp){alert(resp.error.description)}); // will pass error object to error handler
})
</script>
Here I am able to hit the razorpay and pop-up is also visible but as it is hard coded I am not able to get the various options of payment method as it is shown in the demo. Directly it is giving me success and fail message(last page of gateway).
https://razorpay.com/demo
This option I am not getting it.Different payment methods
I will be at cloud no.9 if I get the answer or I am able to integrate it in either way or any other way. If options of integrating in Java is also welcomed.
Hi, i think you need to generate an actual order_id from server side as it is mentioned in their docs here. Instead of the sample order id.
I want to send to user my receipt with dummy data.
I use this library which simplifies message sending to Facebook.
The structure of my payload is this:
var payload = {
template_type: 'receipt',
recipient_name: '#' + user.name + ' ' + user.surname,
order_number: (new Date).getTime(),
currency: 'USD',
payment_method: 'Наличными',
order_url: 'http://www.example.com',
timestamp: (new Date).getTime() + '',
elements: [
{
title: title,
subtitle: subtitle,
quantity: 1,
price: 20,
currency: 'USD',
image_url: image_url
}
],
address: {
street_1:"Nurly tau",
street_2:"",
city:"Almaty",
postal_code:"050000",
state:"KZ",
country:"KZ"
},
summary: {
subtotal: 20,
shipping_cost: 0,
total_tax: 0,
total_cost: 20
},
adjustments: []
};
I have just filled receipt fields with simple fake data. Also, Facebook tracks the uniqueness of order_numbers of all sent recepts.
When I try to send this receipt I receive an error message:
{ message: '(#1200) Temporary send message failure. Please try again later',
type: 'OAuthException',
code: 1200,
fbtrace_id: 'BHmHRCEQUC4' }
What does this error mean? Facebook's error messages are so enigmatic?
I just had the same problem and after some fiddling I figured it out! The problem is that when you construct the timestamp using (new Date).getTime() it returns the amount of miliseconds since epoch. However, Facebook requires it to be in seconds.
I had the same problem, after a lot of tries, I figured out that the problem is with the timestamp parameter passed with the JSON payload.
I have no clue about what it could be, but it worked for me removing that. (Maybe, the timestamp should be for a moment before the API call, I don't know).
I aggregated some data and published it, but I'm not sure how/where to access the subscribed data. Would I be able to access WeeklyOrders client collection (which is defined as client-only collection i.e WeeklyOrders = new Mongo.Collection(null);)?
Also, I see "self = this;" being used in several examples online and I just used it here, but not sure why. Appreciate anyone explaining that as well.
Here is publish method:
Meteor.publish('customerOrdersByWeek', function(customerId) {
check(customerId, String);
var self = this;
var pipeline = [
{ $match: {customer_id: customerId} },
{ $group: {
_id : { week: { $week: "$_created_at" }, year: { $year: "$_created_at" } },
weekly_order_value: { $sum: "$order_value" }
}
},
{ $project: { week: "$_id.week", year: "$_id:year" } },
{ $limit: 2 }
];
var result = Orders.aggregate(pipeline);
result.forEach(function(wo) {
self.added('WeeklyOrders', objectToHash(wo._id), {year: wo.year, week: wo.week, order_value: wo.weekly_order_value});
});
self.ready();
});
Here is the route:
Router.route('/customers/:_id', {
name: 'customerOrdersByWeek',
waitOn: function() {
return [
Meteor.subscribe('customerOrdersByWeek', this.params._id)
];
},
data: function() { return Customers.findOne(this.params._id); }
});
Here is my template helper:
Template.customerOrdersByWeek.helpers({
ordersByWeek: function() {
return WeeklyOrders.find({});
}
});
You want var self = this (note the var!) so that call to self.added works. See this question for more details. Alternatively you can use the new es6 arrow functions (again see the linked question).
There may be more than one issue where, but in your call to added you are giving a random id. This presents two problems:
If you subscribe N times, you will get N of the same document sent to the client (each with a different id). See this question for more details.
You can't match the document by id on the client.
On the client, you are doing a Customers.findOne(this.params._id) where this.params._id is, I assume, a customer id... but your WeeklyOrders have random ids. Give this a try:
self.added('WeeklyOrders', customerId, {...});
updated answer
You'll need to add a client-only collection as a sort-of mailbox for your publisher to send WeeklyOrders to:
client/collections/weekly-orders.js
WeeklyOrders = new Meteor.Collection('WeeklyOrders');
Also, because you could have multiple docs for the same user, you'll probably need to:
Forget what I said earlier and just use a random id, but never subscribe more that once. This is an easy solution but somewhat brittle.
Use a compound index (combine the customer id + week, or whatever is necessary to make them unique).
Using (2) and adding a customerId field so you can find the docs on the client, results in something like this:
result.forEach(function (wo) {
var id = customerId + wo.year + wo.week;
self.added('WeeklyOrders', id, {
customerId: customerId,
year: wo.year,
week: wo.week,
order_value: wo.weekly_order_value,
});
});
Now on the client you can find all of the WeeklyOrders by customerId via WeeklyOrders.find({customerId: someCustomerId}).
Also note, that instead of using pub/sub you could also just do all of this in a method call. Both are no-reactive. The pub/sub gets you collection semantics (the ability to call find etc.), but it adds the additional complexity of having to deal with ids.
I know that according to the documentation, if I wanted to send a push notification to all installations in the "Giants" and "Mets" channels, I would do it like so:
Parse.Push.send({
channels: [ "Giants", "Mets" ],
data: {
alert: "The Giants won against the Mets 2-3."
}
}, {
success: function() {
// Push was successful
},
error: function(error) {
// Handle error
}
});
What I want to do however, is send a push notification to only the installations that are in BOTH channels, not one of them only.
So going off the example above, rather than sending it to any installation in the "Giants" or "Mets" channels, I would only send it to installations that are in both at the same time, not ones that have only "Giants" or only "Mets". Is there a way to do this?
The alternative to sending a channels based push, is to push-to-query. You construct a Parse.Query matching the installations you want, and send that way.
Because of this, you can use the 'containsAll' constraint on the channel key. Give this a shot.
var query = new Parse.Query(Parse.Installation);
query.containsAll("channels", ["Giants", "Mets"]);
Parse.Push.send({
where: query,
data: {
alert: "The Mets Won!"
}
}).then(function() {
// success
}, function(error) {
// error
});