<commit_before><?php
namespace Minds\Core\Payments\Stripe\Connect;

use Minds\Core\Config\Config;
use Minds\Core\Di\Di;
use Minds\Core\Entities\Actions\Save;
use Minds\Core\Router\Exceptions\UnverifiedEmailException;
use Minds\Entities\User;
use Minds\Exceptions\NotFoundException;
use Minds\Exceptions\StopEventException;
use Minds\Exceptions\UserErrorException;
use Stripe;
use Stripe\Account;
use Stripe\Exception\ApiErrorException;

class ManagerV2
{
    public function __construct(
        private ?Config $config = null,
        private ?Stripe\StripeClient $stripeClient = null,
        private ?Save $save = null,
    ) {
        $this->config ??= Di::_()->get('Config');
        $this->stripeClient ??= new Stripe\StripeClient($this->config->get('payments')['stripe']['api_key']);
        $this->save ??= new Save();
    }

    /**
     * Creates a stripe connect account
     * @param User $user
     * @return Account
     * @throws ApiErrorException
     * @throws UserErrorException
     * @throws UnverifiedEmailException
     * @throws StopEventException
     */
    public function createAccount(User $user): Stripe\Account
    {
        try {
            $this->getAccountIdByUser($user);
            throw new UserErrorException("A stripe account already exists for this user", 400);
        } catch (NotFoundException $e) {
            // All good, we want a NotFoundException
        }

        $account = $this->stripeClient->accounts->create([
            'type' => 'express',
            'email' => $user->getEmail(),
            'metadata' => [
                'user_guid' => (string) $user->getGuid(),
            ],
            'business_profile' => [
                //'url' => $this->getSiteUrl() . $user->getUsername(),
                'url' => 'https://www.minds.com/minds',
            ],
            'settings' => [
                'payouts' => [
                    'schedule' => [
                        'interval' => 'monthly',
                        'monthly_anchor' => 28,
                        'delay_days' => 30,
                    ]
                ]
            ]
        ]);

        /**
         * Update the user with the stripe id
         */
        $user->setMerchant([
                'service' => 'stripe',
                'id' => $account->id,
        ]);
        $this->save->setEntity($user)->save();

        return $account;
    }

    /**
     * Returns a stripe user account, if exists
     * @param User $user
     * @return Account
     * @throws ApiErrorException
     * @throws NotFoundException
     */
    public function getAccount(User $user): Stripe\Account
    {
        return $this->stripeClient->accounts->retrieve($this->getAccountIdByUser($user));
    }

    /**
     * Redirect to onboarding
     * @param User $user
     * @return string
     * @throws NotFoundException
     * @throws ApiErrorException
     */
    public function getAccountLink(User $user): string
    {
        $accountLink = $this->stripeClient->accountLinks->create([
            'account' => $this->getAccountIdByUser($user),
            'refresh_url' => $this->getSiteUrl(). 'api/v3/payments/stripe/connect/onboarding',
            'return_url' => $this->getSiteUrl() . 'wallet/cash/settings',
            'type' => 'account_onboarding',
            'collect' => 'currently_due',
        ]);

        return $accountLink->url;
    }

    /**
     * @param User $user
     * @return string
     * @throws NotFoundException
     */
    protected function getAccountIdByUser(User $user): string
    {
        $merchantData = $user->getMerchant() ?: [];
        if (isset($merchantData['service']) && $merchantData['service'] === 'stripe') {
            return $merchantData['id'];
        }

        throw new NotFoundException("User does not have Stripe Connect Account");
    }

    /**
     * Helper to get the site url
     * @return string
     */
    protected function getSiteUrl(): string
    {
        return $this->config->get('site_url');
    }
}
<commit_msg>(fix): ensure all our connect accounts are using the 'full' service agreement<commit_after><?php
namespace Minds\Core\Payments\Stripe\Connect;

