I was looking at the Payment Request API which is apparently available on some browsers but I was wondering, where/how do you set the account to which the payments get sent? I see nowhere in the following code that specifies an account to which the payment will be sent upon success:
function onBuyClicked() {
if (!window.PaymentRequest) {
// PaymentRequest API is not available. Forwarding to
// legacy form based experience.
location.href = '/checkout';
return;
}
// Supported payment methods
var supportedInstruments = [{
supportedMethods: ['basic-card']
data: {
supportedNetworks: [
'visa', 'mastercard', 'amex', 'discover',
'diners', 'jcb', 'unionpay'
]
}
}];
// Checkout details
var details = {
displayItems: [{
label: 'Original donation amount',
amount: { currency: 'USD', value: '65.00' }
}, {
label: 'Friends and family discount',
amount: { currency: 'USD', value: '-10.00' }
}],
total: {
label: 'Total due',
amount: { currency: 'USD', value : '55.00' }
}
};
// 1. Create a `PaymentRequest` instance
var request = new PaymentRequest(supportedInstruments, details);
// 2. Show the native UI with `.show()`
request.show()
// 3. Process the payment
.then(result => {
// POST the payment information to the server
return fetch('/pay', {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(result.toJSON())
}).then(response => {
// 4. Display payment results
if (response.status === 200) {
// Payment successful
return result.complete('success');
} else {
// Payment failure
return result.complete('fail');
}
}).catch(() => {
return result.complete('fail');
});
});
}
document.querySelector('#start').addEventListener('click', onBuyClicked);
Ref. https://developers.google.com/web/fundamentals/discovery-and-monetization/payment-request/deep-dive-into-payment-request
Ref. https://www.w3.org/TR/payment-request/
Long story short: you don't.
Payment Request API is not a replacement for payment processors. The browser itself has no means to process the funds transfer to your account - it can't even validate whether the provided payment method is valid (though Android Pay can do this).
Per Introducing the Payment Request API doc (emphasis mine):
...
The browser then presents the payments UI to the user, who selects a
payment method and authorizes the transaction. A payment method can be
as straightforward as a credit card that is already stored by the
browser, or as esoteric as third-party application written
specifically to deliver payments to the site (this functionality is
coming soon). After the user authorizes the transaction, all the
necessary payment details are sent directly back to the site. For
example, for a credit card payment, the site will get back a card
number, a cardholder name, an expiration date, and a CVC.
...
In other words, the Payment Request API is just an easier, safer way for you to collect the user's card number and other info necessary to process a payment. Once you receive this information, it's pretty much the same as if the user submitted it via a normal form. You still need a payment processor (or something similar) to actually create a transaction.
Related
Thanks for your time in reading this query. I am implementing a Dialogflow bot using google cloud functions and firebase.
The use case, I wish to design is that
User says "I wish to know information about New York"
Dialog flow captures the intent "lookingForInformation" with Geo.City parameter as "New York" and Action as getInfo.
I have set up Google cloud functions and integrated with Apiai as well as Firebase.
My Firebase Json looks like
{
information :
{
1 :
{
destinationName : "New Jersey",
DestinationInfo : "Its a nice place"
},
2 :
{
destinationName : "London",
DestinationInfo : "Its a lovely place"
},
}
}
My Google cloud function like this
function processV1Request (request, response) {
let action = request.body.result.action; // https://dialogflow.com/docs/actions-and-parameters
let parameters = request.body.result.parameters; // https://dialogflow.com/docs/actions-and-parameters
let inputContexts = request.body.result.contexts; // https://dialogflow.com/docs/contexts
let requestSource = (request.body.originalRequest) ? request.body.originalRequest.source : undefined;
const googleAssistantRequest = 'google'; // Constant to identify Google Assistant requests
const app = new DialogflowApp({request: request, response: response});
// Create handlers for Dialogflow actions as well as a 'default' handler
const actionHandlers = {
// The default welcome intent has been matched, welcome the user (https://dialogflow.com/docs/events#default_welcome_intent)
// Default handler for unknown or undefined actions
'checkDestination': () =>{
// Use the Actions on Google lib to respond to Google requests; for other requests use JSON
if (requestSource === googleAssistantRequest) {
let responseToUser = {
speech: 'I wish to check destinations', // spoken response
text: 'I wish to check destinations' // displayed response
};
sendGoogleResponse(responseToUser);
} else {
console.log(mySet);
let responseToUser = {
speech: 'I wish to check destinations', // spoken response
text: 'I wish to check destinations' // displayed response
};
sendResponse(responseToUser);
}
},
};
I wish to update the above cloud function above to query the firebase database, if the parameter geo.city exist as valid destination in my database. If yes, it should return the information from database, if 'No', it should respond with "Sorry, I don't have information about this city".
Warm Regards
I am trying out Braintree in sandbox mode using the Drop-in feature.
I am creating a Client token using an existing customerID. But when I make transaction.sale call with this customerId and the option storeInVault = true it gives error the following error:
Sale Customer ID has already been taken.
As per the documentation it should update the customer with the payment nonce.
Here is the code:
gateway.transaction.sale({
amount: '10.00',
paymentMethodNonce: nonceFromTheClient, // Generated nonce passed from client
customer: {
id: 232057823, //this customer exist in the vault
email : user.emails[0].address
},
options: {
submitForSettlement: true,
storeInVault: true
//storeInVaultOnSuccess: true
}
}, function (err, result) {
if (err) {
console.log(err);
} else {
if (result.success) {
return result.success;
} else {
console.log('ERR Sale '+result.message);
return result.success;
}
}
});
I'm using Meteor with the package patrickml:braintree.
You look to be using the Braintree Transaction Sale API Call, in which you have included the storeInVault: true option. What this is doing is creating a transaction with the included payment method nonce and attempting to create a customer id of 232057823, which is why you are encountering that error.
If your goal is to simply update an existing customer, you'd want to use the Customer Update API call.
Stopping a watch channel is not working, though it's not responding with an error, even after allowing for propagation overnight. I'm still receiving 5 notifications for one calendarlist change. Sometimes 6. Sometimes 3. It's sporadic. We're also receiving a second round of notifications for the same action after 8 seconds. Sometimes 6 seconds. Sometimes a third set with a random count. Also sporadic. Received a total of 10 unique messages for a single calendar created via web browser.
You can perform infinite amount of watch requests on specific calendar resource, Google will always return the same calendar resource Id for the same calendar, but the uuid you generate in the request will be different, and because of that, you will receive multiple notifications for each watch request that you've made. One way to stop all notifications from specific calendar resource, is to listen for notifications, pull out "x-goog-channel-id" and "x-goog-resource-id" from notification headers, and use them in Channels.stop request.
{
"id": string,
"resourceId": string
}
Every time you perform a watch request, you should persist the data from the response, and check if the uuid or resource id already exist, if yes don't perform watch request for that resource id again (if you don't want to receive multiple notifications).
e.g.
app.post("/calendar/listen", async function (req, res) {
var pushNotification = req.headers;
res.writeHead(200, {
'Content-Type': 'text/html'
});
res.end("Post recieved");
var userData = await dynamoDB.getSignInData(pushNotification["x-goog-channel-token"]).catch(function (err) {
console.log("Promise rejected: " + err);
});
if (!userData) {
console.log("User data not found in the database");
} else {
if (!userData.calendar) {
console.log("Calendar token not found in the user data object, can't perform Calendar API calls");
} else {
oauth2client.credentials = userData.calendar;
await calendarManager.stopWatching(oauth2client, pushNotification["x-goog-channel-id"], pushNotification["x-goog-resource-id"])
}
}
};
calendarManager.js
module.exports.stopWatching = function (oauth2client, channelId, resourceId) {
return new Promise(function (resolve, reject) {
calendar.channels.stop({
auth: oauth2client,
resource: {
id: channelId,
resourceId: resourceId
}
}, async function (err, response) {
if (err) {
console.log('The API returned an error: ' + err);
return reject(err);
} else {
console.log("Stopped watching channel " + channelId);
await dynamoDB.deleteWatchData(channelId)
resolve(response);
}
})
})
}
Not a google expert but I recently implement it in my application,
I am trying to answer some of your questions for future readers:
It's sporadic
Tha's because you have create more than 1 channels for watching events.
We're also receiving a second round of notifications for the same action after 8 seconds
Google doesn't say anything about the maximum delay for sending a push notification.
Suggestions:
CREATE:
When you create a new channel, always save the channel_id and channel_resource in your database.
DELETE:
When you want to delete a channel just use stop API endpoint with the channel data saved in your database
RENEW:
As you have noticed the channels do expire, so you need to update them once in a while. To do that create a crone in your server that is going to STOP all previous channels and it will create new one.
Comment: Whenever something is going wrong please read the error message sent from the Google API calendar. Most of the time, it tells you what is wrong.
Use Channels.stop which is mentioned in the docs. Supply the following data in your request body:
{
"id": string,
"resourceId": string
}
id is the channel ID when you created your watch request. Same goes with resource ID.
Read this SO thread and this github forum for additional reference.
I'd like to update the users database after a successful payment. Basically, converting $ to site credits. I've used https://github.com/tirtohadi/meteor-paypal-demo/, basically using his code implementing paypal to the web app. The only idea I have is when the site gets routed to the return page after payment. Code's here.
Router.map(function() {
this.route('/payment_return/:invoice_no/:amount/', {
where: 'server',
onBeforeAction: function() {
console.log("result");
result = paypal_return(this.params.invoice_no,this.params.amount,this.params.query.token,this.params.query.PayerID);
console.log(result);
if (result)
{
this.response.end("Payment captured successfully");
}
else
{
this.response.end("Error in processing payment");
}
}
});
});
I guess my question is, how do I securely update the db after a successful payment. Because I know client side update is dangerous (from what I've read anyway)
I am having trouble sorting out the new MailChimp API (V3.0). It does not seem like there is a way to call a subscribe method. It seems like I have to use their Sign Up Form. Am I correct?
If by "subscribe" you mean that your application will add someone to a mailing list, you may want to take a look at the List Members Collection portion of their documentation.
http://kb.mailchimp.com/api/resources/lists/members/lists-members-collection
Adding/editing a subscriber via MailChimp v3.0 REST API.
// node/javascript specific, but pretty basic PUT request to MailChimp API endpoint
// dependencies (npm)
var request = require('request'),
url = require('url'),
crypto = require('crypto');
// variables
var datacenter = "yourMailChimpDatacenter", // something like 'us11' (after '-' in api key)
listId = "yourMailChimpListId",
email = "subscriberEmailAddress",
apiKey = "yourMailChimpApiKey";
// mailchimp options
var options = {
url: url.parse('https://'+datacenter+'.api.mailchimp.com/3.0/lists/'+listId+'/members/'+crypto.createHash('md5').update(email).digest('hex')),
headers: {
'Authorization': 'authId '+apiKey // any string works for auth id
},
json: true,
body: {
email_address: email,
status_if_new: 'pending', // pending if new subscriber -> sends 'confirm your subscription' email
status: 'subscribed',
merge_fields: {
FNAME: "subscriberFirstName",
LNAME: "subscriberLastName"
},
interests: {
MailChimpListGroupId: true // if you're using groups within your list
}
}
};
// perform update
request.put(options, function(err, response, body) {
if (err) {
// handle error
} else {
console.log('subscriber added to mailchimp list');
}
});