dmitry-kulikov/yii2-braintree

View on GitHub
src/Braintree.php

Summary

Maintainability
B
4 hrs
Test Coverage
<?php

/**
 * @author Anton Tuyakhov <atuyakhov@gmail.com>
 */

namespace tuyakhov\braintree;

use Braintree\Address;
use Braintree\ClientToken;
use Braintree\Configuration;
use Braintree\CreditCard;
use Braintree\Customer;
use Braintree\MerchantAccount;
use Braintree\PaymentMethod;
use Braintree\Plan;
use Braintree\ResourceCollection;
use Braintree\Result\Error;
use Braintree\Result\Successful;
use Braintree\Subscription;
use Braintree\Transaction;
use Braintree\WebhookNotification;
use yii\base\Component;
use yii\base\InvalidConfigException;
use yii\helpers\ArrayHelper;

/**
 * Class Braintree.
 * @package tuyakhov\braintree
 *
 * @property UrlManager $urlManager provides methods for creating of URLs to Braintree; this property is read-only
 */
class Braintree extends Component
{
    public $environment = 'sandbox';
    public $merchantId;
    public $publicKey;
    public $privateKey;

    protected $clientToken;
    protected $options;

    /**
     * @var UrlManager
     */
    protected $urlManager;

    /**
     * @var Plan[] cached plans from Braintree
     */
    protected $plans;

    /**
     * Sets up Braintree configuration from config file.
     * @throws InvalidConfigException
     */
    public function init()
    {
        foreach (['merchantId', 'publicKey', 'privateKey', 'environment'] as $attribute) {
            if ($this->$attribute === null) {
                throw new InvalidConfigException(
                    strtr(
                        '"{class}::{attribute}" cannot be empty.',
                        [
                            '{class}' => static::class,
                            '{attribute}' => '$' . $attribute,
                        ]
                    )
                );
            }
            Configuration::$attribute($this->$attribute);
        }

        parent::init();
    }

    /**
     * Returns the URL manager object.
     * @return UrlManager
     */
    public function getUrlManager(): UrlManager
    {
        if (!isset($this->urlManager)) {
            $this->urlManager = new UrlManager();
        }

        return $this->urlManager;
    }

    public function getClientToken($params = []): string
    {
        if (!isset($this->clientToken)) {
            $this->clientToken = ClientToken::generate($params);
        }

        return $this->clientToken;
    }

    /**
     * Braintree sale function.
     * @param bool $submitForSettlement
     * @param bool $storeInVaultOnSuccess
     * @return array
     */
    public function sale(bool $submitForSettlement = true, bool $storeInVaultOnSuccess = true): array
    {
        $this->options['options']['submitForSettlement'] = $submitForSettlement;
        $this->options['options']['storeInVaultOnSuccess'] = $storeInVaultOnSuccess;
        $result = Transaction::sale($this->options);

        return ['status' => $result->success, 'result' => $result];
    }

    public function saleWithServiceFee($merchantAccountId, $amount, $paymentMethodNonce, $serviceFeeAmount): array
    {
        $result = Transaction::sale(
            [
                'merchantAccountId' => $merchantAccountId,
                'amount' => $amount,
                'paymentMethodNonce' => $paymentMethodNonce,
                'serviceFeeAmount' => $serviceFeeAmount,
            ]
        );

        return ['status' => $result->success, 'result' => $result];
    }

    public function saleWithPaymentNonce($amount, $paymentMethodNonce): array
    {
        $result = Transaction::sale(
            [
                'amount' => $amount,
                'paymentMethodNonce' => $paymentMethodNonce,
                'options' => [
                    'submitForSettlement' => true,
                    'storeInVaultOnSuccess' => true,
                ],
            ]
        );

        return ['status' => $result->success, 'result' => $result];
    }

    public function savePaymentMethod(): array
    {
        $result = PaymentMethod::create($this->options['paymentMethod']);

        return ['status' => $result->success, 'result' => $result];
    }

    public function updatePaymentMethod(): array
    {
        $result = PaymentMethod::update($this->options['paymentMethodToken'], $this->options['paymentMethod']);

        return ['status' => $result->success, 'result' => $result];
    }

    public function deletePaymentMethod(): array
    {
        $result = PaymentMethod::delete($this->options['paymentMethodToken']);

        return ['status' => $result->success, 'result' => $result];
    }

    /**
     * This method saves the customer to the Braintree and returns the resulting array.
     * @return array
     */
    public function saveCustomer(): array
    {
        if (isset($this->options['customerId'])) {
            $this->options['customer']['id'] = $this->options['customerId'];
        }
        $result = Customer::create($this->options['customer']);

        return ['status' => $result->success, 'result' => $result];
    }

    /**
     * This method saves the credit card to the Braintree and returns the resulting array.
     * @return array
     */
    public function saveCreditCard(): array
    {
        $sendArray = $this->options['creditCard'];
        if (isset($this->options['billing'])) {
            $sendArray['billingAddress'] = $this->options['billing'];
        }
        if (isset($this->options['customerId'])) {
            $sendArray['customerId'] = $this->options['customerId'];
        }
        $result = CreditCard::create($sendArray);

        return ['status' => $result->success, 'result' => $result];
    }

    public function saveAddress(): array
    {
        $sendArray = $this->options['billing'];
        if (isset($this->options['customerId'])) {
            $sendArray['customerId'] = $this->options['customerId'];
        }
        $result = Address::create($sendArray);

        return ['status' => $result->success, 'result' => $result];
    }

