Skip to content

Commit

Permalink
feat: Simulator API (#84)
Browse files Browse the repository at this point in the history
Co-authored-by: davidgrayston-paddle <[email protected]>
  • Loading branch information
vifer and davidgrayston-paddle authored Oct 17, 2024
1 parent 7bdd4ab commit d370f6c
Show file tree
Hide file tree
Showing 64 changed files with 3,289 additions and 14 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Check our main [developer changelog](https://developer.paddle.com/?utm_source=dx

### Added

- Added simulations api [related changelog](https://developer.paddle.com/changelog/2024/webhook-simulator?utm_source=dx&utm_medium=paddle-php-sdk)
- Added `traffic_source` property to `NotificationSetting` entity
- Support notification settings `traffic_source` filter
- Support new payment methods `offline`, `unknown`, `wire_transfer`
Expand All @@ -24,6 +25,7 @@ Check our main [developer changelog](https://developer.paddle.com/?utm_source=dx
- `items[]->priceId` is now nullable
- `details->lineItems[]->product` can now return `Product` (with `id`) or `TransactionPreviewProduct` (with nullable `id`)
- Empty custom data array will now serialize to empty JSON object `{}`
- `EventsClient::list` and `Notification->payload` will now return `UndefinedEvent` for unknown event types.

### Added
- `TransactionsClient::create()` now supports operation items with optional properties:
Expand Down
2 changes: 1 addition & 1 deletion examples/catalog_management.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

require __DIR__ . '/../vendor/autoload.php';

$environment = Paddle\SDK\Environment::tryFrom(getenv('PAD') ?: '') ?? Paddle\SDK\Environment::SANDBOX;
$environment = Paddle\SDK\Environment::tryFrom(getenv('PADDLE_ENVIRONMENT') ?: '') ?? Paddle\SDK\Environment::SANDBOX;
$apiKey = getenv('PADDLE_API_KEY') ?: null;

if (is_null($apiKey)) {
Expand Down
130 changes: 130 additions & 0 deletions examples/simulations.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
<?php

declare(strict_types=1);

use Paddle\SDK\Entities\Event\EventTypeName;
use Paddle\SDK\Exceptions\ApiError;
use Paddle\SDK\Exceptions\SdkExceptions\MalformedResponse;
use Paddle\SDK\Notifications\Entities\Address;
use Paddle\SDK\Resources\Shared\Operations\List\Pager;
use Paddle\SDK\Resources\Simulations\Operations\CreateSimulation;
use Paddle\SDK\Resources\Simulations\Operations\ListSimulations;
use Paddle\SDK\Resources\Simulations\Operations\UpdateSimulation;

require __DIR__ . '/../vendor/autoload.php';

$environment = Paddle\SDK\Environment::tryFrom(getenv('PADDLE_ENVIRONMENT') ?: '') ?? Paddle\SDK\Environment::SANDBOX;
$apiKey = getenv('PADDLE_API_KEY') ?: null;
$simulationId = getenv('PADDLE_SIMULATION_ID') ?: null;
$notificationSettingId = getenv('PADDLE_NOTIFICATION_SETTING_ID') ?: null;

if (is_null($apiKey)) {
echo "You must provide the PADDLE_API_KEY in the environment:\n";
echo "PADDLE_API_KEY=your-key php examples/basic_usage.php\n";
exit(1);
}

$paddle = new Paddle\SDK\Client($apiKey, options: new Paddle\SDK\Options($environment));

// ┌───
// │ List Simulations │
// └──────────────────┘
try {
$simulations = $paddle->simulations->list(new ListSimulations(new Pager(perPage: 10)));
} catch (ApiError|MalformedResponse $e) {
var_dump($e);
exit;
}

echo "List Simulations\n";

foreach ($simulations as $simulation) {
echo sprintf("- %s:\n", $simulation->name);
echo sprintf(" - ID: %s\n", $simulation->id);
echo sprintf(" - Type: %s\n", $simulation->type->getValue());
echo sprintf(" - Notification Setting ID: %s\n", $simulation->notificationSettingId);
}

// ┌───
// │ Create Simulation │
// └───────────────────┘
try {
$simulation = $paddle->simulations->create(
new CreateSimulation(
notificationSettingId: $notificationSettingId,
type: EventTypeName::AddressCreated(),
name: 'Simulate Address Creation',
payload: Address::from([
'id' => 'add_01hv8gq3318ktkfengj2r75gfx',
'country_code' => 'US',
'status' => 'active',
'created_at' => '2024-04-12T06:42:58.785000Z',
'updated_at' => '2024-04-12T06:42:58.785000Z',
'customer_id' => 'ctm_01hv6y1jedq4p1n0yqn5ba3ky4',
'description' => 'Head Office',
'first_line' => '4050 Jefferson Plaza, 41st Floor',
'second_line' => null,
'city' => 'New York',
'postal_code' => '10021',
'region' => 'NY',
]),
),
);
} catch (ApiError|MalformedResponse $e) {
var_dump($e);
exit;
}

echo sprintf("Created Simulation: %s\n", $simulation->name);
echo sprintf("- ID: %s\n", $simulation->id);
echo sprintf("- Type: %s\n", $simulation->type->getValue());
echo sprintf("- Notification Setting ID: %s\n", $simulation->notificationSettingId);

// ┌───
// │ Get Simulation │
// └────────────────┘
try {
$simulation = $paddle->simulations->get($simulationId);
} catch (ApiError|MalformedResponse $e) {
var_dump($e);
exit;
}

echo sprintf("Get Simulation: %s\n", $simulation->name);
echo sprintf("- ID: %s\n", $simulation->id);
echo sprintf("- Type: %s\n", $simulation->type->getValue());
echo sprintf("- Notification Setting ID: %s\n", $simulation->notificationSettingId);

// ┌───
// │ Update Simulation │
// └───────────────────┘
try {
$simulation = $paddle->simulations->update(
$simulationId,
new UpdateSimulation(
type: EventTypeName::AddressCreated(),
payload: Address::from([
'id' => 'add_01hv8gq3318ktkfengj2r75gfx',
'country_code' => 'US',
'status' => 'active',
'created_at' => '2024-04-12T06:42:58.785000Z',
'updated_at' => '2024-04-12T06:42:58.785000Z',
'customer_id' => 'ctm_01hv6y1jedq4p1n0yqn5ba3ky4',
'description' => 'Head Office',
'first_line' => '4050 Jefferson Plaza, 41st Floor',
'second_line' => null,
'city' => 'New York',
'postal_code' => '10021',
'region' => 'NY',
]),
),
);
} catch (ApiError|MalformedResponse $e) {
var_dump($e);
exit;
}

echo sprintf("Updated Simulation: %s\n", $simulation->name);
echo sprintf("- ID: %s\n", $simulation->id);
echo sprintf("- Type: %s\n", $simulation->type->getValue());
echo sprintf("- Notification Setting ID: %s\n", $simulation->notificationSettingId);
21 changes: 20 additions & 1 deletion src/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use Http\Discovery\HttpAsyncClientDiscovery;
use Http\Discovery\Psr17FactoryDiscovery;
use Http\Message\Authentication\Bearer;
use Paddle\SDK\Entities\DateTime;
use Paddle\SDK\Logger\Formatter;
use Paddle\SDK\Resources\Addresses\AddressesClient;
use Paddle\SDK\Resources\Adjustments\AdjustmentsClient;
Expand All @@ -32,6 +33,10 @@
use Paddle\SDK\Resources\PricingPreviews\PricingPreviewsClient;
use Paddle\SDK\Resources\Products\ProductsClient;
use Paddle\SDK\Resources\Reports\ReportsClient;
use Paddle\SDK\Resources\SimulationRunEvents\SimulationRunEventsClient;
use Paddle\SDK\Resources\SimulationRuns\SimulationRunsClient;
use Paddle\SDK\Resources\Simulations\SimulationsClient;
use Paddle\SDK\Resources\SimulationTypes\SimulationTypesClient;
use Paddle\SDK\Resources\Subscriptions\SubscriptionsClient;
use Paddle\SDK\Resources\Transactions\TransactionsClient;
use Psr\Http\Message\RequestFactoryInterface;
Expand All @@ -45,6 +50,7 @@
use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter;
use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer;
use Symfony\Component\Serializer\Normalizer\BackedEnumNormalizer;
use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer;
use Symfony\Component\Serializer\Normalizer\JsonSerializableNormalizer;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Serializer;
Expand Down Expand Up @@ -73,6 +79,10 @@ class Client
public readonly NotificationsClient $notifications;
public readonly NotificationLogsClient $notificationLogs;
public readonly ReportsClient $reports;
public readonly SimulationsClient $simulations;
public readonly SimulationRunsClient $simulationRuns;
public readonly SimulationRunEventsClient $simulationRunEvents;
public readonly SimulationTypesClient $simulationTypes;

private readonly HttpAsyncClient $httpClient;
private readonly RequestFactoryInterface $requestFactory;
Expand Down Expand Up @@ -116,6 +126,10 @@ public function __construct(
$this->notifications = new NotificationsClient($this);
$this->notificationLogs = new NotificationLogsClient($this);
$this->reports = new ReportsClient($this);
$this->simulations = new SimulationsClient($this);
$this->simulationRuns = new SimulationRunsClient($this);
$this->simulationRunEvents = new SimulationRunEventsClient($this);
$this->simulationTypes = new SimulationTypesClient($this);
}

public function getRaw(string|UriInterface $uri, array|HasParameters $parameters = []): ResponseInterface
Expand Down Expand Up @@ -173,7 +187,12 @@ private function requestRaw(string $method, string|UriInterface $uri, array|\Jso
$request = $this->requestFactory->createRequest($method, $uri);

$serializer = new Serializer(
[new BackedEnumNormalizer(), new JsonSerializableNormalizer(), new ObjectNormalizer(nameConverter: new CamelCaseToSnakeCaseNameConverter())],
[
new BackedEnumNormalizer(),
new DateTimeNormalizer([DateTimeNormalizer::FORMAT_KEY => DateTime::PADDLE_RFC3339]),
new JsonSerializableNormalizer(),
new ObjectNormalizer(nameConverter: new CamelCaseToSnakeCaseNameConverter()),
],
[new JsonEncoder()],
);

Expand Down
30 changes: 30 additions & 0 deletions src/Entities/Collections/SimulationCollection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

declare(strict_types=1);

/**
* |------
* | ! Generated code !
* | Altering this code will result in changes being overwritten |
* |-------------------------------------------------------------|.
*/

namespace Paddle\SDK\Entities\Collections;

use Paddle\SDK\Entities\Simulation;

class SimulationCollection extends Collection
{
public static function from(array $itemsData, Paginator|null $paginator = null): self
{
return new self(
array_map(fn (array $item): Simulation => Simulation::from($item), $itemsData),
$paginator,
);
}

public function current(): Simulation
{
return parent::current();
}
}
30 changes: 30 additions & 0 deletions src/Entities/Collections/SimulationRunCollection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

declare(strict_types=1);

/**
* |------
* | ! Generated code !
* | Altering this code will result in changes being overwritten |
* |-------------------------------------------------------------|.
*/

namespace Paddle\SDK\Entities\Collections;

use Paddle\SDK\Entities\SimulationRun;

class SimulationRunCollection extends Collection
{
public static function from(array $itemsData, Paginator|null $paginator = null): self
{
return new self(
array_map(fn (array $item): SimulationRun => SimulationRun::from($item), $itemsData),
$paginator,
);
}

public function current(): SimulationRun
{
return parent::current();
}
}
30 changes: 30 additions & 0 deletions src/Entities/Collections/SimulationRunEventCollection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

declare(strict_types=1);

/**
* |------
* | ! Generated code !
* | Altering this code will result in changes being overwritten |
* |-------------------------------------------------------------|.
*/

namespace Paddle\SDK\Entities\Collections;

use Paddle\SDK\Entities\SimulationRunEvent;

class SimulationRunEventCollection extends Collection
{
public static function from(array $itemsData, Paginator|null $paginator = null): self
{
return new self(
array_map(fn (array $item): SimulationRunEvent => SimulationRunEvent::from($item), $itemsData),
$paginator,
);
}

public function current(): SimulationRunEvent
{
return parent::current();
}
}
30 changes: 30 additions & 0 deletions src/Entities/Collections/SimulationTypeCollection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

declare(strict_types=1);

/**
* |------
* | ! Generated code !
* | Altering this code will result in changes being overwritten |
* |-------------------------------------------------------------|.
*/

namespace Paddle\SDK\Entities\Collections;

use Paddle\SDK\Entities\SimulationType;

class SimulationTypeCollection extends Collection
{
public static function from(array $itemsData, Paginator|null $paginator = null): self
{
return new self(
array_map(fn (array $item): SimulationType => SimulationType::from($item), $itemsData),
$paginator,
);
}

public function current(): SimulationType
{
return parent::current();
}
}
14 changes: 4 additions & 10 deletions src/Entities/Event.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

use Paddle\SDK\Entities\Event\EventTypeName;
use Paddle\SDK\Notifications\Entities\Entity as NotificationEntity;
use Paddle\SDK\Notifications\Entities\EntityFactory;
use Paddle\SDK\Notifications\Events\UndefinedEvent;
use Psr\Http\Message\ServerRequestInterface;

abstract class Event implements Entity
Expand All @@ -22,28 +24,20 @@ protected function __construct(
public static function from(array $data): self
{
$type = explode('.', (string) $data['event_type']);
$entity = $type[0] ?? 'Unknown';
$identifier = str_replace('_', '', ucwords(implode('_', $type), '_'));

/** @var class-string<Event> $event */
$event = sprintf('\Paddle\SDK\Notifications\Events\%s', $identifier);

if (! class_exists($event) || ! is_subclass_of($event, self::class)) {
throw new \UnexpectedValueException("Event type '{$identifier}' cannot be mapped to an object");
}

/** @var class-string<NotificationEntity> $entity */
$entity = sprintf('\Paddle\SDK\Notifications\Entities\%s', ucfirst($entity));

if (! class_exists($entity) || ! in_array(NotificationEntity::class, class_implements($entity), true)) {
throw new \UnexpectedValueException("Event type '{$identifier}' cannot be mapped to an object");
$event = UndefinedEvent::class;
}

return $event::fromEvent(
$data['event_id'],
EventTypeName::from($data['event_type']),
DateTime::from($data['occurred_at']),
$entity::from($data['data']),
EntityFactory::create($data['event_type'], $data['data']),
$data['notification_id'] ?? null,
);
}
Expand Down
Loading

0 comments on commit d370f6c

Please sign in to comment.