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

fix: Add proration to transation line items #105

Merged
merged 1 commit into from
Dec 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading