diff --git a/src/FinanceOrder.php b/src/FinanceOrder.php new file mode 100644 index 0000000..33dc427 --- /dev/null +++ b/src/FinanceOrder.php @@ -0,0 +1,114 @@ +json); + + // Creates SHA hashes for string files in each project. + foreach ($this->locales as $language => $strings) { + $sha[$language . '.lproj/' . self::FILE_TYPE . '.strings'] = hash(self::HASH_ALGO, $strings); + } + + foreach ($this->files as $name => $path) { + $sha[$name] = hash(self::HASH_ALGO, file_get_contents($path)); + } + + foreach ($this->remote_file_urls as $name => $url) { + $sha[$name] = hash(self::HASH_ALGO, file_get_contents($url)); + } + + foreach ($this->files_content as $name => $content) { + $sha[$name] = hash(self::HASH_ALGO, $content); + } + + return json_encode((object)$sha); + } + + /** + * Creates .pkpass zip archive. + * + * @param string $manifest + * @param string $signature + * @return string + * @throws PKPassException + */ + protected function createZip($manifest, $signature) + { + // Package file in Zip (as .order) + $zip = new ZipArchive(); + $filename = tempnam($this->tempPath, self::FILE_TYPE); + if (!$zip->open($filename, ZipArchive::OVERWRITE)) { + throw new PKPassException('Could not open ' . basename($filename) . ' with ZipArchive extension.'); + } + $zip->addFromString('signature', $signature); + $zip->addFromString('manifest.json', $manifest); + $zip->addFromString(self::PAYLOAD_FILE, $this->json); + + // Add translation dictionary + foreach ($this->locales as $language => $strings) { + if (!$zip->addEmptyDir($language . '.lproj')) { + throw new PKPassException('Could not create ' . $language . '.lproj folder in zip archive.'); + } + $zip->addFromString($language . '.lproj/' . self::FILE_TYPE . '.strings', $strings); + } + + foreach ($this->files as $name => $path) { + $zip->addFile($path, $name); + } + + foreach ($this->remote_file_urls as $name => $url) { + $download_file = file_get_contents($url); + $zip->addFromString($name, $download_file); + } + + foreach ($this->files_content as $name => $content) { + $zip->addFromString($name, $content); + } + + $zip->close(); + + if (!file_exists($filename) || filesize($filename) < 1) { + @unlink($filename); + throw new PKPassException('Error while creating order.order. Check your ZIP extension.'); + } + + $content = file_get_contents($filename); + unlink($filename); + + return $content; + } +} diff --git a/src/PKPass.php b/src/PKPass.php index b8e1e45..f46ade5 100644 --- a/src/PKPass.php +++ b/src/PKPass.php @@ -23,6 +23,9 @@ */ class PKPass { + const FILE_TYPE = 'pass'; + const FILE_EXT = 'pkpass'; + const MIME_TYPE = 'application/vnd.apple.pkpass'; /** * Holds the path to the certificate. * @var string @@ -55,7 +58,7 @@ class PKPass /** * Holds the JSON payload. - * @var object|array + * @var string */ protected $json; @@ -312,7 +315,7 @@ public function create($output = false) // Output pass header('Content-Description: File Transfer'); - header('Content-Type: application/vnd.apple.pkpass'); + header('Content-Type: ' . self::MIME_TYPE); header('Content-Disposition: attachment; filename="' . $this->getName() . '"'); header('Content-Transfer-Encoding: binary'); header('Connection: Keep-Alive'); @@ -332,9 +335,9 @@ public function create($output = false) */ public function getName() { - $name = $this->name ?: 'pass'; + $name = $this->name ?: self::FILE_TYPE; if (!strstr($name, '.')) { - $name .= '.pkpass'; + $name .= '.' . self::FILE_EXT; } return $name; diff --git a/tests/FinanceOrderTest.php b/tests/FinanceOrderTest.php new file mode 100644 index 0000000..98296a4 --- /dev/null +++ b/tests/FinanceOrderTest.php @@ -0,0 +1,100 @@ +assertIsString($orer); + $this->assertGreaterThan(100, strlen($orer)); + $this->assertStringContainsString('logo.png', $orer); + $this->assertStringContainsString('ws03-xs-red.jpg', $orer); + $this->assertStringContainsString('manifest.json', $orer); + + // try to read the ZIP file + $temp_name = tempnam(sys_get_temp_dir(), 'pkpass'); + file_put_contents($temp_name, $orer); + $zip = new ZipArchive(); + $res = $zip->open($temp_name); + $this->assertTrue($res, 'Invalid ZIP file.'); + $this->assertEquals(count($expected_files), $zip->numFiles); + + // extract zip to temp dir + $temp_dir = $temp_name . '_dir'; + mkdir($temp_dir); + $zip->extractTo($temp_dir); + $zip->close(); + echo $temp_dir; + foreach ($expected_files as $file) { + $this->assertFileExists($temp_dir . DIRECTORY_SEPARATOR . $file); + } + } + + public function testBasicGeneration() + { + $pass = new FinanceOrder(__DIR__ . '/fixtures/example-certificate.p12', 'password'); + $pass->setData([ + "createdAt" => "2024-02-01T19:45:50+00:00", + "merchant" => [ + "displayName" => "Luma", + "merchantIdentifier" => "merchant.com.pkpass.unit-test", + "url" => "https://demo-store.test/", + 'logo' => 'logo.png', + ], + "orderIdentifier" => "1", + "orderManagementURL" => "https://demo-store.test/sales/order/view", + 'orderNumber' => '#000000001', + "orderType" => "ecommerce", + "orderTypeIdentifier" => "order.com.pkpass.unit-test", + 'payment' => [ + 'summaryItems' => [ + [ + 'label' => 'Shipping & Handling', + 'value' => [ + 'amount' => '5.00', + 'currency' => 'USD', + ] + ], + ], + 'total' => [ + 'amount' => '36.39', + 'currency' => 'USB', + ], + 'status' => 'paid' + ], + "status" => "open", + "updatedAt" => "2024-02-01T19:45:50+00:00", + 'customer' => [ + 'emailAddress' => 'roni_cost@example.com', + 'familyName' => 'Veronica', + 'givenName' => 'Costello', + ], + 'lineItems' => [ + [ + 'image' => 'ws03-xs-red.jpg', + 'price' => [ + 'amount' => '31.39', + 'currency' => 'USD', + ], + 'quantity' => 1, + 'title' => 'Iris Workout Top', + 'sku' => 'WS03-XS-Red', + ], + ], + "schemaVersion" => 1, + ]); + $pass->addFile(__DIR__ . '/fixtures/order/logo.png'); + $pass->addFile(__DIR__ . '/fixtures/order/ws03-xs-red.jpg'); + $value = $pass->create(); + $this->validateOrder($value, [ + 'logo.png', + 'ws03-xs-red.jpg', + 'manifest.json', + 'order.json', + 'signature', + ]); + } +} \ No newline at end of file diff --git a/tests/fixtures/order/logo.png b/tests/fixtures/order/logo.png new file mode 100644 index 0000000..9841d93 Binary files /dev/null and b/tests/fixtures/order/logo.png differ diff --git a/tests/fixtures/order/ws03-xs-red.jpg b/tests/fixtures/order/ws03-xs-red.jpg new file mode 100644 index 0000000..972fbe5 Binary files /dev/null and b/tests/fixtures/order/ws03-xs-red.jpg differ