android in app billing hack

android in app billing hack

bruno oliveira: all right,welcome everyone. my name is bruno oliveira. and i work with androiddeveloper relations. and we're going to talk aboutin-app billing version 3. so it's pretty cool. in-app billing is actuallya pretty cool thing. and every time we talk tosomeone about in-app billing, we sort of expect themto go like this. and but sometimes, in theprevious versions of the api,

if you've worked with in-appbilling v2, they sometimes go like that. and we really don't understandwhy that happens. because if they're all ina billing spree, it's in command, right? so just to go back intime a little bit, things used to be-- well, things wereactually pretty straightforward back then.

in v2, you would just haveto make a purchase, kind of like that. and then that's done, right? easy. well, actually, i'm justoversimplifying a little bit. you actually had to have codeto handle the purchase date changes, like that. still, it's not thatmuch code. but then you have that casewhere the app could be

sleeping when you gotthat message. so you also had to have abroadcast receiver to make sure that that didn't happen,so just broadcast receiver. still not complicated, right? and then, of course, thebroadcast receiver couldn't really run for very long becauseit would get killed by the system. so it's probably goodpractice [inaudible] a service, like pretty simpleand straightforward service,

to handle that broadcastreceiver. and of course, then since youcan't credit for the purchases all the time because it's anexpensive api call, you probably need to persist atusing a database of some sort. but that's ok. everyone loves databases. it's sql. and well, why does it reallygets so complicated? and even this is anoversimplification because you

have to encrypt the database. make sure that the usersdon't tamper with it. so why does it get socomplicated with in-app billing in the previousversions? well, let's start witha simple case. so the user-- that's mydrawing of a user. again, it's probably fortunatethat i write code better than i draw pictures. but then the userbuys something.

and that gets deliveredto the app. no problem there, right? so standard. but what happens if theapplication is not there to receive the package, becauseapplications on android sometimes have otherstuff to do? so sometimes apps go tosleep on android. they take naps. so what if the appis sleeping?

then a different component hasto pick up the package. and then that component has totake care of delivering the item to the application whenit finally wakes up. and that's when the problemsmight happen because that component right there could endup losing your package. as you can see from this diagramin v2, the developer had to write many componentsthat had to work together to ensure that the user'spurchase went to the right place.

and of course, it can geta little complicated. in comparison, this is whatthe v3 diagram looks like. actually, let me use a littlebit more of the slide. no, let's make ita full slide. the diagram for v3 hasseven subcomponents. we're going to spend the nextseveral minutes discussing each component and subcomponentin detail. actually, just kidding. this is the actual diagram.

so the [inaudible] in v3, apart from glass boxes,is that api calls are now synchronous. this means that your applicationgets a response right away. so if i want to-- and it's alsomuch more straightforward to think about too. so if i want to buy somethinglike 50 gold coins, all i have to do is make a request thatlooks like buy 50 gold coins.

then if google play thinks apurchase is ok, it's going to return something that's prettyintuitive, like say ok. and you get that responseright away. if you worked with v2 before,you're probably going to remember that our advice, theadvice we gave every time is that listing items, which wecall restoring purchases, was a very, very expensiveoperation. nobody could afford to do thata whole bunch of times. so you couldn't just get awaywith calling it every single

time the app launched. you had to somehow keep aclient-side storage to know which items either owns. so that's no longer the casebecause in v3, google play implements a client-side cacheand takes care of keeping that cache in sync with the server. so whenever you make your queryto the api, you're going to hit that client-side cacheinstead of incurring that cost of a network round trip.

so you can call it as manytimes as you want. so listing the user's items hasactually become a pretty cheap operation. and you can do that asoften as you need. for example, you can do thatevery time the application starts, which we figure is apretty natural place where you might want to ask yourself, whatitems does this user own? all right, so i probably soundlike i'm trying to sell you something, right?

so if you're a developer, imean, if you're in this room, you've probably grown a healthymistrust over the years of people who stand on astage like me and then don't show you any actual code. so let's stop with the salesand talk about something that's entirely different. let's talk aboutselling stuff. so before you can make v3 apicalls, you have to check that it's actually supported.

you can do that by calling theisbillingsupported api call. the good news is that in-appbilling is actually supported on a whole variety of devices,so running froyo and above and a recent version ofthe play store. and there was actually more than90% on the day that we launched, which was backa few months ago. and right now, of course,there's much more than that. so you don't have to be tooworried about that. now how do you go ahead and geta list of the items that

