Passed
Push — master ( 81aa1e...cfb983 )
by Adar
02:17
created

Response::getRequestDate()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
namespace ReceiptValidator\iTunes;
3
4
use ReceiptValidator\RunTimeException;
5
use Carbon\Carbon;
6
7
class Response
8
{
9
10
  /* @var int
11
   * receipt response is valid
12
   */
13
  const RESULT_OK = 0;
14
15
  /* @var int
16
   * The App Store could not read the JSON object you provided.
17
   */
18
  const RESULT_APPSTORE_CANNOT_READ = 21000;
19
20
  /* @var int
21
   * The data in the receipt-data property was malformed or missing.
22
   */
23
  const RESULT_DATA_MALFORMED = 21002;
24
25
  /* @var int
26
   * The receipt could not be authenticated.
27
   */
28
  const RESULT_RECEIPT_NOT_AUTHENTICATED = 21003;
29
30
  /* @var int
31
   * The shared secret you provided does not match the shared secret on file for your account.
32
   * Only returned for iOS 6 style transaction receipts for auto-renewable subscriptions.
33
   */
34
  const RESULT_SHARED_SECRET_NOT_MATCH = 21004;
35
36
  /* @var int
37
   * The receipt server is not currently available.
38
   */
39
  const RESULT_RECEIPT_SERVER_UNAVAILABLE = 21005;
40
41
  /* @var int
42
   * This receipt is valid but the subscription has expired. When this status code is returned to your server,
43
   * the receipt data is also decoded and returned as part of the response.
44
   * Only returned for iOS 6 style transaction receipts for auto-renewable subscriptions.
45
   */
46
  const RESULT_RECEIPT_VALID_BUT_SUB_EXPIRED = 21006;
47
48
  /* @var int
49
   * This receipt is from the test environment, but it was sent to the production environment for verification.
50
   * Send it to the test environment instead.
51
   * special case for app review handling - forward any request that is intended for the Sandbox but was sent to Production,
52
   * this is what the app review team does
53
   */
54
  const RESULT_SANDBOX_RECEIPT_SENT_TO_PRODUCTION = 21007;
55
56
  /* @var int
57
   * This receipt is from the production environment, but it was sent to the test environment for verification.
58
   * Send it to the production environment instead.
59
   */
60
  const RESULT_PRODUCTION_RECEIPT_SENT_TO_SANDBOX = 21008;
61
62
  /**
63
   * Result Code
64
   *
65
   * @var int
66
   */
67
  protected $result_code;
68
69
  /**
70
   * bundle_id (app) belongs to the receipt
71
   *
72
   * @var string
73
   */
74
  protected $bundle_id;
75
76
  /**
77
   * item id
78
   *
79
   * @var string
80
   */
81
  protected $app_item_id;
82
83
  /**
84
   * original_purchase_date
85
   *
86
   * @var Carbon|null
87
   */
88
  protected $original_purchase_date;
89
90
  /**
91
   * request date
92
   *
93
   * @var Carbon|null
94
   */
95
  protected $request_date;
96
97
  /**
98
   * The date when the app receipt was created
99
   *
100
   * @var Carbon|null
101
   */
102
  protected $receipt_creation_date;
103
104
  /**
105
   * receipt info
106
   *
107
   * @var array
108
   */
109
  protected $receipt = [];
110
111
  /**
112
   * latest receipt
113
   *
114
   * @var string
115
   */
116
  protected $latest_receipt;
117
118
  /**
119
   * latest receipt info (for auto-renewable subscriptions)
120
   *
121
   * @var PurchaseItem[]
122
   */
123
  protected $latest_receipt_info = [];
124
125
  /**
126
   * purchases info
127
   * @var PurchaseItem[]
128
   */
129
  protected $purchases = [];
130
131
  /**
132
   * pending renewal info
133
   * @var PendingRenewalInfo[]
134
   */
135
  protected $pending_renewal_info = [];
136
137
  /**
138
   * entire response of receipt
139
   * @var ?array
140
   */
141
  protected $raw_data = null;
142
143
  /**
144
   * Response constructor.
145
   * @param array|null $data
146
   * @throws RunTimeException
147
   */
148 5
  public function __construct(?array $data = null)
149
  {
150 5
    $this->raw_data = $data;
151 5
    $this->parseData();
152 4
  }
153
154
  /**
155
   * Get Result Code
156
   *
157
   * @return int
158
   */
159 4
  public function getResultCode(): int
160
  {
161 4
    return $this->result_code;
162
  }
163
164
  /**
165
   * Set Result Code
166
   *
167
   * @param int $code
168
   * @return self
169
   */
170
  public function setResultCode(int $code): self
171
  {
172
    $this->result_code = $code;
173
174
    return $this;
175
  }
176
177
  /**
178
   * Get purchases info
179
   *
180
   * @return PurchaseItem[]
181
   */
182
  public function getPurchases()
183
  {
184
    return $this->purchases;
185
  }
186
187
  /**
188
   * Get receipt info
189
   *
190
   * @return array
191
   */
192
  public function getReceipt(): array
193
  {
194
    return $this->receipt;
195
  }
196
197
  /**
198
   * Get latest receipt info
199
   *
200
   * @return PurchaseItem[]
201
   */
202 1
  public function getLatestReceiptInfo()
203
  {
204 1
    return $this->latest_receipt_info;
205
  }
206
207
  /**
208
   * Get latest receipt
209
   *
210
   * @return string
211
   */
212 1
  public function getLatestReceipt(): string
213
  {
214 1
    return $this->latest_receipt;
215
  }
216
217
  /**
218
   * Get the bundle id associated with the receipt
219
   *
220
   * @return string
221
   */
222 1
  public function getBundleId(): string
223
  {
224 1
    return $this->bundle_id;
225
  }
226
227
  /**
228
   * A string that the App Store uses to uniquely identify the application that created the transaction.
229
   *
230
   * @return string
231
   */
232 1
  public function getAppItemId(): string
233
  {
234 1
    return $this->app_item_id;
235
  }
236
237
  /**
238
   * @return Carbon|null
239
   */
240 1
  public function getOriginalPurchaseDate(): ?Carbon
241
  {
242 1
    return $this->original_purchase_date;
243
  }
244
245
  /**
246
   * @return Carbon|null
247
   */
248 1
  public function getRequestDate(): ?Carbon
249
  {
250 1
    return $this->request_date;
251
  }
252
253
  /**
254
   * @return Carbon|null
255
   */
256 1
  public function getReceiptCreationDate(): ?Carbon
257
  {
258 1
    return $this->receipt_creation_date;
259
  }
260
261
  /**
262
   * Get the pending renewal info
263
   *
264
   * @return PendingRenewalInfo[]
265
   */
266 1
  public function getPendingRenewalInfo()
267
  {
268 1
    return $this->pending_renewal_info;
269
  }
270
271
  /**
272
   * returns if the receipt is valid or not
273
   *
274
   * @return boolean
275
   */
276 3
  public function isValid(): bool
277
  {
278 3
    return ($this->result_code == self::RESULT_OK);
279
  }
280
281
  /**
282
   * Parse Data from JSON Response
283
   *
284
   * @throws RunTimeException
285
   * @return $this
286
   */
287 5
  public function parseData(): self
288
  {
289 5
    if (!is_array($this->raw_data)) {
290 1
      throw new RuntimeException('Response must be a scalar value');
291
    }
292
293
    // ios > 7 receipt validation
294 4
    if (array_key_exists('receipt', $this->raw_data) && is_array($this->raw_data['receipt']) && array_key_exists('in_app', $this->raw_data['receipt']) && is_array($this->raw_data['receipt']['in_app'])) {
295 1
      $this->result_code = $this->raw_data['status'];
296 1
      $this->receipt = $this->raw_data['receipt'];
297 1
      $this->app_item_id = $this->raw_data['receipt']['app_item_id'];
298 1
      $this->purchases = [];
299
300 1
      if (array_key_exists('original_purchase_date_ms', $this->raw_data['receipt'])) {
301 1
        $this->original_purchase_date = Carbon::createFromTimestampUTC(intval(round($this->raw_data['receipt']['original_purchase_date_ms'] / 1000)));
302
      }
303
304 1
      if (array_key_exists('request_date_ms', $this->raw_data['receipt'])) {
305 1
        $this->request_date = Carbon::createFromTimestampUTC(intval(round($this->raw_data['receipt']['request_date_ms'] / 1000)));
306
      }
307
308 1
      if (array_key_exists('receipt_creation_date_ms', $this->raw_data['receipt'])) {
309 1
        $this->receipt_creation_date = Carbon::createFromTimestampUTC(intval(round($this->raw_data['receipt']['receipt_creation_date_ms'] / 1000)));
310
      }
311
312 1
      foreach ($this->raw_data['receipt']['in_app'] as $purchase_item_data) {
313 1
        $this->raw_data[] = new PurchaseItem($purchase_item_data);
314
      }
315
316 1
      if (array_key_exists('bundle_id', $this->raw_data['receipt'])) {
317 1
        $this->bundle_id = $this->raw_data['receipt']['bundle_id'];
318
      }
319
320 1
      if (array_key_exists('latest_receipt_info', $this->raw_data)) {
321
322
        $this->latest_receipt_info = array_map(function ($data) {
323 1
          return new PurchaseItem($data);
324 1
        }, $this->raw_data['latest_receipt_info']);
325
326
        usort($this->latest_receipt_info, function (PurchaseItem $a, PurchaseItem $b) {
327 1
          return $b->getPurchaseDate()->timestamp - $a->getPurchaseDate()->timestamp;
328 1
        });
329
      }
330
331 1
      if (array_key_exists('latest_receipt', $this->raw_data)) {
332 1
        $this->latest_receipt = $this->raw_data['latest_receipt'];
333
      }
334
335 1
      if (array_key_exists('pending_renewal_info', $this->raw_data)) {
336
        $this->pending_renewal_info = array_map(function ($data) {
337 1
            return new PendingRenewalInfo($data);
338 1
        }, $this->raw_data['pending_renewal_info']);
339
      }
340 3
    } elseif (array_key_exists('receipt', $this->raw_data)) {
341
342
      // ios <= 6.0 validation
343 2
      $this->result_code = $this->raw_data['status'];
344
345 2
      if (array_key_exists('receipt', $this->raw_data)) {
346 2
        $this->receipt = $this->raw_data['receipt'];
347 2
        $this->purchases = [];
348 2
        $this->purchases[] = new PurchaseItem($this->raw_data['receipt']);
349
350 2
        if (array_key_exists('bid', $this->raw_data['receipt'])) {
351 2
          $this->bundle_id = $this->raw_data['receipt']['bid'];
352
        }
353
      }
354 1
    } elseif (array_key_exists('status', $this->raw_data)) {
355 1
      $this->result_code = $this->raw_data['status'];
356
    } else {
357
      $this->result_code = self::RESULT_DATA_MALFORMED;
358
    }
359
360 4
    return $this;
361
  }
362
}
363