...
 
......@@ -153,7 +153,7 @@ class PaymentsDelegate
->setTimeCreated(time());
try {
$this->onchainPayments->register($payment);
$this->onchainPayments->record($payment);
} catch (Exception $e) {
throw new CampaignException("Error registering payment: {$e->getMessage()}");
}
......
<?php
/**
* Onchain
* @author edgebal
*/
namespace Minds\Core\Boost\Campaigns\Payments;
......@@ -18,13 +14,10 @@ class Onchain
{
/** @var Config */
protected $config;
/** @var Ethereum */
protected $eth;
/** @var Repository */
protected $repository;
/** @var TransactionsManager */
protected $txManager;
......@@ -45,7 +38,7 @@ class Onchain
* @return bool
* @throws Exception
*/
public function register(Payment $payment)
public function record(Payment $payment)
{
if ($this->txManager->exists($payment->getTx())) {
throw new Exception('Payment transaction already exists');
......@@ -103,8 +96,7 @@ class Onchain
]),
]);
$payment
->setTx($refundTx);
$payment->setTx($refundTx);
$transaction = new Transaction();
$transaction
......
<?php
/**
* Payment
* @author edgebal
*/
namespace Minds\Core\Boost\Campaigns\Payments;
......
<?php
/**
* Repository
* @author edgebal
*/
namespace Minds\Core\Boost\Campaigns\Payments;
......@@ -73,9 +69,9 @@ class Repository
try {
// TODO: Use Cassandra Scroll for getList
$rows = $this->db->request($prepared);
$rows = $this->db->request($prepared) ?: [];
foreach ($rows ?: [] as $row) {
foreach ($rows as $row) {
$payment = new Payment();
$payment
->setOwnerGuid($row['owner_guid']->toInt())
......
......@@ -76,7 +76,7 @@ class PaymentsDelegateSpec extends ObjectBehavior
$campaign->getBudgetType()->shouldBeCalled()->willReturn('tokens');
$campaign->getOwnerGuid()->shouldBeCalled()->willReturn(12345);
$campaign->getGuid()->shouldBeCalled()->willReturn(23456);
$this->onchainPayments->register(Argument::type(Payment::class))->willThrow(\Exception::class);
$this->onchainPayments->record(Argument::type(Payment::class))->willThrow(\Exception::class);
$this->shouldThrow(CampaignException::class)->duringPay($campaign, $payload);
}
......@@ -90,7 +90,7 @@ class PaymentsDelegateSpec extends ObjectBehavior
$campaign->getBudgetType()->shouldBeCalled()->willReturn('tokens');
$campaign->getOwnerGuid()->shouldBeCalled()->willReturn(12345);
$campaign->getGuid()->shouldBeCalled()->willReturn(23456);
$this->onchainPayments->register(Argument::type(Payment::class))->shouldBeCalled();
$this->onchainPayments->record(Argument::type(Payment::class))->shouldBeCalled();
$campaign->pushPayment(Argument::type(Payment::class))->shouldBeCalled();
$this->pay($campaign, $payload);
}
......
......@@ -2,14 +2,162 @@
namespace Spec\Minds\Core\Boost\Campaigns\Payments;
use Minds\Core\Blockchain\Services\Ethereum;
use Minds\Core\Blockchain\Transactions\Manager as TransactionsManager;
use Minds\Core\Blockchain\Transactions\Transaction;
use Minds\Core\Boost\Campaigns\Payments\OnChain;
use Minds\Core\Boost\Campaigns\Payments\Payment;
use Minds\Core\Boost\Campaigns\Payments\Repository;
use Minds\Core\Config;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class OnChainSpec extends ObjectBehavior
{
/** @var Config */
protected $config;
/** @var Ethereum */
protected $eth;
/** @var Repository */
protected $repository;
/** @var TransactionsManager */
protected $txManager;
public function let(Config $config, Ethereum $eth, Repository $repository, TransactionsManager $txManager)
{
$this->beConstructedWith($config, $eth, $repository, $txManager);
$this->config = $config;
$this->eth = $eth;
$this->repository = $repository;
$this->txManager = $txManager;
}
public function it_is_initializable()
{
$this->shouldHaveType(OnChain::class);
}
public function it_should_throw_an_exception_if_transaction_already_exists(Payment $payment)
{
$tx = 'some_tx_value';
$payment->getTx()->shouldBeCalled()->willReturn($tx);
$this->txManager->exists($tx)->shouldBeCalled()->willReturn(true);
$this->shouldThrow(\Exception::class)->duringRecord($payment);
}
public function it_should_record_a_transaction(Payment $payment)
{
$tx = 'some_tx_value';
$payment->getTx()->shouldBeCalled()->willReturn($tx);
$this->txManager->exists($tx)->shouldBeCalled()->willReturn(false);
$payment->getAmount()->shouldBeCalled()->willReturn(1);
$payment->getSource()->shouldBeCalled()->willReturn('0x1234');
$payment->getTimeCreated()->shouldBeCalled()->willReturn(1570191034);
$payment->getOwnerGuid()->shouldBeCalled()->willReturn();
$payment->export()->shouldBeCalled()->willReturn([]);
$this->repository->add($payment)->shouldBeCalled();
$this->txManager->add(Argument::type(Transaction::class))->shouldBeCalled();
$this->record($payment);
}
public function it_should_throw_an_exception_if_invalid_token_contract_address_on_refund(Payment $payment)
{
$blockchainConfig = [
'token_address' => null,
'contracts' => [
'boost_campaigns' => [
'wallet_address' => '0x123abc',
'wallet_pkey' => '0x123abc'
]
]
];
$this->config->get('blockchain')->willReturn($blockchainConfig);
$this->shouldThrow(\Exception::class)->duringRefund($payment);
}
public function it_should_throw_an_exception_if_invalid_wallet_address_on_refund(Payment $payment)
{
$blockchainConfig = [
'token_address' => '0x123abc',
'contracts' => [
'boost_campaigns' => [
'wallet_address' => null,
'wallet_pkey' => '0x123abc'
]
]
];
$this->config->get('blockchain')->willReturn($blockchainConfig);
$this->shouldThrow(\Exception::class)->duringRefund($payment);
}
public function it_should_throw_an_exception_if_invalid_destination_wallet_address_on_refund(Payment $payment)
{
$blockchainConfig = [
'token_address' => '0x123abc',
'contracts' => [
'boost_campaigns' => [
'wallet_address' => '0x123abc',
'wallet_pkey' => '0x123abc'
]
]
];
$this->config->get('blockchain')->willReturn($blockchainConfig);
$payment->getSource()->shouldBeCalled()->willReturn(null);
$this->shouldThrow(\Exception::class)->duringRefund($payment);
}
public function it_should_throw_an_exception_if_positive_amount_on_refund(Payment $payment)
{
$blockchainConfig = [
'token_address' => '0x123abc',
'contracts' => [
'boost_campaigns' => [
'wallet_address' => '0x123abc',
'wallet_pkey' => '0x123abc'
]
]
];
$this->config->get('blockchain')->willReturn($blockchainConfig);
$payment->getSource()->shouldBeCalled()->willReturn('0xabc123');
$payment->getAmount()->shouldBeCalled()->willReturn(2);
$this->shouldThrow(\Exception::class)->duringRefund($payment);
}
public function it_should_record_a_refund(Payment $payment)
{
$blockchainConfig = [
'token_address' => '0x123abc',
'contracts' => [
'boost_campaigns' => [
'wallet_address' => '0x123abc',
'wallet_pkey' => '0x123abc'
]
]
];
$this->config->get('blockchain')->willReturn($blockchainConfig);
$payment->getSource()->shouldBeCalled()->willReturn('0xabc123');
$payment->getAmount()->shouldBeCalled()->willReturn(-2);
$this->eth->sendRawTransaction(Argument::type('string'), Argument::type('array'))->willReturn('0xdef456');
$this->eth->encodeContractMethod('transfer(address,uint256)', [
'0xabc123', '0x1bc16d674ec80000'
])->shouldBeCalled()->willReturn('');
$payment->setTx('0xdef456')->shouldBeCalled();
$payment->getTx()->shouldBeCalled()->willReturn('0xdef456');
$payment->getTimeCreated()->shouldBeCalled()->willReturn(1570191034);
$payment->getOwnerGuid()->shouldBeCalled()->willReturn(1234);
$payment->export()->shouldBeCalled()->willReturn([]);
$this->repository->add($payment)->shouldBeCalled();
$this->txManager->add(Argument::type(Transaction::class), false)->shouldBeCalled();
$this->refund($payment);
}
}
......@@ -12,4 +12,23 @@ class PaymentSpec extends ObjectBehavior
{
$this->shouldHaveType(Payment::class);
}
public function it_should_export()
{
$this->setOwnerGuid(12345)
->setCampaignGuid(67890)
->setTx('0x123abc')
->setSource('0x456def')
->setAmount(2)
->setTimeCreated(1570224115);
$this->export()->shouldReturn([
'owner_guid' => '12345',
'campaign_guid' => '67890',
'tx' => '0x123abc',
'source' => '0x456def',
'amount' => 2,
'time_created' => 1570224115
]);
}
}
......@@ -2,14 +2,65 @@
namespace Spec\Minds\Core\Boost\Campaigns\Payments;
use Cassandra\Bigint;
use Cassandra\Decimal;
use Cassandra\Timestamp;
use Minds\Common\Repository\Response;
use Minds\Core\Boost\Campaigns\Payments\Payment;
use Minds\Core\Boost\Campaigns\Payments\Repository;
use Minds\Core\Data\Cassandra\Client as CassandraClient;
use Minds\Core\Data\Cassandra\Prepared\Custom;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class RepositorySpec extends ObjectBehavior
{
/** @var CassandraClient */
protected $db;
public function let(CassandraClient $db)
{
$this->beConstructedWith($db);
$this->db = $db;
}
public function it_is_initializable()
{
$this->shouldHaveType(Repository::class);
}
public function it_should_return_a_list_of_payments()
{
$paymentsData = [
[
'owner_guid' => new Bigint('12345'),
'campaign_guid' => new Bigint('67890'),
'tx' => '0x123abc',
'source' => '0x456def',
'amount' => new Decimal(2),
'time_created' => new Timestamp(1570224115)
]
];
$this->db->request(Argument::type(Custom::class))->shouldBeCalled()->willReturn($paymentsData);
$this->getList([
'owner_guid' => 12345,
'campaign_guid' => 67890,
'tx' => '0x123abc'
])[0]->shouldBeAnInstanceOf(Payment::class);
}
public function it_should_add_a_payment(Payment $payment)
{
$payment->getOwnerGuid()->shouldBeCalled()->willReturn(12345);
$payment->getCampaignGuid()->shouldBeCalled()->willReturn(67890);
$payment->getTx()->shouldBeCalled()->willReturn('0x123abc');
$payment->getSource()->shouldBeCalled()->willReturn('0x456def');
$payment->getAmount()->shouldBeCalled()->willReturn(2);
$payment->getTimeCreated()->shouldBeCalled()->willReturn(1570224115);
$this->db->request(Argument::type(Custom::class), true)->shouldBeCalled()->willReturn(true);
$this->add($payment)->shouldReturn(true);
}
}