I am seeing the same behavior with sandbox purchases on macOS: first purchase with a new sandbox user succeeds then fails to renew with DID_FAIL_TO_RENEW
subsequent purchases with the same user succeed then are canceled at renewal time with 'expiration_intent': '1'
Is the issue still under investigation? What were the results of the previous investigation @rkubota ?
As it stands now we have no way to test subscription renewal other than to deploy to production and pray.
Post
Replies
Boosts
Views
Activity
StoreKit V2 (Swift-only and only in iOS 15+) allows you to set a field when making a purchase that you can retrieve later in the appstore server API (still in beta) to find the account of the user (in your system) belonging to the Apple account that made the purchase. This field can't be set using the Objective-C StoreKit apis
https://developer.apple.com/documentation/storekit/product/purchaseoption/3749440-appaccounttoken
https://developer.apple.com/documentation/appstoreserverapi/appaccounttoken
I am trying to implement this and it is not working. I don't know which certificate(s) I am supposed to use, and I'm having a hard time believing that there isn't some page of documentation we're all missing that properly addresses the intended best practices here.
For example: https://developer.apple.com/documentation/sign_in_with_apple/fetch_apple_s_public_key_for_verifying_token_signature
Here it is much more clear what we are intended to do. I've actually implemented this and it works. I see other posts here and elsewhere on the internet on this same topic and in most cases, people aren't really sure what to do, and most of the answers are misunderstanding the basic issue. There are one or two who say they've gotten something working in Golang or something, but I wonder how reliable those solutions are.
Please provide better documentation on securing appstore server notifications (with the REST api this is less of an issue for obvious reasons)
Please provide a specific endpoint to fetch a public key for this, and document it clearly.
Please do not respond with a link to https://www.apple.com/certificateauthority/. That link is already available, I get the urge to save time by reusing an existing page as the solution for how to implement this new feature but it's not helpful.
Thanks
if you are using node there is this: https://github.com/agisboye/app-store-server-api
I'm using python in an environment where I can't install any new native libs.. but this seems to work (I just copied what the above project does, I have no idea if it's best practice etc)
import base64
from asn1crypto import x509, pem
from jose import jwt
_APPLE_ROOT_CA_G3_FINGERPRINT = "63 34 3A BF B8 9A 6A 03 EB B5 7E 9B 3F 5F A7 BE 7C 4F 5C 75 6F 30 17 B3 A8 C4 88 C3 65 3E 91 79"
def verifyAppleSignedPayload(jwsData):
headers = jwt.get_unverified_headers(jwsData)
certs = map(lambda x: x509.Certificate.load(base64.b64decode(x)), headers['x5c'])
# root certificate is Apple ROOT CA G3
root = certs[-1]
if root.sha256_fingerprint != _APPLE_ROOT_CA_G3_FINGERPRINT:
raise ValueError("Expected Apple Root CA G3, found {}".format(root.subject.human_friendly))
# each cert in the list was issued by the next one
for i, cert in enumerate(certs[:-1]):
if cert.issuer.sha256 != certs[i+1].subject.sha256:
raise ValueError("Cert chain not valid: {} not issued by cert {}".format(cert.subject.human_friendly, certs[i+1].subject.human_friendly))
pk = pem.armor(u"PUBLIC KEY", certs[0].public_key.dump())
return jwt.decode(jwsData, pk, 'ES256')
When I fetch the notification history, it's also missing there. We can refetch the data to get some of the missing details such as expire date - in fact we are usually doing this anyway, which is probably an abuse of the appstore server API - but our code is expecting the documented format, and we had always planned to get rid of the re-fetch step, and we keep logs of the other details in the transaction info so it's still not ideal.
Just now noticed there was a previous thread about this, apologies
https://developer.apple.com/forums/thread/718661
Edit: from the data they attached, this could be a different issue actually although it's similar.
I created a ticket in feedback assistant: FB11938220
We usually see only 2-3 of these a month but in the last 3 days it has gone up a lot. It still works most of the time but can be bad especially if the user was creating a new account in our app when this happens. It's not super-clear, but it looks like they are recommending to always fetch this fresh since they might change the keys. We are going to increase our timeout for this to 10s (it was 5s), and cache the result for 10m but re-fetch it if the key we are looking for isn't in cache.