-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
c095d6f
commit 91d4954
Showing
2 changed files
with
345 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
<?php | ||
|
||
namespace Playbloom\Payment\Operation; | ||
|
||
use Playbloom\Payment\Operation\PaymentInterface; | ||
use Playbloom\Payment\Operation\OperationId; | ||
use Playbloom\Payment\CardInterface; | ||
use Playbloom\Payment\Exception\OperationException; | ||
use Money\Money; | ||
|
||
/** | ||
* Card payment operation | ||
* | ||
* @author Ludovic Fleury <ludo.fleury@gmail.com> | ||
*/ | ||
class Payment implements PaymentInterface | ||
{ | ||
/** | ||
* OperationId | ||
* | ||
* @var Playbloom\Payment\OperationId | ||
*/ | ||
private $id; | ||
|
||
/** | ||
* @var string | ||
*/ | ||
private $label; | ||
|
||
/** | ||
* @var Playbloom\Payment\CardInterface | ||
*/ | ||
private $card; | ||
|
||
/** | ||
* @var Money\Money | ||
*/ | ||
private $amount; | ||
|
||
/** | ||
* @var Playbloom\Payment\Operation\RefundInterface[] | ||
*/ | ||
public $refunds; | ||
|
||
/** | ||
* Constructor | ||
* | ||
* @param string $id | ||
* @param string $label | ||
* @param CardInterface $card | ||
* @param Money $amount | ||
*/ | ||
public function __construct(OperationId $id, $label, Money $amount, CardInterface $card) | ||
{ | ||
$this->id = $id; | ||
$this->label = $label; | ||
$this->amount = $amount; | ||
$this->card = $card; | ||
$this->refunds = []; | ||
} | ||
|
||
/** | ||
* Get the string representation | ||
* | ||
* @return string | ||
*/ | ||
public function __toString() | ||
{ | ||
return sprintf( | ||
'Payment "%s" for "%s": %s %s', | ||
$this->id, | ||
$this->label, | ||
$this->getAmount()->getAmount() / 100, | ||
$this->getAmount()->getCurrency() | ||
); | ||
} | ||
|
||
/** | ||
* Get the unique transaction identifier | ||
* | ||
* @return Playbloom\Payment\OperationId | ||
*/ | ||
public function getId() | ||
{ | ||
return $this->id; | ||
} | ||
|
||
/** | ||
* Get the related Card | ||
* | ||
* @return Playbloom\Payment\CardInterface | ||
*/ | ||
public function getCard() | ||
{ | ||
return $this->card; | ||
} | ||
|
||
/** | ||
* Get the label | ||
* | ||
* @return string | ||
*/ | ||
public function getLabel() | ||
{ | ||
return $this->label; | ||
} | ||
|
||
/** | ||
* Get the amount | ||
* | ||
* @return Money\Money | ||
*/ | ||
public function getAmount() | ||
{ | ||
return $this->amount; | ||
} | ||
|
||
/** | ||
* Check whether or not the Payment has been refunded | ||
* | ||
* @return boolean | ||
*/ | ||
public function isRefunded() | ||
{ | ||
return $this->getRefundedAmount()->isPositive(); | ||
} | ||
|
||
/** | ||
* Get the refundable money | ||
* | ||
* @return Money\Money | ||
*/ | ||
public function getRefundableAmount() | ||
{ | ||
$amount = $this->getAmount()->subtract($this->getRefundedAmount()); | ||
|
||
return $amount->isNegative() ? new Money(0, $this->amount->getCurrency()) : $amount; | ||
} | ||
|
||
/** | ||
* Get the refunded money | ||
* | ||
* @return Money\Money | ||
*/ | ||
public function getRefundedAmount() | ||
{ | ||
$refunded = new Money(0, $this->amount->getCurrency()); | ||
|
||
foreach ($this->refunds as $refund) { | ||
$refunded = $refunded->add($refund->getAmount()); | ||
} | ||
|
||
return $refunded; | ||
} | ||
|
||
/** | ||
* Refund | ||
* | ||
* @param RefundInterface $refund | ||
* | ||
* @throws Playbloom\Payment\Exception\OperationException | ||
* | ||
* @return static | ||
*/ | ||
public function refund(RefundInterface $refund) | ||
{ | ||
if ($refund->getPayment() !== $this) { | ||
throw new OperationException(sprintf('Unable to refund [%s] with [%s]: operations are not related', $this, $refund)); | ||
} | ||
|
||
$hash = spl_object_hash($refund); | ||
if (isset($this->refunds[$hash])) { | ||
throw new OperationException(sprintf('Unable to refund [%s] with [%s]: operations are already related', $this, $refund)); | ||
} | ||
|
||
$this->refunds[$hash] = $refund; | ||
|
||
return $this; | ||
} | ||
} |
165 changes: 165 additions & 0 deletions
165
tests/Playbloom/Payment/Tests/Units/Operation/Payment.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
<?php | ||
|
||
namespace Playbloom\Payment\Tests\Units\Operation; | ||
|
||
use Playbloom\Payment\Tests\Units\Operation\AbstractOperation as OperationTest; | ||
use Playbloom\Payment\Operation; | ||
use Playbloom\Payment\Operation\OperationId as Id; | ||
use Money\Money; | ||
use Mock; | ||
|
||
class Payment extends OperationTest | ||
{ | ||
/** | ||
* Data provider required by OperationTest | ||
*/ | ||
public function provideTestedInstance() | ||
{ | ||
$toString = 'Payment "id" for "saving the world": 1337.42 EUR'; | ||
$id = new Id('id'); | ||
$label = 'saving the world'; | ||
$amount = Money::EUR(133742); | ||
$card = new Mock\Playbloom\Payment\CardInterface(); | ||
|
||
return [ | ||
[ | ||
new Operation\Payment($id, $label, $amount, $card), | ||
$toString, | ||
$id, | ||
$label, | ||
$amount, | ||
$card | ||
] | ||
]; | ||
} | ||
|
||
public function testIsNotRefunded() | ||
{ | ||
$payment = $this->createInstance(); | ||
|
||
$this | ||
->boolean($payment->isRefunded()) | ||
->isFalse() | ||
; | ||
} | ||
|
||
public function testRefundUnrelatedOperation() | ||
{ | ||
$payment = $this->createInstance(['label' => 'original']); | ||
$anotherPayment = $this->createInstance(['label' => 'another']); | ||
|
||
$refund = new Mock\Playbloom\Payment\Operation\RefundInterface; | ||
$refund->getMockController()->getPayment = $anotherPayment; | ||
$refund->getMockController()->getAmount = Money::EUR(1); | ||
$refund->getMockController()->__toString = 'Refund for another payment'; | ||
|
||
$this | ||
->exception(function () use ($payment, $refund) { | ||
$payment->refund($refund); | ||
}) | ||
->isInstanceOf('Playbloom\\Payment\\Exception\\OperationException') | ||
->hasMessage(sprintf('Unable to refund [%s] with [Refund for another payment]: operations are not related', $payment->__toString())) | ||
; | ||
} | ||
|
||
public function testRefundAlreadyRelatedOperation() | ||
{ | ||
$payment = $this->createInstance(['label' => 'original']); | ||
|
||
$refund = new Mock\Playbloom\Payment\Operation\RefundInterface; | ||
$refund->getMockController()->getPayment = $payment; | ||
$refund->getMockController()->getAmount = Money::EUR(1); | ||
$refund->getMockController()->__toString = 'Refund for original payment'; | ||
$payment->refund($refund); | ||
|
||
$this | ||
->exception(function () use ($payment, $refund) { | ||
$payment->refund($refund); | ||
}) | ||
->isInstanceOf('Playbloom\\Payment\\Exception\\OperationException') | ||
->hasMessage(sprintf('Unable to refund [%s] with [Refund for original payment]: operations are already related', $payment->__toString())) | ||
; | ||
} | ||
|
||
public function testIsRefunded() | ||
{ | ||
$payment = $this->createInstance(['amount' => Money::EUR(10000)]); | ||
|
||
$refund = new Mock\Playbloom\Payment\Operation\RefundInterface; | ||
$refund->getMockController()->getPayment = $payment; | ||
$refund->getMockController()->getAmount = Money::EUR(1); | ||
|
||
$payment->refund($refund); | ||
|
||
$this | ||
->boolean($payment->isRefunded()) | ||
->isTrue() | ||
; | ||
} | ||
|
||
public function testGetRefundableAmount() | ||
{ | ||
$payment = $this->createInstance(['amount' => Money::EUR(10000)]); | ||
|
||
$refund = new Mock\Playbloom\Payment\Operation\RefundInterface; | ||
$refund->getMockController()->getPayment = $payment; | ||
$refund->getMockController()->getAmount = Money::EUR(1000); | ||
$payment->refund($refund); | ||
$this | ||
->object($payment->getRefundableAmount()) | ||
->isInstanceOf('Money\\Money') | ||
->boolean($payment->getRefundableAmount()->equals(Money::EUR(9000))) | ||
->isTrue() | ||
; | ||
|
||
$refund = new Mock\Playbloom\Payment\Operation\RefundInterface; | ||
$refund->getMockController()->getPayment = $payment; | ||
$refund->getMockController()->getAmount = Money::EUR(9000); | ||
$payment->refund($refund); | ||
$this | ||
->object($payment->getRefundableAmount()) | ||
->isInstanceOf('Money\\Money') | ||
->boolean($payment->getRefundableAmount()->equals(Money::EUR(0))) | ||
->isTrue() | ||
; | ||
|
||
$refund = new Mock\Playbloom\Payment\Operation\RefundInterface; | ||
$refund->getMockController()->getPayment = $payment; | ||
$refund->getMockController()->getAmount = Money::EUR(500); | ||
$payment->refund($refund); | ||
$this | ||
->object($payment->getRefundableAmount()) | ||
->isInstanceOf('Money\\Money') | ||
->boolean($payment->getRefundableAmount()->equals(Money::EUR(0))) | ||
->isTrue() | ||
; | ||
} | ||
|
||
public function testGetRefundedAmount() | ||
{ | ||
$payment = $this->createInstance(['amount' => Money::EUR(10000)]); | ||
|
||
$refund = new Mock\Playbloom\Payment\Operation\RefundInterface; | ||
$refund->getMockController()->getPayment = $payment; | ||
$refund->getMockController()->getAmount = Money::EUR(2000); | ||
|
||
$payment->refund($refund); | ||
|
||
$this | ||
->object($payment->getRefundableAmount()) | ||
->isInstanceOf('Money\\Money') | ||
->boolean($payment->getRefundedAmount()->equals(Money::EUR(2000))) | ||
->isTrue() | ||
; | ||
} | ||
|
||
private function createInstance(array $arguments = []) | ||
{ | ||
$id = isset($arguments['id']) ? $arguments['id'] : new Id('id'); | ||
$label = isset($arguments['label']) ? $arguments['label'] : 'saving the world'; | ||
$amount = isset($arguments['amount']) ? $arguments['amount'] : Money::EUR(133742); | ||
$card = isset($arguments['card']) ? $arguments['card'] : new Mock\Playbloom\Payment\CardInterface(); | ||
|
||
return new Operation\Payment($id, $label, $amount, $card); | ||
} | ||
} |