src/Braintree.php
<?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);
}
}