the user owns? well, we just call thegetpurchases api call. and remember that this call isactually pretty cheap on v3. notice also that youget the results right away right there. so there's no funny call backsor state management, no funny business you have to implementto get the result right away. all right, so now thisis very important. it's that single moment yourapplication has been waiting

for ever since theuser launched it. it's that profound moment inmodern technology that's very hard to explain to oldergenerations, where the user actually realizes that thevirtual item or service that you're offering them is sovaluable and they like it so much that they are actuallywilling to give you real money for it. that's awesome, right? so it's at that singular momentyou have something

that's really, really, really,really very valuable. and i'm not talkingabout those $0.99 that you just made. what you have on that momentis the user's trust. so they give you their money. and they expect to getsomething in return. and nothing is more valuable toan application than that. so what happens if at thatcritical moment you take the user's money and thenyou lose a purchase?

that's going to be a verybad user experience. so again, they've paid forsomething and didn't get it. the least you can expect afterthat is a pretty bad review. and of course, if you'reunlucky, it may be very, very much worse than that, as thisvery reliable data that i in no way made up clearlydemonstrates. so you don't want the user tolose their purchase ever, which is why one of the centralpoints in v3 was to make it easier todo exactly that.

so it was to make it easiernot to lose purchases. one of the ways we do that isby making all items managed, which means that google playkeeps track of the ownership of those items for you. so going back to code, how doyou write this reliable purchase flow? first of all, you launchthe purchase screen. and that can be done bycalling getbuyintent. so that's going to get to aready-to-use intent that you

can then fire, and that'sgoing to bring you the purchase screen. and what does that look like? so i have a game callednostalgic racer. and then i have 1,000 points,1,000 nostalgic points, if you will, and that the usercan purchase. so notice that this dialogue ispretty much it's simple and to the point. and the user doesn't losecontext, because it's overlaid

on top of the game. this is an improvement welaunched a few months ago. so and there's really onlyone button in that ui. and it really says, click me. right? and so there's no contextlost, no confusion. and the result of the dialoguewhen the user clicks buy is that you're going toget the results through an activity result.

so what do you doat the point? well, at that point, you havethe purchase result. so you have the response code. you have the purchase data andthe purchase signature. everything is right there. so when i say purchase data,what i mean is just this j as an object that lookskind of like that. so you have the order id. you have the package namethat bought the product.

you have the product id, whichis also known as your sku. you have the purchase time,the purchase date, the developer payload. that's something very importantthat we're going to talk about next. and you have the purchasetoken, which is a long, alphanumeric stringthat identifies that particular purchase. so that's pretty much allthere is for a simple

application. so on startup, you callgetpurchases to figure out what the user owns. and then when the user wantsto purchase something, you call getbuyintent. that gives you a ready-to-useintent that you can fire. when you fire that intent, youget the purchase flow. and then onactivityresult,you handle that purchase. notice that it's actuallypretty hard to lose a

purchase that way. you would actually have tomake an effort to lose a purchase this way, becauseeven if the unthinkable happens and, say, yourapplication has a bug, what happens, if your applicationcrashes right after the purchase and never gets theonactivityresult, that's not a problem because then the nexttime the user is going to start the app, you're goingto call getpurchases. and you're going to realizethat the item's there.

so the user has notlost the purchase. all right, so phones arefunny things, actually. so they have a tendency tofall into all sorts of liquids, like from seato margaritas. so they have a tendency. they have an attractionto liquids that science doesn't explain. what also happens is thatphones tend to fall. but as you know, contrary topopular belief, falling

doesn't really do anythingto a phone. the problem is when the phonecollides with something massive, like say a planet. so the planet tends towin in that case. but don't worry. that's ok, because the beauty ofmanaged in-app purchases is that they're like diamonds. they're forever. so even if the user deletes theapp or even if they switch

to a different device, it's okbecause the purchase is still going to be there. and they haven't lost it. so this is good for thingsthat the user should only purchase once, like forinstance, a premium upgrade-- even if the user switchesphones, you don't want them to lose that-- an ad-free upgrade, specialitems that the user can never lose, or even level packs,content, and

so on and so forth. so for all that, it's thispurchase once and use everywhere is a prettygood approach. but sometimes, of course,you don't want a purchase to be forever. you want to implement thingslike consumable items, like health potions and whatnot, goldcoins that are put into the player's wallet and thenthey go away, things that expires like season passes.

