• Downloading from our site will require you to have a paid membership. Upgrade to a Premium Membership from 10$ a month today!

    Dont forget read our Rules! Also anyone caught Sharing this content will be banned. By using this site you are agreeing to our rules so read them. Saying I did not know is simply not an excuse! You have been warned.

Hướng dẫn fix bug trên Xenforo 2.1.9 trở xuống và Xenforo 2.0.13 trở xuống

Admin

Well-Known Member
Staff member
Administrator
Chào các bạn, hôm nay tuoitreit.vn xin hướng dẫn các bạn fix các bug trên, bug ảnh hưởng khi diễn đàn sử dụng thanh toán bằng Paypal
Để fix bug bạn truy cập theo đường dẫn src/XF/Payment/PayPal.php
Với Xenforo 2.1.9 trở xuống

Xóa hết đi và thay bằng
PHP:
<?php

namespace XF\Payment;

use XF\Entity\PaymentProfile;
use XF\Entity\PurchaseRequest;
use XF\Mvc\Controller;
use XF\Purchasable\Purchase;
use XF\Util\Arr;

class PayPal extends AbstractProvider
{
    public function getTitle()
    {
        return 'PayPal';
    }

    public function getApiEndpoint()
    {
        if (\XF::config('enableLivePayments'))
        {
            return 'https://www.paypal.com/cgi-bin/webscr';
        }
        else
        {
            return 'https://www.sandbox.paypal.com/cgi-bin/webscr';
        }
    }

    public function verifyConfig(array &$options, &$errors = [])
    {
        if (empty($options['primary_account']))
        {
            $errors[] = \XF::phrase('you_must_provide_primary_paypal_account_email_to_set_up_this_payment');
            return false;
        }

        $emails = [];
        if (!empty($options['alternate_accounts']))
        {
            $emails = Arr::stringToArray($options['alternate_accounts'], '#\r?\n#');
        }
        $emails[] = $options['primary_account'];

        $validator = \XF::app()->validator('Email');
        foreach ($emails AS $email)
        {
            if (!$validator->isValid($email))
            {
                $errors[] = \XF::phrase('value_x_is_not_valid_email_address', ['email' => $email]);
            }
        }

        if ($errors)
        {
            return false;
        }

        return true;
    }

    protected function getPaymentParams(PurchaseRequest $purchaseRequest, Purchase $purchase)
    {
        $paymentProfile = $purchase->paymentProfile;
        $purchaser = $purchase->purchaser;

        $params = [
            'business' => $paymentProfile->options['primary_account'],
            'currency_code' => $purchase->currency,
            'item_name' => $purchase->title,
            'quantity' => 1,
            'no_note' => 1,

            // 2 = required, 1 = not required, 0 = optional
            'no_shipping' => !empty($paymentProfile->options['require_address']) ? 2 : 1,

            'custom' => $purchaseRequest->request_key,

            'charset' => 'utf8',
            'email' => $purchaser->email,

            'return' => $purchase->returnUrl,
            'cancel_return' => $purchase->cancelUrl,
            'notify_url' => $this->getCallbackUrl()
        ];
        if ($purchase->recurring)
        {
            $params['cmd'] = '_xclick-subscriptions';
            $params['a3'] = $purchase->cost;
            $params['p3'] = $purchase->lengthAmount;

            switch ($purchase->lengthUnit)
            {
                case 'day': $params['t3'] = 'D'; break;
                case 'week': $params['t3'] = 'W'; break;
                case 'month': $params['t3'] = 'M'; break;
                case 'year': $params['t3'] = 'Y'; break;
                default: $params['t3'] = ''; break;
            }

            $params['src'] = 1;
            $params['sra'] = 1;
        }
        else
        {
            $params['cmd'] = '_xclick';
            $params['amount'] = $purchase->cost;
        }

        return $params;
    }

    public function initiatePayment(Controller $controller, PurchaseRequest $purchaseRequest, Purchase $purchase)
    {
        $params = $this->getPaymentParams($purchaseRequest, $purchase);

        $endpointUrl = $this->getApiEndpoint();
        $endpointUrl .= '?' . http_build_query($params);
        return $controller->redirect($endpointUrl, '');
    }

    public function renderCancellationTemplate(PurchaseRequest $purchaseRequest)
    {
        $data = [
            'purchaseRequest' => $purchaseRequest,
            'endpoint' => $this->getApiEndpoint()
        ];
        return \XF::app()->templater()->renderTemplate('public:payment_cancel_recurring_paypal', $data);
    }

