Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ abstract class OrderPaymentPlatformFeeDomainObjectAbstract extends \HiEvents\Dom
final public const APPLICATION_FEE_NET_AMOUNT = 'application_fee_net_amount';
final public const APPLICATION_FEE_VAT_AMOUNT = 'application_fee_vat_amount';
final public const CHARGE_ID = 'charge_id';
final public const APPLICATION_FEE_VAT_RATE = 'application_fee_vat_rate';

protected int $id;
protected int $order_id;
Expand All @@ -41,6 +42,7 @@ abstract class OrderPaymentPlatformFeeDomainObjectAbstract extends \HiEvents\Dom
protected ?float $application_fee_net_amount = null;
protected ?float $application_fee_vat_amount = null;
protected ?string $charge_id = null;
protected ?float $application_fee_vat_rate = null;

public function toArray(): array
{
Expand All @@ -60,6 +62,7 @@ public function toArray(): array
'application_fee_net_amount' => $this->application_fee_net_amount ?? null,
'application_fee_vat_amount' => $this->application_fee_vat_amount ?? null,
'charge_id' => $this->charge_id ?? null,
'application_fee_vat_rate' => $this->application_fee_vat_rate ?? null,
];
}

Expand Down Expand Up @@ -227,4 +230,15 @@ public function getChargeId(): ?string
{
return $this->charge_id;
}

public function setApplicationFeeVatRate(?float $application_fee_vat_rate): self
{
$this->application_fee_vat_rate = $application_fee_vat_rate;
return $this;
}

public function getApplicationFeeVatRate(): ?float
{
return $this->application_fee_vat_rate;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ abstract class StripePaymentDomainObjectAbstract extends \HiEvents\DomainObjects
final public const PAYOUT_CURRENCY = 'payout_currency';
final public const PAYOUT_EXCHANGE_RATE = 'payout_exchange_rate';
final public const BALANCE_TRANSACTION_ID = 'balance_transaction_id';
final public const APPLICATION_FEE_VAT_RATE = 'application_fee_vat_rate';

protected int $id;
protected int $order_id;
Expand All @@ -48,13 +49,14 @@ abstract class StripePaymentDomainObjectAbstract extends \HiEvents\DomainObjects
protected ?string $stripe_platform = null;
protected ?int $application_fee_net = null;
protected ?int $application_fee_vat = null;
protected string $currency = 'USD';
protected ?string $currency = null;
protected ?string $payout_id = null;
protected ?int $payout_stripe_fee = null;
protected ?int $payout_net_amount = null;
protected ?string $payout_currency = null;
protected ?float $payout_exchange_rate = null;
protected ?string $balance_transaction_id = null;
protected ?float $application_fee_vat_rate = null;

public function toArray(): array
{
Expand All @@ -81,6 +83,7 @@ public function toArray(): array
'payout_currency' => $this->payout_currency ?? null,
'payout_exchange_rate' => $this->payout_exchange_rate ?? null,
'balance_transaction_id' => $this->balance_transaction_id ?? null,
'application_fee_vat_rate' => $this->application_fee_vat_rate ?? null,
];
}

Expand Down Expand Up @@ -249,13 +252,13 @@ public function getApplicationFeeVat(): ?int
return $this->application_fee_vat;
}

public function setCurrency(string $currency): self
public function setCurrency(?string $currency): self
{
$this->currency = $currency;
return $this;
}

public function getCurrency(): string
public function getCurrency(): ?string
{
return $this->currency;
}
Expand Down Expand Up @@ -325,4 +328,15 @@ public function getBalanceTransactionId(): ?string
{
return $this->balance_transaction_id;
}

public function setApplicationFeeVatRate(?float $application_fee_vat_rate): self
{
$this->application_fee_vat_rate = $application_fee_vat_rate;
return $this;
}

public function getApplicationFeeVatRate(): ?float
{
return $this->application_fee_vat_rate;
}
}
2 changes: 1 addition & 1 deletion backend/app/Models/StripePayment.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ protected function getCastMap(): array
return [
'last_error' => 'array',
'payout_exchange_rate' => 'float',

'application_fee_vat_rate' => 'float',
];
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ public function handle(string $orderShortId): CreatePaymentIntentResponseDTO
StripePaymentDomainObjectAbstract::APPLICATION_FEE_GROSS => $applicationFeeData?->grossApplicationFee?->toMinorUnit() ?? 0,
StripePaymentDomainObjectAbstract::APPLICATION_FEE_NET => $applicationFeeData?->netApplicationFee?->toMinorUnit() ?? 0,
StripePaymentDomainObjectAbstract::APPLICATION_FEE_VAT => $applicationFeeData?->applicationFeeVatAmount?->toMinorUnit() ?? 0,
StripePaymentDomainObjectAbstract::APPLICATION_FEE_VAT_RATE => $applicationFeeData?->applicationFeeVatRate,
StripePaymentDomainObjectAbstract::CURRENCY => strtoupper($order->getCurrency()),
StripePaymentDomainObjectAbstract::STRIPE_PLATFORM => $stripePlatform?->value,
]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public function handle(StripeWebhookDTO $webhookDTO): void
'data' => $event->data->object->toArray(),
]);

