Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions backend/app/DomainObjects/Enums/OfflinePaymentAvailability.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace HiEvents\DomainObjects\Enums;

enum OfflinePaymentAvailability
{
use BaseEnum;

case EVERYONE;
case PROMO_CODE_ONLY;
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ abstract class EventSettingDomainObjectAbstract extends \HiEvents\DomainObjects\
final public const PAYMENT_PROVIDERS = 'payment_providers';
final public const OFFLINE_PAYMENT_INSTRUCTIONS = 'offline_payment_instructions';
final public const ALLOW_ORDERS_AWAITING_OFFLINE_PAYMENT_TO_CHECK_IN = 'allow_orders_awaiting_offline_payment_to_check_in';
final public const OFFLINE_PAYMENT_AVAILABILITY = 'offline_payment_availability';
final public const INVOICE_PAYMENT_TERMS_DAYS = 'invoice_payment_terms_days';
final public const INVOICE_NOTES = 'invoice_notes';
final public const TICKET_DESIGN_SETTINGS = 'ticket_design_settings';
Expand Down Expand Up @@ -116,6 +117,7 @@ abstract class EventSettingDomainObjectAbstract extends \HiEvents\DomainObjects\
protected array|string|null $payment_providers = null;
protected ?string $offline_payment_instructions = null;
protected bool $allow_orders_awaiting_offline_payment_to_check_in = false;
protected string $offline_payment_availability = 'EVERYONE';
protected ?int $invoice_payment_terms_days = null;
protected ?string $invoice_notes = null;
protected array|string|null $ticket_design_settings = null;
Expand Down Expand Up @@ -180,6 +182,7 @@ public function toArray(): array
'payment_providers' => $this->payment_providers ?? null,
'offline_payment_instructions' => $this->offline_payment_instructions ?? null,
'allow_orders_awaiting_offline_payment_to_check_in' => $this->allow_orders_awaiting_offline_payment_to_check_in ?? null,
'offline_payment_availability' => $this->offline_payment_availability ?? null,
'invoice_payment_terms_days' => $this->invoice_payment_terms_days ?? null,
'invoice_notes' => $this->invoice_notes ?? null,
'ticket_design_settings' => $this->ticket_design_settings ?? null,
Expand Down Expand Up @@ -694,6 +697,17 @@ public function getAllowOrdersAwaitingOfflinePaymentToCheckIn(): bool
return $this->allow_orders_awaiting_offline_payment_to_check_in;
}

public function setOfflinePaymentAvailability(string $offline_payment_availability): self
{
$this->offline_payment_availability = $offline_payment_availability;
return $this;
}

public function getOfflinePaymentAvailability(): string
{
return $this->offline_payment_availability;
}

public function setInvoicePaymentTermsDays(?int $invoice_payment_terms_days): self
{
$this->invoice_payment_terms_days = $invoice_payment_terms_days;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ abstract class PromoCodeDomainObjectAbstract extends \HiEvents\DomainObjects\Abs
final public const UPDATED_AT = 'updated_at';
final public const DELETED_AT = 'deleted_at';
final public const DISCOUNT_APPLIES_TO = 'discount_applies_to';
final public const ALLOWS_OFFLINE_PAYMENT = 'allows_offline_payment';

protected int $id;
protected int $event_id;
Expand All @@ -39,6 +40,7 @@ abstract class PromoCodeDomainObjectAbstract extends \HiEvents\DomainObjects\Abs
protected ?string $updated_at = null;
protected ?string $deleted_at = null;
protected string $discount_applies_to = 'EACH_PRODUCT';
protected bool $allows_offline_payment = false;

public function toArray(): array
{
Expand All @@ -57,6 +59,7 @@ public function toArray(): array
'updated_at' => $this->updated_at ?? null,
'deleted_at' => $this->deleted_at ?? null,
'discount_applies_to' => $this->discount_applies_to ?? null,
'allows_offline_payment' => $this->allows_offline_payment ?? null,
];
}

Expand Down Expand Up @@ -213,4 +216,15 @@ public function getDiscountAppliesTo(): string
{
return $this->discount_applies_to;
}

public function setAllowsOfflinePayment(bool $allows_offline_payment): self
{
$this->allows_offline_payment = $allows_offline_payment;
return $this;
}

