I've been following fireship.io's tutorial for setting up stripe payments. In their repo's auth service, I'm seeing a lot of conversions of observables to promises, presumably to allow for the very useful and readable async/await syntax in the checkout component. For instance, this method exists in the auth service:
getUser(): Promise<any> {
return this.afAuth.authState.pipe(first()).toPromise();
}
allowing for a call like this in the component
const user = await this.auth.getUser();
I'm trying to essentially allow users to make a donation to my webapp, which doesn't require any login whatsoever. Currently, I have a call to stripeCreateCharge in my checkout component (see below), but it depends on the auth.getUser()
method, which I think means I need some kind of login.
So, I created an anonymous login method in the auth service, and call it upstream of the handler call.
From auth.service.ts:
anonymousLogin() {
console.log("anonymousLogin entered");
// console.log('AuthIsStateResolved: ' + this.afAuth.auth.isStateResolved_);
this.afAuth.auth.signInAnonymously()
.then((credential) => {
console.log("success inside callback for signInAnonymously");
console.log(credential.user);
// this.authState = credential;
this.updateUserData(credential.user);
return this.getUser();
})
.catch(error => console.log(error));
}
In my checkout.component.ts:
ngOnInit() {
this.auth.signOut();
this.handler = StripeCheckout.configure({
key: 'pk_live_ztqhLfPwjEf2SFC26LDmWkXr',
locale: 'auto',
source: async (source) => {
this.displayThings = true;
this.loading = true;
console.log("got into source callback from handler in checkout.component");
this.auth.anonymousLogin();
const user = await this.auth.getUser();
console.log(user);
if(user){
const fun = this.functions.httpsCallable('stripeCreateCharge');
this.confirmation = await fun({ source: source.id, uid: user.uid, amount: this.amount }).toPromise();
console.log("this.confirmation");
console.log(this.confirmation.outcome.seller_message);
this.loading = false;
if(this.confirmation.outcome.seller_message === "Payment complete."){
this.paymentStatus = this.confirmation.outcome.seller_message + " Thank you!";
} else{
this.paymentStatus = this.confirmation.outcome.seller_message;
}
} else{
console.log("ack never happened");
}
}
});
}
Paying special attention to the lines
this.auth.anonymousLogin();
const user = await this.auth.getUser();
console.log(user);
above, I'm seeing that this.auth.anonymousLogin()
doesn't complete until after const user = await this.auth.getUser();
gets called, so we have an asynchronicity issue, as evidenced by the console output:
My question: is there a way I can modify anonymousLogin() to return a promise when it's done, so that I can continue using the async/await syntax, and have something like,
const loggedInStatus = await this.auth.anonymousLogin();
if(loggedInStatus){
const user = await this.auth.getUser();
console.log(user);
// More stuff in here...
}
Alternatively, is there better advice for how to handle this whole thing?
Update 17 December, 2019:
When I add return to my signInAnonymously method, it returns undefined in my checkout.component.ts:
auth.service.ts
...
anonymousLogin() {
console.log("anonymousLogin entered");
// console.log('AuthIsStateResolved: ' + this.afAuth.auth.isStateResolved_);
return this.afAuth.auth.signInAnonymously()
.then((credential) => {
console.log("success inside callback for signInAnonymously");
console.log(credential.user);
// this.authState = credential;
this.updateUserData(credential.user);
// return this.getUser();
})
.catch(error => console.log(error));
}
checkout.component.ts
...
const loggedInStatus = await this.auth.anonymousLogin();
console.log("test user");
console.log(loggedInStatus);
const user = await this.auth.getUser();
console.log(loggedInStatus);
//below not updated yet
if(user){
const fun = this.functions.httpsCallable('stripeCreateCharge');
this.confirmation = await fun({ source: source.id, uid: user.uid, amount: this.amount }).toPromise();
console.log("this.confirmation");
console.log(this.confirmation.outcome.seller_message);
this.loading = false;
if(this.confirmation.outcome.seller_message === "Payment complete."){
this.paymentStatus = this.confirmation.outcome.seller_message + " Thank you!";
} else{
this.paymentStatus = this.confirmation.outcome.seller_message;
}
} else{
console.log("ack never happened");
}
...
In order to create a proper Promise chain you need to:
return a Promise
pass the result through the chain of .then handlers
So your code should be like:
anonymousLogin() {
return this.afAuth.auth.signInAnonymously()
^^^^^^
add this
.then((credential) => {
return this.getUser();
^^^^^^
return smth in then callback
})
...
}