    /**
     * @param \XF\Http\Request $request
     *
     * @return CallbackState
     */
    public function setupCallback(\XF\Http\Request $request)
    {
        $state = new CallbackState();

        $state->business = $request->filter('business', 'str');
        $state->receiverEmail = $request->filter('receiver_email', 'str');
        $state->transactionType = $request->filter('txn_type', 'str');
        $state->parentTransactionId = $request->filter('parent_txn_id', 'str');
        $state->transactionId = $request->filter('txn_id', 'str');
        $state->subscriberId = $request->filter('subscr_id', 'str');
        $state->paymentCountry = $request->filter('residence_country', 'str');
        $state->costAmount = $request->filter('mc_gross', 'unum');
        $state->taxAmount = $request->filter('tax', 'unum');
        $state->costCurrency = $request->filter('mc_currency', 'str');
        $state->paymentStatus = $request->filter('payment_status', 'str');

        $state->requestKey = $request->filter('custom', 'str');
        $state->testMode = $request->filter('test_ipn', 'bool');

        if (\XF::config('enableLivePayments'))
        {
            // explicitly disable test mode if live payments are enabled
            $state->testMode = false;
        }

        $state->ip = $request->getIp();
        $state->_POST = $_POST;

        return $state;
    }

    public function validateCallback(CallbackState $state)
    {
        try
        {
            $params = ['form_params' => $state->_POST + ['cmd' => '_notify-validate']];
            $client = \XF::app()->http()->client();
            if ($state->testMode)
            {
                $url = 'https://ipnpb.sandbox.paypal.com/cgi-bin/webscr';
            }
            else
            {
                $url = 'https://ipnpb.paypal.com/cgi-bin/webscr';
            }
            $response = $client->post($url, $params);
            if (!$response || $response->getBody()->getContents() != 'VERIFIED' || $response->getStatusCode() != 200)
            {
                $host = \XF\Util\Ip::getHost($state->ip);
                if (preg_match('#(^|\.)paypal.com$#i', $host))
                {
                    $state->logType = 'error';
                    $state->logMessage = 'Request not validated';
                }
                else
                {
                    $state->logType = false;
                    $state->logMessage = 'Request not validated (from unknown source)';
                }
                return false;
            }
        }
        catch (\GuzzleHttp\Exception\RequestException $e)
        {
            $state->logType = 'error';
            $state->logMessage = 'Connection to PayPal failed: ' . $e->getMessage();
            return false;
        }

        return true;
    }

    public function validateTransaction(CallbackState $state)
    {
        if (!$state->requestKey)
        {
            $state->logType = 'info';
            $state->logMessage = 'No purchase request key. Unrelated payment, no action to take.';
            return false;
        }

        if ($state->legacy)
        {
            // The custom data in legacy calls is <user_id>,<user_upgrade_id>,<validation_type>,<validation>.
            // We only need the user_id and user_upgrade_id but we can at least verify it's a familiar format.
            if (!preg_match(
                '/^(?P<user_id>\d+),(?P<user_upgrade_id>\d+),token,.*$/',
                $state->requestKey,
                $itemParts)
                && count($itemParts) !== 3 // full match + 2 groups
            )
            {
                $state->logType = 'info';
                $state->logMessage = 'Invalid custom field. Unrelated payment, no action to take.';
                return false;
            }

            $user = \XF::em()->find('XF:User', $itemParts['user_id']);
            if (!$user)
            {
                $state->logType = 'error';
                $state->logMessage = 'Could not find user with user_id ' . $itemParts['user_id'] . '.';
                return false;
            }
            $state->purchaser = $user;

            $state->userUpgrade = \XF::em()->find('XF:UserUpgrade', $itemParts['user_upgrade_id']);
            if (!$state->userUpgrade)
            {
                $state->logType = 'error';
                $state->logMessage = 'Could not find user upgrade with user_upgrade_id ' . $itemParts['user_upgrade_id'] . '.';
                return false;
            }
        }
        else
        {
            if (!$state->getPurchaseRequest())
            {
                $state->logType = 'info';
                $state->logMessage = 'Invalid request key. Unrelated payment, no action to take.';
                return false;
            }
        }

        if (!$state->transactionId && !$state->subscriberId)
        {
            $state->logType = 'info';
            $state->logMessage = 'No transaction or subscriber ID. No action to take.';
            return false;
        }

        $paymentRepo = \XF::repository('XF:Payment');
        $matchingLogsFinder = $paymentRepo->findLogsByTransactionIdForProvider($state->transactionId, $this->providerId);
        if ($matchingLogsFinder->total())
        {
            $logs = $matchingLogsFinder->fetch();
            foreach ($logs AS $log)
            {
                if ($log->log_type == 'cancel' && $state->paymentStatus == 'Canceled_Reversal')
                {
                    // This is a cancelled transaction we've already seen, but has now been reversed.
                    // Let it go through.
                    return true;
                }
            }

            $state->logType = 'info';
            $state->logMessage = 'Transaction already processed. Skipping.';
            return false;
        }

        return true;
    }

