Commit eaacfe65 authored by Emiliano Balbuena's avatar Emiliano Balbuena

(wip): Payments

1 merge request!235WIP: Boost Campaigns (&24)
Pipeline #69479989 failed with stages
in 2 minutes and 54 seconds
......@@ -82,23 +82,31 @@ class campaigns implements Interfaces\Api
$campaign = new Campaign();
$campaign
->setName(trim($_POST['name'] ?? ''))
->setHashtags($_POST['hashtags'] ?? [])
->setStart((int) ($_POST['start'] ?? 0))
->setEnd((int) ($_POST['end'] ?? 0))
->setBudget((float) ($_POST['budget'] ?? 0));
if (!$isEditing) {
$campaign
->setType($_POST['type'] ?? '')
->setEntityUrns($_POST['entity_urns'] ?? [])
->setBudgetType($_POST['budget_type'] ?? '')
->setChecksum($_POST['checksum'] ?? '');
} else {
$campaign
->setUrn($urn);
}
$campaign
->setName(trim($_POST['name'] ?? ''))
->setHashtags($_POST['hashtags'] ?? [])
->setStart((int) ($_POST['start'] ?? 0))
->setEnd((int) ($_POST['end'] ?? 0))
->setBudget((float) ($_POST['budget'] ?? 0));
$payment = $_POST['payment'] ?? null;
if ($payment) {
$campaign
->pushPayment($payment);
}
/** @var Manager $manager */
$manager = Di::_()->get('Boost\Campaigns\Manager');
$manager->setActor(Session::getLoggedInUser());
......
......@@ -65,6 +65,7 @@ class Manager
'client_network' => $blockchainConfig['client_network'],
'wallet_address' => $blockchainConfig['wallet_address'],
'boost_wallet_address' => $blockchainConfig['contracts']['boost']['wallet_address'],
'boost_campaigns_wallet_address' => $blockchainConfig['contracts']['boost_campaigns']['wallet_address'],
'token_distribution_event_address' => $blockchainConfig['contracts']['token_sale_event']['contract_address'],
'rate' => $blockchainConfig['eth_rate'],
'plus_address' => $blockchainConfig['contracts']['wire']['plus_address'],
......
......@@ -37,6 +37,7 @@ use Minds\Traits\MagicAttributes;
* @method Campaign setBudget(string $budget)
* @method string getBudgetType()
* @method Campaign setBudgetType(string $budgetType)
* @method array getPayments()
* @method string getChecksum()
* @method Campaign setChecksum(string $checksum)
* @method int getImpressions()
......@@ -109,6 +110,9 @@ class Campaign implements JsonSerializable
/** @var string */
protected $budgetType;
/** @var array */
protected $payments = [];
/** @var string */
protected $checksum;
......@@ -159,6 +163,26 @@ class Campaign implements JsonSerializable
return $this;
}
/**
* @param array|string $payments
* @return $this
*/
public function setPayments($payments = [])
{
$this->payments = (is_string($payments) ? json_decode($payments, true) : $payments) ?: [];
return $this;
}
/**
* @param mixed $payment
* @return $this
*/
public function pushPayment($payment)
{
$this->payments[] = $payment;
return $this;
}
/**
* @return string
*/
......@@ -207,6 +231,7 @@ class Campaign implements JsonSerializable
'end' => $this->end,
'budget' => $this->budget,
'budget_type' => $this->budgetType,
'payments' => $this->payments,
'checksum' => $this->checksum,
'impressions' => $this->impressions,
'impressions_met' => $this->impressionsMet,
......
<?php
/**
* BudgetDelegate
* PaymentsDelegate
* @author edgebal
*/
......@@ -11,7 +11,7 @@ use Minds\Core\Boost\Campaigns\CampaignException;
use Minds\Core\Config;
use Minds\Core\Di\Di;
class BudgetDelegate
class PaymentsDelegate
{
/**
* @var Config
......@@ -19,7 +19,7 @@ class BudgetDelegate
protected $config;
/**
* BudgetDelegate constructor.
* PaymentsDelegate constructor.
* @param Config $config
*/
public function __construct(
......@@ -36,12 +36,9 @@ class BudgetDelegate
*/
public function onCreate(Campaign $campaign)
{
if (!$campaign->getBudget() || $campaign->getBudget() <= 0) {
throw new CampaignException('Campaign should have a budget');
}
$campaign->setBudgetType('tokens');
// NOTE: Do not mock
$this->validateBudget($campaign);
$campaign = $this->updateImpressionsByCpm($campaign);
// TODO: Validate offchain balance, or set as pending for onchain
......@@ -57,22 +54,14 @@ class BudgetDelegate
*/
public function onUpdate(Campaign $campaign, Campaign $campaignRef)
{
if (!$campaignRef->getBudget() || $campaignRef->getBudget() <= 0) {
throw new CampaignException('Campaign should have a budget');
}
// NOTE: Do not mock
$campaign
->setBudget($campaignRef->getBudget());
$this->validateBudget($campaignRef);
$campaign = $this->updateImpressionsByCpm($campaign);
// TODO: Validate balance, set as pending for onchain, refund if needed
// TODO: Ensure budget didn't go lower than impressions met threshold
$campaign = $this->updateImpressionsByCpm($campaign);
if (!$campaign->getImpressions()) {
throw new CampaignException('Impressions value cannot be 0');
}
return $campaign;
}
......@@ -94,7 +83,25 @@ class BudgetDelegate
* @return Campaign
* @throws CampaignException
*/
protected function updateImpressionsByCpm(Campaign $campaign)
public function validateBudget(Campaign $campaign)
{
if (!$campaign->getBudget() || $campaign->getBudget() <= 0) {
throw new CampaignException('Campaign should have a budget');
}
if (!in_array($campaign->getBudgetType(), ['tokens'])) {
throw new CampaignException("Invalid budget type: {$campaign->getBudgetType()}");
}
return $campaign;
}
/**
* @param Campaign $campaign
* @return Campaign
* @throws CampaignException
*/
public function updateImpressionsByCpm(Campaign $campaign)
{
$cpm = (float) $this->config->get('boost')['cpm'];
......@@ -107,6 +114,10 @@ class BudgetDelegate
$campaign
->setImpressions($impressions);
if (!$campaign->getImpressions()) {
throw new CampaignException('Impressions value cannot be 0');
}
return $campaign;
}
}
......@@ -178,14 +178,15 @@ class ElasticRepository
->setEnd((int) $doc['_source']['end'])
->setBudget((string) $doc['_source']['budget'])
->setBudgetType($doc['_source']['budget_type'])
->setPayments($doc['_source']['payments'])
->setChecksum($doc['_source']['checksum'])
->setImpressions((int) $doc['_source']['impressions'])
->setImpressionsMet($doc['_source']['impressions_met'])
->setCreatedTimestamp((int) $doc['_source']['@timestamp'])
->setReviewedTimestamp((int) $doc['_source']['@reviewed'])
->setRejectedTimestamp((int) $doc['_source']['@rejected'])
->setRevokedTimestamp((int) $doc['_source']['@revoked'])
->setCompletedTimestamp((int) $doc['_source']['@completed']);
->setCreatedTimestamp(((int) $doc['_source']['@timestamp']) ?: null)
->setReviewedTimestamp(((int) $doc['_source']['@reviewed']) ?: null)
->setRejectedTimestamp(((int) $doc['_source']['@rejected']) ?: null)
->setRevokedTimestamp(((int) $doc['_source']['@revoked']) ?: null)
->setCompletedTimestamp(((int) $doc['_source']['@completed']) ?: null);
$response[] = $campaign;
$offset = $campaign->getCreatedTimestamp();
......@@ -215,6 +216,7 @@ class ElasticRepository
'end' => (int) $campaign->getEnd(),
'budget' => $campaign->getBudget(),
'budget_type' => $campaign->getBudgetType(),
'payments' => json_encode($campaign->getPayments()),
'checksum' => $campaign->getChecksum(),
'impressions' => $campaign->getImpressions(),
'@timestamp' => $campaign->getCreatedTimestamp(),
......
......@@ -31,8 +31,8 @@ class Manager
/** @var Delegates\NormalizeHashtagsDelegate */
protected $normalizeHashtagsDelegate;
/** @var Delegates\BudgetDelegate */
protected $budgetDelegate;
/** @var Delegates\PaymentsDelegate */
protected $paymentsDelegate;
/** @var User */
protected $actor;
......@@ -45,7 +45,7 @@ class Manager
* @param Delegates\NormalizeDatesDelegate $normalizeDatesDelegate
* @param Delegates\NormalizeEntityUrnsDelegate $normalizeEntityUrnsDelegate
* @param Delegates\NormalizeHashtagsDelegate $normalizeHashtagsDelegate
* @param Delegates\BudgetDelegate $budgetDelegate
* @param Delegates\PaymentsDelegate $paymentsDelegate
*/
public function __construct(
$repository = null,
......@@ -54,7 +54,7 @@ class Manager
$normalizeDatesDelegate = null,
$normalizeEntityUrnsDelegate = null,
$normalizeHashtagsDelegate = null,
$budgetDelegate = null
$paymentsDelegate = null
)
{
$this->repository = $repository ?: new Repository();
......@@ -66,7 +66,7 @@ class Manager
$this->normalizeDatesDelegate = $normalizeDatesDelegate ?: new Delegates\NormalizeDatesDelegate();
$this->normalizeEntityUrnsDelegate = $normalizeEntityUrnsDelegate ?: new Delegates\NormalizeEntityUrnsDelegate();
$this->normalizeHashtagsDelegate = $normalizeHashtagsDelegate ?: new Delegates\NormalizeHashtagsDelegate();
$this->budgetDelegate = $budgetDelegate ?: new Delegates\BudgetDelegate();
$this->paymentsDelegate = $paymentsDelegate ?: new Delegates\PaymentsDelegate();
}
/**
......@@ -155,18 +155,21 @@ class Manager
throw new CampaignException('Invalid checksum value');
}
// Run delegates
// Run data delegates
$campaign = $this->normalizeDatesDelegate->onCreate($campaign);
$campaign = $this->normalizeEntityUrnsDelegate->onCreate($campaign);
$campaign = $this->normalizeHashtagsDelegate->onCreate($campaign);
$campaign = $this->budgetDelegate->onCreate($campaign); // Should be ALWAYS called after normalizing dates
// Add
$campaign
->setCreatedTimestamp(time() * 1000);
// Run payments delegate (should be ALWAYS called after normalizing dates)
$campaign = $this->paymentsDelegate->onCreate($campaign);
// Write
$this->repository->add($campaign);
......@@ -220,7 +223,10 @@ class Manager
$campaign = $this->normalizeDatesDelegate->onUpdate($campaign, $campaignRef);
$campaign = $this->normalizeHashtagsDelegate->onUpdate($campaign, $campaignRef);
$campaign = $this->budgetDelegate->onUpdate($campaign, $campaignRef); // Should be ALWAYS called after normalizing dates
// Run payments delegate (should be ALWAYS called after normalizing dates)
$campaign = $this->paymentsDelegate->onUpdate($campaign, $campaignRef);
// Write
......@@ -306,14 +312,16 @@ class Manager
$campaign
->setRevokedTimestamp(time() * 1000);
// Run payments delegate (should be ALWAYS called after normalizing dates)
$this->paymentsDelegate->onStateChange($campaign);
// Write
$this->repository->update($campaign);
$this->elasticRepository->update($campaign);
// Budget updates
$this->budgetDelegate->onStateChange($campaign);
//
return $campaign;
}
......@@ -349,18 +357,22 @@ class Manager
$campaign
->setRejectedTimestamp(time() * 1000);
// Run payments delegate (should be ALWAYS called after normalizing dates)
$this->paymentsDelegate->onStateChange($campaign);
// Write
$this->repository->update($campaign);
$this->elasticRepository->update($campaign);
// Budget update
$this->budgetDelegate->onStateChange($campaign);
//
return $campaign;
}
// TODO: Check for completion (on [analytics beacon | cron | delivery manager] -> queue to complete)
// TODO: (time() * 1000 >= $campaign->getEnd() || $campaign->getImpressionsMet() >= $campaign->getImpressions())
/**
* @param Campaign $campaignRef
* @return Campaign
......@@ -387,23 +399,19 @@ class Manager
throw new CampaignException('Campaign should be in [approved] state in order to complete it');
}
// Check for completion
if (time() * 1000 >= $campaign->getEnd() || $campaign->getImpressionsMet() >= $campaign->getImpressions()) {
// Update
// Update
$campaign
->setCompletedTimestamp(time() * 1000);
$campaign
->setCompletedTimestamp(time() * 1000);
// Write
// Run payments delegate (should be ALWAYS called after normalizing dates)
$this->repository->update($campaign);
$this->elasticRepository->update($campaign);
$this->paymentsDelegate->onStateChange($campaign);
// Budget update
// Write
$this->budgetDelegate->onStateChange($campaign);
}
$this->repository->update($campaign);
$this->elasticRepository->update($campaign);
return $campaign;
}
......
......@@ -63,6 +63,7 @@ class Repository
'end' => $campaign->getEnd(),
'budget' => $campaign->getBudget(),
'budget_type' => $campaign->getBudgetType(),
'payments' => $campaign->getPayments(),
'checksum' => $campaign->getChecksum(),
'impressions' => $campaign->getImpressions(),
'impressions_met' => $campaign->getImpressionsMet(),
......
......@@ -361,6 +361,10 @@ $CONFIG->set('blockchain', [
'wallet_address' => '0x4CDc1C1fd1A3F4DD63231afF8c16501BcC11Df95',
'wallet_pkey' => '',
],
'boost_campaigns' => [
'wallet_address' => '',
'wallet_pkey' => '',
],
],
'eth_rate' => 2000, //1 ETH = 2,000 TOKENS
......@@ -477,4 +481,4 @@ $CONFIG->set('features', [
'dark-mode' => true,
]);
$CONFIG->set('last_tos_update', 1);
\ No newline at end of file
$CONFIG->set('last_tos_update', 1);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment