Skip to content

Commit 8789348

Browse files
authored
feat: create MQ jobs to resynch payment profiles from external MS (#453)
* feat: create MQ jobs to resynch payment profiles from external MS * chore: update jobs mappings * chore: add application_type check * chore: fix retries * chore: add log info chore: fix enum at DB * chore: fix webhook creation type * chore: add command mq:setup_payment_service_message_broker {exchange_name} direct * chore: update readme.md
1 parent 34382a8 commit 8789348

22 files changed

+798
-44
lines changed

.env.example

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,3 +257,11 @@ L5_SWAGGER_CONST_TOKEN_URL=${IDP_TOKEN_ENDPOINT}
257257

258258
MEMCACHED_SERVER_HOST=127.0.0.1
259259
MEMCACHED_SERVER_PORT=11211
260+
261+
# PAYMENT SERVICE
262+
263+
PAYMENTS_SERVICE_BASE_URL=
264+
# CLIENT SHOULD BE CONFIGURED with SECRET POST at IDP
265+
PAYMENTS_SERVICE_OAUTH2_CLIENT_ID=
266+
PAYMENTS_SERVICE_OAUTH2_CLIENT_SECRET=
267+
PAYMENTS_SERVICE_OAUTH2_SCOPES=payment-profile/read
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
<?php namespace App\Console\Commands;
2+
/*
3+
* Copyright 2025 OpenStack Foundation
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
**/
14+
use App\Jobs\Payments\EventTypes;
15+
use Exception;
16+
use Illuminate\Console\Command;
17+
use Illuminate\Support\Facades\Config;
18+
use Illuminate\Support\Facades\Log;
19+
use PhpAmqpLib\Connection\AMQPStreamConnection;
20+
21+
/**
22+
* Class SetupSponsorServiceMessageBrokerCommand
23+
* @package App\Console\Commands
24+
*/
25+
final class SetupPaymentServiceMessageBrokerCommand extends Command
26+
{
27+
/**
28+
* The console command name.
29+
*
30+
* @var string
31+
*/
32+
protected $name = "setup_payment_service_message_broker";
33+
34+
/**
35+
* The name and signature of the console command.
36+
*
37+
* @var string
38+
*/
39+
protected $signature = "mq:setup_payment_service_message_broker {exchange_name} {exchange_type}";
40+
41+
/**
42+
* The console command description.
43+
*
44+
* @var string
45+
*/
46+
protected $description = "Set up Payment Service rabbitmq exchange, queue and bindings";
47+
48+
/**
49+
* Execute the console command.
50+
*
51+
* @return void
52+
*/
53+
public function handle(): void
54+
{
55+
$host_settings_path = "queue.connections.message_broker.hosts.0";
56+
$queue_settings_path = "queue.connections.payments_sync_consumer";
57+
$host_settings = Config::get($host_settings_path);
58+
59+
$exchange_name = $this->argument('exchange_name');
60+
61+
if (empty($exchange_name))
62+
throw new \InvalidArgumentException("exchange_name is required");
63+
64+
$exchange_type = $this->argument('exchange_type');
65+
66+
if (empty($exchange_type))
67+
throw new \InvalidArgumentException("exchange_type is required");
68+
69+
if (!$host_settings) {
70+
throw new \InvalidArgumentException("Host setting not found at {$host_settings_path}");
71+
}
72+
$queue_settings = Config::get($queue_settings_path);
73+
74+
$host = $host_settings['host'];
75+
$port = $host_settings['port'];
76+
$user = $host_settings['user'];
77+
$password = $host_settings['password'];
78+
$vhost = $host_settings['vhost'];
79+
80+
$queue = $queue_settings['queue'];
81+
82+
$routingKeys = [
83+
EventTypes::PAYMENT_PROFILE_UPDATED,
84+
EventTypes::PAYMENT_PROFILE_CREATED,
85+
EventTypes::PAYMENT_PROFILE_DELETED,
86+
];
87+
88+
try {
89+
$connection = new AMQPStreamConnection($host, $port, $user, $password, $vhost);
90+
$channel = $connection->channel();
91+
92+
// Exchange
93+
$channel->exchange_declare($exchange_name, $exchange_type, false, true, false);
94+
95+
// Queue
96+
$channel->queue_declare($queue, false, true, false, false);
97+
98+
// Bindings
99+
foreach ($routingKeys as $key) {
100+
$channel->queue_bind($queue, $exchange_name, $key);
101+
echo "Binding created: $queue$exchange_name ($key)\n";
102+
}
103+
104+
$channel->close();
105+
$connection->close();
106+
echo "Done.\n";
107+
108+
} catch (Exception $ex) {
109+
echo "Error: " . $ex->getMessage() . "\n";
110+
Log::error($ex);
111+
exit(1);
112+
}
113+
}
114+
}

app/Console/Kernel.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ class Kernel extends ConsoleKernel
5353
\App\Console\Commands\PurgeSummitsMarkAsDeletedCommand::class,
5454
\App\Console\Commands\IngestSummitOrderPaymentInfoCommand::class,
5555
\App\Console\Commands\SetupSponsorServiceMessageBrokerCommand::class,
56+
\App\Console\Commands\SetupPaymentServiceMessageBrokerCommand::class,
5657
];
5758