public function getAllowsOfflinePayment(): bool
{
return $this->allows_offline_payment;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public function __invoke(CreateUpdatePromoCodeRequest $request, int $eventId): J
discount_applies_to: PromoCodeDiscountAppliesToEnum::fromName(
$request->input('discount_applies_to', PromoCodeDiscountAppliesToEnum::EACH_PRODUCT->name)
),
allows_offline_payment: $request->boolean('allows_offline_payment'),
));
} catch (ResourceConflictException $e) {
throw ValidationException::withMessages([
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public function __invoke(CreateUpdatePromoCodeRequest $request, int $eventId, in
discount_applies_to: $request->has('discount_applies_to')
? PromoCodeDiscountAppliesToEnum::fromName($request->input('discount_applies_to'))
: null,
allows_offline_payment: $request->boolean('allows_offline_payment'),
));
} catch (ResourceConflictException $e) {
throw ValidationException::withMessages([
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use HiEvents\DomainObjects\Enums\AttendeeDetailsCollectionMethod;
use HiEvents\DomainObjects\Enums\HomepageBackgroundType;
use HiEvents\DomainObjects\Enums\HomepageFontFamily;
use HiEvents\DomainObjects\Enums\OfflinePaymentAvailability;
use HiEvents\DomainObjects\Enums\PaymentProviders;
use HiEvents\DomainObjects\Enums\PriceDisplayMode;
use HiEvents\DomainObjects\Enums\TicketDateDisplayMode;
Expand Down Expand Up @@ -54,6 +55,7 @@ public function rules(): array
'payment_providers.*' => ['string', Rule::in(PaymentProviders::valuesArray())],
'offline_payment_instructions' => ['string', 'nullable', Rule::requiredIf(fn () => in_array(PaymentProviders::OFFLINE->name, $this->input('payment_providers', []), true))],
'allow_orders_awaiting_offline_payment_to_check_in' => ['boolean'],
'offline_payment_availability' => ['sometimes', 'string', Rule::in(OfflinePaymentAvailability::valuesArray())],

// Invoice settings
'enable_invoicing' => ['boolean'],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ function ($attribute, $value, $fail) {
'sometimes',
Rule::in(PromoCodeDiscountAppliesToEnum::valuesArray()),
],
'allows_offline_payment' => ['sometimes', 'boolean'],
];
}
}
2 changes: 2 additions & 0 deletions backend/app/Models/PromoCode.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ protected function getCastMap(): array
PromoCodeDomainObjectAbstract::DISCOUNT => 'float',
PromoCodeDomainObjectAbstract::EXPIRY_DATE => 'datetime',
PromoCodeDomainObjectAbstract::APPLICABLE_PRODUCT_IDS => 'array',
PromoCodeDomainObjectAbstract::ALLOWS_OFFLINE_PAYMENT => 'boolean',
];
}

Expand All @@ -29,6 +30,7 @@ protected function getFillableFields(): array
PromoCodeDomainObjectAbstract::EXPIRY_DATE,
PromoCodeDomainObjectAbstract::EVENT_ID,
PromoCodeDomainObjectAbstract::MAX_ALLOWED_USAGES,
PromoCodeDomainObjectAbstract::ALLOWS_OFFLINE_PAYMENT,
];
}
}
1 change: 1 addition & 0 deletions backend/app/Resources/Event/EventSettingsResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ public function toArray($request): array
'payment_providers' => $this->getPaymentProviders(),
'offline_payment_instructions' => $this->getOfflinePaymentInstructions(),
'allow_orders_awaiting_offline_payment_to_check_in' => $this->getAllowOrdersAwaitingOfflinePaymentToCheckIn(),
'offline_payment_availability' => $this->getOfflinePaymentAvailability(),

