Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Simulator API #84

Merged
merged 10 commits into from
Oct 17, 2024
Merged
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
Loading