    /**
     * Constructs the array of credit card data for payment.
     * @param array $values credit card data, an array with structure:
     * [
     *     'number' => int (required) credit card number
     *     'cvv' => int (optional) credit card security code
     *     'expirationMonth' => int (optional) format: MM
     *         (use 'expirationMonth' and 'expirationYear' or 'expirationDate', not all at once)
     *     'expirationYear' => int (optional) format: YYYY
     *         (use 'expirationMonth' and 'expirationYear' or 'expirationDate', not all at once)
     *     'expirationDate' => string (optional) format: MM/YYYY
     *         (use 'expirationMonth' and 'expirationYear' or 'expirationDate', not all at once)
     *     'cardholderName' => string (optional) the cardholder name associated with the credit card
     * ]
     */
    public function setCreditCard(array $values)
    {
        $creditCard = ['number' => $values['number']];
        $optionalParamNames = ['cvv', 'expirationMonth', 'expirationYear', 'expirationDate', 'cardholderName'];
        foreach ($optionalParamNames as $optionalParamName) {
            $optionalValue = ArrayHelper::getValue($values, $optionalParamName);
            if (isset($optionalValue)) {
                $creditCard[$optionalParamName] = $optionalValue;
            }
        }
        $this->options['creditCard'] = $creditCard;
    }

    /**
     * @param array $values
     * @return $this
     */
    public function setOptions(array $values): Braintree
    {
        if (!empty($values)) {
            foreach ($values as $key => $value) {
                if ($key === 'amount') {
                    $this->setAmount($values['amount']);
                } elseif ($key === 'creditCard') {
                    $this->setCreditCard($values['creditCard']);
                } else {
                    $this->options[$key] = $value;
                }
            }
        }

        return $this;
    }

    /**
     * Set the amount to charge.
     * @param float $amount no currency sign needed
     */
    public function setAmount(float $amount)
    {
        $this->options['amount'] = round($amount, 2);
    }

    /**
     * @param bool $allowCaching whether to allow caching the result of retrieving of data from Braintree;
     * when this parameter is true (default), if data was retrieved before,
     * result will be directly returned when calling this method;
     * if this parameter is false, this method will always perform request to Braintree to obtain the up-to-date data;
     * note that this caching is effective only within the same HTTP request
     * @return Plan[]
     */
    public function getAllPlans(bool $allowCaching = true): array
    {
        if (!$allowCaching || !isset($this->plans)) {
            $this->plans = Plan::all();
        }

        return $this->plans;
    }

    /**
     * @param bool $allowCaching whether to allow caching the result of retrieving of data from Braintree;
     * when this parameter is true (default), if data was retrieved before,
     * result will be directly returned when calling this method;
     * if this parameter is false, this method will always perform request to Braintree to obtain the up-to-date data;
     * note that this caching is effective only within the same HTTP request
     * @return array
     */
    public function getPlanIds(bool $allowCaching = true): array
    {
        $plans = $this->getAllPlans($allowCaching);
        $planIds = [];
        foreach ($plans as $plan) {
            $planIds[] = $plan->id;
        }

        return $planIds;
    }

    /**
     * @param string $planId
     * @param bool $allowCaching whether to allow caching the result of retrieving of data from Braintree;
     * when this parameter is true (default), if data was retrieved before,
     * result will be directly returned when calling this method;
     * if this parameter is false, this method will always perform request to Braintree to obtain the up-to-date data;
     * note that this caching is effective only within the same HTTP request
     * @return null|Plan
     */
    public function getPlanById(string $planId, bool $allowCaching = true): ?Plan
    {
        $plans = $this->getAllPlans($allowCaching);
        foreach ($plans as $plan) {
            if ($plan->id === $planId) {
                return $plan;
            }
        }

        return null;
    }

    /**
     * Finds transaction by id.
     * @param string $id
     * @return Transaction
     */
    public function findTransaction(string $id): Transaction
    {
        return Transaction::find($id);
    }

    public function searchTransaction($params = []): ResourceCollection
    {
        return Transaction::search($params);
    }

    /**
     * @param string $merchantId
     * @return MerchantAccount
     */
    public function findMerchant(string $merchantId): MerchantAccount
    {
        return MerchantAccount::find($merchantId);
    }

    /**
     * @param string $customerId
     * @return Customer
     */
    public function findCustomer(string $customerId): Customer
    {
        return Customer::find($customerId);
    }

    public function findSubscription(string $subscriptionId): Subscription
    {
        return Subscription::find($subscriptionId);
    }

    public function searchSubscription($params = []): ResourceCollection
    {
        return Subscription::search($params);
    }

    /**
     * Create subscription.
     * @param array $params
     * @return Error|Successful
     */
    public function createSubscription(array $params)
    {
        return Subscription::create($params);
    }

    /**
     * Update subscription.
     * @param string $subscriptionId
     * @param array $params
     * @return Error|Successful
     */
    public function updateSubscription(string $subscriptionId, array $params)
    {
        return Subscription::update($subscriptionId, $params);
    }

    /**
     * Cancel subscription.
     * @param string $subscriptionId
     * @return Error|Successful
     */
    public function cancelSubscription(string $subscriptionId)
    {
        return Subscription::cancel($subscriptionId);
    }

    public function retryChargeSubscription(string $subscriptionId, $amount)
    {
        $retryResult = Subscription::retryCharge($subscriptionId, $amount);

        if ($retryResult->success) {
            $result = Transaction::submitForSettlement($retryResult->transaction->id);

            return $result;
        }

        return $retryResult;
    }

    public function parseWebhookNotification(string $signature, $payload): WebhookNotification
    {
        return WebhookNotification::parse($signature, $payload);
    }
}