    public function validatePurchaseRequest(CallbackState $state)
    {
        // validated in validateTransaction
        return true;
    }

    public function validatePurchasableHandler(CallbackState $state)
    {
        if ($state->legacy)
        {
            $purchasable = \XF::em()->find('XF:Purchasable', 'user_upgrade');
            $state->purchasableHandler = $purchasable->handler;
        }
        return parent::validatePurchasableHandler($state);
    }

    public function validatePaymentProfile(CallbackState $state)
    {
        if ($state->legacy)
        {
            $finder = \XF::finder('XF:PaymentProfile')
                ->where('provider_id', 'paypal');
            foreach ($finder->fetch() AS $profile)
            {
                if (!empty($profile->options['legacy']))
                {
                    $state->paymentProfile = $profile;
                    break;
                }
            }
        }
        return parent::validatePaymentProfile($state);
    }

    public function validatePurchaser(CallbackState $state)
    {
        if ($state->legacy)
        {
            // validated in validateTransaction
            return true;
        }
        else
        {
            return parent::validatePurchaser($state);
        }
    }

    public function validatePurchasableData(CallbackState $state)
    {
        $paymentProfile = $state->getPaymentProfile();

        $business = strtolower($state->business);
        $receiverEmail = strtolower($state->receiverEmail);

        $options = $paymentProfile->options;
        $accounts = Arr::stringToArray($options['alternate_accounts'], '#\r?\n#');
        $accounts[] = $options['primary_account'];

        $matched = false;
        foreach ($accounts AS $account)
        {
            $account = trim(strtolower($account));
            if ($account && ($business == $account || $receiverEmail == $account))
            {
                $matched = true;
                break;
            }
        }
        if (!$matched)
        {
            $state->logType = 'error';
            $state->logMessage = 'Invalid business or receiver_email.';
            return false;
        }

        return true;
    }

    public function validateCost(CallbackState $state)
    {
        if ($state->legacy)
        {
            $upgrade = $state->userUpgrade;

            $upgradeRecord = \XF::em()->findOne('XF:UserUpgradeActive', [
                'user_upgrade_id' => $upgrade->user_upgrade_id,
                'user_id' => $state->purchaser->user_id
            ]);

            if (!$upgradeRecord && $state->subscriberId)
            {
                $logFinder = \XF::finder('XF:PaymentProviderLog')
                    ->where('subscriber_id', $state->subscriberId)
                    ->order('log_date', 'DESC');
                
                foreach ($logFinder->fetch() AS $log)
                {
                    if (is_numeric($log->purchase_request_key))
                    {
                        $upgradeRecord = \XF::em()->find('XF:UserUpgradeExpired', $log->purchase_request_key);
                        if ($upgradeRecord)
                        {
                            $state->userUpgradeRecordId = $upgradeRecord->user_upgrade_record_id;
                            break;
                        }
                    }
                }
            }

            if (!$upgradeRecord && $state->parentTransactionId)
            {
                $logFinder = \XF::finder('XF:PaymentProviderLog')
                    ->where('transaction_id', $state->parentTransactionId)
                    ->order('log_date', 'DESC');

                foreach ($logFinder->fetch() AS $log)
                {
                    if (is_numeric($log->purchase_request_key))
                    {
                        $upgradeRecord = \XF::em()->find('XF:UserUpgradeExpired', $log->purchase_request_key);
                        if ($upgradeRecord)
                        {
                            $state->userUpgradeRecordId = $upgradeRecord->user_upgrade_record_id;
                            break;
                        }
                    }
                }
            }

            $cost = $upgrade->cost_amount;
            $currency = $upgrade->cost_currency;
        }
        else
        {
            $upgradeRecord = false;
            $purchaseRequest = $state->getPurchaseRequest();
            $cost = $purchaseRequest->cost_amount;
            $currency = $purchaseRequest->cost_currency;
        }

        switch ($state->transactionType)
        {
            case 'web_accept':
            case 'subscr_payment':
                $costValidated = (
                    round(($state->costAmount - $state->taxAmount), 2) == round($cost, 2)
                    && $state->costCurrency == $currency
                );

                if ($state->legacy && !$costValidated && $upgradeRecord && $upgradeRecord->extra)
                {
                    $cost = $upgradeRecord->extra['cost_amount'];
                    $currency = $upgradeRecord->extra['cost_currency'];

                    $costValidated = (
                        round(($state->costAmount - $state->taxAmount), 2) == round($cost, 2)
                        && $state->costCurrency == strtoupper($currency)
                    );
                    if ($costValidated)
                    {
                        // the upgrade's cost has changed, but we need to continue as if it hasn't
                        $state->extraData = [
                            'cost_amount' => round($state->costAmount, 2),
                            'cost_currency' => $state->costCurrency
                        ];
                    }
                }

                if (!$costValidated)
                {
                    $state->logType = 'error';
                    $state->logMessage = 'Invalid cost amount';
                    return false;
                }
        }
        return true;
    }

