Skip to content

Commit a2a6d14

Browse files
authored
Support multiple stripe platforms (#750)
1 parent b5c2a8f commit a2a6d14

File tree

21 files changed

+633
-52
lines changed

21 files changed

+633
-52
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
namespace HiEvents\DomainObjects\Enums;
4+
5+
enum StripePlatform: string
6+
{
7+
case CANADA = 'ca';
8+
case IRELAND = 'ie';
9+
10+
public static function fromString(?string $value): ?self
11+
{
12+
if ($value === null) {
13+
return null;
14+
}
15+
16+
return self::tryFrom($value);
17+
}
18+
19+
public function toString(): string
20+
{
21+
return $this->value;
22+
}
23+
24+
public static function getAllValues(): array
25+
{
26+
return array_column(self::cases(), 'value');
27+
}
28+
}

backend/app/DomainObjects/Generated/AccountDomainObjectAbstract.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ abstract class AccountDomainObjectAbstract extends \HiEvents\DomainObjects\Abstr
2525
final public const ACCOUNT_VERIFIED_AT = 'account_verified_at';
2626
final public const STRIPE_CONNECT_ACCOUNT_TYPE = 'stripe_connect_account_type';
2727
final public const IS_MANUALLY_VERIFIED = 'is_manually_verified';
28+
final public const STRIPE_PLATFORM = 'stripe_platform';
2829

2930
protected int $id;
3031
protected ?int $account_configuration_id = null;
@@ -41,6 +42,7 @@ abstract class AccountDomainObjectAbstract extends \HiEvents\DomainObjects\Abstr
4142
protected ?string $account_verified_at = null;
4243
protected ?string $stripe_connect_account_type = null;
4344
protected bool $is_manually_verified = false;
45+
protected ?string $stripe_platform = null;
4446

4547
public function toArray(): array
4648
{
@@ -60,6 +62,7 @@ public function toArray(): array
6062
'account_verified_at' => $this->account_verified_at ?? null,
6163
'stripe_connect_account_type' => $this->stripe_connect_account_type ?? null,
6264
'is_manually_verified' => $this->is_manually_verified ?? null,
65+
'stripe_platform' => $this->stripe_platform ?? null,
6366
];
6467
}
6568

@@ -227,4 +230,15 @@ public function getIsManuallyVerified(): bool
227230
{
228231
return $this->is_manually_verified;
229232
}
233+
234+
public function setStripePlatform(?string $stripe_platform): self
235+
{
236+
$this->stripe_platform = $stripe_platform;
237+
return $this;
238+
}
239+
240+
public function getStripePlatform(): ?string
241+
{
242+
return $this->stripe_platform;
243+
}
230244
}

backend/app/DomainObjects/Generated/EmailTemplateDomainObjectAbstract.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ abstract class EmailTemplateDomainObjectAbstract extends \HiEvents\DomainObjects
2222
final public const IS_ACTIVE = 'is_active';
2323
final public const CREATED_AT = 'created_at';
2424
final public const UPDATED_AT = 'updated_at';
25+
final public const DELETED_AT = 'deleted_at';
2526

2627
protected int $id;
2728
protected int $account_id;
@@ -35,6 +36,7 @@ abstract class EmailTemplateDomainObjectAbstract extends \HiEvents\DomainObjects
3536
protected bool $is_active = true;
3637
protected ?string $created_at = null;
3738
protected ?string $updated_at = null;
39+
protected ?string $deleted_at = null;
3840

3941
public function toArray(): array
4042
{
@@ -51,6 +53,7 @@ public function toArray(): array
5153
'is_active' => $this->is_active ?? null,
5254
'created_at' => $this->created_at ?? null,
5355
'updated_at' => $this->updated_at ?? null,
56+
'deleted_at' => $this->deleted_at ?? null,
5457
];
5558
}
5659

@@ -185,4 +188,15 @@ public function getUpdatedAt(): ?string
185188
{
186189
return $this->updated_at;
187190
}
191+
192+
public function setDeletedAt(?string $deleted_at): self
193+
{
194+
$this->deleted_at = $deleted_at;
195+
return $this;
196+
}
197+
198+
public function getDeletedAt(): ?string
199+
{
200+
return $this->deleted_at;
201+
}
188202
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
namespace HiEvents\Exceptions\Stripe;
4+
5+
use HiEvents\Exceptions\BaseException;
6+
7+
class StripeClientConfigurationException extends BaseException
8+
{
9+
}