and then you have to, of course,to purchases that are not permanent. and this is what unmanageditem was used for in v2. but of course, there are nounmanaged items in v3. so how do you do that? well, this is why we have theconsumption apis on v3. so to understand how this works,so somewhere in google play-- so there's a buildingwhere google play works. and in the basement of thatbuilding, every single user at

google has a box. and inside that box, withmy name on it, are all my virtual items. i know. i've been there. i've seen that box. when i buy a cool item, whathappens is that somebody goes in there and puts the cool iteminside the box that has my name on it.

and from that point on, i knowi own the cool item. when i subsequently ask, whatitems does bruno own, through an api call, it returns well,bruno owns a cool item. then what happens when i tellthe api to consume cool item? what happens is thatthe item goes up in flames, just like that. and then, if i ask what itemsdoes bruno own, i don't own anything at all. so consumption is the oppositeof a purchase.

when you consume, theitem goes away. as far as code goes, this ishow you consume an item. we would just call theconsumepurchase on the api. and then you give itthe purchase token associated with that item. remember that the purchase tokenis the same one that you got when you got thegetpurchases call. and then that's the purchasetoken field on that json. and then, well, thenyou call that.

and the item's goingto be consumed. now, there is one decision thatyou're going to have to make, which is when toconsume something. so it's up to you how todecide how to use the consumption api. the contract is thatwhen you consume something, it goes away. so we're going to talk about twoof the most used methods when you're using theconsumption api.

first of all, maybe you want toconsume the item when it's actually used because that's, byfar, the most intuitive way to consume an item, right? then you don't need to managethe items yourself. now the second approach, whichis pretty popular, is to consume it immediatelyupon purchase. in that case, yourapp takes care of managing the user's inventory. so you might call that inventorya wallet, a purse, a

bag, account, orany other name. but you will manage the user'sinventory in that case. so to explain methodone quickly-- so let's give an example. so again, that's me. it's a stick figurecharacter of me, anyway, somewhat slimmer. and those are the items thati own in google play. so right now, it's anempty box, right?

now, as we all know, the worldout there is a pretty dangerous place, especially forstick figure characters like myself. so i decided before i go outinto the world, i'm going to buy a health potion. so i buy a health potion. and when i do that, it appearsin my inventory. also like everybody else, i walkaround with a head point bar on top of my headthat tells me what

my life points are. so now i have a potionthat i bought. and it's on my boxin google play. and i own that potion. now i go out, and i venture intothe wild lands of bugs. and then i battle bugs,and i write code. pretty soon i realizethat i'm starting to get low on life points. and then i decide toconsume the potion.

that's when your applicationwould call the consumption api to make that potion disappearand then restore my life point bar back to whatit was before. so this is consumptionupon usage. now, that's a perfectly goodmethod, except that it has one fundamental limitation. and that has to do withhow google play thinks about numbers. so google play, at least as faras in-app purchases are

concerned, only reallyknows two numbers. and they are 0 and 1. so if the world out there isreally, really dangerous, and i think i'm going to need morethan one healing potion out there to battle bugs,then i'm going to want to buy two potions. but what happens if i tryto buy a potion when i already have one? so google play is going to lookat me, and it's going to

see that i have a potionright there. and it's going to say,that makes no sense. you already have a potion. why are you trying tobuy a second one? so clearly, this approachdoesn't work if you want to have more than one item of eachkind, which is why method two might come in handy, whichis consuming upon purchase. in this case, your applicationis actually responsible for so notice my excellent[inaudible] of a plastic bag

right there, which is myclient-side inventory or service-side. either way, it's managedby my application. so i buy a potion. and now the potion exists inmy google play inventory. then what i do is, regardlessof when the user is going to consume this, i consumethe potion right away through the api. and i credit that to myin-game inventory.

and then if the user buys asecond one, that's no problem because google play doesn't knowabout a potion anymore. so i can buy a second potion andconsume it right away and credit it to my bag. so now i have two potionsin my inventory. if you're using method two, it'svery important not only to consume upon purchase,but also upon startup. and then on startup, you checkif there any outstanding items that you should be consumed.

