Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

/verifyReceipt endpoint is forbidden by Apple #490

Open
9 tasks done
gerchicov-bp opened this issue Sep 24, 2019 · 3 comments
Open
9 tasks done

/verifyReceipt endpoint is forbidden by Apple #490

gerchicov-bp opened this issue Sep 24, 2019 · 3 comments
Labels
answered Questions which have accepted answers. area: receipt-validation validating receipts for customer or purchase verification difficulty: advanced we really need help with this one

Comments

@gerchicov-bp
Copy link

gerchicov-bp commented Sep 24, 2019

Platform

  • iOS
  • macOS
  • tvOS

In-app purchase type

  • Consumable
  • Non-consumable
  • Auto-Renewable Subscription
  • Non-Renewing Subscription

Environment

  • Sandbox
  • Production

Version

Latest (0.15.0), maybe all versions

Related issues

Report

Issue summary

"Important: Do not call the App Store server /verifyReceipt endpoint from your app."

What did you expect to happen

verifyReceipt endpoint is not used (how it is explained in the official Apple documentation)

What happened instead

verifyReceipt endpoint is used

@canhth
Copy link

canhth commented Oct 2, 2019

Hi @gerchicov-bp,
Do you know any case got rejected by Apple because of this reason? I'm about to use this approach to avoid BE workload, hope this won't be an issue.

@gerchicov-bp
Copy link
Author

@canhth the problem I faced is another one - my customers want to have a possibility to move apps from a publisher to another and it is not possible when receipts are checked with remote server.

@revg75
Copy link

revg75 commented May 22, 2020

Yes, you are correct, Apple says:

Do not call the App Store server verifyReceipt endpoint from your app. You also never want to store your app-specific shared secret in your app binary either.

This can actually be fixed rather easily. Look at the struct AppleReceiptValidator. It conforms to the ReceiptValidator protocol. The AppleReceiptValidator calls Apple's verifyReceipt endpoint from the device which requires you to store your App-Specific Shared Secret in the binary which is not safe! You need to call Apple's verifyReceipt endpoint from your own server in order to be safe.

So in my case, I am using PHP so I make a new PHP page that mimics exactly what apple's verifyReceipt.php does, except that it only requires the receipt-data field because I will provide the app-specific shared secret from my server instead as shown below. My PHP page will return the exact same data as Apple's verifyReceipt page does so it will still be compatible with SwiftyStoreKit

<?php
header('Content-type: application/json');

$encoded = file_get_contents('php://input');
$data = json_decode($encoded, true);

const APPLE_SHARED_SECRET =               "<insert your shared secret here>";
const APPLE_RECEIPT_VERIFY_URL =          "https://buy.itunes.apple.com/verifyReceipt";
const APPLE_SANDBOX_RECEIPT_VERIFY_URL =  "https://sandbox.itunes.apple.com/verifyReceipt";

$request = json_encode(array("receipt-data" => $data["receipt-data"],"password"=>APPLE_SHARED_SECRET));
$ch = curl_init(APPLE_RECEIPT_VERIFY_URL);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $request);
$jsonresult = curl_exec($ch);
$jsonresult = json_decode($jsonresult);
$http_status = $jsonresult->status;
curl_close($ch);

if($http_status == 21007){
      $ch = curl_init(APPLE_SANDBOX_RECEIPT_VERIFY_URL);
      curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
      curl_setopt($ch, CURLOPT_POST, true);
      curl_setopt($ch, CURLOPT_POSTFIELDS, $request);
      $jsonresult = curl_exec($ch);
      $jsonresult = json_decode($jsonresult);
      $http_status = $jsonresult->status;
      curl_close($ch);
}

echo json_encode($jsonresult);
?>

Create a new struct that conforms to the ReceiptValidator protocol. Then copy and paste all the code out of AppleReceiptValidator into that struct. In the VerifyReceiptURLType enum add an entry called custom that points to a page on your server such as

case custom = "https://www.mywebsite.com/verifyReceipt.php"

Now change the default VerifyReceiptURLType to .custom in the public init function. You might get an error about how receiptStatus.isValid is inaccessible due to protection level, so instead of checking isValid just do this:

if receiptStatus == .valid {
    // The receipt is valid...
}

Since we check for the 21007 error code on our PHP page we can also get rid of that check for .testReceipt too.

I hope this helps someone 🥂 !

@Sam-Spencer Sam-Spencer added answered Questions which have accepted answers. area: receipt-validation validating receipts for customer or purchase verification difficulty: advanced we really need help with this one labels May 29, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
answered Questions which have accepted answers. area: receipt-validation validating receipts for customer or purchase verification difficulty: advanced we really need help with this one
Projects
None yet
Development

No branches or pull requests

4 participants