Skip to content

Commit

Permalink
fix: Adjustment items can be omitted when adjustment type is full (#110)
Browse files Browse the repository at this point in the history
  • Loading branch information
davidgrayston-paddle authored Dec 17, 2024
1 parent 6f5191e commit 31b5cc0
Show file tree
Hide file tree
Showing 9 changed files with 219 additions and 5 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),

Check our main [developer changelog](https://developer.paddle.com/?utm_source=dx&utm_medium=paddle-php-sdk) for information about changes to the Paddle Billing platform, the Paddle API, and other developer tools.

## [1.7.2] - 2024-12-17

### Fixed

- Adjustment items can be omitted for when adjustment type is full

## [1.7.1] - 2024-12-13

### Fixed
Expand Down
69 changes: 69 additions & 0 deletions examples/adjustments.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php

declare(strict_types=1);

use Paddle\SDK\Entities\Shared\Action;
use Paddle\SDK\Entities\Shared\AdjustmentType;
use Paddle\SDK\Exceptions\ApiError;
use Paddle\SDK\Exceptions\SdkExceptions\MalformedResponse;
use Paddle\SDK\Resources\Adjustments\Operations\Create\AdjustmentItem;
use Paddle\SDK\Resources\Adjustments\Operations\CreateAdjustment;

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

$environment = Paddle\SDK\Environment::tryFrom(getenv('PADDLE_ENVIRONMENT') ?: '') ?? Paddle\SDK\Environment::SANDBOX;
$apiKey = getenv('PADDLE_API_KEY') ?: null;
$transactionId = getenv('PADDLE_TRANSACTION_ID') ?: null;
$transactionItemId = getenv('PADDLE_TRANSACTION_ITEM_ID') ?: null;
$fullAdjustmentTransactionId = getenv('PADDLE_FULL_ADJUSTMENT_TRANSACTION_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));

// ┌───
// │ Create Partial Adjustment │
// └───────────────────────────┘
try {
$partialAdjustment = $paddle->adjustments->create(
CreateAdjustment::partial(
Action::Refund(),
[
new AdjustmentItem(
$transactionItemId,
AdjustmentType::Partial(),
'100',
),
],
'error',
$transactionId,
),
);
} catch (ApiError|MalformedResponse $e) {
var_dump($e);
exit;
}

echo sprintf("Partial Adjustment ID: %s\n", $partialAdjustment->id);

// ┌───
// │ Create Full Adjustment │
// └────────────────────────┘
try {
$fullAdjustment = $paddle->adjustments->create(
CreateAdjustment::full(
Action::Refund(),
'error',
$fullAdjustmentTransactionId,
),
);
} catch (ApiError|MalformedResponse $e) {
var_dump($e);
exit;
}

echo sprintf("Full Adjustment ID: %s\n", $fullAdjustment->id);
2 changes: 1 addition & 1 deletion src/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@

class Client
{
private const SDK_VERSION = '1.7.1';
private const SDK_VERSION = '1.7.2';

public readonly LoggerInterface $logger;
public readonly Options $options;
Expand Down
26 changes: 22 additions & 4 deletions src/Resources/Adjustments/Operations/CreateAdjustment.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,28 +22,46 @@ class CreateAdjustment implements \JsonSerializable
*/
public function __construct(
public readonly Action $action,
public readonly array|Undefined $items,
public readonly array|Undefined|null $items,
public readonly string $reason,
public readonly string $transactionId,
public readonly AdjustmentType|Undefined $type = new Undefined(),
) {
if ($this->type === AdjustmentType::Partial() && ($this->items instanceof Undefined || empty($this->items))) {
$typeIsFull = AdjustmentType::Full()->equals($this->type);

if (! $typeIsFull && ($this->items instanceof Undefined || empty($this->items))) {
throw InvalidArgumentException::arrayIsEmpty('items');
}

if ($typeIsFull && is_array($this->items)) {
throw new InvalidArgumentException('items are not allowed when the adjustment type is full');
}
}

public static function full(Action $action, string $reason, string $transactionId): self
{
return new self($action, new Undefined(), $reason, $transactionId, AdjustmentType::Full());
}

public static function partial(Action $action, array $items, string $reason, string $transactionId): self
{
return new self($action, $items, $reason, $transactionId, AdjustmentType::Partial());
}

public function jsonSerialize(): array
{
$items = [];
if (is_array($this->items)) {
$items = [];

if (! $this->items instanceof Undefined) {
foreach ($this->items as $item) {
$items[] = [
'item_id' => $item->itemId,
'type' => $item->type->getValue(),
'amount' => $item->amount,
];
}
} else {
$items = $this->items;
}

return $this->filterUndefined([
Expand Down
37 changes: 37 additions & 0 deletions tests/Functional/Resources/Adjustments/AdjustmentsClientTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,43 @@ public static function createOperationsProvider(): \Generator
new Response(200, body: self::readRawJsonFixture('response/full_entity')),
self::readRawJsonFixture('request/create_full'),
];

yield 'Partial type with items' => [
CreateAdjustment::partial(
Action::Refund(),
[new AdjustmentItem(
'txnitm_01h8bxryv3065dyh6103p3yg28',
AdjustmentType::Partial(),
'100',
)],
'error',
'txn_01h8bxpvx398a7zbawb77y0kp5',
),
new Response(200, body: self::readRawJsonFixture('response/minimal_entity')),
self::readRawJsonFixture('request/create_type_partial_with_items'),
];

yield 'Full type with no items' => [
CreateAdjustment::full(
Action::Refund(),
'error',
'txn_01h8bxpvx398a7zbawb77y0kp5',
),
new Response(200, body: self::readRawJsonFixture('response/minimal_entity')),
self::readRawJsonFixture('request/create_type_full_with_no_items'),
];

yield 'Full type with null items' => [
new CreateAdjustment(
Action::Refund(),
null,
'error',
'txn_01h8bxpvx398a7zbawb77y0kp5',
\Paddle\SDK\Entities\Adjustment\AdjustmentType::Full(),
),
new Response(200, body: self::readRawJsonFixture('response/minimal_entity')),
self::readRawJsonFixture('request/create_type_full_with_null_items'),
];
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"action": "refund",
"type": "full",
"reason": "error",
"transaction_id": "txn_01h8bxpvx398a7zbawb77y0kp5"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"action": "refund",
"type": "full",
"items": null,
"reason": "error",
"transaction_id": "txn_01h8bxpvx398a7zbawb77y0kp5"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"action": "refund",
"type": "partial",
"items": [
{
"item_id": "txnitm_01h8bxryv3065dyh6103p3yg28",
"type": "partial",
"amount": "100"
}
],
"reason": "error",
"transaction_id": "txn_01h8bxpvx398a7zbawb77y0kp5"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

declare(strict_types=1);

namespace Paddle\SDK\Tests\Unit\Resources\Adjustments\Operations;

use Paddle\SDK\Entities\Adjustment\AdjustmentType;
use Paddle\SDK\Entities\Shared\Action;
use Paddle\SDK\Exceptions\SdkExceptions\InvalidArgumentException;
use Paddle\SDK\Resources\Adjustments\Operations\CreateAdjustment;
use Paddle\SDK\Undefined;
use PHPUnit\Framework\TestCase;

class CreateAdjustmentTest extends TestCase
{
/**
* @test
*
* @dataProvider invalidItemsDataProvider
*/
public function it_validates_items(array|Undefined|null $items, AdjustmentType $type, string $expectedExceptionMessage): void
{
self::expectException(InvalidArgumentException::class);
self::expectExceptionMessage($expectedExceptionMessage);

new CreateAdjustment(
Action::Refund(),
$items,
'error',
'txn_01h8bxpvx398a7zbawb77y0kp5',
$type,
);
}

public static function invalidItemsDataProvider(): \Generator
{
yield 'Empty' => [
[],
AdjustmentType::Partial(),
'items cannot be empty',
];
yield 'Undefined' => [
new Undefined(),
AdjustmentType::Partial(),
'items cannot be empty',
];
yield 'Null' => [
null,
AdjustmentType::Partial(),
'items cannot be empty',
];
yield 'Items for full type' => [
[],
AdjustmentType::Full(),
'items are not allowed when the adjustment type is full',
];
}
}

0 comments on commit 31b5cc0

Please sign in to comment.