    public function getPaymentResult(CallbackState $state)
    {
        switch ($state->transactionType)
        {
            case 'web_accept':
            case 'subscr_payment':
                if ($state->paymentStatus == 'Completed')
                {
                    $state->paymentResult = CallbackState::PAYMENT_RECEIVED;
                }
                break;
        }

        if ($state->paymentStatus == 'Refunded' || $state->paymentStatus == 'Reversed')
        {
            $state->paymentResult = CallbackState::PAYMENT_REVERSED;
        }
        else if ($state->paymentStatus == 'Canceled_Reversal')
        {
            $state->paymentResult = CallbackState::PAYMENT_REINSTATED;
        }
    }

    public function prepareLogData(CallbackState $state)
    {
        $state->logDetails = $state->_POST;
    }

    protected function getSupportedRecurrenceRanges()
    {
        return [
            'day' => [1, 90],
            'week' => [1, 52],
            'month' => [1, 24],
            'year' => [1, 5]
        ];
    }
}
Với Xenforo 2.0.13 trở xuống
Xóa hết đi và thay bằng
PHP:
<?php

namespace XF\Payment;

use XF\Entity\PaymentProfile;
use XF\Entity\PurchaseRequest;
use XF\Mvc\Controller;
use XF\Purchasable\Purchase;

class PayPal extends AbstractProvider
{
    public function getTitle()
    {
        return 'PayPal';
    }

    public function getApiEndpoint()
    {
        if (\XF::config('enableLivePayments'))
        {
            return 'https://www.paypal.com/cgi-bin/webscr';
        }
        else
        {
            return 'https://www.sandbox.paypal.com/cgi-bin/webscr';
        }
    }

    public function verifyConfig(array &$options, &$errors = [])
    {
        if (empty($options['primary_account']))
        {
            $errors[] = \XF::phrase('you_must_provide_primary_paypal_account_email_to_set_up_this_payment');
            return false;
        }

        $emails = [];
        if (!empty($options['alternate_accounts']))
        {
            $emails = preg_split('#\r?\n#', $options['alternate_accounts'], -1, PREG_SPLIT_NO_EMPTY);
        }
        $emails[] = $options['primary_account'];

        $validator = \XF::app()->validator('Email');
        foreach ($emails AS $email)
        {
            if (!$validator->isValid($email))
            {
                $errors[] = \XF::phrase('value_x_is_not_valid_email_address', ['email' => $email]);
            }
        }

        if ($errors)
        {
            return false;
        }

        return true;
    }

    protected function getPaymentParams(PurchaseRequest $purchaseRequest, Purchase $purchase)
    {
        $paymentProfile = $purchase->paymentProfile;
        $purchaser = $purchase->purchaser;

        $params = [
            'business' => $paymentProfile->options['primary_account'],
            'currency_code' => $purchase->currency,
            'item_name' => $purchase->title,
            'quantity' => 1,
            'no_note' => 1,

            // 2 = required, 1 = not required, 0 = optional
            'no_shipping' => !empty($paymentProfile->options['require_address']) ? 2 : 1,

            'custom' => $purchaseRequest->request_key,

            'charset' => 'utf8',
            'email' => $purchaser->email,

            'return' => $purchase->returnUrl,
            'cancel_return' => $purchase->cancelUrl,
            'notify_url' => $this->getCallbackUrl()
        ];
        if ($purchase->recurring)
        {
            $params['cmd'] = '_xclick-subscriptions';
            $params['a3'] = $purchase->cost;
            $params['p3'] = $purchase->lengthAmount;

            switch ($purchase->lengthUnit)
            {
                case 'day': $params['t3'] = 'D'; break;
                case 'month': $params['t3'] = 'M'; break;
                case 'year': $params['t3'] = 'Y'; break;
                default: $params['t3'] = ''; break;
            }

            $params['src'] = 1;
            $params['sra'] = 1;
        }
        else
        {
            $params['cmd'] = '_xclick';
            $params['amount'] = $purchase->cost;
        }

        return $params;
    }

