Forum

Thread tagged as: Question, Runway, Shop

Braintree Checkout add custom field

Hello,

I am using Braintree for checkout and have replaced the Drop In UI with the Hosted Fields implementation which essentially is changing the gateway template and adding some more javascript. This works fine.

What I want to do is add a custom field for the Card Holder Name which is not included by default. According to their support, the way to do that is

add a standard form inputs inside your form, then they'd be passed together in your POST variable

The code example they provide for passing custom fields:

$customerCreation = Braintree_Customer::create([
   'firstName' => $_POST['firstName'],
   'lastName' => $_POST['lastName'],
   'email' => $_POST['email'],
   'phone' => $_POST['phone'],
   'paymentMethodNonce' => $_POST['payment_method_nonce'],
   'creditCard' => [
      'billingAddress' => [
         'firstName' => $_POST['firstName'],
         'lastName' => $_POST['lastName'],
         'streetAddress' => $_POST['billingStreetAddress'],
         'locality' => $_POST['billingCity'],
         'region' => $_POST['billingState'],
         'postalCode' => $_POST['billingPostal']
      ]
   ]
]);
  • happens to be a Customer Create call, but the concept is similar

Can you deduce from the above whether it is or not possible to send custom fields to Braintree without hacking Perch's core?

I understand this is something outside your support boundaries in any case. If this is absolutely not possible, is there any other way I could at least save this field in Perch instead of sending it to Braintree?

Many thanks in advance!!

Proko Mountrichas

Proko Mountrichas 3 points

  • 4 years ago
Drew McLellan

Drew McLellan 2638 points
Perch Support

I think this will come down to what Omnipay supports. If you look at the "Driver specific usage" section in the docs, it looks like it should be possible: https://github.com/thephpleague/omnipay-braintree

You'd need to create your own gateway driver to extend the default braintree one:

<?php
class PerchShopGateway_custombraintree extends PerchShopGateway_braintree 
{

}

And then implement a custom get_payment_card() method to do whatever you need to do. The default is:

public function get_payment_card($Order)
    {
        $Customers = new PerchShop_Customers($this->api);
        $Customer = $Customers->find($Order->customerID());

        $Addresses = new PerchShop_Addresses($this->api);

        $ShippingAddr = $Addresses->find((int)$Order->orderShippingAddress());
        $BillingAddr  = $Addresses->find((int)$Order->orderBillingAddress());

        $data = [
            'firstName'        => $Customer->customerFirstName(),
            'lastName'         => $Customer->customerLastName(),
            'billingAddress1'  => $BillingAddr->get('address_1'),
            'billingAddress2'  => $BillingAddr->get('address_2'),
            'billingCity'      => $BillingAddr->get('city'),
            'billingPostcode'  => $BillingAddr->get('postcode'),
            'billingState'     => $BillingAddr->get('county'),
            'billingCountry'   => $BillingAddr->get_country_iso2(),
            'shippingAddress1' => $ShippingAddr->get('address_1'),
            'shippingAddress2' => $ShippingAddr->get('address_2'),
            'shippingCity'     => $ShippingAddr->get('city'),
            'shippingPostcode' => $ShippingAddr->get('postcode'),
            'shippingState'    => $ShippingAddr->get('county'),
            'shippingCountry'  => $ShippingAddr->get_country_iso2(),
            'company'          => $BillingAddr->get('addressCompany'),
            'email'            => $Customer->customerEmail(),
        ];

        $card = new CreditCard($data);

        return $card;
    }

That is encouraging but not so easy for a front-end developer :)

So I created my custom driver including only the default method I need to edit and also use Omnipay\Common\CreditCard; because otherwise the $card = new CreditCard($data); was giving error.

<?php

use Omnipay\Common\CreditCard;

class PerchShopGateway_prokobraintree extends PerchShopGateway_braintree
{
    protected $slug = 'prokobraintree';
    public $omnipay_name = 'Braintree';

