Skip to content

Commit 871479e

Browse files
fix: Add proration to transation line items (#105)
1 parent 0005443 commit 871479e

File tree

9 files changed

+360
-46
lines changed

9 files changed

+360
-46
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ Check our main [developer changelog](https://developer.paddle.com/?utm_source=dx
1616
### Fixed
1717

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

2022
## [1.5.0] - 2024-11-18
2123

src/Entities/Shared/TransactionLineItemPreview.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Paddle\SDK\Entities\Product;
1515
use Paddle\SDK\Entities\Transaction\TransactionPreviewProduct;
16+
use Paddle\SDK\Entities\Transaction\TransactionProration;
1617

1718
class TransactionLineItemPreview
1819
{
@@ -23,6 +24,7 @@ private function __construct(
2324
public UnitTotals $unitTotals,
2425
public Totals $totals,
2526
public Product|TransactionPreviewProduct $product,
27+
public TransactionProration|null $proration,
2628
) {
2729
}
2830

@@ -37,6 +39,7 @@ public static function from(array $data): self
3739
isset($data['product']['id'])
3840
? Product::from($data['product'])
3941
: TransactionPreviewProduct::from($data['product']),
42+
isset($data['proration']) ? TransactionProration::from($data['proration']) : null,
4043
);
4144
}
4245
}

tests/Functional/Resources/Subscriptions/SubscriptionsClientTest.php

Lines changed: 100 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -683,7 +683,7 @@ public static function previewOneTimeChargeOperationsProvider(): \Generator
683683
new SubscriptionItems('pri_01gsz98e27ak2tyhexptwc58yk', 1),
684684
],
685685
),
686-
new Response(200, body: self::readRawJsonFixture('response/preview_update_full_entity')),
686+
new Response(200, body: self::readRawJsonFixture('response/preview_charge_full_entity')),
687687
self::readRawJsonFixture('request/preview_one_time_charge_minimal'),
688688
];
689689

@@ -696,8 +696,106 @@ public static function previewOneTimeChargeOperationsProvider(): \Generator
696696
new SubscriptionItems('pri_01h7zd9mzfq79850w4ryc39v38', 845),
697697
],
698698
),
699-
new Response(200, body: self::readRawJsonFixture('response/preview_update_full_entity')),
699+
new Response(200, body: self::readRawJsonFixture('response/preview_charge_full_entity')),
700700
self::readRawJsonFixture('request/preview_one_time_charge_full'),
701701
];
702702
}
703+
704+
/**
705+
* @test
706+
*/
707+
public function get_with_includes_returns_nullable_proration(): void
708+
{
709+
$this->mockClient->addResponse(new Response(200, body: self::readRawJsonFixture('response/full_entity_with_includes')));
710+
$subscription = $this->client->subscriptions->get('sub_01h8bx8fmywym11t6swgzba704');
711+
712+
$recurringTransactionProration = $subscription->recurringTransactionDetails->lineItems[0]->proration;
713+
self::assertNotNull($recurringTransactionProration);
714+
self::assertEquals('1', $recurringTransactionProration->rate);
715+
self::assertEquals(
716+
'2024-02-08T11:02:03+00:00',
717+
$recurringTransactionProration->billingPeriod->startsAt->format(DATE_RFC3339),
718+
);
719+
self::assertEquals(
720+
'2024-03-08T11:02:03+00:00',
721+
$recurringTransactionProration->billingPeriod->endsAt->format(DATE_RFC3339),
722+
);
723+
724+
$nullRecurringTransactionProration = $subscription->recurringTransactionDetails->lineItems[1]->proration;
725+
self::assertNull($nullRecurringTransactionProration);
726+
727+
$nextTransactionProration = $subscription->nextTransaction->details->lineItems[0]->proration;
728+
self::assertNotNull($nextTransactionProration);
729+
self::assertEquals('1', $nextTransactionProration->rate);
730+
self::assertEquals(
731+
'2023-12-03T16:38:53+00:00',
732+
$nextTransactionProration->billingPeriod->startsAt->format(DATE_RFC3339),
733+
);
734+
self::assertEquals(
735+
'2024-01-03T16:38:53+00:00',
736+
$nextTransactionProration->billingPeriod->endsAt->format(DATE_RFC3339),
737+
);
738+
739+
$nullNextTransactionProration = $subscription->nextTransaction->details->lineItems[1]->proration;
740+
self::assertNull($nullNextTransactionProration);
741+
}
742+
743+
/**
744+
* @test
745+
*/
746+
public function preview_returns_nullable_proration(): void
747+
{
748+
$this->mockClient->addResponse(new Response(200, body: self::readRawJsonFixture('response/preview_update_full_entity')));
749+
$subscriptionPreview = $this->client->subscriptions->previewUpdate(
750+
'sub_01h8bx8fmywym11t6swgzba704',
751+
new PreviewUpdateSubscription(
752+
prorationBillingMode: SubscriptionProrationBillingMode::ProratedNextBillingPeriod(),
753+
),
754+
);
755+
756+
$recurringTransactionProration = $subscriptionPreview->recurringTransactionDetails->lineItems[0]->proration;
757+
self::assertNotNull($recurringTransactionProration);
758+
self::assertEquals('1', $recurringTransactionProration->rate);
759+
self::assertEquals(
760+
'2024-02-08T11:02:03+00:00',
761+
$recurringTransactionProration->billingPeriod->startsAt->format(DATE_RFC3339),
762+
);
763+
self::assertEquals(
764+
'2024-03-08T11:02:03+00:00',
765+
$recurringTransactionProration->billingPeriod->endsAt->format(DATE_RFC3339),
766+
);
767+
768+
$nullRecurringTransactionProration = $subscriptionPreview->recurringTransactionDetails->lineItems[1]->proration;
769+
self::assertNull($nullRecurringTransactionProration);
770+
771+
$nextTransactionProration = $subscriptionPreview->nextTransaction->details->lineItems[0]->proration;
772+
self::assertNotNull($nextTransactionProration);
773+
self::assertEquals('1', $nextTransactionProration->rate);
774+
self::assertEquals(
775+
'2024-03-08T11:02:03+00:00',
776+
$nextTransactionProration->billingPeriod->startsAt->format(DATE_RFC3339),
777+
);
778+
self::assertEquals(
779+
'2024-04-08T11:02:03+00:00',
780+
$nextTransactionProration->billingPeriod->endsAt->format(DATE_RFC3339),
781+
);
782+
783+
$nullNextTransactionProration = $subscriptionPreview->nextTransaction->details->lineItems[1]->proration;
784+
self::assertNull($nullNextTransactionProration);
785+
786+
$immediateTransactionProration = $subscriptionPreview->immediateTransaction->details->lineItems[0]->proration;
787+
self::assertNotNull($immediateTransactionProration);
788+
self::assertEquals('0.99993', $immediateTransactionProration->rate);
789+
self::assertEquals(
790+
'2024-02-08T11:05:53+00:00',
791+
$immediateTransactionProration->billingPeriod->startsAt->format(DATE_RFC3339),
792+
);
793+
self::assertEquals(
794+
'2024-03-08T11:02:03+00:00',
795+
$immediateTransactionProration->billingPeriod->endsAt->format(DATE_RFC3339),
796+
);
797+
798+
$nullImmediateTransactionProration = $subscriptionPreview->immediateTransaction->details->lineItems[1]->proration;
799+
self::assertNull($nullImmediateTransactionProration);
800+
}
703801
}