    public function initiatePayment(Controller $controller, PurchaseRequest $purchaseRequest, Purchase $purchase)
    {
        $params = $this->getPaymentParams($purchaseRequest, $purchase);

        $endpointUrl = $this->getApiEndpoint();
        $endpointUrl .= '?' . http_build_query($params);
        return $controller->redirect($endpointUrl, '');
    }

    public function renderCancellationTemplate(PurchaseRequest $purchaseRequest)
    {
        $data = [
            'purchaseRequest' => $purchaseRequest,
            'endpoint' => $this->getApiEndpoint()
        ];
        return \XF::app()->templater()->renderTemplate('public:payment_cancel_recurring_paypal', $data);
    }

    /**
     * @param \XF\Http\Request $request
     *
     * @return CallbackState
     */
    public function setupCallback(\XF\Http\Request $request)
    {
        $state = new CallbackState();

        $state->business = $request->filter('business', 'str');
        $state->receiverEmail = $request->filter('receiver_email', 'str');
        $state->transactionType = $request->filter('txn_type', 'str');
        $state->parentTransactionId = $request->filter('parent_txn_id', 'str');
        $state->transactionId = $request->filter('txn_id', 'str');
        $state->subscriberId = $request->filter('subscr_id', 'str');
        $state->costAmount = $request->filter('mc_gross', 'unum');
        $state->taxAmount = $request->filter('tax', 'unum');
        $state->costCurrency = $request->filter('mc_currency', 'str');
        $state->paymentStatus = $request->filter('payment_status', 'str');

        $state->requestKey = $request->filter('custom', 'str');
        $state->testMode = $request->filter('test_ipn', 'bool');

        if (\XF::config('enableLivePayments'))
        {
            // explicitly disable test mode if live payments are enabled
            $state->testMode = false;
        }

        $state->ip = $request->getIp();
        $state->_POST = $_POST;

        return $state;
    }

    public function validateCallback(CallbackState $state)
    {
        try
        {
            $params = ['body' => $state->_POST + ['cmd' => '_notify-validate']];
            $client = \XF::app()->http()->client();
            if ($state->testMode)
            {
                $url = 'https://ipnpb.sandbox.paypal.com/cgi-bin/webscr';
            }
            else
            {
                $url = 'https://ipnpb.paypal.com/cgi-bin/webscr';
            }
            $response = $client->post($url, $params);
            if (!$response || $response->getBody() != 'VERIFIED' || $response->getStatusCode() != 200)
            {
                $host = \XF\Util\Ip::getHost($state->ip);
                if (preg_match('#(^|\.)paypal.com$#i', $host))
                {
                    $state->logType = 'error';
                    $state->logMessage = 'Request not validated';
                }
                else
                {
                    $state->logType = false;
                    $state->logMessage = 'Request not validated (from unknown source)';
                }
                return false;
            }
        }
        catch (\GuzzleHttp\Exception\RequestException $e)
        {
            $state->logType = 'error';
            $state->logMessage = 'Connection to PayPal failed: ' . $e->getMessage();
            return false;
        }

        return true;
    }

    public function validateTransaction(CallbackState $state)
    {
        if (!$state->requestKey)
        {
            $state->logType = 'info';
            $state->logMessage = 'No purchase request key. Unrelated payment, no action to take.';
            return false;
        }

        if ($state->legacy)
        {
            // The custom data in legacy calls is <user_id>,<user_upgrade_id>,<validation_type>,<validation>.
            // We only need the user_id and user_upgrade_id but we can at least verify it's a familiar format.
            if (!preg_match(
                '/^(?P<user_id>\d+),(?P<user_upgrade_id>\d+),token,.*$/',
                $state->requestKey,
                $itemParts)
                && count($itemParts) !== 3 // full match + 2 groups
            )
            {
                $state->logType = 'info';
                $state->logMessage = 'Invalid custom field. Unrelated payment, no action to take.';
                return false;
            }

            $user = \XF::em()->find('XF:User', $itemParts['user_id']);
            if (!$user)
            {
                $state->logType = 'error';
                $state->logMessage = 'Could not find user with user_id ' . $itemParts['user_id'] . '.';
                return false;
            }
            $state->purchaser = $user;

            $state->userUpgrade = \XF::em()->find('XF:UserUpgrade', $itemParts['user_upgrade_id']);
            if (!$state->userUpgrade)
            {
                $state->logType = 'error';
                $state->logMessage = 'Could not find user upgrade with user_upgrade_id ' . $itemParts['user_upgrade_id'] . '.';
                return false;
            }
        }
        else
        {
            if (!$state->getPurchaseRequest())
            {
                $state->logType = 'info';
                $state->logMessage = 'Invalid request key. Unrelated payment, no action to take.';
                return false;
            }
        }

        if (!$state->transactionId && !$state->subscriberId)
        {
            $state->logType = 'info';
            $state->logMessage = 'No transaction or subscriber ID. No action to take.';
            return false;
        }

        $paymentRepo = \XF::repository('XF:Payment');
        $matchingLogsFinder = $paymentRepo->findLogsByTransactionId($state->transactionId);
        if ($matchingLogsFinder->total())
        {
            $logs = $matchingLogsFinder->fetch();
            foreach ($logs AS $log)
            {
                if ($log->log_type == 'cancel' && $state->paymentStatus == 'Canceled_Reversal')
                {
                    // This is a cancelled transaction we've already seen, but has now been reversed.
                    // Let it go through.
                    return true;
                }
            }

            $state->logType = 'info';
            $state->logMessage = 'Transaction already processed. Skipping.';
            return false;
        }

        return true;
    }

