How to customize the payment method in payment intent example in stripe - node.js

I am trying to follow the example shown in the following link https://stripe.com/docs/payments/accept-a-payment.
I have the following code in the client side
var cardNumber = elements.create('cardNumber', {
placeholder:'',
style: style
});
var cardexpiry = elements.create('cardExpiry',{
placeholder:'',
style:style
});
var cardCVV = elements.create('cardCvc',{
placeholder:'',
style:style
});
// Add an instance of the card Element into the `card-element` <div>.
cardNumber.mount('#card-element');
cardexpiry.mount("#card-expiry")
cardCVV.mount("#card-cvv")
instead of this
var card = elements.create("card", { style: style });
card.mount("#card-element");
Because the I wanted to some UI manipulation. According to the code posted in the link
I should do the following
var submitButton = document.getElementById('submit');
submitButton.addEventListener('click', function(ev) {
stripe.confirmCardPayment(clientSecret, {
payment_method: {card: card}
}).then(function(result) {
if (result.error) {
// Show error to your customer (e.g., insufficient funds)
console.log(result.error.message);
} else {
// The payment has been processed!
if (result.paymentIntent.status === 'succeeded') {
// Show a success message to your customer
// There's a risk of the customer closing the window before callback
// execution. Set up a webhook or plugin to listen for the
// payment_intent.succeeded event that handles any business critical
// post-payment actions.
}
}
});
});
However in the example above in the payment_method the card object is passed, which is not the case in my code. How can I pass my card number and exp/date as well CVC separately in the stripe.confirmCardPayment(clientSecret, {
payment_method: {card: card}

There was a similar question about how to call stripe.createToken() when you aren't using a card element.
According to the Stripe documentation for createToken:
The cardNumber Element you wish to tokenize data from. If applicable, the Element pulls data from other elements you’ve created on the same instance of Elements to tokenize—you only need to supply one Element as the parameter.
Now, for this case in particular, the section for confirmCardPayment says:
Use stripe.confirmCardPayment with payment data from an Element by passing a card or cardNumber Element as payment_method[card] in the data argument.
Basically you just have to pass the cardNumber element to payment_method["card"] and it will pull the data from the other elements you’ve created.
...
stripe.confirmCardPayment(clientSecret, {
payment_method: {card: cardNumber}
})
...

I ended up using this code
var stripe = Stripe('#YOUR KEY');
var elements = stripe.elements();
/// STYLE HERE
var style = {
base: {
fontSize: '16px',
color: "#32325d",
'::placeholder': {
color: '#CFD7E0',
fontSize: '12px'
}
}
};
// Create an instance of the card Element.
var card = elements.create('card', {
hidePostalCode: true,
placeholder: '',
style: style,
});
card.mount('#card-element');
card.addEventListener('change', function (event) {
var displayError = document.getElementById('card-errors');
if (event.error) {
displayError.textContent = '';
} else {
displayError.textContent = '';
}
});

Related

How do you fetch a list of records and then add a checkbox next to corresponding record so to edit and save changes in suiteScript

I want to create a suitelet that could fetch some sales order then be able to edit those on click on submit button, so far I was I'm able to fetch records but I'm not able to add checkboxes and update records in netsuite - suitescript
var list=form.createList({title:"Sales order"});
list.addColumn({
id : 'column1',
type : serverWidget.FieldType.TEXT,
label : 'tranid',
align : serverWidget.LayoutJustification.LEFT
});list.addColumn({
id : 'column2',
type : serverWidget.FieldType.TEXT,
label : 'shipaddress',
align : serverWidget.LayoutJustification.LEFT
});list.addColumn({
id : 'column3',
type : serverWidget.FieldType.TEXT,
label : 'rate',
align : serverWidget.LayoutJustification.LEFT
});
salesorderSearchObj.run().each(function(result){
tranid= result.getValue({name: 'tranid'})
shipaddress= result.getValue({name: 'shipaddress'})
rate= result.getValue({name: 'rate'})
list.addRows({
rows : [{
column1 : tranid,
column2 : shipaddress,
column3 : rate
}]
});
Suite Answer Id 40768 has sample code in SuiteScript 1.0. An outline in 2.0 format is below. The full 1.0 sample from NetSuite is also below. The key is to for the GET req method create the page and display the information as a Sublist instead of a List. Then for all other req methods, get the parameters and take desired action.
2.0 partial outline
define(['N/ui/serverWidget'], function(serverWidget) {
function onRequest(context){
if(context.request.method === 'GET'){
//create page to display results and allow for user input
var form = serverWidget.createForm({
title : 'Simple Form'
});
var sublist = form.addSublist({
id : 'sublistid',
type : serverWidget.SublistType.INLINEEDITOR,
label : 'Inline Editor Sublist'
});
//Add checkbox and atleast internalid
var printField = sublist.addField({
id: 'custpage_rec_process',
label: 'Process',
type: serverWidget.FieldType.CHECKBOX
});
var idField = sublist.addField({
id: 'custpage_rec_id',
label: 'Internal ID',
type: serverWidget.FieldType.TEXT
});
//empty Array to hold Sales Order data
var TranIds = [];
//run search to get Sales Order data
...salesorderSearchObj.run().each(function(result){
//add search column names as columns in the sublist
//add each column value to the sublist
}
//add buttons to sublist and form
sublist.addMarkAllButtons();
sublist.addRefreshButton();
form.addResetButton({label: 'Reset'});
form.addSubmitButton({label: 'Create File'});
//display page for user input
context.response.writePage(form);
} else { //if the previously created/displayed form has been submitted display the following to the user
var req = context.request;
var params = JSON.stringify(context.request.parameters);//can log this to see exactly how the information comes through
//gather submitted data
//take desired action
//display response to user
context.response.writePage('Done');
}
} return {
onRequest: onRequest
}
});
1.0 full sample
function suitelet(request, response){
//Create the form that will be used by the POST and GET requests
var form = nlapiCreateForm('Delete Transactions');
//GET - Show a list of transactions from the search results so the user can select the ones to be deleted
if (request.getMethod() == 'GET' )
{
// Run an existing transaction search
var results = nlapiSearchRecord('transaction', 'customsearch_mass_deletion_results');
// Create a sublist to show the search results
var sublist = form.addSubList('custpage_transaction_list', 'list','Transactions');
// Create an array to store the transactions from the search results
var transactionArray = new Array();
if (results!=null)
{
// Add a checkbox column to the sublist to select the transactions that will be deleted.
sublist.addField('delete','checkbox', 'Delete');
// Add hidden columns for the Internal ID and for the Record type.
// These fields are necessary for the nlapiDeleteRecord function.
sublist.addField('internalid','text', 'Internal ID').setDisplayType('hidden');
sublist.addField('recordtype','text', 'Record Type').setDisplayType('hidden');
// Add a column for the Internal ID link
sublist.addField('internalidlink','text', 'Internal ID');
// Get the the search result columns
var columns = results[0].getAllColumns();
// Add the search columns to the sublist
for(var i=0; i< columns.length; i++)
{
sublist.addField(columns[i].getName() ,'text', columns[i].getName() );
}
// For each search results row, create a transaction object and attach it to the transactionArray
for(var i=0; i< results.length; i++)
{
var transaction = new Object();
// Set the Delete column to False
transaction['delete'] = 'F';
// Set the hidden internal ID field
transaction['internalid'] = results[i].getId();
// Set the hidden record type field
transaction['recordtype'] = results[i].getRecordType();
// Create a link so users can navigate from the list of transactions to a specific transaction
var url = nlapiResolveURL('RECORD', results[i].getRecordType() ,results[i].getId(), null);
internalIdLink = " " + results[i].getId() +" ";
// Set the link
transaction['internalidlink'] = internalIdLink;
// Copy the row values to the transaction object
for(var j=0; j< columns.length ; j++)
{
transaction[columns[j].getName()] = results[i].getValue(columns[j].getName());
}
// Attach the transaction object to the transaction array
transactionArray[i] = transaction;
}
}
// Initiate the sublist with the transactionArray
sublist.setLineItemValues(transactionArray);
sublist.addMarkAllButtons();
form.addSubmitButton('Submit' );
response.writePage( form );
}
//POST - Delete the selected transactions and show a confirmation message
else
{
// Check how many lines in the sublist
var count = request.getLineItemCount('custpage_transaction_list');
// This variable will keep track of how many records are deleted.
var num = 0;
//for each line in the sublist
for(var i=1; i< count+1; i++)
{
//get the value of the Delete checkbox
var deleteTransaction = request.getLineItemValue('custpage_transaction_list', 'delete', i);
// If it's checked, delete the transaction
if(deleteTransaction == 'T')
{
// Get the transaction internal ID
var internalId = request.getLineItemValue('custpage_transaction_list', 'internalid', i);
// Get the transaction type
var recordType = request.getLineItemValue('custpage_transaction_list', 'recordtype', i);
try
{
// Delete the transaction
nlapiDeleteRecord(recordType, internalId);
num++;
}
// Errors will be logged in the Execution Log
catch(ex)
{
nlapiLogExecution('ERROR', 'Error', 'Transaction ID '+ internalId +': '+ ex);
}
}
}
// Show how many records were deleted.
form.addField("custpage_transaction_total", "text").setDisplayType('inline').setDefaultValue(num + " transactions deleted");
response.writePage( form );
}
}

In SuiteScript, can you set the customform field using record.submitFields?

I have a partner record where I would like to change the form if the category field is set to a certain value. However, I can't use this with certain SuiteScript functions because changing the form wipes out any changes that were made to the record. I'm trying to work around this using an afterSubmit function that will use record.SubmitFields to change the form and then redirect.toRecord to reload the page with the change. However, it's not changing the form value. Is there a way to do this with record.submitFields? Am I doing something incorrectly?
var currentRecord = scriptContext.newRecord;
var category = currentRecord.getValue('category');
if(category == '3'){
try{
record.submitFields({
type: record.Type.PARTNER,
id: currentRecord.id,
values: {
'customform': '105'
}
});
log.debug('success');
} catch (e) {
log.error({title: 'error', details: e});
}
}
redirect.toRecord({
type: 'partner',
id: currentRecord.id,
});
}
Yes you can. Whenever you create a url for a record you can generally add a cf parameter that takes the form id. It's the same vaule you'd use if you were setting the field 'customform'. So just skip the submitFields part and do:
redirect.toRecord({
type: 'partner',
id: currentRecord.id,
parameters:{
cf:105
}
});
You can also set the custom form using the submitFields call but that only works for some types of records.
If you need to do this in the beforeLoad here is a fragment in Typescript. The trick to avoid an infinite loop is to check to see if you already have the correct form:
export function beforeLoad(ctx){
let rec : record.Record = ctx.newRecord;
let user = runtime.getCurrentUser();
if(user.roleCenter =='EMPLOYEE'){
if(rec.getValue({fieldId:'assigned'}) != user.id){
throw new Error('You do not have access to this record');
return;
}
}else{
log.debug({
title:'Access for '+ user.entityid,
details:user.roleCenter
});
}
if(ctx.type == ctx.UserEventType.EDIT){
var approvalForm = runtime.getCurrentScript().getParameter({name:'custscript_kotn_approval_form'});
let rec : record.Record = ctx.newRecord;
if( 3 == rec.getValue({fieldId:'custevent_kotn_approval_status'})){
if(approvalForm != rec.getValue({fieldId:'customform'}) && approvalForm != ctx.request.parameters.cf){
redirect.toRecord({
type: <string>rec.type,
id : ''+rec.id,
isEditMode:true,
parameters :{
cf:approvalForm
}
});
return;
}
}
}

Telegraf.js pass data from button to a function that handle a WizardScene

I need to pass some data retrieved from a Database.
When i click into a button i send a private message to the user who have cliecked. I need to pass some datas from that button, to the message sent. Because, into that message i have an other button that starts a WizardScene. SO how can i do to pass data? Thank you.
Here is my code.
This is into a function that post a photo with a description and with a callbackup button.
my function() {
...
let productId = //obtain from a db;
await bot.telegram.sendPhoto(
channel_id,
{ source: filepath },
{
caption: description.join("\n"),
parse_mode: 'MarkdownV2',
reply_markup: productButtons.reply_markup
}
)
return productId;
...}
and the button is:
const productButtons = Extra.markup((m) =>
m.inlineKeyboard([
[m.callbackButton('TEST', 'test_btn')],
])
)
when someone clicsk on it, it sends a message on a private user with this:
bot.action('testa_btn', (ctx) => {
console.log('testa')
let text = `My text about ${productId}`
ctx.telegram.sendMessage(ctx.from.id, o_functions.escapeReplace(text), {
parse_mode: 'MarkdownV2',
reply_markup: startBtn.reply_markup
})
});
this sends a text where i need to write my productid, and an other button where i start my wizard scene:
const startBtn = Extra.markup((m) =>
m.inlineKeyboard([
[m.callbackButton('START', 'start_btn_wizard')],
])
);
bot.action('start_btn_wizard', (ctx) => {
return ctx.scene.enter('super-wizard');
})
So how can i pass my productId variable, first to the button TEST, then to the wizard scene? I need to use it on the Wizard on the user dialogue.
THank you
You need to do a couple of things to pass data from a callback button to a handler and then towards a wizard scene.
Add the desired data to the button. Notice how I attached the product Id to the callback data.
const startBtn = Extra.markup((m) =>
m.inlineKeyboard([
[m.callbackButton('START', `start_btn_wizard_${product_id}`)],
])
);
Receive the button callback using a regex rather than using a literal string and extract the product ID from the callback data.
bot.action(/start_btn_wizard_+/, (ctx) => {
let product_id = ctx.match.input.substring(17);
// add all necessary validations for the product_id here
return ctx.scene.enter('super-wizard');
});
Pass the acquired id to the wizard scene and extract it again from inside the scene using ctx.scene.state.
bot.action(/start_btn_wizard_+/, (ctx) => {
let product_id = ctx.match.input.substring(17);
// add all necessary validations for the product_id here
return ctx.scene.enter('super-wizard', {product_id: product_id});
});
From inside the wizard, you can access the product_id using ctx.scene.state.product_id.
Hope this helps someone even though it's probably too late for the OP

Pass CVC to stripe.createPaymentMethod JS?

I need a separate input for CVC and Expiry, so I have created 3 Stripe Elements:
let elements = stripe.elements();
let cardNumber = elements.create('cardNumber');
cardNumber.mount('#card-number');
let cardExpiry = elements.create('cardExpiry');
cardExpiry.mount('#card-expiry');
let cardCvc = elements.create('cardCvc');
cardCvc.mount('#card-cvc');
...
Stripe Docs only mention about how to pass card or cardNumber Element to PaymentMethod, but how do I pass cardExpiry and cardCvc?
function paymentMethod() {
return stripe.createPaymentMethod({
type: 'card',
card: cardNumber,
billing_details: {
name: userName,
email: userEmail,
},
}).then(function(result) {
// code
});
}
Stripe Docs only mention that
Stripe.js methods automatically use all the different elements on your page under the hood, so you don't need to pass CVC and Expiry elements manually, createPaymentMethod will do that automatically under the hood for you.

Creating an SMS group forwarder in Twilio with NodeJS

Here's what I'm trying to accomplish:
Set a list of names and numbers (my "group")
When a text message is sent to the Twilio number, forward it on to every member in the group
At a high-level, the idea seems straight forward enough. My programming / syntax skills are rusty, though, and I'd love some help.
I'm using Twilio Functions, and I've been able to send and receive messages successfully. Now I am stuck on how to carry this idea of iterating through a group.
Here's what I've written so far:
var groupmembers = {
jonathan:{
name: 'Jonathan',
number: '+0000000000'
},
joshua:{
name: 'Joshua',
number: '+1110000000'
}
}
exports.handler = function(context, event, callback) {
// Set some values for later use
this.fromNumber = event.From
this.body = event.Body || ''
let twiml = new Twilio.twiml.MessagingResponse();
groupmembers.forEach(function(member) {
// Skip sending if it's the same number
if (member.number === this.fromNumber ) {
return;
}
// Otherwise, let's send a mesage!
twiml.message("Hello World").to( member.number );
callback(null, twiml);
});
};
The issues I believe I have:
Not being sure how to properly set my array or "dictionary"
Not knowing the proper syntax for passing the "to" variable to the message
Not knowing the proper syntax for doing a loop in NodeJS (the Functions console is telling me that 'groupmembers.forEach is not a function')
Thank you for any and all feedback and for pointing me in the right direction!
The mistake you have is pretty simple. groupmembers is an object, you want an array.
You may want something akin to this instead:
var groupmembers = [{
name: 'Jonathan',
number: '+0000000000'
},
{
name: 'Joshua',
number: '+1110000000'
}]
Apart from that, it looks to me as okay.
With more searching and the point in the right direction from Slava I was able to figure it out! Here's the complete code:
/**
* Represents a search trough an array.
* #function search
* #param {Array} array - The array you wanna search trough
* #param {string} key - The key to search for
* #param {string} [prop] - The property name to find it in
* Props: https://stackoverflow.com/a/33097318/315818
*/
function search(array, key, prop){
// Optional, but fallback to key['name'] if not selected
prop = (typeof prop === 'undefined') ? 'name' : prop;
for (var i=0; i < array.length; i++) {
if (array[i][prop] === key) {
return array[i];
}
}
}
var groupmembers = [
{
name: 'Jonathan',
number: '+000000000'
},
{
name: 'Joshua',
number: '+111111111'
}
];
exports.handler = function(context, event, callback) {
let twiml = new Twilio.twiml.MessagingResponse();
// Search for the group member that matches the sender number
let sender = search(groupmembers, event.From, 'number');
// Now, loop through each of the group members
groupmembers.forEach(function(member) {
// Skip sending if it's the same number
if (member.number === event.From ) {
return;
}
// Now, forward on the message to the group member, using the sender's name
twiml.message(`${sender.name}: ${event.Body}`, {
to: member.number
});
});
// Loop ended
callback(null, twiml);
};

Resources