backend/app/Http/Actions/Orders/Payment/Stripe/CreatePaymentIntentActionPublic.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,10 @@
1010

1111
class CreatePaymentIntentActionPublic extends BaseAction
1212
{
13-
private CreatePaymentIntentHandler $createPaymentIntentHandler;
14-
15-
public function __construct(CreatePaymentIntentHandler $createPaymentIntentHandler)
13+
public function __construct(
14+
private readonly CreatePaymentIntentHandler $createPaymentIntentHandler,
15+
)
1616
{
17-
$this->createPaymentIntentHandler = $createPaymentIntentHandler;
1817
}
1918

2019
public function __invoke(int $eventId, string $orderShortId): JsonResponse
@@ -28,6 +27,8 @@ public function __invoke(int $eventId, string $orderShortId): JsonResponse
2827
return $this->jsonResponse([
2928
'client_secret' => $createIntent->clientSecret,
3029
'account_id' => $createIntent->accountId,
30+
'public_key' => $createIntent->publicKey,
31+
'stripe_platform' => $createIntent->stripePlatform?->value,
3132
]);
3233
}
3334
}

backend/app/Providers/AppServiceProvider.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,15 @@
1919
use Illuminate\Support\Facades\URL;
2020
use Illuminate\Support\ServiceProvider;
2121
use Stripe\StripeClient;
22+
use HiEvents\Services\Infrastructure\Stripe\StripeConfigurationService;
23+
use HiEvents\Services\Infrastructure\Stripe\StripeClientFactory;
2224

2325
class AppServiceProvider extends ServiceProvider
2426
{
2527
public function register(): void
2628
{
2729
$this->bindDoctrineConnection();
28-
$this->bindStripeClient();
30+
$this->bindStripeServices();
2931
$this->bindCurrencyConversionClient();
3032
}
3133

@@ -67,8 +69,11 @@ function () {
6769
);
6870
}
6971