    public function validatePurchaseRequest(CallbackState $state)
    {
        // validated in validateTransaction
        return true;
    }

    public function validatePurchasableHandler(CallbackState $state)
    {
        if ($state->legacy)
        {
            $purchasable = \XF::em()->find('XF:Purchasable', 'user_upgrade');
            $state->purchasableHandler = $purchasable->handler;
        }
        return parent::validatePurchasableHandler($state);
    }

    public function validatePaymentProfile(CallbackState $state)
    {
        if ($state->legacy)
        {
            $finder = \XF::finder('XF:PaymentProfile')
                ->where('provider_id', 'paypal');
            foreach ($finder->fetch() AS $profile)
            {
                if (!empty($profile->options['legacy']))
                {
                    $state->paymentProfile = $profile;
                    break;
                }
            }
        }
        return parent::validatePaymentProfile($state);
    }

    public function validatePurchaser(CallbackState $state)
    {
        if ($state->legacy)
        {
            // validated in validateTransaction
            return true;
        }
        else
        {
            return parent::validatePurchaser($state);
        }
    }

    public function validatePurchasableData(CallbackState $state)
    {
        $paymentProfile = $state->getPaymentProfile();

        $business = strtolower($state->business);
        $receiverEmail = strtolower($state->receiverEmail);

        $options = $paymentProfile->options;
        $accounts = preg_split('#\r?\n#', $options['alternate_accounts'], -1, PREG_SPLIT_NO_EMPTY);
        $accounts[] = $options['primary_account'];

        $matched = false;
        foreach ($accounts AS $account)
        {
            $account = trim(strtolower($account));
            if ($account && ($business == $account || $receiverEmail == $account))
            {
                $matched = true;
                break;
            }
        }
        if (!$matched)
        {
            $state->logType = 'error';
            $state->logMessage = 'Invalid business or receiver_email.';
            return false;
        }

        return true;
    }

