Skip to content

Commit

Permalink
fix: Add proration to transation line items
Browse files Browse the repository at this point in the history
  • Loading branch information
davidgrayston-paddle committed Dec 4, 2024
1 parent 0005443 commit dbe0e70
Show file tree
Hide file tree
Showing 9 changed files with 360 additions and 46 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ Check our main [developer changelog](https://developer.paddle.com/?utm_source=dx
### Fixed

- `Client->notifications->replay()` now calls the correct endpoint
- Subscription transaction line items now include proration (`nextTransaction`, `recurringTransactionDetails`, `immediateTransaction`)
- Transaction preview line items now include proration

## [1.5.0] - 2024-11-18

Expand Down
3 changes: 3 additions & 0 deletions src/Entities/Shared/TransactionLineItemPreview.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

use Paddle\SDK\Entities\Product;
use Paddle\SDK\Entities\Transaction\TransactionPreviewProduct;
use Paddle\SDK\Entities\Transaction\TransactionProration;

class TransactionLineItemPreview
{
Expand All @@ -23,6 +24,7 @@ private function __construct(
public UnitTotals $unitTotals,
public Totals $totals,
public Product|TransactionPreviewProduct $product,
public TransactionProration|null $proration,
) {
}

Expand All @@ -37,6 +39,7 @@ public static function from(array $data): self
isset($data['product']['id'])
? Product::from($data['product'])
: TransactionPreviewProduct::from($data['product']),
isset($data['proration']) ? TransactionProration::from($data['proration']) : null,
);
}
}
102 changes: 100 additions & 2 deletions tests/Functional/Resources/Subscriptions/SubscriptionsClientTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -683,7 +683,7 @@ public static function previewOneTimeChargeOperationsProvider(): \Generator
new SubscriptionItems('pri_01gsz98e27ak2tyhexptwc58yk', 1),
],
),
new Response(200, body: self::readRawJsonFixture('response/preview_update_full_entity')),
new Response(200, body: self::readRawJsonFixture('response/preview_charge_full_entity')),
self::readRawJsonFixture('request/preview_one_time_charge_minimal'),
];

Expand All @@ -696,8 +696,106 @@ public static function previewOneTimeChargeOperationsProvider(): \Generator
new SubscriptionItems('pri_01h7zd9mzfq79850w4ryc39v38', 845),
],
),
new Response(200, body: self::readRawJsonFixture('response/preview_update_full_entity')),
new Response(200, body: self::readRawJsonFixture('response/preview_charge_full_entity')),
self::readRawJsonFixture('request/preview_one_time_charge_full'),
];
}

/**
* @test
*/
public function get_with_includes_returns_nullable_proration(): void
{
$this->mockClient->addResponse(new Response(200, body: self::readRawJsonFixture('response/full_entity_with_includes')));
$subscription = $this->client->subscriptions->get('sub_01h8bx8fmywym11t6swgzba704');

$recurringTransactionProration = $subscription->recurringTransactionDetails->lineItems[0]->proration;
self::assertNotNull($recurringTransactionProration);
self::assertEquals('1', $recurringTransactionProration->rate);
self::assertEquals(
'2024-02-08T11:02:03+00:00',
$recurringTransactionProration->billingPeriod->startsAt->format(DATE_RFC3339),
);
self::assertEquals(
'2024-03-08T11:02:03+00:00',
$recurringTransactionProration->billingPeriod->endsAt->format(DATE_RFC3339),
);

$nullRecurringTransactionProration = $subscription->recurringTransactionDetails->lineItems[1]->proration;
self::assertNull($nullRecurringTransactionProration);

$nextTransactionProration = $subscription->nextTransaction->details->lineItems[0]->proration;
self::assertNotNull($nextTransactionProration);
self::assertEquals('1', $nextTransactionProration->rate);
self::assertEquals(
'2023-12-03T16:38:53+00:00',
$nextTransactionProration->billingPeriod->startsAt->format(DATE_RFC3339),
);
self::assertEquals(
'2024-01-03T16:38:53+00:00',
$nextTransactionProration->billingPeriod->endsAt->format(DATE_RFC3339),
);

$nullNextTransactionProration = $subscription->nextTransaction->details->lineItems[1]->proration;
self::assertNull($nullNextTransactionProration);
}