this is going to be necessary,for example, if your application crashed beforeit consumed the item. so suppose the usermade the purchase. the purchase went through. but then your applicationcrashed. then when your applicationrecovers and starts again, you're going to make sure thaton startup you check if there's any item in the user'sinventory that should have been consumed, but was not.

so in our example, what i'mgoing to do on startup is call getpurchases. and if i realize that i own apotion that should have been consumed, i'm going to consumeit right away and then put it into the player's inventory. you don't even have to notifythe player that you're actually doing that. you can do that inthe background. so summarizing what we have sofar-- on startup, you would

call getpurchases. then if i notice that there isa potion there that should have been consumed before,i consume it. when the user wants to make apurchase, i call getbuyintent and then launch that intentto get the purchase flow. and that's the windowthat prompts the user to buy something. then when i get theonactivityresult call back, what i do is i check if thepurchase was successful.

and then i consume the purchaseif the purchase was successful. and then when i get the consumecall back, i see if the consume was successful. if the consume was successful,then i finally add the potion to the inventory. it's very important to wait forthe last part before you actually credit the item to theuser's inventory because what may happen is that--remember that the google play

inventory is the samefor every device. so it's per user andnot per device. so if i own a potion on onedevice, then i own a potion on all devices. so when you are going to consumethat potion, you should make sure that you onlycreated the potion once. so if you try to consume thesame potion from several devices, only one of those consumptions is going to succeed.

so that's guaranteed. so this means you should onlycredit the potion once it's been successfully consumed. all right? so in-app billing v3 also bringsa long-anticipated feature that developers havebeen really asking for a long time, which is the ability toquery for a product's details from code, like for instance,title, description, and price. so if you take a look at theapi, you're going to find this

method call, which is calledgetskudetails. it's actually prettystraightforward. you just specify the list ofskus that you want to get information about. and when you make that call,the api returns the information in a bundle. and that bundle kindof looks like this. so it has your product's id,which is the sku, the type, whether it's in a product or asubscription, the price, the

title, description, soon and so forth. one thing that's pretty niceabout this is that the price is actually returned in a waythat's formatted according to the user's locale. so it's going to look right tothe user when you display it on the screen. however, one thing to be carefulabout is that price is only formatted for displayand not for parsing. so don't try to usethat as a number.

don't try to add two together. this is just a number that'sformatted for display only. and you can showit to the user. now let's talk aboutsubscriptions. so in v3, that's actuallypretty simple too. if you have used v2, you'llremember that there was a whole bunch of messages andcall backs and broadcast receivers that you had to writeto implement and to know the status of a particularsubscription

at any given time. and there were asynchronouscall backs that you had to implement. but in v3, it's much easier. they are actuallyjust like items. they're much simpler. to check if substitutions aresupported, you can call isbillingsupported api call. so it's the same as before,except you pass

subs instead of in-app. and then that's going toreturn whether or not subscription are supportedon that device. so it's important tomake that check. now launching the purchase flowfor a subscription is not really very different fromlaunching the purchase flow for a regular item. so we just use getbuyintent. but except that instead ofsaying in-app, you say subs.

and then you go past the itemtype, and then get an intent. you can launch the intent. and that brings the purchasewindow, just like did before. one thing to be carefulabout is that subscriptions can't be consumed. so you cannot use theconsumption api on a subscription. if the effect that you're tryingto accomplish is to cancel a subscription, thenthis is another way to do.

you're going to have to do thatthrough the server-side, as we're going to see next. all right, so remember how isaid that in v2 it's actually pretty difficult to knowwhat's the state of a subscription? so in v3, it's actually prettyeasy because you just call when you call getpurchaseswith the item type set to subs, it's going to return allthe subscriptions that are actually active right now.

what i mean by actually activeis that while subscriptions are either in the active stateor in the trial state-- the trial state that the usercan have before buying the subscription-- those are going to appear inyour getpurchases results. now once a subscription expires,you're not going to get any notification of that. but then the next time youcall getpurchases-- which, remember, became a prettycheap call, so every

time your application starts,you can call getpurchases. so the very next time you callgetpurchases, what's going to happen is that the expiredsubscription is no longer so it does not appearon getpurchases. so your whole logic can be, ifi see the subscription in getpurchases, then i providethe content. if i don't see the subscriptionin getpurchases, then i don't providethe content. it's much easier logic thantrying to manage what's a