    public function validateCost(CallbackState $state)
    {
        if ($state->legacy)
        {
            $upgrade = $state->userUpgrade;

            $upgradeRecord = \XF::em()->findOne('XF:UserUpgradeActive', [
                'user_upgrade_id' => $upgrade->user_upgrade_id,
                'user_id' => $state->purchaser->user_id
            ]);

            if (!$upgradeRecord && $state->subscriberId)
            {
                $logFinder = \XF::finder('XF:PaymentProviderLog')
                    ->where('subscriber_id', $state->subscriberId)
                    ->order('log_date', 'DESC');
                
                foreach ($logFinder->fetch() AS $log)
                {
                    if (is_int($log->purchase_request_key))
                    {
                        $upgradeRecord = \XF::em()->find('XF:UserUpgradeExpired', $log->purchase_request_key);
                        if ($upgradeRecord)
                        {
                            $state->userUpgradeRecordId = $upgradeRecord->user_upgrade_record_id;
                            break;
                        }
                    }
                }
            }

            if (!$upgradeRecord && $state->parentTransactionId)
            {
                $logFinder = \XF::finder('XF:PaymentProviderLog')
                    ->where('transaction_id', $state->parentTransactionId)
                    ->order('log_date', 'DESC');

                foreach ($logFinder->fetch() AS $log)
                {
                    if (is_int($log->purchase_request_key))
                    {
                        $upgradeRecord = \XF::em()->find('XF:UserUpgradeExpired', $log->purchase_request_key);
                        if ($upgradeRecord)
                        {
                            $state->userUpgradeRecordId = $upgradeRecord->user_upgrade_record_id;
                            break;
                        }
                    }
                }
            }

            $cost = $upgrade->cost_amount;
            $currency = $upgrade->cost_currency;
        }
        else
        {
            $upgradeRecord = false;
            $purchaseRequest = $state->getPurchaseRequest();
            $cost = $purchaseRequest->cost_amount;
            $currency = $purchaseRequest->cost_currency;
        }

        switch ($state->transactionType)
        {
            case 'web_accept':
            case 'subscr_payment':
                $costValidated = (
                    round(($state->costAmount - $state->taxAmount), 2) == round($cost, 2)
                    && $state->costCurrency == $currency
                );

                if ($state->legacy && !$costValidated && $upgradeRecord && $upgradeRecord->extra)
                {
                    $cost = $upgradeRecord->extra['cost_amount'];
                    $currency = $upgradeRecord->extra['cost_currency'];

                    $costValidated = (
                        round(($state->costAmount - $state->taxAmount), 2) == round($cost, 2)
                        && $state->costCurrency == strtoupper($currency)
                    );
                    if ($costValidated)
                    {
                        // the upgrade's cost has changed, but we need to continue as if it hasn't
                        $state->extraData = [
                            'cost_amount' => round($state->costAmount, 2),
                            'cost_currency' => $state->costCurrency
                        ];
                    }
                }

                if (!$costValidated)
                {
                    $state->logType = 'error';
                    $state->logMessage = 'Invalid cost amount';
                    return false;
                }
        }
        return true;
    }

    public function getPaymentResult(CallbackState $state)
    {
        switch ($state->transactionType)
        {
            case 'web_accept':
            case 'subscr_payment':
                if ($state->paymentStatus == 'Completed')
                {
                    $state->paymentResult = CallbackState::PAYMENT_RECEIVED;
                }
                break;
        }

        if ($state->paymentStatus == 'Refunded' || $state->paymentStatus == 'Reversed')
        {
            $state->paymentResult = CallbackState::PAYMENT_REVERSED;
        }
        else if ($state->paymentStatus == 'Canceled_Reversal')
        {
            $state->paymentResult = CallbackState::PAYMENT_REINSTATED;
        }
    }

    public function prepareLogData(CallbackState $state)
    {
        $state->logDetails = $state->_POST;
    }
}

Sau đó lưu lại là xong
Chúc bạn thành công!
 

Facebook Comments