/**
* @test
*/
public function preview_returns_nullable_proration(): void
{
$this->mockClient->addResponse(new Response(200, body: self::readRawJsonFixture('response/preview_update_full_entity')));
$subscriptionPreview = $this->client->subscriptions->previewUpdate(
'sub_01h8bx8fmywym11t6swgzba704',
new PreviewUpdateSubscription(
prorationBillingMode: SubscriptionProrationBillingMode::ProratedNextBillingPeriod(),
),
);

$recurringTransactionProration = $subscriptionPreview->recurringTransactionDetails->lineItems[0]->proration;
self::assertNotNull($recurringTransactionProration);
self::assertEquals('1', $recurringTransactionProration->rate);
self::assertEquals(
'2024-02-08T11:02:03+00:00',
$recurringTransactionProration->billingPeriod->startsAt->format(DATE_RFC3339),
);
self::assertEquals(
'2024-03-08T11:02:03+00:00',
$recurringTransactionProration->billingPeriod->endsAt->format(DATE_RFC3339),
);

$nullRecurringTransactionProration = $subscriptionPreview->recurringTransactionDetails->lineItems[1]->proration;
self::assertNull($nullRecurringTransactionProration);

$nextTransactionProration = $subscriptionPreview->nextTransaction->details->lineItems[0]->proration;
self::assertNotNull($nextTransactionProration);
self::assertEquals('1', $nextTransactionProration->rate);
self::assertEquals(
'2024-03-08T11:02:03+00:00',
$nextTransactionProration->billingPeriod->startsAt->format(DATE_RFC3339),
);
self::assertEquals(
'2024-04-08T11:02:03+00:00',
$nextTransactionProration->billingPeriod->endsAt->format(DATE_RFC3339),
);

$nullNextTransactionProration = $subscriptionPreview->nextTransaction->details->lineItems[1]->proration;
self::assertNull($nullNextTransactionProration);

$immediateTransactionProration = $subscriptionPreview->immediateTransaction->details->lineItems[0]->proration;
self::assertNotNull($immediateTransactionProration);
self::assertEquals('0.99993', $immediateTransactionProration->rate);
self::assertEquals(
'2024-02-08T11:05:53+00:00',
$immediateTransactionProration->billingPeriod->startsAt->format(DATE_RFC3339),
);
self::assertEquals(
'2024-03-08T11:02:03+00:00',
$immediateTransactionProration->billingPeriod->endsAt->format(DATE_RFC3339),
);

$nullImmediateTransactionProration = $subscriptionPreview->immediateTransaction->details->lineItems[1]->proration;
self::assertNull($nullImmediateTransactionProration);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -117,13 +117,7 @@
"tax": "500",
"total": "3000"
},
"proration": {
"rate": "1",
"billing_period": {
"starts_at": "2023-12-03T16:38:53.111897Z",
"ends_at": "2024-01-03T16:38:53.111897Z"
}
}
"proration": null
}
]
},
Expand Down Expand Up @@ -160,6 +154,116 @@
}
]
},
"recurring_transaction_details": {
"tax_rates_used": [
{
"tax_rate": "0.08875",
"totals": {
"subtotal": "178500",
"discount": "0",
"tax": "15841",
"total": "194341"
}
}
],
"totals": {
"subtotal": "178500",
"tax": "15841",
"discount": "0",
"total": "194341",
"fee": null,
"credit": "0",
"credit_to_balance": "0",
"balance": "194341",
"grand_total": "194341",
"earnings": null,
"currency_code": "USD",
"exchange_rate": "1"
},
"line_items": [
{
"item_id": null,
"price_id": "pri_01gsz8x8sawmvhz1pv30nge1ke",
"quantity": 50,
"totals": {
"subtotal": "150000",
"tax": "13312",
"discount": "0",
"total": "163312"
},
"product": {
"id": "pro_01gsz4t5hdjse780zja8vvr7jg",
"name": "ChatApp Pro",
"type": "standard",
"tax_category": "standard",
"description": "Everything in basic, plus access to a suite of powerful tools and features designed to take your team's productivity to the next level.",
"image_url": "https://paddle-sandbox.s3.amazonaws.com/user/10889/2nmP8MQSret0aWeDemRw_icon1.png",
"custom_data": {
"features": {
"crm": true,
"data_retention": false,
"reports": true
},
"suggested_addons": [
"pro_01h1vjes1y163xfj1rh1tkfb65",
"pro_01gsz97mq9pa4fkyy0wqenepkz"
],
"upgrade_description": "Move from Basic to Pro to take advantage of advanced reporting and a CRM that's right where you're chatting."
},
"status": "active",
"import_meta": null,
"created_at": "2023-08-16T14:38:08.3Z",
"updated_at": "2023-08-16T14:38:08.3Z"
},
"tax_rate": "0.08875",
"unit_totals": {
"subtotal": "3000",
"discount": "0",
"tax": "266",
"total": "3266"
},
"proration": {
"rate": "1",
"billing_period": {
"starts_at": "2024-02-08T11:02:03.946454Z",
"ends_at": "2024-03-08T11:02:03.946454Z"
}
}
},
{
"item_id": null,
"price_id": "pri_01gsz95g2zrkagg294kpstx54r",
"quantity": 1,
"totals": {
"subtotal": "28500",
"tax": "2529",
"discount": "0",
"total": "31029"
},
"product": {
"id": "pro_01gsz92krfzy3hcx5h5rtgnfwz",
"name": "VIP support",
"type": "standard",
"tax_category": "standard",
"description": "Get exclusive access to our expert team of product specialists, available to help you make the most of your ChatApp subscription.",
"image_url": "https://paddle-sandbox.s3.amazonaws.com/user/10889/SW3OevDQ92dUHSkN5a2x_icon3.png",
"custom_data": null,
"status": "active",
"import_meta": null,
"created_at": "2023-08-16T14:38:08.3Z",
"updated_at": "2023-08-16T14:38:08.3Z"
},
"tax_rate": "0.08875",
"unit_totals": {
"subtotal": "28500",
"discount": "0",
"tax": "2529",
"total": "31029"
},
"proration": null
}
]
},
"scheduled_change": null,
"items": [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,13 +128,7 @@
"tax": "887",
"total": "10887"
},
"proration": {
"rate": "1",
"billing_period": {
"starts_at": "2024-02-08T11:17:08.807055Z",
"ends_at": "2024-03-08T11:17:08.807055Z"
}
}
"proration": null
}
]
},
Expand Down Expand Up @@ -249,13 +243,7 @@
"tax": "887",
"total": "10887"
},
"proration": {
"rate": "1",
"billing_period": {
"starts_at": "2024-03-08T11:17:08.807055Z",
"ends_at": "2024-04-08T11:17:08.807055Z"
}
}
"proration": null
}
]
},
Expand Down Expand Up @@ -324,7 +312,8 @@
"discount": "0",
"tax": "1766",
"total": "21666"
}
},
"proration": null
}
]
},
Expand Down Expand Up @@ -378,6 +367,19 @@
"import_meta": null,
"created_at": "2023-02-23T13:55:22.538367Z",
"updated_at": "2023-11-09T14:07:16.051528Z"
},
"product": {
"id": "pri_01gsz8x8sawmvhz1pv30nge1ke",
"name": "VIP support",
"type": "standard",
"tax_category": "standard",
"description": "Get exclusive access to our expert team of product specialists, available to help you make the most of your ChatApp subscription.",
"image_url": "https://paddle-sandbox.s3.amazonaws.com/user/10889/SW3OevDQ92dUHSkN5a2x_icon3.png",
"custom_data": null,
"status": "active",
"import_meta": null,
"created_at": "2023-08-16T14:38:08.3Z",
"updated_at": "2023-08-16T14:38:08.3Z"
}
},
{
Expand Down Expand Up @@ -415,6 +417,19 @@
"import_meta": null,
"created_at": "2023-06-01T13:31:12.625056Z",
"updated_at": "2023-08-30T10:34:33.862679Z"
},
"product": {
"id": "pri_01h1vjfevh5etwq3rb416a23h2",
"name": "VIP support",
"type": "standard",
"tax_category": "standard",
"description": "Get exclusive access to our expert team of product specialists, available to help you make the most of your ChatApp subscription.",
"image_url": "https://paddle-sandbox.s3.amazonaws.com/user/10889/SW3OevDQ92dUHSkN5a2x_icon3.png",
"custom_data": null,
"status": "active",
"import_meta": null,
"created_at": "2023-08-16T14:38:08.3Z",
"updated_at": "2023-08-16T14:38:08.3Z"
}
}
],
Expand Down
Loading

0 comments on commit dbe0e70

Please sign in to comment.