5859
/**
@@ -112,4 +113,4 @@ protected function schedule(Schedule $schedule)
112113
$schedule->command('summit:registration-orders-payment-info-ingest')->everySixHours()->withoutOverlapping()->onOneServer();
113114

114115
}
115-
}
116+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
<?php namespace App\Jobs\Payments;
2+
/*
3+
* Copyright 2025 OpenStack Foundation
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
**/
14+
15+
use App\Services\Apis\IPaymentsApi;
16+
use App\Services\Model\Imp\PaymentGatewayProfileService;
17+
use Illuminate\Bus\Queueable;
18+
use Illuminate\Contracts\Queue\ShouldQueue;
19+
use Illuminate\Foundation\Bus\Dispatchable;
20+
use Illuminate\Queue\InteractsWithQueue;
21+
use Illuminate\Queue\SerializesModels;
22+
use Illuminate\Support\Facades\Log;
23+
use models\summit\IPaymentConstants;
24+
use models\summit\ISummitRepository;
25+
use models\summit\Summit;
26+
27+
class CreatePaymentProfileMQJob implements ShouldQueue
28+
{
29+
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
30+
31+
public int $tries = 3;
32+
33+
private PaymentGatewayProfileService $service;
34+
35+
private IPaymentsApi $payments_api;
36+
37+
private ISummitRepository $summit_repository;
38+
39+
40+
/**
41+
* @param ISummitRepository $summit_repository
42+
* @param PaymentGatewayProfileService $service
43+
* @param IPaymentsApi $payments_api
44+
*/
45+
public function __construct
46+
(
47+
ISummitRepository $summit_repository,
48+
PaymentGatewayProfileService $service,
49+
IPaymentsApi $payments_api
50+
){
51+
$this->summit_repository = $summit_repository;
52+
$this->service = $service;
53+
$this->payments_api = $payments_api;
54+
}
55+
56+
public function handle(PaymentsMQJob $job): void{
57+
try {
58+
$payload = $job->payload();
59+
$json = json_encode($payload);
60+
Log::debug("CreatePaymentProfileMQJob::handle", ['payload' => $json ]);
61+
$data = $payload['data'];
62+
$id = intval($data['id']);
63+
$summit_id = intval($data['summit_id']);
64+
$application_type = $data['application_type'];
65+
if(!in_array($application_type, IPaymentConstants::ValidApplicationTypes)){
66+
Log::warning("CreatePaymentProfileMQJob::handle Application Type $application_type is not valid.");
67+
return;
68+
}
69+
$response = $this->payments_api->getPaymentProfile($summit_id, $id);
70+
Log::debug("CreatePaymentProfileMQJob::handle", ['response' => $response]);
71+
$summit = $this->summit_repository->getById($summit_id);
72+
if($summit instanceof Summit) {
73+
// mappings
74+
$response['external_id'] = $id;
75+
$response['active'] = $response['is_active'] ?? false;
76+
Log::debug("CreatePaymentProfileMQJob::handle creating payment profile", ['response' => $response ]);
77+
$this->service->addPaymentProfile($summit, $response);
78+
}
79+
$job->delete();
80+
} catch (\Exception $ex) {
81+
Log::error($ex);
82+
throw $ex;
83+
}
84+
}
85+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?php namespace App\Jobs\Payments;
2+
/*
3+
* Copyright 2025 OpenStack Foundation
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
**/
14+
15+
use App\Models\Foundation\Summit\Repositories\IPaymentGatewayProfileRepository;
16+
use App\Services\Model\Imp\PaymentGatewayProfileService;
17+
use Illuminate\Bus\Queueable;
18+
use Illuminate\Contracts\Queue\ShouldQueue;
19+
use Illuminate\Foundation\Bus\Dispatchable;
20+
use Illuminate\Queue\InteractsWithQueue;
21+
use Illuminate\Queue\SerializesModels;
22+
use Illuminate\Support\Facades\Log;
23+
use models\summit\IPaymentConstants;
24+
use models\summit\ISummitRepository;
25+
use models\summit\PaymentGatewayProfile;
26+
use models\summit\Summit;
27+
28+
class DeletePaymentProfileMQJob implements ShouldQueue
29+
{
30+
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
31+
32+
public int $tries = 3;
33+
34+
private PaymentGatewayProfileService $service;
35+
36+
private ISummitRepository $summit_repository;
37+
38+
private IPaymentGatewayProfileRepository $payment_gateway_profile_repository;
39+
40+
public function __construct
41+
(
42+
ISummitRepository $summit_repository,
43+
IPaymentGatewayProfileRepository $payment_gateway_profile_repository,
44+
PaymentGatewayProfileService $service
45+
){
46+
$this->summit_repository = $summit_repository;
47+
$this->payment_gateway_profile_repository = $payment_gateway_profile_repository;
48+
$this->service = $service;
49+
}
50+
51+
public function handle(PaymentsMQJob $job): void{
52+
try {
53+
$payload = $job->payload();
54+
$json = json_encode($payload);
55+
Log::debug("DeletePaymentProfileMQJob::handle", ['payload' => $json ]);
56+
57+
$data = $payload['data'];
58+
59+
$id = intval($data['id']);
60+
$summit_id = intval($data['summit_id']);
61+
$application_type = $data['application_type'];
62+
if(!in_array($application_type, IPaymentConstants::ValidApplicationTypes)){
63+
Log::warning("DeletePaymentProfileMQJob::handle Application Type $application_type is not valid.");
64+
return;
65+
}
66+
67+
$summit = $this->summit_repository->getById($summit_id);
68+
$local_payment_profile = $this->payment_gateway_profile_repository->getByExternalId($id);
69+
70+
if($summit instanceof Summit && $local_payment_profile instanceof PaymentGatewayProfile){
71+
$local_payment_profile_id = $local_payment_profile->getId();
72+
Log::warning("DeletePaymentProfileMQJob::handle deleting payment profile", ['local_payment_profile_id' => $local_payment_profile_id ]);
73+
$this->service->deletePaymentProfile($summit, $local_payment_profile_id);
74+
}
75+
$job->delete();
76+
} catch (\Exception $ex) {
77+
Log::error($ex);
78+
throw $ex;
79+
}
80+
}
81+
}
82+

app/Jobs/Payments/EventTypes.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php namespace App\Jobs\Payments;
2+
/*
3+
* Copyright 2025 OpenStack Foundation
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
**/
14+
15+
/**
16+
* Class EventTypes
17+
* @package App\Jobs\SponsorServices
18+
*/
19+
final class EventTypes
20+
{
21+
const string PAYMENT_PROFILE_CREATED = 'payment_profile_created';
22+
const string PAYMENT_PROFILE_UPDATED = 'payment_profile_updated';
23+
const string PAYMENT_PROFILE_DELETED = 'payment_profile_deleted';
24+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php namespace App\Jobs\Payments;
2+
/**
3+
* Copyright 2025 OpenStack Foundation
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
**/
14+
15+
use Illuminate\Support\Facades\Log;
16+
use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob as BaseJob;
17+
18+
class PaymentsMQJob extends BaseJob
19+
{
20+
public int $tries = 1;
21+
22+
/**
23+
* Get the decoded body of the job.
24+
*
25+
* @return array
26+
*/
27+
public function payload(): array
28+
{
29+
$routing_key = $this->getRabbitMQMessage()->getRoutingKey();
30+
Log::debug("PaymentsMQJob::payload processing job", ['routing_key' => $routing_key]);
31+
switch ($routing_key) {
32+
case EventTypes::PAYMENT_PROFILE_CREATED:
33+
$job = 'App\Jobs\Payments\CreatePaymentProfileMQJob@handle';
34+
break;
35+
case EventTypes::PAYMENT_PROFILE_UPDATED:
36+
$job = 'App\Jobs\Payments\UpdatePaymentProfileMQJob@handle';
37+
break;
38+
case EventTypes::PAYMENT_PROFILE_DELETED:
39+
$job = 'App\Jobs\Payments\DeletePaymentProfileMQJob@handle';
40+
break;
41+
default:
42+
Log::warning('Received an unknown routing key', ['routing_key' => $routing_key, 'message' => $this->getRawBody()]);
43+
return [];
44+
}
45+
return [
46+
'job' => $job,
47+
'data' => json_decode($this->getRawBody(), true)
48+
];
49+
}
50+
}

0 commit comments

Comments
 (0)