Shrine with Square - Adding In App Payments (The Boring Flutter Development Show, Ep. 20)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[HIGH TONE] [MUSIC PLAYING] EMILY FORTUNA: Hello, and welcome to "The Boring Show." I'm Emily Fortuna. And with us is a special guest. SHANNON SKIPPER: I'm Shannon Skipper. I'm with Square. EMILY FORTUNA: So today, we are going to be shifting our focus to a different app. If you have ever tried the Flutter gallery demo app, one of the demos within that demo is called Shrine. It's a shopping app. And I pulled that out. I made it a separate repo on my GitHub repo, which is efortuna. It's called Shrine with Square. And so basically, what we're going to be doing is we're going to be adding In-App Payments to that shopping app. And Shannon is going to tell us how. SHANNON SKIPPER: Absolutely. EMILY FORTUNA: So yeah, so first of all, here's our little Shrine app. We're not going to log in for now. I'm just doing the basic thing. SHANNON SKIPPER: Perfect. EMILY FORTUNA: You can click on things, add them to your shopping cart. And right now, all you can do is clear your cart. So I think the next obvious thing is to add a button that's like, take my money. SHANNON SKIPPER: Pay me. EMILY FORTUNA: So I was poking around a little bit. Shopping cart is this page here. Oh, I should actually say one other quick thing. Also, this app is featured in some of the material code labs. There's a four-part-- four different Material codelabs that all help you build out this very app as well. So it's many different ways you can reach this app. But yes, OK, so here, we've got-- here's our button for clear cart. It is a fancy button. I guess, since we're-- the gross thing to do would be to copy-paste. Let's-- SHANNON SKIPPER: Yeah. EMILY FORTUNA: Whoa, let's not do that. How about we pull this out into a function? SHANNON SKIPPER: Nice. EMILY FORTUNA: And then we can have our text. Although-- yeah, so it'll have our text and our OnPressed method. SHANNON SKIPPER: Keep it dry. EMILY FORTUNA: Yes. So I think there's a shortcut for this, but I don't remember it. So I'm not going to do it. Pretty button-- and we will figure out what we're passing in in a second, return that. We need our model. OK, so pretty button-- and let's pass in model. Or wait, how-- this code base is a little bit new to me too, so I'm trying to figure out what's going on here. So we have this model. It's a scoped model, great. So we don't actually need to pass it in, because-- oh, well, so we need our context. I'm going to pass it in for right now, because that's not the focus. And we will come back to fixing up state management afterwards. So scoped model-- or wait, app state model is what we want. And we will pass in model. OK, so that's that. And then let's add our text that we want to add in. SHANNON SKIPPER: OK. EMILY FORTUNA: So string text. SHANNON SKIPPER: So the original one says clear cart. And we'll add something like pay or check out. EMILY FORTUNA: Yeah, exactly. So cut that. Pass in text. Why is that not-- oh. Oh, it thinks it's constant, right? OK-- no longer constant. We were pass in clear cart. And we also want to pass in our function for what we're going to do. Because that will change depending on what we're doing. SHANNON SKIPPER: Nice. EMILY FORTUNA: So we'll cut that out. And we'll say action-- function action. And we'll pass that in. Great. OK, so that's button 1. We didn't change anything. We will-- what are you doing, Shannon? SHANNON SKIPPER: Oh, I'm just-- I'm exploring the-- I was looking-- I was just trying to figure out what these buttons are. EMILY FORTUNA: The raise button? SHANNON SKIPPER: No, the-- checking out what state-- what state it needed, and then, when it took the string. And then you got ahead of me with the-- that it needs the function. EMILY FORTUNA: OK, cool. All right, so we've got everything. Let's add our second function, the function that does the-- brings the money. SHANNON SKIPPER: Yay. EMILY FORTUNA: So let's wrap this in a column. SHANNON SKIPPER: OK. EMILY FORTUNA: And we can put-- we could have our Buy button up top, and then the Clear Cart button just below it. SHANNON SKIPPER: Nice. And so if it was a column, it doesn't need to be fixed positioning? It'll float? EMILY FORTUNA: Yeah it'll just-- we'll see. SHANNON SKIPPER: OK. EMILY FORTUNA: But I believe-- well, so yeah, this is wrapped in a positioned. But I believe it should work. We might need to tweak it. We will find out. SHANNON SKIPPER: Nice. EMILY FORTUNA: OK, so-- nope. Why-- OK. There we go. So wrap with column. And we are going to-- let's stick it below-- or no, above. And we'll say, take my money. SHANNON SKIPPER: Nice. EMILY FORTUNA: And nothing happens. That's awesome. SHANNON SKIPPER: Uh-oh. EMILY FORTUNA: Hmm, go away. Hot restart? Did my computer-- don't seem to be doing anything. Let's try again. And here I thought I had just been coding so well up until this point. And so it was just not actually running or something. While this is loading up, you were looking at some GitHub pages, like documentation about that. Do you want to-- SHANNON SKIPPER: Absolutely, I was looking at the Payments Flutter plug-in documentation, just to see how we initialize the Payments SDK with our application ID, and then what we do when we click this button, which is the payment part, where it's going to put up, on the screen, a pretty credit card. And we can customize that. We probably should match it to the app. EMILY FORTUNA: Nice. SHANNON SKIPPER: And then when the user puts in all their card details, their secrets. Instead of getting those secrets back-- so we have to deal with the PCI compliance, or that sort of thing-- it's going to give us just a one-time use nonce. And so that is basically this card in a one-time use secret key that we then will pass to the Square API to either make the payment or save the card on file with the customer, which is a really more flexible way to do it. We just need to tell the customer, do you want to save this card? And if they do, then we can go buy more Shrine shopping cart items. EMILY FORTUNA: Yes, more clothes and things. SHANNON SKIPPER: Make it easy for the user. EMILY FORTUNA: Nice. OK, so this is up. We'll see if this is working. Got that. Take my money is available. SHANNON SKIPPER: Oh, nice, so the function works. EMILY FORTUNA: I don't know why it hung there. SHANNON SKIPPER: Just thinking about it. EMILY FORTUNA: What's going on. MyApp isn't defined. Oh, that's the test thing. That shouldn't be a problem. Let's try making a change somewhere else. I'm very confused. Add some exclamation points, because what could go wrong? OK. Well, we're off to a great start. SHANNON SKIPPER: It liked extracting one button, it just didn't like the second. EMILY FORTUNA: It didn't like putting in-- it didn't like hot reload is what it didn't like-- SHANNON SKIPPER: Oh. EMILY FORTUNA: --for some reason. SHANNON SKIPPER: Hmm. EMILY FORTUNA: As we build again, what would be-- I mean, this shouldn't be causing problems. Going to clear our test thing so there's no problems. OK, so we have Shrine. We've got our entry point. Part of this problem is my lack of familiarity with this app. We're figuring this out together. Let's see-- username. This is where I should-- I'm embarrassed that I am not a-- I'm going to search, on command line, our username for our login-- git grep username. Login.dart-- I'm just testing hot reload. SHANNON SKIPPER: I am just trying to do the non-dry copy-paste the button just get make sure that works. EMILY FORTUNA: It's not reloading. What's going on here? So it's not-- it's also not-- I wonder if I have some weird setting in VS Code. Because it's not even attempting-- like normally, here, it would say-- it'll say something along the hot reload. Let me try launching this from the command line. And if-- I could also try from Android Studio. SHANNON SKIPPER: See if a Flutter run works? EMILY FORTUNA: Mm-hmm. I'm wondering if I just somehow borked my Visual Studio. SHANNON SKIPPER: Oh, no EMILY FORTUNA: This is very mysterious. OK, so we've got our username, question mark? Save it. Do hot restart. OK, so it works from the command line. There's something funky in my Visual Studio Code. Maybe I'm just going to try restarting it. And if that doesn't work, I will go to Android Studio. SHANNON SKIPPER: Turning it back off and on again should always work. EMILY FORTUNA: Look at you, you've already got your-- meanwhile Shannon is dashing ahead here. SHANNON SKIPPER: I just copy-pasted the button. That's not fair. EMILY FORTUNA: No, but you had the-- you had the payment flow too. SHANNON SKIPPER: Yeah. EMILY FORTUNA: OK, hold on. I will try loading it up one more time. SHANNON SKIPPER: And I'm trying from Android Studio, just to see. EMILY FORTUNA: OK, so username-- so you got that-- question mark. Now it works. SHANNON SKIPPER: Yay. EMILY FORTUNA: So I'm just going to say Visual Studio Code was in a weird state, I think. So we add this. Because we're going to spend $120 on a backpack, plus shipping. SHANNON SKIPPER: OK. EMILY FORTUNA: And we've got two buttons. SHANNON SKIPPER: Yay. EMILY FORTUNA: All right-- SHANNON SKIPPER: Turning it off and back on again worked. EMILY FORTUNA: All right, so we've got our buttons. And right now though, take my money is going to clear our cart. So what do we do Shannon? SHANNON SKIPPER: OK, so we need to trigger the checkout the cart, or take my money, function. Which, I guess we could put it with the other model items, or define it wherever. But we basically need to-- EMILY FORTUNA: Yeah I can-- I should pull this out so it's not this huge thing. Let me-- payment-- just make a payment function. And we can do what we want with it. SHANNON SKIPPER: So I'm looking at the Square Flutter plug-in In-App Payments quick start docs. EMILY FORTUNA: Is that this one? SHANNON SKIPPER: Let's see, that is that one. EMILY FORTUNA: OK. SHANNON SKIPPER: And checking out the main functions-- actually, I think there is a link to the methods at a glance. That's in the In-App Payments Flutter plug-in. [INAUDIBLE] plug-in. [? That's where I ?] get started. EMILY FORTUNA: Is it this one? SHANNON SKIPPER: It's under master-- it's under doc/reference.markdown. EMILY FORTUNA: There we go. It's a different page. This is the-- oh, yeah, reference.markdown. OK, methods at a glance-- SHANNON SKIPPER: OK, so at a glance, we have setSquareApplicationId. So we'll need to do the-- declare a future for that and then put it somewhere where it can-- we could do it on every pay me, but we could also just do it once. Because it's like, if we were changing merchants or something fancy-- but assuming we have just one merchant, one location, or one area here, we can just do it up front. EMILY FORTUNA: Would you-- the only-- you were saying changing merchants. Would it be more changing payment-- like, who's handling the payments though? SHANNON SKIPPER: No, not really. It's more just like, if you had, for some re-- like, let's say it was Etsy, or you had multiple different-- EMILY FORTUNA: Sellers. SHANNON SKIPPER: --sellers who were on the same Shrine. But since Shrine is, let's say, one company selling-- EMILY FORTUNA: Right. Right. SHANNON SKIPPER: --we can-- I think we could do it along-- we could just call the future along with the start card entry flow, which is sort of the, let's take a payment. So these are the two functions we need to call, setSquareApplicationId and startCardEntryFlow. EMILY FORTUNA: So application ID is kind of like a-- SHANNON SKIPPER: Initializer. EMILY FORTUNA: Yes, but also like a-- words are hard-- the seller ID, sort of? SHANNON SKIPPER: Totally, yeah, it's your ID for your app. I guess you could have multiple apps. But it's basically like, this is Shrine, and here's our ID in Square's API. EMILY FORTUNA: OK. SHANNON SKIPPER: And then the startCardEntryFlow will like make magic happen. EMILY FORTUNA: OK. So string, set application-- SHANNON SKIPPER: So in this-- EMILY FORTUNA: So how do we get a Square application ID? SHANNON SKIPPER: OK, so we-- you go into-- you basically-- there's a link for, step forward, get your Square application ID. EMILY FORTUNA: OK. SHANNON SKIPPER: We can just-- we can skip this. Since we're not hitting the back end right now, we can just put "test application ID." EMILY FORTUNA: Oh, OK, it'll just-- it won't error out if you're-- if you put garbage? SHANNON SKIPPER: Well, I guess that's a good question. Maybe we should use one of my Square application IDs. EMILY FORTUNA: I mean-- SHANNON SKIPPER: Let's start with just, like-- EMILY FORTUNA: --we can test it and see what happens. SHANNON SKIPPER: Yeah, let's start with just "application ID" as the ID. EMILY FORTUNA: All right. SHANNON SKIPPER: When we really make a payment, it will then be like, wait, that's not an application. EMILY FORTUNA: OK, so we need to define-- do we need to add a pubspec and all that fun stuff? SHANNON SKIPPER: Yes. So we have-- we should have to add-- OK, here it is. We add one dependency, which is the Square In-App Payments plug-in. And it's square_in_app_payments. EMILY FORTUNA: I feel like I'm on a different page than you. Oh, here we go Here we go. SHANNON SKIPPER: Are we on the right-- are we on the same demo, get started? There's a couple different get started. OK. EMILY FORTUNA: OK, so copy that-- 1.1.0. SHANNON SKIPPER: And then the uptick is like a permissive versioning. Does that let you-- EMILY FORTUNA: Yeah, it's basically anything up to the next major version. SHANNON SKIPPER: Gotcha. So if it was 1.9.0, it would still-- EMILY FORTUNA: Yes. SHANNON SKIPPER: --do it. Or we could just pin it to one. EMILY FORTUNA: Yeah, if you have nothing, it'll just be literally that. But this allows for minor bug fixes. SHANNON SKIPPER: Semantic versioning. EMILY FORTUNA: Yes, exactly. So we got that. OK, we're running flutter packages get. And then we will import-- once this-- SHANNON SKIPPER: That should be it for getting the plug-in available. EMILY FORTUNA: So import package square. SHANNON SKIPPER: Looks like we're just doing even steps, so like step 3, step 5, step 7. EMILY FORTUNA: What are the odd steps? SHANNON SKIPPER: No odds, yeah. EMILY FORTUNA: OK, So we got that. What did I-- I just saw red. And yes, all right, so now we've got our garbage. SHANNON SKIPPER: Sweet. EMILY FORTUNA: OK, so now we've initialized our app. And now we want to, as you said, make the payment. So-- SHANNON SKIPPER: OK, so that is on to step 7-- implement the payment flow. So we need to import the Square In-App Payments. EMILY FORTUNA: One quick question-- SHANNON SKIPPER: Yes. EMILY FORTUNA: I saw, in step 6, there's configuration stuff for iOS, Android. Do we need to do-- is there anything. SHANNON SKIPPER: There's no-- it will do-- it inherits from a common, normal SAN configuration. So it's not going to match our app. We'll probably want to tweak it. EMILY FORTUNA: Ah, I see. SHANNON SKIPPER: But I think we can make it happen-- EMILY FORTUNA: Got it. Got it. SHANNON SKIPPER: --and then see how it looks. EMILY FORTUNA: OK, perfect. OK, so sorry, you were saying? SHANNON SKIPPER: So let's see, so we basically define-- we defined the on-start-- we define an on-start entry flow future. And then-- well, this doesn't have the function that we actually need to call, which is the one from that markdown. Let me find the-- it's the startCardEntryFlow. So it displays a full-screen card entry view. And it takes two callback parameters, which correspond to the possible results of the request. So I think we should define the pay me function and then call this-- call this-- call the startCardEntryFlow when we-- that basically should get us going. EMILY FORTUNA: OK. So just call-- is this asynchronous or anything? Or are we just good? SHANNON SKIPPER: Yes, it should be asynchronous. Let's see. EMILY FORTUNA: My question is, do we need to wait-- or is it-- whatever. We'll wait. Start card entry flow-- possible results-- example usage. OK, on success or on cancel-- SHANNON SKIPPER: Right, so on cancel is just if you click the Back button. I don't-- I changed my mind. I want to add something else to the cart. EMILY FORTUNA: OK. SHANNON SKIPPER: And then the on success is the function where-- yeah, where we got the nonce back. And then we want to do some processing. So the Square In-App Payments will validate for, like, this couldn't possibly be a credit card, or that date for expiration is before this one. But then you'll want to hit the actual API with that nonce in your payment information to make sure that billing address and other things correspond. EMILY FORTUNA: And just for-- to clarify for everybody, a nonce is your handle of, like, this is the valid payment. And attach to this payment so I can follow it through? SHANNON SKIPPER: Exactly. So what happens is, when we call-- when we call the function to bring up the Square payments, all the information that the user types is securely transmitted to Square. And then Square returns, to the In-App Payments, a nonce that stands for those secrets. EMILY FORTUNA: All that stuff-- got it. SHANNON SKIPPER: And so you get back a nonce. And then you get back the publicly available information. But that nonce is super important, because it's what allows you to then send that nonce along to your back end server, and then use that to either store a card on file or to go ahead and immediately make a payment. And it's just a one-time use nonce. So we get this-- we basically are asking them to put in their card secrets. And in return, we're going to get back the secret key nonce. EMILY FORTUNA: I see. So yes, that should give you confidence that, if you use Square payments, you are-- the people that are writing apps don't have access to all your credit card stuff. They just have this nonce. So they're not storing it and potentially leaking it elsewhere. SHANNON SKIPPER: Right, and you're not even-- you're not able to see the secret information. So you do, then, get out of the PCI compliance. Because it's not available to you. EMILY FORTUNA: Got it. OK, so-- brr, brr, brr-- so what do we want to put in our onCardEntryNonceRequestSuccess? SHANNON SKIPPER: OK, so let's see, let me pull up the method definition for that. onCardNonce-- so we'll want to handle-- we'll basically want to handle two different possibilities. Yeah, you have it right there. So we'll want to take the nonce that we got back and then validate it against our server. Because we want to check that the prices were right and that everything is kosher. And then we can pass that nonce on to Square APIs. You can use whatever language you want in the back end, use one of Square SDKs to just like-- here's the nonce, along with the card we'd like to add on file to the customer, or the payment we'd like to make. And then this is going to-- if it's the happy path, then In-App Payments complete card entry will let you-- you can finish up. Like, we'll want to probably show an invoice, or clear the cart, or something. You don't want to-- like, buy it again. EMILY FORTUNA: Yeah, yeah, you're like-- if it goes into vaporware, you're like, did it work? SHANNON SKIPPER: But then on exception-- so if we get an exception back-- then we won't be leaving the card. We'll stay in that card entry. And it will let us give a message, like "that zip code doesn't match what we have for you as a customer." EMILY FORTUNA: Yeah, you don't want to-- you don't want to clear the form. Pay attention, web form creators who do that. Oh my god, it's just so annoying. Sorry. [LAUGHING] Rant over. SHANNON SKIPPER: Yeah, exactly, we leave them-- if we popped out of the form, then they would have to, when they come back in, be like, OK, I'll just change the one-- oh no, I'll be re-entering my whole credit card. EMILY FORTUNA: Yeah, yeah. OK, so I'm going to just copy this. Wait, we can-- SHANNON SKIPPER: Yeah, I think that is a good template placeholder for-- yeah, so the on exception portion is really, what do you want to show the user when they're not going to be leaving the Square credit card entry? Just, hey, you can-- EMILY FORTUNA: OK, so for starters, let's just say-- we'll just print out "success," maybe, here. SHANNON SKIPPER: Perfect. EMILY FORTUNA: Success-- and card details, I guess we-- SHANNON SKIPPER: Yeah, the card details-- I think we could print out the result.nonce. Or I guess we get Dart type pending. So it should give us some of the functions available. EMILY FORTUNA: OK. SHANNON SKIPPER: Nice. EMILY FORTUNA: What's going on here? SHANNON SKIPPER: I think it wants-- EMILY FORTUNA: Oh, is this another import thing? Start card entry flow. At least on the docs, I don't see-- SHANNON SKIPPER: Oh, where startCardEntryFlow-- EMILY FORTUNA: Is defined. SHANNON SKIPPER: --is defined? Let me put the example app real quick. Because I think it's defined on the plug-in. Start card entry flow. EMILY FORTUNA: Oh, is it InAppPayments dot? SHANNON SKIPPER: Exactly, EMILY FORTUNA: OK. SHANNON SKIPPER: Yeah. EMILY FORTUNA: I thought if we imported it, it would be there. OK. All right, so-- SHANNON SKIPPER: Yeah, that should work. EMILY FORTUNA: --we go. So there's that. And then you were saying we can have, maybe, a snackbar or something appear with the error message. Or what do you-- SHANNON SKIPPER: Yeah, I think-- let's see, yeah, the show card nonce processing error gives you that thing to display. And then we can go and customize the look of that. But yeah, this is that-- calling that showCardProcessingError is the hook to get-- EMILY FORTUNA: It does-- oh, OK. SHANNON SKIPPER: --to get into the-- EMILY FORTUNA: Why does it not like message? I copied that from the thing. SHANNON SKIPPER: Does it think that an exception doesn't have a message? EMILY FORTUNA: So it should. SHANNON SKIPPER: It should, right? EMILY FORTUNA: On exception-- yeah. Whoops, not that-- SHANNON SKIPPER: We could just say something in there right now, just like oops-- or actually, in the processing error. EMILY FORTUNA: I mean, let's just try printing out the string. Because it should-- yeah, the string version should have the error message, I think. SHANNON SKIPPER: Perfect. EMILY FORTUNA: OK, so now if we try paying, things should happen, yeah? SHANNON SKIPPER: Hopefully, yeah. If we-- yes. EMILY FORTUNA: OK. Calling startCardEntryFlow should work. Because we've set up our Square application ID. So we told it which Square application. So-- EMILY FORTUNA: Oh, boy. SHANNON SKIPPER: --fingers crossed. EMILY FORTUNA: Exception. SHANNON SKIPPER: Oh. EMILY FORTUNA: OK, so missing plug-in ID-- so OK, it doesn't like garbage. SHANNON SKIPPER: It doesn't like the garbage entry. OK, that's-- EMILY FORTUNA: Shocking. SHANNON SKIPPER: It's like, something is amiss. OK, so let's-- EMILY FORTUNA: Turns out garbage is garbage. SHANNON SKIPPER: So let's get it a real application ID. EMILY FORTUNA: OK, I'm going to kill this. Because it'll be unhappy with the-- SHANNON SKIPPER: OK, so I have a bunch of application IDs. It's really easy to make one. You basically just click on your Square developer portal. And then you just say, new application. And then you can click on the ID. EMILY FORTUNA: OK, so just to tell people a little bit more, like, you go to the Square website? SHANNON SKIPPER: Yes. So let me pull up the [INAUDIBLE] again. So I think it's step 2 on the guide we're following. Let's see, where is it? Lost my tabs. The Getting Started Guide-- before you start-- create a Flutter project, add In-App Payments SDK to your project, configure a dependency, get your Square application ID. So it's, open the Square application dashboard, which is just our main developer dashboard. And so I'll sign into that. And then we have-- so like, you can just create-- click New Application. We'll type our application name, Shrine. Create Application. And here it is, our application ID. EMILY FORTUNA: Awesome. SHANNON SKIPPER: So I will shoot you that. EMILY FORTUNA: OK. All this will be grayed out, because nobody wants to read my email. [HIGH TONE] All right, so we're back with a real application ID. OK, so now-- this build failed. Oh gosh, OK, let's try again. All right, so we've got her application ID. So that sets that up, and is like, I'm going to make a transaction with Shrine on the Square servers. Oh, jeez. SHANNON SKIPPER: OK, so we have, like-- EMILY FORTUNA: Why didn't this happen before? SHANNON SKIPPER: I guess it didn't get far enough along to see that there was a version mismatch. EMILY FORTUNA: Is this in my Android's-- I'm going to pull up the iOS simulator, just for grins. So what were you-- you were saying [INAUDIBLE]?? SHANNON SKIPPER: I think that that the demo, that Shrine might be pinned to a version-- EMILY FORTUNA: It could be old. SHANNON SKIPPER: --earlier than what In-App Payments since it's a fairly new plug-in. EMILY FORTUNA: Let's move it. SHANNON SKIPPER: Yeah, can we move the Shrine version forward? EMILY FORTUNA: Yes. SHANNON SKIPPER: Awesome. EMILY FORTUNA: So is it this version, minSdk? Yeah. All right, yeah, we've got 16, and it says we need 21. Let's try that. SHANNON SKIPPER: Awesome. EMILY FORTUNA: We will hide the iOS simulator for now. SHANNON SKIPPER: Fingers crossed. EMILY FORTUNA: Oh, yes-- Android. It's weird that that didn't happen before. SHANNON SKIPPER: Yeah, it is. EMILY FORTUNA: Go away. Seems it was full of fun technical difficulties. So Shannon, have there been-- have there been funny bugs in the process of implementing this whole thing? SHANNON SKIPPER: Oh, yeah. Actually had some versioning. I was-- the other day was-- I didn't realize I was on Flutter master and was like, it doesn't work. EMILY FORTUNA: Too bleeding-edge. SHANNON SKIPPER: Yeah, then I was like, oh, wait, I should probably go to Flutter beta. And it worked. EMILY FORTUNA: Or the release, yeah. SHANNON SKIPPER: Yeah, really, even better-- stable. EMILY FORTUNA: While my computer tries to blast off to the moon-- SHANNON SKIPPER: Yes. EMILY FORTUNA: You can hear it. I guess it's working on the Android SDK version thing. OK, so it's built. So that's better, except it's a white screen. There we go. Back to where we were, we're going to buy an overpriced backpack. Take my money. SHANNON SKIPPER: Yay. EMILY FORTUNA: Oh, yeah. SHANNON SKIPPER: Awesome. EMILY FORTUNA: Look at that-- gorgeous. SHANNON SKIPPER: Sweet. EMILY FORTUNA: So now I can enter some garbage numbers, and it will tell me there's an error? SHANNON SKIPPER: Yes. Well, so actually, I'm not-- it won't-- it might not tell you there's an error. But it should do something. EMILY FORTUNA: What should I do? Do you want to test the success-- SHANNON SKIPPER: I would say try out-- let's try out just trying entry. Because there are Flutter animations out of the box that should-- OK, so that's just like a-- so it's just like, I don't recognize that card. So if you try a Flut-- try a 411. Or start with different numbers, and you should-- it should auto-detect the card. Oh, wow, that's a long-- so try, like, 41111, the canonical credit card example. Yeah, so it's going to detect your card, if it's an AmEx, or MasterCard, or Visa. And then-- EMILY FORTUNA: Expiration. SHANNON SKIPPER: And then it's doing, yeah, validation here. But it's just like, this is for possibilities. This is a permissive-- yeah, so that's too old to be possibly valid. So it's giving me a newer date. And your CVE-- OK, so now if-- so this is where that Back button in the upper left is the on cancel callback. EMILY FORTUNA: Actually, I don't want to buy an overpriced backpack. SHANNON SKIPPER: Yeah, and then the check mark is the, yes, we're going forward. And then basically, this-- the entry worked. So now it's up to you to then take that onCardNonceRequestSuccess. Because we're going to get the nonce back here. And then it's up to us to then-- oh nice, success, yay. EMILY FORTUNA: Success-- so now-- but why is it just spinning? SHANNON SKIPPER: That is a good question. What do we have defined right now under card OC? It should be calling-- oh, because we're-- EMILY FORTUNA: So it did this. SHANNON SKIPPER: So we just need to add the bit that tells it, inside of try, the In-App Payments complete card entry. So basically, it's just going to-- EMILY FORTUNA: That's just the animation? SHANNON SKIPPER: It's just the animation. It's not going to yield until we tell it. EMILY FORTUNA: I see. SHANNON SKIPPER: And the idea here is that, inside that try, you get to choose if you want to throw an exception. And then we can not leave this modal. And to leave the modal, you have to get into the happy path of complete card entry. EMILY FORTUNA: OK. SHANNON SKIPPER: And then we can define the onCardEntryComplete function for any cleanup we need to do. It's sort of the, we're leaving the modal now. EMILY FORTUNA: OK, so onCardEntryComplete-- SHANNON SKIPPER: Yeah, and so we can name that-- we can name that one arbitrarily. And it's just like, yeah, our own business logic. It doesn't have any-- EMILY FORTUNA: It just calls it. And that does that closing thing. SHANNON SKIPPER: So the whole In-App Payments plug-in is basically the ability to launch this card entry so people can put in their card. And then we get the nonce back. And you can stay in the card entry if there's errors that you want to let them resolve. But then when you finish, you get into that complete card entry, where we get to clear a cart, or show an invoice, or whatever the user feedback we want to give from this worked. EMILY FORTUNA: OK, so I probably need to do a full restart though, because-- SHANNON SKIPPER: Yes, I think it was like, oh, we're not ever leaving this. OK, and that's awesome. So you did an anonymous function for the print('yay'). EMILY FORTUNA: Yeah. So this is native code here, yeah? SHANNON SKIPPER: Yes. EMILY FORTUNA: OK, so this-- hot restart-- nothing's going to work. SHANNON SKIPPER: Yeah, while we're in this, it's sort of like a black box of, we-- don't look at what the card entry is when it's not possible to. Because that way, you don't-- you're spared having to see the secret information of the card. EMILY FORTUNA: OK, so we will rebuild. And I can enter that stuff again. And hopefully, it will close our-- the dialog, as my computer takes off again. All right, backpack added. Take my money. SHANNON SKIPPER: Yay. EMILY FORTUNA: So 411, you said? SHANNON SKIPPER: Yes, the 411 is just like the-- 4 and then ones is the signal that this credit card is just a placeholder fake. EMILY FORTUNA: Mm, OK. SHANNON SKIPPER: OK, so this should-- yep. Yay. So we're back. EMILY FORTUNA: Sweet. So now I imagine, on the Flutter side, we want to return a value that says success or whatever so that we can handle it-- handle the UI. Because this is here. We don't want to be like, why is the backpack stil in my cart? SHANNON SKIPPER: So we'll want to do that, probably, in the try before we do the complete card entry. so the onCard not success is going to be we successfully got back from the card entry and we now have a result. EMILY FORTUNA: Yes. SHANNON SKIPPER: I think if you do result dot-- it should be I can't remember the name of the two-- nonce is really what we're after here. And then so we take that nonce and we can then send it to our back end, and send it onto Square API to put it on a customer card-- EMILY FORTUNA: Wait, sorry, when you say our back end this is Square's? SHANNON SKIPPER: Square's back end, so we have SDKs in Ruby, PHB, Java, C Sharp, et cetera, or you can use our API directly to basically hit the new card for customer route. And you give it this nonce, and other information you want to send to us. And then we can save a card on file, so it just has a card ID, and so when you want to then recharge-- if they come back and want to shop again-- you can just charge that card ID and customer ID. I know that-- I was poking around in Shrine and I saw that sort of the data back end right now is spiked out as like a repository. It's just like a dart file that returns the data, so we could probably do a similar pattern of a payment service or something where we give it this nonce and then it would act like the server that you're going to have the real customer data in. EMILY FORTUNA: The server for your app-- not the Square server. SHANNON SKIPPER: Yes, the server your apps. So the normal path is you send whatever came from the mobile app. You send that nonce and the information to your back end and there you can check things like-- it's just like with a JavaScript form-- you're like, is this a valid shopping cart? Like are we willing to send you this? And also then you have your secret token on your back end, your square token. And so you're-- EMILY FORTUNA: Which is different from the nonce? EMILY FORTUNA: It's different-- it's like an OAuth token type, so it's your bearer token. So you keep that secret on your server side. EMILY FORTUNA: OK. SHANNON SKIPPER: Inside of the try, we get our nonce so we can call a function that's going to put the card on file or charge the card, and that will go to your back end server-- EMILY FORTUNA: In your back end called Square. SHANNON SKIPPER: In your back end server called Square EMILY FORTUNA: To actually do the charge. SHANNON SKIPPER: To actually do the charge and then returns to your mobile app. Either the exception saying like, hey, actually stay in this card entry, or the success, and the complete card entry can go forward. EMILY FORTUNA: So here's maybe a dumb question. Say like I don't care about like-- I don't know. I'm trying to think of a service where-- maybe games where you're buying pixels on your local-- say you have a game that's entirely local, could-- I guess it would be a similar thing to what we're going to set up here where you just have your dart stuff and it's all stored locally. Or could you-- SHANNON SKIPPER: The problem there-- you can make the HTTP requests, but you can't securely store your bearer token for using the Square ecosystem payment APIs on the mobile app. EMILY FORTUNA: On an app. SHANNON SKIPPER: Yeah, just because of security reasons. You wouldn't want to-- EMILY FORTUNA: I see. SHANNON SKIPPER: --have people be able to go into a debugger or get at those codes. So you just want to put your secrets in it just like you wouldn't want to put it in your JavaScript. It's like somebody can dig in there. Even if you obfuscate it, it's there. EMILY FORTUNA: Yeah. SHANNON SKIPPER: So it's sort of better. Usually, people most often have a backup server that's doing some functions. And then you can set up a super simple path here. Just send it the nonce. Got to have one pathway that's just like hit Square APIs now we have a secret key that they can't at. EMILY FORTUNA: OK, it's obvious that I don't do payment things. I'm like, why not keep it local? OK, cool. So as you're saying though for this-- since we don't really have a back end we're going to have-- SHANNON SKIPPER: We could either extract it to a functions like a service-- we sort of have the spiked, the fake back end for the shopping cart, items or first even do inside of try, just sort of like comment out either we raise an exception or we presume it to be successful. EMILY FORTUNA: If you're willing, let's stick it in the back end. SHANNON SKIPPER: I think that's nice because it's where the app currently is getting its source of data truth, and that's where you'd the same thing. Yeah. EMILY FORTUNA: Yeah, so where is that? SHANNON SKIPPER: That's a good question. OK. It's in the model directory and it's the projects repository dot dart file and I believe. So this is where it has all of the Vagabond stack and Stella sunglasses and Whitney belt, et cetera. EMILY FORTUNA: Oh, my goodness look at all those products. SHANNON SKIPPER: Oh, yeah. OK. EMILY FORTUNA: OK, yes, all right. So would we just want to have another class, or would we want to call it into the product repository? Probably another class. SHANNON SKIPPER: Yeah or maybe a payments repository or payment service. EMILY FORTUNA: Great. Payment repository. SHANNON SKIPPER: And just like in reality, the products repository would probably be doing an HTTP request to a back end to fetch those products. The payment service would really be doing an HTTP request that passes that nonce on. EMILY FORTUNA: OK, so this would normally be on on the server as your back end. SHANNON SKIPPER: And we have Heroku one click deploy for spinning up a back end, but it feels like this is in the right place here since our source of truth is the model. EMILY FORTUNA: Cool. OK, so what should I stick here? SHANNON SKIPPER: Oh, that's a good question. EMILY FORTUNA: We just have a function that's received nonce? SHANNON SKIPPER: I think so, yeah. EMILY FORTUNA: And so we can call this something like actually make the charge. SHANNON SKIPPER: I like it, yeah. EMILY FORTUNA: The charge and takes that nonce. SHANNON SKIPPER: Perfect. EMILY FORTUNA: And secrets. I can spell-- secrets here. Is there anything we actually want to do here to help simulate-- SHANNON SKIPPER: I mean I guess the two things you can do are not raise an error-- in which case-- EMILY FORTUNA: Done. SHANNON SKIPPER: So we've done it. Or the other case is we could raise an exception, and that exception message is going to be the user feedback-- or at least how we have it wired up right now-- is going to be the user feedback they get inside of that card entry field. So this is like hitting the server and us saying like-- EMILY FORTUNA: There is a problem with your-- SHANNON SKIPPER: Yeah, there's a problem with whatever it is that we learned like, yes, this is a valid card. EMILY FORTUNA: Your credit card was declined. SHANNON SKIPPER: That would be a problem. Your credit card was declined, or, yeah, there is a problem with the zip or something. Maybe we can do a random [? chant, ?] like have this function be-- EMILY FORTUNA: Oh, a random [INAUDIBLE].. SHANNON SKIPPER: Yeah like-- EMILY FORTUNA: OK, yeah. SHANNON SKIPPER: --you have a 10% chance of getting an exception. EMILY FORTUNA: So imports-- SHANNON SKIPPER: How do we do a random? EMILY FORTUNA: --dart math. Let's just say random, random close random. And random dot nextBool, and so if random dot nextBool-- SHANNON SKIPPER: Oh, that's fancy. EMILY FORTUNA: We can-- SHANNON SKIPPER: I love that of instead of being like, if it's less than 0.5. EMILY FORTUNA: Yes. OK, so what do we-- just raise an error or return a string because if this is a server like-- SHANNON SKIPPER: It would be-- EMILY FORTUNA: --pass the an error back-- SHANNON SKIPPER: Yeah, it would be-- EMILY FORTUNA: --a message back. SHANNON SKIPPER: Exactly. EMILY FORTUNA: So let's have this as a string I guess. SHANNON SKIPPER: Yeah. EMILY FORTUNA: It would be like JSON in practice, I think. SHANNON SKIPPER: Totally, you would be sending your HTTP request to your server, which would be hitting Square API and then giving back an error code that it would just be passing back to the mobile app. EMILY FORTUNA: Right, yeah, I guess it would be a number. SHANNON SKIPPER: Right. Yeah. Or you could on your server translate that number to the message. EMILY FORTUNA: OK, well-- SHANNON SKIPPER: It's your choice whether you sort of handle it on the Flutter or on the server end. EMILY FORTUNA: OK, so credit card was declined this is not the best checkable error messages, but you know. Return, success. Great. SHANNON SKIPPER: Perfect, I like it. EMILY FORTUNA: So we don't want that private. That would be sad. And then shopping carts we're going to say, payments repository dot actually make the charge and we have to import that. See if this works. SHANNON SKIPPER: I think you have a missing paren. EMILY FORTUNA: You're right. There you go. And this is unhappy because-- oh, yes, we want this to be static. Oops, get rid of products. Yeah, because there's no need to make an object version of that. SHANNON SKIPPER: Totally. EMILY FORTUNA: Static. Oh. Yeah, yeah. Great. OK, so now we've got this string that we would check. So our results equals that. And then we might say like if results not equal success-- SHANNON SKIPPER: Nice. EMILY FORTUNA: --then we would throw our error, yeah? SHANNON SKIPPER: Exactly. EMILY FORTUNA: So throw new-- I'm just going to do error and I'm going to pass the result. SHANNON SKIPPER: Is error the standard error class? EMILY FORTUNA: Yeah. SHANNON SKIPPER: OK. EMILY FORTUNA: Oh, wait. Yes. OK, so yes but you can't pass a message to it. We need a more specific error. SHANNON SKIPPER: Gotcha. EMILY FORTUNA: How about-- SHANNON SKIPPER: One that takes an error with an error message. EMILY FORTUNA: Yeah, this argument error is not good. What else is good? Where are subclasses? OK, AssertionError, UnsupportedError, RemoteError maybe? Let's call it RemoteError. SHANNON SKIPPER: It's remote of sorts. EMILY FORTUNA: Yeah, let's do that. RemoteError. SHANNON SKIPPER: It didn't happen on this mobile device. EMILY FORTUNA: OK. Why is it not like response? Oh, because we did it again. Results-- chargeResults. OK. What is this? Oh, gosh. Maybe this is the wrong stat description? OK, we don't want that. New error. StateError? It's not the best, but we'll call it that. Technically, we should define our own, but we're not going to do that right now. OK, cool, so that's all implemented. SHANNON SKIPPER: Nice. EMILY FORTUNA: Now we say, take my money, and we have to do this. Oops, no W's in the zipcode. Oh my gosh. SHANNON SKIPPER: Oh, I guess it doesn't balk at that because it's like some zip codes might. EMILY FORTUNA: Sure. SHANNON SKIPPER: Well, depends on the country. EMILY FORTUNA: OK, so-- SHANNON SKIPPER: So I expected that to-- EMILY FORTUNA: OK, so I think part of the problem is I need to do a hot restart because I declared this completely new class. If you have stuff that-- but and because this is the native code-- I'm going to just kill the app. SHANNON SKIPPER: Nice. EMILY FORTUNA: OK, try again. SHANNON SKIPPER: So when you create a new file it's like it's hot reloading files that already exist. EMILY FORTUNA: Yeah, basically, anytime you define like a completely new object-- like hot restart-- you can add things to existing objects, but it can't just like create new objects out of thin air. SHANNON SKIPPER: Gotcha. EMILY FORTUNA: So, yeah. So tell us more about-- while this is loading up-- fun in developing Square plugins or fun Square bugs. SHANNON SKIPPER: Yeah, absolutely. So this is the in-app payments where you want to have users pay on their own mobile device. And so we also have fun where you can your mobile app with our hardware, so that's the Reader SDK. EMILY FORTUNA: Right. SHANNON SKIPPER: So that one has a lot of fun you're-- you, actually, on that one just tell it the amount currency and that's it. It then wakes the Reader up via Bluetooth and the user pays. And the SDK itself sends the nonce along with the amount in currency, and does the full round trip back to your app. So it's designed a little different. The in-app payments is really about popping this user interaction and giving you back a nonce to handle it the way you like. Whereas the in-app payments is about using our Square hardware, the little dongle or the various readers. So that's definitely been fun to make the multiple Flutter plugins. EMILY FORTUNA: Nice. This is still hanging so when all else fails, use print debugging. SHANNON SKIPPER: Nice. EMILY FORTUNA: Guess we don't need this. SHANNON SKIPPER: So it's like, was our payments repo called? EMILY FORTUNA: Yeah. Why would this be-- so the card entry flow try actually charge. SHANNON SKIPPER: It should work. Results nonce is just a string. EMILY FORTUNA: We could also use break points, but old habits die hard. SHANNON SKIPPER: Oh, print based debugging is the best. EMILY FORTUNA: OK. SHANNON SKIPPER: Got our card. EMILY FORTUNA: Sure. About to make the charge. SHANNON SKIPPER: OK, so it got and printed a bug to make the call or no? EMILY FORTUNA: Yeah. Is it-- Did we get here? SHANNON SKIPPER: Good question. EMILY FORTUNA: This is the downside of having this thing be in native view is we have to keep restarting if we're trying to debug this part. SHANNON SKIPPER: Yes, that's the prickly portion. EMILY FORTUNA: Let's see here, if charge result does not equal success throw our poorly used StateError. We complete card entry to close it. Otherwise, if there is an exception, which there should be, then we should see that. SHANNON SKIPPER: Yes, maybe it is working and exception [? to ?] string because it should be showing us the issue. It shouldn't be leaving the model if there is an exception. So I guess-- EMILY FORTUNA: But we should also see this print statement. SHANNON SKIPPER: Oh, we totally should you're right. No, you're right. Yeah. EMILY FORTUNA: So there's something strange going on here. 411. SHANNON SKIPPER: To get to the point of the one. That's where you switch to the twos. EMILY FORTUNA: You know you guys need-- SHANNON SKIPPER: What? EMILY FORTUNA: -is a-- to be able to pass debug or something. Where it's like auto-populated with this like-- SHANNON SKIPPER: That would be good. EMILY FORTUNA: --garbage. SHANNON SKIPPER: That would be good. Save you from guessing 41111-- one key. Hey, OK, it liked that one. So that was a yay. EMILY FORTUNA: Yeah. SHANNON SKIPPER: That could be that we [? are ?] role. Maybe it's not working an exception or maybe the starting over fixed it. EMILY FORTUNA: So-- SHANNON SKIPPER: Randomness. EMILY FORTUNA: Yeah, try one more time just to see if we can, actually, oh, it's in the And I can't restart. I was going to say, let's force it to take this, but we will keep going. About to make the charge. SHANNON SKIPPER: But it's not-- EMILY FORTUNA: I need to put-- I bet it's hitting this. Why is it failing so spectacularly, not going to our error? That's the question. SHANNON SKIPPER: It doesn't seem like there's anything happening that should hang it. EMILY FORTUNA: Let's look at our-- maybe-- SHANNON SKIPPER: Is this-- EMILY FORTUNA: Maybe because we're catching on exceptions, specifically, rather than errors as well. SHANNON SKIPPER: StateError maybe not an exception. EMILY FORTUNA: Yes, I bet that's it. Yeah. Yeah, so exceptions versus errors. In fact, maybe I should be using exception, but I think we also shouldn't be saying we should catch on anything. SHANNON SKIPPER: Nice. EMILY FORTUNA: So I'm just going to say, catch. SHANNON SKIPPER: Got fewer characters. EMILY FORTUNA: Yes, and maybe we'll stick with error, I think. Well, OK-- what do we want to call this? We'll call it-- IO. No, it's not [? IOexception. ?] We'll stick with error, but yeah, I bet that's exactly the problem. So we will step through this one more time. SHANNON SKIPPER: Yay. OK, so that's the way that you can communicate without the user having to-- EMILY FORTUNA: Perfect. SHANNON SKIPPER: --redo. Awesome. EMILY FORTUNA: And so then you can go back and you got your thing. Yay! SHANNON SKIPPER: Yay, nicely done. That's awesome. EMILY FORTUNA: OK, so let me take out my print statements. If we have just one more little thing we can do-- I think on the success we should clear this guy out. We should clear our cart. SHANNON SKIPPER: Yes. EMILY FORTUNA: Should we return here like a Boolean success failure here? Rule? SHANNON SKIPPER: Yeah, that's a good question. Yeah. EMILY FORTUNA: And then we can call model dot clearCart. SHANNON SKIPPER: I think it might be even nicer to-- where we have the print yay-- just have it call a function. EMILY FORTUNA: Model dot clearCart. SHANNON SKIPPER: Exactly, yeah. EMILY FORTUNA: OK. AppStateModel, and I know I'm abusing this model. I apologize, people. Model clearCart. Great, OK. SHANNON SKIPPER: Could we even give it clearCart as the on CartEntry complete? EMILY FORTUNA: Oh, did I mess up this? StateModel. Sorry, you were saying? SHANNON SKIPPER: Oh, I was wondering if we could give it the name of the function to call for our on CartEntry complete? EMILY FORTUNA: Oh, my gosh. Yes, please. Thank you. SHANNON SKIPPER: And get rid of the [? non ?] assumption. EMILY FORTUNA: Yeah. SHANNON SKIPPER: Oh, and could we omit the model dot and have it just be clearCart? EMILY FORTUNA: I think we need-- SHANNON SKIPPER: We need it. EMILY FORTUNA: --the clearCart needs to be accessed through the model. SHANNON SKIPPER: Oh, right. Of course, yeah. EMILY FORTUNA: OK, so add our bag. Whoa. App state model dynamic is not a subtype. Oh, payment. Did I-- but I thought I was very vague on what my actual function could be. Oh, OK. Yep. All right, so the problem is [? pretty ?] button is expecting this-- just call this with a no parameters, but this payment thing has this. So I'm going to do a quick fix here. Pass and model-- I have to make this a function. That other function that are passing in-- we will just not use. We'll use underscore. SHANNON SKIPPER: Nice. EMILY FORTUNA: OK, and this should be the last thing. 41111111. OK, that's-- can we try again? SHANNON SKIPPER: Awesome. EMILY FORTUNA: Well done, so we have added in that payment flow to our app. SHANNON SKIPPER: And then we could-- with just some configuration-- like match the color, match the font, effect the error color in those models and everything. EMILY FORTUNA: Nice, and that's on these pages here. SHANNON SKIPPER: Yes, there's one in particular that has a nice graphic of the-- EMILY FORTUNA: How do we get to that page? SHANNON SKIPPER: It is in the in-app payments SDK cookbook. I sent a link to your-- EMILY FORTUNA: App payments. SHANNON SKIPPER: And it's the customized payment form. EMILY FORTUNA: Oh, I think I saw that. SHANNON SKIPPER: And it's nice because instead of a bunch of text stuff, you get the picture of the form with arrows showing you what to set for each of the-- EMILY FORTUNA: Nice. SHANNON SKIPPER: Yep, that's it. EMILY FORTUNA: Cool. Awesome. SHANNON SKIPPER: OK. EMILY FORTUNA: Well, thank you, Shannon. SHANNON SKIPPER: Thank you so much. EMILY FORTUNA: Is there any last parting words you want to share with people? SHANNON SKIPPER: No, I think consider Square for online, in person, or in-app payments and definitely try the in-app payments with Flutter. EMILY FORTUNA: Awesome. Thanks for showing us how it works. We will see you again in two weeks. [MUSIC PLAYING]
Info
Channel: Flutter
Views: 33,205
Rating: undefined out of 5
Keywords: In-app payments, Square payments, Flutter Payment, In-App Payments SDK Flutter Plugin, Payment plugin, Flutter Payment Plugin, Shrine app, Payment demo app, Square mobile payments, Flutter Material, Flutter Material Codelabs, Flutter Codelabs, shopping app payment, flutter developers, google developer experts, flutter tutorials, flutter demonstrations, code flutter, how to use flutter, flutter tutorial, google flutter, flutter development, flutter mobile apps, GDS: Yes;
Id: _necENw9CUs
Channel Id: undefined
Length: 66min 13sec (3973 seconds)
Published: Thu Apr 18 2019
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.