subscription state atany given point. so how about canceledsubscriptions? well, we do the right thing forcanceled subscriptions as well, which is canceledsubscriptions are actually going to appear in getpurchasesuntil the end of the billing period thatthe user has paid for. so after that expires,they cease to appear. so you may see canceledsubscriptions in getpurchases. and that may happen becausethe user has paid for the

billing period, but theyhave canceled it. so on the next billingperiod, that subscription's going to expire. so as far as the developer'sconcerned, all they have to do is check if it's ingetpurchases. if it is, you deliverthe content. if it's not, youdon't deliver. now remember that google playactually now has a local cache on the client, which meansthat when a subscription

expires, it might actuallytake some time for it to disappear from getpurchases. usually that doesn't reallytake more than 24 hours, of course. so that cache is refreshedevery 24 hours at most. so you might actually see asubscription there that has already expired. but then, hopefully, that'sonly going to last 24 hours or so.

that's usually not a big deal. so when you're making any apicalls, it's very important to be careful aboutthe ui thread. so the ui thread's somethingthat's very delicate. why? because if you block the uithread, your application is going to stop responding,of course. and in the android world, imean, actually in any mobile application, that's not justsomething that's frowned upon.

the system actually punishesyour app for that. and the punishment, of course,this is very, very friendly dialogue box. so if you don't want the usersto see this dialogue box, because users just absolutelylove that dialogue box, then you shouldn't ever block theui thread by calling [inaudible] that take along time to return. particularly in the in-appbilling api, the safe ones to call are isbillingsupportedand getbuyintent.

those two you can call safelyfrom the ui thread because they're returned right away. now the other three that wetalked about are not safe to call from the ui thread becausethey might actually take a while to execute. so if you're callinggetpurchases, consumepurchase, getskudetails, remember thatthey might actually take a while to execute. so you definitely do not wantto call them from the ui

thread in order not torisk a application not responding error. so always call them from aseparate thread, async task, whatever else. but whatever you do, don'tblock the ui thread while waiting for that response. if you download the samplethat's called trivial drive, it implements that for youbecause it has asynchronous wrappers so that you canactually call them from the ui

thread and get a call backwhen they are done. now many developersdon't know this. but there's actually aserver-side api that allows you to check forsubscriptions. the way it works is this apiallows you to take a particular subscription and thenquery whether or not it's valid, when it's going toexpire, and whether or not it's going to renew itselfautomatically. it also allows you to cancel anexisting subscription from

the server side. so how do you go ahead andspecify a particular well, remember the purchase dataon the client side when you call getpurchases? so one of the fields in thatis called purchasetoken. so that identifies thatparticular subscription for that particular user. so that's the string that youneed to use on the server side to call that api.

so this is what i know of whata sample api call looks like. it's basically rest. and i'm calling the state ofthat particular subscription. so i pass it to you. your package name goes there. your subscription id, so this isthe sku of the subscription goes there. and then that's the token thatyou get from the client-side. the response looks somethinglike this.

and it gives you when thesubscription started, when it's going to expire, and thenwhether or not it's going to renew automatically. if you want to know more aboutthis api, there's that url right there. so there's the fullspecification of that api. it also allows you to cancel asubscription from the server side, which might be usefulfor your application. now, next, let's talk abouta very important

topic, which is security. now security is importantbecause, believe it or not, there are some shady charactersout there in the interwebs who mightwant to take your stuff without paying. i know, it's shocking. and they are out there onthe web, on the loose. so it's very important toimplement some measure of security in applications.

so i don't really know whatthe technical term is. but i'm going to say they'repirates, mostly because we spent a really long time drawingthis character, and i wanted to use it ina presentation. so actually, i think they'recalled crackers. but let's go with pirates. so anyway, anytime you seea purchase result in your application, you should alwaysask yourself if you should trust the purchase or not.

of course, a pirate's is goingto be-- pirates spend all day being a pirate. and their job is to convince youthat a particular purchase is correct. and of course, your job is todetect that lie and see that that purchase is a fake. so how do you go aheadand do that? well, depending on yourparticular type of app and market, piracy mightbe a small problem,

might be a big problem. that's going to dependon your type of app. but you should always think interms of risk model that takes into account several variables,such as the target audience, the item's value, thelikelihood that somebody would make a fake purchase, thetechnical difficulty in making a purchase. and based on that, make adecision on how much security to implement.

