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

INTEG-254: checkout create #68

Merged
merged 3 commits into from
Sep 6, 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
30 changes: 30 additions & 0 deletions Tests/Feature/Checkout/Mock/CheckoutStoreMockClient.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

declare(strict_types=1);

namespace PlugAndPay\Sdk\Tests\Feature\Checkout\Mock;

use PlugAndPay\Sdk\Entity\Response;

class CheckoutStoreMockClient extends CheckoutShowMockClient
{
protected array $requestBody;

public function path(): string
{
return $this->path;
}

public function post(string $path, array $body): Response
{
$this->path = $path;
$this->requestBody = $body;

return new Response(Response::HTTP_CREATED, $this->responseBody);
}

public function requestBody(): array
{
return $this->requestBody;
}
}
27 changes: 27 additions & 0 deletions Tests/Feature/Checkout/Mock/CheckoutUpdateMockClient.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace PlugAndPay\Sdk\Tests\Feature\Checkout\Mock;

use PlugAndPay\Sdk\Entity\Response;
use PlugAndPay\Sdk\Support\Arr;

class CheckoutUpdateMockClient extends CheckoutShowMockClient
{
protected array $requestBody;

public function patch(string $path, array $data): Response
{
$this->responseBody = Arr::mergeDistinct($this->responseBody, ['data' => $data]);
$this->path = $path;
$this->requestBody = $data;

return new Response(Response::HTTP_OK, $this->responseBody);
}

public function path(): string
{
return $this->path;
}
}
142 changes: 142 additions & 0 deletions Tests/Feature/Checkout/StoreCheckoutTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
<?php

/** @noinspection EfferentObjectCouplingInspection */
/* @noinspection PhpUnhandledExceptionInspection */

declare(strict_types=1);

namespace PlugAndPay\Sdk\Tests\Feature\Checkout;

use BadFunctionCallException;
use PHPUnit\Framework\TestCase;
use PlugAndPay\Sdk\Director\ToBody\CheckoutToBody;
use PlugAndPay\Sdk\Entity\Checkout;
use PlugAndPay\Sdk\Entity\Response;
use PlugAndPay\Sdk\Exception\ValidationException;
use PlugAndPay\Sdk\Service\CheckoutService;
use PlugAndPay\Sdk\Tests\Feature\Checkout\Mock\CheckoutStoreMockClient;
use PlugAndPay\Sdk\Tests\Feature\ClientMock;

class StoreCheckoutTest extends TestCase
{
/** @test */
public function it_should_throw_exception_when_calling_non_existing_method(): void
{
$exception = null;

try {
$checkout = new Checkout();
$checkout->isset('bad_function');
} catch (BadFunctionCallException $exception) {
}

static::assertInstanceOf(BadFunctionCallException::class, $exception);
}

/** @test */
public function it_should_convert_basic_checkout_to_body(): void
{
$body = CheckoutToBody::build($this->generateCheckout());

static::assertEquals([
'is_active' => true,
'is_expired' => false,
'name' => 'the-name',
'preview_url' => 'the-url',
'primary_color' => 'the-primary-color',
'return_url' => 'the-return-url',
'secondary_color' => 'the-secondary-color',
'slug' => 'the-slug',
'url' => 'the-url',
'product' => [
'id' => 1,
],
], $body);
}

/** @test */
public function it_should_store_basic_checkout(): void
{
$client = new CheckoutStoreMockClient();
$service = new CheckoutService($client);

$checkout = $this->generateCheckout();
$checkout = $service->create($checkout);

static::assertEquals('/v2/checkouts', $client->path());
static::assertEquals(1, $checkout->id());
}

/** @test */
public function create_order_with_validation_error(): void
{
$client = new ClientMock(
Response::HTTP_UNPROCESSABLE_ENTITY,
[
'message' => 'The given data was invalid.',
'errors' => [
'name' => [
'Naam is verplicht.',
],
],
],
);
$service = new CheckoutService($client);
$exception = null;

try {
$service->find(1);
} catch (ValidationException $exception) {
}

static::assertEquals('Naam is verplicht.', $exception->getMessage());
static::assertEquals('Naam is verplicht.', $exception->errors()[0]->message());
static::assertEquals('name', $exception->errors()[0]->field());
}

/** @test */
public function create_order_with_multiple_validation_errors(): void
{
$client = new ClientMock(
Response::HTTP_UNPROCESSABLE_ENTITY,
[
'message' => 'The given data was invalid.',
'errors' => [
'name' => [
'First error.',
'Second error.',
],
'productId' => [
'Last error.',
],
],
],
);
$service = new CheckoutService($client);
$exception = null;

try {
$service->find(1);
} catch (ValidationException $exception) {
}

static::assertEquals('First error. Second error. Last error.', $exception->getMessage());
static::assertEquals('First error.', $exception->errors()[0]->message());
static::assertEquals('name', $exception->errors()[0]->field());
}

private function generateCheckout(): Checkout
{
return (new Checkout())
->setIsActive(true)
->setIsExpired(false)
->setName('the-name')
->setPreviewUrl('the-url')
->setPrimaryColor('the-primary-color')
->setProductId(1)
->setReturnUrl('the-return-url')
->setSecondaryColor('the-secondary-color')
->setSlug('the-slug')
->setUrl('the-url');
}
}
61 changes: 61 additions & 0 deletions Tests/Feature/Checkout/UpdateCheckoutTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php

