Fixed Easier way of extending the PayPal payment variables

Affected version
2.0.2

DragonByte Tech

Well-known member
At the moment, there is no way for me to add additional parameters to the $params array used in XF\Payment\PayPal without overriding the entire initiatePayment function.

Could you please change the code to the following:
PHP:
/**
     * @param Controller $controller
     * @param PurchaseRequest $purchaseRequest
     * @param Purchase $purchase
     *
     * @return array
     */
    protected function _getParams(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;
    }
   
    /**
     * @param Controller $controller
     * @param PurchaseRequest $purchaseRequest
     * @param Purchase $purchase
     *
     * @return mixed
     */
    public function initiatePayment(Controller $controller, PurchaseRequest $purchaseRequest, Purchase $purchase)
    {
        $params = $this->_getParams($purchaseRequest, $purchase);
       
        $endpointUrl = $this->getApiEndpoint();
        $endpointUrl .= '?' . http_build_query($params);
        return $controller->redirect($endpointUrl, '');
    }
So that it can be extended without issue?

Use case: I want to specify tax rate, which I could do by adding $params['tax'] = '50.00'; (flat rate) or $params['tax_rate'] = '13.00'; (percent), but I have to destroy the work of anyone else also extending the same function just to add this variable.

Thanks!


Fillip
 
Last edited:

Snog

Well-known member
@Chris D could you be sure that this change is noted in the release notes when it is released.

I have an add-on that does something similar and I'll need to know when this happens so I can change my add-on.
 

DragonByte Tech

Well-known member
@Chris D could you be sure that this change is noted in the release notes when it is released.

I have an add-on that does something similar and I'll need to know when this happens so I can change my add-on.
Will our addons conflict just now then?

This is the exact code I run with just now:
PHP:
<?php

namespace DBTech\eCommerce\XF\Payment;

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

/**
* Class Register
*
* @package DBTech\eCommerce\XF\Payment
*/
class PayPal extends XFCP_PayPal
{
    /**
     * @param PurchaseRequest $purchaseRequest
     * @param Purchase $purchase
     *
     * @return array
     */
    protected function _getParams(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;
        }
     
        if (isset($purchase->extraData['tax_rate']))
        {
            $params['tax_rate'] = $purchase->extraData['tax_rate'];
        }
     
        return $params;
    }
 
    /**
     * @param Controller $controller
     * @param PurchaseRequest $purchaseRequest
     * @param Purchase $purchase
     *
     * @return mixed
     */
    public function initiatePayment(Controller $controller, PurchaseRequest $purchaseRequest, Purchase $purchase)
    {
        $params = $this->_getParams($purchaseRequest, $purchase);
     
        $endpointUrl = $this->getApiEndpoint();
        $endpointUrl .= '?' . http_build_query($params);
        return $controller->redirect($endpointUrl, '');
    }
}
If you also override the initiatePayment function, maybe we should both add a check for is_callable('parent::_getParams') and return the $params array?

Something like
PHP:
        if (is_callable('parent::_getParams'))
        {
            $params = parent::_getParams($controller, $purchaseRequest, $purchase);
        }
        else
        {
            $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;
            }
        }
(Obviously no idea if that would work, but I think so?)

I doubt eCommerce will be released to the wider public before 2.0.3 is released, if they do plan to release another bugfix version before locking things down for 2.1, but it might be worth figuring this out between ourselves just in case :)


Fillip
 
Last edited:

Snog

Well-known member
As it is now, for the most part, I completely take over the PayPal payment method. So, yes they would conflict. Once XenForo makes the change, I don't see them conflicting unless we end up adding the same data (item_number to be exact),

And I also take over the setupCallback (\XF\Http\Request $request) function to obtain information that is sent by PayPal that XenForo does not include in that function. IE: mc_fee

This is the full list of what I filter in that function...
Code:
$request->filter('mc_fee', 'unum');
$request->filter('item_name', 'str');
$request->filter('item_number', 'str');
$request->filter('payer_email', 'str');
$request->filter('first_name', 'str');
$request->filter('last_name', 'str');
$request->filter('payer_business_name', 'str');
$request->filter('address_street', 'str');
$request->filter('address_city', 'str');
$request->filter('address_state', 'str');
$request->filter('address_zip', 'str');
$request->filter('address_country', 'str');
$request->filter('payment_type', 'str');
Note that the item_number addition is only for my own use. However, everything else is used by clients that have the add-on,
 

Snog

Well-known member
Just a quick note...

@DragonByte Tech and I discussed this via PC and the Controller $controller, in the _getParams function isn't needed.

Just mentioning it to keep compatibility when this is released by XenForo. ;)
 

Chris D

XenForo developer
Staff member
Probably worth noting that we'd probably call it getPaymentParams() and note that we wouldn't prefix it with an underscore as we've tended to avoid that convention for protected methods. It may also be an abstract method and we may also call it from the other handlers too (albeit for view reply params in most cases rather than redirect link params).
 

Snog

Well-known member
Probably worth noting that we'd probably call it getPaymentParams() and note that we wouldn't prefix it with an underscore as we've tended to avoid that convention for protected methods. It may also be an abstract method and we may also call it from the other handlers too (albeit for view reply params in most cases rather than redirect link params).
Noted and changed in my add-on.
 
Top