use Minds\Core\Config\Config;
use Minds\Core\Di\Di;
use Minds\Core\Entities\Actions\Save;
use Minds\Core\Router\Exceptions\UnverifiedEmailException;
use Minds\Entities\User;
use Minds\Exceptions\NotFoundException;
use Minds\Exceptions\StopEventException;
use Minds\Exceptions\UserErrorException;
use Stripe;
use Stripe\Account;
use Stripe\Exception\ApiErrorException;

class ManagerV2
{
    public function __construct(
        private ?Config $config = null,
        private ?Stripe\StripeClient $stripeClient = null,
        private ?Save $save = null,
    ) {
        $this->config ??= Di::_()->get('Config');
        $this->stripeClient ??= new Stripe\StripeClient($this->config->get('payments')['stripe']['api_key']);
        $this->save ??= new Save();
    }

    /**
     * Creates a stripe connect account
     * @param User $user
     * @return Account
     * @throws ApiErrorException
     * @throws UserErrorException
     * @throws UnverifiedEmailException
     * @throws StopEventException
     */
    public function createAccount(User $user): Stripe\Account
    {
        try {
            $this->getAccountIdByUser($user);
            throw new UserErrorException("A stripe account already exists for this user", 400);
        } catch (NotFoundException $e) {
            // All good, we want a NotFoundException
        }

        $account = $this->stripeClient->accounts->create([
            'type' => 'express',
            'email' => $user->getEmail(),
            'metadata' => [
                'user_guid' => (string) $user->getGuid(),
            ],
            'business_profile' => [
                //'url' => $this->getSiteUrl() . $user->getUsername(),
                'url' => 'https://www.minds.com/minds',
            ],
            'settings' => [
                'payouts' => [
                    'schedule' => [
                        'interval' => 'monthly',
                        'monthly_anchor' => 28,
                        'delay_days' => 30,
                    ]
                ]
            ],
            'tos_acceptance' => [
                'service_agreement' => 'full', // Required to support on_behalf_of
            ],
        ]);

        /**
         * Update the user with the stripe id
         */
        $user->setMerchant([
                'service' => 'stripe',
                'id' => $account->id,
        ]);
        $this->save->setEntity($user)->save();

        return $account;
    }

    /**
     * Returns a stripe user account, if exists
     * @param User $user
     * @return Account
     * @throws ApiErrorException
     * @throws NotFoundException
     */
    public function getAccount(User $user): Stripe\Account
    {
        return $this->stripeClient->accounts->retrieve($this->getAccountIdByUser($user));
    }

    /**
     * Redirect to onboarding
     * @param User $user
     * @return string
     * @throws NotFoundException
     * @throws ApiErrorException
     */
    public function getAccountLink(User $user): string
    {
        $accountLink = $this->stripeClient->accountLinks->create([
            'account' => $this->getAccountIdByUser($user),
            'refresh_url' => $this->getSiteUrl(). 'api/v3/payments/stripe/connect/onboarding',
            'return_url' => $this->getSiteUrl() . 'wallet/cash/settings',
            'type' => 'account_onboarding',
            'collect' => 'currently_due',
        ]);

        return $accountLink->url;
    }

    /**
     * @param User $user
     * @return string
     * @throws NotFoundException
     */
    protected function getAccountIdByUser(User $user): string
    {
        $merchantData = $user->getMerchant() ?: [];
        if (isset($merchantData['service']) && $merchantData['service'] === 'stripe') {
            return $merchantData['id'];
        }

        throw new NotFoundException("User does not have Stripe Connect Account");
    }

    /**
     * Helper to get the site url
     * @return string
     */
    protected function getSiteUrl(): string
    {
        return $this->config->get('site_url');
    }
}

{'fraction_non_alphanumeric': 0.13791428918295495, 'fraction_numerical': 0.0021852616243778072, 'mean_word_length': 2.0420974889217134, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 111, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5383927', 'n_tokens_mistral': 2389, 'n_tokens_neox': 2320, 'n_words': 486}