diff --git a/CHANGELOG.md b/CHANGELOG.md index 6548d11..431524f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ Check our main [developer changelog](https://developer.paddle.com/?utm_source=dx ### Added - Added `product` to `subscription.items[]`, see [related changelog](https://developer.paddle.com/changelog/2024/subscription-items-product?utm_source=dx&utm_medium=paddle-php-sdk) +- Added `import_meta` to `transaction` ## [1.1.2] - 2024-08-23 diff --git a/src/Entities/Transaction.php b/src/Entities/Transaction.php index 53f7c98..a80c47b 100644 --- a/src/Entities/Transaction.php +++ b/src/Entities/Transaction.php @@ -17,6 +17,7 @@ use Paddle\SDK\Entities\Shared\CollectionMode; use Paddle\SDK\Entities\Shared\CurrencyCode; use Paddle\SDK\Entities\Shared\CustomData; +use Paddle\SDK\Entities\Shared\ImportMeta; use Paddle\SDK\Entities\Shared\TransactionOrigin; use Paddle\SDK\Entities\Shared\TransactionPaymentAttempt; use Paddle\SDK\Entities\Shared\TransactionStatus; @@ -64,6 +65,7 @@ private function __construct( public Customer|null $customer, public Discount|null $discount, public array $availablePaymentMethods, + public ImportMeta|null $importMeta, ) { } @@ -100,6 +102,7 @@ public static function from(array $data): self customer: isset($data['customer']) ? Customer::from($data['customer']) : null, discount: isset($data['discount']) ? Discount::from($data['discount']) : null, availablePaymentMethods: array_map(fn (string $item): AvailablePaymentMethods => AvailablePaymentMethods::from($item), $data['available_payment_methods'] ?? []), + importMeta: isset($data['import_meta']) ? ImportMeta::from($data['import_meta']) : null, ); } } diff --git a/tests/Functional/Resources/Subscriptions/SubscriptionsClientTest.php b/tests/Functional/Resources/Subscriptions/SubscriptionsClientTest.php index a9cc387..a916be3 100644 --- a/tests/Functional/Resources/Subscriptions/SubscriptionsClientTest.php +++ b/tests/Functional/Resources/Subscriptions/SubscriptionsClientTest.php @@ -397,11 +397,12 @@ public static function cancelOperationsProvider(): \Generator * @dataProvider getPaymentMethodChangeTransactionRequestProvider */ public function get_payment_method_change_transaction_hits_expected_uri( + string $id, ResponseInterface $response, string $expectedUri, ): void { $this->mockClient->addResponse($response); - $this->client->subscriptions->getPaymentMethodChangeTransaction('sub_01h7zcgmdc6tmwtjehp3sh7azf'); + $this->client->subscriptions->getPaymentMethodChangeTransaction($id); $request = $this->mockClient->getLastRequest(); self::assertInstanceOf(RequestInterface::class, $request); @@ -412,11 +413,28 @@ public function get_payment_method_change_transaction_hits_expected_uri( public static function getPaymentMethodChangeTransactionRequestProvider(): \Generator { yield 'Basic' => [ + 'sub_01h7zcgmdc6tmwtjehp3sh7azf', new Response(200, body: self::readRawJsonFixture('response/get_payment_method_change_transaction_entity')), sprintf('%s/subscriptions/sub_01h7zcgmdc6tmwtjehp3sh7azf/update-payment-method-transaction', Environment::SANDBOX->baseUrl()), ]; } + /** + * @test + */ + public function get_payment_method_change_transaction_has_import_meta(): void + { + $this->mockClient->addResponse( + new Response(200, body: self::readRawJsonFixture('response/get_payment_method_change_transaction_entity_with_import_meta')), + ); + + $transaction = $this->client->subscriptions->getPaymentMethodChangeTransaction('sub_01h7zcgmdc6tmwtjehp3sh7azf'); + + self::assertNotNull($transaction->importMeta); + self::assertSame('billing_platform', $transaction->importMeta->importedFrom); + self::assertSame('9b95b0b8-e10f-441a-862e-1936a6d818ab', $transaction->importMeta->externalId); + } + /** * @test * diff --git a/tests/Functional/Resources/Subscriptions/_fixtures/response/get_payment_method_change_transaction_entity_with_import_meta.json b/tests/Functional/Resources/Subscriptions/_fixtures/response/get_payment_method_change_transaction_entity_with_import_meta.json new file mode 100644 index 0000000..7d06ad3 --- /dev/null +++ b/tests/Functional/Resources/Subscriptions/_fixtures/response/get_payment_method_change_transaction_entity_with_import_meta.json @@ -0,0 +1,180 @@ +{ + "data": { + "id": "txn_01h8by3n3w1zn9fsq9c93afsq3", + "status": "ready", + "customer_id": "ctm_01h8441jn5pcwrfhwh78jqt8hk", + "address_id": "add_01h848pep46enq8y372x7maj0p", + "business_id": null, + "custom_data": null, + "origin": "subscription_payment_method_change", + "collection_mode": "automatic", + "subscription_id": "sub_01h8bxswamxysj44zt5n48njwh", + "invoice_id": null, + "invoice_number": null, + "discount_id": null, + "billing_details": null, + "billing_period": { + "starts_at": "2023-08-21T11:31:08.689295Z", + "ends_at": "2023-08-21T11:31:08.689295Z" + }, + "currency_code": "USD", + "created_at": "2023-08-21T11:36:30.96803057Z", + "updated_at": "2023-08-21T11:36:30.96803057Z", + "billed_at": null, + "items": [ + { + "price": { + "id": "pri_01gsz8x8sawmvhz1pv30nge1ke", + "description": "Monthly (per seat)", + "name": "Monthly (per seat)", + "product_id": "pro_01gsz4t5hdjse780zja8vvr7jg", + "billing_cycle": { + "interval": "month", + "frequency": 1 + }, + "trial_period": null, + "tax_mode": "account_setting", + "unit_price": { + "amount": "3000", + "currency_code": "USD" + }, + "unit_price_overrides": [ + { + "country_codes": ["AU"], + "unit_price": { + "amount": "5000", + "currency_code": "AUD" + } + } + ], + "quantity": { + "minimum": 10, + "maximum": 999 + }, + "status": "active", + "created_at": "2023-08-16T14:38:08.3Z", + "updated_at": "2023-08-16T14:38:08.3Z" + }, + "quantity": 30, + "proration": { + "rate": "0", + "billing_period": { + "starts_at": "2023-08-21T11:31:08.689295Z", + "ends_at": "2023-09-21T11:31:08.689295Z" + } + } + } + ], + "details": { + "tax_rates_used": [ + { + "tax_rate": "0.08875", + "totals": { + "subtotal": "0", + "discount": "0", + "tax": "0", + "total": "0" + } + } + ], + "totals": { + "subtotal": "0", + "tax": "0", + "discount": "0", + "total": "0", + "fee": null, + "credit": "0", + "credit_to_balance": "0", + "balance": "0", + "grand_total": "0", + "earnings": null, + "currency_code": "USD" + }, + "adjusted_totals": { + "subtotal": "0", + "tax": "0", + "total": "0", + "grand_total": "0", + "fee": "0", + "earnings": "0", + "currency_code": "USD" + }, + "payout_totals": null, + "adjusted_payout_totals": null, + "line_items": [ + { + "id": "txnitm_01h8by3nfc02fjtbda5gta732d", + "price_id": "pri_01gsz8x8sawmvhz1pv30nge1ke", + "quantity": 30, + "totals": { + "subtotal": "0", + "tax": "0", + "discount": "0", + "total": "0" + }, + "product": { + "id": "pro_01gsz4t5hdjse780zja8vvr7jg", + "name": "ChatApp Pro", + "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.", + "tax_category": "standard", + "image_url": "https://paddle-sandbox.s3.amazonaws.com/user/10889/2nmP8MQSret0aWeDemRw_icon1.png", + "status": "active", + "created_at": "2023-08-16T14:38:08.3Z", + "updated_at": "2023-08-16T14:38:08.3Z" + }, + "tax_rate": "0.08875", + "unit_totals": { + "subtotal": "0", + "discount": "0", + "tax": "0", + "total": "0" + }, + "proration": { + "rate": "0", + "billing_period": { + "starts_at": "2023-08-21T11:31:08.689295Z", + "ends_at": "2023-09-21T11:31:08.689295Z" + } + } + } + ] + }, + "payments": [], + "checkout": { + "url": "https://magnificent-entremet-7ae0c6.netlify.app/default/overlay?_ptxn=txn_01h8by3n3w1zn9fsq9c93afsq3" + }, + "customer": { + "id": "ctm_01h8441jn5pcwrfhwh78jqt8hk", + "name": "Sam Miller", + "email": "sam@example.com", + "locale": "en", + "marketing_consent": false, + "custom_data": null, + "status": "active", + "created_at": "2023-11-15T11:15:44.673Z", + "updated_at": "2023-11-15T11:15:44.673Z" + }, + "address": { + "id": "add_01h848pep46enq8y372x7maj0p", + "customer_id": "ctm_01h8441jn5pcwrfhwh78jqt8hk", + "description": "Head Office", + "first_line": "4050 Jefferson Plaza, 41st Floor", + "second_line": "", + "city": "New York", + "postal_code": "10021", + "region": "NY", + "country_code": "US", + "status": "active", + "custom_data": null, + "created_at": "2023-11-15T11:15:44.673Z", + "updated_at": "2023-11-15T11:15:44.673Z" + }, + "import_meta": { + "external_id": "9b95b0b8-e10f-441a-862e-1936a6d818ab", + "imported_from": "billing_platform" + } + }, + "meta": { + "request_id": "e4747324-738b-4707-ac7a-7eaabb7c7a26" + } +} diff --git a/tests/Functional/Resources/Transactions/TransactionsClientTest.php b/tests/Functional/Resources/Transactions/TransactionsClientTest.php index 9e97bc8..2f04d16 100644 --- a/tests/Functional/Resources/Transactions/TransactionsClientTest.php +++ b/tests/Functional/Resources/Transactions/TransactionsClientTest.php @@ -380,6 +380,27 @@ public static function listOperationsProvider(): \Generator ]; } + /** + * @test + */ + public function list_has_import_meta(): void + { + $this->mockClient->addResponse( + new Response(200, body: self::readRawJsonFixture('response/list_default')), + ); + + $transactionCollection = $this->client->transactions->list(new ListTransactions()); + $transactions = array_values(iterator_to_array($transactionCollection)); + + $transactionWithoutImportMeta = $transactions[0]; + self::assertNull($transactionWithoutImportMeta->importMeta); + + $transactionWithImportMeta = $transactions[1]; + self::assertNotNull($transactionWithImportMeta->importMeta); + self::assertSame('billing_platform', $transactionWithImportMeta->importMeta->importedFrom); + self::assertSame('9b95b0b8-e10f-441a-862e-1936a6d818ab', $transactionWithImportMeta->importMeta->externalId); + } + /** * @test * @@ -416,6 +437,34 @@ public static function getRequestProvider(): \Generator ]; } + /** + * @test + */ + public function get_has_import_meta(): void + { + $this->mockClient->addResponse( + new Response(200, body: self::readRawJsonFixture('response/full_entity_with_import_meta')), + ); + $transaction = $this->client->transactions->get('txn_01hen7bxc1p8ep4yk7n5jbzk9r'); + + self::assertNotNull($transaction->importMeta); + self::assertSame('billing_platform', $transaction->importMeta->importedFrom); + self::assertSame('9b95b0b8-e10f-441a-862e-1936a6d818ab', $transaction->importMeta->externalId); + } + + /** + * @test + */ + public function get_has_no_import_meta(): void + { + $this->mockClient->addResponse( + new Response(200, body: self::readRawJsonFixture('response/full_entity')), + ); + $transaction = $this->client->transactions->get('txn_01hen7bxc1p8ep4yk7n5jbzk9r'); + + self::assertNull($transaction->importMeta); + } + /** * @test * diff --git a/tests/Functional/Resources/Transactions/_fixtures/response/full_entity_with_import_meta.json b/tests/Functional/Resources/Transactions/_fixtures/response/full_entity_with_import_meta.json new file mode 100644 index 0000000..3f5908c --- /dev/null +++ b/tests/Functional/Resources/Transactions/_fixtures/response/full_entity_with_import_meta.json @@ -0,0 +1,174 @@ +{ + "data": { + "id": "txn_01hen7bxc1p8ep4yk7n5jbzk9r", + "status": "billed", + "customer_id": "ctm_01he849dseyj0zgrc589eeb1c7", + "address_id": "add_01hen28ebw1ew99y295jhd4n3n", + "business_id": "biz_01hen2ng2290g84twtefdn5s00", + "custom_data": null, + "origin": "api", + "collection_mode": "manual", + "subscription_id": "sub_01hen7byqyfeh7d0cw0qg8tphh", + "invoice_id": "inv_01hen7bys9g6d2xkj9n208e2yw", + "invoice_number": "325-10261", + "billing_details": { + "enable_checkout": true, + "payment_terms": { + "interval": "month", + "frequency": 1 + }, + "purchase_order_number": "10009", + "additional_information": null + }, + "billing_period": { + "starts_at": "2023-11-07T15:45:40.606Z", + "ends_at": "2023-12-07T15:45:40.606Z" + }, + "currency_code": "GBP", + "discount_id": "dsc_01hen7bjzh12m0v2peer15d9qt", + "created_at": "2023-11-07T15:45:39.297512Z", + "updated_at": "2023-11-07T15:45:45.086499Z", + "billed_at": "2023-11-07T15:45:39.201442Z", + "items": [ + { + "price": { + "id": "pri_01gsz8x8sawmvhz1pv30nge1ke", + "description": "Monthly (per seat)", + "name": null, + "product_id": "pro_01gsz4t5hdjse780zja8vvr7jg", + "billing_cycle": { + "interval": "month", + "frequency": 1 + }, + "trial_period": null, + "tax_mode": "account_setting", + "unit_price": { + "amount": "3000", + "currency_code": "USD" + }, + "unit_price_overrides": [ + { + "country_codes": [ + "AU" + ], + "unit_price": { + "amount": "5000", + "currency_code": "AUD" + } + } + ], + "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." + }, + "quantity": { + "minimum": 1, + "maximum": 999 + }, + "status": "active", + "created_at": "2023-08-16T14:38:08.3Z", + "updated_at": "2023-08-16T14:38:08.3Z" + }, + "quantity": 1 + } + ], + "details": { + "tax_rates_used": [ + { + "tax_rate": "0.2", + "totals": { + "subtotal": "2439", + "discount": "10", + "tax": "486", + "total": "2915" + } + } + ], + "totals": { + "subtotal": "2439", + "tax": "486", + "discount": "10", + "total": "2915", + "grand_total": "2915", + "fee": null, + "credit": "0", + "credit_to_balance": "0", + "balance": "2915", + "earnings": null, + "currency_code": "GBP" + }, + "adjusted_totals": { + "subtotal": "2429", + "tax": "486", + "total": "2915", + "grand_total": "2915", + "fee": "0", + "earnings": "0", + "currency_code": "GBP" + }, + "payout_totals": null, + "adjusted_payout_totals": null, + "line_items": [ + { + "id": "txnitm_01hen7bxecbsbdd65s8qhyv7jw", + "price_id": "pri_01gsz8x8sawmvhz1pv30nge1ke", + "quantity": 1, + "totals": { + "subtotal": "2439", + "tax": "486", + "discount": "10", + "total": "2915" + }, + "product": { + "id": "pro_01gsz4t5hdjse780zja8vvr7jg", + "name": "ChatApp Pro", + "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.", + "tax_category": "standard", + "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", + "created_at": "2023-08-16T14:38:08.3Z", + "updated_at": "2023-08-16T14:38:08.3Z" + }, + "tax_rate": "0.2", + "unit_totals": { + "subtotal": "2439", + "tax": "486", + "discount": "10", + "total": "2915" + } + } + ] + }, + "payments": [], + "checkout": { + "url": "https://magnificent-entremet-7ae0c6.netlify.app/default/overlay?_ptxn=txn_01hen7bxc1p8ep4yk7n5jbzk9r" + }, + "import_meta": { + "external_id": "9b95b0b8-e10f-441a-862e-1936a6d818ab", + "imported_from": "billing_platform" + } + }, + "meta": { + "request_id": "0daa7c59-f2eb-41b6-bf2e-bb3b070873a5" + } +} diff --git a/tests/Functional/Resources/Transactions/_fixtures/response/list_default.json b/tests/Functional/Resources/Transactions/_fixtures/response/list_default.json index 4f7502f..3c761a4 100644 --- a/tests/Functional/Resources/Transactions/_fixtures/response/list_default.json +++ b/tests/Functional/Resources/Transactions/_fixtures/response/list_default.json @@ -497,6 +497,10 @@ "payments": [], "checkout": { "url": "https://magnificent-entremet-7ae0c6.netlify.app/default/overlay?_ptxn=txn_01h8bh3jn3a1kfwk4kdw6rf3gp" + }, + "import_meta": { + "external_id": "9b95b0b8-e10f-441a-862e-1936a6d818ab", + "imported_from": "billing_platform" } }, {