Transcript
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
[ Silence ]
[ Applause ]
>> Thank you.
Good afternoon.
Hi, my name is James Wilson
and I'm the Engineering Manager
for the App Store in OS X.
It's great to see
so many of you here
that are interested
in getting paid.
In this session, we're going
to be talking all about how
to protect your digital
sales using receipts.
There's a huge amount of
revenue that is being generated
through In-App Purchases.
In fact, so much so that just a
few days ago, 96% of the top 25,
top grossing apps for the
iPhone were free apps,
and they were using
In-App Purchases alone
to generate enough revenue to
make them top grossing apps.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Now that's huge groundswell
of momentum and popularity
for the free with In-App
Purchase, or Freemium model,
combined with the
continued popularity of paid,
with or without In-App
Purchases,
means that there is a lot
of importance for you,
the developer, to protect
those digital sales
and to secure your revenue,
because if your app is easy
to be pirated, cracked,
stolen, and shared around,
there's no money
in that for you.
So in this session, I'm going
to introduce you to the concept
of the receipt, and the
receipt is the foundation upon
which you can build your
business model enforcement logic
directly into your app, as
well as into your servers
that are providing the
content out to your users.
We are going to introduce you
to the receipt, how it works,
what's in it, when you
get them, then we're going
to actually look down into the
code level on both iOS and OS X
as to how you actually go
about validating these receipts
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and getting the purchase
information out of them.
We'll do some -- we'll do a look
into some platform
specific concerns about how
to implement receipt
validation, and then finally,
we'll look at the test
environment that you can use
to make sure that you
ship code that's bug free.
So the receipt.
It's exactly like the
receipt that you get
when you're shopping
at a physical store.
Just like when you're
at a department store,
you're at the checkout,
you exchange your cash
for goods that you're buying.
Not only do you get those
goods that you've bought,
but you get a receipt, as well.
That little piece of paper that
verifies exactly what you paid
for in this store, and it's
that little piece of paper
that that physical store that
you're in can use as the basis
of their security
model to make sure
that their goods don't
go walking out the door
with a five finger discount.
Likewise in the App
Store, the receipt is
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that digital equivalent.
The receipt is your trusted and
verifiable record of purchase.
It's issued by the App Store
and contains signatures
and certificates and security
measures that make sure
that it came from
Apple and is unaltered,
and that it ties directly to
your app on a specific device.
Now if you're a paid app, this
is really important obviously
for implementing
copy protection,
and if you're a free app
with In-App Purchases
or a paid app using
In-App Purchases,
the receipt is what you use
to know exactly what
the customer paid for so
that you can unlock
features and content.
So understanding the receipt
and knowing how it works,
knowing how to validate it and
get the purchase information
out of it, is how you
enable your business model
in your apps directly,
as well as in servers
that you have issuing content
and assets out to customers.
Using the receipt is how you
know exactly what the user has
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
paid for.
Now if you haven't
looked into how
to do receipt validation before,
or maybe you've looked at it
but haven't yet implemented
it, this session is going
to be great for you because
we're going to take you
through a step-by-step look
at exactly how you
validate the receipt
and get the purchase
information out of it.
For my iOS developers who are
already doing receipt checking
in iOS 6 and earlier, using the
methods we had available then,
I've got some new
APIs for you and also,
a change in the receipt
format that is going
to give you even
greater flexibility
and a whole lot more power to
not only validate purchases
on your servers, but to
validate the receipts
on the device itself.
But now for my OS X developers,
I think you'll particularly
like this because the
receipt format that you know
and understand and that we've
been using since we debuted
on the -- since we
debuted the Mac App Store,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
is now the exact same receipt
format that we are using
across iOS 7 and OS X.
This gives you a unified,
trusted, validated,
proof of purchase or purchase
record across both platforms
to know exactly what the
customer has paid for.
Not only do we now have the
unified receipt model across iOS
and OS X, but the receipt
now includes two new pieces
of information.
If you've opted into the
Volume Purchase Program
to license your app out
to business and education,
there's extra information
in the receipt now
that tells you whether that
receipt is allocated to a user
or whether it has been
revoked from them,
but I think the most
exciting thing that's
in the receipt this year,
especially for you guys,
if you have a paid
app in the store,
is that we've included
information
in the receipt that's going
to let you do a transition
from being a paid app to being
a free app with In-App Purchases
without leaving behind
all the customers
that have already
paid for your app.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
[Applause] Right.
Which is important, right?
Because you can see that
there's this huge groundswell
of momentum towards the Freemium
model but you don't want
to leave behind people who've
already paid for your app.
So the receipts, this
foundation, this core upon
which you build enforcement
of your business model,
directly into your apps.
When it comes to the finding
exactly how you're going
to enforce your business
model, that's going
to be something that's
unique to your app.
Everyone's app is
different, right?
You have a different
value proposition,
different price point,
different target market,
different customers, different
expectations, everything.
Everyone's app is
unique, and the products
that you're selling,
be it the app itself
or through In-App Purchases,
they have different values.
That uniqueness needs to
translate into how you decide
to enforce your business model
and protect your revenue,
your app and servers.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So it's like a recipe.
Together we're going to
bake a cake that's going
to make sure you get paid.
Apple has got some ingredients
that we're going to bring
to the table, but there's
some that you'll need to bring
to the table as well, but the
great thing is that I'm going
to show you that all of these
things that you need to work out
and work out how you want to
approach them, they're all based
on standards that are open and
very well used and there's lots
of examples, third-party
libraries ready to go,
plus you have the flexibility
to write the code
yourself if you choose.
Because Apple gives you the
receipt format specification
itself and the receipt is
based on open standards,
everything in the
receipt is based
on an open standard that's
very well documented,
used industry wide, and is used
in many, many different ways.
We give you the receipt itself,
and we give you instructions
on how to do both on-device
validation for the receipt,
as well as validating the
receipt server-to-server,
if you have servers
out there that need
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to validate these
before issuing content.
But it's up to you guys,
you the developers,
to make some decisions
along the way
about exactly what security
level is important for you,
and I'm going to
call those out to you
as we go through these slides.
[ Pause ]
So let's start with an
understanding of the receipts.
So a receipt, again, just like
the physical receipt you get
in the store, is issued when
a transaction takes place
between the App Store
and your app.
So when your app is purchased
or updated, a receipt is issued.
If someone performs an In-App
Purchase or there are stores
that have completed
transactions,
a receipt is issued.
If you've opted into the
Great Volume Purchase Program,
when the license is
allocated or when it's revoked
from that user, a
receipt's issued.
There's also some on-demand APIs
that you can use within your app
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to get in your receipt
if it appears
that the one there is missing
or doesn't appear to be valid
for your app on this device.
So what's in these things?
So within the receipt itself,
first and foremost there's
certificates and signatures,
and this allows you to make
sure it came from Apple
and hasn't been tampered with.
There's information that ties
the receipt directly to your app
on a specific device,
so it hasn't been copied
around between people, it
hasn't been copied from one app
to another app, it's
legitimately for your app
on a single device, and then
once you know that it's trusted
and it's for your app on this
device, you can get a whole lot
of purchase information
out of that receipt.
You've got purchase information
about the app itself,
plus the In-App Purchases
that the user has made,
and there's also information
there, I mentioned as well,
about the Volume
Purchase Program,
but I think the most exciting
thing that we've added
to the receipt this year is
the initial purchase date
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and initial purchase version.
So for everyone that's got a
paid app on the store today,
and you want to make the
transition to being a free app
with In-App Purchases,
previously that used
to be quite a challenge for you,
because if you simply switch
to being a free app
with In-App Purchases,
your customers would have to go
and buy all those
In-App Purchases again,
but they've already paid for it,
and they're not going
to like that.
So now in the receipt
itself we have the date,
when the user first purchased
your app, and the version
that it was at that time.
So you can use this to
make a really informed,
intelligent decision about
what features and content
to grant this user into, so if
your app looks at the receipt
and inspects it and sees this
user bought my app before I made
the switch to being free
with In-App Purchases,
grant them into what
they've paid for,
but if they purchased your app
after you've made the transition
to being free with
In-App Purchases,
you know then not too
unlock features and content
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
until they make the purchase
and you verify that transaction
with the receipt itself.
So understanding the
receipt not only allows you
to protect your revenue
and your digital sales,
but it's also really
powerful for you
to adjust your business model
to suit changes in the market.
Now speaking of transitions,
I want to talk for a second
to iOS developers who are
already doing receipt checking
for In-App Purchases on iOS 6.
For OS X developers and anyone
else that's not doing this
receipt checking, if
you've got a moment,
hit the documentation
slide and have a look
for the receipt validation
guide,
because that's what we're going
to be going through
very shortly.
But for iOS developers who are
already doing receipt checking
on iOS 6 and earlier
-- don't panic.
Your app as it is today in the
store doing receipt checking
on iOS 6 will continue
to work in iOS 7.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
iOS 7 is binary compatible with
the receipt checking methods
that we shipped in iOS 6, but
those APIs are deprecated.
So you need to make the
jump to using the iOS 7 way
of doing receipt validation,
not just because the
receipts are deprecated,
but because now you can actually
do the receipt validation
entirely on the device itself.
You no longer need to
have a server just there
for the purpose of doing
receipt validation for you.
[ Pause ]
[ Applause ]
Thank you.
But what if you want to have one
binary on the store that works
in iOS 6 and 7, and
takes advantage
of the best possible
receipt checking methods
on both of those platforms?
Well, you can, and the way you
do this is you just Weak Link
to the iOS 7 APIs, and
if I lose any of you
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
when I said Weak
Linking, it's not nearly
as complicated as it can sound.
Weak Linking simply means check
that the API exists
before you go and use it.
This prevents your app crashing
on a platform where the method
or the API doesn't exist; it
prevents you from crashing
with an unrecognized selector
or an unresolved symbol.
So Weak Linking, first
of all, you run --
you call the Response
to Selector
to see whether a given
object will actually respond
to this method.
In this case I'm calling
App Store Receipt URL to ask
for the location of
the unified receipt.
If that returns true,
then I know that this device I'm
running on supports the new way
of doing receipt checking
with a unified receipt
and I can proceed down
that course, but when I go
to call App Store Receipt
URL, I use Perform Selector.
That way I avoid crashing
with unrecognized selector.
So this is what we mean
when we say Weak Linking,
but do this instead of
checking the system version.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
This uses the runtime to
know exactly what methods are
available and allows you
to take advantage of those
when they're available.
Much better, much
more future proof
than checking the system version
or making arbitrary
decisions based
on the running version
of the OS.
So now we're going to
delve into some code,
but I'll say this
from the outset.
Some of the code and the
concepts that we're going
to look at now might be a bit
outside your comfort zone,
especially if you're used to
dealing with Objective-C and all
of its wonderful,
developer friendly APIs
and App Kit new iKit,
because to validate receipts,
we're going to delve
into some cryptography,
and also some doddering coding
standards, but let's take it
down to the simple fact;
the receipt is just a file.
It's stored in the app bundle,
we give you an API to get a hold
of it, and it's a single
file that has purchased data
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and signatures to
check authenticity.
Don't let the acronyms and
cryptic function names we are
about to look at fool you.
In essence, all we're doing
here is opening a file,
reading it into memory,
running some functions over it,
calling some methods,
checking the return codes
and comparing values, and that's
within the reach
of any developer.
In fact, what I'm
going to take you
through is a three-step process,
and this is a three-step process
to guarantee your
revenue, protect the value
of your products, and make sure
that you retain your
customer loyalty.
Three steps.
This is the WWDC session
that pays for itself.
So step one.
You verify the signature
of the receipt.
This makes sure it
came from Apple
and it hasn't been altered.
No one's tried to add in
some extra In-App Purchases
that they haven't really made.
No one's tried to doctor up
a receipt that's not really
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
for an app that they purchased.
That's step one.
Step two is we confirm
that the receipt is
for your app on a given device.
This makes sure that no
one's just copied the receipt
between devices or has tried
to copy a receipt from one app
to another in hope
that it works.
The third step is that
now we trust the receipt
and we know it's for
our app on this device,
we get the purchase
information up out of it
and we make decisions about
what to give the customer,
because they're, at that point,
we know exactly what
they've paid for.
So let's start with step
one, verifying the signature.
Verifying the signature in
simple terms is, step one,
locate the file, and we
do that with this API here
on NSBundle we call
App Store Receipt URL
and this works in
iOS 7 and OS X.
That gives us a URL to find
the actual receipt file,
and the great thing is is
that the OS manages this
for us on both iOS and OS X.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
It keeps the receipt
file there for you.
You don't have to process
the receipt yourself anymore
like you used to.
We read the contents into memory
and we verify the signature.
Here's your first decision
point as a developer.
You need to decide what
you're going to use to verify
that signature, and the
great thing is that if you're
at all familiar with
cryptography standards
and secure exchange of data, the
receipt itself is what we refer
to as a PKCS #7 container.
Like I said, don't let
the acronyms fool you.
This is doable.
It's a PKCS #7 container, which
is an open, industry standard,
cryptographic scheme for
embedding a payload of data
around certificates
and signature
to guarantee its
origin and authenticity.
The good thing is that
because that is such an open
and very widely used standard,
you have a huge amount
of options available
to you in terms
of deciding how you
verify this signature.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
At one end of the extreme,
there are third party libraries
that you can get off the shelf
that are freely available,
put them in your
project, use them,
and they will do the
certificate validation for you.
But at the other end of the
extreme, if the security
that you want for protecting
your sales requires you
to own this code
end-to-end yourself,
then because this
is an open standard
with specifications
available, you can do that.
You could write your own code
to validate this signature
if you wanted, and of course
there's the great middle ground
of there being lots of examples
and sample code out there
that you can use and
leverage to suit your needs.
The example that we provide
in the documentation is
to use Open SSL to
verify the signature.
Open SSL is an extremely well
used cryptographic library.
It's used on many different
operating systems for millions
and millions of secure
operations,
and if we used Open SSL to
do the verification for us,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
here's what it would look like.
We assume, first of all,
you've loaded up the receipt
into memory and a copy of
Apple's Root CA Certificate
and you can find that
certificate online,
and we've stored these in two
root variables here, B receipt
and BX 509, X 509 being
the standard that's used
to encode Apple's
Root CA Certificate.
The first thing we do is we
want to take those raw bytes
from the receipt
and convert them
into a PKCS #7 data structure
so that Open SSL can work
with it at a code level.
Next, to verify the signature,
we need to tell Open SSL
who we are actually expecting
to have signed this certificate,
who we're expecting
it to have come from.
So we load up Apple's Root CA
and we create a certificate
store.
We add that certificate in.
With our certificate in hand,
and our PKCS #7 data
structure ready to go,
we simply call PKCS #7 Verify.
We check the result.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
If result's one, the
receipt is valid.
That's it.
And the great thing is
that calling PKCS #7 Verify also
actually returns back to you
that payload of data
that's inside the receipt,
the actual purchase information
that you want to get a hold
of to make decisions about
what the users purchased.
So right now, we have a receipt
that we know came from Apple
for an App Store Purchased app,
and we know that it's authentic
and hasn't been altered.
The next thing we need to do is
confirm that it is definitely
for our app on this device,
and to do that we need
to actually inspect these raw
bytes of data that we took
out from the receipt
payload itself.
Now that payload of data,
we arrange that in a series
of attributes that have
a Type and a value.
We encode these using a
standard called ASN.1.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
ASN.1 again, I know I sound like
I'm repeating myself, but again,
is a very widely used open
and industry-standard,
and it's been round for a long,
long time, and that's great
because that means that there
is a wealth of options available
for you for how to read
that doddering ASN.1 format.
ASN.1 is the abstract
syntax notation.
It allows us to write in
a textual form like this,
a description of how we've laid
out the actual bytes contained
within the receipt,
and what this body
of text here tells you is that
we have arranged those bytes
in the receipt as a
sequence of attributes.
In a cocoa sense, it would be
kind of like having NSArray full
of NSObject sub-classes
that we've created
that have a type property
and a value property.
That's essentially what we've
defined here with this block
of ASN.1, and here's your second
decision point as a developer.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
You need to decide what
you're going to use to read
that ASN.1 encoded
data at the code level.
Now documentation that we've
had on the developer site
since we debuted the Mac App
Store has included an example
of using a third-party
tool called ASN1c.
ASN1c is a third-party
tool that allows you
to essentially take this textual
representation of the data
and ASN1c creates for you
a bunch of boilerplate code
that you can copy straight
into your project, build it,
and it gives you some
functions and data structures
that you can use to actually
work with these attributes
that are in the receipt,
the actual purchase
information you want.
Another option you
could use is Open SSL.
It can also open read ASN.1
coded data, and again,
it's this wide range of
options you've got available,
from the extreme of taking
a library that already exist
to the extreme of
writing it yourself,
and all the middle
ground of finding examples
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and making informed decisions
about exactly how you
want to do this yourself.
Just remember it's a risk and
reward calculation for you.
At this end of the spectrum of
taking a third-party library
and using it, the
reward is obviously rapid
development time.
You can get this up and
running really quickly.
The risk is when you take
someone else's library
and use it, you get
their bugs, as well.
The middle ground of finding
some examples to guide you,
of finding some samples that
are out there and using some
of that code, may be
a good middle ground
if that's the level of security
you want, and at the other end
of the extreme, if your
products are so high value
that you absolutely have to
own this yourself end-to-end,
the ASN.1 specifications
are available for you,
you could write your
own parser if you want.
Here's how it would
look if we used ASN1c.
The first one I'm trying
to call there, BRD Coder,
takes in my receipt bytes
itself, the actual payload
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
of data we got, and it gives
me a data structure back
that I can use to set up a
full loop and iterate over each
of these attributes
in the receipt.
Whether you use ASN1c,
Open SSL, or roll your own,
you're generally going to end
up in some sort of full loop
to iterate over these
attributes in the receipt,
just like with an NSArray, you'd
use 4 ID Object NArray and have
that loop ready to
iterate over these things.
So getting back to
validating that the receipts
for our app on this device.
There's four attributes that
we are particularly interested
in here.
Let's go with the first
part of this equation,
that the receipt is for
your app on this device.
To know it's for your
app, you want to look
at attributes Type 2 and
3, so in that full loop,
when I'm looping
over the attributes,
checking the actual attribute
type, we'd look for Type 2
and we'd know that that's
the bundle identifier.
We'd look for Type 3 and know
that it's the bundle version.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
We can take these and compare
these to the running app.
If they match, the
receipt's for your app
and this version of your app.
You might not want to check it
against the info
paylist, though.
That's all too easy for
someone to edit the info paylist
and have it match the receipt
that they want you to validate,
so you might want to hardcode
these values into your app.
The second part of the
equation is we now know it's
for your app, but is it for
your app on this device only?
Now the way we do that
is we need three pieces
of information.
Firstly, we need the
unique identifier
for the device that
it's running on.
If you're on an iOS device,
that's your identifier
for vendor that you
find from UIDevice.
If you're on OS X, that's
the machine's globally unique
identifier, or GUID, and
there's a sample code available
on the documentation site
that shows you exactly
how to derive that GUID.
So that's the first
bit of information.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
A sequence of bytes that
uniquely identifies this device
that you're running on.
The second piece of information
we need is your bundle
identifier, because
it's for your app.
So we take these
bytes and memory
that uniquely identified
the device,
concatenate on the string of
bytes that represent your app,
the bundle identifier.
Then we take these attribute
Type 4, the opaque value.
It's just a series of bytes.
Append those series of bytes
onto this long string of bytes
that you're forming in
memory, device identifier,
bundle identifier, opaque value.
When you've got this
long string of bytes,
you create an SHA-1
hash of those,
which gives you a 20 byte hash
of this long string of bytes.
Creating a hash is easy.
You can use common crypto;
you could use Open SSL,
whatever you want to do.
If you compare that hash to the
value here in attribute Type 5
and it matches, you've
successfully confirmed it's
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
for your app on this device.
Now how does that work?
How does that convoluted set
of steps there actually confirm
that it's for your
app on this device?
Well, the reason is --
well the way it works,
is that at the time
of the transaction,
the result in this receipt
being issued by the store,
we did the exact
same calculation.
When an In-App Purchase
was made, for example,
we took the identifier of the
device making the purchase,
the bundle identifier of
the app making the purchase,
[inaudible] opaque value,
hashed it, put it in the receipt
as attribute Type 5,
and then signed it
and shipped the receipt to you.
So if your calculation of
runtime matches our calculation
of the time of purchase, it's
for your app on this device.
But now we can get into the
really interesting end of this
which is what did
they actually pay for?
When you're iterating over those
attributes within the receipt
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and you've got those
Type 2, three, four,
and five attributes out,
if you're offering In-App
Purchases, you're also going
to see one or more
Type 17 attributes.
Now the Type 17 attribute,
the value of that is actually
a nested set of attributes
that tells you about an In-App
Purchase that has been made,
so which of these Type 17
attributes contains an attribute
-- a nested attribute in there,
these Type 17 01, 02, 03, 04,
that tell you the quantity
and the product identifier
of what was purchased?
And again we give you the
ASN.1 textual representation
of how we've encoded this data
so you've got the same
options available.
You could use Open SSL to read
this, ASN.1 to generate code
to read it, write your own,
whatever suits the value
of your products and the
level of security you want.
So let's recap some of the
key technologies we've just
talked about.
Firstly, it's a PKCS
#7 container.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
I guarantee if you Google
for that, you'll be amazed
at the wealth of information
that's out there to find out how
to read those, verify them,
and validate the signature.
This is not something unique
to the App Store receipt.
It's done in millions
of different ways.
You could get Open SSL to
do it if you wanted to,
you could roll your own,
or use any other option
that's available to you.
It's up to you.
You decide the complexity.
And the actual data itself
is encoded using ASN.1,
and again the same applies.
Lots of options available to
decide how to read that data.
Now those of you that are really
in the know might be thinking
to yourself this guy doesn't
know what he's talking about.
Open SSL doesn't exist on iOS,
and if I link to it on OS X,
I get a billion and
one compile errors.
You got me, but there's
a good reason for that,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
because if you're using a
dynamically linked library
on the device that
you're running
on to verify your signature and
get your purchase information,
imagine how easy it
would be for someone
with less honorable intents to
swap that library out with one
that said everything is good.
So make sure all the
code that you are using
to validate your receipts
and get this purchase
information is linked statically
into your binary
you're submitting.
That way you are certain
that the code that's going
to confirm this,
protect your revenues,
protect your digital sales,
is the code you intended
and hasn't been messed with.
Now everything we
talked about there is
about validating the
receipt on the device itself,
which is great now that
you can do that in iOS 7
with the unified receipt format.
A lot of you are going
to have servers out there
that issue these -- these,
you know, Game Levels, assets,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
contents, periodicals,
whatever it might be,
and you don't want those servers
to just be handing those assets
out to anyone that
asks for them right?
Your servers need to
be able to validate
that these purchases
are real and authentic
and that the user
really has paid for it
to make sure you
get paid for it.
So Apple provides you
with a server-to-server
online validation service.
Here's how this works.
Your app on the device
gets the receipt.
Your app sends the
receipt up to Apple --
sorry, up to, your app sends
the receipt up to your server,
and it would usually do that as
part of the request for a piece
of content or an
asset, so for example,
if they've just bought
Game Level 5,
your app would just make
a request to your servers
and say "Hey, give
me Game Level 5,"
and it would include the
receipt in that request.
Then your server can take
that receipt and send it
to Apple's validation service.
We crack open the receipt,
confirm that the receipt
is authentic and unaltered,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and we return back to you a
JSON code, coded block of data,
that describes the
purchase that was made,
and because that's returned
in JSON, it's really easy
for your service to pause it,
no matter what platform you're
running on, but this is only
to be used for your server
to talk to our server
to validate the receipt.
It's not to be used for
your app to talk directly
to the validation service.
So if you're doing that today,
you really need to stop,
because you can now validate the
receipt on the device in itself.
And another thing
that's important to note
about the online validation
service is it can only do two
of those three steps
that we just looked at.
It can validate that the receipt
is authentic and unaltered,
and it can return back to
you the purchase information,
but it doesn't confirm that
the receipt was intended
for your app on a given device.
You still want to do
that at the device level.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So now let's look at some
platform specific implementation
details about how we
validate the receipts.
Let's start with iOS 7.
On iOS, when your
app 4 first launches,
you want to validate the
receipt as soon as possible,
and I mean way before you get
to application did
finish launching,
in fact way before you get
anywhere near UIApplication
or the main run loop.
Do this in the main function.
Do this before anything else.
Check that the receipt
exists and validate it.
Now if it doesn't exist or
it appears to be invalid
on that device, and
that can happen,
refresh it using Store Kit.
Store Kit now has the SK
Receipt Refresh Request,
which you can use to get
yourself a new receipt
to do your receipt validation,
but you don't always
need the latest
and most up-to-date receipt.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Use what's there first,
because as soon as you start one
of these SK Receipt
Refresh Requests,
the first thing the
user is going
to see is an authentication
prompt to sign
into the App Store, and
it's not acceptable to do
that on every launch, so only
do it if the receipt is missing
or appears to be invalid,
and this will require
a network connection.
So just keep that in mind when
you're working out how stringent
to be, and how secure your
business model enforcement is,
that if the device doesn't
have a network connection,
it won't be able
to get a receipt.
Now OS X is a little different.
On OS X, for an App
Store purchased app,
the receipt will
always be there.
The only time you wouldn't
see a receipt on OS X is
when you're developing the app
and you run it and you need
to get a receipt to test with,
and the receipt should always
be there, but if it appears
to be invalid, as in it's
perhaps not for this device,
which could happen if apps
are migrated from one machine
to another, then your app
exists with a Code 173.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
This special Exit Code
tells OS X in the App Store
that you believe your receipt's
invalid and you want a new one.
Again, the first thing
that your user is going
to see here is a prompt to
sign into the App Store,
even if they're already signed
in, so definitely don't do this
on every launch, only do
it if the receipt appears
to be invalid, and again,
network connection
will be required.
Now for everyone doing In-App
Purchases, whether it's on iOS
or OS X, because now OS X
supports subscription In-App
Purchases as well, there are
some differences in the type
of In-App Purchases
that are offered insofar
as how they're represented
in the receipt,
especially over the
lifecycle of the receipt.
Consumables and non-renewing
subscriptions.
They're a one-off
purchase, right?
So if you've got a racing car
game, you might offer the user
to purchase 500 gallons of gas
through an In-App Purchase.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
You would expect that that 500
gallons of gas is used once,
only on that device, and
once it's done, it's gone.
You can't restore
transactions and get 500 gallons
of gas back each time, nor would
you expect to have 500 gallons
of gas on your iPad and
then magically another 500
on your iPhone and whatever
other devices you might have.
So these are one-time purchases.
Once they're used, they go on.
Likewise in the receipt,
you'll only ever see a record
of a consumable or a
non-renewing subscription once,
and you'll only see it in
the receipt that's issued
at the time of that transaction,
at the time they
make that purchase.
It won't be present in receipts
that are issued in the future,
so if the user then
makes another purchase,
that consumable that they
bought before won't be in there.
If they restore transactions,
that consumable,
it won't be in there, so you
get one chance to see it,
one chance to validate it,
and then set whatever
state you need.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Now non-consumables
and auto-renewable subscriptions
are the exact opposite
because these are permanent
purchases that are designed
to persist across devices,
so if I bought Game Level 5,
but I then get a new iPhone,
and I restore my transactions
or I restore transactions
on another device,
I expect that Game Level 5 is
there ready for me to play.
Likewise, with auto-renewable
subscriptions.
If I subscribe on one device,
I want to be able to use
that subscription on others.
So these non-consumables
and auto-renewable subscriptions
are always in the receipt,
and they can be restored
using the Store Kit API
to restore completed
transactions.
So keep that in mind when you're
deciding how you persist state
based on In-App Purchases
that have been made.
Consumables, non-renewing
subscriptions,
only in there once.
One shot. Non-consumables and
auto-renewable subscriptions,
always in the receipt.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
But what happens if, even after
you've requested a new receipt,
it still appears to be invalid?
Your app launched, you
did the receipt checking,
it didn't look right,
you requested a new one,
checked it again, it
still didn't look right.
This doesn't mean necessarily
that something evil
is happening.
There might be reasons
why this is happening,
and you should make sure
that whatever user experience
you implement here, particularly
on iOS, is tasteful and suits
the value of your products,
and I say iOS in particular
because on iOS, apps can't quit.
There's no way to quit your app.
It keeps running.
So if the receipt appears to be
invalid and you've determined
that this user is not
eligible to use your app,
or the In-App Purchases that
they think they have based
on the receipt, then
it's up to you
to design that user experience.
You might decide to
make the app read only,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
block parts of the UI,
whatever you want to do,
but please make sure
it's tasteful
and make sure it respects
the value of your products
and what your users
would expect.
Now OS X is different because in
the model of OS X, apps can quit
and they do, and that's
exactly what we want you to do
if the receipt's invalid.
Every time.
Always Exit 173.
The App Store will handle
showing the UI for you depending
on various conditions of why
that receipt may
still be invalid.
For example, if the receipt
just plain appears to be invalid
or perhaps it looks as though
it might have been forged,
we'll present UI telling
the user please go
and re-download this app
from the purchases page,
which if they have
really bought it,
should get them a new
copy with a new receipt.
Or if you've opted into
the Volume Purchase Program
and they had a license
but it's been revoked
and they're expired,
we tell them that, too,
and encourage them to go and
buy their own copy, but on OS X,
let us drive the UI,
always just Exit 173.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
[ Pause ]
Now let's talk about
the test environment.
As powerful as this is to
protecting your revenue,
making sure you get paid, that
your app's not being ripped off,
retain those loyal customers
that are happy that they've paid
for what they're getting,
it's really powerful.
The receipt's the foundation
upon which you do that,
but a bug in this part of your
code could have some really
bad consequences.
A bug in this part of your code
could potentially give away
assets and content that maybe
the user hasn't paid for,
but even worse, a
bug in this part
of your code could
lock customers
out of things they really
actually have paid for.
We all know exactly how they're
going to tell you about that.
So Apple gives you
the test environment.
The test environment is like
a replica of the App Store
that you can use to make
transactions, get receipts,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
use In-App Purchases, without
actually exchanging any money.
The test environment allows you
to test your code in this area
for receipt validation, as well
as In-App Purchases,
really thoroughly.
You can see how your app has
with no receipt, with a receipt,
when it's invalid, when it's
refreshes, all those sorts
of code parts, and there's also
APIs in Store Kit that allow you
to get a Volume Purchase Program
license in various states
of valid, revoked, expired,
so you can really thoroughly
test this, and please do,
because it's crucial that when a
user pays money for your content
and your app, that they get it,
and that a bug doesn't
prevent them,
so the test environment is
exactly what you use to verify
that this works before
you submit your code.
So how in iOS?
Run the device from
Xcode; use the Shake It API
to get yourself a receipt.
Make sure your app is
development signed, though.
That's how the iOS App Store
knows to route your request
to the test environment.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
OS X developers.
A little bit different.
Build your app in Xcode,
run it from find to first.
Why? Because if you Exit 173
while you're debugging an Xcode,
Xcode doesn't care.
So you've got to run your
app from Finder to get
that initial receipt,
because when you Exit 173
after being launched from
Finder, the App Store
in OS X sees it, we
get you a new receipt,
but make sure you're development
signed because that's how,
again, the App Store in OS X
knows to route that request
to the test environment,
and just in case you missed
the very deliberate repetition
on those last two slides,
your app must be signed
with your development
certificate
to use the test environment.
It's the only way the OS
knows to route your request
for a receipt and also
to do In-App Purchases
to the test environment, and
not the production store.
Also another common
gotcha with this is,
sign-out of the App Store
with your production account,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
because that production account
that you use, your Apple ID
that you use to buy songs and
movies and apps, that won't work
in a test environment.
The test environment only works
with test environment accounts
that you set up for
your app only.
Now one last word, and that's
on the app submission
review process
that we all know and love.
When you're developing your app,
you're using your development
certificate, building
and compiling and code
signing with that certificate,
and that allows you to
use the test environment.
When you submit your app to
the store to get it sold,
you're assigning that with
your distribution certificate,
your production certificate,
which allows the app
to then work with the
production store to make sure
that you get paid when
you app is purchased
and when In-App Purchases
are made,
but app review is
a little different
and it's a really
important distinction,
especially when you go to
implement receipt validation.
The app reviewers are actually
testing your production site,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
ready to go into
the store binary,
but against the test
environment,
so your production signed
out will see test
environment receipts.
So don't invalidate them,
just because you think
I'm production signed
and I should never be
seeing the test environment.
That means the reviewers won't
be able to test your app,
won't be able to confirm
your receipt validation works
and your In-App Purchases
work, and it's an express
to the rejection queue.
In summary, to make sure
that your digital
sales are protected,
that your revenue is
secured, and the value
of your product is maintained
and that your products can't be
stolen, cracked, and pirated,
verify and inspect the receipts.
Now on iOS 7, just like on OS
X, you can do this on device
and you can do it
server-to-server as well,
for servers that
are issuing content.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
But choose a model
that suits you.
Remember that long
gamut of options
that you've got available,
from the third-party libraries
that are ready to go, to
rolling your own with some help
from examples and samples, to
looking at the technical specs
and writing your own code from
scratch, the point you arrive
at on that spectrum should speak
to the value of your products
and the level of
security you want
in your receipt model
enforcement.
Also, don't forget that it's
important to choose a way
of doing the receipt validation
that's a little bit unique,
because if everyone
chose the exact same way
to do receipt validation,
then it would be all too easy
for those evil folks with the
less honorable intentions to go
and find those really
common code segments
and work around them.
So make sure you do things
a little bit special,
a little bit unique to your app,
but no matter how you do it,
use the test environment,
and to use that you've got
to be development signed, using
your test environment accounts.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Now for more information,
you can contact our excellent
evangelist Paul Marcus.
We have documentation that
we're updating for iOS
and OS X available online
and that's the receipt
validation programming guide,
and of course there's the very
active Apple developer forum
as well, where you're welcome
to ask all manner of questions
about receipt validations,
Store Kit,
In-App Purchases,
whatever you need.
Thank you.
[ Applause ]
[ Silence ]