From 00f27eb3d97dfefe6973fbbe6edb3487ee649c5e Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Tue, 9 Apr 2024 13:07:31 +0200 Subject: [PATCH 01/58] Add invests pump --- src/Library/Benzina/Pump/InvestsPump.php | 147 +++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 src/Library/Benzina/Pump/InvestsPump.php diff --git a/src/Library/Benzina/Pump/InvestsPump.php b/src/Library/Benzina/Pump/InvestsPump.php new file mode 100644 index 0000000..5345651 --- /dev/null +++ b/src/Library/Benzina/Pump/InvestsPump.php @@ -0,0 +1,147 @@ +hasAllKeys($data[0], self::INVEST_KEYS); + } + + public function process(mixed $data): void + { + $invested = $this->getInvested($data); + $investors = $this->getInvestors($data); + + foreach ($data as $key => $data) { + // Ignore invests to non-migrated projects + // Shouldn't be much, as the non-migrated are projects that were never funded in any way + if (!\array_key_exists($data['project'], $invested)) { + continue; + } + + if ($data['amount'] === 0) { + continue; + } + + /** @var User */ + $user = $investors[$data['user']]; + + /** @var Project */ + $project = $invested[$data['project']]; + + $money = new Money($data['amount'] * 100, $data['currency']); + + $transaction = new Transaction; + $transaction->setMoney($money); + $transaction->setOrigin($user->getAccounting()); + $transaction->setTarget($project->getAccounting()); + $transaction->setGateway($data['method']); + $transaction->setGatewayReference($this->getGatewayReference($data)); + + $this->entityManager->persist($transaction); + } + + $this->entityManager->flush(); + $this->entityManager->clear(); + } + + private function getInvested(array $data): array + { + $projects = $this->projectRepository->findBy(['migratedReference' => \array_map(function ($data) { + return $data['project']; + }, $data)]); + + $invested = []; + foreach ($projects as $project) { + $invested[$project->getMigratedReference()] = $project; + } + + return $invested; + } + + private function getInvestors(array $data): array + { + $users = $this->userRepository->findBy(['migratedReference' => \array_map(function ($data) { + return $data['user']; + }, $data)]); + + $investors = []; + foreach ($users as $user) { + $investors[$user->getMigratedReference()] = $user; + } + + return $investors; + } + + private function getGatewayReference(array $data): string + { + if (!empty($data['transaction'])) { + return $data['transaction']; + } + + if (!empty($data['payment'])) { + return $data['payment']; + } + + if (!empty($data['preapproval'])) { + return $data['preapproval']; + } + + return ''; + } +} From cf7bc18d38043b3c6a3dac3543f6c719cb86402d Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Tue, 9 Apr 2024 13:18:07 +0200 Subject: [PATCH 02/58] Prefer payment over transaction as gateway reference --- src/Library/Benzina/Pump/InvestsPump.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Library/Benzina/Pump/InvestsPump.php b/src/Library/Benzina/Pump/InvestsPump.php index 5345651..3d01584 100644 --- a/src/Library/Benzina/Pump/InvestsPump.php +++ b/src/Library/Benzina/Pump/InvestsPump.php @@ -130,14 +130,14 @@ class InvestsPump implements PumpInterface private function getGatewayReference(array $data): string { - if (!empty($data['transaction'])) { - return $data['transaction']; - } - if (!empty($data['payment'])) { return $data['payment']; } + if (!empty($data['transaction'])) { + return $data['transaction']; + } + if (!empty($data['preapproval'])) { return $data['preapproval']; } From ee4630cce0531b92185b855365b96fed3723e77f Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Tue, 9 Apr 2024 13:46:12 +0200 Subject: [PATCH 03/58] Gateway checkouts include store the gateway reference --- src/Dto/GatewayCheckoutUpdateDto.php | 19 ++++++++++ src/Entity/GatewayCheckout.php | 22 +++++++++++ src/State/GatewayCheckoutUpdateProcessor.php | 40 ++++++++++++++++++++ 3 files changed, 81 insertions(+) create mode 100644 src/Dto/GatewayCheckoutUpdateDto.php create mode 100644 src/State/GatewayCheckoutUpdateProcessor.php diff --git a/src/Dto/GatewayCheckoutUpdateDto.php b/src/Dto/GatewayCheckoutUpdateDto.php new file mode 100644 index 0000000..82a2cb6 --- /dev/null +++ b/src/Dto/GatewayCheckoutUpdateDto.php @@ -0,0 +1,19 @@ +gatewayReference = $gatewayReference; + } +} diff --git a/src/Entity/GatewayCheckout.php b/src/Entity/GatewayCheckout.php index 4b7a073..c650750 100644 --- a/src/Entity/GatewayCheckout.php +++ b/src/Entity/GatewayCheckout.php @@ -3,8 +3,10 @@ namespace App\Entity; use ApiPlatform\Metadata as API; +use App\Dto\GatewayCheckoutUpdateDto; use App\Repository\GatewayCheckoutRepository; use App\State\GatewayCheckoutProcessor; +use App\State\GatewayCheckoutUpdateProcessor; use App\Validator\GatewayName; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; @@ -18,6 +20,7 @@ use Symfony\Component\Validator\Constraints as Assert; #[API\GetCollection()] #[API\Post(processor: GatewayCheckoutProcessor::class)] #[API\Get()] +#[API\Patch(input: GatewayCheckoutUpdateDto::class, processor: GatewayCheckoutUpdateProcessor::class)] #[ORM\Entity(repositoryClass: GatewayCheckoutRepository::class)] class GatewayCheckout { @@ -34,6 +37,13 @@ class GatewayCheckout #[ORM\Column(length: 255)] private ?string $gateway = null; + /** + * An external identifier provided by the Gateway for the payment.\ + * Required when a GatewayCheckout is completed. + */ + #[ORM\Column(length: 255)] + private ?string $gatewayReference = null; + /** * The Accounting that will issue the Transactions of the GatewayCharges after a successful checkout. */ @@ -118,4 +128,16 @@ class GatewayCheckout return $this; } + + public function getGatewayReference(): ?string + { + return $this->gatewayReference; + } + + public function setGatewayReference(string $gatewayReference): static + { + $this->gatewayReference = $gatewayReference; + + return $this; + } } diff --git a/src/State/GatewayCheckoutUpdateProcessor.php b/src/State/GatewayCheckoutUpdateProcessor.php new file mode 100644 index 0000000..3ed4605 --- /dev/null +++ b/src/State/GatewayCheckoutUpdateProcessor.php @@ -0,0 +1,40 @@ +checkoutRepository->find($uriVariables['id']); + + if (!$checkout) { + throw new HttpException(Response::HTTP_NOT_FOUND, sprintf("The checkout '%s' does not exist.", $uriVariables['id'])); + } + + $checkout->setGatewayReference($data->gatewayReference); + + $this->entityManager->persist($checkout); + $this->entityManager->flush(); + + return $checkout; + } +} From 622f662bec238ff3933c21264d0059225a15cd10 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Tue, 9 Apr 2024 13:56:15 +0200 Subject: [PATCH 04/58] Move gateway properties to bottom in checkout --- src/Entity/GatewayCheckout.php | 54 +++++++++++++++++----------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/src/Entity/GatewayCheckout.php b/src/Entity/GatewayCheckout.php index c650750..df3cd74 100644 --- a/src/Entity/GatewayCheckout.php +++ b/src/Entity/GatewayCheckout.php @@ -29,21 +29,6 @@ class GatewayCheckout #[ORM\Column] private ?int $id = null; - /** - * The name of the Gateway implementation to checkout with. - */ - #[GatewayName] - #[Assert\NotBlank()] - #[ORM\Column(length: 255)] - private ?string $gateway = null; - - /** - * An external identifier provided by the Gateway for the payment.\ - * Required when a GatewayCheckout is completed. - */ - #[ORM\Column(length: 255)] - private ?string $gatewayReference = null; - /** * The Accounting that will issue the Transactions of the GatewayCharges after a successful checkout. */ @@ -65,6 +50,21 @@ class GatewayCheckout )] private Collection $charges; + /** + * The name of the Gateway implementation to checkout with. + */ + #[GatewayName] + #[Assert\NotBlank()] + #[ORM\Column(length: 255)] + private ?string $gateway = null; + + /** + * An external identifier provided by the Gateway for the payment.\ + * Required when a GatewayCheckout is completed. + */ + #[ORM\Column(length: 255)] + private ?string $gatewayReference = null; + public function __construct() { $this->charges = new ArrayCollection(); @@ -75,18 +75,6 @@ class GatewayCheckout return $this->id; } - public function getGateway(): ?string - { - return $this->gateway; - } - - public function setGateway(string $gateway): static - { - $this->gateway = $gateway; - - return $this; - } - public function getOrigin(): ?Accounting { return $this->origin; @@ -129,6 +117,18 @@ class GatewayCheckout return $this; } + public function getGateway(): ?string + { + return $this->gateway; + } + + public function setGateway(string $gateway): static + { + $this->gateway = $gateway; + + return $this; + } + public function getGatewayReference(): ?string { return $this->gatewayReference; From cb0514e1919b53fa8fee6b00bfbe857f263728dc Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Tue, 9 Apr 2024 13:56:27 +0200 Subject: [PATCH 05/58] Remove gateway references in transactions --- src/Entity/Transaction.php | 38 -------------------------------------- 1 file changed, 38 deletions(-) diff --git a/src/Entity/Transaction.php b/src/Entity/Transaction.php index 810b97a..766e8c2 100644 --- a/src/Entity/Transaction.php +++ b/src/Entity/Transaction.php @@ -47,20 +47,6 @@ class Transaction #[ORM\JoinColumn(nullable: false)] private ?Accounting $target = null; - /** - * The name of the Gateway implementation that secured the money in this Transaction. - */ - #[Assert\NotBlank()] - #[ORM\Column(length: 255)] - private ?string $gateway = null; - - /** - * An external identifier provided by the Gateway for this Transaction. - */ - #[Assert\NotBlank()] - #[ORM\Column(length: 255)] - private ?string $gatewayReference = null; - public function getId(): ?int { return $this->id; @@ -101,28 +87,4 @@ class Transaction return $this; } - - public function getGateway(): ?string - { - return $this->gateway; - } - - public function setGateway(string $gateway): static - { - $this->gateway = $gateway; - - return $this; - } - - public function getGatewayReference(): ?string - { - return $this->gatewayReference; - } - - public function setGatewayReference(string $gatewayReference): static - { - $this->gatewayReference = $gatewayReference; - - return $this; - } } From 7333fca83f44eeb15a40245c4f037a4155406b3e Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Tue, 9 Apr 2024 14:41:35 +0200 Subject: [PATCH 06/58] Add gateway flow before transactions on invests pump --- src/Library/Benzina/Pump/InvestsPump.php | 38 +++++++++++++++++++----- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/src/Library/Benzina/Pump/InvestsPump.php b/src/Library/Benzina/Pump/InvestsPump.php index 3d01584..d2546df 100644 --- a/src/Library/Benzina/Pump/InvestsPump.php +++ b/src/Library/Benzina/Pump/InvestsPump.php @@ -2,6 +2,8 @@ namespace App\Library\Benzina\Pump; +use App\Entity\GatewayCharge; +use App\Entity\GatewayCheckout; use App\Entity\Money; use App\Entity\Project; use App\Entity\Transaction; @@ -68,9 +70,12 @@ class InvestsPump implements PumpInterface $investors = $this->getInvestors($data); foreach ($data as $key => $data) { - // Ignore invests to non-migrated projects - // Shouldn't be much, as the non-migrated are projects that were never funded in any way - if (!\array_key_exists($data['project'], $invested)) { + if ( + !$data['project'] || + // Ignore invests to non-migrated projects + // Shouldn't be much, as the non-migrated are projects that were never funded in any way + !\array_key_exists($data['project'], $invested) + ) { continue; } @@ -84,14 +89,33 @@ class InvestsPump implements PumpInterface /** @var Project */ $project = $invested[$data['project']]; + $origin = $user->getAccounting(); + $target = $project->getAccounting(); + $money = new Money($data['amount'] * 100, $data['currency']); + $reference = $this->getGatewayReference($data); + + if ($data['method'] === 'tpv') { + $charge = new GatewayCharge; + $charge->setMoney($money); + $charge->setTarget($target); + $charge->setExtradata([ + 'migrated' => true, + 'migratedReference' => $data['id'] + ]); + + $checkout = new GatewayCheckout; + $checkout->setOrigin($origin); + $checkout->addCharge($charge); + $checkout->setGateway($data['method']); + $checkout->setGatewayReference($reference); + } + $transaction = new Transaction; $transaction->setMoney($money); - $transaction->setOrigin($user->getAccounting()); - $transaction->setTarget($project->getAccounting()); - $transaction->setGateway($data['method']); - $transaction->setGatewayReference($this->getGatewayReference($data)); + $transaction->setOrigin($origin); + $transaction->setTarget($target); $this->entityManager->persist($transaction); } From 95ff6bdb52e56166f6dab1bec92b42000addcb74 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Tue, 9 Apr 2024 14:54:12 +0200 Subject: [PATCH 07/58] Link transactions to gateway charges --- src/Entity/GatewayCharge.php | 21 ++++++++++++++++++++- src/Entity/Transaction.php | 28 ++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/Entity/GatewayCharge.php b/src/Entity/GatewayCharge.php index 7ae246a..de5ad62 100644 --- a/src/Entity/GatewayCharge.php +++ b/src/Entity/GatewayCharge.php @@ -37,11 +37,18 @@ class GatewayCharge #[ORM\Column(nullable: true)] private ?array $extradata = null; - #[API\ApiProperty(writable: false)] + #[API\ApiProperty(writable: false, readable: false)] #[ORM\ManyToOne(inversedBy: 'charges')] #[ORM\JoinColumn(nullable: false)] private ?GatewayCheckout $checkout = null; + /** + * The generated Transaction inside the platform for this GatewayCharge. + */ + #[API\ApiProperty(writable: false)] + #[ORM\OneToOne(inversedBy: 'gatewayCharge', cascade: ['persist', 'remove'])] + private ?Transaction $transaction = null; + public function getId(): ?int { return $this->id; @@ -94,4 +101,16 @@ class GatewayCharge return $this; } + + public function getTransaction(): ?Transaction + { + return $this->transaction; + } + + public function setTransaction(?Transaction $transaction): static + { + $this->transaction = $transaction; + + return $this; + } } diff --git a/src/Entity/Transaction.php b/src/Entity/Transaction.php index 766e8c2..54ba4ea 100644 --- a/src/Entity/Transaction.php +++ b/src/Entity/Transaction.php @@ -47,6 +47,12 @@ class Transaction #[ORM\JoinColumn(nullable: false)] private ?Accounting $target = null; + /** + * GatewayCharges generate Transactions after a Gateway validates the charges. + */ + #[ORM\OneToOne(mappedBy: 'transaction', cascade: ['persist', 'remove'])] + private ?GatewayCharge $gatewayCharge = null; + public function getId(): ?int { return $this->id; @@ -87,4 +93,26 @@ class Transaction return $this; } + + public function getGatewayCharge(): ?GatewayCharge + { + return $this->gatewayCharge; + } + + public function setGatewayCharge(?GatewayCharge $gatewayCharge): static + { + // unset the owning side of the relation if necessary + if ($gatewayCharge === null && $this->gatewayCharge !== null) { + $this->gatewayCharge->setTransaction(null); + } + + // set the owning side of the relation if necessary + if ($gatewayCharge !== null && $gatewayCharge->getTransaction() !== $this) { + $gatewayCharge->setTransaction($this); + } + + $this->gatewayCharge = $gatewayCharge; + + return $this; + } } From b6ef62c7d6f3fb60808867157a1e160890c07d46 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Wed, 10 Apr 2024 21:12:50 +0200 Subject: [PATCH 08/58] GatewayCharges have a type --- src/Entity/GatewayChargeType.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 src/Entity/GatewayChargeType.php diff --git a/src/Entity/GatewayChargeType.php b/src/Entity/GatewayChargeType.php new file mode 100644 index 0000000..84c7ba1 --- /dev/null +++ b/src/Entity/GatewayChargeType.php @@ -0,0 +1,16 @@ + Date: Wed, 10 Apr 2024 21:13:09 +0200 Subject: [PATCH 09/58] Re-draft GatewayCharge model --- src/Entity/GatewayCharge.php | 85 ++++++++++-------------------------- 1 file changed, 22 insertions(+), 63 deletions(-) diff --git a/src/Entity/GatewayCharge.php b/src/Entity/GatewayCharge.php index de5ad62..793e748 100644 --- a/src/Entity/GatewayCharge.php +++ b/src/Entity/GatewayCharge.php @@ -2,12 +2,16 @@ namespace App\Entity; -use ApiPlatform\Metadata as API; +use ApiPlatform\Metadata\ApiResource; use App\Repository\GatewayChargeRepository; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Validator\Constraints as Assert; +/** + * A GatewayCharge represents a monetary payment that can be done by an issuer at checkout with the Gateway. + */ #[ORM\Entity(repositoryClass: GatewayChargeRepository::class)] +#[ApiResource] class GatewayCharge { #[ORM\Id] @@ -15,45 +19,36 @@ class GatewayCharge #[ORM\Column] private ?int $id = null; - /** - * The money to be charged by the Gateway at checkout. - */ #[Assert\NotBlank()] - #[ORM\Embedded(class: Money::class)] + #[ORM\Column()] + private ?GatewayChargeType $type = null; + + #[Assert\NotBlank()] + #[ORM\Embedded(Money::class)] private ?Money $money = null; - /** - * The Accounting that will receive the Transaction of this GatewayCharge after a successful checkout. - */ #[Assert\NotBlank()] #[ORM\ManyToOne] #[ORM\JoinColumn(nullable: false)] private ?Accounting $target = null; - /** - * Custom data to be passed to the Gateway.\ - * E.g: billing period, product name, custom metadata, etc. - */ - #[ORM\Column(nullable: true)] - private ?array $extradata = null; - - #[API\ApiProperty(writable: false, readable: false)] - #[ORM\ManyToOne(inversedBy: 'charges')] - #[ORM\JoinColumn(nullable: false)] - private ?GatewayCheckout $checkout = null; - - /** - * The generated Transaction inside the platform for this GatewayCharge. - */ - #[API\ApiProperty(writable: false)] - #[ORM\OneToOne(inversedBy: 'gatewayCharge', cascade: ['persist', 'remove'])] - private ?Transaction $transaction = null; - public function getId(): ?int { return $this->id; } + public function getType(): ?GatewayChargeType + { + return $this->type; + } + + public function setType(GatewayChargeType $type): static + { + $this->type = $type; + + return $this; + } + public function getMoney(): ?Money { return $this->money; @@ -77,40 +72,4 @@ class GatewayCharge return $this; } - - public function getExtradata(): ?array - { - return $this->extradata; - } - - public function setExtradata(?array $extradata): static - { - $this->extradata = $extradata; - - return $this; - } - - public function getCheckout(): ?GatewayCheckout - { - return $this->checkout; - } - - public function setCheckout(?GatewayCheckout $checkout): static - { - $this->checkout = $checkout; - - return $this; - } - - public function getTransaction(): ?Transaction - { - return $this->transaction; - } - - public function setTransaction(?Transaction $transaction): static - { - $this->transaction = $transaction; - - return $this; - } } From 36b2786a390f1e0a1df154ceb35dec7a2e81f55d Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Wed, 10 Apr 2024 21:13:28 +0200 Subject: [PATCH 10/58] Remove link between GatewayCharge and Transaction --- src/Entity/Transaction.php | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/src/Entity/Transaction.php b/src/Entity/Transaction.php index 54ba4ea..766e8c2 100644 --- a/src/Entity/Transaction.php +++ b/src/Entity/Transaction.php @@ -47,12 +47,6 @@ class Transaction #[ORM\JoinColumn(nullable: false)] private ?Accounting $target = null; - /** - * GatewayCharges generate Transactions after a Gateway validates the charges. - */ - #[ORM\OneToOne(mappedBy: 'transaction', cascade: ['persist', 'remove'])] - private ?GatewayCharge $gatewayCharge = null; - public function getId(): ?int { return $this->id; @@ -93,26 +87,4 @@ class Transaction return $this; } - - public function getGatewayCharge(): ?GatewayCharge - { - return $this->gatewayCharge; - } - - public function setGatewayCharge(?GatewayCharge $gatewayCharge): static - { - // unset the owning side of the relation if necessary - if ($gatewayCharge === null && $this->gatewayCharge !== null) { - $this->gatewayCharge->setTransaction(null); - } - - // set the owning side of the relation if necessary - if ($gatewayCharge !== null && $gatewayCharge->getTransaction() !== $this) { - $gatewayCharge->setTransaction($this); - } - - $this->gatewayCharge = $gatewayCharge; - - return $this; - } } From 4cc59c7feb1210707826c8add5bdd131992cd899 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Wed, 10 Apr 2024 21:14:10 +0200 Subject: [PATCH 11/58] GatewayCheckout has a status --- src/Dto/GatewayCheckoutUpdateDto.php | 16 +++++++++--- src/Entity/GatewayCheckout.php | 37 +++++++++++++++++----------- src/Entity/GatewayCheckoutStatus.php | 10 ++++++++ 3 files changed, 46 insertions(+), 17 deletions(-) create mode 100644 src/Entity/GatewayCheckoutStatus.php diff --git a/src/Dto/GatewayCheckoutUpdateDto.php b/src/Dto/GatewayCheckoutUpdateDto.php index 82a2cb6..1ce5e7f 100644 --- a/src/Dto/GatewayCheckoutUpdateDto.php +++ b/src/Dto/GatewayCheckoutUpdateDto.php @@ -2,18 +2,28 @@ namespace App\Dto; +use App\Entity\GatewayCheckoutStatus; use Symfony\Component\Validator\Constraints as Assert; final class GatewayCheckoutUpdateDto { + /** + * The status of the checkout with the Gateway. + */ + #[Assert\NotBlank()] + public GatewayCheckoutStatus $status; + /** * An external identifier provided by the Gateway for the payment. */ #[Assert\NotBlank()] - public readonly string $gatewayReference; + public string $gatewayReference; - public function __construct(string $gatewayReference) - { + public function __construct( + GatewayCheckoutStatus $status, + string $gatewayReference + ) { + $this->status = $status; $this->gatewayReference = $gatewayReference; } } diff --git a/src/Entity/GatewayCheckout.php b/src/Entity/GatewayCheckout.php index df3cd74..fa92c6f 100644 --- a/src/Entity/GatewayCheckout.php +++ b/src/Entity/GatewayCheckout.php @@ -38,18 +38,20 @@ class GatewayCheckout private ?Accounting $origin = null; /** - * The charges to be made by the Gateway at checkout.\ - * Each GatewayCharge will generate a separate Transaction after a successful checkout. + * The GatewayCharges to be charged at checkout with the Gateway. */ #[Assert\NotBlank()] #[Assert\Count(min: 1)] - #[ORM\OneToMany( - mappedBy: 'checkout', - targetEntity: GatewayCharge::class, - cascade: ['persist'] - )] + #[ORM\ManyToMany(targetEntity: GatewayCharge::class)] private Collection $charges; + /** + * The status of the checkout with the Gateway. + */ + #[API\ApiProperty(writable: false)] + #[ORM\Column()] + private ?GatewayCheckoutStatus $status = null; + /** * The name of the Gateway implementation to checkout with. */ @@ -68,6 +70,7 @@ class GatewayCheckout public function __construct() { $this->charges = new ArrayCollection(); + $this->status = GatewayCheckoutStatus::Pending; } public function getId(): ?int @@ -99,7 +102,6 @@ class GatewayCheckout { if (!$this->charges->contains($charge)) { $this->charges->add($charge); - $charge->setCheckout($this); } return $this; @@ -107,12 +109,19 @@ class GatewayCheckout public function removeCharge(GatewayCharge $charge): static { - if ($this->charges->removeElement($charge)) { - // set the owning side to null (unless already changed) - if ($charge->getCheckout() === $this) { - $charge->setCheckout(null); - } - } + $this->charges->removeElement($charge); + + return $this; + } + + public function getStatus(): ?GatewayCheckoutStatus + { + return $this->status; + } + + public function setStatus(GatewayCheckoutStatus $status): static + { + $this->status = $status; return $this; } diff --git a/src/Entity/GatewayCheckoutStatus.php b/src/Entity/GatewayCheckoutStatus.php new file mode 100644 index 0000000..4e8bc86 --- /dev/null +++ b/src/Entity/GatewayCheckoutStatus.php @@ -0,0 +1,10 @@ + Date: Wed, 10 Apr 2024 21:18:34 +0200 Subject: [PATCH 12/58] Draft invest pump --- src/Library/Benzina/Pump/InvestsPump.php | 146 +++++++++++++---------- 1 file changed, 83 insertions(+), 63 deletions(-) diff --git a/src/Library/Benzina/Pump/InvestsPump.php b/src/Library/Benzina/Pump/InvestsPump.php index d2546df..fc65c1f 100644 --- a/src/Library/Benzina/Pump/InvestsPump.php +++ b/src/Library/Benzina/Pump/InvestsPump.php @@ -3,11 +3,13 @@ namespace App\Library\Benzina\Pump; use App\Entity\GatewayCharge; +use App\Entity\GatewayChargeType; use App\Entity\GatewayCheckout; +use App\Entity\GatewayCheckoutStatus; use App\Entity\Money; use App\Entity\Project; -use App\Entity\Transaction; -use App\Entity\User; +use App\Library\Economy\Payment\StripeGateway; +use App\Library\Economy\Payment\WalletGateway; use App\Repository\ProjectRepository; use App\Repository\UserRepository; use Doctrine\ORM\EntityManagerInterface; @@ -16,6 +18,8 @@ class InvestsPump implements PumpInterface { use ArrayPumpTrait; + private const MAX_INT = 2147483647; + private const INVEST_KEYS = [ 'id', 'user', @@ -66,93 +70,109 @@ class InvestsPump implements PumpInterface public function process(mixed $data): void { - $invested = $this->getInvested($data); - $investors = $this->getInvestors($data); + $users = $this->getUsers($data); + $projects = $this->getProjects($data); foreach ($data as $key => $data) { - if ( - !$data['project'] || - // Ignore invests to non-migrated projects - // Shouldn't be much, as the non-migrated are projects that were never funded in any way - !\array_key_exists($data['project'], $invested) - ) { + if (!\array_key_exists($data['project'], $projects)) { continue; } - if ($data['amount'] === 0) { + if (!$data['amount'] || $data['amount'] < 1) { continue; } - /** @var User */ - $user = $investors[$data['user']]; + $user = $users[$data['user']]; + $project = $projects[$data['project']]; - /** @var Project */ - $project = $invested[$data['project']]; + $charge = new GatewayCharge; + $charge->setType($this->getChargeType($data)); + $charge->setMoney($this->getChargeMoney($data, $project)); + $charge->setTarget($project->getAccounting()); - $origin = $user->getAccounting(); - $target = $project->getAccounting(); - - $money = new Money($data['amount'] * 100, $data['currency']); - - $reference = $this->getGatewayReference($data); - - if ($data['method'] === 'tpv') { - $charge = new GatewayCharge; - $charge->setMoney($money); - $charge->setTarget($target); - $charge->setExtradata([ - 'migrated' => true, - 'migratedReference' => $data['id'] - ]); - - $checkout = new GatewayCheckout; - $checkout->setOrigin($origin); - $checkout->addCharge($charge); - $checkout->setGateway($data['method']); - $checkout->setGatewayReference($reference); - } - - $transaction = new Transaction; - $transaction->setMoney($money); - $transaction->setOrigin($origin); - $transaction->setTarget($target); - - $this->entityManager->persist($transaction); + $checkout = new GatewayCheckout; + $checkout->setOrigin($user->getAccounting()); + $checkout->addCharge($charge); + $checkout->setStatus($this->getCheckoutStatus($data)); + $checkout->setGateway($this->getCheckoutGateway($data)); + $checkout->setGatewayReference($this->getCheckoutReference($data)); } $this->entityManager->flush(); $this->entityManager->clear(); } - private function getInvested(array $data): array - { - $projects = $this->projectRepository->findBy(['migratedReference' => \array_map(function ($data) { - return $data['project']; - }, $data)]); - - $invested = []; - foreach ($projects as $project) { - $invested[$project->getMigratedReference()] = $project; - } - - return $invested; - } - - private function getInvestors(array $data): array + private function getUsers(array $data): array { $users = $this->userRepository->findBy(['migratedReference' => \array_map(function ($data) { return $data['user']; }, $data)]); - $investors = []; + $usersByMigratedReference = []; foreach ($users as $user) { - $investors[$user->getMigratedReference()] = $user; + $usersByMigratedReference[$user->getMigratedReference()] = $user; } - return $investors; + return $usersByMigratedReference; } - private function getGatewayReference(array $data): string + private function getProjects(array $data): array + { + $projects = $this->projectRepository->findBy(['migratedReference' => \array_map(function ($data) { + return $data['project']; + }, $data)]); + + $projectsByMigratedReference = []; + foreach ($projects as $project) { + $projectsByMigratedReference[$project->getMigratedReference()] = $project; + } + + return $projectsByMigratedReference; + } + + private function getChargeType(array $data): GatewayChargeType + { + if (\in_array($data['method'], ['stripe_subscription'])) { + return GatewayChargeType::Recurring; + } + + return GatewayChargeType::Single; + } + + private function getChargeMoney(array $data, Project $project): Money + { + $amount = $data['amount'] * 100; + + if ($amount >= self::MAX_INT) { + $amount = $data['amount']; + } + + return new Money($amount, $project->getAccounting()->getCurrency()); + } + + private function getCheckoutStatus(array $data): GatewayCheckoutStatus + { + if ($data['status'] === -1) { + return GatewayCheckoutStatus::Pending; + } + + if ($data['status'] === 1) { + return GatewayCheckoutStatus::Charged; + } + } + + private function getCheckoutGateway(array $data): string + { + if ($data['method'] === 'stripe_subscription') { + return StripeGateway::getName(); + } + + if ($data['method'] === 'pool') { + return WalletGateway::getName(); + } + } + + private function getCheckoutReference(array $data): string { if (!empty($data['payment'])) { return $data['payment']; From 2a2e4eb015d299ec729f12bd2448b99d15b9677e Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Thu, 11 Apr 2024 12:35:52 +0200 Subject: [PATCH 13/58] Remove unused transaction related classes --- src/Entity/TransactionGateway.php | 68 ------------------------- src/State/TransactionStateProcessor.php | 30 ----------- 2 files changed, 98 deletions(-) delete mode 100644 src/Entity/TransactionGateway.php delete mode 100644 src/State/TransactionStateProcessor.php diff --git a/src/Entity/TransactionGateway.php b/src/Entity/TransactionGateway.php deleted file mode 100644 index 92962f3..0000000 --- a/src/Entity/TransactionGateway.php +++ /dev/null @@ -1,68 +0,0 @@ -name; - } - - public function setName(string $name): static - { - $this->name = $name; - - return $this; - } - - public function getSuccessUrl(): ?string - { - return $this->successUrl; - } - - public function setSuccessUrl(string $successUrl): static - { - $this->successUrl = $successUrl; - - return $this; - } - - public function getFailureUrl(): ?string - { - return $this->failureUrl; - } - - public function setFailureUrl(string $failureUrl): static - { - $this->failureUrl = $failureUrl; - - return $this; - } -} diff --git a/src/State/TransactionStateProcessor.php b/src/State/TransactionStateProcessor.php deleted file mode 100644 index 342d6c7..0000000 --- a/src/State/TransactionStateProcessor.php +++ /dev/null @@ -1,30 +0,0 @@ -gatewayLocator->getGatewayOf($data); - - $gateway->process($data); - - return $data; - } -} From ef91edaefdcb9bdf28254ffa18a2bfd61ab3757f Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Thu, 11 Apr 2024 12:36:08 +0200 Subject: [PATCH 14/58] Rename Transaction --- ...nsaction.php => AccountingTransaction.php} | 6 +++--- ...hp => AccountingTransactionRepository.php} | 20 +++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) rename src/Entity/{Transaction.php => AccountingTransaction.php} (93%) rename src/Repository/{TransactionRepository.php => AccountingTransactionRepository.php} (55%) diff --git a/src/Entity/Transaction.php b/src/Entity/AccountingTransaction.php similarity index 93% rename from src/Entity/Transaction.php rename to src/Entity/AccountingTransaction.php index 766e8c2..7e132f9 100644 --- a/src/Entity/Transaction.php +++ b/src/Entity/AccountingTransaction.php @@ -3,7 +3,7 @@ namespace App\Entity; use ApiPlatform\Metadata as API; -use App\Repository\TransactionRepository; +use App\Repository\AccountingTransactionRepository; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Validator\Constraints as Assert; @@ -13,10 +13,10 @@ use Symfony\Component\Validator\Constraints as Assert; * When a Transaction targets an Accounting it means that the Accounting receives it, this will add to that Accounting. * When a Transaction originates from an Accounting the Accounting issues the Transaction and it will deduct from it. */ -#[ORM\Entity(repositoryClass: TransactionRepository::class)] +#[ORM\Entity(repositoryClass: AccountingTransactionRepository::class)] #[API\GetCollection()] #[API\Get()] -class Transaction +class AccountingTransaction { #[ORM\Id] #[ORM\GeneratedValue] diff --git a/src/Repository/TransactionRepository.php b/src/Repository/AccountingTransactionRepository.php similarity index 55% rename from src/Repository/TransactionRepository.php rename to src/Repository/AccountingTransactionRepository.php index d77f9af..674c6a1 100644 --- a/src/Repository/TransactionRepository.php +++ b/src/Repository/AccountingTransactionRepository.php @@ -2,27 +2,27 @@ namespace App\Repository; -use App\Entity\Transaction; +use App\Entity\AccountingTransaction; use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; use Doctrine\Persistence\ManagerRegistry; /** - * @extends ServiceEntityRepository + * @extends ServiceEntityRepository * - * @method Transaction|null find($id, $lockMode = null, $lockVersion = null) - * @method Transaction|null findOneBy(array $criteria, array $orderBy = null) - * @method Transaction[] findAll() - * @method Transaction[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) + * @method AccountingTransaction|null find($id, $lockMode = null, $lockVersion = null) + * @method AccountingTransaction|null findOneBy(array $criteria, array $orderBy = null) + * @method AccountingTransaction[] findAll() + * @method AccountingTransaction[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) */ -class TransactionRepository extends ServiceEntityRepository +class AccountingTransactionRepository extends ServiceEntityRepository { public function __construct(ManagerRegistry $registry) { - parent::__construct($registry, Transaction::class); + parent::__construct($registry, AccountingTransaction::class); } // /** - // * @return Transaction[] Returns an array of Transaction objects + // * @return AccountingTransaction[] Returns an array of AccountingTransaction objects // */ // public function findByExampleField($value): array // { @@ -36,7 +36,7 @@ class TransactionRepository extends ServiceEntityRepository // ; // } - // public function findOneBySomeField($value): ?Transaction + // public function findOneBySomeField($value): ?AccountingTransaction // { // return $this->createQueryBuilder('t') // ->andWhere('t.exampleField = :val') From 75de2b71b0e21e974392a3c57d472a2b2eeacc78 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Thu, 11 Apr 2024 12:36:30 +0200 Subject: [PATCH 15/58] Remove direct relation between Accounting and Transaction --- src/Entity/Accounting.php | 71 --------------------------------------- 1 file changed, 71 deletions(-) diff --git a/src/Entity/Accounting.php b/src/Entity/Accounting.php index 5beb044..314c6b6 100644 --- a/src/Entity/Accounting.php +++ b/src/Entity/Accounting.php @@ -32,14 +32,6 @@ class Accounting #[ORM\Column(length: 3)] private ?string $currency = null; - #[API\ApiProperty(writable: false)] - #[ORM\OneToMany(mappedBy: 'origin', targetEntity: Transaction::class)] - private Collection $transactionsIssued; - - #[API\ApiProperty(writable: false)] - #[ORM\OneToMany(mappedBy: 'target', targetEntity: Transaction::class)] - private Collection $transactionsReceived; - #[API\ApiProperty(writable: false, readable: false)] #[ORM\Column(length: 255)] private ?string $ownerClass = null; @@ -51,9 +43,6 @@ class Accounting * ideally a configuration that can be updated via a frontend, not env var only */ $this->currency = 'EUR'; - - $this->transactionsIssued = new ArrayCollection(); - $this->transactionsReceived = new ArrayCollection(); } public function getId(): ?int @@ -73,66 +62,6 @@ class Accounting return $this; } - /** - * @return Collection - */ - public function getTransactionsIssued(): Collection - { - return $this->transactionsIssued; - } - - public function addTransactionsIssued(Transaction $transactionsIssued): static - { - if (!$this->transactionsIssued->contains($transactionsIssued)) { - $this->transactionsIssued->add($transactionsIssued); - $transactionsIssued->setOrigin($this); - } - - return $this; - } - - public function removeTransactionsIssued(Transaction $transactionsIssued): static - { - if ($this->transactionsIssued->removeElement($transactionsIssued)) { - // set the owning side to null (unless already changed) - if ($transactionsIssued->getOrigin() === $this) { - $transactionsIssued->setOrigin(null); - } - } - - return $this; - } - - /** - * @return Collection - */ - public function getTransactionsReceived(): Collection - { - return $this->transactionsReceived; - } - - public function addTransactionsReceived(Transaction $transactionsReceived): static - { - if (!$this->transactionsReceived->contains($transactionsReceived)) { - $this->transactionsReceived->add($transactionsReceived); - $transactionsReceived->setTarget($this); - } - - return $this; - } - - public function removeTransactionsReceived(Transaction $transactionsReceived): static - { - if ($this->transactionsReceived->removeElement($transactionsReceived)) { - // set the owning side to null (unless already changed) - if ($transactionsReceived->getTarget() === $this) { - $transactionsReceived->setTarget(null); - } - } - - return $this; - } - public function getOwnerClass(): ?string { return $this->ownerClass; From 24670db2aed0a47b7bfc5a28633fe8e644b9747e Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Thu, 11 Apr 2024 12:36:40 +0200 Subject: [PATCH 16/58] Add AccountingStatement --- src/Entity/AccountingStatement.php | 69 +++++++++++++++++++ src/Entity/AccountingStatementDirection.php | 10 +++ .../AccountingStatementRepository.php | 48 +++++++++++++ 3 files changed, 127 insertions(+) create mode 100644 src/Entity/AccountingStatement.php create mode 100644 src/Entity/AccountingStatementDirection.php create mode 100644 src/Repository/AccountingStatementRepository.php diff --git a/src/Entity/AccountingStatement.php b/src/Entity/AccountingStatement.php new file mode 100644 index 0000000..4263cc7 --- /dev/null +++ b/src/Entity/AccountingStatement.php @@ -0,0 +1,69 @@ +id; + } + + public function getMoney(): ?Money + { + return $this->money; + } + + public function setMoney(Money $money): static + { + $this->money = $money; + + return $this; + } + + public function getDirection(): ?AccountingStatementDirection + { + return $this->direction; + } + + public function setDirection(AccountingStatementDirection $direction): static + { + $this->direction = $direction; + + return $this; + } +} diff --git a/src/Entity/AccountingStatementDirection.php b/src/Entity/AccountingStatementDirection.php new file mode 100644 index 0000000..d5a80cb --- /dev/null +++ b/src/Entity/AccountingStatementDirection.php @@ -0,0 +1,10 @@ + + * + * @method AccountingStatement|null find($id, $lockMode = null, $lockVersion = null) + * @method AccountingStatement|null findOneBy(array $criteria, array $orderBy = null) + * @method AccountingStatement[] findAll() + * @method AccountingStatement[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) + */ +class AccountingStatementRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, AccountingStatement::class); + } + + // /** + // * @return AccountingStatement[] Returns an array of AccountingStatement objects + // */ + // public function findByExampleField($value): array + // { + // return $this->createQueryBuilder('a') + // ->andWhere('a.exampleField = :val') + // ->setParameter('val', $value) + // ->orderBy('a.id', 'ASC') + // ->setMaxResults(10) + // ->getQuery() + // ->getResult() + // ; + // } + + // public function findOneBySomeField($value): ?AccountingStatement + // { + // return $this->createQueryBuilder('a') + // ->andWhere('a.exampleField = :val') + // ->setParameter('val', $value) + // ->getQuery() + // ->getOneOrNullResult() + // ; + // } +} From f3f01fe83ce1c7ce4fe1ca3e06b40397a4eec6e4 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Thu, 11 Apr 2024 12:39:38 +0200 Subject: [PATCH 17/58] Relate AccountingStatements to AccountingTransactions --- src/Entity/AccountingStatement.php | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/src/Entity/AccountingStatement.php b/src/Entity/AccountingStatement.php index 4263cc7..4c3e121 100644 --- a/src/Entity/AccountingStatement.php +++ b/src/Entity/AccountingStatement.php @@ -4,14 +4,13 @@ namespace App\Entity; use ApiPlatform\Metadata as API; use App\Repository\AccountingStatementRepository; -use Doctrine\DBAL\Types\Types; use Doctrine\ORM\Mapping as ORM; /** * An AccountingStatement represents a change in the balance of an Accounting.\ * \ - * When money is received, an statement holds it as available. - * When money is issued, an statement draws available money from previous statements.\ + * When money is received, an Statement holds it as available. + * When money is issued, an Statement draws available money from previous statements.\ * \ * The sum of available money in the statements is the available money at that Accounting. */ @@ -25,19 +24,26 @@ class AccountingStatement private ?int $id = null; /** - * The money held in this statement. + * The money held in this Statement. */ #[ORM\Embedded(class: Money::class)] private ?Money $money = null; /** - * The direction of this statement.\ + * The direction of this Statement.\ * `incoming` means the transaction was received and the money is available.\ * `outgoing` means the transaction was issued and the money is an expenditure. */ #[ORM\Column()] private ?AccountingStatementDirection $direction = null; + /** + * The Transaction represented in this Statement. + */ + #[ORM\OneToOne(cascade: ['persist', 'remove'])] + #[ORM\JoinColumn(nullable: false)] + private ?AccountingTransaction $transaction = null; + public function getId(): ?int { return $this->id; @@ -66,4 +72,16 @@ class AccountingStatement return $this; } + + public function getTransaction(): ?AccountingTransaction + { + return $this->transaction; + } + + public function setTransaction(AccountingTransaction $transaction): static + { + $this->transaction = $transaction; + + return $this; + } } From 6a3f7dd0e33741956ab476f5211ff9f4aeecb43e Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Thu, 11 Apr 2024 12:43:15 +0200 Subject: [PATCH 18/58] Add owner resource data to Accounting --- src/Entity/Accounting.php | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/Entity/Accounting.php b/src/Entity/Accounting.php index 314c6b6..ca4779f 100644 --- a/src/Entity/Accounting.php +++ b/src/Entity/Accounting.php @@ -32,6 +32,10 @@ class Accounting #[ORM\Column(length: 3)] private ?string $currency = null; + #[API\ApiProperty(writable: false, readable: false)] + #[ORM\Column] + private ?int $ownerId = null; + #[API\ApiProperty(writable: false, readable: false)] #[ORM\Column(length: 255)] private ?string $ownerClass = null; @@ -62,6 +66,18 @@ class Accounting return $this; } + public function getOwnerId(): ?int + { + return $this->ownerId; + } + + public function setOwnerId(int $ownerId): static + { + $this->ownerId = $ownerId; + + return $this; + } + public function getOwnerClass(): ?string { return $this->ownerClass; @@ -73,4 +89,22 @@ class Accounting return $this; } + + /** + * The type of the owner resource. + */ + public function getOwnerResource(): string + { + $classPieces = explode('\\', $this->getOwnerClass()); + + return end($classPieces); + } + + /** + * The ID of the recorded resource. + */ + public function getOwnerResourceId(): int + { + return $this->getOwnerId(); + } } From 0d7edac50e53dac092c3b26ad80a44baf7268a26 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Thu, 11 Apr 2024 13:13:53 +0200 Subject: [PATCH 19/58] Update Accounting API --- src/Entity/Accounting.php | 35 ++++++++++++++++++++++++++++ src/Entity/AccountingStatement.php | 31 +++++++++++++++++++++++- src/Entity/AccountingTransaction.php | 3 --- 3 files changed, 65 insertions(+), 4 deletions(-) diff --git a/src/Entity/Accounting.php b/src/Entity/Accounting.php index ca4779f..faa61d4 100644 --- a/src/Entity/Accounting.php +++ b/src/Entity/Accounting.php @@ -32,6 +32,10 @@ class Accounting #[ORM\Column(length: 3)] private ?string $currency = null; + #[API\ApiProperty(writable: false)] + #[ORM\OneToMany(mappedBy: 'accounting', targetEntity: AccountingStatement::class)] + private Collection $statements; + #[API\ApiProperty(writable: false, readable: false)] #[ORM\Column] private ?int $ownerId = null; @@ -47,6 +51,7 @@ class Accounting * ideally a configuration that can be updated via a frontend, not env var only */ $this->currency = 'EUR'; + $this->statements = new ArrayCollection(); } public function getId(): ?int @@ -66,6 +71,36 @@ class Accounting return $this; } + /** + * @return Collection + */ + public function getStatements(): Collection + { + return $this->statements; + } + + public function addStatement(AccountingStatement $statement): static + { + if (!$this->statements->contains($statement)) { + $this->statements->add($statement); + $statement->setAccounting($this); + } + + return $this; + } + + public function removeStatement(AccountingStatement $statement): static + { + if ($this->statements->removeElement($statement)) { + // set the owning side to null (unless already changed) + if ($statement->getAccounting() === $this) { + $statement->setAccounting(null); + } + } + + return $this; + } + public function getOwnerId(): ?int { return $this->ownerId; diff --git a/src/Entity/AccountingStatement.php b/src/Entity/AccountingStatement.php index 4c3e121..0d1daef 100644 --- a/src/Entity/AccountingStatement.php +++ b/src/Entity/AccountingStatement.php @@ -14,7 +14,19 @@ use Doctrine\ORM\Mapping as ORM; * \ * The sum of available money in the statements is the available money at that Accounting. */ -#[API\ApiResource] +#[API\GetCollection( + uriTemplate: '/accountings/{id}/statements', + uriVariables: [ + 'id' => new API\Link(fromClass: Accounting::class, toProperty: 'accounting'), + ], +)] +#[API\Get( + uriTemplate: '/accountings/{id}/statements/{statementId}', + uriVariables: [ + 'id' => new API\Link(fromClass: Accounting::class, toProperty: 'accounting'), + 'statementId' => new API\Link(fromClass: AccountingStatement::class) + ], +)] #[ORM\Entity(repositoryClass: AccountingStatementRepository::class)] class AccountingStatement { @@ -23,6 +35,11 @@ class AccountingStatement #[ORM\Column] private ?int $id = null; + #[API\ApiProperty(readable: false)] + #[ORM\ManyToOne(inversedBy: 'statements')] + #[ORM\JoinColumn(nullable: false)] + private ?Accounting $accounting = null; + /** * The money held in this Statement. */ @@ -49,6 +66,18 @@ class AccountingStatement return $this->id; } + public function getAccounting(): ?Accounting + { + return $this->accounting; + } + + public function setAccounting(?Accounting $accounting): static + { + $this->accounting = $accounting; + + return $this; + } + public function getMoney(): ?Money { return $this->money; diff --git a/src/Entity/AccountingTransaction.php b/src/Entity/AccountingTransaction.php index 7e132f9..1281365 100644 --- a/src/Entity/AccountingTransaction.php +++ b/src/Entity/AccountingTransaction.php @@ -2,7 +2,6 @@ namespace App\Entity; -use ApiPlatform\Metadata as API; use App\Repository\AccountingTransactionRepository; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Validator\Constraints as Assert; @@ -14,8 +13,6 @@ use Symfony\Component\Validator\Constraints as Assert; * When a Transaction originates from an Accounting the Accounting issues the Transaction and it will deduct from it. */ #[ORM\Entity(repositoryClass: AccountingTransactionRepository::class)] -#[API\GetCollection()] -#[API\Get()] class AccountingTransaction { #[ORM\Id] From a67d5292f534206f0844be2de5d57c2e8d32811c Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Thu, 11 Apr 2024 13:20:18 +0200 Subject: [PATCH 20/58] Make max int max amount of money --- src/Library/Benzina/Pump/InvestsPump.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Library/Benzina/Pump/InvestsPump.php b/src/Library/Benzina/Pump/InvestsPump.php index fc65c1f..48b7ffb 100644 --- a/src/Library/Benzina/Pump/InvestsPump.php +++ b/src/Library/Benzina/Pump/InvestsPump.php @@ -144,7 +144,7 @@ class InvestsPump implements PumpInterface $amount = $data['amount'] * 100; if ($amount >= self::MAX_INT) { - $amount = $data['amount']; + $amount = self::MAX_INT; } return new Money($amount, $project->getAccounting()->getCurrency()); From 376493378f4117c5fce6e0b492b8cb8e0b97c394 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Thu, 11 Apr 2024 14:22:12 +0200 Subject: [PATCH 21/58] Update Accounting setting --- src/Entity/Accounting.php | 4 ++-- src/Entity/Project.php | 4 +--- src/Entity/User.php | 5 ++--- src/Library/Benzina/Pump/UsersPump.php | 2 ++ 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/Entity/Accounting.php b/src/Entity/Accounting.php index faa61d4..a5ecd0e 100644 --- a/src/Entity/Accounting.php +++ b/src/Entity/Accounting.php @@ -37,7 +37,7 @@ class Accounting private Collection $statements; #[API\ApiProperty(writable: false, readable: false)] - #[ORM\Column] + #[ORM\Column(nullable: true)] private ?int $ownerId = null; #[API\ApiProperty(writable: false, readable: false)] @@ -106,7 +106,7 @@ class Accounting return $this->ownerId; } - public function setOwnerId(int $ownerId): static + public function setOwnerId(?int $ownerId): static { $this->ownerId = $ownerId; diff --git a/src/Entity/Project.php b/src/Entity/Project.php index f8e454c..40f3c7e 100644 --- a/src/Entity/Project.php +++ b/src/Entity/Project.php @@ -57,9 +57,6 @@ class Project public function __construct() { $this->migrated = false; - - $this->accounting = new Accounting(); - $this->accounting->setOwnerClass(Project::class); } public function getId(): ?int @@ -87,6 +84,7 @@ class Project public function setAccounting(Accounting $accounting): static { $this->accounting = $accounting; + $this->accounting->setOwnerId($this->id); $this->accounting->setOwnerClass(self::class); return $this; diff --git a/src/Entity/User.php b/src/Entity/User.php index b89128d..056e18e 100644 --- a/src/Entity/User.php +++ b/src/Entity/User.php @@ -127,9 +127,6 @@ class User implements UserInterface, UserOwnedInterface, PasswordAuthenticatedUs { $this->migrated = false; - $this->accounting = new Accounting(); - $this->accounting->setOwnerClass(User::class); - $this->tokens = new ArrayCollection(); $this->projects = new ArrayCollection(); } @@ -257,6 +254,8 @@ class User implements UserInterface, UserOwnedInterface, PasswordAuthenticatedUs public function setAccounting(Accounting $accounting): static { $this->accounting = $accounting; + $this->accounting->setOwnerId($this->id); + $this->accounting->setOwnerClass(self::class); return $this; } diff --git a/src/Library/Benzina/Pump/UsersPump.php b/src/Library/Benzina/Pump/UsersPump.php index bca058f..7dec348 100644 --- a/src/Library/Benzina/Pump/UsersPump.php +++ b/src/Library/Benzina/Pump/UsersPump.php @@ -2,6 +2,7 @@ namespace App\Library\Benzina\Pump; +use App\Entity\Accounting; use App\Entity\User; use Doctrine\ORM\EntityManagerInterface; @@ -34,6 +35,7 @@ class UsersPump implements PumpInterface } $user = new User; + $user->setAccounting(new Accounting); $user->setUsername($username); $user->setPassword($userData['password'] ?? ""); $user->setEmail($userData['email']); From 365b5f95557114c9fc6bb5ef47e05d78acd421d1 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Fri, 12 Apr 2024 10:31:27 +0200 Subject: [PATCH 22/58] Set user accounting on pumping --- src/Library/Benzina/Pump/UsersPump.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Library/Benzina/Pump/UsersPump.php b/src/Library/Benzina/Pump/UsersPump.php index b45a993..daba8fb 100644 --- a/src/Library/Benzina/Pump/UsersPump.php +++ b/src/Library/Benzina/Pump/UsersPump.php @@ -72,6 +72,7 @@ class UsersPump implements PumpInterface } $user = new User; + $user->setAccounting(new Accounting); $user->setUsername($this->getUsername($record)); $user->setPassword($record['password'] ?? ""); $user->setEmail($record['email']); From 5b98cec08764f014bd855493b78c780cf24bef78 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Fri, 12 Apr 2024 10:54:18 +0200 Subject: [PATCH 23/58] Basic port of old payment methods --- src/Library/Economy/Payment/CashGateway.php | 11 +++++++++++ src/Library/Economy/Payment/CecaGateway.php | 11 +++++++++++ src/Library/Economy/Payment/DropGateway.php | 11 +++++++++++ src/Library/Economy/Payment/PaypalGateway.php | 11 +++++++++++ 4 files changed, 44 insertions(+) create mode 100644 src/Library/Economy/Payment/CashGateway.php create mode 100644 src/Library/Economy/Payment/CecaGateway.php create mode 100644 src/Library/Economy/Payment/DropGateway.php create mode 100644 src/Library/Economy/Payment/PaypalGateway.php diff --git a/src/Library/Economy/Payment/CashGateway.php b/src/Library/Economy/Payment/CashGateway.php new file mode 100644 index 0000000..9b24ecb --- /dev/null +++ b/src/Library/Economy/Payment/CashGateway.php @@ -0,0 +1,11 @@ + Date: Fri, 12 Apr 2024 10:54:34 +0200 Subject: [PATCH 24/58] Add missing payment methods in invests pump --- src/Library/Benzina/Pump/InvestsPump.php | 30 ++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/src/Library/Benzina/Pump/InvestsPump.php b/src/Library/Benzina/Pump/InvestsPump.php index 48b7ffb..173f28b 100644 --- a/src/Library/Benzina/Pump/InvestsPump.php +++ b/src/Library/Benzina/Pump/InvestsPump.php @@ -8,6 +8,10 @@ use App\Entity\GatewayCheckout; use App\Entity\GatewayCheckoutStatus; use App\Entity\Money; use App\Entity\Project; +use App\Library\Economy\Payment\CashGateway; +use App\Library\Economy\Payment\CecaGateway; +use App\Library\Economy\Payment\DropGateway; +use App\Library\Economy\Payment\PaypalGateway; use App\Library\Economy\Payment\StripeGateway; use App\Library\Economy\Payment\WalletGateway; use App\Repository\ProjectRepository; @@ -82,6 +86,10 @@ class InvestsPump implements PumpInterface continue; } + if (!$data['method'] || empty($data['method'])) { + continue; + } + $user = $users[$data['user']]; $project = $projects[$data['project']]; @@ -152,13 +160,11 @@ class InvestsPump implements PumpInterface private function getCheckoutStatus(array $data): GatewayCheckoutStatus { - if ($data['status'] === -1) { + if ($data['status'] < 1) { return GatewayCheckoutStatus::Pending; } - if ($data['status'] === 1) { - return GatewayCheckoutStatus::Charged; - } + return GatewayCheckoutStatus::Charged; } private function getCheckoutGateway(array $data): string @@ -170,6 +176,22 @@ class InvestsPump implements PumpInterface if ($data['method'] === 'pool') { return WalletGateway::getName(); } + + if ($data['method'] === 'tpv') { + return CecaGateway::getName(); + } + + if ($data['method'] === 'paypal') { + return PaypalGateway::getName(); + } + + if ($data['method'] === 'cash') { + return CashGateway::getName(); + } + + if ($data['method'] === 'drop') { + return DropGateway::getName(); + } } private function getCheckoutReference(array $data): string From 6f1f47df9dde2351f03b45bb8a6e8a120a4c068e Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Fri, 12 Apr 2024 11:02:48 +0200 Subject: [PATCH 25/58] Mark GatewayCheckouts as migrated from an invest --- src/Entity/GatewayCheckout.php | 38 ++++++++++++++++++++++++ src/Library/Benzina/Pump/InvestsPump.php | 2 ++ 2 files changed, 40 insertions(+) diff --git a/src/Entity/GatewayCheckout.php b/src/Entity/GatewayCheckout.php index fa92c6f..3e226ac 100644 --- a/src/Entity/GatewayCheckout.php +++ b/src/Entity/GatewayCheckout.php @@ -67,6 +67,20 @@ class GatewayCheckout #[ORM\Column(length: 255)] private ?string $gatewayReference = null; + /** + * GatewayCheckout was migrated from an invest record in Goteo v3 platform. + */ + #[API\ApiProperty(writable: false)] + #[ORM\Column] + private ?bool $migrated = null; + + /** + * The id of the original invest record in the Goteo v3 platform. + */ + #[API\ApiProperty(writable: false)] + #[ORM\Column(length: 255, nullable: true)] + private ?string $migratedReference = null; + public function __construct() { $this->charges = new ArrayCollection(); @@ -149,4 +163,28 @@ class GatewayCheckout return $this; } + + public function isMigrated(): ?bool + { + return $this->migrated; + } + + public function setMigrated(bool $migrated): static + { + $this->migrated = $migrated; + + return $this; + } + + public function getMigratedReference(): ?string + { + return $this->migratedReference; + } + + public function setMigratedReference(?string $migratedReference): static + { + $this->migratedReference = $migratedReference; + + return $this; + } } diff --git a/src/Library/Benzina/Pump/InvestsPump.php b/src/Library/Benzina/Pump/InvestsPump.php index 173f28b..ead026b 100644 --- a/src/Library/Benzina/Pump/InvestsPump.php +++ b/src/Library/Benzina/Pump/InvestsPump.php @@ -104,6 +104,8 @@ class InvestsPump implements PumpInterface $checkout->setStatus($this->getCheckoutStatus($data)); $checkout->setGateway($this->getCheckoutGateway($data)); $checkout->setGatewayReference($this->getCheckoutReference($data)); + $checkout->setMigrated(true); + $checkout->setMigratedReference($data['id']); } $this->entityManager->flush(); From 7d4e6f3fa7ccbb70100aa904e8253a0e20f4a2a7 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Fri, 12 Apr 2024 11:07:12 +0200 Subject: [PATCH 26/58] Rename single data item var to record --- src/Library/Benzina/Pump/InvestsPump.php | 64 ++++++++++++------------ 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/src/Library/Benzina/Pump/InvestsPump.php b/src/Library/Benzina/Pump/InvestsPump.php index ead026b..d43f9b1 100644 --- a/src/Library/Benzina/Pump/InvestsPump.php +++ b/src/Library/Benzina/Pump/InvestsPump.php @@ -77,35 +77,35 @@ class InvestsPump implements PumpInterface $users = $this->getUsers($data); $projects = $this->getProjects($data); - foreach ($data as $key => $data) { - if (!\array_key_exists($data['project'], $projects)) { + foreach ($data as $key => $record) { + if (!\array_key_exists($record['project'], $projects)) { continue; } - if (!$data['amount'] || $data['amount'] < 1) { + if (!$record['amount'] || $record['amount'] < 1) { continue; } - if (!$data['method'] || empty($data['method'])) { + if (!$record['method'] || empty($record['method'])) { continue; } - $user = $users[$data['user']]; - $project = $projects[$data['project']]; + $user = $users[$record['user']]; + $project = $projects[$record['project']]; $charge = new GatewayCharge; - $charge->setType($this->getChargeType($data)); - $charge->setMoney($this->getChargeMoney($data, $project)); + $charge->setType($this->getChargeType($record)); + $charge->setMoney($this->getChargeMoney($record, $project)); $charge->setTarget($project->getAccounting()); $checkout = new GatewayCheckout; $checkout->setOrigin($user->getAccounting()); $checkout->addCharge($charge); - $checkout->setStatus($this->getCheckoutStatus($data)); - $checkout->setGateway($this->getCheckoutGateway($data)); - $checkout->setGatewayReference($this->getCheckoutReference($data)); + $checkout->setStatus($this->getCheckoutStatus($record)); + $checkout->setGateway($this->getCheckoutGateway($record)); + $checkout->setGatewayReference($this->getCheckoutReference($record)); $checkout->setMigrated(true); - $checkout->setMigratedReference($data['id']); + $checkout->setMigratedReference($record['id']); } $this->entityManager->flush(); @@ -140,18 +140,18 @@ class InvestsPump implements PumpInterface return $projectsByMigratedReference; } - private function getChargeType(array $data): GatewayChargeType + private function getChargeType(array $record): GatewayChargeType { - if (\in_array($data['method'], ['stripe_subscription'])) { + if (\in_array($record['method'], ['stripe_subscription'])) { return GatewayChargeType::Recurring; } return GatewayChargeType::Single; } - private function getChargeMoney(array $data, Project $project): Money + private function getChargeMoney(array $record, Project $project): Money { - $amount = $data['amount'] * 100; + $amount = $record['amount'] * 100; if ($amount >= self::MAX_INT) { $amount = self::MAX_INT; @@ -160,54 +160,54 @@ class InvestsPump implements PumpInterface return new Money($amount, $project->getAccounting()->getCurrency()); } - private function getCheckoutStatus(array $data): GatewayCheckoutStatus + private function getCheckoutStatus(array $record): GatewayCheckoutStatus { - if ($data['status'] < 1) { + if ($record['status'] < 1) { return GatewayCheckoutStatus::Pending; } return GatewayCheckoutStatus::Charged; } - private function getCheckoutGateway(array $data): string + private function getCheckoutGateway(array $record): string { - if ($data['method'] === 'stripe_subscription') { + if ($record['method'] === 'stripe_subscription') { return StripeGateway::getName(); } - if ($data['method'] === 'pool') { + if ($record['method'] === 'pool') { return WalletGateway::getName(); } - if ($data['method'] === 'tpv') { + if ($record['method'] === 'tpv') { return CecaGateway::getName(); } - if ($data['method'] === 'paypal') { + if ($record['method'] === 'paypal') { return PaypalGateway::getName(); } - if ($data['method'] === 'cash') { + if ($record['method'] === 'cash') { return CashGateway::getName(); } - if ($data['method'] === 'drop') { + if ($record['method'] === 'drop') { return DropGateway::getName(); } } - private function getCheckoutReference(array $data): string + private function getCheckoutReference(array $record): string { - if (!empty($data['payment'])) { - return $data['payment']; + if (!empty($record['payment'])) { + return $record['payment']; } - if (!empty($data['transaction'])) { - return $data['transaction']; + if (!empty($record['transaction'])) { + return $record['transaction']; } - if (!empty($data['preapproval'])) { - return $data['preapproval']; + if (!empty($record['preapproval'])) { + return $record['preapproval']; } return ''; From 439fb32799cff98d37820bba030566c6cf0d8f3b Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Fri, 12 Apr 2024 11:08:30 +0200 Subject: [PATCH 27/58] Use progressive trait on invests pumping --- src/Library/Benzina/Pump/InvestsPump.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Library/Benzina/Pump/InvestsPump.php b/src/Library/Benzina/Pump/InvestsPump.php index d43f9b1..97c38d8 100644 --- a/src/Library/Benzina/Pump/InvestsPump.php +++ b/src/Library/Benzina/Pump/InvestsPump.php @@ -21,6 +21,7 @@ use Doctrine\ORM\EntityManagerInterface; class InvestsPump implements PumpInterface { use ArrayPumpTrait; + use ProgressivePumpTrait; private const MAX_INT = 2147483647; @@ -77,7 +78,13 @@ class InvestsPump implements PumpInterface $users = $this->getUsers($data); $projects = $this->getProjects($data); + $pumped = $this->getPumped(GatewayCheckout::class, $data, ['migratedReference' => 'id']); + foreach ($data as $key => $record) { + if ($this->isPumped($record, $pumped)) { + continue; + } + if (!\array_key_exists($record['project'], $projects)) { continue; } From c2a8cf5e86a4302bf550192952c9b84dad3c25a2 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Tue, 16 Apr 2024 11:10:34 +0200 Subject: [PATCH 28/58] Make GatewayCharge expose POST and GET operations only --- src/Entity/GatewayCharge.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Entity/GatewayCharge.php b/src/Entity/GatewayCharge.php index 793e748..5651b6b 100644 --- a/src/Entity/GatewayCharge.php +++ b/src/Entity/GatewayCharge.php @@ -2,7 +2,7 @@ namespace App\Entity; -use ApiPlatform\Metadata\ApiResource; +use ApiPlatform\Metadata as API; use App\Repository\GatewayChargeRepository; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Validator\Constraints as Assert; @@ -10,8 +10,9 @@ use Symfony\Component\Validator\Constraints as Assert; /** * A GatewayCharge represents a monetary payment that can be done by an issuer at checkout with the Gateway. */ +#[API\Post()] +#[API\Get()] #[ORM\Entity(repositoryClass: GatewayChargeRepository::class)] -#[ApiResource] class GatewayCharge { #[ORM\Id] From 837af8257574fb455b4f4bbd1844423b04c5a012 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Tue, 16 Apr 2024 11:36:47 +0200 Subject: [PATCH 29/58] Add basic search filters to gateway checkouts --- src/Entity/GatewayCheckout.php | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/Entity/GatewayCheckout.php b/src/Entity/GatewayCheckout.php index 3e226ac..83c666c 100644 --- a/src/Entity/GatewayCheckout.php +++ b/src/Entity/GatewayCheckout.php @@ -2,6 +2,7 @@ namespace App\Entity; +use ApiPlatform\Doctrine\Orm\Filter\SearchFilter; use ApiPlatform\Metadata as API; use App\Dto\GatewayCheckoutUpdateDto; use App\Repository\GatewayCheckoutRepository; @@ -49,6 +50,7 @@ class GatewayCheckout * The status of the checkout with the Gateway. */ #[API\ApiProperty(writable: false)] + #[API\ApiFilter(SearchFilter::class)] #[ORM\Column()] private ?GatewayCheckoutStatus $status = null; @@ -57,6 +59,7 @@ class GatewayCheckout */ #[GatewayName] #[Assert\NotBlank()] + #[API\ApiFilter(SearchFilter::class)] #[ORM\Column(length: 255)] private ?string $gateway = null; @@ -64,6 +67,7 @@ class GatewayCheckout * An external identifier provided by the Gateway for the payment.\ * Required when a GatewayCheckout is completed. */ + #[API\ApiFilter(SearchFilter::class)] #[ORM\Column(length: 255)] private ?string $gatewayReference = null; @@ -81,6 +85,12 @@ class GatewayCheckout #[ORM\Column(length: 255, nullable: true)] private ?string $migratedReference = null; + /** + * A free-form collection of additional data associated with this checkout operation. + */ + #[ORM\Column(nullable: true)] + private ?array $metadata = null; + public function __construct() { $this->charges = new ArrayCollection(); @@ -187,4 +197,16 @@ class GatewayCheckout return $this; } + + public function getMetadata(): ?array + { + return $this->metadata; + } + + public function setMetadata(?array $metadata): static + { + $this->metadata = $metadata; + + return $this; + } } From cb7dc6ff0da1127d0246c467632d7d3073468a28 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Thu, 18 Apr 2024 10:06:01 +0200 Subject: [PATCH 30/58] Skip continue invest pumping for status pending --- src/Library/Benzina/Pump/InvestsPump.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/Library/Benzina/Pump/InvestsPump.php b/src/Library/Benzina/Pump/InvestsPump.php index 97c38d8..55312e9 100644 --- a/src/Library/Benzina/Pump/InvestsPump.php +++ b/src/Library/Benzina/Pump/InvestsPump.php @@ -113,6 +113,18 @@ class InvestsPump implements PumpInterface $checkout->setGatewayReference($this->getCheckoutReference($record)); $checkout->setMigrated(true); $checkout->setMigratedReference($record['id']); + $checkout->setMetadata([ + 'payment' => $record['payment'], + 'transaction' => $record['transaction'], + 'preapproval' => $record['preapproval'] + ]); + + $this->entityManager->persist($charge); + $this->entityManager->persist($checkout); + + if ($checkout->getStatus() === GatewayCheckoutStatus::Pending) { + continue; + } } $this->entityManager->flush(); From affc5e8a1bbc70d430a3f9691a8ea74d1fc978cc Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Thu, 18 Apr 2024 17:28:14 +0200 Subject: [PATCH 31/58] Allow to filter checkouts by origin and charge.target --- src/Entity/GatewayCheckout.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Entity/GatewayCheckout.php b/src/Entity/GatewayCheckout.php index 83c666c..1b1919f 100644 --- a/src/Entity/GatewayCheckout.php +++ b/src/Entity/GatewayCheckout.php @@ -22,6 +22,7 @@ use Symfony\Component\Validator\Constraints as Assert; #[API\Post(processor: GatewayCheckoutProcessor::class)] #[API\Get()] #[API\Patch(input: GatewayCheckoutUpdateDto::class, processor: GatewayCheckoutUpdateProcessor::class)] +#[API\ApiFilter(filterClass: SearchFilter::class, properties: ['origin' => 'exact', 'charges.target' => 'exact'])] #[ORM\Entity(repositoryClass: GatewayCheckoutRepository::class)] class GatewayCheckout { From 25efa6876df4f6d4194ab3645816ec4d198acf14 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Fri, 19 Apr 2024 12:28:57 +0200 Subject: [PATCH 32/58] Remove owner id from prop from accounting --- src/Entity/Accounting.php | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/Entity/Accounting.php b/src/Entity/Accounting.php index 5ddb99f..d51bf25 100644 --- a/src/Entity/Accounting.php +++ b/src/Entity/Accounting.php @@ -37,10 +37,6 @@ class Accounting #[ORM\OneToMany(mappedBy: 'accounting', targetEntity: AccountingStatement::class)] private Collection $statements; - #[API\ApiProperty(writable: false, readable: false)] - #[ORM\Column(nullable: true)] - private ?int $ownerId = null; - #[API\ApiProperty(writable: false, readable: false)] #[ORM\Column(length: 255)] private ?string $ownerClass = null; @@ -108,18 +104,6 @@ class Accounting return $this; } - public function getOwnerId(): ?int - { - return $this->ownerId; - } - - public function setOwnerId(?int $ownerId): static - { - $this->ownerId = $ownerId; - - return $this; - } - public function getOwnerClass(): ?string { return $this->ownerClass; From c939403f00301cd0f2730942b96c107f322ec890 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Fri, 19 Apr 2024 12:31:16 +0200 Subject: [PATCH 33/58] Remove proto accounting ownership setting from user and project --- src/Entity/Project.php | 2 -- src/Entity/User.php | 2 -- 2 files changed, 4 deletions(-) diff --git a/src/Entity/Project.php b/src/Entity/Project.php index 603cd76..8636cd9 100644 --- a/src/Entity/Project.php +++ b/src/Entity/Project.php @@ -83,8 +83,6 @@ class Project public function setAccounting(Accounting $accounting): static { $this->accounting = $accounting; - $this->accounting->setOwnerId($this->id); - $this->accounting->setOwnerClass(self::class); return $this; } diff --git a/src/Entity/User.php b/src/Entity/User.php index 8f71e5e..204d048 100644 --- a/src/Entity/User.php +++ b/src/Entity/User.php @@ -252,8 +252,6 @@ class User implements UserInterface, UserOwnedInterface, PasswordAuthenticatedUs public function setAccounting(Accounting $accounting): static { $this->accounting = $accounting; - $this->accounting->setOwnerId($this->id); - $this->accounting->setOwnerClass(self::class); return $this; } From 87620d67a0175b31c5be4016cff9eaa14d62992b Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Fri, 19 Apr 2024 15:18:31 +0200 Subject: [PATCH 34/58] Add Tipjar entity for tips --- src/Entity/Tipjar.php | 63 +++++++++++++++++++++++++++++ src/Repository/TipjarRepository.php | 48 ++++++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 src/Entity/Tipjar.php create mode 100644 src/Repository/TipjarRepository.php diff --git a/src/Entity/Tipjar.php b/src/Entity/Tipjar.php new file mode 100644 index 0000000..57507f9 --- /dev/null +++ b/src/Entity/Tipjar.php @@ -0,0 +1,63 @@ +id; + } + + public function getName(): ?string + { + return $this->name; + } + + public function setName(string $name): static + { + $this->name = $name; + + return $this; + } + + public function getAccounting(): ?Accounting + { + return $this->accounting; + } + + public function setAccounting(Accounting $accounting): static + { + $this->accounting = $accounting; + + return $this; + } +} diff --git a/src/Repository/TipjarRepository.php b/src/Repository/TipjarRepository.php new file mode 100644 index 0000000..86fa53b --- /dev/null +++ b/src/Repository/TipjarRepository.php @@ -0,0 +1,48 @@ + + * + * @method Tipjar|null find($id, $lockMode = null, $lockVersion = null) + * @method Tipjar|null findOneBy(array $criteria, array $orderBy = null) + * @method Tipjar[] findAll() + * @method Tipjar[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) + */ +class TipjarRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, Tipjar::class); + } + + // /** + // * @return Tipjar[] Returns an array of Tipjar objects + // */ + // public function findByExampleField($value): array + // { + // return $this->createQueryBuilder('t') + // ->andWhere('t.exampleField = :val') + // ->setParameter('val', $value) + // ->orderBy('t.id', 'ASC') + // ->setMaxResults(10) + // ->getQuery() + // ->getResult() + // ; + // } + + // public function findOneBySomeField($value): ?Tipjar + // { + // return $this->createQueryBuilder('t') + // ->andWhere('t.exampleField = :val') + // ->setParameter('val', $value) + // ->getQuery() + // ->getOneOrNullResult() + // ; + // } +} From d7a67eaea66248112445bae71e74cf98de92c858 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Mon, 22 Apr 2024 12:13:44 +0200 Subject: [PATCH 35/58] Add platform tips on invest pumps --- src/Library/Benzina/Pump/InvestsPump.php | 64 ++++++++++++++++++++---- 1 file changed, 54 insertions(+), 10 deletions(-) diff --git a/src/Library/Benzina/Pump/InvestsPump.php b/src/Library/Benzina/Pump/InvestsPump.php index 55312e9..c0139e3 100644 --- a/src/Library/Benzina/Pump/InvestsPump.php +++ b/src/Library/Benzina/Pump/InvestsPump.php @@ -2,12 +2,13 @@ namespace App\Library\Benzina\Pump; +use App\Entity\Accounting; use App\Entity\GatewayCharge; use App\Entity\GatewayChargeType; use App\Entity\GatewayCheckout; use App\Entity\GatewayCheckoutStatus; use App\Entity\Money; -use App\Entity\Project; +use App\Entity\Tipjar; use App\Library\Economy\Payment\CashGateway; use App\Library\Economy\Payment\CecaGateway; use App\Library\Economy\Payment\DropGateway; @@ -15,6 +16,7 @@ use App\Library\Economy\Payment\PaypalGateway; use App\Library\Economy\Payment\StripeGateway; use App\Library\Economy\Payment\WalletGateway; use App\Repository\ProjectRepository; +use App\Repository\TipjarRepository; use App\Repository\UserRepository; use Doctrine\ORM\EntityManagerInterface; @@ -23,6 +25,8 @@ class InvestsPump implements PumpInterface use ArrayPumpTrait; use ProgressivePumpTrait; + private const PLATFORM_TIPJAR_NAME = 'platform'; + private const MAX_INT = 2147483647; private const INVEST_KEYS = [ @@ -60,6 +64,7 @@ class InvestsPump implements PumpInterface public function __construct( private UserRepository $userRepository, private ProjectRepository $projectRepository, + private TipjarRepository $tipjarRepository, private EntityManagerInterface $entityManager ) { } @@ -78,6 +83,8 @@ class InvestsPump implements PumpInterface $users = $this->getUsers($data); $projects = $this->getProjects($data); + $tipjar = $this->getPlatformTipjar(); + $pumped = $this->getPumped(GatewayCheckout::class, $data, ['migratedReference' => 'id']); foreach ($data as $key => $record) { @@ -100,14 +107,8 @@ class InvestsPump implements PumpInterface $user = $users[$record['user']]; $project = $projects[$record['project']]; - $charge = new GatewayCharge; - $charge->setType($this->getChargeType($record)); - $charge->setMoney($this->getChargeMoney($record, $project)); - $charge->setTarget($project->getAccounting()); - $checkout = new GatewayCheckout; $checkout->setOrigin($user->getAccounting()); - $checkout->addCharge($charge); $checkout->setStatus($this->getCheckoutStatus($record)); $checkout->setGateway($this->getCheckoutGateway($record)); $checkout->setGatewayReference($this->getCheckoutReference($record)); @@ -119,7 +120,29 @@ class InvestsPump implements PumpInterface 'preapproval' => $record['preapproval'] ]); + $charge = new GatewayCharge; + $charge->setType($this->getChargeType($record)); + $charge->setMoney($this->getChargeMoney($record['amount'], $record['currency'])); + $charge->setTarget($project->getAccounting()); + + if ($record['donate_amount'] > 0) { + $charge->setMoney($this->getChargeMoney( + $record['amount'] - $record['donate_amount'], + $record['currency'] + )); + + $tip = new GatewayCharge; + $tip->setType(GatewayChargeType::Single); + $tip->setMoney($this->getChargeMoney($record['donate_amount'], $record['currency'])); + $tip->setTarget($tipjar->getAccounting()); + + $this->entityManager->persist($tip); + $checkout->addCharge($tip); + } + $this->entityManager->persist($charge); + $checkout->addCharge($charge); + $this->entityManager->persist($checkout); if ($checkout->getStatus() === GatewayCheckoutStatus::Pending) { @@ -159,6 +182,27 @@ class InvestsPump implements PumpInterface return $projectsByMigratedReference; } + private function getPlatformTipjar(): Tipjar + { + $tipjar = $this->tipjarRepository->findOneBy(['name' => self::PLATFORM_TIPJAR_NAME]); + + if ($tipjar) { + return $tipjar; + } + + $tipjar = new Tipjar; + $tipjar->setName(SELF::PLATFORM_TIPJAR_NAME); + + $accounting = new Accounting; + $accounting->setTipjar($tipjar); + + $this->entityManager->persist($tipjar); + $this->entityManager->persist($accounting); + $this->entityManager->flush(); + + return $tipjar; + } + private function getChargeType(array $record): GatewayChargeType { if (\in_array($record['method'], ['stripe_subscription'])) { @@ -168,15 +212,15 @@ class InvestsPump implements PumpInterface return GatewayChargeType::Single; } - private function getChargeMoney(array $record, Project $project): Money + private function getChargeMoney(int $amount, string $currency): Money { - $amount = $record['amount'] * 100; + $amount = $amount * 100; if ($amount >= self::MAX_INT) { $amount = self::MAX_INT; } - return new Money($amount, $project->getAccounting()->getCurrency()); + return new Money($amount, $currency); } private function getCheckoutStatus(array $record): GatewayCheckoutStatus From 95323f658b5705133038aa02de91613402a779d2 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Mon, 22 Apr 2024 12:14:08 +0200 Subject: [PATCH 36/58] Relate accountings to tipjars --- src/Entity/Accounting.php | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/Entity/Accounting.php b/src/Entity/Accounting.php index d51bf25..89bc2b5 100644 --- a/src/Entity/Accounting.php +++ b/src/Entity/Accounting.php @@ -47,6 +47,9 @@ class Accounting #[ORM\OneToOne(mappedBy: 'accounting', cascade: ['persist', 'remove'])] private ?Project $project = null; + #[ORM\OneToOne(mappedBy: 'accounting', cascade: ['persist', 'remove'])] + private ?Tipjar $tipjar = null; + public function __construct() { /** @@ -168,4 +171,24 @@ class Accounting return $this; } + + public function getTipjar(): ?Tipjar + { + return $this->tipjar; + } + + public function setTipjar(Tipjar $tipjar): static + { + // set the owning side of the relation if necessary + if ($tipjar->getAccounting() !== $this) { + $tipjar->setAccounting($this); + } + + // set the owner class + $this->setOwnerClass($tipjar::class); + + $this->tipjar = $tipjar; + + return $this; + } } From 794aee27c6b3e5bad1020303929da74b2d333f25 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Mon, 22 Apr 2024 12:58:52 +0200 Subject: [PATCH 37/58] Make gateway charges embedded in gateway checkout --- src/Entity/GatewayCharge.php | 3 --- src/Entity/GatewayCheckout.php | 3 ++- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Entity/GatewayCharge.php b/src/Entity/GatewayCharge.php index 5651b6b..41ce3dc 100644 --- a/src/Entity/GatewayCharge.php +++ b/src/Entity/GatewayCharge.php @@ -2,7 +2,6 @@ namespace App\Entity; -use ApiPlatform\Metadata as API; use App\Repository\GatewayChargeRepository; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Validator\Constraints as Assert; @@ -10,8 +9,6 @@ use Symfony\Component\Validator\Constraints as Assert; /** * A GatewayCharge represents a monetary payment that can be done by an issuer at checkout with the Gateway. */ -#[API\Post()] -#[API\Get()] #[ORM\Entity(repositoryClass: GatewayChargeRepository::class)] class GatewayCharge { diff --git a/src/Entity/GatewayCheckout.php b/src/Entity/GatewayCheckout.php index 1b1919f..e174853 100644 --- a/src/Entity/GatewayCheckout.php +++ b/src/Entity/GatewayCheckout.php @@ -44,7 +44,8 @@ class GatewayCheckout */ #[Assert\NotBlank()] #[Assert\Count(min: 1)] - #[ORM\ManyToMany(targetEntity: GatewayCharge::class)] + #[API\ApiProperty(readableLink: true, writableLink: true)] + #[ORM\ManyToMany(targetEntity: GatewayCharge::class, cascade: ['persist'])] private Collection $charges; /** From 2baa050314b9b5ed128389886eaa25593c921bd4 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Tue, 23 Apr 2024 10:21:36 +0200 Subject: [PATCH 38/58] Add docblock comments for GatewayCharge properties --- src/Entity/GatewayCharge.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/Entity/GatewayCharge.php b/src/Entity/GatewayCharge.php index 41ce3dc..6bed9ab 100644 --- a/src/Entity/GatewayCharge.php +++ b/src/Entity/GatewayCharge.php @@ -17,14 +17,23 @@ class GatewayCharge #[ORM\Column] private ?int $id = null; + /** + * The type of a GatewayCharge represents the kind of payment. + */ #[Assert\NotBlank()] #[ORM\Column()] private ?GatewayChargeType $type = null; + /** + * The charged monetary sum. + */ #[Assert\NotBlank()] #[ORM\Embedded(Money::class)] private ?Money $money = null; + /** + * The Accounting receiving the consequent Transaction for this GatewayCharge. + */ #[Assert\NotBlank()] #[ORM\ManyToOne] #[ORM\JoinColumn(nullable: false)] From af3c97981edc8bd2b95d1284f21bbc6c0d6c81c9 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Tue, 23 Apr 2024 10:36:08 +0200 Subject: [PATCH 39/58] GatewayCheckouts are versioned resources --- src/Entity/GatewayCheckout.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Entity/GatewayCheckout.php b/src/Entity/GatewayCheckout.php index e174853..ecbf8b8 100644 --- a/src/Entity/GatewayCheckout.php +++ b/src/Entity/GatewayCheckout.php @@ -12,12 +12,14 @@ use App\Validator\GatewayName; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; +use Gedmo\Mapping\Annotation as Gedmo; use Symfony\Component\Validator\Constraints as Assert; /** * A GatewayCheckout bundles the data to perform a charge operation at a Gateway. * Use it in order to create Transactions and have the transferred money be backed by a Gateway's payment processing. */ +#[Gedmo\Loggable()] #[API\GetCollection()] #[API\Post(processor: GatewayCheckoutProcessor::class)] #[API\Get()] @@ -51,6 +53,7 @@ class GatewayCheckout /** * The status of the checkout with the Gateway. */ + #[Gedmo\Versioned] #[API\ApiProperty(writable: false)] #[API\ApiFilter(SearchFilter::class)] #[ORM\Column()] From a7fea45e0b4f1bcaebec0be7f51ed8551d09dd1e Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Tue, 23 Apr 2024 11:10:13 +0200 Subject: [PATCH 40/58] Use VersionedResourceService to get resource names --- src/Filter/VersionResourceFilter.php | 6 ++- src/Service/VersionedResourceService.php | 65 ++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 src/Service/VersionedResourceService.php diff --git a/src/Filter/VersionResourceFilter.php b/src/Filter/VersionResourceFilter.php index fe7b33d..cc8ee83 100644 --- a/src/Filter/VersionResourceFilter.php +++ b/src/Filter/VersionResourceFilter.php @@ -5,6 +5,7 @@ namespace App\Filter; use ApiPlatform\Doctrine\Orm\Filter\AbstractFilter; use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface; use ApiPlatform\Metadata\Operation; +use App\Service\VersionedResourceService; use Doctrine\ORM\QueryBuilder; use Doctrine\Persistence\ManagerRegistry; use Psr\Log\LoggerInterface; @@ -14,10 +15,11 @@ use Symfony\Component\Serializer\NameConverter\NameConverterInterface; final class VersionResourceFilter extends AbstractFilter { public function __construct( + private VersionedResourceService $versionedResourceService, protected ManagerRegistry $managerRegistry, ?LoggerInterface $logger = null, protected ?array $properties = null, - protected ?NameConverterInterface $nameConverter = null + protected ?NameConverterInterface $nameConverter = null, ) { parent::__construct($managerRegistry, $logger, $properties, $nameConverter); } @@ -45,7 +47,7 @@ final class VersionResourceFilter extends AbstractFilter 'description' => 'The name of the resource.', 'schema' => [ 'type' => Type::BUILTIN_TYPE_STRING, - 'enum' => ['User'] + 'enum' => $this->versionedResourceService->getNames() ], 'openapi' => [ 'allowEmptyValue' => false, diff --git a/src/Service/VersionedResourceService.php b/src/Service/VersionedResourceService.php new file mode 100644 index 0000000..9fe8ed1 --- /dev/null +++ b/src/Service/VersionedResourceService.php @@ -0,0 +1,65 @@ +makeCompileDir(); + + \file_put_contents( + self::getNamesLockFile(), + implode(PHP_EOL, $names) + ); + } + + /** + * @return array List of the versioned resource names + * @see compileNames() + */ + public static function getNames(): array + { + return explode(PHP_EOL, \file_get_contents(self::getNamesLockFile())); + } +} From 0fcc0aaf3369e426d153fa40d596dc039bed6568 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Tue, 23 Apr 2024 11:10:37 +0200 Subject: [PATCH 41/58] Add VersionedResourcePass to compiler resource names into lock file --- .../Compiler/VersionedResourcePass.php | 39 +++++++++++++++++++ src/Kernel.php | 6 +++ 2 files changed, 45 insertions(+) create mode 100644 src/DependencyInjection/Compiler/VersionedResourcePass.php diff --git a/src/DependencyInjection/Compiler/VersionedResourcePass.php b/src/DependencyInjection/Compiler/VersionedResourcePass.php new file mode 100644 index 0000000..84272ed --- /dev/null +++ b/src/DependencyInjection/Compiler/VersionedResourcePass.php @@ -0,0 +1,39 @@ +getAttributes(Gedmo\Loggable::class); + + if (empty($attributes)) { + continue; + } + + $versionedResourceNames = [ + ...$versionedResourceNames, + ApiResourceNormalizer::toResource($entityClass) + ]; + } + + /** @var VersionedResourceService */ + $versionedResourceService = $container->get(VersionedResourceService::class); + $versionedResourceService->compileNames($versionedResourceNames); + } +} diff --git a/src/Kernel.php b/src/Kernel.php index d8bf758..0388b2a 100644 --- a/src/Kernel.php +++ b/src/Kernel.php @@ -3,6 +3,7 @@ namespace App; use App\DependencyInjection\Compiler\GatewayPass; +use App\DependencyInjection\Compiler\VersionedResourcePass; use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait; use Symfony\Component\DependencyInjection\Compiler\PassConfig; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -18,5 +19,10 @@ class Kernel extends BaseKernel new GatewayPass, PassConfig::TYPE_AFTER_REMOVING ); + + $container->addCompilerPass( + new VersionedResourcePass, + PassConfig::TYPE_AFTER_REMOVING + ); } } From 15e11fb6ca043b64aa818266e30e6455d9d68efa Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Tue, 23 Apr 2024 11:13:28 +0200 Subject: [PATCH 42/58] Only lowercase first letter of class names in ApiResourceNormalizer --- src/Service/ApiResourceNormalizer.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Service/ApiResourceNormalizer.php b/src/Service/ApiResourceNormalizer.php index f8d958f..f615b99 100644 --- a/src/Service/ApiResourceNormalizer.php +++ b/src/Service/ApiResourceNormalizer.php @@ -14,7 +14,7 @@ class ApiResourceNormalizer { $classPieces = explode("\\", $className); - return strtolower(end($classPieces)); + return lcfirst(end($classPieces)); } /** @@ -22,6 +22,6 @@ class ApiResourceNormalizer */ public static function toEntity(string $resourceName): string { - return sprintf("App\\Entity\\%s", ucfirst(strtolower($resourceName))); + return sprintf("App\\Entity\\%s", ucfirst($resourceName)); } } From 345a2809c6622b8a11a2b4e734b47353bb13a1a3 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Tue, 23 Apr 2024 11:26:14 +0200 Subject: [PATCH 43/58] Rename API resource Version related classes to ResourceVersion name pattern --- src/ApiResource/Version.php | 14 +++++++------- ...ilter.php => ResourceVersionResourceFilter.php} | 2 +- ...ter.php => ResourceVersionResourceIdFilter.php} | 2 +- ...ovider.php => ResourceVersionStateProvider.php} | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) rename src/Filter/{VersionResourceFilter.php => ResourceVersionResourceFilter.php} (96%) rename src/Filter/{VersionResourceIdFilter.php => ResourceVersionResourceIdFilter.php} (94%) rename src/State/{VersionStateProvider.php => ResourceVersionStateProvider.php} (96%) diff --git a/src/ApiResource/Version.php b/src/ApiResource/Version.php index f37b79e..ba15176 100644 --- a/src/ApiResource/Version.php +++ b/src/ApiResource/Version.php @@ -3,10 +3,10 @@ namespace App\ApiResource; use ApiPlatform\Metadata as API; -use App\Filter\VersionResourceFilter; -use App\Filter\VersionResourceIdFilter; +use App\Filter\ResourceVersionResourceFilter; +use App\Filter\ResourceVersionResourceIdFilter; use App\Service\ApiResourceNormalizer; -use App\State\VersionStateProvider; +use App\State\ResourceVersionStateProvider; use Gedmo\Loggable\Entity\LogEntry; /** @@ -15,10 +15,10 @@ use Gedmo\Loggable\Entity\LogEntry; * This allows us to keep track of the flow and the evolution of records in the platform. * Looking at the changes done between one version and the next one we can reconstruct how a resource was at a certain point in time. */ -#[API\ApiFilter(VersionResourceFilter::class, properties: ['resource'])] -#[API\ApiFilter(VersionResourceIdFilter::class, properties: ['resourceId'])] -#[API\GetCollection(provider: VersionStateProvider::class)] -#[API\Get(provider: VersionStateProvider::class)] +#[API\ApiFilter(ResourceVersionResourceFilter::class, properties: ['resource'])] +#[API\ApiFilter(ResourceVersionResourceIdFilter::class, properties: ['resourceId'])] +#[API\GetCollection(provider: ResourceVersionStateProvider::class)] +#[API\Get(provider: ResourceVersionStateProvider::class)] class Version { public function __construct( diff --git a/src/Filter/VersionResourceFilter.php b/src/Filter/ResourceVersionResourceFilter.php similarity index 96% rename from src/Filter/VersionResourceFilter.php rename to src/Filter/ResourceVersionResourceFilter.php index cc8ee83..1c33eba 100644 --- a/src/Filter/VersionResourceFilter.php +++ b/src/Filter/ResourceVersionResourceFilter.php @@ -12,7 +12,7 @@ use Psr\Log\LoggerInterface; use Symfony\Component\PropertyInfo\Type; use Symfony\Component\Serializer\NameConverter\NameConverterInterface; -final class VersionResourceFilter extends AbstractFilter +final class ResourceVersionResourceFilter extends AbstractFilter { public function __construct( private VersionedResourceService $versionedResourceService, diff --git a/src/Filter/VersionResourceIdFilter.php b/src/Filter/ResourceVersionResourceIdFilter.php similarity index 94% rename from src/Filter/VersionResourceIdFilter.php rename to src/Filter/ResourceVersionResourceIdFilter.php index 0b68986..3f699a9 100644 --- a/src/Filter/VersionResourceIdFilter.php +++ b/src/Filter/ResourceVersionResourceIdFilter.php @@ -8,7 +8,7 @@ use ApiPlatform\Metadata\Operation; use Doctrine\ORM\QueryBuilder; use Symfony\Component\PropertyInfo\Type; -final class VersionResourceIdFilter extends AbstractFilter +final class ResourceVersionResourceIdFilter extends AbstractFilter { protected function filterProperty( string $property, diff --git a/src/State/VersionStateProvider.php b/src/State/ResourceVersionStateProvider.php similarity index 96% rename from src/State/VersionStateProvider.php rename to src/State/ResourceVersionStateProvider.php index cef0545..274fe30 100644 --- a/src/State/VersionStateProvider.php +++ b/src/State/ResourceVersionStateProvider.php @@ -12,7 +12,7 @@ use Gedmo\Loggable\Entity\LogEntry; use Gedmo\Loggable\Entity\Repository\LogEntryRepository; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -class VersionStateProvider implements ProviderInterface +class ResourceVersionStateProvider implements ProviderInterface { private LogEntryRepository $versionRepository; From 1e2c538c0e0235d2550c1f1ebe7e49e0e658fab5 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Tue, 23 Apr 2024 11:42:47 +0200 Subject: [PATCH 44/58] Add error handling for resource and resource id not found in ResourceVersionStateProvider --- src/State/ResourceVersionStateProvider.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/State/ResourceVersionStateProvider.php b/src/State/ResourceVersionStateProvider.php index 274fe30..a14b3d6 100644 --- a/src/State/ResourceVersionStateProvider.php +++ b/src/State/ResourceVersionStateProvider.php @@ -8,6 +8,7 @@ use ApiPlatform\State\ProviderInterface; use App\ApiResource\Version; use App\Service\ApiResourceNormalizer; use Doctrine\ORM\EntityManagerInterface; +use Doctrine\Persistence\Mapping\MappingException; use Gedmo\Loggable\Entity\LogEntry; use Gedmo\Loggable\Entity\Repository\LogEntryRepository; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; @@ -58,7 +59,16 @@ class ResourceVersionStateProvider implements ProviderInterface { $resourceClass = ApiResourceNormalizer::toEntity($resourceName); - $entity = $this->entityManager->find($resourceClass, $resourceId); + try { + $entity = $this->entityManager->find($resourceClass, $resourceId); + } catch (MappingException $e) { + throw new NotFoundHttpException(sprintf("Resource '%s' does not exist", $resourceName)); + } + + if (!$entity) { + throw new NotFoundHttpException(sprintf("Resource '%s' with ID '%s' not found", $resourceName, $resourceId)); + } + $logs = $this->versionRepository->getLogEntries($entity); $versions = []; From 4f64cb046dd6d3095b66e44c9cfad3ba1f0ff103 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Tue, 23 Apr 2024 12:02:28 +0200 Subject: [PATCH 45/58] It didn't make sense to reconstruct a resource version data from the current state of the resource --- src/ApiResource/Version.php | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/ApiResource/Version.php b/src/ApiResource/Version.php index ba15176..103e649 100644 --- a/src/ApiResource/Version.php +++ b/src/ApiResource/Version.php @@ -67,21 +67,6 @@ class Version return $this->log->getObjectId(); } - /** - * The full resource data, reconstructed from the current resource data merged with versioned data. - */ - public function getResourceData() - { - $entity = $this->entity; - - foreach ($this->log->getData() as $changedProperty => $changedValue) { - $setter = sprintf("set%s", ucfirst($changedProperty)); - $entity->$setter($changedValue); - } - - return $entity; - } - /** * The changed resource data, i.e the new values of the changed properties. */ From 35a20a1cc4a7fe092f03efe5dcc8dea2c5683384 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Tue, 23 Apr 2024 12:02:54 +0200 Subject: [PATCH 46/58] GatewayCheckout is Timestampable --- src/Entity/GatewayCheckout.php | 3 +++ src/Library/Benzina/Pump/InvestsPump.php | 2 ++ 2 files changed, 5 insertions(+) diff --git a/src/Entity/GatewayCheckout.php b/src/Entity/GatewayCheckout.php index ecbf8b8..6ab9046 100644 --- a/src/Entity/GatewayCheckout.php +++ b/src/Entity/GatewayCheckout.php @@ -13,6 +13,7 @@ use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; use Gedmo\Mapping\Annotation as Gedmo; +use Gedmo\Timestampable\Traits\TimestampableEntity; use Symfony\Component\Validator\Constraints as Assert; /** @@ -28,6 +29,8 @@ use Symfony\Component\Validator\Constraints as Assert; #[ORM\Entity(repositoryClass: GatewayCheckoutRepository::class)] class GatewayCheckout { + use TimestampableEntity; + #[ORM\Id] #[ORM\GeneratedValue] #[ORM\Column] diff --git a/src/Library/Benzina/Pump/InvestsPump.php b/src/Library/Benzina/Pump/InvestsPump.php index c0139e3..bc4a001 100644 --- a/src/Library/Benzina/Pump/InvestsPump.php +++ b/src/Library/Benzina/Pump/InvestsPump.php @@ -119,6 +119,8 @@ class InvestsPump implements PumpInterface 'transaction' => $record['transaction'], 'preapproval' => $record['preapproval'] ]); + $checkout->setCreatedAt(new \DateTime($record['invested'])); + $checkout->setUpdatedAt(new \DateTime()); $charge = new GatewayCharge; $charge->setType($this->getChargeType($record)); From ae981cdfd0074eb41f7571a591327acfc6ae81f4 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Wed, 24 Apr 2024 14:28:03 +0200 Subject: [PATCH 47/58] Make origin accounting embedded object in API response --- src/Entity/GatewayCheckout.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Entity/GatewayCheckout.php b/src/Entity/GatewayCheckout.php index 6ab9046..f4bc525 100644 --- a/src/Entity/GatewayCheckout.php +++ b/src/Entity/GatewayCheckout.php @@ -40,6 +40,7 @@ class GatewayCheckout * The Accounting that will issue the Transactions of the GatewayCharges after a successful checkout. */ #[Assert\NotBlank()] + #[API\ApiProperty(readableLink: true)] #[ORM\ManyToOne] #[ORM\JoinColumn(nullable: false)] private ?Accounting $origin = null; From e2fdd78415bc242f72d6b9c5771ad4e1c0897765 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Wed, 24 Apr 2024 14:28:35 +0200 Subject: [PATCH 48/58] Not deduct tip donated money from charge in invests pump --- src/Library/Benzina/Pump/InvestsPump.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Library/Benzina/Pump/InvestsPump.php b/src/Library/Benzina/Pump/InvestsPump.php index bc4a001..ed99ca0 100644 --- a/src/Library/Benzina/Pump/InvestsPump.php +++ b/src/Library/Benzina/Pump/InvestsPump.php @@ -128,11 +128,6 @@ class InvestsPump implements PumpInterface $charge->setTarget($project->getAccounting()); if ($record['donate_amount'] > 0) { - $charge->setMoney($this->getChargeMoney( - $record['amount'] - $record['donate_amount'], - $record['currency'] - )); - $tip = new GatewayCharge; $tip->setType(GatewayChargeType::Single); $tip->setMoney($this->getChargeMoney($record['donate_amount'], $record['currency'])); From 6835e3811644c3c3a3604397408156a787b386d2 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Wed, 8 May 2024 13:46:12 +0200 Subject: [PATCH 49/58] Refactor payment method matching function into switch statement --- src/Library/Benzina/Pump/InvestsPump.php | 37 ++++++++++-------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/src/Library/Benzina/Pump/InvestsPump.php b/src/Library/Benzina/Pump/InvestsPump.php index ed99ca0..643aaaf 100644 --- a/src/Library/Benzina/Pump/InvestsPump.php +++ b/src/Library/Benzina/Pump/InvestsPump.php @@ -231,28 +231,21 @@ class InvestsPump implements PumpInterface private function getCheckoutGateway(array $record): string { - if ($record['method'] === 'stripe_subscription') { - return StripeGateway::getName(); - } - - if ($record['method'] === 'pool') { - return WalletGateway::getName(); - } - - if ($record['method'] === 'tpv') { - return CecaGateway::getName(); - } - - if ($record['method'] === 'paypal') { - return PaypalGateway::getName(); - } - - if ($record['method'] === 'cash') { - return CashGateway::getName(); - } - - if ($record['method'] === 'drop') { - return DropGateway::getName(); + switch ($record['method']) { + case 'stripe_subscription': + return StripeGateway::getName(); + case 'pool': + return WalletGateway::getName(); + case 'paypal': + return PaypalGateway::getName(); + case 'tpv': + return CecaGateway::getName(); + case 'cash': + return CashGateway::getName(); + case 'drop': + return DropGateway::getName(); + default: + return ''; } } From 55f13fab60fc55e3c103f60c882e9ff26db48b19 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Wed, 22 May 2024 11:49:30 +0200 Subject: [PATCH 50/58] Allow null on project owner setter method --- src/Entity/Project.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Entity/Project.php b/src/Entity/Project.php index 1b89953..7e8d765 100644 --- a/src/Entity/Project.php +++ b/src/Entity/Project.php @@ -104,7 +104,7 @@ class Project return $this->owner; } - public function setOwner(User $owner): static + public function setOwner(?User $owner): static { $this->owner = $owner; From 21f12cba37b3db5b73a4736466a417734314040a Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Wed, 22 May 2024 12:21:20 +0200 Subject: [PATCH 51/58] Update GatewayCheckout to have checkout url and require gatewayReference on edit operations --- src/Entity/GatewayCheckout.php | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/Entity/GatewayCheckout.php b/src/Entity/GatewayCheckout.php index f4bc525..0611269 100644 --- a/src/Entity/GatewayCheckout.php +++ b/src/Entity/GatewayCheckout.php @@ -11,6 +11,7 @@ use App\State\GatewayCheckoutUpdateProcessor; use App\Validator\GatewayName; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; +use Doctrine\DBAL\Types\Types; use Doctrine\ORM\Mapping as ORM; use Gedmo\Mapping\Annotation as Gedmo; use Gedmo\Timestampable\Traits\TimestampableEntity; @@ -22,7 +23,7 @@ use Symfony\Component\Validator\Constraints as Assert; */ #[Gedmo\Loggable()] #[API\GetCollection()] -#[API\Post(processor: GatewayCheckoutProcessor::class)] +#[API\Post(processor: GatewayCheckoutProcessor::class, validationContext: ['groups' => ['default', 'postValidation']])] #[API\Get()] #[API\Patch(input: GatewayCheckoutUpdateDto::class, processor: GatewayCheckoutUpdateProcessor::class)] #[API\ApiFilter(filterClass: SearchFilter::class, properties: ['origin' => 'exact', 'charges.target' => 'exact'])] @@ -76,10 +77,18 @@ class GatewayCheckout * An external identifier provided by the Gateway for the payment.\ * Required when a GatewayCheckout is completed. */ + #[Assert\NotBlank(['groups' => ['postValidation']])] #[API\ApiFilter(SearchFilter::class)] #[ORM\Column(length: 255)] private ?string $gatewayReference = null; + /** + * The URL where the checkout with the Gateway is available. + */ + #[API\ApiProperty(writable: false)] + #[ORM\Column(type: Types::TEXT)] + private ?string $checkoutUrl = null; + /** * GatewayCheckout was migrated from an invest record in Goteo v3 platform. */ @@ -183,6 +192,18 @@ class GatewayCheckout return $this; } + public function getCheckoutUrl(): ?string + { + return $this->checkoutUrl; + } + + public function setCheckoutUrl(string $checkoutUrl): static + { + $this->checkoutUrl = $checkoutUrl; + + return $this; + } + public function isMigrated(): ?bool { return $this->migrated; From 17074ae56e4801f3048f8e021de266d41f279356 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Wed, 22 May 2024 12:21:48 +0200 Subject: [PATCH 52/58] Update GatewayInterface to expose create method for GatewayCheckouts --- src/Library/Economy/Payment/GatewayInterface.php | 4 ++-- src/State/GatewayCheckoutProcessor.php | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Library/Economy/Payment/GatewayInterface.php b/src/Library/Economy/Payment/GatewayInterface.php index 8e26a34..d579da1 100644 --- a/src/Library/Economy/Payment/GatewayInterface.php +++ b/src/Library/Economy/Payment/GatewayInterface.php @@ -12,11 +12,11 @@ interface GatewayInterface public static function getName(): string; /** - * When processing a GatewayCheckout a Gateway must validate it using it's own means. + * Connects with the payment gateway and creates a checkout session to process payment. * * Gateways are trusted to have secured the funds in the GatewayCheckout. * @param GatewayCheckout $checkout * @return GatewayCheckout */ - public function process(GatewayCheckout $checkout): GatewayCheckout; + public function create(GatewayCheckout $checkout): GatewayCheckout; } diff --git a/src/State/GatewayCheckoutProcessor.php b/src/State/GatewayCheckoutProcessor.php index 006c97a..d907d8f 100644 --- a/src/State/GatewayCheckoutProcessor.php +++ b/src/State/GatewayCheckoutProcessor.php @@ -26,8 +26,6 @@ class GatewayCheckoutProcessor implements ProcessorInterface $this->entityManager->persist($data); $this->entityManager->flush(); - $gateway->process($data); - - return $data; + return $gateway->create($data); } } From 0004f0e89d50dbd5e3acd4a236dc5ba08064e5c4 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Thu, 23 May 2024 13:27:26 +0200 Subject: [PATCH 53/58] Add TestCase for EuropeanCentralBankExchange --- .../EuropeanCentralBankExchangeTest.php | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 tests/Library/Economy/Currency/EuropeanCentralBankExchangeTest.php diff --git a/tests/Library/Economy/Currency/EuropeanCentralBankExchangeTest.php b/tests/Library/Economy/Currency/EuropeanCentralBankExchangeTest.php new file mode 100644 index 0000000..424adaf --- /dev/null +++ b/tests/Library/Economy/Currency/EuropeanCentralBankExchangeTest.php @@ -0,0 +1,35 @@ +getData(); + + $this->assertIsArray($exchangeData); + $this->assertArrayHasKey('Cube', $exchangeData); + $this->assertArrayHasKey('@attributes', $exchangeData); + } + + public function testStoresDataInApcuCache() + { + $exchange = new EuropeanCentralBankExchange; + // Data should be available on instantiation + + $data = \apcu_fetch($exchange::ECB_DATA); + + $this->assertNotFalse($data); + + $exchangeData = \json_decode($data, true); + + $this->assertIsArray($exchangeData); + $this->assertArrayHasKey('Cube', $exchangeData); + $this->assertArrayHasKey('@attributes', $exchangeData); + } +} From 27be8dee9bb25797f271234e50a2ac6bc34a5d1b Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Thu, 23 May 2024 13:27:54 +0200 Subject: [PATCH 54/58] Update EuropeanCentralBankExchange to use apcu cache and minor patches --- .../Currency/EuropeanCentralBankExchange.php | 37 +++++++++++++++++-- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/src/Library/Economy/Currency/EuropeanCentralBankExchange.php b/src/Library/Economy/Currency/EuropeanCentralBankExchange.php index f51e298..9d2e2fc 100644 --- a/src/Library/Economy/Currency/EuropeanCentralBankExchange.php +++ b/src/Library/Economy/Currency/EuropeanCentralBankExchange.php @@ -25,7 +25,7 @@ class EuropeanCentralBankExchange implements ExchangeInterface public function __construct() { - $data = json_decode(json_encode(\simplexml_load_file(self::ECB_DATA)), true)['Cube']['Cube']; + $data = $this->getData(); $this->date = \DateTime::createFromFormat( \DateTimeInterface::RFC3339, @@ -33,7 +33,6 @@ class EuropeanCentralBankExchange implements ExchangeInterface new \DateTimeZone('CET') ); - $provider = new ConfigurableProvider(); foreach ($data['Cube'] as $item) { $rate = $item['@attributes']; @@ -44,6 +43,38 @@ class EuropeanCentralBankExchange implements ExchangeInterface $this->converter = new CurrencyConverter($this->provider); } + private function getDataLatest(): array + { + $data = \simplexml_load_file(self::ECB_DATA); + if (!$data) { + throw new \Exception("Could not retrieve XML data"); + } + + return \json_decode(\json_encode($data), true)['Cube']['Cube']; + } + + private function getDataCached(): array + { + $data = \apcu_fetch(self::ECB_DATA); + if (!$data) { + throw new \Exception("Could not retrieve cached data"); + } + + return \json_decode($data, true); + } + + public function getData(): array + { + try { + return $this->getDataCached(); + } catch (\Exception $e) { + $data = $this->getDataLatest(); + + \apcu_store(self::ECB_DATA, json_encode($data)); + return $this->getData(); + } + } + public function getName(): string { return 'european_central_bank'; @@ -71,7 +102,7 @@ class EuropeanCentralBankExchange implements ExchangeInterface public function getConversionRate(string $source, string $target): float { - return $this->provider->getExchangeRate($source, $target); + return $this->provider->getExchangeRate($source, $target)->toFloat(); } public function getConversionDate(string $source, string $target): \DateTimeInterface From 7b4bf0466efb9ced7b308a6425a54dd129bd01c0 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Thu, 23 May 2024 14:08:27 +0200 Subject: [PATCH 55/58] Add creation and updation times to pumped projects --- src/Library/Benzina/Pump/ProjectsPump.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Library/Benzina/Pump/ProjectsPump.php b/src/Library/Benzina/Pump/ProjectsPump.php index f0c54b0..0f435b7 100644 --- a/src/Library/Benzina/Pump/ProjectsPump.php +++ b/src/Library/Benzina/Pump/ProjectsPump.php @@ -131,6 +131,8 @@ class ProjectsPump implements PumpInterface $project->setStatus($this->getProjectStatus($record['status'])); $project->setMigrated(true); $project->setMigratedReference($record['id']); + $project->setCreatedAt(new \DateTime($record['created'])); + $project->setUpdatedAt(new \DateTime()); $accounting = $this->getAccounting($record); $accounting->setProject($project); From ecea97e9a2663340a2072b61d7b57bf6bd8e8182 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Thu, 23 May 2024 14:19:19 +0200 Subject: [PATCH 56/58] Add default url to pumping checkouts --- src/Library/Benzina/Pump/InvestsPump.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Library/Benzina/Pump/InvestsPump.php b/src/Library/Benzina/Pump/InvestsPump.php index 643aaaf..9858a59 100644 --- a/src/Library/Benzina/Pump/InvestsPump.php +++ b/src/Library/Benzina/Pump/InvestsPump.php @@ -26,6 +26,7 @@ class InvestsPump implements PumpInterface use ProgressivePumpTrait; private const PLATFORM_TIPJAR_NAME = 'platform'; + private const CHECKOUT_URL_DEFAULT = 'https://www.goteo.org/invest'; private const MAX_INT = 2147483647; @@ -112,6 +113,7 @@ class InvestsPump implements PumpInterface $checkout->setStatus($this->getCheckoutStatus($record)); $checkout->setGateway($this->getCheckoutGateway($record)); $checkout->setGatewayReference($this->getCheckoutReference($record)); + $checkout->setCheckoutUrl(self::CHECKOUT_URL_DEFAULT); $checkout->setMigrated(true); $checkout->setMigratedReference($record['id']); $checkout->setMetadata([ From 649f141b2368c45fb82e241c0bc8bdee7ab654e2 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Thu, 23 May 2024 14:29:42 +0200 Subject: [PATCH 57/58] Move pump traits to it's own namespace --- src/Library/Benzina/Pump/InvestsPump.php | 2 ++ src/Library/Benzina/Pump/ProjectsPump.php | 2 ++ src/Library/Benzina/Pump/{ => Trait}/ArrayPumpTrait.php | 2 +- src/Library/Benzina/Pump/{ => Trait}/ProgressivePumpTrait.php | 2 +- src/Library/Benzina/Pump/UsersPump.php | 2 ++ 5 files changed, 8 insertions(+), 2 deletions(-) rename src/Library/Benzina/Pump/{ => Trait}/ArrayPumpTrait.php (91%) rename src/Library/Benzina/Pump/{ => Trait}/ProgressivePumpTrait.php (97%) diff --git a/src/Library/Benzina/Pump/InvestsPump.php b/src/Library/Benzina/Pump/InvestsPump.php index 9858a59..be70f8b 100644 --- a/src/Library/Benzina/Pump/InvestsPump.php +++ b/src/Library/Benzina/Pump/InvestsPump.php @@ -9,6 +9,8 @@ use App\Entity\GatewayCheckout; use App\Entity\GatewayCheckoutStatus; use App\Entity\Money; use App\Entity\Tipjar; +use App\Library\Benzina\Pump\Trait\ArrayPumpTrait; +use App\Library\Benzina\Pump\Trait\ProgressivePumpTrait; use App\Library\Economy\Payment\CashGateway; use App\Library\Economy\Payment\CecaGateway; use App\Library\Economy\Payment\DropGateway; diff --git a/src/Library/Benzina/Pump/ProjectsPump.php b/src/Library/Benzina/Pump/ProjectsPump.php index 0f435b7..1a6e4cf 100644 --- a/src/Library/Benzina/Pump/ProjectsPump.php +++ b/src/Library/Benzina/Pump/ProjectsPump.php @@ -6,6 +6,8 @@ use App\Entity\Accounting; use App\Entity\Project; use App\Entity\ProjectStatus; use App\Entity\User; +use App\Library\Benzina\Pump\Trait\ArrayPumpTrait; +use App\Library\Benzina\Pump\Trait\ProgressivePumpTrait; use App\Repository\UserRepository; use Doctrine\ORM\EntityManagerInterface; diff --git a/src/Library/Benzina/Pump/ArrayPumpTrait.php b/src/Library/Benzina/Pump/Trait/ArrayPumpTrait.php similarity index 91% rename from src/Library/Benzina/Pump/ArrayPumpTrait.php rename to src/Library/Benzina/Pump/Trait/ArrayPumpTrait.php index 1903820..64d5c8b 100644 --- a/src/Library/Benzina/Pump/ArrayPumpTrait.php +++ b/src/Library/Benzina/Pump/Trait/ArrayPumpTrait.php @@ -1,6 +1,6 @@ Date: Thu, 23 May 2024 14:30:47 +0200 Subject: [PATCH 58/58] Rename InvestsPump to CheckoutsPump --- .../Benzina/Pump/{InvestsPump.php => CheckoutsPump.php} | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) rename src/Library/Benzina/Pump/{InvestsPump.php => CheckoutsPump.php} (98%) diff --git a/src/Library/Benzina/Pump/InvestsPump.php b/src/Library/Benzina/Pump/CheckoutsPump.php similarity index 98% rename from src/Library/Benzina/Pump/InvestsPump.php rename to src/Library/Benzina/Pump/CheckoutsPump.php index be70f8b..e113865 100644 --- a/src/Library/Benzina/Pump/InvestsPump.php +++ b/src/Library/Benzina/Pump/CheckoutsPump.php @@ -22,7 +22,7 @@ use App\Repository\TipjarRepository; use App\Repository\UserRepository; use Doctrine\ORM\EntityManagerInterface; -class InvestsPump implements PumpInterface +class CheckoutsPump implements PumpInterface { use ArrayPumpTrait; use ProgressivePumpTrait; @@ -145,10 +145,6 @@ class InvestsPump implements PumpInterface $checkout->addCharge($charge); $this->entityManager->persist($checkout); - - if ($checkout->getStatus() === GatewayCheckoutStatus::Pending) { - continue; - } } $this->entityManager->flush();