Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00% covered (danger)
0.00%
0 / 1
83.33% covered (warning)
83.33%
10 / 12
CRAP
86.96% covered (warning)
86.96%
40 / 46
Receipt
0.00% covered (danger)
0.00%
0 / 1
83.33% covered (warning)
83.33%
10 / 12
14.43
86.96% covered (warning)
86.96%
40 / 46
 __construct
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
3 / 3
 getBundleId
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
3 / 3
 getAppVersion
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
3 / 3
 getOpaque
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 3
 getSha1
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 3
 getCreationDate
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
3 / 3
 getCreationDateTimestamp
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 getInApp
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
4 / 4
 getOriginalAppVersion
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
4 / 4
 getExpirationDate
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
3 / 3
 getExpirationDateTimestamp
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 jsonSerialize
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
15 / 15
1<?php
2
3namespace Proton\IosReceiptParser;
4
5use Proton\IosReceiptParser\ASN1\SimpleDecoder;
6use Proton\IosReceiptParser\Attribute\AttributeSet;
7use Proton\IosReceiptParser\Attribute\AttributeType;
8use phpseclib3\File\ASN1;
9
10/**
11 * @psalm-import-type AttributeSequence from AttributeSet
12 */
13final class Receipt implements \JsonSerializable
14{
15    /** @var AttributeSet */
16    private $attributes;
17
18    /** @var SimpleDecoder */
19    private $decoder;
20
21    /**
22     * @psalm-param list<AttributeSet> $data
23     */
24    public function __construct(array $data, SimpleDecoder $decoder)
25    {
26        $this->attributes = new AttributeSet($data, 'receipt');
27        $this->decoder = $decoder;
28    }
29
30    /**
31     * @throws Exception\AttributeMissingException
32     */
33    public function getBundleId(): string
34    {
35        return $this->decoder->decode(
36            $this->attributes->getRequired(AttributeType::RECEIPT_BUNDLE_ID),
37            ASN1::TYPE_UTF8_STRING,
38        );
39    }
40
41    /**
42     * @throws Exception\AttributeMissingException
43     */
44    public function getAppVersion(): string
45    {
46        return $this->decoder->decode(
47            $this->attributes->getRequired(AttributeType::RECEIPT_APP_VERSION),
48            ASN1::TYPE_UTF8_STRING,
49        );
50    }
51
52    /**
53     * @throws Exception\AttributeMissingException
54     */
55    public function getOpaque(): string
56    {
57        return $this->decoder->decodeBase64(
58            $this->attributes->getRequired(AttributeType::RECEIPT_OPAQUE),
59            ASN1::TYPE_OCTET_STRING,
60        );
61    }
62
63    /**
64     * @throws Exception\AttributeMissingException
65     */
66    public function getSha1(): string
67    {
68        return $this->decoder->decodeBase64(
69            $this->attributes->getRequired(AttributeType::RECEIPT_SHA1),
70            ASN1::TYPE_NULL,
71        );
72    }
73
74    /**
75     * @throws Exception\AttributeMissingException
76     */
77    public function getCreationDate(): string
78    {
79        return $this->decoder->decode(
80            $this->attributes->getRequired(AttributeType::RECEIPT_CREATION_DATE),
81            ASN1::TYPE_IA5_STRING,
82        );
83    }
84
85    /**
86     * @throws Exception\AttributeMissingException
87     */
88    public function getCreationDateTimestamp(): string
89    {
90        return Parser::convertTimestampMs($this->getCreationDate());
91    }
92
93    public function getInApp(): array
94    {
95        return array_map(function (string $inApp): InApp {
96            $attributes = $this->decoder->decodeAttributesSet($inApp);
97            return new InApp($attributes, $this->decoder);
98        }, $this->attributes->getMulti(AttributeType::RECEIPT_IN_APP));
99    }
100
101    /**
102     * @throws Exception\AttributeMissingException
103     */
104    public function getOriginalAppVersion(): string
105    {
106        $raw = $this->attributes->get(AttributeType::RECEIPT_ORIGINAL_APP_VERSION);
107
108        return $raw === null
109            ? $this->getAppVersion()
110            : $this->decoder->decode($raw, ASN1::TYPE_UTF8_STRING);
111    }
112
113    /**
114     * @throws Exception\AttributeMissingException
115     */
116    public function getExpirationDate(): string
117    {
118        return $this->decoder->decode(
119            $this->attributes->getRequired(AttributeType::RECEIPT_EXPIRATION_DATE),
120            ASN1::TYPE_IA5_STRING,
121        );
122    }
123
124    /**
125     * @throws Exception\AttributeMissingException
126     */
127    public function getExpirationDateTimestamp(): string
128    {
129        return Parser::convertTimestampMs($this->getExpirationDate());
130    }
131
132    /**
133     * @throws Exception\AttributeMissingException
134     */
135    public function jsonSerialize(): array
136    {
137        $return = [];
138
139        foreach ([
140            AttributeType::RECEIPT_APP_ITEM_ID => 0,
141            AttributeType::RECEIPT_BUNDLE_ID => $this->getBundleId(),
142            AttributeType::RECEIPT_APP_VERSION => $this->getAppVersion(),
143            AttributeType::RECEIPT_CREATION_DATE => $this->getCreationDate(),
144            AttributeType::RECEIPT_CREATION_DATE_MS => $this->getCreationDateTimestamp(),
145            AttributeType::RECEIPT_IN_APP => array_map(function (InApp $inApp): array {
146                return $inApp->jsonSerialize();
147            }, $this->getInApp()),
148            AttributeType::RECEIPT_ORIGINAL_APP_VERSION => $this->getOriginalAppVersion(),
149            AttributeType::RECEIPT_EXPIRATION_DATE => $this->getExpirationDate(),
150            AttributeType::RECEIPT_EXPIRATION_DATE_MS => $this->getExpirationDateTimestamp(),
151        ] as $type => $value) {
152            $return[AttributeType::getJsonFieldName($type)] = $value;
153        }
154
155        return $return;
156    }
157}