minder change
This commit is contained in:
@@ -302,6 +302,10 @@ final class Router
|
||||
Http::json(['data' => $this->saveMinerOffer($projectKey, Http::input())], 201);
|
||||
}
|
||||
|
||||
if ($resource === 'offer-preview' && $method === 'POST') {
|
||||
Http::json(['data' => $this->offerPreview($projectKey, Http::input())]);
|
||||
}
|
||||
|
||||
if ($resource === 'purchased-miners' && $method === 'GET') {
|
||||
Http::json(['data' => $this->purchasedMiners($projectKey)]);
|
||||
}
|
||||
@@ -310,9 +314,9 @@ final class Router
|
||||
Http::json(['data' => $this->updatePurchasedMiner($projectKey, (int) $matches[1], Http::input())]);
|
||||
}
|
||||
|
||||
if (preg_match('~^miner-offers/(\d+)/purchase$~', $resource, $matches) && $method === 'POST') {
|
||||
if (preg_match('~^miner-offers/([^/]+)/purchase$~', $resource, $matches) && $method === 'POST') {
|
||||
$this->repository()->ensureProject($projectKey);
|
||||
Http::json(['data' => $this->purchaseMiner($projectKey, (int) $matches[1], Http::input())], 201);
|
||||
Http::json(['data' => $this->purchaseMiner($projectKey, rawurldecode((string) $matches[1]), Http::input())], 201);
|
||||
}
|
||||
|
||||
throw new ApiException('Ressource nicht gefunden.', 404, ['resource' => $resource, 'method' => $method]);
|
||||
@@ -1645,7 +1649,59 @@ final class Router
|
||||
|
||||
private function minerOffers(string $projectKey): array
|
||||
{
|
||||
return $this->repository()->listMinerOffers($projectKey);
|
||||
$settings = $this->settings($projectKey, [
|
||||
'cost_plans' => true,
|
||||
'currencies' => false,
|
||||
'payouts' => true,
|
||||
'wallet_withdrawals' => true,
|
||||
'miner_offers' => false,
|
||||
'purchased_miners' => true,
|
||||
]);
|
||||
$measurements = $this->measurements($projectKey, $settings);
|
||||
$summary = $this->analytics()->buildSummary($measurements, $settings, [], [
|
||||
'include_offer_scenarios' => true,
|
||||
'include_long_term_projection' => true,
|
||||
]);
|
||||
return is_array($summary['miner_offers'] ?? null) ? $summary['miner_offers'] : [];
|
||||
}
|
||||
|
||||
private function offerPreview(string $projectKey, array $input): array
|
||||
{
|
||||
$settings = $this->settings($projectKey, [
|
||||
'cost_plans' => true,
|
||||
'currencies' => false,
|
||||
'payouts' => true,
|
||||
'wallet_withdrawals' => true,
|
||||
'miner_offers' => false,
|
||||
'purchased_miners' => true,
|
||||
]);
|
||||
|
||||
$settings['crypto_base_price_amount'] = $this->optionalDecimal($input['crypto_base_price_amount'] ?? null);
|
||||
$settings['crypto_base_price_currency'] = $this->requiredCurrency($input['crypto_base_price_currency'] ?? 'USD', 'crypto_base_price_currency');
|
||||
$settings['fiat_base_price_amount'] = $this->optionalDecimal($input['fiat_base_price_amount'] ?? null);
|
||||
|
||||
if ($settings['crypto_base_price_amount'] !== null) {
|
||||
if ($settings['crypto_base_price_amount'] <= 0) {
|
||||
throw new ApiException('crypto_base_price_amount muss groesser als 0 sein.', 422, ['field' => 'crypto_base_price_amount']);
|
||||
}
|
||||
if (!in_array($settings['crypto_base_price_currency'], ['USD', $settings['crypto_currency']], true)) {
|
||||
throw new ApiException('crypto_base_price_currency muss USD oder die Projekt-Kryptowaehrung sein.', 422, ['field' => 'crypto_base_price_currency']);
|
||||
}
|
||||
}
|
||||
|
||||
if ($settings['fiat_base_price_amount'] !== null && $settings['fiat_base_price_amount'] <= 0) {
|
||||
throw new ApiException('fiat_base_price_amount muss groesser als 0 sein.', 422, ['field' => 'fiat_base_price_amount']);
|
||||
}
|
||||
|
||||
$measurements = $this->measurements($projectKey, $settings);
|
||||
$summary = $this->analytics()->buildSummary($measurements, $settings, [], [
|
||||
'include_offer_scenarios' => true,
|
||||
'include_long_term_projection' => true,
|
||||
]);
|
||||
|
||||
return [
|
||||
'miner_offers' => is_array($summary['miner_offers'] ?? null) ? $summary['miner_offers'] : [],
|
||||
];
|
||||
}
|
||||
|
||||
private function purchasedMiners(string $projectKey): array
|
||||
@@ -1905,56 +1961,17 @@ final class Router
|
||||
|
||||
private function saveMinerOffer(string $projectKey, array $input): array
|
||||
{
|
||||
$paymentType = $this->enumValue($input['payment_type'] ?? 'fiat', ['fiat', 'crypto'], 'payment_type');
|
||||
$baseSpeed = self::BASE_OFFER_SPEEDS[$paymentType] ?? self::BASE_OFFER_SPEEDS['fiat'];
|
||||
$projectCryptoCurrency = $this->requiredCurrency($this->settings($projectKey, [
|
||||
'cost_plans' => false,
|
||||
'currencies' => false,
|
||||
'payouts' => false,
|
||||
'wallet_withdrawals' => false,
|
||||
'miner_offers' => false,
|
||||
'purchased_miners' => false,
|
||||
])['crypto_currency'] ?? 'DOGE', 'crypto_currency');
|
||||
$basePriceCurrency = $this->requiredCurrency($input['base_price_currency'] ?? ($input['reference_price_currency'] ?? $input['price_currency'] ?? null), 'base_price_currency');
|
||||
$payload = [
|
||||
'label' => $this->requiredString($input['label'] ?? null, 'label', 120),
|
||||
'runtime_months' => 3,
|
||||
'mining_speed_value' => $baseSpeed['value'],
|
||||
'mining_speed_unit' => $baseSpeed['unit'],
|
||||
'bonus_speed_value' => null,
|
||||
'bonus_speed_unit' => null,
|
||||
'bonus_percent' => null,
|
||||
'base_price_amount' => $this->requiredDecimal($input['base_price_amount'] ?? ($input['reference_price_amount'] ?? $input['price_amount'] ?? null), 'base_price_amount'),
|
||||
'base_price_currency' => $basePriceCurrency,
|
||||
'payment_type' => $paymentType,
|
||||
'auto_renew' => !empty($input['auto_renew']) ? 1 : 0,
|
||||
'note' => $this->optionalString($input['note'] ?? null, 1000),
|
||||
'is_active' => !empty($input['is_active']) ? 1 : 0,
|
||||
];
|
||||
|
||||
if ($paymentType === 'crypto') {
|
||||
$allowedCryptoCurrencies = array_values(array_unique(['USD', $projectCryptoCurrency]));
|
||||
if (!in_array($payload['base_price_currency'], $allowedCryptoCurrencies, true)) {
|
||||
throw new ApiException('Bei Krypto-Angeboten sind nur USD oder die Projekt-Kryptowaehrung erlaubt.', 422, [
|
||||
'field' => 'base_price_currency',
|
||||
'allowed' => $allowedCryptoCurrencies,
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
if ($payload['base_price_currency'] !== 'EUR') {
|
||||
throw new ApiException('Bei FIAT-Angeboten ist aktuell nur EUR als Basiswaehrung erlaubt.', 422, [
|
||||
'field' => 'base_price_currency',
|
||||
'allowed' => ['EUR'],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->repository()->saveMinerOffer($projectKey, $payload);
|
||||
throw new ApiException(
|
||||
'Basis-Angebote werden nicht mehr in der Datenbank gespeichert. Bitte die Basispreise direkt im Bereich Miner-Angebote eingeben.',
|
||||
410
|
||||
);
|
||||
}
|
||||
|
||||
private function purchaseMiner(string $projectKey, int $offerId, array $input): array
|
||||
private function purchaseMiner(string $projectKey, string $offerId, array $input): array
|
||||
{
|
||||
$offer = $this->repository()->getMinerOffer($projectKey, $offerId);
|
||||
$offer = is_array($input['computed_offer'] ?? null)
|
||||
? $this->normalizeComputedMinerOffer((array) $input['computed_offer'], $offerId)
|
||||
: $this->findComputedMinerOffer($projectKey, $offerId);
|
||||
if (!is_array($offer)) {
|
||||
throw new ApiException('Miner-Angebot nicht gefunden.', 404);
|
||||
}
|
||||
@@ -2001,7 +2018,7 @@ final class Router
|
||||
? $this->requiredDateTime($input['purchased_at'], 'purchased_at', $this->projectTimezone($projectKey))
|
||||
: $this->currentTimestamp();
|
||||
|
||||
return $this->repository()->purchaseMiner($projectKey, $offerId, [
|
||||
return $this->repository()->purchaseMiner($projectKey, null, [
|
||||
'purchased_at' => $purchasedAt,
|
||||
'label' => $this->optionalString($input['label'] ?? ($offer['label'] ?? null), 120) ?? $offer['label'],
|
||||
'runtime_months' => $offer['runtime_months'] ?? null,
|
||||
@@ -2031,16 +2048,6 @@ final class Router
|
||||
throw new ApiException('Es kann aktuell nur auto_renew geaendert werden.', 422, ['field' => 'auto_renew']);
|
||||
}
|
||||
|
||||
$offerId = is_numeric($miner['miner_offer_id'] ?? null) ? (int) $miner['miner_offer_id'] : null;
|
||||
if ($offerId === null) {
|
||||
throw new ApiException('Dieser Miner kann nicht ueber ein Angebot aktualisiert werden.', 422);
|
||||
}
|
||||
|
||||
$offer = $this->repository()->getMinerOffer($projectKey, $offerId);
|
||||
if (!is_array($offer) || empty($offer['auto_renew'])) {
|
||||
throw new ApiException('Dieser Miner unterstuetzt keine automatische Verlaengerung.', 422);
|
||||
}
|
||||
|
||||
return $this->repository()->updatePurchasedMinerAutoRenew(
|
||||
$projectKey,
|
||||
$minerId,
|
||||
@@ -2048,6 +2055,91 @@ final class Router
|
||||
);
|
||||
}
|
||||
|
||||
private function findComputedMinerOffer(string $projectKey, string $offerId): ?array
|
||||
{
|
||||
foreach ($this->minerOffers($projectKey) as $offer) {
|
||||
if ((string) ($offer['id'] ?? '') === $offerId) {
|
||||
return $offer;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function normalizeComputedMinerOffer(array $offer, string $offerId): array
|
||||
{
|
||||
$resolvedOfferId = trim((string) ($offer['id'] ?? ''));
|
||||
if ($resolvedOfferId !== '' && $resolvedOfferId !== $offerId) {
|
||||
throw new ApiException('computed_offer passt nicht zur angeforderten Offer-ID.', 422, [
|
||||
'field' => 'computed_offer.id',
|
||||
'expected' => $offerId,
|
||||
'actual' => $resolvedOfferId,
|
||||
]);
|
||||
}
|
||||
|
||||
$paymentType = $this->enumValue($offer['payment_type'] ?? 'fiat', ['fiat', 'crypto'], 'computed_offer.payment_type');
|
||||
$runtimeMonths = $this->requiredPositiveInt($offer['runtime_months'] ?? null, 'computed_offer.runtime_months');
|
||||
$miningSpeedValue = $this->optionalDecimal($offer['mining_speed_value'] ?? null);
|
||||
$miningSpeedUnit = $this->optionalSpeedUnit($offer['mining_speed_unit'] ?? null);
|
||||
if (($miningSpeedValue === null) xor ($miningSpeedUnit === null)) {
|
||||
throw new ApiException('computed_offer muss Mining-Geschwindigkeit und Einheit gemeinsam enthalten.', 422, [
|
||||
'field' => 'computed_offer.mining_speed_value',
|
||||
]);
|
||||
}
|
||||
|
||||
$bonusPercent = $this->optionalDecimal($offer['bonus_percent'] ?? null);
|
||||
if ($bonusPercent !== null && $bonusPercent < 0) {
|
||||
throw new ApiException('computed_offer.bonus_percent darf nicht negativ sein.', 422, [
|
||||
'field' => 'computed_offer.bonus_percent',
|
||||
]);
|
||||
}
|
||||
|
||||
$bonusSpeedValue = $this->optionalDecimal($offer['bonus_speed_value'] ?? null);
|
||||
$bonusSpeedUnit = $this->optionalSpeedUnit($offer['bonus_speed_unit'] ?? null);
|
||||
if (($bonusSpeedValue === null) xor ($bonusSpeedUnit === null)) {
|
||||
throw new ApiException('computed_offer muss Bonus-Hashrate und Einheit gemeinsam enthalten.', 422, [
|
||||
'field' => 'computed_offer.bonus_speed_value',
|
||||
]);
|
||||
}
|
||||
|
||||
$basePriceAmount = $this->optionalDecimal($offer['base_price_amount'] ?? null);
|
||||
$basePriceCurrency = $this->optionalCurrency($offer['base_price_currency'] ?? null);
|
||||
$effectivePriceAmount = $this->optionalDecimal($offer['effective_price_amount'] ?? null);
|
||||
$effectivePriceCurrency = $this->optionalCurrency($offer['effective_price_currency'] ?? ($offer['price_currency'] ?? null));
|
||||
$referencePriceAmount = $this->optionalDecimal($offer['reference_price_amount'] ?? $basePriceAmount);
|
||||
$referencePriceCurrency = $this->optionalCurrency($offer['reference_price_currency'] ?? $basePriceCurrency);
|
||||
$usdReferenceAmount = $this->optionalDecimal($offer['usd_reference_amount'] ?? null);
|
||||
|
||||
if ($basePriceAmount === null && $effectivePriceAmount === null) {
|
||||
throw new ApiException('computed_offer muss mindestens einen Preis enthalten.', 422, [
|
||||
'field' => 'computed_offer.effective_price_amount',
|
||||
]);
|
||||
}
|
||||
|
||||
return array_merge($offer, [
|
||||
'id' => $resolvedOfferId !== '' ? $resolvedOfferId : $offerId,
|
||||
'label' => $this->optionalString($offer['label'] ?? null, 120),
|
||||
'payment_type' => $paymentType,
|
||||
'runtime_months' => $runtimeMonths,
|
||||
'mining_speed_value' => $miningSpeedValue,
|
||||
'mining_speed_unit' => $miningSpeedUnit,
|
||||
'bonus_percent' => $bonusPercent,
|
||||
'bonus_speed_value' => $bonusSpeedValue,
|
||||
'bonus_speed_unit' => $bonusSpeedUnit,
|
||||
'base_price_amount' => $basePriceAmount,
|
||||
'base_price_currency' => $basePriceCurrency,
|
||||
'effective_price_amount' => $effectivePriceAmount,
|
||||
'effective_price_currency' => $effectivePriceCurrency,
|
||||
'price_currency' => $effectivePriceCurrency,
|
||||
'reference_price_amount' => $referencePriceAmount,
|
||||
'reference_price_currency' => $referencePriceCurrency,
|
||||
'usd_reference_amount' => $usdReferenceAmount,
|
||||
'auto_renew' => !empty($offer['auto_renew']),
|
||||
'is_active' => !array_key_exists('is_active', $offer) || !empty($offer['is_active']),
|
||||
'note' => $this->optionalString($offer['note'] ?? null, 1000),
|
||||
]);
|
||||
}
|
||||
|
||||
private function saveDashboard(string $projectKey, array $input): array
|
||||
{
|
||||
$payload = [
|
||||
|
||||
Reference in New Issue
Block a user