Skip to content

Commit 31b5cc0

Browse files
fix: Adjustment items can be omitted when adjustment type is full (#110)
1 parent 6f5191e commit 31b5cc0

File tree

9 files changed

+219
-5
lines changed

9 files changed

+219
-5
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66

77
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.
88

9+
## [1.7.2] - 2024-12-17
10+
11+
### Fixed
12+
13+
- Adjustment items can be omitted for when adjustment type is full
14+
915
## [1.7.1] - 2024-12-13
1016

1117
### Fixed

examples/adjustments.php

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use Paddle\SDK\Entities\Shared\Action;
6+
use Paddle\SDK\Entities\Shared\AdjustmentType;
7+
use Paddle\SDK\Exceptions\ApiError;
8+
use Paddle\SDK\Exceptions\SdkExceptions\MalformedResponse;
9+
use Paddle\SDK\Resources\Adjustments\Operations\Create\AdjustmentItem;
10+
use Paddle\SDK\Resources\Adjustments\Operations\CreateAdjustment;
11+
12+
require __DIR__ . '/../vendor/autoload.php';
13+
14+
$environment = Paddle\SDK\Environment::tryFrom(getenv('PADDLE_ENVIRONMENT') ?: '') ?? Paddle\SDK\Environment::SANDBOX;
15+
$apiKey = getenv('PADDLE_API_KEY') ?: null;
16+
$transactionId = getenv('PADDLE_TRANSACTION_ID') ?: null;
17+
$transactionItemId = getenv('PADDLE_TRANSACTION_ITEM_ID') ?: null;
18+
$fullAdjustmentTransactionId = getenv('PADDLE_FULL_ADJUSTMENT_TRANSACTION_ID') ?: null;
19+
20+
if (is_null($apiKey)) {
21+
echo "You must provide the PADDLE_API_KEY in the environment:\n";
22+
echo "PADDLE_API_KEY=your-key php examples/basic_usage.php\n";
23+
exit(1);
24+
}
25+
26+
$paddle = new Paddle\SDK\Client($apiKey, options: new Paddle\SDK\Options($environment));
27+
28+
// ┌───
29+
// │ Create Partial Adjustment │
30+
// └───────────────────────────┘
31+
try {
32+
$partialAdjustment = $paddle->adjustments->create(
33+
CreateAdjustment::partial(
34+
Action::Refund(),
35+
[
36+
new AdjustmentItem(
37+
$transactionItemId,
38+
AdjustmentType::Partial(),
39+
'100',
40+
),
41+
],
42+
'error',
43+
$transactionId,
44+
),
45+
);
46+
} catch (ApiError|MalformedResponse $e) {
47+
var_dump($e);
48+
exit;
49+
}
50+
51+
echo sprintf("Partial Adjustment ID: %s\n", $partialAdjustment->id);
52+
53+
// ┌───
54+
// │ Create Full Adjustment │
55+
// └────────────────────────┘
56+
try {
57+
$fullAdjustment = $paddle->adjustments->create(
58+
CreateAdjustment::full(
59+
Action::Refund(),
60+
'error',
61+
$fullAdjustmentTransactionId,
62+
),
63+
);
64+
} catch (ApiError|MalformedResponse $e) {
65+
var_dump($e);
66+
exit;
67+
}
68+
69+
echo sprintf("Full Adjustment ID: %s\n", $fullAdjustment->id);

src/Client.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060

6161
class Client
6262
{
63-
private const SDK_VERSION = '1.7.1';
63+
private const SDK_VERSION = '1.7.2';
6464

6565
public readonly LoggerInterface $logger;
6666
public readonly Options $options;

src/Resources/Adjustments/Operations/CreateAdjustment.php

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,28 +22,46 @@ class CreateAdjustment implements \JsonSerializable
2222
*/
2323
public function __construct(
2424
public readonly Action $action,
25-
public readonly array|Undefined $items,
25+
public readonly array|Undefined|null $items,
2626
public readonly string $reason,
2727
public readonly string $transactionId,
2828
public readonly AdjustmentType|Undefined $type = new Undefined(),
2929
) {
30-
if ($this->type === AdjustmentType::Partial() && ($this->items instanceof Undefined || empty($this->items))) {
30+
$typeIsFull = AdjustmentType::Full()->equals($this->type);
31+
32+
if (! $typeIsFull && ($this->items instanceof Undefined || empty($this->items))) {
3133
throw InvalidArgumentException::arrayIsEmpty('items');
3234
}
35+
36+
if ($typeIsFull && is_array($this->items)) {
37+
throw new InvalidArgumentException('items are not allowed when the adjustment type is full');
38+
}
39+
}
40+
41+
public static function full(Action $action, string $reason, string $transactionId): self
42+
{
43+
return new self($action, new Undefined(), $reason, $transactionId, AdjustmentType::Full());
44+
}
45+
46+
public static function partial(Action $action, array $items, string $reason, string $transactionId): self
47+
{
48+
return new self($action, $items, $reason, $transactionId, AdjustmentType::Partial());
3349
}
3450

3551
public function jsonSerialize(): array
3652
{
37-
$items = [];
53+
if (is_array($this->items)) {
54+
$items = [];
3855

39-
if (! $this->items instanceof Undefined) {
4056
foreach ($this->items as $item) {
4157
$items[] = [
4258
'item_id' => $item->itemId,
4359
'type' => $item->type->getValue(),
4460
'amount' => $item->amount,
4561
];
4662
}
63+
} else {
64+
$items = $this->items;
4765
}
4866

4967
return $this->filterUndefined([

tests/Functional/Resources/Adjustments/AdjustmentsClientTest.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,43 @@ public static function createOperationsProvider(): \Generator
100100
new Response(200, body: self::readRawJsonFixture('response/full_entity')),
101101
self::readRawJsonFixture('request/create_full'),
102102
];
103+
104+
yield 'Partial type with items' => [
105+
CreateAdjustment::partial(
106+
Action::Refund(),
107+
[new AdjustmentItem(
108+
'txnitm_01h8bxryv3065dyh6103p3yg28',
109+
AdjustmentType::Partial(),
110+
'100',
111+
)],
112+
'error',
113+
'txn_01h8bxpvx398a7zbawb77y0kp5',
114+
),
115+
new Response(200, body: self::readRawJsonFixture('response/minimal_entity')),
116+
self::readRawJsonFixture('request/create_type_partial_with_items'),
117+
];
118+
119+
yield 'Full type with no items' => [
120+
CreateAdjustment::full(
121+
Action::Refund(),
122+
'error',
123+
'txn_01h8bxpvx398a7zbawb77y0kp5',
124+
),
125+
new Response(200, body: self::readRawJsonFixture('response/minimal_entity')),
126+
self::readRawJsonFixture('request/create_type_full_with_no_items'),
127+
];
128+
129+
yield 'Full type with null items' => [
130+
new CreateAdjustment(
131+
Action::Refund(),
132+
null,
133+
'error',
134+
'txn_01h8bxpvx398a7zbawb77y0kp5',
135+
\Paddle\SDK\Entities\Adjustment\AdjustmentType::Full(),
136+
),
137+
new Response(200, body: self::readRawJsonFixture('response/minimal_entity')),
138+
self::readRawJsonFixture('request/create_type_full_with_null_items'),
139+
];
103140
}
104141

105142
/**
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"action": "refund",
3+
"type": "full",
4+
"reason": "error",
5+
"transaction_id": "txn_01h8bxpvx398a7zbawb77y0kp5"
6+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"action": "refund",
3+
"type": "full",
4+
"items": null,
5+
"reason": "error",
6+
"transaction_id": "txn_01h8bxpvx398a7zbawb77y0kp5"
7+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"action": "refund",
3+
"type": "partial",
4+
"items": [
5+
{
6+
"item_id": "txnitm_01h8bxryv3065dyh6103p3yg28",
7+
"type": "partial",
8+
"amount": "100"
9+
}
10+
],
11+
"reason": "error",
12+
"transaction_id": "txn_01h8bxpvx398a7zbawb77y0kp5"
13+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Paddle\SDK\Tests\Unit\Resources\Adjustments\Operations;
6+
7+
use Paddle\SDK\Entities\Adjustment\AdjustmentType;
8+
use Paddle\SDK\Entities\Shared\Action;
9+
use Paddle\SDK\Exceptions\SdkExceptions\InvalidArgumentException;
10+
use Paddle\SDK\Resources\Adjustments\Operations\CreateAdjustment;
11+
use Paddle\SDK\Undefined;
12+
use PHPUnit\Framework\TestCase;
13+
14+
class CreateAdjustmentTest extends TestCase
15+
{
16+
/**
17+
* @test
18+
*
19+
* @dataProvider invalidItemsDataProvider
20+
*/
21+
public function it_validates_items(array|Undefined|null $items, AdjustmentType $type, string $expectedExceptionMessage): void
22+
{
23+
self::expectException(InvalidArgumentException::class);
24+
self::expectExceptionMessage($expectedExceptionMessage);
25+
26+
new CreateAdjustment(
27+
Action::Refund(),
28+
$items,
29+
'error',
30+
'txn_01h8bxpvx398a7zbawb77y0kp5',
31+
$type,
32+
);
33+
}
34+
35+
public static function invalidItemsDataProvider(): \Generator
36+
{
37+
yield 'Empty' => [
38+
[],
39+
AdjustmentType::Partial(),
40+
'items cannot be empty',
41+
];
42+
yield 'Undefined' => [
43+
new Undefined(),
44+
AdjustmentType::Partial(),
45+
'items cannot be empty',
46+
];
47+
yield 'Null' => [
48+
null,
49+
AdjustmentType::Partial(),
50+
'items cannot be empty',
51+
];
52+
yield 'Items for full type' => [
53+
[],
54+
AdjustmentType::Full(),
55+
'items are not allowed when the adjustment type is full',
56+
];
57+
}
58+
}

0 commit comments

Comments
 (0)