protonlabs / ios-receipt-parser
Utility to parse Apple-issued PKCS#7 container with receipts
Installs: 24 818
Dependents: 0
Suggesters: 0
Security: 0
Stars: 3
Watchers: 2
Forks: 6
Language:HTML
Requires
- php: >= 7.3
- phpseclib/phpseclib: ^2.0
Requires (Dev)
- dms/phpunit-arraysubset-asserts: ^0.2.0
- phpunit/phpunit: ^9.3
- symfony/process: ^5.1
Suggests
- symfony/process: To run OpenSSL in process to actually verify PKCS#7 container signatures
This package is not auto-updated.
Last update: 2024-12-12 06:29:31 UTC
README
This library is forked from cthulhu/ios-receipt-parser and mantained by Proton.
Description
This library can be used to parse Apple billing receipts without calling Apple servers. The parser is built in accordance with the official Apple documentation. More information can be found at the following links:
This parser is required, in particular, when you use the latest XCode features to test your billing flow since receipts generated using this method are signed by your local special-purpose certificate and can not be validated via Apple's own servers. In all other cases you should strongly prefer to validate all receipts against Apple servers.
Installation
You can install this library via Composer:
composer install protonlabs/ios-receipt-parser
Usage
The main use case is to parse receipt generated by StoreKit locally. There is no need to validate the signatures.
<?php use Proton\IosReceiptParser\InApp; use Proton\IosReceiptParser\Parser; use Proton\IosReceiptParser\Receipt; include __DIR__ . '/vendor/autoload.php'; const RECEIPT = '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'; $receipt = (new Parser())->parseUnverified(RECEIPT); // Just for documentation purpose assert($receipt instanceof Receipt); var_dump($receipt->getBundleId()); foreach ($receipt->getInApp() as $inApp) { // Just for documentation purpose assert($inApp instanceof InApp); var_dump("{$inApp->getQuantity()} x {$inApp->getProductIdentifier()}"); }
Verifying signatures
It is possible to verify the signatures by passing to Parser
an instance of OpenSslProcessPkcs7Reader
(PKCS#7 reader).
At the momenr only this type of reader is implemented which requires you to:
- install
symfony/process
; - have
openssl
installed in your system; - set your PHP configuration to allow the execution of shell scripts.
<?php use Proton\IosReceiptParser\ASN1\OpenSslProcessPkcs7Reader; use Proton\IosReceiptParser\Parser; include __DIR__ . '/vendor/autoload.php'; $parser = new Parser(new OpenSslProcessPkcs7Reader()); // certificates must be PEM-encoded x.509 certificates $receipt = $parser->parseUsingOnlyTrustedCerts(RECEIPT, CERT_OR_PATH_TO_CERT, OTHER_CERT); var_dump($receipt->getBundleId());
The use of parseUsingOnlyTrustedCerts
guarantees that the signature in PKCS#7 has been validated against one of the certificates you have provided. The certificates are considered trusted (their own signatures are not verified). That means it is strongly suggested to pass to this function only trusted certificates.
Documentation
Check out the documentation for the library here.
Code coverage
Check out the code coverage for the library here.
Contributing
All contributions are welcome. Please see this page before you get started.
License
Project License can be found here.