From 652634531cbb70666e941d5e0a02cf7231014cd7 Mon Sep 17 00:00:00 2001 From: kaioken Date: Mon, 18 Nov 2024 00:58:45 -0400 Subject: [PATCH 01/11] feat: add langauge model domain --- .../KanvasWorkflowSynActionCommand.php | 2 + composer.json | 1 + composer.lock | 197 +++++++++++++++--- .../Connectors/CMLink/CMLinkHandler.php | 2 +- src/Domains/Connectors/CMLink/Client.php | 2 +- ...etupService.php => CMLinkSetupService.php} | 0 .../CMLink/Services/CarrierService.php | 2 +- .../CMLink/Services/CustomerService.php | 2 +- .../{OderService.php => OrderService.php} | 2 +- .../CMLink/Services/PlanService.php | 2 +- src/Domains/Connectors/ESim/Client.php | 57 +++++ .../ESim/Enums/ConfigurationEnum.php | 11 + .../Connectors/ESim/Enums/CustomFieldEnum.php | 9 + .../Connectors/ESim/Services/OrderService.php | 30 +++ .../UpdateOrderWithESimMetaDataActivity.php | 57 +++++ .../LanguageModel/Enums/ConfigurationEnum.php | 10 + .../LanguageModel/Enums/CustomFieldEnum.php | 9 + .../Connectors/LanguageModel/OpenAI.php | 37 ++++ 18 files changed, 401 insertions(+), 31 deletions(-) rename src/Domains/Connectors/CMLink/Services/{IPlusSetupService.php => CMLinkSetupService.php} (100%) rename src/Domains/Connectors/CMLink/Services/{OderService.php => OrderService.php} (95%) create mode 100644 src/Domains/Connectors/ESim/Client.php create mode 100644 src/Domains/Connectors/ESim/Enums/ConfigurationEnum.php create mode 100644 src/Domains/Connectors/ESim/Enums/CustomFieldEnum.php create mode 100644 src/Domains/Connectors/ESim/Services/OrderService.php create mode 100644 src/Domains/Connectors/ESim/WorkflowActivities/UpdateOrderWithESimMetaDataActivity.php create mode 100644 src/Domains/Connectors/LanguageModel/Enums/ConfigurationEnum.php create mode 100644 src/Domains/Connectors/LanguageModel/Enums/CustomFieldEnum.php create mode 100644 src/Domains/Connectors/LanguageModel/OpenAI.php diff --git a/app/Console/Commands/Workflows/KanvasWorkflowSynActionCommand.php b/app/Console/Commands/Workflows/KanvasWorkflowSynActionCommand.php index de87e0913..3a230965d 100644 --- a/app/Console/Commands/Workflows/KanvasWorkflowSynActionCommand.php +++ b/app/Console/Commands/Workflows/KanvasWorkflowSynActionCommand.php @@ -7,6 +7,7 @@ use Illuminate\Console\Command; use Kanvas\Apps\Activities\AppUsersNotificationByRoleActivity; use Kanvas\Connectors\Apollo\Workflows\Activities\ScreeningPeopleActivity; +use Kanvas\Connectors\ESim\WorkflowActivities\UpdateOrderWithESimMetaDataActivity; use Kanvas\Connectors\Ghost\Jobs\CreatePeopleFromGhostReceiverJob; use Kanvas\Connectors\Google\Activities\GenerateUserForYouFeedActivity; use Kanvas\Connectors\Google\Activities\SyncMessageToDocumentActivity; @@ -83,6 +84,7 @@ public function handle(): void ProcessNetSuiteCompanyCustomerWebhookJob::class, CreatePeopleFromGhostReceiverJob::class, CashierStripeWebhookJob::class, + UpdateOrderWithESimMetaDataActivity::class ]; $createdActions = []; diff --git a/composer.json b/composer.json index d488d4e4e..80e7420c5 100644 --- a/composer.json +++ b/composer.json @@ -40,6 +40,7 @@ "mll-lab/laravel-graphiql": "^3.1.0", "nevadskiy/laravel-tree": "^0.5.0", "nuwave/lighthouse": "^6.0", + "openai-php/client": "^0.10.3", "phpclassic/php-shopify": "^1.2", "powersync/authorizenet-sdk-php": "^2.0", "pusher/pusher-php-server": "^7.2", diff --git a/composer.lock b/composer.lock index cd75e3a8b..2be6bc390 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "9baa23547846dc46a3a607b09fe19321", + "content-hash": "a1b6489e5a6fd2c0c29fc6197673f700", "packages": [ { "name": "algolia/algoliasearch-client-php", @@ -1195,16 +1195,16 @@ }, { "name": "aws/aws-sdk-php", - "version": "3.327.1", + "version": "3.328.0", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "3d52ec587989b136e486f94eff3dd316465aeb42" + "reference": "a99b58e166ae367f2b067937afb04e843e900745" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/3d52ec587989b136e486f94eff3dd316465aeb42", - "reference": "3d52ec587989b136e486f94eff3dd316465aeb42", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/a99b58e166ae367f2b067937afb04e843e900745", + "reference": "a99b58e166ae367f2b067937afb04e843e900745", "shasum": "" }, "require": { @@ -1287,9 +1287,9 @@ "support": { "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80", "issues": "https://github.com/aws/aws-sdk-php/issues", - "source": "https://github.com/aws/aws-sdk-php/tree/3.327.1" + "source": "https://github.com/aws/aws-sdk-php/tree/3.328.0" }, - "time": "2024-11-15T01:53:30+00:00" + "time": "2024-11-15T19:06:57+00:00" }, { "name": "berkayk/onesignal-laravel", @@ -2618,16 +2618,16 @@ }, { "name": "google/apiclient-services", - "version": "v0.381.0", + "version": "v0.382.0", "source": { "type": "git", "url": "https://github.com/googleapis/google-api-php-client-services.git", - "reference": "e26fd3ea9c1931f205481843519b8fdc166e7026" + "reference": "9d9d154c8fc3c4b300c27e492f0e917d8ac35124" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/googleapis/google-api-php-client-services/zipball/e26fd3ea9c1931f205481843519b8fdc166e7026", - "reference": "e26fd3ea9c1931f205481843519b8fdc166e7026", + "url": "https://api.github.com/repos/googleapis/google-api-php-client-services/zipball/9d9d154c8fc3c4b300c27e492f0e917d8ac35124", + "reference": "9d9d154c8fc3c4b300c27e492f0e917d8ac35124", "shasum": "" }, "require": { @@ -2656,9 +2656,9 @@ ], "support": { "issues": "https://github.com/googleapis/google-api-php-client-services/issues", - "source": "https://github.com/googleapis/google-api-php-client-services/tree/v0.381.0" + "source": "https://github.com/googleapis/google-api-php-client-services/tree/v0.382.0" }, - "time": "2024-11-11T01:08:23+00:00" + "time": "2024-11-15T01:10:24+00:00" }, { "name": "google/auth", @@ -3105,16 +3105,16 @@ }, { "name": "google/longrunning", - "version": "0.4.4", + "version": "0.4.5", "source": { "type": "git", "url": "https://github.com/googleapis/php-longrunning.git", - "reference": "ce921cf2a59082b09ab04f36b1afef4686c3a9ab" + "reference": "062eab0f3b9310da9498bfe20b273f074580b916" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/googleapis/php-longrunning/zipball/ce921cf2a59082b09ab04f36b1afef4686c3a9ab", - "reference": "ce921cf2a59082b09ab04f36b1afef4686c3a9ab", + "url": "https://api.github.com/repos/googleapis/php-longrunning/zipball/062eab0f3b9310da9498bfe20b273f074580b916", + "reference": "062eab0f3b9310da9498bfe20b273f074580b916", "shasum": "" }, "require-dev": { @@ -3143,9 +3143,9 @@ ], "description": "Google LongRunning Client for PHP", "support": { - "source": "https://github.com/googleapis/php-longrunning/tree/v0.4.4" + "source": "https://github.com/googleapis/php-longrunning/tree/v0.4.5" }, - "time": "2024-11-06T21:50:43+00:00" + "time": "2024-11-16T00:28:46+00:00" }, { "name": "google/protobuf", @@ -6993,6 +6993,97 @@ ], "time": "2024-09-09T07:06:30+00:00" }, + { + "name": "openai-php/client", + "version": "v0.10.3", + "source": { + "type": "git", + "url": "https://github.com/openai-php/client.git", + "reference": "4a565d145e0fb3ea1baba8fffe39d86c56b6dc2c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/openai-php/client/zipball/4a565d145e0fb3ea1baba8fffe39d86c56b6dc2c", + "reference": "4a565d145e0fb3ea1baba8fffe39d86c56b6dc2c", + "shasum": "" + }, + "require": { + "php": "^8.1.0", + "php-http/discovery": "^1.20.0", + "php-http/multipart-stream-builder": "^1.4.2", + "psr/http-client": "^1.0.3", + "psr/http-client-implementation": "^1.0.1", + "psr/http-factory-implementation": "*", + "psr/http-message": "^1.1.0|^2.0.0" + }, + "require-dev": { + "guzzlehttp/guzzle": "^7.9.2", + "guzzlehttp/psr7": "^2.7.0", + "laravel/pint": "^1.18.1", + "mockery/mockery": "^1.6.12", + "nunomaduro/collision": "^7.11.0|^8.5.0", + "pestphp/pest": "^2.36.0|^3.5.0", + "pestphp/pest-plugin-arch": "^2.7|^3.0", + "pestphp/pest-plugin-type-coverage": "^2.8.7|^3.1.0", + "phpstan/phpstan": "^1.12.7", + "symfony/var-dumper": "^6.4.11|^7.1.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/OpenAI.php" + ], + "psr-4": { + "OpenAI\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + }, + { + "name": "Sandro Gehri" + } + ], + "description": "OpenAI PHP is a supercharged PHP API client that allows you to interact with the Open AI API", + "keywords": [ + "GPT-3", + "api", + "client", + "codex", + "dall-e", + "language", + "natural", + "openai", + "php", + "processing", + "sdk" + ], + "support": { + "issues": "https://github.com/openai-php/client/issues", + "source": "https://github.com/openai-php/client/tree/v0.10.3" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/gehrisandro", + "type": "github" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + } + ], + "time": "2024-11-12T20:51:16+00:00" + }, { "name": "paragonie/constant_time_encoding", "version": "v3.0.0", @@ -7556,6 +7647,62 @@ }, "time": "2024-10-02T11:34:13+00:00" }, + { + "name": "php-http/multipart-stream-builder", + "version": "1.4.2", + "source": { + "type": "git", + "url": "https://github.com/php-http/multipart-stream-builder.git", + "reference": "10086e6de6f53489cca5ecc45b6f468604d3460e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/multipart-stream-builder/zipball/10086e6de6f53489cca5ecc45b6f468604d3460e", + "reference": "10086e6de6f53489cca5ecc45b6f468604d3460e", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0", + "php-http/discovery": "^1.15", + "psr/http-factory-implementation": "^1.0" + }, + "require-dev": { + "nyholm/psr7": "^1.0", + "php-http/message": "^1.5", + "php-http/message-factory": "^1.0.2", + "phpunit/phpunit": "^7.5.15 || ^8.5 || ^9.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Http\\Message\\MultipartStream\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com" + } + ], + "description": "A builder class that help you create a multipart stream", + "homepage": "http://php-http.org", + "keywords": [ + "factory", + "http", + "message", + "multipart stream", + "stream" + ], + "support": { + "issues": "https://github.com/php-http/multipart-stream-builder/issues", + "source": "https://github.com/php-http/multipart-stream-builder/tree/1.4.2" + }, + "time": "2024-09-04T13:22:54+00:00" + }, { "name": "php-http/promise", "version": "1.3.1", @@ -14995,16 +15142,16 @@ }, { "name": "phpstan/phpstan", - "version": "2.0.1", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "ab4e9b4415a5fc9e4d27f7fe16c8bc9d067dcd6d" + "reference": "6c98c7600fc717b2c78c11ef60040d5b1e359c82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/ab4e9b4415a5fc9e4d27f7fe16c8bc9d067dcd6d", - "reference": "ab4e9b4415a5fc9e4d27f7fe16c8bc9d067dcd6d", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/6c98c7600fc717b2c78c11ef60040d5b1e359c82", + "reference": "6c98c7600fc717b2c78c11ef60040d5b1e359c82", "shasum": "" }, "require": { @@ -15049,7 +15196,7 @@ "type": "github" } ], - "time": "2024-11-11T15:43:04+00:00" + "time": "2024-11-17T14:17:00+00:00" }, { "name": "phpunit/php-code-coverage", @@ -16458,6 +16605,6 @@ "platform": { "php": "^8.2" }, - "platform-dev": [], + "platform-dev": {}, "plugin-api-version": "2.6.0" } diff --git a/src/Domains/Connectors/CMLink/CMLinkHandler.php b/src/Domains/Connectors/CMLink/CMLinkHandler.php index 03e99bcd7..2ddf020b8 100644 --- a/src/Domains/Connectors/CMLink/CMLinkHandler.php +++ b/src/Domains/Connectors/CMLink/CMLinkHandler.php @@ -4,10 +4,10 @@ namespace Kanvas\Connectors\CMLink; -use Domains\Connectors\CMLink\Client; use Kanvas\Connectors\CMLink\DataTransferObject\CMLink; use Kanvas\Connectors\CMLink\Services\CMLinkSetupService; use Kanvas\Connectors\Contracts\BaseIntegration; +use Kanvas\Connectors\CMLink\Client; class CMLinkHandler extends BaseIntegration { diff --git a/src/Domains/Connectors/CMLink/Client.php b/src/Domains/Connectors/CMLink/Client.php index e7d6dddea..4c0cd9cb9 100644 --- a/src/Domains/Connectors/CMLink/Client.php +++ b/src/Domains/Connectors/CMLink/Client.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Domains\Connectors\CMLink; +namespace Kanvas\Connectors\CMLink; use Baka\Contracts\AppInterface; use Baka\Contracts\CompanyInterface; diff --git a/src/Domains/Connectors/CMLink/Services/IPlusSetupService.php b/src/Domains/Connectors/CMLink/Services/CMLinkSetupService.php similarity index 100% rename from src/Domains/Connectors/CMLink/Services/IPlusSetupService.php rename to src/Domains/Connectors/CMLink/Services/CMLinkSetupService.php diff --git a/src/Domains/Connectors/CMLink/Services/CarrierService.php b/src/Domains/Connectors/CMLink/Services/CarrierService.php index becf0055a..27fbe4d66 100644 --- a/src/Domains/Connectors/CMLink/Services/CarrierService.php +++ b/src/Domains/Connectors/CMLink/Services/CarrierService.php @@ -6,7 +6,7 @@ use Baka\Contracts\AppInterface; use Baka\Contracts\CompanyInterface; -use Domains\Connectors\CMLink\Client; +use Kanvas\Connectors\CMLink\Client; class CarrierService { diff --git a/src/Domains/Connectors/CMLink/Services/CustomerService.php b/src/Domains/Connectors/CMLink/Services/CustomerService.php index 841dbfe7e..77f004bee 100644 --- a/src/Domains/Connectors/CMLink/Services/CustomerService.php +++ b/src/Domains/Connectors/CMLink/Services/CustomerService.php @@ -4,7 +4,7 @@ namespace Kanvas\Connectors\CMLink\Services; -use Domains\Connectors\CMLink\Client; +use Kanvas\Connectors\CMLink\Client; use Kanvas\Guild\Customers\Models\People; class CustomerService diff --git a/src/Domains/Connectors/CMLink/Services/OderService.php b/src/Domains/Connectors/CMLink/Services/OrderService.php similarity index 95% rename from src/Domains/Connectors/CMLink/Services/OderService.php rename to src/Domains/Connectors/CMLink/Services/OrderService.php index 95ec710a8..9cbb43965 100644 --- a/src/Domains/Connectors/CMLink/Services/OderService.php +++ b/src/Domains/Connectors/CMLink/Services/OrderService.php @@ -6,7 +6,7 @@ use Baka\Contracts\AppInterface; use Baka\Contracts\CompanyInterface; -use Domains\Connectors\CMLink\Client; +use Kanvas\Connectors\CMLink\Client; class OrderService { diff --git a/src/Domains/Connectors/CMLink/Services/PlanService.php b/src/Domains/Connectors/CMLink/Services/PlanService.php index ee8766929..d26708b2a 100644 --- a/src/Domains/Connectors/CMLink/Services/PlanService.php +++ b/src/Domains/Connectors/CMLink/Services/PlanService.php @@ -6,7 +6,7 @@ use Baka\Contracts\AppInterface; use Baka\Contracts\CompanyInterface; -use Domains\Connectors\CMLink\Client; +use Kanvas\Connectors\CMLink\Client; class PlanService { diff --git a/src/Domains/Connectors/ESim/Client.php b/src/Domains/Connectors/ESim/Client.php new file mode 100644 index 000000000..e9a2bfddb --- /dev/null +++ b/src/Domains/Connectors/ESim/Client.php @@ -0,0 +1,57 @@ +baseUri = $this->app->get(ConfigurationEnum::BASE_URL->value); + $this->appToken = $this->app->get(ConfigurationEnum::APP_TOKEN->value); + + if (empty($this->baseUri) || empty($this->appToken)) { + throw new ValidationException('ESim configuration is missing'); + } + + $this->client = new GuzzleClient([ + 'base_uri' => $this->baseUri, + 'headers' => [ + 'Content-Type' => 'application/json', + 'Authorization' => 'Bearer ' . $this->appToken, + ], + ]); + } + + protected function request($method, $uri, $body): array + { + $response = $this->client->request($method, $uri, [ + 'json' => $body, + ]); + + return json_decode($response->getBody()->getContents(), true); + } + + public function get($uri, $body = []): array + { + return $this->request('GET', $uri, $body); + } + + public function post($uri, $body = []): array + { + return $this->request('POST', $uri, $body); + } +} diff --git a/src/Domains/Connectors/ESim/Enums/ConfigurationEnum.php b/src/Domains/Connectors/ESim/Enums/ConfigurationEnum.php new file mode 100644 index 000000000..17eeb9be4 --- /dev/null +++ b/src/Domains/Connectors/ESim/Enums/ConfigurationEnum.php @@ -0,0 +1,11 @@ +client = new Client($order->app, $order->company); + } + + public function createOrder() + { + $item = $this->order->items()->first(); + + return $this->client->post('orders', [ + 'type' => 'bundle', //@todo replace + 'quantity' => $item->quantity, + 'item' => $item->product_sku, + ]); + } +} diff --git a/src/Domains/Connectors/ESim/WorkflowActivities/UpdateOrderWithESimMetaDataActivity.php b/src/Domains/Connectors/ESim/WorkflowActivities/UpdateOrderWithESimMetaDataActivity.php new file mode 100644 index 000000000..39053bbd9 --- /dev/null +++ b/src/Domains/Connectors/ESim/WorkflowActivities/UpdateOrderWithESimMetaDataActivity.php @@ -0,0 +1,57 @@ +overwriteAppService($app); + $createOrder = new OrderService($order); + $response = $createOrder->createOrder(); + + $order->metadata = $response; + $order->saveOrFail(); + + //create the esim for the user + $messageType = (new CreateMessageTypeAction( + new MessageTypeInput( + $app->getId(), + 0, + 'esim', + 'esim', + ) + ))->execute(); + $createMessage = new CreateMessageAction( + new MessageInput( + $app, + $order->company, + $order->user, + $messageType, + $response + ) + ); + + $message = $createMessage->execute(); + + return [ + 'status' => 'success', + 'message' => 'Order updated with eSim metadata', + 'response' => $response, + ]; + } +} diff --git a/src/Domains/Connectors/LanguageModel/Enums/ConfigurationEnum.php b/src/Domains/Connectors/LanguageModel/Enums/ConfigurationEnum.php new file mode 100644 index 000000000..0eb0854d9 --- /dev/null +++ b/src/Domains/Connectors/LanguageModel/Enums/ConfigurationEnum.php @@ -0,0 +1,10 @@ +apiKey = $this->app->get(ConfigurationEnum::OPENAI_API_KEY->value); + + if (empty($this->apiKey)) { + throw new Exception('OpenAI API Key is required'); + } + } + + public function client(): Client + { + return GlobalOpenAI::client($this->apiKey); + } + + public function factory(): Factory + { + return GlobalOpenAI::factory()->withApiKey($this->apiKey); + } +} From 34f3fb35d64aa2cb43f49de1dc541806a6f9b53a Mon Sep 17 00:00:00 2001 From: kaioken Date: Mon, 18 Nov 2024 01:22:03 -0400 Subject: [PATCH 02/11] hotfix: address --- .../Guild/Customers/Actions/CreatePeopleAction.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Domains/Guild/Customers/Actions/CreatePeopleAction.php b/src/Domains/Guild/Customers/Actions/CreatePeopleAction.php index b185b7a5d..3a10ba7f9 100644 --- a/src/Domains/Guild/Customers/Actions/CreatePeopleAction.php +++ b/src/Domains/Guild/Customers/Actions/CreatePeopleAction.php @@ -106,6 +106,8 @@ public function execute(): People $addressesToAdd = []; + $isDefaultSet = false; + foreach ($this->peopleData->address as $address) { $newAddress = [ 'address' => $address->address, @@ -120,9 +122,13 @@ public function execute(): People ]; if (! in_array($newAddress, $existingAddresses)) { - $addressesToAdd[] = new Address(array_merge($newAddress, [ - 'is_default' => $address->is_default, + $addressesToAdd[] = $addressesToAdd[] = new Address(array_merge($newAddress, [ + 'is_default' => $isDefaultSet ? 0 : ($address->is_default ? 1 : 0), ])); + + if ($address->is_default && ! $isDefaultSet) { + $isDefaultSet = true; // Ensure only one is marked as default + } } } } From 05ca5823f48ad6bcaf244cc81cbe14f92c510b74 Mon Sep 17 00:00:00 2001 From: kaioken Date: Mon, 18 Nov 2024 01:40:09 -0400 Subject: [PATCH 03/11] refact: default --- .../Guild/Customers/Actions/CreatePeopleAction.php | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/Domains/Guild/Customers/Actions/CreatePeopleAction.php b/src/Domains/Guild/Customers/Actions/CreatePeopleAction.php index 3a10ba7f9..a123f3e98 100644 --- a/src/Domains/Guild/Customers/Actions/CreatePeopleAction.php +++ b/src/Domains/Guild/Customers/Actions/CreatePeopleAction.php @@ -104,9 +104,9 @@ public function execute(): People ->get() ->toArray(); - $addressesToAdd = []; + $hasDefaultAddress = $people->address()->where('is_default', 1)->exists(); - $isDefaultSet = false; + $addressesToAdd = []; foreach ($this->peopleData->address as $address) { $newAddress = [ @@ -123,12 +123,8 @@ public function execute(): People if (! in_array($newAddress, $existingAddresses)) { $addressesToAdd[] = $addressesToAdd[] = new Address(array_merge($newAddress, [ - 'is_default' => $isDefaultSet ? 0 : ($address->is_default ? 1 : 0), + 'is_default' => $hasDefaultAddress ? 0 : ($address->is_default ? 1 : 0), ])); - - if ($address->is_default && ! $isDefaultSet) { - $isDefaultSet = true; // Ensure only one is marked as default - } } } } From f8b408df36afea2b75d3b3ee4b690796318ef0a2 Mon Sep 17 00:00:00 2001 From: kaioken Date: Mon, 18 Nov 2024 08:58:24 -0400 Subject: [PATCH 04/11] feat: microcenter --- .../Commands/Setup/SoukSetupCommand.php | 52 +++++++++++++++++++ .../Souk/Orders/Actions/CreateOrderAction.php | 16 +++++- .../Souk/Support/CreateSystemModule.php | 26 ++++++++++ src/Domains/Souk/Support/Setup.php | 36 +++++++++++++ 4 files changed, 128 insertions(+), 2 deletions(-) create mode 100644 app/Console/Commands/Setup/SoukSetupCommand.php create mode 100644 src/Domains/Souk/Support/CreateSystemModule.php create mode 100644 src/Domains/Souk/Support/Setup.php diff --git a/app/Console/Commands/Setup/SoukSetupCommand.php b/app/Console/Commands/Setup/SoukSetupCommand.php new file mode 100644 index 000000000..b787039b1 --- /dev/null +++ b/app/Console/Commands/Setup/SoukSetupCommand.php @@ -0,0 +1,52 @@ +argument('company_id')); + $user = Users::getById((int) $this->argument('user_id')); + $app = Apps::getById((int) $this->argument('app_id')); + + (new Setup( + $app, + $user, + $company + ))->run(); + + $this->newLine(); + $this->info('Social Souk for Company ' . $company->name . ' and App ' . $app->name . ' completed successfully'); + $this->newLine(); + + return; + } +} diff --git a/src/Domains/Souk/Orders/Actions/CreateOrderAction.php b/src/Domains/Souk/Orders/Actions/CreateOrderAction.php index 72683e614..db0e75a40 100644 --- a/src/Domains/Souk/Orders/Actions/CreateOrderAction.php +++ b/src/Domains/Souk/Orders/Actions/CreateOrderAction.php @@ -6,14 +6,16 @@ use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Validator; -use Kanvas\Connectors\Shopify\Notifications\NewManualPaidOrderNotification; use Kanvas\Exceptions\ValidationException; use Kanvas\Souk\Orders\DataTransferObject\Order; use Kanvas\Souk\Orders\Models\Order as ModelsOrder; use Kanvas\Souk\Orders\Validations\UniqueOrderNumber; +use Kanvas\Workflow\Enums\WorkflowEnum; class CreateOrderAction { + protected bool $runWorkflow = true; + public function __construct( protected Order $orderData ) { @@ -41,7 +43,7 @@ public function execute(): ModelsOrder $order->user_phone = $this->orderData->phone; $order->token = $this->orderData->token; $order->order_number = $this->orderData->orderNumber; - $order->shipping_address_id = $this->orderData?->shippingAddress?->getId() ?? null; + $order->shipping_address_id = $this->orderData?->shippingAddress?->getId() ?? null; $order->billing_address_id = $this->orderData?->billingAddress?->getId() ?? null; $order->total_gross_amount = $this->orderData->total; $order->total_net_amount = $this->orderData->total - $this->orderData->taxes; @@ -61,6 +63,16 @@ public function execute(): ModelsOrder $order->addItems($this->orderData->items); + if ($this->runWorkflow) { + $order->fireWorkflow( + WorkflowEnum::CREATED->value, + true, + [ + 'app' => $this->orderData->app, + ] + ); + } + return $order; }); } diff --git a/src/Domains/Souk/Support/CreateSystemModule.php b/src/Domains/Souk/Support/CreateSystemModule.php new file mode 100644 index 000000000..6d8b30fce --- /dev/null +++ b/src/Domains/Souk/Support/CreateSystemModule.php @@ -0,0 +1,26 @@ +app); + + $createSystemModule->execute(Order::class); + $createSystemModule->execute(OrderItem::class); + } +} diff --git a/src/Domains/Souk/Support/Setup.php b/src/Domains/Souk/Support/Setup.php new file mode 100644 index 000000000..0c792c647 --- /dev/null +++ b/src/Domains/Souk/Support/Setup.php @@ -0,0 +1,36 @@ +app); + // $createSystemModule->execute(Interactions::class); + (new CreateSystemModule($this->app))->run(); + + return true; + } +} From 111a7827876386073765afbdee4dade9bb73dc7e Mon Sep 17 00:00:00 2001 From: kaioken Date: Mon, 18 Nov 2024 09:00:39 -0400 Subject: [PATCH 05/11] fix: install --- app/Console/Commands/Setup/SoukSetupCommand.php | 2 +- src/Domains/Souk/Support/Setup.php | 8 -------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/app/Console/Commands/Setup/SoukSetupCommand.php b/app/Console/Commands/Setup/SoukSetupCommand.php index b787039b1..a6416847a 100644 --- a/app/Console/Commands/Setup/SoukSetupCommand.php +++ b/app/Console/Commands/Setup/SoukSetupCommand.php @@ -24,7 +24,7 @@ class SoukSetupCommand extends Command * * @var string|null */ - protected $description = 'Initializes the social system'; + protected $description = 'Initializes the commerce system'; /** * Execute the console command. diff --git a/src/Domains/Souk/Support/Setup.php b/src/Domains/Souk/Support/Setup.php index 0c792c647..2ae7607b2 100644 --- a/src/Domains/Souk/Support/Setup.php +++ b/src/Domains/Souk/Support/Setup.php @@ -7,14 +7,10 @@ use Baka\Contracts\AppInterface; use Baka\Contracts\CompanyInterface; use Baka\Users\Contracts\UserInterface; -use Kanvas\Social\Interactions\Models\Interactions; use Kanvas\SystemModules\Actions\CreateInCurrentAppAction; class Setup { - /** - * Constructor. - */ public function __construct( protected AppInterface $app, protected UserInterface $user, @@ -22,13 +18,9 @@ public function __construct( ) { } - /** - * Setup all the default inventory data for this current company. - */ public function run(): bool { $createSystemModule = new CreateInCurrentAppAction($this->app); - // $createSystemModule->execute(Interactions::class); (new CreateSystemModule($this->app))->run(); return true; From 00dbf268c0554ff00f4cabd03c5f9fae0a469fab Mon Sep 17 00:00:00 2001 From: kaioken Date: Mon, 18 Nov 2024 12:29:34 -0400 Subject: [PATCH 06/11] feat: add in app purchase webhooks --- app/Http/Controllers/ReceiverController.php | 11 +- composer.json | 1 + composer.lock | 251 +++++++++++++++++- config/liap.php | 104 ++++++++ ...4_11_18_151842_add_webhook_async_field.php | 27 ++ .../AppStoreReceiptVerficationWebhookJob.php | 0 .../Workflow/Models/ReceiverWebhook.php | 24 +- 7 files changed, 413 insertions(+), 5 deletions(-) create mode 100644 config/liap.php create mode 100644 database/migrations/Workflow/2024_11_18_151842_add_webhook_async_field.php create mode 100644 src/Domains/Connectors/InAppPurchase/AppStoreReceiptVerficationWebhookJob.php diff --git a/app/Http/Controllers/ReceiverController.php b/app/Http/Controllers/ReceiverController.php index c5640a51b..6f22eff18 100644 --- a/app/Http/Controllers/ReceiverController.php +++ b/app/Http/Controllers/ReceiverController.php @@ -10,7 +10,6 @@ use Illuminate\Routing\Controller as BaseController; use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\Auth; -use Illuminate\Support\Facades\Log; use Kanvas\Apps\Models\Apps; use Kanvas\Connectors\Zoho\Actions\SyncZohoAgentAction; use Kanvas\Connectors\Zoho\Actions\SyncZohoLeadAction; @@ -42,7 +41,15 @@ public function store(string $uuid, Request $request): JsonResponse $webhookRequest = (new ProcessWebhookAttemptAction($receiver, $request))->execute(); $job = new $receiver->action->model_name($webhookRequest); - dispatch($job); + + if ($receiver->runAsync()) { + dispatch($job); + } else { + return response()->json(array_merge( + ['message' => 'Receiver processed'], + dispatch_sync($job) + )); + } return response()->json(['message' => 'Receiver processed']); } diff --git a/composer.json b/composer.json index 80e7420c5..2bb9030ab 100644 --- a/composer.json +++ b/composer.json @@ -24,6 +24,7 @@ "google/cloud-recommendations-ai": "^0.7.5", "guzzlehttp/guzzle": "^7.6", "http-interop/http-factory-guzzle": "^1.0", + "imdhemy/laravel-purchases": "^1.14", "laravel-workflow/laravel-workflow": "^1.0.24", "laravel/cashier": "^15.4.0", "laravel/framework": "^11.0", diff --git a/composer.lock b/composer.lock index 2be6bc390..30d48603e 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "a1b6489e5a6fd2c0c29fc6197673f700", + "content-hash": "176c92541fc8b6defbb61da223882e47", "packages": [ { "name": "algolia/algoliasearch-client-php", @@ -3808,6 +3808,189 @@ }, "time": "2021-07-21T13:50:14+00:00" }, + { + "name": "imdhemy/appstore-iap", + "version": "1.7.0", + "source": { + "type": "git", + "url": "https://github.com/imdhemy/appstore-iap.git", + "reference": "c82aa2ad083c8029121ca4062c0bd494c09746c1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/imdhemy/appstore-iap/zipball/c82aa2ad083c8029121ca4062c0bd494c09746c1", + "reference": "c82aa2ad083c8029121ca4062c0bd494c09746c1", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-openssl": "*", + "ext-sodium": "*", + "guzzlehttp/guzzle": "^7.6.0", + "lcobucci/clock": "^3.0", + "lcobucci/jwt": "^5.3", + "nesbot/carbon": "^2.66|^3.1", + "php": ">=8.1" + }, + "require-dev": { + "fakerphp/faker": "^1.22", + "friendsofphp/php-cs-fixer": "^3.16", + "phpunit/phpunit": "^9.6", + "roave/security-advisories": "dev-latest", + "vimeo/psalm": "^5.11" + }, + "type": "library", + "autoload": { + "psr-4": { + "Imdhemy\\AppStore\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "imdhemy", + "email": "imdhemy@gmail.com" + } + ], + "description": "PHP Appstore In-App Purchase implementation", + "support": { + "issues": "https://github.com/imdhemy/appstore-iap/issues", + "source": "https://github.com/imdhemy/appstore-iap/tree/1.7.0" + }, + "time": "2024-06-30T18:08:57+00:00" + }, + { + "name": "imdhemy/google-play-billing", + "version": "1.5.3", + "source": { + "type": "git", + "url": "https://github.com/imdhemy/google-play-billing.git", + "reference": "5528bdff8eff5c4711e157541caa29283b632ae4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/imdhemy/google-play-billing/zipball/5528bdff8eff5c4711e157541caa29283b632ae4", + "reference": "5528bdff8eff5c4711e157541caa29283b632ae4", + "shasum": "" + }, + "require": { + "ext-json": "*", + "google/auth": "^1.26", + "guzzlehttp/guzzle": "^7.5.1", + "nesbot/carbon": "^2.66|^3.0", + "php": ">=8.0" + }, + "require-dev": { + "fakerphp/faker": "^1.21", + "friendsofphp/php-cs-fixer": "^3.16", + "phpunit/phpunit": "^9.6.7", + "roave/security-advisories": "dev-latest", + "vimeo/psalm": "^5.9" + }, + "type": "library", + "autoload": { + "psr-4": { + "Imdhemy\\GooglePlay\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "imdhemy", + "email": "imdhemy@gmail.com" + } + ], + "description": "Google Play Billing", + "support": { + "issues": "https://github.com/imdhemy/google-play-billing/issues", + "source": "https://github.com/imdhemy/google-play-billing/tree/1.5.3" + }, + "time": "2024-11-09T11:08:22+00:00" + }, + { + "name": "imdhemy/laravel-purchases", + "version": "1.14.1", + "source": { + "type": "git", + "url": "https://github.com/imdhemy/laravel-in-app-purchases.git", + "reference": "5504d2ab800c5bf68009b86185b5482b2b6fdfcc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/imdhemy/laravel-in-app-purchases/zipball/5504d2ab800c5bf68009b86185b5482b2b6fdfcc", + "reference": "5504d2ab800c5bf68009b86185b5482b2b6fdfcc", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-openssl": "*", + "imdhemy/appstore-iap": "^1.6", + "imdhemy/google-play-billing": "^1.5", + "laravel/framework": ">=8.0", + "php": ">=8.1" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.14", + "orchestra/testbench": "^6.24", + "psalm/plugin-laravel": "^2.0", + "psalm/plugin-phpunit": "^0.19.0", + "roave/security-advisories": "dev-latest", + "vimeo/psalm": "^5.11" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Imdhemy\\Purchases\\ServiceProviders\\LiapServiceProvider" + ], + "aliases": { + "Product": "\\Imdhemy\\Purchases\\Facades\\Product", + "Subscription": "\\Imdhemy\\Purchases\\Facades\\Subscription" + } + } + }, + "autoload": { + "psr-4": { + "Imdhemy\\Purchases\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "imdhemy", + "email": "imdhemy@gmail.com" + } + ], + "description": "The top-notch Laravel receipt validator.", + "homepage": "https://imdhemy.com/laravel-iap-docs", + "keywords": [ + "app_store", + "google_play", + "in_app_purchases", + "laravel", + "laravel-in-app-purchases" + ], + "support": { + "issues": "https://github.com/imdhemy/laravel-in-app-purchases/issues", + "source": "https://github.com/imdhemy/laravel-in-app-purchases/tree/1.14.1" + }, + "funding": [ + { + "url": "https://github.com/imdhemy", + "type": "github" + } + ], + "time": "2024-11-07T08:52:08+00:00" + }, { "name": "jean85/pretty-package-versions", "version": "2.0.6", @@ -4915,6 +5098,70 @@ }, "time": "2024-09-23T13:32:56+00:00" }, + { + "name": "lcobucci/clock", + "version": "3.3.1", + "source": { + "type": "git", + "url": "https://github.com/lcobucci/clock.git", + "reference": "db3713a61addfffd615b79bf0bc22f0ccc61b86b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/lcobucci/clock/zipball/db3713a61addfffd615b79bf0bc22f0ccc61b86b", + "reference": "db3713a61addfffd615b79bf0bc22f0ccc61b86b", + "shasum": "" + }, + "require": { + "php": "~8.2.0 || ~8.3.0 || ~8.4.0", + "psr/clock": "^1.0" + }, + "provide": { + "psr/clock-implementation": "1.0" + }, + "require-dev": { + "infection/infection": "^0.29", + "lcobucci/coding-standard": "^11.1.0", + "phpstan/extension-installer": "^1.3.1", + "phpstan/phpstan": "^1.10.25", + "phpstan/phpstan-deprecation-rules": "^1.1.3", + "phpstan/phpstan-phpunit": "^1.3.13", + "phpstan/phpstan-strict-rules": "^1.5.1", + "phpunit/phpunit": "^11.3.6" + }, + "type": "library", + "autoload": { + "psr-4": { + "Lcobucci\\Clock\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Luís Cobucci", + "email": "lcobucci@gmail.com" + } + ], + "description": "Yet another clock abstraction", + "support": { + "issues": "https://github.com/lcobucci/clock/issues", + "source": "https://github.com/lcobucci/clock/tree/3.3.1" + }, + "funding": [ + { + "url": "https://github.com/lcobucci", + "type": "github" + }, + { + "url": "https://www.patreon.com/lcobucci", + "type": "patreon" + } + ], + "time": "2024-09-24T20:45:14+00:00" + }, { "name": "lcobucci/jwt", "version": "5.4.2", @@ -16605,6 +16852,6 @@ "platform": { "php": "^8.2" }, - "platform-dev": {}, + "platform-dev": [], "plugin-api-version": "2.6.0" } diff --git a/config/liap.php b/config/liap.php new file mode 100644 index 000000000..286f4557d --- /dev/null +++ b/config/liap.php @@ -0,0 +1,104 @@ + [ + 'signed' => false, + 'middleware' => [], + 'prefix' => '', + ], + + /* + |-------------------------------------------------------------------------- + | Google Play Default Package name + |-------------------------------------------------------------------------- + | + | This value is the default package name used when the package name is not + | provided while verifying the receipts. + | + */ + 'google_play_package_name' => env('GOOGLE_PLAY_PACKAGE_NAME', 'com.some.thing'), + + /* + |-------------------------------------------------------------------------- + | App Store Password + |-------------------------------------------------------------------------- + | + | This value is the app-specific share password generated by the app store. + | @see https://imdhemy.com/laravel-iap-docs/docs/credentials/app-store + | + */ + 'appstore_password' => env('APPSTORE_PASSWORD', ''), + + /* + |-------------------------------------------------------------------------- + | Event Listeners + |-------------------------------------------------------------------------- + | + | This configuration is used to determine the event listeners that will be + | registered with the application. + | You can find a list of all available events of the documentation + | + | @see https://imdhemy.com/laravel-iap-docs/docs/server-notifications/event-list + | @see https://imdhemy.com/laravel-iap-docs/docs/get-started/event-listeners + | + */ + 'eventListeners' => [ + /* + |-------------------------------------------------------------------------- + | App Store Events + |-------------------------------------------------------------------------- + | + | These event listeners are triggered when a new notification is received from App Store. + | @see https://imdhemy.com/laravel-iap-docs/docs/server-notifications/event-list#app-store-events + | + */ + + /* \Imdhemy\Purchases\Events\AppStore\Cancel::class => [ + \App\Listeners\AppStore\Cancel::class, + ],*/ + + /* + |-------------------------------------------------------------------------- + | Google Play Events + |-------------------------------------------------------------------------- + | + | These event listeners are triggered when a new notification is received from Google Play + | @see @see https://imdhemy.com/laravel-iap-docs/docs/server-notifications/event-list#google-play-events + */ + + /* \Imdhemy\Purchases\Events\GooglePlay\SubscriptionRecovered::class => [ + \App\Listeners\GooglePlay\SubscriptionRecovered::class, + ],*/ + ], + + /* + | -------------------------------------------------------------------------- + | App store JWT configuration + | -------------------------------------------------------------------------- + | + | The following configuration is used to generate the JWT token used to + | authenticate with the App Store server. + */ + // Your private key ID from App Store Connect (Ex: 2X9R4HXF34) + 'appstore_private_key_id' => env('APPSTORE_PRIVATE_KEY_ID'), + // The path to your private key file (Ex: /path/to/SuperSecretKey_ABC123.p8) + 'appstore_private_key' => env('APPSTORE_PRIVATE_KEY'), + // Your issuer ID from the Keys page in App Store Connect (Ex: "57246542-96fe-1a63-e053-0824d011072a") + 'appstore_issuer_id' => env('APPSTORE_ISSUER_ID'), + // Your app’s bundle ID (Ex: “com.example.testbundleid2021”) + 'appstore_bundle_id' => env('APPSTORE_BUNDLE_ID'), +]; diff --git a/database/migrations/Workflow/2024_11_18_151842_add_webhook_async_field.php b/database/migrations/Workflow/2024_11_18_151842_add_webhook_async_field.php new file mode 100644 index 000000000..7b077ab4f --- /dev/null +++ b/database/migrations/Workflow/2024_11_18_151842_add_webhook_async_field.php @@ -0,0 +1,27 @@ +tinyInteger('run_async')->default(1)->after('is_active'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('receiver_webhooks', function (Blueprint $table) { + $table->dropColumn('run_async'); + }); + } +}; diff --git a/src/Domains/Connectors/InAppPurchase/AppStoreReceiptVerficationWebhookJob.php b/src/Domains/Connectors/InAppPurchase/AppStoreReceiptVerficationWebhookJob.php new file mode 100644 index 000000000..e69de29bb diff --git a/src/Domains/Workflow/Models/ReceiverWebhook.php b/src/Domains/Workflow/Models/ReceiverWebhook.php index fb23b9609..9e69c6f22 100644 --- a/src/Domains/Workflow/Models/ReceiverWebhook.php +++ b/src/Domains/Workflow/Models/ReceiverWebhook.php @@ -6,12 +6,28 @@ use Baka\Casts\Json; use Baka\Traits\UuidTrait; +use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; use Kanvas\Workflow\Factories\ReceiverWebhookFactory; -use Illuminate\Database\Eloquent\Factories\Factory; +/** + * @property int $id + * @property string $uuid + * @property int $apps_id + * @property int $action_id + * @property int $companies_id + * @property int $users_id + * @property string $name + * @property string $description + * @property array $configuration + * @property bool $is_active + * @property bool $run_async + * @property bool $is_deleted + * @property string $created_at + * @property string $updated_at + */ class ReceiverWebhook extends BaseModel { use UuidTrait; @@ -35,6 +51,7 @@ class ReceiverWebhook extends BaseModel protected $casts = [ 'configuration' => Json::class, 'is_active' => 'boolean', + 'run_async' => 'boolean', 'is_deleted' => 'boolean', ]; @@ -52,4 +69,9 @@ protected static function newFactory(): Factory { return ReceiverWebhookFactory::new(); } + + public function runAsync(): bool + { + return $this->run_async; + } } From 5c3c1477a0fe45ee653c3735be92b64967f7c5e0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Nov 2024 23:12:44 +0000 Subject: [PATCH 07/11] build(deps): bump appleboy/ssh-action from 1.1.0 to 1.2.0 Bumps [appleboy/ssh-action](https://github.com/appleboy/ssh-action) from 1.1.0 to 1.2.0. - [Release notes](https://github.com/appleboy/ssh-action/releases) - [Changelog](https://github.com/appleboy/ssh-action/blob/master/.goreleaser.yaml) - [Commits](https://github.com/appleboy/ssh-action/compare/v1.1.0...v1.2.0) --- updated-dependencies: - dependency-name: appleboy/ssh-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/ec2-deploy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ec2-deploy.yaml b/.github/workflows/ec2-deploy.yaml index 8a68bd1a2..9bcdd2a7d 100644 --- a/.github/workflows/ec2-deploy.yaml +++ b/.github/workflows/ec2-deploy.yaml @@ -33,7 +33,7 @@ jobs: environment: ${{ github.ref_name }} steps: - name: executing remote ssh commands using password - uses: appleboy/ssh-action@v1.1.0 + uses: appleboy/ssh-action@v1.2.0 with: host: ${{ secrets.AWS_EC2_HOST }} username: ${{ secrets.AWS_EC2_USERNAME }} From 02a47cb3f70d43d578d2dec925449e2da59a0977 Mon Sep 17 00:00:00 2001 From: kaioken Date: Mon, 18 Nov 2024 20:53:21 -0400 Subject: [PATCH 08/11] feat: in app purchase mutation --- .github/workflows/static-analysis.yml | 3 ++- .github/workflows/tests.yml | 25 ++++++++++--------- app/Http/Controllers/ReceiverController.php | 7 ++++-- graphql/schemas/Souk/order.graphql | 14 ++++++++++- .../CMLink/DataTransferObject/CMLink.php | 3 ++- .../IPlus/DataTransferObject/IPlus.php | 3 ++- .../AppStoreReceiptVerficationWebhookJob.php | 0 .../Inventory/Regions/Models/Regions.php | 4 +-- .../Inventory/Variants/Models/Variants.php | 10 ++++++++ .../Variants/Services/VariantService.php | 6 +++++ .../Souk/Orders/DataTransferObject/Order.php | 3 ++- .../Orders/Validations/UniqueOrderNumber.php | 3 ++- 12 files changed, 59 insertions(+), 22 deletions(-) delete mode 100644 src/Domains/Connectors/InAppPurchase/AppStoreReceiptVerficationWebhookJob.php diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index 7a37af6ff..84c97bc8d 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -1,6 +1,6 @@ name: static analysis -on: [ "push" ] +on: ["push"] jobs: run-tests: @@ -70,6 +70,7 @@ jobs: NET_SUITE_CONSUMER_SECRET: ${{ secrets.NET_SUITE_CONSUMER_SECRET }} NET_SUITE_TOKEN: ${{ secrets.NET_SUITE_TOKEN }} NET_SUITE_TOKEN_SECRET: ${{ secrets.NET_SUITE_TOKEN_SECRET }} + TEST_APPLE_PAYMENT_SHARED_SECRET: ${{ secrets.TEST_APPLE_PAYMENT_SHARED_SECRET }} strategy: fail-fast: false matrix: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 6145a5531..118b1df8d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,6 +1,6 @@ name: CI -on: [ "push" ] +on: ["push"] jobs: run-tests: @@ -51,12 +51,6 @@ jobs: AWS_DEFAULT_REGION_CICD: ${{ secrets.AWS_DEFAULT_REGION_CICD }} AWS_BUCKET_CICD: ${{ secrets.AWS_BUCKET_CICD }} AWS_URL: ${{ secrets.AWS_URL }} - NET_SUITE_CUSTOMER_ID: ${{ secrets.NET_SUITE_CUSTOMER_ID }} - NET_SUITE_ACCOUNT: ${{ secrets.NET_SUITE_ACCOUNT }} - NET_SUITE_CONSUMER_KEY: ${{ secrets.NET_SUITE_CONSUMER_KEY }} - NET_SUITE_CONSUMER_SECRET: ${{ secrets.NET_SUITE_CONSUMER_SECRET }} - NET_SUITE_TOKEN: ${{ secrets.NET_SUITE_TOKEN }} - NET_SUITE_TOKEN_SECRET: ${{ secrets.NET_SUITE_TOKEN_SECRET }} MODEL_CACHE_STORE: model MODEL_CACHE_ENABLED: false LIGHTHOUSE_CACHE_ENABLE: true @@ -79,6 +73,13 @@ jobs: TEST_STRIPE_SECRET_KEY: ${{ secrets.TEST_STRIPE_SECRET_KEY }} TEST_AMPLITUDE_KEY: ${{ secrets.TEST_AMPLITUDE_KEY }} TEST_AMPLITUDE_SECRET: ${{ secrets.TEST_AMPLITUDE_SECRET }} + NET_SUITE_CUSTOMER_ID: ${{ secrets.NET_SUITE_CUSTOMER_ID }} + NET_SUITE_ACCOUNT: ${{ secrets.NET_SUITE_ACCOUNT }} + NET_SUITE_CONSUMER_KEY: ${{ secrets.NET_SUITE_CONSUMER_KEY }} + NET_SUITE_CONSUMER_SECRET: ${{ secrets.NET_SUITE_CONSUMER_SECRET }} + NET_SUITE_TOKEN: ${{ secrets.NET_SUITE_TOKEN }} + NET_SUITE_TOKEN_SECRET: ${{ secrets.NET_SUITE_TOKEN_SECRET }} + TEST_APPLE_PAYMENT_SHARED_SECRET: ${{ secrets.TEST_APPLE_PAYMENT_SHARED_SECRET }} strategy: fail-fast: false matrix: @@ -91,7 +92,7 @@ jobs: MYSQL_DATABASE: kanvas ports: - 3306:3306 - options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 + options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 redis: # Docker Hub image image: redis @@ -152,11 +153,11 @@ jobs: - name: Setup WORKFLOW DB run: | mysql -uroot -h127.0.0.1 -ppassword -e 'CREATE DATABASE IF NOT EXISTS workflow;' - + - name: Setup ACTION ENGINE DB run: | mysql -uroot -h127.0.0.1 -ppassword -e 'CREATE DATABASE IF NOT EXISTS action_engine;' - + - name: Setup COMMERCE DB run: | mysql -uroot -h127.0.0.1 -ppassword -e 'CREATE DATABASE IF NOT EXISTS commerce;' @@ -167,7 +168,7 @@ jobs: - name: Configure MySQL run: | - mysql -uroot -h127.0.0.1 -ppassword -e "SET GLOBAL max_connections = 1000;" + mysql -uroot -h127.0.0.1 -ppassword -e "SET GLOBAL max_connections = 1000;" - name: Install Composer dependencies run: composer install --prefer-dist --no-interaction --no-progress @@ -184,7 +185,7 @@ jobs: - name: Run Tests if: success() run: php artisan test - + - name: Upload artifacts uses: actions/upload-artifact@master if: failure() diff --git a/app/Http/Controllers/ReceiverController.php b/app/Http/Controllers/ReceiverController.php index 6f22eff18..b07e68ed8 100644 --- a/app/Http/Controllers/ReceiverController.php +++ b/app/Http/Controllers/ReceiverController.php @@ -45,10 +45,13 @@ public function store(string $uuid, Request $request): JsonResponse if ($receiver->runAsync()) { dispatch($job); } else { + $response = dispatch_sync($job); + $status = $response['status'] ?? 200; + return response()->json(array_merge( ['message' => 'Receiver processed'], - dispatch_sync($job) - )); + $response + ), $status); } return response()->json(['message' => 'Receiver processed']); diff --git a/graphql/schemas/Souk/order.graphql b/graphql/schemas/Souk/order.graphql index cdc5318c7..651cd1d0b 100644 --- a/graphql/schemas/Souk/order.graphql +++ b/graphql/schemas/Souk/order.graphql @@ -8,7 +8,7 @@ type Order { billing_address_id: Int shipping_address_id: Int order_number: Int - user_id: Int + user_id: Int @deprecated(reason: "Use user field instead") user: User! @belongsTo people: People! @belongsTo company: Company! @belongsTo @@ -153,6 +153,14 @@ input ProcessPaymentInput { payment_provider: String! # e.g., "stripe", "paypal" } +input AppleInAppPurchaseReceipt { + product_id: ID! + transaction_id: String! + receipt: String! + transaction_date: Mixed! + region_id: ID +} + extend type Mutation @guard { # @deprecated(reason: "Use createOrderFromCart instead") createOrder(input: OrderInput!): Mixed! @@ -167,6 +175,10 @@ extend type Mutation @guard { @field( resolver: "App\\GraphQL\\Souk\\Mutations\\Orders\\DraftOrderManagementMutation@create" ) + createOrderFromAppleInAppPurchase(input: AppleInAppPurchaseReceipt!): Order! + @field( + resolver: "App\\GraphQL\\Souk\\Mutations\\Orders\\AppleInAppPurchaseMutation@create" + ) processOrderPayment(input: ProcessPaymentInput!): PaymentResult! @field( resolver: "App\\GraphQL\\Souk\\Mutations\\Orders\\PaymentManagementMutation@processPayment" diff --git a/src/Domains/Connectors/CMLink/DataTransferObject/CMLink.php b/src/Domains/Connectors/CMLink/DataTransferObject/CMLink.php index 83ed37d78..d607a8783 100644 --- a/src/Domains/Connectors/CMLink/DataTransferObject/CMLink.php +++ b/src/Domains/Connectors/CMLink/DataTransferObject/CMLink.php @@ -6,8 +6,9 @@ use Baka\Contracts\AppInterface; use Baka\Contracts\CompanyInterface; +use Spatie\LaravelData\Data; -class CMLink +class CMLink extends Data { public function __construct( public CompanyInterface $company, diff --git a/src/Domains/Connectors/IPlus/DataTransferObject/IPlus.php b/src/Domains/Connectors/IPlus/DataTransferObject/IPlus.php index 662745909..6c124016a 100644 --- a/src/Domains/Connectors/IPlus/DataTransferObject/IPlus.php +++ b/src/Domains/Connectors/IPlus/DataTransferObject/IPlus.php @@ -6,8 +6,9 @@ use Baka\Contracts\AppInterface; use Baka\Contracts\CompanyInterface; +use Spatie\LaravelData\Data; -class IPlus +class IPlus extends Data { public function __construct( public CompanyInterface $company, diff --git a/src/Domains/Connectors/InAppPurchase/AppStoreReceiptVerficationWebhookJob.php b/src/Domains/Connectors/InAppPurchase/AppStoreReceiptVerficationWebhookJob.php deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/Domains/Inventory/Regions/Models/Regions.php b/src/Domains/Inventory/Regions/Models/Regions.php index a761ae39a..039c1487d 100644 --- a/src/Domains/Inventory/Regions/Models/Regions.php +++ b/src/Domains/Inventory/Regions/Models/Regions.php @@ -11,7 +11,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany; use Kanvas\Currencies\Models\Currencies; use Kanvas\Inventory\Warehouses\Models\Warehouses; -use Kanvas\Models\BaseModel as KanvasBaseModel; +use Kanvas\Regions\Models\Regions as ModelsRegions; use Kanvas\Traits\DefaultTrait; /** @@ -31,7 +31,7 @@ * @property string $created_at * @property string $updated_at */ -class Regions extends KanvasBaseModel +class Regions extends ModelsRegions { use UuidTrait; use SlugTrait; diff --git a/src/Domains/Inventory/Variants/Models/Variants.php b/src/Domains/Inventory/Variants/Models/Variants.php index 964a981e5..4dfb84481 100644 --- a/src/Domains/Inventory/Variants/Models/Variants.php +++ b/src/Domains/Inventory/Variants/Models/Variants.php @@ -5,6 +5,8 @@ namespace Kanvas\Inventory\Variants\Models; use Awobaz\Compoships\Compoships; +use Baka\Contracts\AppInterface; +use Baka\Contracts\CompanyInterface; use Baka\Enums\StateEnums; use Baka\Support\Str; use Baka\Traits\HasLightHouseCache; @@ -461,4 +463,12 @@ public function setTotalQuantity(): int return (int) $total; } + + public static function getBySku(string $sku, CompanyInterface $company, AppInterface $app): self + { + return self::fromApp($app) + ->fromCompany($company) + ->where('sku', $sku) + ->firstOrFail(); + } } diff --git a/src/Domains/Inventory/Variants/Services/VariantService.php b/src/Domains/Inventory/Variants/Services/VariantService.php index 5d4d415f0..723854a90 100644 --- a/src/Domains/Inventory/Variants/Services/VariantService.php +++ b/src/Domains/Inventory/Variants/Services/VariantService.php @@ -135,6 +135,12 @@ public static function createDefaultVariant(Products $product, UserInterface $us } else { $variant['warehouse']['status_id'] = Status::getDefault($company)->getId(); } + + if (! empty($productDto->warehouses) && isset($productDto->warehouses[0]['quantity']) && isset($productDto->warehouses[0]['price'])) { + $variant['warehouse']['quantity'] = $productDto->warehouses[0]['quantity']; + $variant['warehouse']['price'] = $productDto->warehouses[0]['price']; + } + $variantWarehouses = VariantsWarehouses::viaRequest($variantModel, $warehouse, $variant['warehouse'] ?? []); (new AddToWarehouse($variantModel, $warehouse, $variantWarehouses))->execute(); diff --git a/src/Domains/Souk/Orders/DataTransferObject/Order.php b/src/Domains/Souk/Orders/DataTransferObject/Order.php index a002e3a06..460c62100 100644 --- a/src/Domains/Souk/Orders/DataTransferObject/Order.php +++ b/src/Domains/Souk/Orders/DataTransferObject/Order.php @@ -11,6 +11,7 @@ use Kanvas\Guild\Customers\Models\Address; use Kanvas\Guild\Customers\Models\People; use Kanvas\Inventory\Regions\Models\Regions; +use Kanvas\Regions\Models\Regions as ModelsRegions; use Spatie\LaravelData\Attributes\DataCollectionOf; use Spatie\LaravelData\Data; use Spatie\LaravelData\DataCollection; @@ -19,7 +20,7 @@ class Order extends Data { public function __construct( public readonly Apps $app, - public readonly Regions $region, + public readonly Regions|ModelsRegions $region, public readonly CompanyInterface $company, public readonly People $people, public readonly UserInterface $user, diff --git a/src/Domains/Souk/Orders/Validations/UniqueOrderNumber.php b/src/Domains/Souk/Orders/Validations/UniqueOrderNumber.php index 9c28be8ef..e5a3925f7 100644 --- a/src/Domains/Souk/Orders/Validations/UniqueOrderNumber.php +++ b/src/Domains/Souk/Orders/Validations/UniqueOrderNumber.php @@ -9,6 +9,7 @@ use Closure; use Illuminate\Contracts\Validation\ValidationRule; use Kanvas\Inventory\Regions\Models\Regions; +use Kanvas\Regions\Models\Regions as ModelsRegions; use Kanvas\Souk\Orders\Models\Order; class UniqueOrderNumber implements ValidationRule @@ -16,7 +17,7 @@ class UniqueOrderNumber implements ValidationRule public function __construct( protected AppInterface $app, protected CompanyInterface $company, - protected Regions $region + protected Regions|ModelsRegions $region ) { } From a416d20055722d09cf1906b1f6a3f51353967d04 Mon Sep 17 00:00:00 2001 From: kaioken Date: Mon, 18 Nov 2024 20:55:03 -0400 Subject: [PATCH 09/11] feat: in app purchase mutation --- .../Orders/AppleInAppPurchaseMutation.php | 34 ++++ .../CreateOrderFromAppleReceiptAction.php | 159 ++++++++++++++++++ .../AppleInAppPurchaseReceipt.php | 45 +++++ .../InAppPurchase/Enums/ConfigurationEnum.php | 10 ++ .../InAppPurchase/Enums/CustomFieldEnum.php | 9 + .../InAppPurchase/AppleInAppPurchaseTest.php | 69 ++++++++ tests/GraphQL/Souk/InAppPurchaseOrderTest.php | 82 +++++++++ 7 files changed, 408 insertions(+) create mode 100644 app/GraphQL/Souk/Mutations/Orders/AppleInAppPurchaseMutation.php create mode 100644 src/Domains/Connectors/InAppPurchase/Actions/CreateOrderFromAppleReceiptAction.php create mode 100644 src/Domains/Connectors/InAppPurchase/DataTransferObject/AppleInAppPurchaseReceipt.php create mode 100644 src/Domains/Connectors/InAppPurchase/Enums/ConfigurationEnum.php create mode 100644 src/Domains/Connectors/InAppPurchase/Enums/CustomFieldEnum.php create mode 100644 tests/Connectors/Integration/InAppPurchase/AppleInAppPurchaseTest.php create mode 100644 tests/GraphQL/Souk/InAppPurchaseOrderTest.php diff --git a/app/GraphQL/Souk/Mutations/Orders/AppleInAppPurchaseMutation.php b/app/GraphQL/Souk/Mutations/Orders/AppleInAppPurchaseMutation.php new file mode 100644 index 000000000..d8dd800ee --- /dev/null +++ b/app/GraphQL/Souk/Mutations/Orders/AppleInAppPurchaseMutation.php @@ -0,0 +1,34 @@ +user(); + $company = $user->getCurrentCompany(); + $app = app(Apps::class); + $region = Regions::getDefault($company, $app); + + $appleInAppPurchase = AppleInAppPurchaseReceipt::from( + $app, + $company, + $user, + $region, + $request['input'] + ); + + $createOrderFromInAppPurchase = new CreateOrderFromAppleReceiptAction($appleInAppPurchase); + + return $createOrderFromInAppPurchase->execute(); + } +} diff --git a/src/Domains/Connectors/InAppPurchase/Actions/CreateOrderFromAppleReceiptAction.php b/src/Domains/Connectors/InAppPurchase/Actions/CreateOrderFromAppleReceiptAction.php new file mode 100644 index 000000000..9c34646ef --- /dev/null +++ b/src/Domains/Connectors/InAppPurchase/Actions/CreateOrderFromAppleReceiptAction.php @@ -0,0 +1,159 @@ +app = $appleInAppPurchase->app; + $this->company = $appleInAppPurchase->company; + $this->user = $appleInAppPurchase->user; + $this->region = $appleInAppPurchase->region; + } + + /** + * @throws ValidationException + */ + public function execute(): ModelsOrder + { + $receipt = [ + 'productId' => $this->appleInAppPurchase->product_id, + 'transactionId' => $this->appleInAppPurchase->transaction_id, + 'transactionReceipt' => $this->appleInAppPurchase->receipt, + 'transactionDate' => $this->appleInAppPurchase->transaction_date, + ]; + + $verifiedReceipt = $this->verifyReceipt($receipt); + $receiptStatus = $verifiedReceipt->getStatus(); + + if (! $receiptStatus->isValid()) { + throw new ValidationException('Invalid Receipt'); + } + + $people = $this->createPeople(); + $orderData = $this->createOrderData( + $receipt, + $verifiedReceipt->getReceipt(), + $people + ); + + return (new CreateOrderAction($orderData))->execute(); + } + + private function verifyReceipt(array $receipt): ReceiptResponse + { + $sharedSecret = $this->app->get(ConfigurationEnum::APPLE_PAYMENT_SHARED_SECRET->value); + + if (empty($sharedSecret)) { + throw new ValidationException('No Apple Payment Shared Secret Configured'); + } + + $client = $this->runInSandbox ? ClientFactory::createSandbox() : ClientFactory::create(); + $verifier = new Verifier($client, $receipt['transactionReceipt'], $sharedSecret); + + return $verifier->verify(true, $this->runInSandbox ? $client : null); + } + + private function createPeople(): People + { + return (new CreatePeopleFromUserAction( + $this->app, + $this->company->defaultBranch, + $this->user + ))->execute(); + } + + private function createOrderData(array $allReceiptData, mixed $receipt, $people): Order + { + $orderItem = $this->createOrderItem($receipt->getInApp()[0]); + + return new Order( + app: $this->app, + region: $this->region, + company: $this->company, + people: $people, + user: $this->user, + email: $this->user->email, + phone: $this->user->cell_phone_number, + token: Str::random(32), + shippingAddress: null, + billingAddress: null, + total: $this->calculateTotal($orderItem), + taxes: 0.0, + totalDiscount: 0.0, + totalShipping: 0.0, + status: 'completed', + orderNumber: '', + shippingMethod: null, + currency: $this->region->currency, + fulfillmentStatus: 'fulfilled', + items: OrderItem::collect([$orderItem], DataCollection::class), + metadata: $allReceiptData, + weight: 0.0, + checkoutToken: '', + paymentGatewayName: ['manual'], + languageCode: null, + ); + } + + private function createOrderItem($inAppData): OrderItem + { + $variant = $this->getVariant($inAppData->getProductId()); + $warehouse = $this->region->warehouses()->firstOrFail(); + + return new OrderItem( + app: $this->app, + variant: $variant, + name: $variant->name, + sku: $inAppData->getProductId(), + quantity: $inAppData->getQuantity(), + price: $variant->getPrice($warehouse), + tax: 0.0, + discount: 0.0, + currency: Currencies::getByCode(self::DEFAULT_CURRENCY), + quantityShipped: 0 + ); + } + + private function getVariant(string $sku): Variants + { + return Variants::getBySku($sku, $this->company, $this->app); + } + + private function calculateTotal(OrderItem $orderItem): float + { + return $orderItem->quantity * $orderItem->price; + } +} diff --git a/src/Domains/Connectors/InAppPurchase/DataTransferObject/AppleInAppPurchaseReceipt.php b/src/Domains/Connectors/InAppPurchase/DataTransferObject/AppleInAppPurchaseReceipt.php new file mode 100644 index 000000000..1bb90f41a --- /dev/null +++ b/src/Domains/Connectors/InAppPurchase/DataTransferObject/AppleInAppPurchaseReceipt.php @@ -0,0 +1,45 @@ +user(); + $company = $user->getCurrentCompany(); + $setupInventory = new Setup($app, $user, $company); + $setupInventory->run(); + $app->set(ConfigurationEnum::APPLE_PAYMENT_SHARED_SECRET->value, getenv('TEST_APPLE_PAYMENT_SHARED_SECRET')); + + $region = Regions::fromApp($app)->fromCompany($company)->first(); + + $receipt = [ + 'productId' => 'model_price_01', + 'transactionId' => '2000000778751927', + 'transactionReceipt' => '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', + 'transactionDate' => 1731943806000, + ]; + + $productData = new Product( + app: $app, + company: $company, + user: $user, + name: $receipt['productId'], + sku: $receipt['productId'], + warehouses: [[ + 'quantity' => 10, + 'price' => 0.29, + ], + ] + ); + $product = (new CreateProductAction($productData, $user))->execute(); + $createOrderFromReceipt = new CreateOrderFromAppleReceiptAction( + new AppleInAppPurchaseReceipt( + app: $app, + company: $company, + user: $user, + region: $region, + product_id: $receipt['productId'], + transaction_id: $receipt['transactionId'], + receipt: $receipt['transactionReceipt'], + transaction_date: $receipt['transactionDate'] + ), + runInSandbox: true + ); + + $order = $createOrderFromReceipt->execute($receipt); + + $this->assertInstanceOf(Order::class, $order); + } +} diff --git a/tests/GraphQL/Souk/InAppPurchaseOrderTest.php b/tests/GraphQL/Souk/InAppPurchaseOrderTest.php new file mode 100644 index 000000000..3e833e509 --- /dev/null +++ b/tests/GraphQL/Souk/InAppPurchaseOrderTest.php @@ -0,0 +1,82 @@ +user(); + $company = $user->getCurrentCompany(); + $setupInventory = new Setup($app, $user, $company); + $setupInventory->run(); + $app->set(ConfigurationEnum::APPLE_PAYMENT_SHARED_SECRET->value, getenv('TEST_APPLE_PAYMENT_SHARED_SECRET')); + + $region = Regions::fromApp($app)->fromCompany($company)->first(); + + // Prepare input data for the mutation + $receipt = [ + 'product_id' => 'model_price_01', + 'transaction_id' => '2000000778751927', + 'receipt' => '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', + 'transaction_date' => 1731943806000, + ]; + + $productData = new Product( + app: $app, + company: $company, + user: $user, + name: $receipt['product_id'], + sku: $receipt['product_id'], + warehouses: [[ + 'quantity' => 10, + 'price' => 0.29, + ], + ] + ); + $product = (new CreateProductAction($productData, $user))->execute(); + + // Perform GraphQL mutation + $response = $this->graphQL(' + mutation createOrderFromAppleInAppPurchase($input: AppleInAppPurchaseReceipt!) { + createOrderFromAppleInAppPurchase(input: $input) { + id + uuid + user_email + total_gross_amount + currency + status + created_at + } + } + ', [ + 'input' => $receipt, + ]); + + $response->assertSuccessful(); + $response->assertJsonStructure([ + 'data' => [ + 'createOrderFromAppleInAppPurchase' => [ + 'id', + 'uuid', + 'user_email', + 'total_gross_amount', + 'currency', + 'status', + 'created_at', + ], + ], + ]); + } +} From b4aef3a7569522a472f40ceea85984980f9692ce Mon Sep 17 00:00:00 2001 From: kaioken Date: Tue, 19 Nov 2024 00:09:48 -0400 Subject: [PATCH 10/11] refact: limit total --- src/Domains/Inventory/Products/Models/Products.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Domains/Inventory/Products/Models/Products.php b/src/Domains/Inventory/Products/Models/Products.php index 3c9a375e0..9f7523d7b 100644 --- a/src/Domains/Inventory/Products/Models/Products.php +++ b/src/Domains/Inventory/Products/Models/Products.php @@ -239,7 +239,7 @@ public function toSearchableArray(): array 'slug' => $category->slug, ]; }), - 'variants' => $this->variants->map(function ($variant) { + 'variants' => $this->variants->take(15)->map(function ($variant) { return $variant->toSearchableArray(); }), 'status' => [ From 1bf84829a7167a656d32f1840ef8874997b87536 Mon Sep 17 00:00:00 2001 From: kaioken Date: Tue, 19 Nov 2024 00:46:30 -0400 Subject: [PATCH 11/11] refact: limit total --- .../ScoutProductIndexCleanUpCommand.php | 47 +++++++++++++++++++ src/Kanvas/Enums/AppEnums.php | 2 +- 2 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 app/Console/Commands/Inventory/ScoutProductIndexCleanUpCommand.php diff --git a/app/Console/Commands/Inventory/ScoutProductIndexCleanUpCommand.php b/app/Console/Commands/Inventory/ScoutProductIndexCleanUpCommand.php new file mode 100644 index 000000000..c24a858d1 --- /dev/null +++ b/app/Console/Commands/Inventory/ScoutProductIndexCleanUpCommand.php @@ -0,0 +1,47 @@ +argument('app_id')); + $this->overwriteAppService($app); + + $this->info('Cleaning up scout index for deleted products App ' . $app->name); + $products = Products::fromApp($app)->withTrashed()->where('is_published', 0)->orWhere('is_deleted', 1); + + $this->info('Total products to clean up: ' . $products->count()); + $products->unsearchable(); + + return; + } +} diff --git a/src/Kanvas/Enums/AppEnums.php b/src/Kanvas/Enums/AppEnums.php index dc0dd8cf3..26f5daf14 100644 --- a/src/Kanvas/Enums/AppEnums.php +++ b/src/Kanvas/Enums/AppEnums.php @@ -99,7 +99,7 @@ public function getValue(): mixed self::KANVAS_APP_REGION_HEADER => 'X-Kanvas-Region', self::KANVAS_APP_COMPANY_AUTH_HEADER => 'Company-Authorization', //@deprecated self::DISPLAYNAME_LOGIN => 'displayname_login', - self::VERSION => '1.10.0', + self::VERSION => '1.12.0', self::ANONYMOUS_USER_ID => -1, self::DEFAULT_APP_JWT_TOKEN_NAME => 'kanvas-login', self::CSV_DATE_FORMAT => 'csv_date_format',