tests/Functional/Resources/Subscriptions/_fixtures/response/full_entity_with_includes.json

Lines changed: 111 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -117,13 +117,7 @@
117117
"tax": "500",
118118
"total": "3000"
119119
},
120-
"proration": {
121-
"rate": "1",
122-
"billing_period": {
123-
"starts_at": "2023-12-03T16:38:53.111897Z",
124-
"ends_at": "2024-01-03T16:38:53.111897Z"
125-
}
126-
}
120+
"proration": null
127121
}
128122
]
129123
},
@@ -160,6 +154,116 @@
160154
}
161155
]
162156
},
157+
"recurring_transaction_details": {
158+
"tax_rates_used": [
159+
{
160+
"tax_rate": "0.08875",
161+
"totals": {
162+
"subtotal": "178500",
163+
"discount": "0",
164+
"tax": "15841",
165+
"total": "194341"
166+
}
167+
}
168+
],
169+
"totals": {
170+
"subtotal": "178500",
171+
"tax": "15841",
172+
"discount": "0",
173+
"total": "194341",
174+
"fee": null,
175+
"credit": "0",
176+
"credit_to_balance": "0",
177+
"balance": "194341",
178+
"grand_total": "194341",
179+
"earnings": null,
180+
"currency_code": "USD",
181+
"exchange_rate": "1"
182+
},
183+
"line_items": [
184+
{
185+
"item_id": null,
186+
"price_id": "pri_01gsz8x8sawmvhz1pv30nge1ke",
187+
"quantity": 50,
188+
"totals": {
189+
"subtotal": "150000",
190+
"tax": "13312",
191+
"discount": "0",
192+
"total": "163312"
193+
},
194+
"product": {
195+
"id": "pro_01gsz4t5hdjse780zja8vvr7jg",
196+
"name": "ChatApp Pro",
197+
"type": "standard",
198+
"tax_category": "standard",
199+
"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.",
200+
"image_url": "https://paddle-sandbox.s3.amazonaws.com/user/10889/2nmP8MQSret0aWeDemRw_icon1.png",
201+
"custom_data": {
202+
"features": {
203+
"crm": true,
204+
"data_retention": false,
205+
"reports": true
206+
},
207+
"suggested_addons": [
208+
"pro_01h1vjes1y163xfj1rh1tkfb65",
209+
"pro_01gsz97mq9pa4fkyy0wqenepkz"
210+
],
211+
"upgrade_description": "Move from Basic to Pro to take advantage of advanced reporting and a CRM that's right where you're chatting."
212+
},
213+
"status": "active",
214+
"import_meta": null,
215+
"created_at": "2023-08-16T14:38:08.3Z",
216+
"updated_at": "2023-08-16T14:38:08.3Z"
217+
},
218+
"tax_rate": "0.08875",
219+
"unit_totals": {
220+
"subtotal": "3000",
221+
"discount": "0",
222+
"tax": "266",
223+
"total": "3266"
224+
},
225+
"proration": {
226+
"rate": "1",
227+
"billing_period": {
228+
"starts_at": "2024-02-08T11:02:03.946454Z",
229+
"ends_at": "2024-03-08T11:02:03.946454Z"
230+
}
231+
}
232+
},
233+
{
234+
"item_id": null,
235+
"price_id": "pri_01gsz95g2zrkagg294kpstx54r",
236+
"quantity": 1,
237+
"totals": {
238+
"subtotal": "28500",
239+
"tax": "2529",
240+
"discount": "0",
241+
"total": "31029"
242+
},
243+
"product": {
244+
"id": "pro_01gsz92krfzy3hcx5h5rtgnfwz",
245+
"name": "VIP support",
246+
"type": "standard",
247+
"tax_category": "standard",
248+
"description": "Get exclusive access to our expert team of product specialists, available to help you make the most of your ChatApp subscription.",
249+
"image_url": "https://paddle-sandbox.s3.amazonaws.com/user/10889/SW3OevDQ92dUHSkN5a2x_icon3.png",
250+
"custom_data": null,
251+
"status": "active",
252+
"import_meta": null,
253+
"created_at": "2023-08-16T14:38:08.3Z",
254+
"updated_at": "2023-08-16T14:38:08.3Z"
255+
},
256+
"tax_rate": "0.08875",
257+
"unit_totals": {
258+
"subtotal": "28500",
259+
"discount": "0",
260+
"tax": "2529",
261+
"total": "31029"
262+
},
263+
"proration": null
264+
}
265+
]
266+
},
163267
"scheduled_change": null,
164268
"items": [
165269
{

tests/Functional/Resources/Subscriptions/_fixtures/response/preview_charge_full_entity.json

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -128,13 +128,7 @@
128128
"tax": "887",
129129
"total": "10887"
130130
},
131-
"proration": {
132-
"rate": "1",
133-
"billing_period": {
134-
"starts_at": "2024-02-08T11:17:08.807055Z",
135-
"ends_at": "2024-03-08T11:17:08.807055Z"
136-
}
137-
}
131+
"proration": null
138132
}
139133
]
140134
},
@@ -249,13 +243,7 @@
249243
"tax": "887",
250244
"total": "10887"
251245
},
252-
"proration": {
253-
"rate": "1",
254-
"billing_period": {
255-
"starts_at": "2024-03-08T11:17:08.807055Z",
256-
"ends_at": "2024-04-08T11:17:08.807055Z"
257-
}
258-
}
246+
"proration": null
259247
}
260248
]
261249
},
@@ -324,7 +312,8 @@
324312
"discount": "0",
325313
"tax": "1766",
326314
"total": "21666"
327-
}
315+
},
316+
"proration": null
328317
}
329318
]
330319
},
@@ -378,6 +367,19 @@
378367
"import_meta": null,
379368
"created_at": "2023-02-23T13:55:22.538367Z",
380369
"updated_at": "2023-11-09T14:07:16.051528Z"
370+
},
371+
"product": {
372+
"id": "pri_01gsz8x8sawmvhz1pv30nge1ke",
373+
"name": "VIP support",
374+
"type": "standard",
375+
"tax_category": "standard",
376+
"description": "Get exclusive access to our expert team of product specialists, available to help you make the most of your ChatApp subscription.",
377+
"image_url": "https://paddle-sandbox.s3.amazonaws.com/user/10889/SW3OevDQ92dUHSkN5a2x_icon3.png",
378+
"custom_data": null,
379+
"status": "active",
380+
"import_meta": null,
381+
"created_at": "2023-08-16T14:38:08.3Z",
382+
"updated_at": "2023-08-16T14:38:08.3Z"
381383
}
382384
},
383385
{
@@ -415,6 +417,19 @@
415417
"import_meta": null,
416418
"created_at": "2023-06-01T13:31:12.625056Z",
417419
"updated_at": "2023-08-30T10:34:33.862679Z"
420+
},
421+
"product": {
422+
"id": "pri_01h1vjfevh5etwq3rb416a23h2",
423+
"name": "VIP support",
424+
"type": "standard",
425+
"tax_category": "standard",
426+
"description": "Get exclusive access to our expert team of product specialists, available to help you make the most of your ChatApp subscription.",
427+
"image_url": "https://paddle-sandbox.s3.amazonaws.com/user/10889/SW3OevDQ92dUHSkN5a2x_icon3.png",
428+
"custom_data": null,
429+
"status": "active",
430+
"import_meta": null,
431+
"created_at": "2023-08-16T14:38:08.3Z",
432+
"updated_at": "2023-08-16T14:38:08.3Z"
418433
}
419434
}
420435
],

0 commit comments

Comments
 (0)