/** @noinspection PhpUnhandledExceptionInspection */

declare(strict_types=1);

namespace PlugAndPay\Sdk\Tests\Feature\Checkout;

use PHPUnit\Framework\TestCase;
use PlugAndPay\Sdk\Entity\Checkout;
use PlugAndPay\Sdk\Entity\Response;
use PlugAndPay\Sdk\Exception\UnauthenticatedException;
use PlugAndPay\Sdk\Service\CheckoutService;
use PlugAndPay\Sdk\Tests\Feature\Checkout\Mock\CheckoutShowMockClient;
use PlugAndPay\Sdk\Tests\Feature\Checkout\Mock\CheckoutUpdateMockClient;

class UpdateCheckoutTest extends TestCase
{
/** @test */
public function it_should_throw_unauthorized_exception(): void
{
$client = new CheckoutShowMockClient(status: Response::HTTP_UNAUTHORIZED);
$service = new CheckoutService($client);
$exception = null;

try {
$service->update(999, function (Checkout $checkout) {
});
} catch (UnauthenticatedException $exception) {
}

static::assertEquals('Unable to connect with Plug&Pay. Request is unauthenticated.', $exception->getMessage());
}

/** @test */
public function it_should_update_basic_checkout(): void
{
$client = new CheckoutUpdateMockClient();
$service = new CheckoutService($client);

$checkout = $service->update(1, function (Checkout $checkout) {
$checkout->setIsActive(false);
});

static::assertFalse($checkout->isActive());
static::assertEquals('/v2/checkouts/1', $client->path());
}

/** @test */
public function it_should_update_product_id(): void
{
$client = (new CheckoutUpdateMockClient())->productPricing();
$service = new CheckoutService($client);

$checkout = $service->update(1, function (Checkout $checkout) {
$checkout->setProductId(123);
});

static::assertEquals(123, $checkout->product()->id());
}
}
11 changes: 2 additions & 9 deletions src/Entity/Checkout.php
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,9 @@ public function product(): Product
return $this->product;
}

public function setProduct(Product $product): self
public function setProductId(int $id): self
{
$this->product = $product;
$this->product = (new ProductInternal())->setId($id);

return $this;
}
Expand All @@ -134,13 +134,6 @@ public function productPricing(): ProductPricing
return $this->productPricing;
}

public function setProductPricing(ProductPricing $productPricing): self
{
$this->productPricing = $productPricing;

return $this;
}

public function returnUrl(): ?string
{
return $this->returnUrl;
Expand Down
20 changes: 20 additions & 0 deletions src/Entity/CheckoutInternal.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,26 @@ public function setId(int $id): self
return $this;
}

/**
* @internal
*/
public function setProduct(Product $product): self
{
$this->product = $product;

return $this;
}

/**
* @internal
*/
public function setProductPricing(ProductPricing $productPricing): self
{
$this->productPricing = $productPricing;

return $this;
}

/**
* @internal
*/
Expand Down
27 changes: 27 additions & 0 deletions src/Service/CheckoutService.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@

use PlugAndPay\Sdk\Contract\ClientInterface;
use PlugAndPay\Sdk\Director\BodyTo\BodyToCheckout;
use PlugAndPay\Sdk\Director\ToBody\CheckoutToBody;
use PlugAndPay\Sdk\Entity\Checkout;
use PlugAndPay\Sdk\Enum\CheckoutIncludes;
use PlugAndPay\Sdk\Exception\DecodeResponseException;
use PlugAndPay\Sdk\Exception\RelationNotLoadedException;
use PlugAndPay\Sdk\Support\Parameters;

class CheckoutService
Expand Down Expand Up @@ -37,4 +39,29 @@ public function find(int $id): Checkout

return BodyToCheckout::build($response->body()['data']);
}

/** @throws DecodeResponseException */
public function create(Checkout $checkout): Checkout
{
$body = CheckoutToBody::build($checkout);
$query = Parameters::toString(['include' => $this->includes]);
$response = $this->client->post("/v2/checkouts$query", $body);

return BodyToCheckout::build($response->body()['data']);
}

/**
* @throws DecodeResponseException
* @throws RelationNotLoadedException
*/
public function update(int $checkoutId, callable $update): Checkout
{
$order = new Checkout(true);
$update($order);
$body = CheckoutToBody::build($order);
$query = Parameters::toString(['include' => $this->includes]);
$response = $this->client->patch("/v2/checkouts/$checkoutId$query", $body);

return BodyToCheckout::build($response->body()['data']);
}
}
Loading