// return;
return;
}

$this->logger->debug('Stripe event received: ' . $event->type, $event->data->object->toArray());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ private function calculateFeeWithVat(
return new ApplicationFeeValuesDTO(
grossApplicationFee: $netApplicationFee,
netApplicationFee: $netApplicationFee,
applicationFeeVatRate: $vatRate,
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public function createOrderPaymentPlatformFee(
?string $chargeId = null,
?int $applicationFeeNetAmountMinorUnit = null,
?int $applicationFeeVatAmountMinorUnit = null,
?float $applicationFeeVatRate = null,
): void
{
$isZeroDecimalCurrency = Currency::isZeroDecimalCurrency($currency);
Expand Down Expand Up @@ -59,6 +60,7 @@ public function createOrderPaymentPlatformFee(
OrderPaymentPlatformFeeDomainObjectAbstract::APPLICATION_FEE_GROSS_AMOUNT => $applicationFeeGrossAmount,
OrderPaymentPlatformFeeDomainObjectAbstract::APPLICATION_FEE_NET_AMOUNT => $applicationFeeNetAmount,
OrderPaymentPlatformFeeDomainObjectAbstract::APPLICATION_FEE_VAT_AMOUNT => $applicationFeeVatAmount,
OrderPaymentPlatformFeeDomainObjectAbstract::APPLICATION_FEE_VAT_RATE => $applicationFeeVatRate,
OrderPaymentPlatformFeeDomainObjectAbstract::CURRENCY => strtoupper($currency),
OrderPaymentPlatformFeeDomainObjectAbstract::TRANSACTION_ID => $transactionId,
OrderPaymentPlatformFeeDomainObjectAbstract::CHARGE_ID => $chargeId,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace HiEvents\Services\Domain\Payment\Stripe;

use Exception;

class NoStripeCountryCodeException extends Exception
{

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace HiEvents\Services\Domain\Payment\Stripe;

use HiEvents\DomainObjects\AccountStripePlatformDomainObject;
use HiEvents\DomainObjects\Enums\CountryCode;
use HiEvents\DomainObjects\Generated\AccountStripePlatformDomainObjectAbstract;
use HiEvents\DomainObjects\Generated\AccountVatSettingDomainObjectAbstract;
use HiEvents\Helper\Url;
Expand Down Expand Up @@ -60,6 +61,7 @@ public function syncStripeAccountStatus(
/**
* Force update account status when we know it should be complete
* (e.g., from GetStripeConnectAccountsHandler when Stripe says complete but DB doesn't)
* @throws NoStripeCountryCodeException
*/
public function markAccountAsComplete(
AccountStripePlatformDomainObject $accountStripePlatform,
Expand Down Expand Up @@ -182,6 +184,9 @@ private function updateAccountCountryAndVerificationStatus(
}
}

/**
* @throws NoStripeCountryCodeException
*/
private function createVatSettingIfMissing(AccountStripePlatformDomainObject $accountStripePlatform): void
{
if ($this->config->get('app.tax.eu_vat_handling_enabled') !== true) {
Expand All @@ -192,6 +197,29 @@ private function createVatSettingIfMissing(AccountStripePlatformDomainObject $ac
return;
}

$countryCode = $accountStripePlatform->getStripeAccountDetails()['country'];

if ($countryCode === null) {
$this->logger->error('Stripe account country code is missing, cannot create VAT setting.', [
'account_stripe_platform_id' => $accountStripePlatform->getId(),
'account_id' => $accountStripePlatform->getAccountId(),
]);

throw new NoStripeCountryCodeException('Stripe account country code is missing. cannot create VAT setting.',
accountStripePlatformId: $accountStripePlatform->getId(),
accountId: $accountStripePlatform->getAccountId()
);
}

if (!CountryCode::isEuCountry(CountryCode::from($countryCode))) {
$this->logger->info('Account is not in an EU country, skipping VAT setting creation.', [
'account_stripe_platform_id' => $accountStripePlatform->getId(),
'account_id' => $accountStripePlatform->getAccountId(),
'country_code' => $countryCode,
]);
return;
}

$existingVatSetting = $this->vatSettingRepository->findFirstWhere([
AccountVatSettingDomainObjectAbstract::ACCOUNT_ID => $accountStripePlatform->getAccountId(),
]);
Expand All @@ -200,8 +228,7 @@ private function createVatSettingIfMissing(AccountStripePlatformDomainObject $ac
$this->vatSettingRepository->create([
AccountVatSettingDomainObjectAbstract::ACCOUNT_ID => $accountStripePlatform->getAccountId(),
AccountVatSettingDomainObjectAbstract::VAT_VALIDATED => false,
AccountVatSettingDomainObjectAbstract::VAT_COUNTRY_CODE => $accountStripePlatform
->getStripeAccountDetails()['country'] ?? null,
AccountVatSettingDomainObjectAbstract::VAT_COUNTRY_CODE => $countryCode,
]);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ public function extractAndStorePlatformFee(
chargeId: $charge->id ?? null,
applicationFeeNetAmountMinorUnit: $applicationFeeBreakdown['net'],
applicationFeeVatAmountMinorUnit: $applicationFeeBreakdown['vat'],
applicationFeeVatRate: $stripePayment->getApplicationFeeVatRate(),
);

$this->logger->info(__('Platform fee stored successfully'), [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public function up(): void
{
Model::preventLazyLoading(false);

if (!config('app.is_hi_events')) {
if (!config('app.tax.eu_vat_handling_enabled')) {
return;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('stripe_payments', static function (Blueprint $table) {
$table->decimal('application_fee_vat_rate', 5, 4)->nullable();
});

Schema::table('order_payment_platform_fees', static function (Blueprint $table) {
$table->decimal('application_fee_vat_rate', 5, 4)->nullable();
});
}

/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('stripe_payments', static function (Blueprint $table) {
$table->dropColumn('application_fee_vat_rate');
});

Schema::table('order_payment_platform_fees', static function (Blueprint $table) {
$table->dropColumn('application_fee_vat_rate');
});
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ public function testExtractAndStorePlatformFeeWithVatAndExchangeRate(): void
$stripePayment = m::mock(StripePaymentDomainObject::class);
$stripePayment->shouldReceive('getApplicationFeeNet')->andReturn(417);
$stripePayment->shouldReceive('getApplicationFeeVat')->andReturn(83);
$stripePayment->shouldReceive('getApplicationFeeVatRate')->andReturn(0.20);

$balanceTransaction = (object)[
'id' => 'txn_123',
Expand Down Expand Up @@ -285,14 +286,16 @@ public function testExtractAndStorePlatformFeeWithVatAndExchangeRate(): void
$transactionId,
$chargeId,
$applicationFeeNetAmountMinorUnit,
$applicationFeeVatAmountMinorUnit
$applicationFeeVatAmountMinorUnit,
$applicationFeeVatRate
) {
return $orderId === 123
&& $paymentPlatformFeeAmountMinorUnit === 500
&& $applicationFeeGrossAmountMinorUnit === 500
&& $chargeId === 'ch_123'
&& $applicationFeeNetAmountMinorUnit === 488
&& $applicationFeeVatAmountMinorUnit === 97
&& $applicationFeeVatRate === 0.20
&& $currency === 'eur';
});

Expand All @@ -316,6 +319,7 @@ public function testExtractAndStorePlatformFeeWithVatDisabled(): void
$stripePayment = m::mock(StripePaymentDomainObject::class);
$stripePayment->shouldReceive('getApplicationFeeNet')->never();
$stripePayment->shouldReceive('getApplicationFeeVat')->never();
$stripePayment->shouldReceive('getApplicationFeeVatRate')->andReturn(null);

$balanceTransaction = (object)[
'id' => 'txn_123',
Expand Down Expand Up @@ -365,14 +369,16 @@ public function testExtractAndStorePlatformFeeWithVatDisabled(): void
$transactionId,
$chargeId,
$applicationFeeNetAmountMinorUnit,
$applicationFeeVatAmountMinorUnit
$applicationFeeVatAmountMinorUnit,
$applicationFeeVatRate
) {
return $orderId === 123
&& $paymentPlatformFeeAmountMinorUnit === 500
&& $applicationFeeGrossAmountMinorUnit === 500
&& $chargeId === 'ch_123'
&& $applicationFeeNetAmountMinorUnit === null
&& $applicationFeeVatAmountMinorUnit === null
&& $applicationFeeVatRate === null
&& $currency === 'eur';
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,13 @@ export const VatSettingsForm = ({account, onSuccess, showCard = true}: VatSettin
const [vatNumber, setVatNumber] = useState('');
const [vatError, setVatError] = useState<string | undefined>();

const shouldPoll = vatNumber.trim().length > 0;

const vatSettingQuery = useGetAccountVatSetting(account.id, {
refetchInterval: (query) => {
if (!shouldPoll) {
return false;
}
const data = query.state.data;
if (data?.vat_validation_status === 'PENDING' || data?.vat_validation_status === 'VALIDATING') {
return 5000;
Expand Down
Loading