Skip to content

Commit

Permalink
Merge branch 'master' into feature/refactor-discussion-search-pagination
Browse files Browse the repository at this point in the history
  • Loading branch information
nanaya authored Oct 2, 2024
2 parents 3be56c8 + f1af500 commit 9a8c6de
Show file tree
Hide file tree
Showing 186 changed files with 932 additions and 420 deletions.
41 changes: 41 additions & 0 deletions app/Console/Commands/StoreGetPaypalOrder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the GNU Affero General Public License v3.0.
// See the LICENCE file in the repository root for full licence text.

declare(strict_types=1);

namespace App\Console\Commands;

use App\Libraries\Payments\PaypalApiContext;
use App\Models\Store\Order;
use Illuminate\Console\Command;
use PayPalCheckoutSdk\Orders\OrdersGetRequest;

class StoreGetPaypalOrder extends Command
{
protected $signature = 'store:get-paypal-order {orderId}';

protected $description = 'Gets order info from paypal.';

public function handle()
{
$order = Order::findOrFail(get_int($this->argument('orderId')));
if ($order->provider !== 'paypal') {
$this->error('Not a Paypal order');
return static::INVALID;
}

$paypalOrderId = $order->reference;
if ($paypalOrderId === null) {
$this->error('Missing Paypal order id');
return static::INVALID;
}

$this->comment("Getting details for Order {$order->getKey()}, Paypal Id: {$paypalOrderId}");
$client = PaypalApiContext::client();
$response = $client->execute(new OrdersGetRequest($paypalOrderId));

$this->line(json_encode((array) $response->result, JSON_PRETTY_PRINT));
}
}
75 changes: 75 additions & 0 deletions app/Console/Commands/StoreGetShopifyCheckout.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?php

// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the GNU Affero General Public License v3.0.
// See the LICENCE file in the repository root for full licence text.

declare(strict_types=1);

namespace App\Console\Commands;

use App\Models\Store\Order;
use Illuminate\Console\Command;
use Shopify\ApiVersion;
use Shopify\Auth\FileSessionStorage;
use Shopify\Clients\Storefront;
use Shopify\Context;

class StoreGetShopifyCheckout extends Command
{
protected $signature = 'store:get-shopify-checkout {orderId}';

protected $description = 'Gets checkout info from shopify.';

public function handle()
{
$order = Order::findOrFail(get_int($this->argument('orderId')));
if ($order->provider !== 'shopify') {
$this->error('Not a Shopify order');
return static::INVALID;
}

$this->comment("Getting details for Order {$order->getKey()}");
$this->comment($order->reference);

Context::initialize(
// public unauthenticated Storefront API doesn't need OAuth and we can't use blanks.
'unauthenticated_only',
'unauthenticated_only',
'unauthenticated_read_checkouts',
$GLOBALS['cfg']['store']['shopify']['domain'],
new FileSessionStorage(),
ApiVersion::APRIL_2023,
);

$client = new Storefront(
$GLOBALS['cfg']['store']['shopify']['domain'],
$GLOBALS['cfg']['store']['shopify']['storefront_token'],
);

$id = '"'.$order->reference.'"';
$query = <<<QUERY
{
node(id: $id) {
... on Checkout {
id
ready
webUrl
orderStatusUrl
completedAt
createdAt
updatedAt
order {
id
processedAt
orderNumber
}
}
}
}
QUERY;

$response = $client->query($query);
$body = $response->getDecodedBody() ?? '';
$this->line(is_array($body) ? json_encode($body, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) : $body);
}
}
82 changes: 5 additions & 77 deletions app/Console/Kernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,79 +10,12 @@