70-
private function bindStripeClient(): void
72+
private function bindStripeServices(): void
7173
{
74+
$this->app->singleton(StripeConfigurationService::class);
75+
$this->app->singleton(StripeClientFactory::class);
76+
7277
if (!config('services.stripe.secret_key')) {
7378
logger()?->debug('Stripe secret key is not set in the configuration file. Payment processing will not work.');
7479
return;

backend/app/Services/Application/Handlers/Account/Payment/Stripe/CreateStripeConnectAccountHandler.php

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,14 @@
88
use HiEvents\Exceptions\CreateStripeConnectAccountFailedException;
99
use HiEvents\Exceptions\CreateStripeConnectAccountLinksFailedException;
1010
use HiEvents\Exceptions\SaasModeEnabledException;
11+
use HiEvents\Exceptions\Stripe\StripeClientConfigurationException;
1112
use HiEvents\Helper\Url;
1213
use HiEvents\Repository\Interfaces\AccountRepositoryInterface;
1314
use HiEvents\Services\Application\Handlers\Account\Payment\Stripe\DTO\CreateStripeConnectAccountDTO;
1415
use HiEvents\Services\Application\Handlers\Account\Payment\Stripe\DTO\CreateStripeConnectAccountResponse;
16+
use HiEvents\Services\Infrastructure\Stripe\StripeClientFactory;
17+
use HiEvents\Services\Infrastructure\Stripe\StripeConfigurationService;
18+
use HiEvents\DomainObjects\Enums\StripePlatform;
1519
use Illuminate\Config\Repository;
1620
use Illuminate\Database\DatabaseManager;
1721
use Psr\Log\LoggerInterface;
@@ -24,9 +28,10 @@
2428
public function __construct(
2529
private AccountRepositoryInterface $accountRepository,
2630
private DatabaseManager $databaseManager,
27-
private StripeClient $stripe,
2831
private LoggerInterface $logger,
2932
private Repository $config,
33+
private StripeClientFactory $stripeClientFactory,
34+
private StripeConfigurationService $stripeConfigurationService,
3035
)
3136
{
3237
}
@@ -47,13 +52,19 @@ public function handle(CreateStripeConnectAccountDTO $command): CreateStripeConn
4752

4853
/**
4954
* @throws CreateStripeConnectAccountFailedException|CreateStripeConnectAccountLinksFailedException
55+
* @throws StripeClientConfigurationException
5056
*/
5157
private function createOrGetStripeConnectAccount(CreateStripeConnectAccountDTO $command): CreateStripeConnectAccountResponse
5258
{
5359
$account = $this->accountRepository->findById($command->accountId);
5460

61+
$primaryPlatform = $this->stripeConfigurationService->getPrimaryPlatform();
62+
$stripeClient = $this->stripeClientFactory->createForPlatform($primaryPlatform);
63+
5564
$stripeConnectAccount = $this->getOrCreateStripeConnectAccount(
5665
account: $account,
66+
stripeClient: $stripeClient,
67+
platform: $primaryPlatform,
5768
);
5869

5970
$response = new CreateStripeConnectAccountResponse(
@@ -72,22 +83,26 @@ private function createOrGetStripeConnectAccount(CreateStripeConnectAccountDTO $
7283
return $response;
7384
}
7485

75-
$response->connectUrl = $this->getStripeAccountSetupUrl($stripeConnectAccount, $account);
86+
$response->connectUrl = $this->getStripeAccountSetupUrl($stripeConnectAccount, $account, $stripeClient);
7687

7788
return $response;
7889
}
7990

8091
/**
8192
* @throws CreateStripeConnectAccountFailedException
8293
*/
83-
private function getOrCreateStripeConnectAccount(AccountDomainObject $account): Account
94+
private function getOrCreateStripeConnectAccount(
95+
AccountDomainObject $account,
96+
StripeClient $stripeClient,
97+
?StripePlatform $platform
98+
): Account
8499
{
85100
try {
86101
if ($account->getStripeAccountId() !== null) {
87-
return $this->stripe->accounts->retrieve($account->getStripeAccountId());
102+
return $stripeClient->accounts->retrieve($account->getStripeAccountId());
88103
}
89104

90-
$stripeAccount = $this->stripe->accounts->create([
105+
$stripeAccount = $stripeClient->accounts->create([
91106
'type' => $this->config->get('app.stripe_connect_account_type')
92107
?? StripeConnectAccountType::EXPRESS->value,
93108
]);
@@ -105,11 +120,18 @@ private function getOrCreateStripeConnectAccount(AccountDomainObject $account):
105120
);
106121
}
107122

123+
$updateAttributes = [
124+
AccountDomainObjectAbstract::STRIPE_ACCOUNT_ID => $stripeAccount->id,
125+
AccountDomainObjectAbstract::STRIPE_CONNECT_ACCOUNT_TYPE => $stripeAccount->type,
126+
];
127+
128+
// Set the platform that was actually used to create this account
129+
if ($platform && $account->getStripePlatform() === null) {
130+
$updateAttributes[AccountDomainObjectAbstract::STRIPE_PLATFORM] = $platform->value;
131+
}
132+
108133
$this->accountRepository->updateWhere(
109-
attributes: [
110-
AccountDomainObjectAbstract::STRIPE_ACCOUNT_ID => $stripeAccount->id,
111-
AccountDomainObjectAbstract::STRIPE_CONNECT_ACCOUNT_TYPE => $stripeAccount->type,
112-
],
134+
attributes: $updateAttributes,
113135
where: [
114136
'id' => $account->getId(),
115137
]
@@ -131,10 +153,10 @@ private function isStripeAccountComplete(Account $stripAccount): bool
131153
/**
132154
* @throws CreateStripeConnectAccountLinksFailedException
133155
*/
134-
private function getStripeAccountSetupUrl(Account $stripAccount, AccountDomainObject $account): string
156+
private function getStripeAccountSetupUrl(Account $stripAccount, AccountDomainObject $account, StripeClient $stripeClient): string
135157
{
136158
try {
137-
$accountLink = $this->stripe->accountLinks->create([
159+
$accountLink = $stripeClient->accountLinks->create([
138160
'account' => $stripAccount->id,
139161
'refresh_url' => Url::getFrontEndUrlFromConfig(Url::STRIPE_CONNECT_REFRESH_URL, [
140162
'is_refresh' => true,

backend/app/Services/Application/Handlers/Order/Payment/Stripe/CreatePaymentIntentHandler.php

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818
use HiEvents\Repository\Interfaces\AccountRepositoryInterface;
1919
use HiEvents\Repository\Interfaces\OrderRepositoryInterface;
2020
use HiEvents\Repository\Interfaces\StripePaymentsRepositoryInterface;
21+
use HiEvents\Services\Infrastructure\Stripe\StripeClientFactory;
22+
use HiEvents\Services\Infrastructure\Stripe\StripeConfigurationService;
23+
use HiEvents\DomainObjects\Enums\StripePlatform;
2124
use HiEvents\Services\Domain\Payment\Stripe\DTOs\CreatePaymentIntentRequestDTO;
2225
use HiEvents\Services\Domain\Payment\Stripe\DTOs\CreatePaymentIntentResponseDTO;
2326
use HiEvents\Services\Domain\Payment\Stripe\StripePaymentIntentCreationService;
@@ -34,6 +37,8 @@ public function __construct(
3437
private CheckoutSessionManagementService $sessionIdentifierService,
3538
private StripePaymentsRepositoryInterface $stripePaymentsRepository,
3639
private AccountRepositoryInterface $accountRepository,
40+
private StripeClientFactory $stripeClientFactory,
41+
private StripeConfigurationService $stripeConfigurationService,
3742
)
3843
{
3944
}
@@ -71,24 +76,35 @@ public function handle(string $orderShortId): CreatePaymentIntentResponseDTO
7176
))
7277
->findByEventId($order->getEventId());
7378

79+
// Get platform information from account
80+
$stripePlatform = StripePlatform::fromString($account->getStripePlatform());
81+
$stripeClient = $this->stripeClientFactory->createForPlatform($stripePlatform);
82+
$publicKey = $this->stripeConfigurationService->getPublicKey($stripePlatform);
83+
7484
// If we already have a Stripe session then re-fetch the client secret
7585
if ($order->getStripePayment() !== null) {
7686
return new CreatePaymentIntentResponseDTO(
7787
paymentIntentId: $order->getStripePayment()->getPaymentIntentId(),
78-
clientSecret: $this->stripePaymentService->retrievePaymentIntentClientSecret(
88+
clientSecret: $this->stripePaymentService->retrievePaymentIntentClientSecretWithClient(
89+
$stripeClient,
7990
$order->getStripePayment()->getPaymentIntentId(),
8091
$account->getStripeAccountId()
8192
),
8293
accountId: $account->getStripeAccountId(),
94+
stripePlatform: $stripePlatform,
95+
publicKey: $publicKey,
8396
);
8497
}
8598

86-
$paymentIntent = $this->stripePaymentService->createPaymentIntent(CreatePaymentIntentRequestDTO::fromArray([
87-
'amount' => MoneyValue::fromFloat($order->getTotalGross(), $order->getCurrency()),
88-
'currencyCode' => $order->getCurrency(),
89-
'account' => $account,
90-
'order' => $order,
91-
]));
99+
$paymentIntent = $this->stripePaymentService->createPaymentIntentWithClient(
100+
$stripeClient,
101+
CreatePaymentIntentRequestDTO::fromArray([
102+
'amount' => MoneyValue::fromFloat($order->getTotalGross(), $order->getCurrency()),
103+
'currencyCode' => $order->getCurrency(),
104+
'account' => $account,
105+
'order' => $order,
106+
])
107+
);
92108

93109
$this->stripePaymentsRepository->create([
94110
StripePaymentDomainObjectAbstract::ORDER_ID => $order->getId(),
@@ -97,6 +113,13 @@ public function handle(string $orderShortId): CreatePaymentIntentResponseDTO
97113
StripePaymentDomainObjectAbstract::APPLICATION_FEE => $paymentIntent->applicationFeeAmount,
98114
]);
99115

100-
return $paymentIntent;
116+
return new CreatePaymentIntentResponseDTO(
117+
paymentIntentId: $paymentIntent->paymentIntentId,
118+
clientSecret: $paymentIntent->clientSecret,
119+
accountId: $paymentIntent->accountId,
120+
applicationFeeAmount: $paymentIntent->applicationFeeAmount,
121+
stripePlatform: $stripePlatform,
122+
publicKey: $publicKey,
123+
);
101124
}
102125
}

0 commit comments

Comments
 (0)