Stripe checkout - Remove or make email field read only - javascript

I'm using stripe checkout as described in https://stripe.com/docs/payments/checkout
On the server side, I create a new stripe customer (if there isn't one already), and use that customer id to create a checkout session. and then redirect to checkout using stripe api
stripe.redirectToCheckout({
// Make the id field from the Checkout Session creation API response
// available to this file, so you can provide it as parameter here
// instead of the {{CHECKOUT_SESSION_ID}} placeholder.
sessionId: '{{CHECKOUT_SESSION_ID}}'
}).then(function (result) {
// If `redirectToCheckout` fails due to a browser or network
// error, display the localized error message to your customer
// using `result.error.message`.
});
in the checkout page (which is stripe hosted), there is a email field which is pre populated, but is editable. I have had customers changing that email address. Is there a way I can make the email address on stripe checkout readonly field?

A possible work around:
You can use customer_email instead customer parameter when you create a session.
In this case users can not change the value of email field.
Then, you can get the created customer and update it if you need added some data using the API: https://stripe.com/docs/api/customers/update
But, the problem is that stripe will create a new customer for each checkout.

I got an email from stripe today
We are disabling customers’ ability to overwrite their email address
Starting October 26, customers can no longer edit their email address after you pass it into a Checkout Session. We’ve made this change to prevent duplicative subscriptions from being created by accident for existing customers and prevent unexpected updates to the Customer object during Checkout.
And here it is in all its glory

Now, according to the Docs:
If the customer changes their email on the Checkout page, the Customer object will be updated with the new email.

Given that Stripe allows "customers" to change the email id. we pass to the Checkout form, an idea that could work for you is to add your "internal" identifier (be it an email or other internal user id) as Metatadata to the customer you create, like so (below code is in c#, but you get the idea):
Dictionary<string, string> customerMetaData = new Dictionary<string, string>();
customerMetaData.Add("myappuserid", "{useyourinternalidhere}");
var customer = await cs.CreateAsync(new CustomerCreateOptions { Email = "{emailthat youwanttouseforcustomer}", Metadata = customerMetaData });
//Now you can pass this customer when you create the session for checkout
The point being whatever email the user ends up using, you can always get the corresponding internal user id as well from the customer object's metadata.
In the "success" call back, you can then retrieve the customer data and then save the returned customer details - including the stripe customer id / email that end user used in check out form / your own internal id that you can now retrieve from the customer metadata -- that way you can maintain a mapping of stripe's details to your own internal customer details.
//This code is on the "success" call back (when the session id. is passed)
var sessionService = new SessionService();
var session = await sessionService.GetAsync(session_id, new SessionGetOptions { Expand = new List<string> { "customer" , "line_items", "subscription" } });
var sessionCustomer = session.Customer;
//This sessionCustomer now has the metadata that you passed while creating the customer
//Save the Stripe ids / email id along with data retrieved from the Metadata;
//Now you have all the info you required..
Of course this response is delayed - hopefully, it helps others...

Use "customer_email" parameter as shown below and this is the example in Python:
checkout_session = stripe.checkout.Session.create(
customer_email='example#gmail.com', # Here
line_items=[
{
'price_data': {
'currency': 'jpy',
'unit_amount': 1000,
'product_data': {
'name': 'Honey',
'description': 'Good honey',
},
},
'quantity': 1,
},
],
mode='payment',
success_url= "https://example.com/success.html",
cancel_url='https://example.com/cancel',
)
And because you use "customer_email" parameter so the email field is readonly as shown below:

Related

How to add new members in Group using Firebase

I am developing the app which has grouping function.
Now I have the problem about adding new member in group.
Like Slack, in group creating onboarding flow user can decide group name and add members which don't use the app yet.
As adding members function, I want to use inviting mail link using firebase dynamic links.
Data structure is below
User
- id
- name
- email
Group
- id
- groupName
- members[]
Group's members array has user id.
But when someone creates new group, it is possible that new users don't register the app yet.
So they don't have user id property in the app.
How do I fix this problem?
When someone creates new group, should I develop sign up functions new user using Firebase auth? This means then new member has automatically user id, and adding those to members property.
Or should group member array have mail address instead of user id.
Please tell me. I'm happy with Swift or JavaScript you will teach me.
Thank you.
UPDATE
After reading your comment I would propose another approach.
When the Group creator user adds users to a group, if a user does not already exists you could, from the front-end, call a Callable Cloud Function (CF) that creates a temporary Firestore document in a specific collection. The ID of this document will be the (future) userId.
Then, still in this same Cloud Function, you send an email to the email address (you need to generate yourself the email, for example with the email extension) with a link containing the userId as query string value.
Example of code for this first CF:
exports.provisionNewAccount = functions
.https.onCall(async (data, context) => {
try {
// You can check that the caller is authenticated
// if (context.auth.uid) {execute the rest of the code} else {throw an error}
// New user email
const userEmail = data.email;
// Generate the new user docID
const fakeDocRef = admin.firestore().collection('_').doc();
const requestId = fakeDocRef.id;
// Create the doc in a specific collection
await admin.firestore().collection('usersCreationRequests').doc(requestId).set({ email: userEmail, treated: false });
// Generate the link to include in the email
const linkURL = 'https://your.app/register.html?requestId=' + requestId
// Send the email by creating a doc in the Extension collection
await db
.collection("emails")
.add({
to: userEmail,
message: {
subject: "....",
html: `Click to create your account` // adapt the html to add some text
},
});
return {result: 'OK'}
} catch (error) {
console.log(JSON.stringify(error));
throw new functions.https.HttpsError('internal', JSON.stringify(error));
}
});
You call it as explained here, by passing the future user's email.
When the email recipient clicks on the link, you open a specific page of your web app that shows a set of fields for the future user to enter his password, display name etc. Then on clicking on a sign-in button in this page you call a Callable Cloud Function passing it the Firestore document ID plus the field values (you get the document ID from the query string).
As shown below, this Cloud Function creates the user in the Authentication service (using the Admin SDK) and flag the Firestore document as treated. Upon getting back the Cloud Function result in the web app you authenticate the user (you have his email and password, since he/she entered it in the form).
exports.createNewAccount = functions
.https.onCall(async (data, context) => {
try {
const userEmail = data.email;
const userId = data.userId;
const userPassword = data.password;
const userDisplayName = data.displayName;
// Fetch the user doc created in the first CF
const snapshot = await admin.firestore().collection('usersCreationRequests').doc(userId).get();
const treated = snapshot.get('treated');
const email = snapshot.get('email');
if (!treated && userEmail === email) {
const createUserPayload = {
email,
emailVerified: false,
password: userPassword,
displayName: userDisplayName
};
const userRecord = await admin.auth().createUser(createUserPayload);
return { result: 'OK' }
} else {
return { result: 'User already created' }
}
} catch (error) {
console.log(JSON.stringify(error));
throw new functions.https.HttpsError('internal', JSON.stringify(error));
}
});
I’m actually using this exact approach for a B2B collaborative web app in which users can invite new users by email.
INITIAL ANSWER
(Totally different from the update)
So they don't have user id property in the app… How do I fix this
problem? When someone creates new group, should I develop sign up
functions new user using Firebase auth?
You can use the Anonymous Authentication mode, it exactly corresponds to your needs:
You can use Firebase Authentication to create and use temporary
anonymous accounts to authenticate with Firebase. These temporary
anonymous accounts can be used to allow users who haven't yet signed
up to your app to work with data protected by security rules. If an
anonymous user decides to sign up to your app, you can link their
sign-in credentials to the anonymous account so that they can continue
to work with their protected data in future sessions.
When signing-in with Anonymous Authentication a userId (uid) will be created and later you will be able to convert an anonymous account to a permanent account
I always use userId to achive this kind of feature, you can use anonymous authentication to get userId after user click invite link, Then if needed unlock more feature with furter authentication(add more provider).
If you only using mail address without authentication, It's hard to write rules for prevent user access unwanted data, Like anyone knew your email are able to access your account.

Stripe - Save customer payment method only if user ask for it

I use stripe payment element in client side. It sends a request to the server to retrieve the payment intent ID and the client secret.
I would like to be able to save the customer's payment method only if the customer has checked the box save this payment method for future purchases
But I would like the method to register only after I pass my complete flow in server side and be successful.
1- For this I set the capture method to "manual" when I create the
payment intent and I capture and update the payment intent when all
the server-side instructions have succeeded like this :
if (isWantSaveMethod) {
await stripe.paymentIntents.update(paymentIntent.id, { setup_future_usage: 'off_session' })
}
await stripe.paymentIntents.capture(paymentIntent.id)
=> The problem is that if I update the payment intent before it is captured, I get the error: You cannot provide a new payment method to a PaymentIntent when it has a status of requires_capture. You should either capture the remaining funds, or cancel this PaymentIntent and try again with a new PaymentIntent.
2- Then I tried to use an intent setup, which apparently allows us to
link a payment method to a customer
if (isWantSaveMethod) {
await stripe.setupIntents.create({
payment_method: paymentIntent.payment_method,
confirm: true,
customer: paymentIntent.customer,
})
}
await stripe.paymentIntents.capture(paymentIntent.id)
=> But : The provided PaymentMethod was previously used with a PaymentIntent without Customer attachment, shared with a connected account without Customer attachment, or was detached from a Customer. It may not be used again. To use a PaymentMethod multiple times, you must attach it to a Customer first.
3- Then I tried to attach the payment intent to a customer like this :
if (isWantSaveMethod) {
await stripe.paymentMethods.attach(paymentIntent.payment_method, {
customer: paymentIntent.customer,
})
await stripe.setupIntents.create({
payment_method: paymentIntent.payment_method,
confirm: true,
customer: paymentIntent.customer,
})
}
await stripe.paymentIntents.capture(paymentIntent.id)
=> But I remember that the documentation say : To attach a new PaymentMethod to a customer for future payments, we recommend you use a SetupIntent or a PaymentIntent with setup_future_usage. These approaches will perform any necessary steps to ensure that the PaymentMethod can be used in a future payment.
So finally I'm here to know what's the good way to do that ? How popular websites do this thing ?
I just want to make sure that all my instructions in the server side executed successfully before save the payment method and I want to be able to use this method later
How can I do that while respecting the recommendations that stripe gave in relation to the method attachment ?
When using Payment Element the intended user flow is that you would pass in setup_future_usage when the Payment Intent is created. Stripe wants to know if you intend to use setup_future_usage ahead of time so they can choose which Payment Method types to in the Payment Element (since not all of them can be used with setup_future_usage. You should either change your checkout flow to ask whether your customer wants to save their payment method before showing the Payment Element, or you can use the individual elements like Card element instead (each of theses elements should support passing in setup_future_usage with the client-side confirmation call - example here).
Alternatively (I really wouldn't recommend this), you can look into doing the following:
Make sure you haven't enabled any Payment Method types that do not support setup_future_usage (you can find the list here).
Change your client-side code to first send a request to your server to update the Payment Intent to set setup_future_usage, and wait for the response to be successful before continuing to call confirmPayment client-side.

Generate recover email link in firebase

Is there any way to create custom recoverEmail link in firebase/firebase-admin?
I've checked the docs and tutorials there's none.
Any help would be great!
From my understanding there is currently no solution for this within the SDK. Instead, we took the approach of using admin.auth().generateSignInWithEmailLink(email, actionCodeSettings) and then replacing the mode within the returned link from signIn to recoverEmail.
const updatedLink = link.replace('signIn', 'recoverEmail');
This allowed us to customise the auth handler action as suggested here Create the email action handler page in the Firbase documentation.
Now we are able to call on admin.auth().updateUser again to reset the email to it's previous, along with update across our databases, merchant and other services. You'll also need to add the original email to a query in the updatedLink too.
const linkWithOriginalEmail = updatedLink.concat(`&email=${email}`)
Hope that helps and if anyone has a better solution we'd love to discuss.
I´m not sure if I understand your problem correctly, but If your goal is to have a custom link in the automatic email sent by Firebase when someone changes his authentication email address with updateEmail then you can define a custom action url in the Firebase console e.g. https://example.com/__/auth/action in the Authentication section and add the following code to the defined url (ref. https://firebase.google.com/docs/auth/custom-email-handler).
function handleRecoverEmail(auth, actionCode, lang) {
// Localize the UI to the selected language as determined by the lang
// parameter.
var restoredEmail = null;
// Confirm the action code is valid.
auth.checkActionCode(actionCode).then(function(info) {
// Get the restored email address.
restoredEmail = info['data']['email'];
// Revert to the old email.
return auth.applyActionCode(actionCode);
}).then(function() {
// Account email reverted to restoredEmail
// TODO: Display a confirmation message to the user.
// You might also want to give the user the option to reset their password
// in case the account was compromised:
auth.sendPasswordResetEmail(restoredEmail).then(function() {
// Password reset confirmation sent. Ask user to check their email.
}).catch(function(error) {
// Error encountered while sending password reset code.
});
}).catch(function(error) {
// Invalid code.
});
}

Facebook PHP API How to get current user profile_id?

After login with FB JavaScript API, how can I get the profile_id of that user? ('/me'), please help...I used many hours on google and facebook official dev site still no luck on this. How come this kind of simple user profile id also didn't provide in API documentation...is me noob or facebook?.
sesssion_start();
require_once 'autoload.php';
use Facebook\FacebookSession;
use Facebook\FacebookRequest;
use Facebook\FacebookResponse;
use Facebook\FacebookSDKException;
use Facebook\FacebookRequestException;
use Facebook\FacebookAuthorizationException;
use Facebook\GraphObject;
use Facebook\Entities\AccessToken;
use Facebook\HttpClients\FacebookCurlHttpClient;
use Facebook\HttpClients\FacebookHttpable;
use Facebook\FacebookJavaScriptLoginHelper;
FacebookSession::setDefaultApplication( 'APP_ID','APP_SECRET' );
$helper = new FacebookJavaScriptLoginHelper();
$session = new FacebookSession($_POST['access_token']);
$request = new FacebookRequest(
$session,
'GET',
'/me'
);
$response = $request->execute();
$graphObject = $response->getGraphObject();
$profile_id = $graphObject->getProperty('profile-id'); // HOW TO GET PROFILE ID??
P.S: I want profile id not user id...
I think You have to get user_id
A user represents a person on Facebook. The /{user-id} node returns a single user.
Here is the reference of Graph Api .. Check it in detail according to your requirement
https://developers.facebook.com/docs/graph-api/reference/user
For profile-id
check that link of Graph Api
Reference:
Profile /{profile-id}
A profile can be a:
User
Page
Group
Event
Application
The profile object is used within the Graph API to refer to the generic type that includes all of these other objects.
The individual reference docs for each profile type should be used instead.
Fields
When something has a profile, Graph API returns the fields for that object.
https://developers.facebook.com/docs/graph-api/reference/v2.5/profile/

Meteor: Adding Fields on createAccount

I'm trying to use the Meteor Roles package: https://github.com/alanning/meteor-roles
to obviously create a new field in user model.
The user is created no problem but the 'roles' field I'm trying to define isn't created. I can add things like 'Profile' and details within that too. But for some reason I can't make a roles field. Here's my form:
Template.signup.events({
'submit #signup-form' : function(e, t) {
e.preventDefault();
var roles = ['admin'],
email = t.find('#email').value,
password = t.find('#password').value;
Accounts.createUser({email: email, password : password, roles: roles}, function(err){
if (err) {
alert("User Not Added")
} else {
console.log("User Added.")
}
});
}
});
Eventually I'll need to publish this to the client but for right now I just want the field to show in MongoDb, which it's not.
3 things:
I feel like the code above should work but I'm clearly missing something
In the package docs it mentions this Roles.addUsersToRoles which I
tried but no luck
Or do I need to possibly update the record, after it's been created?
I did go into the DB and manually added the field and associated string to update it (with $set) and it worked. But from the form itself though, no luck.
Any pointers would be much appreciated. Thank you.
The Accounts.createUser function only lets you add arbitrary user properties via the profile option which is where they end up getting stored in mongo. That is why Meteor is ignoring the roles: roles part of your Accounts.createUser call.
It is true that the meteor-roles package stores the list of roles assigned to a user directly in the users collection, but that is almost just an implementation detail and you are probably best off sticking to the API that meteor-roles provides for adding users to a role:
Roles.addUsersToRoles(<userId>,[<list of roles>])
The userId passed to Roles.addUsersToRoles is the value returned by Accounts.createUser when its called on the server which is probably where you want to be doing this as that feels way more secure.
The Accounts.createUser function only takes username, email, password and profile as params for the user object. See the documentation here. So, to add another field to a new user object, you need to add it in a second step:
var uid = Accounts.createUser({email: email, password: password});
Meteor.users.update(uid, {$set: {roles: roles}});

Categories

Resources