class Kernel extends ConsoleKernel
{
/**
* The Artisan commands provided by your application.
*
* @var array
*/
protected $commands = [
Commands\DbCreate::class,
Commands\DbSetup::class,

Commands\EsCreateSearchBlacklist::class,
Commands\EsIndexDocuments::class,
Commands\EsIndexScoresQueue::class,
Commands\EsIndexScoresSetSchema::class,
Commands\EsIndexWiki::class,

Commands\Ip2AsnUpdate::class,

// modding stuff
Commands\ModdingRankCommand::class,

Commands\UserForumStatSyncCommand::class,
Commands\BeatmapsetsHypeSyncCommand::class,
Commands\BeatmapsetNominationSyncCommand::class,

Commands\StoreCleanupStaleOrders::class,
Commands\StoreExpireProducts::class,

// builds
Commands\BuildsCreate::class,
Commands\BuildsUpdatePropagationHistory::class,

// forum
Commands\ForumTopicCoversCleanup::class,

// leaderboard recalculation
Commands\RankingsRecalculateCountryStats::class,

// moddingv2 kudosu recalculation
Commands\KudosuRecalculateDiscussionsGrants::class,

// fix username change fail :D
Commands\FixUsernameChangeTopicCache::class,

// fix userchannel deletion fail
Commands\FixMissingUserChannels::class,

// fix forum display order
Commands\FixForumDisplayOrder::class,

Commands\MigrateFreshAllCommand::class,
Commands\MigrateFreshOrRunCommand::class,

Commands\NotificationsSendMail::class,

Commands\OAuthDeleteExpiredTokens::class,

Commands\RouteConvert::class,

Commands\UserBestScoresCheckCommand::class,
Commands\UserRecalculateRankCounts::class,

Commands\UserNotificationsCleanup::class,
Commands\NotificationsCleanup::class,

Commands\ChatExpireAck::class,
Commands\ChatChannelSetLastMessageId::class,

Commands\BeatmapLeadersRefresh::class,
protected function commands(): void
{
$this->load(__DIR__.'/Commands');

Commands\DailyChallengeCreateNext::class,
Commands\DailyChallengeUserStatsCalculate::class,
Commands\DailyChallengeUserStatsRecalculate::class,
];
require base_path('routes/console.php');
}

/**
* Define the application's command schedule.
Expand Down Expand Up @@ -150,9 +83,4 @@ protected function schedule(Schedule $schedule)
->cron('10 0 * * *')
->onOneServer();
}

protected function commands()
{
require base_path('routes/console.php');
}
}
12 changes: 12 additions & 0 deletions app/Exceptions/Store/PaymentRejectedException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the GNU Affero General Public License v3.0.
// See the LICENCE file in the repository root for full licence text.

declare(strict_types=1);

namespace App\Exceptions\Store;

class PaymentRejectedException extends OrderException
{
}
2 changes: 1 addition & 1 deletion app/Http/Controllers/Chat/ChannelsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ public function show($channelId)
'channel' => json_item($channel, ChannelTransformer::forUser($user), ChannelTransformer::LISTING_INCLUDES),
// TODO: probably going to need a better way to list/fetch/update users on larger channels without sending user on every message.
'users' => json_collection(
$channel->visibleUsers($user)->loadMissing(UserCompactTransformer::CARD_INCLUDES_PRELOAD),
$channel->visibleUsers()->loadMissing(UserCompactTransformer::CARD_INCLUDES_PRELOAD),
new UserCompactTransformer(),
UserCompactTransformer::CARD_INCLUDES
),
Expand Down
3 changes: 3 additions & 0 deletions app/Http/Controllers/Payments/PaypalController.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

use App\Exceptions\InvalidSignatureException;
use App\Exceptions\Store\OrderException;
use App\Exceptions\Store\PaymentRejectedException;
use App\Libraries\OrderCheckout;
use App\Libraries\Payments\NotificationType;
use App\Libraries\Payments\PaypalCreatePayment;
Expand Down Expand Up @@ -61,6 +62,8 @@ public function approved()
(new PaypalExecutePayment($order))->run();
} catch (HttpException $e) {
return $this->setAndRedirectCheckoutError($order, $this->userErrorMessage($e));
} catch (PaymentRejectedException) {
return $this->setAndRedirectCheckoutError($order, osu_trans('paypal/errors.unknown'));
}

return redirect(route('store.invoice.show', ['invoice' => $order->order_id, 'thanks' => 1]));
Expand Down
9 changes: 4 additions & 5 deletions app/Http/Controllers/Users/LookupController.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,14 @@ class LookupController extends Controller
{
public function __construct()
{
$this->middleware('auth');
$this->middleware('throttle:30,1');
$this->middleware('throttle:1200,1');
$this->middleware('require-scopes:public');
}

public function lookup()
public function index()
{
// TODO: referer check?
$params = get_params(request()->all(), null, ['ids:string[]'], ['null_missing' => true]);
$ids = array_slice($params['ids'], 0, 50);
$ids = array_slice(array_reject_null(get_arr(request('ids'), presence(...)) ?? []), 0, 50);

$numericIds = [];
$stringIds = [];
Expand Down
13 changes: 0 additions & 13 deletions app/Http/Controllers/UsersController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
namespace App\Http\Controllers;

use App\Exceptions\ModelNotSavedException;
use App\Exceptions\UserProfilePageLookupException;
use App\Exceptions\ValidationException;
use App\Http\Middleware\RequestCost;
use App\Libraries\ClientCheck;
Expand All @@ -24,7 +23,6 @@
use App\Models\Solo\Score as SoloScore;
use App\Models\User;
use App\Models\UserAccountHistory;
use App\Models\UserNotFound;
use App\Transformers\CurrentUserTransformer;
use App\Transformers\ScoreTransformer;
use App\Transformers\UserCompactTransformer;
Expand Down Expand Up @@ -114,17 +112,6 @@ private static function storeClientDisabledError()
], 403);
}

public function card($id)
{
try {
$user = FindForProfilePage::find($id, null, false);
} catch (UserProfilePageLookupException $e) {
$user = UserNotFound::instance();
}

return json_item($user, 'UserCompact', UserCompactTransformer::CARD_INCLUDES);
}

public function create()
{
if (!$GLOBALS['cfg']['osu']['user']['registration_mode']['web']) {
Expand Down
2 changes: 1 addition & 1 deletion app/Libraries/Chat.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public static function createAnnouncement(User $sender, array $rawParams)
throw new InvariantException('missing channel parameter');
}

$users = User::whereIn('user_id', $params['target_ids'])->get();
$users = User::find($params['target_ids']);
if ($users->isEmpty()) {
throw new InvariantException('Nobody to broadcast to!');
}
Expand Down
4 changes: 3 additions & 1 deletion app/Libraries/Markdown/Osu/DocumentProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,9 @@ private function loadToc()
}

$title = presence($this->getText($this->node));
$slug = $this->node->data['attributes']['id'] ?? presence(mb_strtolower(str_replace(' ', '-', $title))) ?? 'page';
$slug = $this->node->data['attributes']['id']
?? presence(mb_strtolower(strtr($title ?? '', ' ', '-')))
?? 'page';

if (array_key_exists($slug, $this->tocSlugs)) {
$this->tocSlugs[$slug] += 1;
Expand Down
33 changes: 30 additions & 3 deletions app/Libraries/Payments/PaypalExecutePayment.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,13 @@

namespace App\Libraries\Payments;

use App\Exceptions\Store\PaymentRejectedException;
use App\Models\Store\Order;
use App\Models\Store\PaypalBanned;
use Log;
use PayPalCheckoutSdk\Core\PayPalHttpClient;
use PayPalCheckoutSdk\Orders\OrdersCaptureRequest;
use PayPalCheckoutSdk\Orders\OrdersGetRequest;
use Throwable;

/**
Expand All @@ -19,8 +23,11 @@
*/
class PaypalExecutePayment
{
private PayPalHttpClient $client;

public function __construct(private Order $order)
{
$this->client = PaypalApiContext::client();
}

public function run()
Expand All @@ -34,13 +41,16 @@ public function run()
);
}

$paypalOrderId = $order->reference;

$this->assertPaypalOrder($paypalOrderId);

$order->status = Order::STATUS_PAYMENT_APPROVED;
$order->saveOrExplode();

$client = PaypalApiContext::client();
$request = new OrdersCaptureRequest($order->reference);
$request = new OrdersCaptureRequest($paypalOrderId);

$response = $client->execute($request);
$response = $this->client->execute($request);

// This block is just extra information for now, errors here should not cause the transaction to fail.
try {
Expand All @@ -53,4 +63,21 @@ public function run()
}
});
}

private function assertPaypalOrder(string $paypalOrderId): void
{
$request = new OrdersGetRequest($paypalOrderId);
$response = $this->client->execute($request);

$paypalSource = $response->result->payment_source->paypal;

if (
PaypalBanned::where('account_id', $paypalSource->account_id)
->orWhere('email', $paypalSource->email_address)
->exists()
) {
datadog_increment('store.payments.banned', ['provider' => $this->order->getPaymentProvider()]);
throw new PaymentRejectedException($this->order);
}
}
}
Loading

0 comments on commit 9a8c6de

Please sign in to comment.