Mostly working and passing token and price to my backend made some changes as per answer still can't get the booking_id from the createBookingSuccessful callback to pass along with my token to my backend. Pretty new to javascript and the simple stripe checkout was fine initially but I now need to use the custom method to pass along my booking id from my third party booking callback.
function initalizeWidget(duration, title, price, contractor) {
var widget = new TimekitBooking();
widget.init({
app_key: 'live_widget_key_zr3c9idDjH',
resources: [
'1b6097f9-4806-3dec48c8'
],
callbacks: {
createBookingSuccessful: function(response) {
if (response.data) {
// Update the booking)id here.
var booking_id = response.data.id;
console.log(booking_id);
handler = StripeCheckout.configure(stripeCheckoutConfig);
handler.open({
name: contractor,
description: title,
zipCode: true,
amount: price
});
// ...
}
}
},
});
}
var stripeCheckoutConfig = {
image: 'https://stripe.com/img/documentation/checkout/marketplace.png',
locale: 'auto',
key: 'pk_test_O9AlqrUIlJTH2a5V0e',
token: function(token) {
// Get the booking_id;
var booking_id = this.booking;
// Send the charge through
$.post("/subscription/web/payment-method/",
{ token: token.id, price: {{ task_price_cents }}, booking_id: booking_id}, function(data) {
if (data["status"] == "ok") {
window.location = "/some-url/";
} else {
// Deal with error
alert(data["message"]);
}
});
}
};
// Simply pass the config.
var handler = StripeCheckout.configure(stripeCheckoutConfig);
You could use a data object to store all the information about your app.
/**
* Your application's data (everything related to your booking
* system on the client-side)
* It can be accessed by the Objects/Functions in the same scope.
*/
var appData = {
booking_id: null,
handler: null
};
/**
* Widget init
*/
var widget = new TimekitBooking();
widget.init({
// ...
callbacks: {
createBookingSuccessful: function(response) {
if (response.data) {
// Update the booking id here.
appData.booking_id = response.data.id;
// ...
}
}
}
};
// Pass the config.
appData.handler = StripeCheckout.configure({
// ...
token: function(token) {
// Get the booking_id;
var booking_id = appData.booking_id;
// Send the charge through
$.post(
"/subscription/web/payment-method/",
{ token: token.id, price: {{ task_price_cents }}, booking_id: appData.booking_id },
// ...
);
}
});
var handler = appData.handler;
Related
I've been trying to crack my problem for quite some time however no matter what I do I can't figure this out. Currently, following the docs from TinyMCE, this code is provided by them.
/* This represents a database of users on the server */
var userDb = {};
userNames.map(function(fullName) {
var name = fullName.toLowerCase().replace(/ /g, '');
var description = descriptions[Math.floor(descriptions.length * Math.random())];
var image = 'https://s3.amazonaws.com/uifaces/faces/twitter/' + images[Math.floor(images.length * Math.random())] + '/128.jpg';
return {
id: name,
name: name,
fullName: fullName,
description: description,
image: image
};
}).forEach(function(user) {
userDb[user.id] = user;
});
/* This represents getting the complete list of users from the server with only basic details */
var fetchUsers = function() {
return new Promise(function(resolve, _reject) {
/* simulate a server delay */
setTimeout(function() {
var users = Object.keys(userDb).map(function(id) {
return {
id: id,
name: userDb[id].name,
};
});
resolve(users);
}, 500);
});
};
/* This represents requesting all the details of a single user from the server database */
var fetchUser = function(id) {
return new Promise(function(resolve, reject) {
/* simulate a server delay */
setTimeout(function() {
if (Object.prototype.hasOwnProperty.call(userDb, id)) {
resolve(userDb[id]);
}
reject('unknown user id "' + id + '"');
}, 300);
});
};
return {
fetchUsers: fetchUsers,
fetchUser: fetchUser
};
})();
/* These are "local" caches of the data returned from the fake server */
var usersRequest = null;
var userRequest = {};
var mentions_fetch = function(query, success) {
/* Fetch your full user list from somewhere */
if (usersRequest === null) {
usersRequest = fakeServer.fetchUsers();
}
usersRequest.then(function(users) {
/* query.term is the text the user typed after the '#' */
users = users.filter(function(user) {
return user.name.indexOf(query.term.toLowerCase()) !== -1;
});
users = users.slice(0, 10);
/* Where the user object must contain the properties `id` and `name`
but you could additionally include anything else you deem useful. */
success(users);
});
};
When I try to change the fake server to get data from my actual server through an API route, however, I get .filter is not a function error. So I figured I would use the Object. values() method, but that doesn't return anything and the console log shows up empty.
This is my logic in my controller (I'm using Laravel btw)
public function getUsers(Request $request) {
$user = User::all();
return $user;
}
The filter problem happens when I change this line :
if (usersRequest === null) {
usersRequest = fakeServer.fetchUsers();
}
To my API call like this:
if (usersRequest === null) {
usersRequest = fetch('api/users/mention');
}
My API response is as follows:
[{id: 1, name: "John", email: "john#doe.com", email_verified_at: null,…},…]
0: {id: 1, name: "John", email: "john#doe.com", email_verified_at: null,…}
1: {id: 2, name: "Admin", email: "vi#example.com", email_verified_at: "2021-02-07 12:01:18",…}
2: {id: 3, name: "Admin2", email: "di#example", email_verified_at: "2021-02-07 12:01:46",…}
Figured it out! After painstakingly trying and trying, I managed to find a solution.
Wrap the tinymce script in a function, I wrapped mine in a function called function tinyMCE()
before the function, run an ajax api call
var usergetNames = [];
$.ajax({
url: '/api/users/mention',
method: 'get',
dataType: 'json',
contentType: 'application/json',
success: function (data) {
usergetNames = data;
tinyMCE();
},
error: function (ex) {
alert(ex.responseText);
}
});
In tinyMCE, replace var userNames line with this
var userNames = usergetNames;
You can get the rest of the code for tinymce mentions in their official documentation page.
I have this in my javascript:
<script>
var stripe = Stripe('pk_test_51Gv0ngD3zt5RrIg0XQiKHaK7TOAqzju9yps8jJB2Gch6ksmG4FSnqgyWLv3Qld2EkNHgAb30PLKduYGBuYtZe71A0066dp27DB');
var elements = stripe.elements();
// Custom styling can be passed to options when creating an Element.
var style = {
base: {
// Add your base input styles here. For example:
fontSize: '16px',
color: '#32325d',
},
};
// Create an instance of the card Element.
var card = elements.create('card', {
hidePostalCode: true, style: style });
// Add an instance of the card Element into the `card-element` <div>.
card.mount('#card-element');
var form = document.getElementById('payment-form');
form.addEventListener('submit', function (event) {
// We don't want to let default form submission happen here,
// which would refresh the page.
event.preventDefault();
stripe.createPaymentMethod({
type: 'card',
card: card,
billing_details: {
// Include any additional collected billing details.
name: 'Jenny Rosen',
},
}).then(stripePaymentMethodHandler);
});
function stripePaymentMethodHandler(result) {
if (result.error) {
// Show error in payment form
} else {
$.ajax({
headers: { 'Content-Type': 'application/json' },
method: 'POST',
url: "/PayStripe",
data: JSON.stringify({
payment_method_id: result.paymentMethod.id,
}),
success: function (json) {
handleServerResponse(json);
}
});
}
}
function handleServerResponse(response) {
if (response.error) {
// Show error from server on payment form
} else if (response.requires_action) {
// Use Stripe.js to handle required card action
stripe.handleCardAction(
response.payment_intent_client_secret
).then(handleStripeJsResult);
} else {
// Show success message
}
}
function handleStripeJsResult(result) {
if (result.error) {
// Show error in payment form
} else {
// The card action has been handled
// The PaymentIntent can be confirmed again on the server
fetch('/pay', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ payment_intent_id: result.paymentIntent.id })
}).then(function (confirmResult) {
return confirmResult.json();
}).then(handleServerResponse);
}
}
</script>
This is my HomeController:
public ActionResult PayStripe(string payment_method_id)
{
StripeConfiguration.ApiKey = "sk_test_51Gv0ngD3zt5RrIg0KmTYo92QYmujb9Gp3dv8zz7fOJYjbLna3gRPOkHzZMSVMISHNgmPSrSncUtKL2DS86R4DEJI00mVv9GusU";
var paymentIntentService = new PaymentIntentService();
PaymentIntent paymentIntent = null;
try
{
if (payment_method_id != "") {
// Create the PaymentIntent
var createOptions = new PaymentIntentCreateOptions
{
PaymentMethod = payment_method_id,
Amount = 1099,
Currency = "gbp",
ConfirmationMethod = "manual",
Confirm = true,
};
paymentIntent = paymentIntentService.Create(createOptions);
}
if (payment_method_id != "")
{
var confirmOptions = new PaymentIntentConfirmOptions { };
paymentIntent = paymentIntentService.Confirm(
payment_method_id,
confirmOptions
); <-- ERROR HERE "No such payment_intent: pm_1Gyj0uD3zt5RrIg0lSfDPKOO"
}
}
catch (StripeException e)
{
return Json(new { error = e.StripeError.Message });
}
return generatePaymentResponse(paymentIntent);
}
ERROR HERE "No such payment_intent: pm_1Gyj0uD3zt5RrIg0lSfDPKOO"
Can any body see what i am missing here?
I created a Connected account and still get the same error.
Your code is calling the PaymentIntent Confirm API but you're passing a PaymentMethod id (pm_123) as the first argument instead of the PaymentIntent id pi_123 which is why you're getting that error. Instead, you need to make sure you pass the PaymentMethod id inside confirmOptions and the PaymentIntent id as the first argument.
Relatedly, your code is creating a PaymentIntent but also passing Confirm = true which means you are already confirming it. And right after you are trying to re-confirm it which does't really make sense. You should pass the PaymentMethod id when you are confirming it.
If you want to create and confirm a PaymentIntent in one call you would do this instead:
var options = new PaymentIntentCreateOptions
{
PaymentMethod = payment_method_id,
Amount = 1099,
Currency = "gbp",
ConfirmationMethod = "manual",
PaymentMethod = payment_method_id,
Confirm = true,
};
var paymentIntent = paymentIntentService.Create(options);
I have made a scheduled script which is sending PDF though email.send()
I have get the filters as params from Suitelet. I want to get the name of the user (from runtime.getCurrentUser) and pass it to my PDF. I m just confused how to pass them and will that API be used in Suitelet or Sched script.
Can anyone help me with the code?
Here is my Scheduled script code:
/**
* #NApiVersion 2.x
* #NScriptType scheduledscript
*/
define(['N/ui/serverWidget', 'N/search', 'N/render', 'N/runtime', 'N/file', 'N/email'],
function (ui, search, render, runtime, file, email) {
function execute() {
try {
generateReport();
}
catch (e) {
log.error('generateReport ERROR', e);
}
}
function generateReport() {
var slfilters = runtime.getCurrentScript().getParameter({ name: 'custscript_searchfilter_report' });
log.debug('slfilters', slfilters);
if (!!slfilters) {
slfilters = JSON.parse(slfilters);
}
log.debug('slfilters2', slfilters);
var user = runtime.getCurrentUser();//Need this user to be passed to my xml template
var gender = slfilters.gender;//getting this from Suitelet
log.debug('gender', gender);
var item = slfilters.item;//getting this from Suitelet
log.debug('item', item);
var item_ = getItems(item, gender);
log.debug('getItems(item, gender)', item_);
//return item;
var xmlTemplateFile = file.load(3918);
//var template = script.getParameter({ name: 'custscript_template' });
var renderer = render.create();
renderer.templateContent = xmlTemplateFile.getContents();
var customSources = {
alias: 'searchdata',
format: render.DataSource.JSON,
data: JSON.stringify({
value: item_,
})
};
renderer.addCustomDataSource(customSources);
var xml = renderer.renderAsString();
var pdf = render.xmlToPdf({
"xmlString": xml
});
email.send({
author: 317,
recipients: 'aniswtf#gmail.com',
subject: 'Item Report',
body: 'Report Generated: ',
attachments: [pdf]
});
}
//
// ─── GET RESULTS ───────────────────────────────────────────────────
//
const getResults = function (set) {
var results = [];
var i = 0;
while (true) {
var result = set.getRange({
"start": i,
"end": i + 1000
});
if (!result) break;
results = results.concat(result);
if (result.length < 1000) break;
i += 1000;
}
return results;
};
//
// ─── GET ITEMS ───────────────────────────────────────────────────
//
function getItems(item, gender,user) {
try {
log.error('getItems Function started');
var itemSearch = search.load({
id: 'customsearch_mx_itemsearch'
});
var defaultFilters = itemSearch.filters;
itemSearch.filters.push(
search.createFilter({
name: "custitem5",
operator: 'anyof',
values: gender
}),
search.createFilter({
name: "internalid",
operator: 'anyof',
values: item
})
);
//defaultFilters = arrFilters;
//defaultFilters = defaultFilters.concat(arrFilters);
//log.error('Updated Filters', defaultFilters)
log.error('itemSearch', itemSearch);
//return defaultFilters;
var results = itemSearch.run().getRange({
start: 0,
end: 150
});
var result2 = results.map(function (x) {
// var results = getResults(itemSearch.run()).map(function (x) {
return {
'category': x.getText({
name: "custitem10",
join: "parent"
}),
'season': x.getValue({
name: "custitem11",
join: "parent"
}),
'riselabel': x.getText({
name: "custitem_itemriselabel",
join: "parent"
}),
'fit': x.getText({
name: "custitem9",
join: "parent"
}),
'name': x.getText({ //sku
name: "itemid",
join: "parent"
}),
'style': x.getText({
name: "custitem8",
join: "parent"
}),
'inseam': x.getText({
name: "custitem7",
join: "parent"
}),
'wash': x.getText({
name: "custitem_washname",
join: "parent"
}),
};
});
log.debug('Results', results.length);
log.debug('results', results);
log.debug('result2', result2);
// return results;//nabeeel's
return result2;//mine
} catch (e) {
log.error('error in getItems', e)
}
}
return {
execute: execute
};
});
There is no User in a Scheduled Script, so runtime.getCurrentUser() there will not return a value. You will need to retrieve the User via that method in the Suitelet (assuming it is not an anonymous external Suitelet).
From there you can add a Script Parameter to the Scheduled Script to hold the User, and then your Scheduled Script can read the Parameter and add the value as another Data Source on your template.
I am trying to put all my vue-resource requests into a separate component/plugin for easy re-use and no duplication, but being new to Vue I am not sure if this is the correct approach.
It seems a little clumsy.
I want to be able to define all my resources in my plugin as well as be able to initialize new sessions and setup the Auth header globally. The following works but I am sure there is a better/cleaner way.
Would appreciate any guidance/advice on the better approach here.
Thanks
Plugin - DataService.js
Created an Init function to setup the global Auth header and then some other properties to store paths and options.
const DataService = {
init : function(newsession) {
this.session = newsession;
Vue.http.headers.common['Authorization'] = newsession;
},
userpath : '/api/users/',
options: {emulateJSON: true},
};
DataService.install = function (Vue, options) {
Vue.prototype.$getUser = function (userid) {
return this.$http.get(DataService.userpath + userid);
}
Vue.prototype.$saveUser = function (user) {
return this.$http.post(DataService.userpath + user.id,{data: user},DataService.options);
}
}
Vue Instance - account.js
Note: I am first initializing my DataService to pass in the new session as well as call it as a Plugin (.use).
DataService.init(session_id);
Vue.use(DataService);
new Vue({
el: '#app',
data: {
user: { id: null, username: null},
session: { },
processing: false,
alert: "",
warning: "",
},
computed: {
},
mounted: function() {
this.loadUser();
},
methods: {
loadUser: function () {
this.$getUser(userid).then(function(response) {
// get body data
var res = response.body;
this.user = res.data;
this.session = res.session;
}, function(response) {
console.error('Error loading user',response);
});
},
saveUser: function () {
this.$saveUser(this.user).then( function(response) {
// success callback
console.log('Saved user',response);
}, function(response){
// error callback
console.error('Error Saving user',response);
});
}
}
});
I have a model "User" that has a Many-to-One relationship with a "Subject".
User.js
attributes: {
subject: { model: 'subject' },
}
Subject.js
attributes: {
name: { type: 'string', unique: true, required: true },
}
When I call the blueprint create function for a User "/user" and pass in the data:
{
"name":"Test",
"subject":{"name":"Do Not Allow"}
}
It creates the user and also creates the Subject. However I do not want to allow the subject to be created, I only want to be able to attach an existing one. For example I would like it to reject the subject being created using the above data but allow the subject to be attached by using the below data.
{
"name":"Test",
"subject":1
}
I tried adding a policy (shown below) but this only stops the subject from being created using the URL "/subject" and not the nested create shown above.
'SubjectController':{
'create':false
}
Edit
To help understand what is going on here this is the lifecycle process it is going through:
Before Validation of Subject
After Validation of Subject
Before Creating Subject
After Creating Subject
Before Validation of User
After Validation of User
Before Creating User
Before Validation of User
After Validation of User
After Creating User
As you can see it is validating and creating the subject before it even gets to validating or creating the user.
You want to avoid the creation of an associated object when calling the blueprint creation route.
Create a policy (I've named it checkSubjectAndHydrate) and add it into the policies.js file:
// checkSubjectAndHydrate.js
module.exports = function (req, res, next) {
// We can create a user without a subject
if (_.isUndefined(req.body.subject)) {
return next();
}
// Check that the subject exists
Subject
.findOne(req.body.subject)
.exec(function (err, subject) {
if (err) return next(err);
// The subject does not exist, send an error message
if (!subject) return res.forbidden('You are not allowed to do that');
// The subject does exist, replace the body param with its id
req.body.subject = subject.id;
return next();
});
};
// policies.js
module.exports.policies = {
UserController: {
create: 'checkSubjectAndHydrate',
update: 'checkSubjectAndHydrate',
}
};
You should be passing the subject id (e.g. 1) instead of an object (e.g. { name: 'Hello, World!' }) containing the name of the subject as it's not necessarily unique.
If it is unique, you should replace the object by its id inside a beforeValidate for example.
// User.js
module.exports = {
...
beforeValidate: function (users, callback) {
// users = [{
// "name":"Test",
// "subject":{"name":"Do Not Allow"}
// }]
async.each(users, function replaceSubject(user, next) {
var where = {};
if (_.isObject(user.subject) && _.isString(user.subject.name)) {
where.name = user.subject.name;
} else if(_.isInteger(user.subject)) {
where.id = user.subject;
} else {
return next();
}
// Check the existence of the subject
Subject
.findOne(where)
.exec(function (err, subject) {
if (err) return next(err);
// Create a user without a subject if it does not exist
user.subject = subject? subject.id : null;
next();
});
}, callback);
// users = [{
// "name":"Test",
// "subject":1
// }]
}
};
You can create custom type for subject, and add your logic inside model. I'm not 100% sure I understood the attach sometimes part but maybe this could help:
models/User.js
module.exports = {
schema: true,
attributes: {
name: {
type: 'string'
},
subject: {
type: 'json',
myValidation: true
}
},
types: {
myValidation: function(value) {
// add here any kind of logic...
// for example... reject if someone passed name key
return !value.name;
}
}
};
You can find more info here http://sailsjs.org/documentation/concepts/models-and-orm/validations at the bottom of the page.
If I totally missed the point... The second option would be to add beforeCreate and beforeUpdate lifecycle callback to your model like this:
models/User.js
module.exports = {
schema: true,
attributes: {
name: {
type: 'string'
},
subject: {
type: 'json'
}
},
beforeCreate: function (values, cb) {
// for example... reject creating of subject if anything else then value of 1
if (values.subject && values.subject !== 1) return cb('make error obj...');
cb();
},
beforeUpdate: function (values, cb) {
// here you can add any kind of logic to check existing user or current update values that are going to be updated
// and allow it or not
return cb();
}
};
By using this you can use one logic for creating and another one for updating... etc...
You can find more info here: http://sailsjs.org/documentation/concepts/models-and-orm/lifecycle-callbacks
EDIT
Realized you have trouble with relation, and in above examples I thought you are handling type json...
module.exports = {
schema: true,
attributes: {
name: {
type: 'string'
},
subject: {
model: 'subject'
}
},
beforeValidate: function (values, cb) {
// subject is not sent at all, so we just go to next lifecycle
if (!values.subject) return cb();
// before we update or create... we will check if subject by id exists...
Subject.findOne(values.subject).exec(function (err, subject) {
// subject is not existing, return an error
if (err || !subject) return cb(err || 'no subject');
//
// you can also remove subject key instead of sending error like this:
// delete values.subject;
//
// subject is existing... continue with update
cb();
});
}
};