diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c4f4058..bfb0e1d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -10,7 +10,7 @@ jobs: strategy: fail-fast: true matrix: - php: [7.4, 8.0, 8.1, 8.2] + php: [8.0, 8.1, 8.2, 8.3, 8.4] name: PHP - ${{ matrix.php }} steps: - name: Checkout code diff --git a/composer.json b/composer.json index 4a2171d..3c5ffa8 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,7 @@ } ], "require": { - "php": "^7.4|^8.0", + "php": "^8.0", "ext-curl": "*", "ext-json": "*", "ext-openssl": "*", diff --git a/docs/Chargebacks.md b/docs/Chargebacks.md index e41c5da..0d9ee4b 100644 --- a/docs/Chargebacks.md +++ b/docs/Chargebacks.md @@ -11,12 +11,14 @@ Below you'll find all properties for the Vatly Chargeback resource. | Name | Type | Description | | --- | --- | --- | | `id` | `string` | Unique identifier for the chargeback (`chb_...`). | -| `status` | `string` | The status: `open`, `won`, or `lost`. | -| `orderId` | `string` | The disputed order ID. | -| `amount` | `integer` | Chargeback amount in cents. | -| `currency` | `string` | Three-letter ISO currency code. | -| `reason` | `string|null` | Reason provided by the bank. | +| `resource` | `string` | Always `chargeback`. | +| `merchantId` | `string` | The merchant ID. | | `testmode` | `bool` | Whether this is a test chargeback. | +| `amount` | `object` | Chargeback amount (`value` and `currency`). | +| `settlementAmount` | `object` | Amount deducted from settlement (may differ from `amount`). | +| `reason` | `string` | Reason code for the dispute (e.g. `fraud`, `product_not_received`, `duplicate`). | +| `originalOrderId` | `string` | The ID of the original order that was disputed. | +| `orderId` | `string\|null` | The credit note order ID (after processing). | | `createdAt` | `string` | Creation timestamp (ISO 8601). | --- @@ -35,9 +37,9 @@ Retrieve a chargeback by its ID. ```php $chargeback = $vatly->chargebacks->get('chb_abc123'); -echo $chargeback->status; -echo $chargeback->amount / 100 . ' ' . $chargeback->currency; echo $chargeback->reason; +echo $chargeback->amount->value . ' ' . $chargeback->amount->currency; +echo $chargeback->originalOrderId; ``` @@ -66,35 +68,6 @@ Retrieve a paginated list of all chargebacks. $chargebacks = $vatly->chargebacks->list(); foreach ($chargebacks as $chargeback) { - echo $chargeback->id . ': ' . $chargeback->status; + echo $chargeback->id . ': ' . $chargeback->reason; } ``` - - - ---- - -## Chargeback statuses - -| Status | Description | -|--------|-------------| -| `open` | Dispute is ongoing | -| `won` | You won the dispute | -| `lost` | You lost the dispute | - ---- - -## Helper methods - - - -The Chargeback object provides convenient helper methods. - - - - -```php -$chargeback->isOpen(); // true if status is 'open' -$chargeback->isWon(); // true if status is 'won' -$chargeback->isLost(); // true if status is 'lost' -``` diff --git a/docs/OneOffProducts.md b/docs/OneOffProducts.md index 645d3bd..34046db 100644 --- a/docs/OneOffProducts.md +++ b/docs/OneOffProducts.md @@ -16,7 +16,7 @@ Below you'll find all properties for the Vatly One-Off Product resource. | `amount` | `integer` | Price in cents. | | `currency` | `string` | Three-letter ISO currency code. | | `testmode` | `bool` | Whether this is a test product. | -| `active` | `bool` | Whether the product is active. | +| `status` | `string` | The status: `approved`, `draft`, or `archived`. | | `createdAt` | `string` | Creation timestamp (ISO 8601). | --- diff --git a/docs/Orders.md b/docs/Orders.md index b884165..83af9da 100644 --- a/docs/Orders.md +++ b/docs/Orders.md @@ -11,7 +11,7 @@ Below you'll find all properties for the Vatly Order resource. | Name | Type | Description | | --- | --- | --- | | `id` | `string` | Unique identifier for the order (`ord_...`). | -| `status` | `string` | The status: `pending`, `paid`, `failed`, or `refunded`. | +| `status` | `string` | The status: `pending`, `paid`, or `failed`. | | `customerId` | `string` | The customer ID. | | `checkoutId` | `string|null` | The checkout ID (for initial orders). | | `subscriptionId` | `string|null` | The subscription ID (for recurring orders). | @@ -94,7 +94,6 @@ $orders = $vatly->orders->list([ | `pending` | Order is awaiting payment | | `paid` | Payment successful | | `failed` | Payment failed | -| `refunded` | Order has been refunded | --- @@ -111,5 +110,4 @@ The Order object provides convenient helper methods. $order->isPaid(); // true if status is 'paid' $order->isPending(); // true if status is 'pending' $order->isFailed(); // true if status is 'failed' -$order->isRefunded(); // true if status is 'refunded' ``` diff --git a/docs/Refunds.md b/docs/Refunds.md index 99708d9..62fa8b7 100644 --- a/docs/Refunds.md +++ b/docs/Refunds.md @@ -11,7 +11,7 @@ Below you'll find all properties for the Vatly Refund resource. | Name | Type | Description | | --- | --- | --- | | `id` | `string` | Unique identifier for the refund (`ref_...`). | -| `status` | `string` | The status: `pending`, `succeeded`, or `failed`. | +| `status` | `string` | The status: `pending`, `completed`, `failed`, or `canceled`. | | `orderId` | `string` | The order ID being refunded. | | `amount` | `integer` | Refund amount in cents. | | `currency` | `string` | Three-letter ISO currency code. | @@ -124,8 +124,9 @@ foreach ($refunds as $refund) { | Status | Description | |--------|-------------| | `pending` | Refund is being processed | -| `succeeded` | Refund completed successfully | +| `completed` | Refund completed successfully | | `failed` | Refund failed | +| `canceled` | Refund was canceled | --- @@ -139,7 +140,8 @@ The Refund object provides convenient helper methods. ```php -$refund->isPending(); // true if status is 'pending' -$refund->isSucceeded(); // true if status is 'succeeded' -$refund->isFailed(); // true if status is 'failed' +$refund->isPending(); // true if status is 'pending' +$refund->isCompleted(); // true if status is 'completed' +$refund->isFailed(); // true if status is 'failed' +$refund->isCanceled(); // true if status is 'canceled' ``` diff --git a/docs/SubscriptionPlans.md b/docs/SubscriptionPlans.md index 04eb424..3f84442 100644 --- a/docs/SubscriptionPlans.md +++ b/docs/SubscriptionPlans.md @@ -19,7 +19,7 @@ Below you'll find all properties for the Vatly Subscription Plan resource. | `intervalCount` | `integer` | Number of intervals between billings. | | `trialDays` | `integer|null` | Default trial period in days. | | `testmode` | `bool` | Whether this is a test plan. | -| `active` | `bool` | Whether the plan is active. | +| `status` | `string` | The status: `approved`, `draft`, or `archived`. | | `createdAt` | `string` | Creation timestamp (ISO 8601). | --- diff --git a/docs/Subscriptions.md b/docs/Subscriptions.md index ddec1e8..77f8160 100644 --- a/docs/Subscriptions.md +++ b/docs/Subscriptions.md @@ -11,7 +11,7 @@ Below you'll find all properties for the Vatly Subscription resource. | Name | Type | Description | | --- | --- | --- | | `id` | `string` | Unique identifier for the subscription (`sub_...`). | -| `status` | `string` | The status: `active`, `canceled`, `past_due`, `trialing`, `paused`, or `ended`. | +| `status` | `string` | The status: `active`, `created`, `trial`, `on_grace_period`, or `paused`. | | `customerId` | `string` | The customer ID. | | `planId` | `string` | The subscription plan ID. | | `testmode` | `bool` | Whether this is a test subscription. | @@ -102,8 +102,8 @@ Cancel a subscription. The subscription will remain active until the end of the ```php $subscription = $vatly->subscriptions->cancel('sub_abc123'); -// Subscription is now 'canceled' but active until period end -echo $subscription->status; // 'canceled' +// Subscription is now on grace period until current period ends +echo $subscription->status; // 'on_grace_period' echo $subscription->currentPeriodEnd; // When it ends ``` @@ -116,11 +116,10 @@ echo $subscription->currentPeriodEnd; // When it ends | Status | Description | |--------|-------------| | `active` | Subscription is active and billing | -| `canceled` | Subscription is canceled, active until period end | -| `past_due` | Payment failed, in grace period | -| `trialing` | In trial period | +| `created` | Subscription created, not yet active | +| `trial` | In trial period | +| `on_grace_period` | Canceled but still active until current period ends | | `paused` | Subscription is paused | -| `ended` | Subscription has ended | --- @@ -134,11 +133,9 @@ The Subscription object provides convenient helper methods. ```php -$subscription->isActive(); // true if status is 'active' -$subscription->isCanceled(); // true if status is 'canceled' -$subscription->isTrialing(); // true if status is 'trialing' -$subscription->isPastDue(); // true if status is 'past_due' -$subscription->hasEnded(); // true if status is 'ended' -$subscription->onTrial(); // true if currently in trial -$subscription->onGracePeriod(); // true if canceled but still active +$subscription->isActive(); // true if status is 'active' +$subscription->isCreated(); // true if status is 'created' +$subscription->onTrial(); // true if status is 'trial' +$subscription->onGracePeriod(); // true if status is 'on_grace_period' +$subscription->isPaused(); // true if status is 'paused' ``` diff --git a/docs/Webhooks.md b/docs/Webhooks.md index 5927184..3f87dd5 100644 --- a/docs/Webhooks.md +++ b/docs/Webhooks.md @@ -15,7 +15,7 @@ Vatly sends webhooks to notify your application when events happen, like success | `subscription.ended` | Subscription ended | | `order.paid` | Order payment received | | `refund.created` | Refund initiated | -| `refund.succeeded` | Refund completed | +| `refund.completed` | Refund completed | | `chargeback.created` | Chargeback received | | `chargeback.won` | Chargeback dispute won | | `chargeback.lost` | Chargeback dispute lost | diff --git a/src/API/Resources/Checkout.php b/src/API/Resources/Checkout.php index 1dafba9..d672c28 100644 --- a/src/API/Resources/Checkout.php +++ b/src/API/Resources/Checkout.php @@ -54,6 +54,8 @@ class Checkout extends BaseResource public ?string $createdAt = null; + public ?string $expiresAt = null; + /** * Is this created? */ @@ -79,11 +81,11 @@ public function isCanceled(): bool } /** - * Is this completed? + * Is this failed? */ - public function isCompleted(): bool + public function isFailed(): bool { - return $this->status === CheckoutStatus::STATUS_COMPLETED; + return $this->status === CheckoutStatus::STATUS_FAILED; } /** @@ -93,12 +95,4 @@ public function isExpired(): bool { return $this->status === CheckoutStatus::STATUS_EXPIRED; } - - /** - * Is this completed? - */ - public function isPending(): bool - { - return $this->status === CheckoutStatus::STATUS_PENDING; - } } diff --git a/src/API/Resources/OneOffProduct.php b/src/API/Resources/OneOffProduct.php index 1151fbe..8a0bfbe 100644 --- a/src/API/Resources/OneOffProduct.php +++ b/src/API/Resources/OneOffProduct.php @@ -4,6 +4,7 @@ use Vatly\API\Resources\Links\OneOffProductLinks; use Vatly\API\Types\Money; +use Vatly\API\Types\ProductStatus; class OneOffProduct extends BaseResource { @@ -26,5 +27,27 @@ class OneOffProduct extends BaseResource */ public Money $basePrice; + public bool $testmode; + + /** @see ProductStatus */ + public string $status; + + public string $createdAt; + public OneOffProductLinks $links; + + public function isApproved(): bool + { + return $this->status === ProductStatus::APPROVED; + } + + public function isDraft(): bool + { + return $this->status === ProductStatus::DRAFT; + } + + public function isArchived(): bool + { + return $this->status === ProductStatus::ARCHIVED; + } } diff --git a/src/API/Resources/Order.php b/src/API/Resources/Order.php index 410a4ca..f732404 100644 --- a/src/API/Resources/Order.php +++ b/src/API/Resources/Order.php @@ -48,15 +48,13 @@ class Order extends BaseResource /** * @example creditcard */ - public string $paymentMethod; + public ?string $paymentMethod = null; public ?string $invoiceNumber = null; /** @see OrderStatus */ public string $status; - public bool $cancelled = false; - public OrderLinks $links; public Address $customerDetails; @@ -68,6 +66,10 @@ class Order extends BaseResource */ public array $lines; + /** + * @var array|object|null + */ + public $metadata = null; /** * Get the line value objects @@ -85,14 +87,6 @@ public function lines(): OrderLineCollection ); } - /** - * Is this order created? - */ - public function isCreated(): bool - { - return $this->status === OrderStatus::STATUS_CREATED; - } - /** * Is this order paid for? */ @@ -102,35 +96,19 @@ public function isPaid(): bool } /** - * Is this order canceled? - */ - public function isCanceled(): bool - { - return $this->status === OrderStatus::STATUS_CANCELED; - } - - /** - * Is this order completed? + * Is this order pending? */ - public function isCompleted(): bool - { - return $this->status === OrderStatus::STATUS_COMPLETED; - } - - /** - * Is this order expired? - */ - public function isExpired(): bool + public function isPending(): bool { - return $this->status === OrderStatus::STATUS_EXPIRED; + return $this->status === OrderStatus::STATUS_PENDING; } /** - * Is this order completed? + * Is this order failed? */ - public function isPending(): bool + public function isFailed(): bool { - return $this->status === OrderStatus::STATUS_PENDING; + return $this->status === OrderStatus::STATUS_FAILED; } /** diff --git a/src/API/Resources/Refund.php b/src/API/Resources/Refund.php index 8015d0d..9c13373 100644 --- a/src/API/Resources/Refund.php +++ b/src/API/Resources/Refund.php @@ -25,6 +25,11 @@ class Refund extends BaseResource */ public string $merchantId; + /** + * @example customer_78b146a7de7d417e9d68d7e6ef193d18 + */ + public string $customerId; + /** * @example 2023-08-11T10:48:51+02:00 */ @@ -49,13 +54,13 @@ class Refund extends BaseResource public RefundLinks $links; /** - * The associated order ID created from this refund + * The associated order ID created from this refund (credit note) * @example order_66fc8a40718b46bea50f1a25f456d243 */ public ?string $orderId = null; /** - * The associated original refund ID from which this refund was created + * The associated original order ID from which this refund was created * @example order_66fc8a40718b46bea50f1a25f456d242 */ public string $originalOrderId; @@ -71,6 +76,38 @@ public function lines(): RefundLineCollection ); } + /** + * Is this refund pending? + */ + public function isPending(): bool + { + return $this->status === RefundStatus::PENDING; + } + + /** + * Is this refund completed? + */ + public function isCompleted(): bool + { + return $this->status === RefundStatus::COMPLETED; + } + + /** + * Is this refund failed? + */ + public function isFailed(): bool + { + return $this->status === RefundStatus::FAILED; + } + + /** + * Is this refund canceled? + */ + public function isCanceled(): bool + { + return $this->status === RefundStatus::CANCELED; + } + /** * @throws ApiException * @return null diff --git a/src/API/Resources/Subscription.php b/src/API/Resources/Subscription.php index dc52081..8335430 100644 --- a/src/API/Resources/Subscription.php +++ b/src/API/Resources/Subscription.php @@ -66,9 +66,7 @@ class Subscription extends BaseResource public ?string $nextRenewalAt; - public ?string $trialEndAt; - - public ?int $trialDays; + public ?string $trialUntil; public SubscriptionLinks $links; diff --git a/src/API/Resources/SubscriptionPlan.php b/src/API/Resources/SubscriptionPlan.php index bbfe7e1..31dfbab 100644 --- a/src/API/Resources/SubscriptionPlan.php +++ b/src/API/Resources/SubscriptionPlan.php @@ -4,6 +4,7 @@ use Vatly\API\Resources\Links\SubscriptionPlanLinks; use Vatly\API\Types\Money; +use Vatly\API\Types\ProductStatus; class SubscriptionPlan extends BaseResource { @@ -30,5 +31,27 @@ class SubscriptionPlan extends BaseResource */ public Money $basePrice; + public bool $testmode; + + /** @see ProductStatus */ + public string $status; + + public string $createdAt; + public SubscriptionPlanLinks $links; + + public function isApproved(): bool + { + return $this->status === ProductStatus::APPROVED; + } + + public function isDraft(): bool + { + return $this->status === ProductStatus::DRAFT; + } + + public function isArchived(): bool + { + return $this->status === ProductStatus::ARCHIVED; + } } diff --git a/src/API/Types/CheckoutStatus.php b/src/API/Types/CheckoutStatus.php index 099cec7..825de84 100644 --- a/src/API/Types/CheckoutStatus.php +++ b/src/API/Types/CheckoutStatus.php @@ -14,24 +14,18 @@ class CheckoutStatus */ public const STATUS_PAID = "paid"; - /** * The checkout has been canceled. */ public const STATUS_CANCELED = "canceled"; /** - * The checkout is completed. + * The checkout payment failed. */ - public const STATUS_COMPLETED = "completed"; + public const STATUS_FAILED = "failed"; /** * The checkout is expired. */ public const STATUS_EXPIRED = "expired"; - - /** - * The checkout is pending. - */ - public const STATUS_PENDING = "pending"; } diff --git a/src/API/Types/OrderStatus.php b/src/API/Types/OrderStatus.php index ead821a..3e159ef 100644 --- a/src/API/Types/OrderStatus.php +++ b/src/API/Types/OrderStatus.php @@ -5,33 +5,17 @@ class OrderStatus { /** - * The order has just been created. + * The order is awaiting payment. */ - public const STATUS_CREATED = "created"; + public const STATUS_PENDING = "pending"; /** * The order has been paid. */ public const STATUS_PAID = "paid"; - - /** - * The order has been canceled. - */ - public const STATUS_CANCELED = "canceled"; - - /** - * The order is completed. - */ - public const STATUS_COMPLETED = "completed"; - - /** - * The order is expired. - */ - public const STATUS_EXPIRED = "expired"; - /** - * The order is pending. + * The order payment failed. */ - public const STATUS_PENDING = "pending"; + public const STATUS_FAILED = "failed"; } diff --git a/src/API/Types/ProductStatus.php b/src/API/Types/ProductStatus.php new file mode 100644 index 0000000..05574e6 --- /dev/null +++ b/src/API/Types/ProductStatus.php @@ -0,0 +1,21 @@ + true, 'redirectUrlSuccess' => 'https://www.sandorian.com/success', 'redirectUrlCanceled' => 'https://www.sandorian.com/canceled', - 'status' => CheckoutStatus::STATUS_PENDING, + 'status' => CheckoutStatus::STATUS_CREATED, 'links' => [ 'checkoutUrl' => [ 'href' => self::WEBSITE_ENDPOINT_URL.'/checkout/checkout_dummy_id', @@ -214,7 +214,7 @@ public function can_get_checkouts_list() $this->assertEquals("merchant_123", $checkout->merchantId); $this->assertEquals("order_123", $checkout->orderId); $this->assertEquals("checkout", $checkout->resource); - $this->assertEquals(CheckoutStatus::STATUS_PENDING, $checkout->status); + $this->assertEquals(CheckoutStatus::STATUS_CREATED, $checkout->status); $this->assertEquals("https://www.sandorian.com/success", $checkout->redirectUrlSuccess); $this->assertEquals("https://www.sandorian.com/canceled", $checkout->redirectUrlCanceled); $this->assertTrue($checkout->testmode); diff --git a/tests/Endpoints/OrderEndpointTest.php b/tests/Endpoints/OrderEndpointTest.php index 935b840..b1d9099 100644 --- a/tests/Endpoints/OrderEndpointTest.php +++ b/tests/Endpoints/OrderEndpointTest.php @@ -131,7 +131,6 @@ public function can_get_order(): void $this->assertFalse($order->testmode); $this->assertEquals('ideal', $order->paymentMethod); $this->assertEquals(OrderStatus::STATUS_PAID, $order->status); - $this->assertFalse($order->cancelled); $this->assertEquals('96.00', $order->total->value); $this->assertEquals('80.00', $order->subtotal->value); $this->assertEquals('VAT', $order->taxes->taxes[0]->name); diff --git a/tests/Endpoints/RefundEndpointTest.php b/tests/Endpoints/RefundEndpointTest.php index 54706b7..7ac012d 100644 --- a/tests/Endpoints/RefundEndpointTest.php +++ b/tests/Endpoints/RefundEndpointTest.php @@ -22,7 +22,7 @@ public function can_get_refund(): void 'resource' => 'refund', 'merchantId' => 'merchant_123', 'testmode' => false, - 'status' => RefundStatus::REFUNDED, + 'status' => RefundStatus::COMPLETED, 'metadata' => [ 'refund_id' => '123456', ], @@ -115,7 +115,7 @@ public function can_get_refund(): void $this->assertEquals('order_dummy_id', $refund->orderId); $this->assertEquals('original_order_dummy_id', $refund->originalOrderId); $this->assertFalse($refund->testmode); - $this->assertEquals(RefundStatus::REFUNDED, $refund->status); + $this->assertEquals(RefundStatus::COMPLETED, $refund->status); $this->assertEquals('96.00', $refund->total->value); $this->assertEquals('80.00', $refund->subtotal->value); $this->assertEquals('VAT', $refund->taxes->taxes[0]->name);