    public function get_payment_card($Order)
    {
        $Customers = new PerchShop_Customers($this->api);
        $Customer = $Customers->find($Order->customerID());

        $Addresses = new PerchShop_Addresses($this->api);

        $ShippingAddr = $Addresses->find((int)$Order->orderShippingAddress());
        $BillingAddr  = $Addresses->find((int)$Order->orderBillingAddress());

        $data = [
            'firstName'        => $Customer->customerFirstName(),
            'lastName'         => $Customer->customerLastName(),
            'billingAddress1'  => $BillingAddr->get('address_1'),
            'billingAddress2'  => $BillingAddr->get('address_2'),
            'billingCity'      => $BillingAddr->get('city'),
            'billingPostcode'  => $BillingAddr->get('postcode'),
            'billingState'     => $BillingAddr->get('county'),
            'billingCountry'   => $BillingAddr->get_country_iso2(),
            'shippingAddress1' => $ShippingAddr->get('address_1'),
            'shippingAddress2' => $ShippingAddr->get('address_2'),
            'shippingCity'     => $ShippingAddr->get('city'),
            'shippingPostcode' => $ShippingAddr->get('postcode'),
            'shippingState'    => $ShippingAddr->get('county'),
            'shippingCountry'  => $ShippingAddr->get_country_iso2(),
            'company'          => $BillingAddr->get('addressCompany'),
            'email'            => $Customer->customerEmail(),
        ];

        $card = new CreditCard($data);

        return $card;
    }
}

Also added the new slug to shop.php and checkout.php

But when loading the checkout page I now get

Fatal error: Uncaught Braintree\Exception\Configuration: Braintree\Configuration::merchantId needs to be set (or accessToken needs to be passed to Braintree\Gateway). in ... Braintree/Configuration.php on line 223

I have not touched the Merchant ID at all. Do you see something missing from the new driver? Anywhere else it has to be referenced maybe?

Drew McLellan

Drew McLellan 2638 points
Perch Support

You changed the slug - I don't think you should. You essentially want everything to be the same as standard.

If I don't change the slug everything works as before. I understand it just ignores the new driver (?). How does Perch know which gateway to use if I don't specify it in shop.php?

Drew McLellan

Drew McLellan 2638 points
Perch Support

You would use perch_shop_checkout('prokobraintree', [...])

So, I do as you said but leave shop.php as it is 'gateways' => ['braintree' => [...]]. Still getting the Fatal error I mentioned before.

Drew McLellan

Drew McLellan 2638 points
Perch Support

What is shop.php ? The config file?

Yes, the gateway settings in config directory.

Drew McLellan

Drew McLellan 2638 points
Perch Support

So it sounds like you're not matching the config settings in the file. What happens if you update the config so that your code finds the merchant ID it's looking for?

I don't think I need to update the config here. And if I do the only thing I can change is the "braintree" slug:

shop.php

    'gateways' => [
            'braintree' => [
                'enabled'   => true,
                'test_mode' => true,
                'live' => [
                  'merchantId'  => 'XXXXXX',
                  'publicKey'   => 'XXXX',
                  'privateKey'  => 'XXXXXXXXXXXXXXXXXX',
                ],
                'test' => [
                  'merchantId'  => 'XXXXXXXXX',
                  'publicKey'   => 'XXXXXXXXX',
                  'privateKey'  => 'XXXXXXXXXXXXXXXXXX',
                ],
            ],
        ],

If I understand well this slug is being used in the default gateway class

PerchShopGateway_default.class.php

public function get_omnipay_instance()
    {
        $config = PerchShop_Config::get('gateways', $this->slug);
        $Omnipay = Omnipay::create($this->omnipay_name);
        $this->set_credentials($Omnipay, $config);
        return $Omnipay;
    }

Here it creates the $config which then passes to set_credentials() which is called in the braintree class.

PerchShopGateway_braintree.class.php

public function set_credentials(&$Omnipay, $config)
    {
        if ($config['test_mode']) {
            $Omnipay->setMerchantId($config['test']['merchantId']);
            $Omnipay->setPublicKey($config['test']['publicKey']);
            $Omnipay->setPrivateKey($config['test']['privateKey']);
            $Omnipay->setTestMode(true);
        }else{
            $Omnipay->setMerchantId($config['live']['merchantId']);
            $Omnipay->setPublicKey($config['live']['publicKey']);
            $Omnipay->setPrivateKey($config['live']['privateKey']);
            $Omnipay->setTestMode(false);
        }
    }

If at the end it complains about unset merchantId, it should be because the slug is wrong right?

But then the error still appears if I use a custom slug for my custom driver. Does all this make any sense to you?

Drew McLellan

Drew McLellan 2638 points
Perch Support

If it complains about merchantId then it's not getting the correct config.

I can't really debug your code for you through forum posts - that's never going to be efficient. If you can't get it working, you'll need to either find a developer to take the work on, or make do without it.

Yeah, I don't even have proper php debugging tools here. Thanks for trying anyway!