of course, in the best casescenario, we implement all possible security. but then you might have tobalance that against engineering time andso on and so forth. so it's important to beconscious of that. of course, nobody can stoppiracy all together. i would be lying to you if isaid that i had a method to stop piracy. but of course, you should alwaysmake life hard for

piracy because that's fun. so here's some of the defensesthat you can employ to make a pirate's life hard. first of all is use ourdeveloper payload, and then use signature verification,and also server-side validation. we're going to give a quickoverview of each one of those. so what's this developerpayload thing? well, developer payloadis really just a

fancy term for a tag. so it's just a tag thatgets attached to a particular purchase. it's an arbitrary string. anytime you launch the purchaseflow, you can specify what the string is. it's just a string that getsattached to the purchase. and then every single time youcredit for that purchase and you get that purchase, thatstring's going to be there.

now, of course, you can writeanything into it. but of course, one of the mostuseful applications of this is to identify the owner ofa particular purchase. why do you want to identifythe owner of a particular purchase? because then it makes it veryhard for somebody to take the purchase data and then replayit into s different device. so again, that's me, the stickfigure, of course. like everyone else--

i mean, it's a scientificfact. it's been scientifically proventhat everyone, everyone has an evil twin somewhere. now this is onurb,my evil twin. he is not nearly ashonest as i am. so last week, he picked up mydevice and did a database dump of the items that i had andthen replayed them on his device, hoping to get someof my items for free. so of course, if yourapplication is using developer

payload, this is what'sgoing to happen. so onurb, my evil twin, isgoing to run your app. and then you're going to checkwhat items does onurb own. and you're going tosee this purchase. do you see anything suspiciousat all about this purchase? well, of course. the developer payload saysit belongs to bruno. but it's onurb that'srunning the app. of course, on a realapplication, you would not use

first names because some peoplehave evil twins that have the same first name. so you would not use names, butsome unique id, like for instance, the google+ idor something like that. also it's actually pretty hardto fake that signature, because every purchase comessigned from the server. so it's not actually trivialjust to change that purchase. so speaking about signatureverification, well, what is this thing about signatureverification?

well, every application has apublic key and a private key. the private key neverleaves google. so the private key is onlyknown to google. and you know the public keyfor your application. what this means is that wheneversomebody signs something with your private keyand that, of course, by definition, has to be googlebecause we are the only ones that have the private key, thenyou can use the public key for your app to verifythat signature to know

that we signed it. so google play signs everysingle purchase that goes through google play gets signedwith your application's private key, which means that,when you get a purchase on your application, you should useyour application's public key to check that thesignature matches. if it does not, it's a fake. it's not actually difficultto implement signature verification.

but if you don't want toimplement that, that's actually availablein our sample. so trivial drive has a helperclass that does all that signature verificationfor you. so you don't even haveto implement that. now, all right, even though-- client-side signatureverification is pretty cool, right? but don't think that client-sidesecurity alone is

going to be enoughfor your app. that would be a mistake becauseregardless of how difficult you make things,the reality is that phones can be hacked. so you should definitelysecure the server side as well. so when we say server-sideverification, what we mean is that on the server side, youshould check the signature again just to make sure thatthe client does not

compromise. and then one cool thing you cando on the server, which you can't do on the client, isthat you can check the order number because the server isseeing every single purchase that comes from everysingle person. so every purchase comeswith an order number. and they should be unique. so if you ever see a secondpurchase with the same number as a purchase that you'vealready provisioned before,

then you know that that secondpurchase is a fake. so you should always check theorder number and dedup it. also, if you're doing this,make sure to secure the handshake because i've seenpretty sad stories of developers that spent countlessman hours devising this perfect server-sidesecurity system. and then their handshake waslike a boolean call. it was like a rest call thatreturned to boolean, saying true, you are validatedor not.

and then, of course, that can beeasily circumvented with a man in the middle attack. so make sure that the handshakeis secure if you're going through the trouble ofimplementing a server-side security like that. also make sure that thehandshake between the server and the client is secure andnot triggered to break. now to give a summary of thesecurity methods that we talked about.

of course, you are freenot to do anything. if you don't do anything, thenyou're pretty much vulnerable to any attack. maybe for your applicationthat's not going to matter. but then you should probablyjust add at least client-side security because if you atleast add client-side signature verification, that'sactually very easy to do because it's implementedfor you. it's available in the sample.