// Invoice settings
'enable_invoicing' => $this->getEnableInvoicing(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ public function toArray($request): array
'payment_providers' => $this->getPaymentProviders(),
'offline_payment_instructions' => $this->getOfflinePaymentInstructions(),
'allow_orders_awaiting_offline_payment_to_check_in' => $this->getAllowOrdersAwaitingOfflinePaymentToCheckIn(),
'offline_payment_availability' => $this->getOfflinePaymentAvailability(),

// Invoice settings
'require_billing_address' => $this->getRequireBillingAddress(),
Expand Down
17 changes: 17 additions & 0 deletions backend/app/Resources/Order/OrderResourcePublic.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
use Carbon\Carbon;
use HiEvents\DomainObjects\OrderDomainObject;
use HiEvents\DomainObjects\Status\OrderStatus;
use HiEvents\Repository\Interfaces\EventSettingsRepositoryInterface;
use HiEvents\Resources\Attendee\AttendeeResourcePublic;
use HiEvents\Resources\BaseResource;
use HiEvents\Resources\Event\EventResourcePublic;
use HiEvents\Resources\Order\Invoice\InvoiceResourcePublic;
use HiEvents\Services\Domain\Payment\OfflinePaymentEligibilityService;
use Illuminate\Http\Request;

/**
Expand Down Expand Up @@ -44,6 +46,7 @@ public function toArray(Request $request): array
'public_id' => $this->getPublicId(),
'is_payment_required' => $this->isPaymentRequired(),
'promo_code' => $this->getPromoCode(),
'offline_payment_available' => $this->resolveOfflinePaymentAvailable(),
'taxes_and_fees_rollup' => $this->getTaxesAndFeesRollup(),
'event' => $this->when(
! is_null($this->getEvent()),
Expand Down Expand Up @@ -77,4 +80,18 @@ public function toArray(Request $request): array
]),
];
}

private function resolveOfflinePaymentAvailable(): bool
{
$eventSettings = $this->getEvent()?->getEventSettings()
?? app(EventSettingsRepositoryInterface::class)->findFirstWhere([
'event_id' => $this->getEventId(),
]);

if ($eventSettings === null) {
return false;
}

return app(OfflinePaymentEligibilityService::class)->isEligible($this->resource, $eventSettings);
}
}
1 change: 1 addition & 0 deletions backend/app/Resources/PromoCode/PromoCodeResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public function toArray(Request $request): array
'attendee_usage_count' => $this->getAttendeeUsageCount(),
'order_usage_count' => $this->getOrderUsageCount(),
'max_allowed_usages' => $this->getMaxAllowedUsages(),
'allows_offline_payment' => $this->getAllowsOfflinePayment(),
];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use HiEvents\DataTransferObjects\BaseDTO;
use HiEvents\DomainObjects\Enums\AttendeeDetailsCollectionMethod;
use HiEvents\DomainObjects\Enums\HomepageBackgroundType;
use HiEvents\DomainObjects\Enums\OfflinePaymentAvailability;
use HiEvents\DomainObjects\Enums\PaymentProviders;
use HiEvents\DomainObjects\Enums\PriceDisplayMode;
use HiEvents\DomainObjects\Enums\TicketDateDisplayMode;
Expand Down Expand Up @@ -50,6 +51,7 @@ public function __construct(
public readonly array $payment_providers = [],
public readonly ?string $offline_payment_instructions = null,
public readonly bool $allow_orders_awaiting_offline_payment_to_check_in = false,
public readonly string $offline_payment_availability = 'EVERYONE',

// Invoice settings
public readonly bool $enable_invoicing = false,
Expand Down Expand Up @@ -127,6 +129,7 @@ public static function createWithDefaults(
// Payment defaults
payment_providers: [PaymentProviders::STRIPE->value],
offline_payment_instructions: null,
offline_payment_availability: OfflinePaymentAvailability::EVERYONE->name,

// Invoice defaults
enable_invoicing: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ public function handle(PartialUpdateEventSettingsDTO $eventSettingsDTO): EventSe
: $existingSettings->getOfflinePaymentInstructions(),
'allow_orders_awaiting_offline_payment_to_check_in' => $eventSettingsDTO->settings['allow_orders_awaiting_offline_payment_to_check_in']
?? $existingSettings->getAllowOrdersAwaitingOfflinePaymentToCheckIn(),
'offline_payment_availability' => $eventSettingsDTO->settings['offline_payment_availability']
?? $existingSettings->getOfflinePaymentAvailability(),

// Invoice settings
'enable_invoicing' => $eventSettingsDTO->settings['enable_invoicing'] ?? $existingSettings->getEnableInvoicing(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ public function handle(UpdateEventSettingsDTO $settings): EventSettingDomainObje
'payment_providers' => $settings->payment_providers,
'offline_payment_instructions' => $this->purifier->purify($settings->offline_payment_instructions),
'allow_orders_awaiting_offline_payment_to_check_in' => $settings->allow_orders_awaiting_offline_payment_to_check_in,
'offline_payment_availability' => $settings->offline_payment_availability,

// Invoice settings
'enable_invoicing' => $settings->enable_invoicing,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use HiEvents\Repository\Interfaces\OrderRepositoryInterface;
use HiEvents\Services\Application\Handlers\Order\DTO\TransitionOrderToOfflinePaymentPublicDTO;
use HiEvents\Services\Domain\Order\OccurrenceStatusValidator;
use HiEvents\Services\Domain\Payment\OfflinePaymentEligibilityService;
use HiEvents\Services\Domain\Product\ProductQuantityUpdateService;
use HiEvents\Services\Infrastructure\DomainEvents\DomainEventDispatcherService;
use HiEvents\Services\Infrastructure\DomainEvents\Enums\DomainEventType;
Expand All @@ -33,6 +34,7 @@ public function __construct(
private readonly OccurrenceStatusValidator $occurrenceStatusValidator,
private readonly DomainEventDispatcherService $domainEventDispatcherService,
private readonly CheckoutSessionManagementService $sessionManagementService,
private readonly OfflinePaymentEligibilityService $offlinePaymentEligibilityService,
) {}

public function handle(TransitionOrderToOfflinePaymentPublicDTO $dto): OrderDomainObject
Expand Down Expand Up @@ -100,6 +102,7 @@ private function updateOrderStatuses(int $orderId): void

/**
* @throws ResourceConflictException
* @throws UnauthorizedException
*/
public function validateOfflinePayment(
OrderDomainObject $order,
Expand All @@ -113,8 +116,12 @@ public function validateOfflinePayment(
throw new ResourceConflictException(__('Order reservation has expired'));
}

if (collect($settings->getPaymentProviders())->contains(PaymentProviders::OFFLINE->value) === false) {
throw new UnauthorizedException(__('Offline payments are not enabled for this event'));
if (! $this->offlinePaymentEligibilityService->isEligible($order, $settings)) {
if (collect($settings->getPaymentProviders())->contains(PaymentProviders::OFFLINE->value) === false) {
throw new UnauthorizedException(__('Offline payments are not enabled for this event'));
}

throw new UnauthorizedException(__('Offline payment is not available for this order'));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public function handle(int $eventId, UpsertPromoCodeDTO $promoCodeDTO): PromoCod
->setMaxAllowedUsages($promoCodeDTO->max_allowed_usages)
->setApplicableProductIds($promoCodeDTO->applicable_product_ids)
->setDiscountAppliesTo(($promoCodeDTO->discount_applies_to ?? PromoCodeDiscountAppliesToEnum::EACH_PRODUCT)->name)
->setAllowsOfflinePayment($promoCodeDTO->allows_offline_payment)
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,6 @@ public function __construct(
public readonly ?string $expiry_date,
public readonly ?int $max_allowed_usages,
public readonly ?PromoCodeDiscountAppliesToEnum $discount_applies_to,
public readonly bool $allows_offline_payment = false,
) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ public function handle(int $promoCodeId, UpsertPromoCodeDTO $promoCodeDTO): Prom
: null,
PromoCodeDomainObjectAbstract::MAX_ALLOWED_USAGES => $promoCodeDTO->max_allowed_usages,
PromoCodeDomainObjectAbstract::APPLICABLE_PRODUCT_IDS => $promoCodeDTO->applicable_product_ids,
PromoCodeDomainObjectAbstract::ALLOWS_OFFLINE_PAYMENT => $promoCodeDTO->allows_offline_payment,
]);
}
}
2 changes: 2 additions & 0 deletions backend/app/Services/Domain/Event/CreateEventService.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use HiEvents\DomainObjects\Enums\EventType;
use HiEvents\DomainObjects\Enums\HomepageBackgroundType;
use HiEvents\DomainObjects\Enums\ImageType;
use HiEvents\DomainObjects\Enums\OfflinePaymentAvailability;
use HiEvents\DomainObjects\Enums\PaymentProviders;
use HiEvents\DomainObjects\Enums\ProductTerminology;
use HiEvents\DomainObjects\EventDomainObject;
Expand Down Expand Up @@ -245,6 +246,7 @@ private function createEventSettings(

'payment_providers' => [PaymentProviders::STRIPE->value],
'offline_payment_instructions' => null,
'offline_payment_availability' => OfflinePaymentAvailability::EVERYONE->name,

'enable_invoicing' => false,
'invoice_label' => __('Invoice'),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

namespace HiEvents\Services\Domain\Payment;

use HiEvents\DomainObjects\Enums\OfflinePaymentAvailability;
use HiEvents\DomainObjects\Enums\PaymentProviders;
use HiEvents\DomainObjects\EventSettingDomainObject;
use HiEvents\DomainObjects\OrderDomainObject;
use HiEvents\Repository\Interfaces\PromoCodeRepositoryInterface;

class OfflinePaymentEligibilityService
{
public function __construct(
private readonly PromoCodeRepositoryInterface $promoCodeRepository,
) {}

public function isEligible(
OrderDomainObject $order,
EventSettingDomainObject $settings,
): bool {
if (collect($settings->getPaymentProviders())->contains(PaymentProviders::OFFLINE->value) === false) {
return false;
}

$availability = $settings->getOfflinePaymentAvailability()
?? OfflinePaymentAvailability::EVERYONE->name;

if ($availability === OfflinePaymentAvailability::EVERYONE->name) {
return true;
}

$promoCodeId = $order->getPromoCodeId();
if ($promoCodeId === null) {
return false;
}

$promoCode = $this->promoCodeRepository->findById($promoCodeId);
if ($promoCode === null) {
return false;
}

return (bool) $promoCode->getAllowsOfflinePayment();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ public function createPromoCode(PromoCodeDomainObject $promoCode): PromoCodeDoma
: null,
PromoCodeDomainObjectAbstract::MAX_ALLOWED_USAGES => $promoCode->getMaxAllowedUsages(),
PromoCodeDomainObjectAbstract::APPLICABLE_PRODUCT_IDS => $promoCode->getApplicableProductIds(),
PromoCodeDomainObjectAbstract::ALLOWS_OFFLINE_PAYMENT => $promoCode->getAllowsOfflinePayment(),
]);
}

Expand Down
Loading
Loading