Similar threads
Thread starter Title Forum Replies Date
Admin Bug chatbox changuondyu trên xenforo và cách fix Security - Local - Hacking 0
PushKiss Hướng dẫn Fix bug like bài viết bang hội JohnCMS Johncms 0
C Hỏi Ai biết fix lỗi bug topic bang hội thì vào giúp mình. Johncms 0
Admin Lỗi SQLI nghiêm trọng của WHMCS 5.2.7 (cách bug và cách fix) Khác 0
Admin Hướng dẫn fix bug sql injection mod music gift vbb Khác 0
Admin Hướng dẫn fix bug mod hide thank johncms part 2 Johncms 2
S Bug gold,chậu game kvvv nhanh kẻo fix Thủ thuật ĐTDĐ 0
Admin Hướng dẫn Fix Bug Changuondyu Static An Toàn Và Hiệu Quả Nhất Khác 0
Admin Hướng dẫn Fix Bug Changuondyu Static Mod Vbb Mới Và Chuẩn Nhất Khác 1
Admin Cách fix bug XSS tại 1 số theme của wordpress Khác 0
Admin Bug Changuondyu Static Mod VBB và cách fix Security - Local - Hacking 3
Admin Share mod game sóc đĩa online bản fix bug đặt tiền cho johncms 4.4 Johncms 4
Admin Share games Sóc đĩa Online bản fix bug đặt tiền cho johncms 4.4 Johncms 0
Admin Hướng dẫn fix lỗi không vào được mạng laptop Masstel E140 Sử dụng, chia sẻ, hỏi đáp 0
Admin Hướng dẫn fix lỗi Communication Error cho máy in Canon LBP 2900, Canon LBP 3300 Windows 11 mới nhất - Fix Communication Error Canon 2900, Canon 3300 Windows 11 Sử dụng, chia sẻ, hỏi đáp 0
Admin Fix the "Access denied. You do not have enough rights to use this virtual machine" error message MacOS Sử dụng, chia sẻ, hỏi đáp 0
Admin Hướng dẫn fix lỗi GET GENUINE OFFICE Your license isn't genuine cho office thành công 100% Sử dụng, chia sẻ, hỏi đáp 0
nutevnn Help Nhờ hướng dẫn fix html trong PHP PHP 0
Admin Fix [E_WARNING] inet_pton(): Unrecognized address unknown xenforo 2.2.10 Xenforo 0
Admin Hướng dẫn gõ tiếng việt có dấu trên vba - Fix lỗi font tiếng việt excel vba Sử dụng, chia sẻ, hỏi đáp 0
Admin Hướng dẫn fix lỗi không xóa được bôi đen trong Word bằng phím Backspace - Backspace doesn't delete highlighted text word Sử dụng, chia sẻ, hỏi đáp 4
thaicutene Help ai đó hãy giúp fix cái Invalid IP Johncms 12
Admin Hướng dẫn fix lỗi No Internet trên Windows 10 20H2 Sử dụng, chia sẻ, hỏi đáp 0
Admin XenForo 2.1.12 Released (Security Fix) Xenforo 0
Admin XenForo 2.2.1 Released (Includes Security Fix) Xenforo 0
Admin Hướng dẫn fix noindex cho wordpress Wordpress 0
Admin Fix ErrorException: Template error: Illegal string offset 'width' with SEO 2 Xenforo 2 Xenforo 0
Admin Hướng dẫn fix màn hình đen black screen phần mềm OBS Studio trên Windows 10 Sử dụng, chia sẻ, hỏi đáp 0
Admin Hướng dẫn fix lỗi tắt kiếm tiền (TKT) lỗi quốc gia Ad Breaks Tut, tool, mmo 0
Admin Hướng dẫn fix lỗi đăng nhập từ iPhone đã ghép nối trên Watch OS 6.2.6 Điện thoại di động 0
Admin [ShikiSuen] Chinese HTML Lang Tag Fix Xenforo 0
Admin Hướng dẫn fix lỗi ABI mismatch xamarin forms Android, iOS 0
Admin Hướng dẫn fix lỗi The selected build configuration is using the shared Mono runtime for faster deployment Xamarin Forms Android, iOS 0
Admin Hướng dẫn fix lỗi WHPX is not configured khi dùng Visual Studio 2019 Android, ios, java, windows phone 0
Admin Hướng dẫn fix lỗi We couldn’t complete the updates, Undoing changes không vào được windows Hệ điều hành 0
Admin Hướng dẫn fix lỗi không hiện IP online xenforo - Fix don't display IP members online xenforo Xenforo 0
Admin Hướng dẫn sửa Oppo A3s và Realme C1 tắt nguồn bật không lên - How to fix A3s and Realme C1 turned off and brick Thủ thuật ĐTDĐ 0
Admin XenForo 2.0.11 Released (Security Fix) Xenforo 0
Admin Hướng dẫn fix lỗi xinput1_3.dll or d3dx9_43.dll is missing khi chơi PES 2017 - How to fix xinput1_3.dll or d3dx9_43.dll is missing simple Thủ thuật máy tính 0
Admin How to fix MySQL query error [1062]: Duplicate entry '***' for key 'expiry_date' xenforo 2 Xenforo 0
Admin How to fix XF\Db\Exception: MySQL query error [1932]: Table 'xf_phrase_map' doesn't exist Xenforo 0
Admin Hướng dẫn fix lỗi XSS trên Xenforo 2.0.9 - XenForo 2.0.9 Released (Security Fix) Xenforo 0
Admin Hướng dẫn fix lỗi Not enough memory RAM - An integer between 96 and 8 is required Thiết kế đồ họa 0
Admin Hướng dẫn fix lỗi /includes/vfchh/php/vfc_hide_core.php on line 163 mod vFCoders - Hide Hack v4 Vbb tutorial 0
Hong98 Hướng dẫn Cách fix lỗi thời gian chạy sai dạng Timeago All Shared Scripts 1
Admin Hướng dẫn fix lỗi android rung liên tục không ngừng ngày 31.12.2016 Android, iOS 0
SuperTroll Fix link code Xenforo 0
N Có ai biết fix lỗi invalid request trong vuivc.xyz không hỏi cái Trò chuyện linh tinh 0
congtukinhloai Ghoster WinXPSP3PROv2016.6.0 RC2 SATA Fix Win32k.sys Hệ điều hành 0
K Share AvatarQ 250 v8 Hỗ Trợ Event Fix Lỗi Crack, hack, mod, ghép game, ứng dụng 0

Similar threads

New posts New threads New resources

Back
Top