it's just a matter of copyingand pasting that. so if you add client-sidesignature verification, you're going to be protected againstman in the middle attacks. so you're going to be protectedagainst everyone's evil twin who tries toreplay a purchase on a different device. now if you want to go the extramile and then add a unique developer payload, ofcourse, you are also protected against the more elaborateforms of purchase replay.

and next, if you want to bereally secure, you should, of course, implement server-sidesignature verification. the server-side signatureverification's going to protect you even in the casethat the client's phone is hacked, and then the frameworkhas been compromised. so definitely think about implementing server-side security. well, so overall, it's importantto approach the problem of piracy in avery rational way.

and that means don't panic andimplement methods that are known to be effective. so at least as far as the in-appbilling platform's concerned, these are the threethat we talked about. and definitely they are the onesthat you should have in mind because they're very easyto implement-- so developer payload that identifies the userof course, then signature verification, and also doingserver-side validation. and the last topic for today,it's something that developers

have been asking usfor a long time. so i'm actually gladthat this launched. so and this is the in-appbilling sandbox. so what's the sandbox thing? well, if you remember how thingsused to be, when you wanted to test to make surethat your in-app billing implementation was workingright, you essentially had two choices when testingin-app purchase. you could just use the testskus, the purchased and

canceled skus, which are, ofcourse, fake product codes. and they always return agiven purchase result. so you could, of course,use that. and those are, of course,mock products with a mock purchase flow. they don't actually gothrough the server. and then they geta mock result. of course, if you were notsatisfied with that and, of course, if you wanted to dosomething that's closer to

production, you coulduse that. you could use a test account. and of course, if you listsomebody as a test account, the only privilege they usedto have is that they could make purchases froman application that was not yet published. but you had to use real money,had to use a real credit card, has to use a realpurchase flow. and then you would actuallyget charged for that.

of course, you could revert thecharge by refunding it. but then maybe the bank's notgoing to be happy and so on and so forth. but it's just verycomplicated. so with in-app billing sandbox,you can now test purchases with the realproducts, using the actual real purchase flow, witha real credit card. so everything is as real aspossible, except that that last step, when your credit cardis about to be charged,

that doesn't happen. so that's the onlypart that's mock. but that's the only part thatdoesn't happen is you don't get charged. but otherwise, the wholeflow is actually real. so how do you go aheadand set this up? to enable that, you simply addsome test account to the list of test accounts in thepublisher's site. so this is the androidpublisher site.

you go to account details. then you add a list of testaccounts over there, so some gmail accounts that you wantto test the app with. so apart from those accountsapart from being able to purchase from your unpublishedapp, what's going to happen when you try to publish with anyof those accounts is that you're going to see a screenjust like this. so do you notice any differencefrom the screen that we saw before?

it's exactly the same one,except there's a warning saying, this is a test purchaseand you won't actually be charged. but everything else is goingto work exactly in the same way as a real purchase would. so this means that you can testyour in-app purchase in an environment that is prettymuch identical to the real life production system. but you won't actuallybe charged.

of course, if you want to seethe in-app billing version 3 code in practice, we have asample called trivial drive. so it's an exciting drivinggame that i wrote. so you get burn fuel andthen buy more fuel. it's kind of like theexperience of having a car in real life. and it's available throughthe sdk manager. and it's also available throughthe google code site. i recommend that you get thecode directly from google code

site because it's moreup-to-date there. and pretty soon, we're goingto launch an update to that app that's going to includeself-driving cars. so summarizing, we talked aboutthe most challenging points of in-app billing v2. we talked about how in-appbilling v3 was designed to address those points and makethings much easier for developers. we talked about subscriptions.

we talked about new apifeatures, such as consumption. we talked about the new productdetails api feature. then we talked about the serverapi and gave some pointers on securitybest practices. and we also presented iabsandbox, which makes it much easier to test your appsin a real environment. so the reason we are improvingthe api constantly and adding these new features is we arealways striving to improve the developer experience because weknow that if we give you a

good experience as a developer,you can focus on what makes your apps great. so you can make a betterexperience for your users, which is everybody'sfinal goal. so i hope that the tipsthat i gave you are useful in that respect. and i thank you very much forcoming to this session. if you have any feedback, thereis the rate this session poster over there.

on previous i/os, i know that wehad real life tomatoes you could throw on stage. but that was pretty messy. so if you want to give yourfeedback, just scan that qr code over there or usethe google i/o app to give your feedback. so thank you very much. [applause]