diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b281d66
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+# Created by .ignore support plugin (hsz.mobi)
+.idea/
+.DS_Store
diff --git a/README.md b/README.md
index 991f90f..c83af9c 100644
--- a/README.md
+++ b/README.md
@@ -17,9 +17,12 @@ Braintree Payments is a payment gateway provider owned by eBAY Inc, which allows
* Supports both production and sandbox environments, enabling you to test payments before going live
* Form entries will only be created when payment is successful
* Quick and easy setup
+* Tax Exempt form feed setting for non-profits
+* Advanced Fraud Prevention integration
## Subscriptions
-The plugin does not currently support Braintree Subscriptions. Keep a look out for it in a future version
+
+* Includes basic subscription functionality that searches for plans in the account and attempts to match the dollar amount of a plan to the amount specified.
## Upgrade Notice
If you are updating from a version previous to 1.0, your existing feeds will not work. Please make sure you check all your feeds and ensure they function correctly.
diff --git a/assets/js/braintree-data-processing.js b/assets/js/braintree-data-processing.js
new file mode 100644
index 0000000..ad8a9da
--- /dev/null
+++ b/assets/js/braintree-data-processing.js
@@ -0,0 +1,33 @@
+/**
+ * Used for Advanced Fraud Tools integration.
+ *
+ * @since 1.4.0
+ */
+jQuery( function($) {
+ braintree.client.create({
+ authorization: braintree_data_processing_strings.bt_magic
+ }, function (err, clientInstance) {
+ // Creation of any other components...
+
+ // Inside of your client create callback...
+ braintree.dataCollector.create({
+ client: clientInstance,
+ kount: true
+ }, function (err, dataCollectorInstance) {
+ if (err) {
+ // Handle error in data collector creation
+ return;
+ }
+ var deviceDataInput = jQuery("[name=" + braintree_data_processing_strings.bt_field + "]");
+
+ if (deviceDataInput == null) {
+ deviceDataInput = document.createElement('input');
+ deviceDataInput.name = 'device_data';
+ deviceDataInput.type = 'hidden';
+ form.appendChild(deviceDataInput);
+ }
+
+ deviceDataInput.val(dataCollectorInstance.deviceData);
+ });
+ });
+});
diff --git a/gravity-forms-braintree.php b/gravity-forms-braintree.php
index 45534e9..7fb990e 100644
--- a/gravity-forms-braintree.php
+++ b/gravity-forms-braintree.php
@@ -4,7 +4,7 @@
Plugin URI: http://plugify.io/
Description: Allow your customers to purchase goods and services through Gravity Forms via Braintree Payments
Author: Plugify
-Version: 1.1.1
+Version: 1.4.0
Author URI: http://plugify.io
*/
diff --git a/lib/.DS_Store b/lib/.DS_Store
new file mode 100644
index 0000000..5008ddf
Binary files /dev/null and b/lib/.DS_Store differ
diff --git a/lib/Braintree.php b/lib/Braintree.php
index e940d65..066107a 100644
--- a/lib/Braintree.php
+++ b/lib/Braintree.php
@@ -1,181 +1,24 @@
_attributes)) {
- return $this->_attributes[$name];
- }
- else {
- trigger_error('Undefined property on ' . get_class($this) . ': ' . $name, E_USER_NOTICE);
- return null;
- }
- }
-
- /**
- * used by isset() and empty()
- * @access public
- * @param string $name property name
- * @return boolean
- */
- public function __isset($name)
- {
- return array_key_exists($name, $this->_attributes);
- }
-
- public function _set($key, $value)
- {
- $this->_attributes[$key] = $value;
- }
- /**
- *
- * @param string $className
- * @param object $resultObj
- * @return object returns the passed object if successful
- * @throws Braintree_Exception_ValidationsFailed
- */
- public static function returnObjectOrThrowException($className, $resultObj)
- {
- $resultObjName = Braintree_Util::cleanClassName($className);
- if ($resultObj->success) {
- return $resultObj->$resultObjName;
- } else {
- throw new Braintree_Exception_ValidationsFailed();
- }
- }
-}
-require_once('Braintree/Modification.php');
-require_once('Braintree/Instance.php');
+require_once(__DIR__ . DIRECTORY_SEPARATOR . 'autoload.php');
-require_once('Braintree/Address.php');
-require_once('Braintree/AddOn.php');
-require_once('Braintree/Collection.php');
-require_once('Braintree/Configuration.php');
-require_once('Braintree/CreditCard.php');
-require_once('Braintree/Customer.php');
-require_once('Braintree/CustomerSearch.php');
-require_once('Braintree/DisbursementDetails.php');
-require_once('Braintree/Descriptor.php');
-require_once('Braintree/Digest.php');
-require_once('Braintree/Discount.php');
-require_once('Braintree/IsNode.php');
-require_once('Braintree/EqualityNode.php');
-require_once('Braintree/Exception.php');
-require_once('Braintree/Http.php');
-require_once('Braintree/KeyValueNode.php');
-require_once('Braintree/MerchantAccount.php');
-require_once('Braintree/MerchantAccount/BusinessDetails.php');
-require_once('Braintree/MerchantAccount/FundingDetails.php');
-require_once('Braintree/MerchantAccount/IndividualDetails.php');
-require_once('Braintree/MerchantAccount/AddressDetails.php');
-require_once('Braintree/MultipleValueNode.php');
-require_once('Braintree/MultipleValueOrTextNode.php');
-require_once('Braintree/PartialMatchNode.php');
-require_once('Braintree/Plan.php');
-require_once('Braintree/RangeNode.php');
-require_once('Braintree/ResourceCollection.php');
-require_once('Braintree/SettlementBatchSummary.php');
-require_once('Braintree/Subscription.php');
-require_once('Braintree/SubscriptionSearch.php');
-require_once('Braintree/TextNode.php');
-require_once('Braintree/Transaction.php');
-require_once('Braintree/Disbursement.php');
-require_once('Braintree/TransactionSearch.php');
-require_once('Braintree/TransparentRedirect.php');
-require_once('Braintree/Util.php');
-require_once('Braintree/Version.php');
-require_once('Braintree/Xml.php');
-require_once('Braintree/Error/Codes.php');
-require_once('Braintree/Error/ErrorCollection.php');
-require_once('Braintree/Error/Validation.php');
-require_once('Braintree/Error/ValidationErrorCollection.php');
-require_once('Braintree/Exception/Authentication.php');
-require_once('Braintree/Exception/Authorization.php');
-require_once('Braintree/Exception/Configuration.php');
-require_once('Braintree/Exception/DownForMaintenance.php');
-require_once('Braintree/Exception/ForgedQueryString.php');
-require_once('Braintree/Exception/InvalidSignature.php');
-require_once('Braintree/Exception/NotFound.php');
-require_once('Braintree/Exception/ServerError.php');
-require_once('Braintree/Exception/SSLCertificate.php');
-require_once('Braintree/Exception/SSLCaFileNotFound.php');
-require_once('Braintree/Exception/Unexpected.php');
-require_once('Braintree/Exception/UpgradeRequired.php');
-require_once('Braintree/Exception/ValidationsFailed.php');
-require_once('Braintree/Result/CreditCardVerification.php');
-require_once('Braintree/Result/Error.php');
-require_once('Braintree/Result/Successful.php');
-require_once('Braintree/Test/CreditCardNumbers.php');
-require_once('Braintree/Test/MerchantAccount.php');
-require_once('Braintree/Test/TransactionAmounts.php');
-require_once('Braintree/Test/VenmoSdk.php');
-require_once('Braintree/Transaction/AddressDetails.php');
-require_once('Braintree/Transaction/CreditCardDetails.php');
-require_once('Braintree/Transaction/CustomerDetails.php');
-require_once('Braintree/Transaction/StatusDetails.php');
-require_once('Braintree/Transaction/SubscriptionDetails.php');
-require_once('Braintree/WebhookNotification.php');
-require_once('Braintree/WebhookTesting.php');
-require_once('Braintree/Xml/Generator.php');
-require_once('Braintree/Xml/Parser.php');
-require_once('Braintree/CreditCardVerification.php');
-require_once('Braintree/CreditCardVerificationSearch.php');
-require_once('Braintree/PartnerMerchant.php');
-
-if (version_compare(PHP_VERSION, '5.2.1', '<')) {
- throw new Braintree_Exception('PHP version >= 5.2.1 required');
+if (version_compare(PHP_VERSION, '5.4.0', '<')) {
+ throw new Braintree_Exception('PHP version >= 5.4.0 required');
}
-
-function requireDependencies() {
- $requiredExtensions = array('xmlwriter', 'SimpleXML', 'openssl', 'dom', 'hash', 'curl');
- foreach ($requiredExtensions AS $ext) {
- if (!extension_loaded($ext)) {
- throw new Braintree_Exception('The Braintree library requires the ' . $ext . ' extension.');
+class Braintree {
+ public static function requireDependencies() {
+ $requiredExtensions = ['xmlwriter', 'openssl', 'dom', 'hash', 'curl'];
+ foreach ($requiredExtensions AS $ext) {
+ if (!extension_loaded($ext)) {
+ throw new Braintree_Exception('The Braintree library requires the ' . $ext . ' extension.');
+ }
}
}
}
-requireDependencies();
+Braintree::requireDependencies();
diff --git a/lib/Braintree/AccountUpdaterDailyReport.php b/lib/Braintree/AccountUpdaterDailyReport.php
new file mode 100644
index 0000000..226bdfe
--- /dev/null
+++ b/lib/Braintree/AccountUpdaterDailyReport.php
@@ -0,0 +1,43 @@
+_attributes = $disputeAttribs;
+ }
+
+ public static function factory($attributes)
+ {
+ $instance = new self();
+ $instance->_initialize($attributes);
+ return $instance;
+ }
+
+ public function __toString()
+ {
+ $display = [
+ 'reportDate', 'reportUrl'
+ ];
+
+ $displayAttributes = [];
+ foreach ($display AS $attrib) {
+ $displayAttributes[$attrib] = $this->$attrib;
+ }
+ return __CLASS__ . '[' .
+ Util::attributesToString($displayAttributes) .']';
+ }
+}
+class_alias('Braintree\AccountUpdaterDailyReport', 'Braintree_AccountUpdaterDailyReport');
diff --git a/lib/Braintree/AchMandate.php b/lib/Braintree/AchMandate.php
new file mode 100644
index 0000000..86eeea6
--- /dev/null
+++ b/lib/Braintree/AchMandate.php
@@ -0,0 +1,55 @@
+_attributes) . ']';
+ }
+
+ /**
+ * sets instance properties from an array of values
+ *
+ * @ignore
+ * @access protected
+ * @param array $achAttribs array of achMandate data
+ * @return void
+ */
+ protected function _initialize($achAttribs)
+ {
+ // set the attributes
+ $this->_attributes = $achAttribs;
+ }
+
+ /**
+ * factory method: returns an instance of AchMandate
+ * to the requesting method, with populated properties
+ * @ignore
+ * @return AchMandate
+ */
+ public static function factory($attributes)
+ {
+ $instance = new self();
+ $instance->_initialize($attributes);
+ return $instance;
+
+ }
+}
+class_alias('Braintree\AchMandate', 'Braintree_Mandate');
diff --git a/lib/Braintree/AddOn.php b/lib/Braintree/AddOn.php
index 98a4c47..3eb5612 100644
--- a/lib/Braintree/AddOn.php
+++ b/lib/Braintree/AddOn.php
@@ -1,22 +1,43 @@
$response['addOns']);
-
- return Braintree_Util::extractAttributeAsArray(
- $addOns,
- 'addOn'
- );
- }
+namespace Braintree;
+/**
+ * @property-read string $amount
+ * @property-read \DateTime $createdAt
+ * @property-read int|null $currentBillingCycle
+ * @property-read string $description
+ * @property-read string $id
+ * @property-read string|null $kind
+ * @property-read string $merchantId
+ * @property-read string $name
+ * @property-read boolean $neverExpires
+ * @property-read int|null $numberOfBillingCycles
+ * @property-read int|null $quantity
+ * @property-read \DateTime $updatedAt
+ */
+class AddOn extends Modification
+{
+ /**
+ *
+ * @param array $attributes
+ * @return AddOn
+ */
public static function factory($attributes)
{
$instance = new self();
$instance->_initialize($attributes);
return $instance;
}
+
+
+ /**
+ * static methods redirecting to gateway
+ *
+ * @return AddOn[]
+ */
+ public static function all()
+ {
+ return Configuration::gateway()->addOn()->all();
+ }
}
+class_alias('Braintree\AddOn', 'Braintree_AddOn');
diff --git a/lib/Braintree/AddOnGateway.php b/lib/Braintree/AddOnGateway.php
new file mode 100644
index 0000000..9ecf942
--- /dev/null
+++ b/lib/Braintree/AddOnGateway.php
@@ -0,0 +1,53 @@
+_gateway = $gateway;
+ $this->_config = $gateway->config;
+ $this->_config->assertHasAccessTokenOrKeys();
+ $this->_http = new Http($gateway->config);
+ }
+
+ /**
+ *
+ * @return AddOn[]
+ */
+ public function all()
+ {
+ $path = $this->_config->merchantPath() . '/add_ons';
+ $response = $this->_http->get($path);
+
+ $addOns = ["addOn" => $response['addOns']];
+
+ return Util::extractAttributeAsArray(
+ $addOns,
+ 'addOn'
+ );
+ }
+}
+class_alias('Braintree\AddOnGateway', 'Braintree_AddOnGateway');
diff --git a/lib/Braintree/Address.php b/lib/Braintree/Address.php
index 821959d..ee12524 100644
--- a/lib/Braintree/Address.php
+++ b/lib/Braintree/Address.php
@@ -1,13 +1,9 @@
$attribs)
- );
- }
-
/**
- * attempts the create operation assuming all data will validate
- * returns a Braintree_Address object instead of a Result
- *
- * @access public
- * @param array $attribs
- * @return object
- * @throws Braintree_Exception_ValidationError
- */
- public static function createNoValidate($attribs)
- {
- $result = self::create($attribs);
- return self::returnObjectOrThrowException(__CLASS__, $result);
-
- }
-
- /**
- * delete an address by id
- *
- * @param mixed $customerOrId
- * @param string $addressId
- */
- public static function delete($customerOrId = null, $addressId = null)
- {
- self::_validateId($addressId);
- $customerId = self::_determineCustomerId($customerOrId);
- Braintree_Http::delete(
- '/customers/' . $customerId . '/addresses/' . $addressId
- );
- return new Braintree_Result_Successful();
- }
-
- /**
- * find an address by id
- *
- * Finds the address with the given addressId that is associated
- * to the given customerOrId.
- * If the address cannot be found, a NotFound exception will be thrown.
- *
- *
- * @access public
- * @param mixed $customerOrId
- * @param string $addressId
- * @return object Braintree_Address
- * @throws Braintree_Exception_NotFound
- */
- public static function find($customerOrId, $addressId)
- {
-
- $customerId = self::_determineCustomerId($customerOrId);
- self::_validateId($addressId);
-
- try {
- $response = Braintree_Http::get(
- '/customers/' . $customerId . '/addresses/' . $addressId
- );
- return self::factory($response['address']);
- } catch (Braintree_Exception_NotFound $e) {
- throw new Braintree_Exception_NotFound(
- 'address for customer ' . $customerId .
- ' with id ' . $addressId . ' not found.'
- );
- }
-
- }
-
- /**
- * returns false if comparing object is not a Braintree_Address,
- * or is a Braintree_Address with a different id
+ * returns false if comparing object is not a Address,
+ * or is a Address with a different id
*
* @param object $other address to compare against
* @return boolean
*/
public function isEqual($other)
{
- return !($other instanceof Braintree_Address) ?
+ return !($other instanceof self) ?
false :
($this->id === $other->id && $this->customerId === $other->customerId);
}
- /**
- * updates the address record
- *
- * if calling this method in static context,
- * customerOrId is the 2nd attribute, addressId 3rd.
- * customerOrId & addressId are not sent in object context.
- *
- *
- * @access public
- * @param array $attributes
- * @param mixed $customerOrId (only used in static call)
- * @param string $addressId (only used in static call)
- * @return object Braintree_Result_Successful or Braintree_Result_Error
- */
- public static function update($customerOrId, $addressId, $attributes)
- {
- self::_validateId($addressId);
- $customerId = self::_determineCustomerId($customerOrId);
- Braintree_Util::verifyKeys(self::updateSignature(), $attributes);
-
- $response = Braintree_Http::put(
- '/customers/' . $customerId . '/addresses/' . $addressId,
- array('address' => $attributes)
- );
-
- return self::_verifyGatewayResponse($response);
-
- }
-
- /**
- * update an address record, assuming validations will pass
- *
- * if calling this method in static context,
- * customerOrId is the 2nd attribute, addressId 3rd.
- * customerOrId & addressId are not sent in object context.
- *
- * @access public
- * @param array $transactionAttribs
- * @param string $customerId
- * @return object Braintree_Transaction
- * @throws Braintree_Exception_ValidationsFailed
- * @see Braintree_Address::update()
- */
- public static function updateNoValidate($customerOrId, $addressId, $attributes)
- {
- $result = self::update($customerOrId, $addressId, $attributes);
- return self::returnObjectOrThrowException(__CLASS__, $result);
- }
-
- /**
- * creates a full array signature of a valid create request
- * @return array gateway create request format
- */
- public static function createSignature()
- {
- return array(
- 'company', 'countryCodeAlpha2', 'countryCodeAlpha3', 'countryCodeNumeric',
- 'countryName', 'customerId', 'extendedAddress', 'firstName',
- 'lastName', 'locality', 'postalCode', 'region', 'streetAddress'
- );
- }
-
- /**
- * creates a full array signature of a valid update request
- * @return array gateway update request format
- */
- public static function updateSignature()
- {
- // TODO: remove customerId from update signature
- return self::createSignature();
-
- }
-
/**
* create a printable representation of the object as:
* ClassName[property=value, property=value]
* @ignore
- * @return var
+ * @return string
*/
public function __toString()
{
return __CLASS__ . '[' .
- Braintree_Util::attributesToString($this->_attributes) .']';
+ Util::attributesToString($this->_attributes) . ']';
}
/**
@@ -227,7 +60,7 @@ public function __toString()
* @ignore
* @access protected
* @param array $addressAttribs array of address data
- * @return none
+ * @return void
*/
protected function _initialize($addressAttribs)
{
@@ -236,117 +69,82 @@ protected function _initialize($addressAttribs)
}
/**
- * verifies that a valid address id is being used
+ * factory method: returns an instance of Address
+ * to the requesting method, with populated properties
* @ignore
- * @param string $id address id
- * @throws InvalidArgumentException
+ * @return Address
*/
- private static function _validateId($id = null)
+ public static function factory($attributes)
{
- if (empty($id) || trim($id) == "") {
- throw new InvalidArgumentException(
- 'expected address id to be set'
- );
- }
- if (!preg_match('/^[0-9A-Za-z_-]+$/', $id)) {
- throw new InvalidArgumentException(
- $id . ' is an invalid address id.'
- );
- }
+ $instance = new self();
+ $instance->_initialize($attributes);
+ return $instance;
+
}
+
+ // static methods redirecting to gateway
+
/**
- * verifies that a valid customer id is being used
- * @ignore
- * @param string $id customer id
- * @throws InvalidArgumentException
+ *
+ * @param array $attribs
+ * @return Address
*/
- private static function _validateCustomerId($id = null)
+ public static function create($attribs)
{
- if (empty($id) || trim($id) == "") {
- throw new InvalidArgumentException(
- 'expected customer id to be set'
- );
- }
- if (!preg_match('/^[0-9A-Za-z_-]+$/', $id)) {
- throw new InvalidArgumentException(
- $id . ' is an invalid customer id.'
- );
- }
-
+ return Configuration::gateway()->address()->create($attribs);
}
/**
- * determines if a string id or Customer object was passed
- * @ignore
- * @param mixed $customerOrId
- * @return string customerId
+ *
+ * @param array $attribs
+ * @return Address
*/
- private static function _determineCustomerId($customerOrId)
+ public static function createNoValidate($attribs)
{
- $customerId = ($customerOrId instanceof Braintree_Customer) ? $customerOrId->id : $customerOrId;
- self::_validateCustomerId($customerId);
- return $customerId;
-
+ return Configuration::gateway()->address()->createNoValidate($attribs);
}
- /* private class methods */
/**
- * sends the create request to the gateway
- * @ignore
- * @param string $url
- * @param array $params
- * @return mixed
+ *
+ * @param Customer|int $customerOrId
+ * @param int $addressId
+ * @throws InvalidArgumentException
+ * @return Result\Successful
*/
- private static function _doCreate($url, $params)
+ public static function delete($customerOrId = null, $addressId = null)
{
- $response = Braintree_Http::post($url, $params);
-
- return self::_verifyGatewayResponse($response);
-
+ return Configuration::gateway()->address()->delete($customerOrId, $addressId);
}
/**
- * generic method for validating incoming gateway responses
- *
- * creates a new Braintree_Address object and encapsulates
- * it inside a Braintree_Result_Successful object, or
- * encapsulates a Braintree_Errors object inside a Result_Error
- * alternatively, throws an Unexpected exception if the response is invalid.
*
- * @ignore
- * @param array $response gateway response values
- * @return object Result_Successful or Result_Error
- * @throws Braintree_Exception_Unexpected
+ * @param Customer|int $customerOrId
+ * @param int $addressId
+ * @throws Exception\NotFound
+ * @return Address
*/
- private static function _verifyGatewayResponse($response)
+ public static function find($customerOrId, $addressId)
{
- if (isset($response['address'])) {
- // return a populated instance of Braintree_Address
- return new Braintree_Result_Successful(
- self::factory($response['address'])
- );
- } else if (isset($response['apiErrorResponse'])) {
- return new Braintree_Result_Error($response['apiErrorResponse']);
- } else {
- throw new Braintree_Exception_Unexpected(
- "Expected address or apiErrorResponse"
- );
- }
-
+ return Configuration::gateway()->address()->find($customerOrId, $addressId);
}
/**
- * factory method: returns an instance of Braintree_Address
- * to the requesting method, with populated properties
- * @ignore
- * @return object instance of Braintree_Address
+ *
+ * @param Customer|int $customerOrId
+ * @param int $addressId
+ * @param array $attributes
+ * @throws Exception\Unexpected
+ * @return Result\Successful|Result\Error
*/
- public static function factory($attributes)
+ public static function update($customerOrId, $addressId, $attributes)
{
- $instance = new self();
- $instance->_initialize($attributes);
- return $instance;
+ return Configuration::gateway()->address()->update($customerOrId, $addressId, $attributes);
+ }
+ public static function updateNoValidate($customerOrId, $addressId, $attributes)
+ {
+ return Configuration::gateway()->address()->updateNoValidate($customerOrId, $addressId, $attributes);
}
}
+class_alias('Braintree\Address', 'Braintree_Address');
diff --git a/lib/Braintree/AddressGateway.php b/lib/Braintree/AddressGateway.php
new file mode 100644
index 0000000..a0256c0
--- /dev/null
+++ b/lib/Braintree/AddressGateway.php
@@ -0,0 +1,314 @@
+_gateway = $gateway;
+ $this->_config = $gateway->config;
+ $this->_config->assertHasAccessTokenOrKeys();
+ $this->_http = new Http($gateway->config);
+ }
+
+
+ /* public class methods */
+ /**
+ *
+ * @access public
+ * @param array $attribs
+ * @return Result\Successful|Result\Error
+ */
+ public function create($attribs)
+ {
+ Util::verifyKeys(self::createSignature(), $attribs);
+ $customerId = isset($attribs['customerId']) ?
+ $attribs['customerId'] :
+ null;
+
+ $this->_validateCustomerId($customerId);
+ unset($attribs['customerId']);
+ try {
+ return $this->_doCreate(
+ '/customers/' . $customerId . '/addresses',
+ ['address' => $attribs]
+ );
+ } catch (Exception\NotFound $e) {
+ throw new Exception\NotFound(
+ 'Customer ' . $customerId . ' not found.'
+ );
+ }
+ }
+
+ /**
+ * attempts the create operation assuming all data will validate
+ * returns a Address object instead of a Result
+ *
+ * @access public
+ * @param array $attribs
+ * @return self
+ * @throws Exception\ValidationError
+ */
+ public function createNoValidate($attribs)
+ {
+ $result = $this->create($attribs);
+ return Util::returnObjectOrThrowException(__CLASS__, $result);
+
+ }
+
+ /**
+ * delete an address by id
+ *
+ * @param mixed $customerOrId
+ * @param string $addressId
+ */
+ public function delete($customerOrId = null, $addressId = null)
+ {
+ $this->_validateId($addressId);
+ $customerId = $this->_determineCustomerId($customerOrId);
+ $path = $this->_config->merchantPath() . '/customers/' . $customerId . '/addresses/' . $addressId;
+ $this->_http->delete($path);
+ return new Result\Successful();
+ }
+
+ /**
+ * find an address by id
+ *
+ * Finds the address with the given addressId that is associated
+ * to the given customerOrId.
+ * If the address cannot be found, a NotFound exception will be thrown.
+ *
+ *
+ * @access public
+ * @param mixed $customerOrId
+ * @param string $addressId
+ * @return Address
+ * @throws Exception\NotFound
+ */
+ public function find($customerOrId, $addressId)
+ {
+
+ $customerId = $this->_determineCustomerId($customerOrId);
+ $this->_validateId($addressId);
+
+ try {
+ $path = $this->_config->merchantPath() . '/customers/' . $customerId . '/addresses/' . $addressId;
+ $response = $this->_http->get($path);
+ return Address::factory($response['address']);
+ } catch (Exception\NotFound $e) {
+ throw new Exception\NotFound(
+ 'address for customer ' . $customerId .
+ ' with id ' . $addressId . ' not found.'
+ );
+ }
+
+ }
+
+ /**
+ * updates the address record
+ *
+ * if calling this method in context,
+ * customerOrId is the 2nd attribute, addressId 3rd.
+ * customerOrId & addressId are not sent in object context.
+ *
+ *
+ * @access public
+ * @param array $attributes
+ * @param mixed $customerOrId (only used in call)
+ * @param string $addressId (only used in call)
+ * @return Result\Successful|Result\Error
+ */
+ public function update($customerOrId, $addressId, $attributes)
+ {
+ $this->_validateId($addressId);
+ $customerId = $this->_determineCustomerId($customerOrId);
+ Util::verifyKeys(self::updateSignature(), $attributes);
+
+ $path = $this->_config->merchantPath() . '/customers/' . $customerId . '/addresses/' . $addressId;
+ $response = $this->_http->put($path, ['address' => $attributes]);
+
+ return $this->_verifyGatewayResponse($response);
+
+ }
+
+ /**
+ * update an address record, assuming validations will pass
+ *
+ * if calling this method in context,
+ * customerOrId is the 2nd attribute, addressId 3rd.
+ * customerOrId & addressId are not sent in object context.
+ *
+ * @access public
+ * @param array $transactionAttribs
+ * @param string $customerId
+ * @return Transaction
+ * @throws Exception\ValidationsFailed
+ * @see Address::update()
+ */
+ public function updateNoValidate($customerOrId, $addressId, $attributes)
+ {
+ $result = $this->update($customerOrId, $addressId, $attributes);
+ return Util::returnObjectOrThrowException(__CLASS__, $result);
+ }
+
+ /**
+ * creates a full array signature of a valid create request
+ * @return array gateway create request format
+ */
+ public static function createSignature()
+ {
+ return [
+ 'company', 'countryCodeAlpha2', 'countryCodeAlpha3', 'countryCodeNumeric',
+ 'countryName', 'customerId', 'extendedAddress', 'firstName',
+ 'lastName', 'locality', 'postalCode', 'region', 'streetAddress'
+ ];
+ }
+
+ /**
+ * creates a full array signature of a valid update request
+ * @return array gateway update request format
+ */
+ public static function updateSignature()
+ {
+ // TODO: remove customerId from update signature
+ return self::createSignature();
+
+ }
+
+ /**
+ * verifies that a valid address id is being used
+ * @ignore
+ * @param string $id address id
+ * @throws InvalidArgumentException
+ */
+ private function _validateId($id = null)
+ {
+ if (empty($id) || trim($id) == "") {
+ throw new InvalidArgumentException(
+ 'expected address id to be set'
+ );
+ }
+ if (!preg_match('/^[0-9A-Za-z_-]+$/', $id)) {
+ throw new InvalidArgumentException(
+ $id . ' is an invalid address id.'
+ );
+ }
+ }
+
+ /**
+ * verifies that a valid customer id is being used
+ * @ignore
+ * @param string $id customer id
+ * @throws InvalidArgumentException
+ */
+ private function _validateCustomerId($id = null)
+ {
+ if (empty($id) || trim($id) == "") {
+ throw new InvalidArgumentException(
+ 'expected customer id to be set'
+ );
+ }
+ if (!preg_match('/^[0-9A-Za-z_-]+$/', $id)) {
+ throw new InvalidArgumentException(
+ $id . ' is an invalid customer id.'
+ );
+ }
+
+ }
+
+ /**
+ * determines if a string id or Customer object was passed
+ * @ignore
+ * @param mixed $customerOrId
+ * @return string customerId
+ */
+ private function _determineCustomerId($customerOrId)
+ {
+ $customerId = ($customerOrId instanceof Customer) ? $customerOrId->id : $customerOrId;
+ $this->_validateCustomerId($customerId);
+ return $customerId;
+
+ }
+
+ /* private class methods */
+ /**
+ * sends the create request to the gateway
+ * @ignore
+ * @param string $subPath
+ * @param array $params
+ * @return Result\Successful|Result\Error
+ */
+ private function _doCreate($subPath, $params)
+ {
+ $fullPath = $this->_config->merchantPath() . $subPath;
+ $response = $this->_http->post($fullPath, $params);
+
+ return $this->_verifyGatewayResponse($response);
+ }
+
+ /**
+ * generic method for validating incoming gateway responses
+ *
+ * creates a new Address object and encapsulates
+ * it inside a Result\Successful object, or
+ * encapsulates an Errors object inside a Result\Error
+ * alternatively, throws an Unexpected exception if the response is invalid
+ *
+ * @ignore
+ * @param array $response gateway response values
+ * @return Result\Successful|Result\Error
+ * @throws Exception\Unexpected
+ */
+ private function _verifyGatewayResponse($response)
+ {
+ if (isset($response['address'])) {
+ // return a populated instance of Address
+ return new Result\Successful(
+ Address::factory($response['address'])
+ );
+ } else if (isset($response['apiErrorResponse'])) {
+ return new Result\Error($response['apiErrorResponse']);
+ } else {
+ throw new Exception\Unexpected(
+ "Expected address or apiErrorResponse"
+ );
+ }
+
+ }
+}
+class_alias('Braintree\AddressGateway', 'Braintree_AddressGateway');
diff --git a/lib/Braintree/AmexExpressCheckoutCard.php b/lib/Braintree/AmexExpressCheckoutCard.php
new file mode 100644
index 0000000..90d48f2
--- /dev/null
+++ b/lib/Braintree/AmexExpressCheckoutCard.php
@@ -0,0 +1,80 @@
+== More information ==
+ *
+ * See {@link https://developers.braintreepayments.com/javascript+php}
+ *
+ * @package Braintree
+ * @category Resources
+ *
+ * @property-read string $bin
+ * @property-read string $cardMemberExpiryDate
+ * @property-read string $cardMemberNumber
+ * @property-read string $cardType
+ * @property-read \DateTime $createdAt
+ * @property-read string $customerId
+ * @property-read boolean $default
+ * @property-read string $expirationMonth
+ * @property-read string $expirationYear
+ * @property-read string $imageUrl
+ * @property-read string $token
+ * @property-read string $sourceDescription
+ * @property-read \Braintree\Subscription[] $subscriptions
+ * @property-read \DateTime $updatedAt
+ */
+class AmexExpressCheckoutCard extends Base
+{
+ /* instance methods */
+ /**
+ * returns false if default is null or false
+ *
+ * @return boolean
+ */
+ public function isDefault()
+ {
+ return $this->default;
+ }
+
+ /**
+ * factory method: returns an instance of AmexExpressCheckoutCard
+ * to the requesting method, with populated properties
+ *
+ * @ignore
+ * @return AmexExpressCheckoutCard
+ */
+ public static function factory($attributes)
+ {
+
+ $instance = new self();
+ $instance->_initialize($attributes);
+ return $instance;
+ }
+
+ /**
+ * sets instance properties from an array of values
+ *
+ * @access protected
+ * @param array $amexExpressCheckoutCardAttribs array of Amex Express Checkout card properties
+ * @return void
+ */
+ protected function _initialize($amexExpressCheckoutCardAttribs)
+ {
+ // set the attributes
+ $this->_attributes = $amexExpressCheckoutCardAttribs;
+
+ $subscriptionArray = [];
+ if (isset($amexExpressCheckoutCardAttribs['subscriptions'])) {
+ foreach ($amexExpressCheckoutCardAttribs['subscriptions'] AS $subscription) {
+ $subscriptionArray[] = Subscription::factory($subscription);
+ }
+ }
+
+ $this->_set('subscriptions', $subscriptionArray);
+ }
+}
+class_alias('Braintree\AmexExpressCheckoutCard', 'Braintree_AmexExpressCheckoutCard');
diff --git a/lib/Braintree/AndroidPayCard.php b/lib/Braintree/AndroidPayCard.php
new file mode 100644
index 0000000..4d73c4d
--- /dev/null
+++ b/lib/Braintree/AndroidPayCard.php
@@ -0,0 +1,90 @@
+== More information ==
+ *
+ * See {@link https://developers.braintreepayments.com/javascript+php}
+ *
+ * @package Braintree
+ * @category Resources
+ *
+ * @property-read string $bin
+ * @property-read string $cardType
+ * @property-read \DateTime $createdAt
+ * @property-read string $customerId
+ * @property-read boolean $default
+ * @property-read string $expirationMonth
+ * @property-read string $expirationYear
+ * @property-read string $googleTransactionId
+ * @property-read string $imageUrl
+ * @property-read string $last4
+ * @property-read string $sourceCardLast4
+ * @property-read string $sourceCardType
+ * @property-read string $sourceDescription
+ * @property-read \Braintree\Subscription[] $subscriptions
+ * @property-read string $token
+ * @property-read \DateTime $updatedAt
+ * @property-read string $virtualCardLast4
+ * @property-read string $virtualCardType
+ */
+class AndroidPayCard extends Base
+{
+ /* instance methods */
+ /**
+ * returns false if default is null or false
+ *
+ * @return boolean
+ */
+ public function isDefault()
+ {
+ return $this->default;
+ }
+
+ /**
+ * factory method: returns an instance of AndroidPayCard
+ * to the requesting method, with populated properties
+ *
+ * @ignore
+ * @return AndroidPayCard
+ */
+ public static function factory($attributes)
+ {
+ $defaultAttributes = [
+ 'expirationMonth' => '',
+ 'expirationYear' => '',
+ 'last4' => $attributes['virtualCardLast4'],
+ 'cardType' => $attributes['virtualCardType'],
+ ];
+
+ $instance = new self();
+ $instance->_initialize(array_merge($defaultAttributes, $attributes));
+ return $instance;
+ }
+
+ /**
+ * sets instance properties from an array of values
+ *
+ * @access protected
+ * @param array $androidPayCardAttribs array of Android Pay card properties
+ * @return void
+ */
+ protected function _initialize($androidPayCardAttribs)
+ {
+ // set the attributes
+ $this->_attributes = $androidPayCardAttribs;
+
+ $subscriptionArray = [];
+ if (isset($androidPayCardAttribs['subscriptions'])) {
+ foreach ($androidPayCardAttribs['subscriptions'] AS $subscription) {
+ $subscriptionArray[] = Subscription::factory($subscription);
+ }
+ }
+
+ $this->_set('subscriptions', $subscriptionArray);
+ }
+}
+class_alias('Braintree\AndroidPayCard', 'Braintree_AndroidPayCard');
diff --git a/lib/Braintree/ApplePayCard.php b/lib/Braintree/ApplePayCard.php
new file mode 100644
index 0000000..51f10fa
--- /dev/null
+++ b/lib/Braintree/ApplePayCard.php
@@ -0,0 +1,103 @@
+== More information ==
+ *
+ * See {@link https://developers.braintreepayments.com/javascript+php}
+ *
+ * @package Braintree
+ * @category Resources
+ *
+ * @property-read string $bin
+ * @property-read string $cardType
+ * @property-read \DateTime $createdAt
+ * @property-read string $customerId
+ * @property-read boolean $default
+ * @property-read string $expirationDate
+ * @property-read string $expirationMonth
+ * @property-read string $expirationYear
+ * @property-read boolean $expired
+ * @property-read string $imageUrl
+ * @property-read string $last4
+ * @property-read string $token
+ * @property-read string $paymentInstrumentName
+ * @property-read string $sourceDescription
+ * @property-read \Braintree\Subscription[] $subscriptions
+ * @property-read \DateTime $updatedAt
+ */
+class ApplePayCard extends Base
+{
+ // Card Type
+ const AMEX = 'Apple Pay - American Express';
+ const MASTER_CARD = 'Apple Pay - MasterCard';
+ const VISA = 'Apple Pay - Visa';
+
+ /* instance methods */
+ /**
+ * returns false if default is null or false
+ *
+ * @return boolean
+ */
+ public function isDefault()
+ {
+ return $this->default;
+ }
+
+ /**
+ * checks whether the card is expired based on the current date
+ *
+ * @return boolean
+ */
+ public function isExpired()
+ {
+ return $this->expired;
+ }
+
+ /**
+ * factory method: returns an instance of ApplePayCard
+ * to the requesting method, with populated properties
+ *
+ * @ignore
+ * @return ApplePayCard
+ */
+ public static function factory($attributes)
+ {
+ $defaultAttributes = [
+ 'expirationMonth' => '',
+ 'expirationYear' => '',
+ 'last4' => '',
+ ];
+
+ $instance = new self();
+ $instance->_initialize(array_merge($defaultAttributes, $attributes));
+ return $instance;
+ }
+
+ /**
+ * sets instance properties from an array of values
+ *
+ * @access protected
+ * @param array $applePayCardAttribs array of Apple Pay card properties
+ * @return void
+ */
+ protected function _initialize($applePayCardAttribs)
+ {
+ // set the attributes
+ $this->_attributes = $applePayCardAttribs;
+
+ $subscriptionArray = [];
+ if (isset($applePayCardAttribs['subscriptions'])) {
+ foreach ($applePayCardAttribs['subscriptions'] AS $subscription) {
+ $subscriptionArray[] = Subscription::factory($subscription);
+ }
+ }
+
+ $this->_set('subscriptions', $subscriptionArray);
+ $this->_set('expirationDate', $this->expirationMonth . '/' . $this->expirationYear);
+ }
+}
+class_alias('Braintree\ApplePayCard', 'Braintree_ApplePayCard');
diff --git a/lib/Braintree/ApplePayGateway.php b/lib/Braintree/ApplePayGateway.php
new file mode 100644
index 0000000..c8291be
--- /dev/null
+++ b/lib/Braintree/ApplePayGateway.php
@@ -0,0 +1,65 @@
+_gateway = $gateway;
+ $this->_config = $gateway->config;
+ $this->_config->assertHasAccessTokenOrKeys();
+ $this->_http = new Http($gateway->config);
+ }
+
+ public function registerDomain($domain)
+ {
+ $path = $this->_config->merchantPath() . '/processing/apple_pay/validate_domains';
+ $response = $this->_http->post($path, ['url' => $domain]);
+ if (array_key_exists('response', $response) && $response['response']['success'])
+ {
+ return new Result\Successful;
+ }
+ else if (array_key_exists('apiErrorResponse', $response))
+ {
+ return new Result\Error($response['apiErrorResponse']);
+ }
+ }
+
+ public function unregisterDomain($domain)
+ {
+ $path = $this->_config->merchantPath() . '/processing/apple_pay/unregister_domain';
+ $this->_http->delete($path, ['url' => $domain]);
+ return new Result\Successful;
+ }
+
+ public function registeredDomains()
+ {
+ $path = $this->_config->merchantPath() . '/processing/apple_pay/registered_domains';
+ $response = $this->_http->get($path);
+ if (array_key_exists('response', $response) && array_key_exists('domains', $response['response']))
+ {
+ $options = ApplePayOptions::factory($response['response']);
+ return new Result\Successful($options, 'applePayOptions');
+ }
+ else if (array_key_exists('apiErrorResponse', $response))
+ {
+ return new Result\Error($response['apiErrorResponse']);
+ }
+ else
+ {
+ throw new Exception\Unexpected('expected response or apiErrorResponse');
+ }
+ }
+}
+class_alias('Braintree\ApplePayGateway', 'Braintree_ApplePayGateway');
diff --git a/lib/Braintree/ApplePayOptions.php b/lib/Braintree/ApplePayOptions.php
new file mode 100644
index 0000000..40b2cef
--- /dev/null
+++ b/lib/Braintree/ApplePayOptions.php
@@ -0,0 +1,28 @@
+_initialize($attributes);
+ return $instance;
+ }
+
+ protected function _initialize($attributes)
+ {
+ $this->_attributes = $attributes;
+ }
+}
+class_alias('Braintree\ApplePayOptions', 'Braintree_ApplePayOptions');
diff --git a/lib/Braintree/AuthorizationAdjustment.php b/lib/Braintree/AuthorizationAdjustment.php
new file mode 100644
index 0000000..a2eeea5
--- /dev/null
+++ b/lib/Braintree/AuthorizationAdjustment.php
@@ -0,0 +1,35 @@
+_initialize($attributes);
+
+ return $instance;
+ }
+
+ protected function _initialize($authorizationAdjustmentAttribs)
+ {
+ $this->_attributes = $authorizationAdjustmentAttribs;
+ }
+
+ public function __toString()
+ {
+ return __CLASS__ . '[' . Util::attributesToString($this->_attributes) . ']';
+ }
+}
+class_alias('Braintree\AuthorizationAdjustment', 'Braintree_Authorization_Adjustment');
diff --git a/lib/Braintree/Base.php b/lib/Braintree/Base.php
new file mode 100644
index 0000000..8e027c7
--- /dev/null
+++ b/lib/Braintree/Base.php
@@ -0,0 +1,88 @@
+_attributes)) {
+ return $this->_attributes[$name];
+ }
+ else {
+ trigger_error('Undefined property on ' . get_class($this) . ': ' . $name, E_USER_NOTICE);
+ return null;
+ }
+ }
+
+ /**
+ * Checks for the existance of a property stored in the private $_attributes property
+ *
+ * @ignore
+ * @param string $name
+ * @return boolean
+ */
+ public function __isset($name)
+ {
+ return array_key_exists($name, $this->_attributes);
+ }
+
+ /**
+ * Mutator for instance properties stored in the private $_attributes property
+ *
+ * @ignore
+ * @param string $key
+ * @param mixed $value
+ */
+ public function _set($key, $value)
+ {
+ $this->_attributes[$key] = $value;
+ }
+
+ /**
+ * Implementation of JsonSerializable
+ *
+ * @ignore
+ * @return array
+ */
+ public function jsonSerialize()
+ {
+ return $this->_attributes;
+ }
+}
diff --git a/lib/Braintree/BinData.php b/lib/Braintree/BinData.php
new file mode 100644
index 0000000..5a668de
--- /dev/null
+++ b/lib/Braintree/BinData.php
@@ -0,0 +1,41 @@
+_initialize($attributes);
+
+ return $instance;
+ }
+
+ protected function _initialize($attributes)
+ {
+ $this->_attributes = $attributes;
+ }
+
+ /**
+ * returns a string representation of the bin data
+ * @return string
+ */
+ public function __toString()
+ {
+ return __CLASS__ . '[' .
+ Util::attributesToString($this->_attributes) .']';
+ }
+
+}
+class_alias('Braintree\BinData', 'Braintree_BinData');
diff --git a/lib/Braintree/ClientToken.php b/lib/Braintree/ClientToken.php
new file mode 100644
index 0000000..f3d4884
--- /dev/null
+++ b/lib/Braintree/ClientToken.php
@@ -0,0 +1,49 @@
+clientToken()->generate($params);
+ }
+
+ /**
+ *
+ * @param type $params
+ * @throws InvalidArgumentException
+ */
+ public static function conditionallyVerifyKeys($params)
+ {
+ return Configuration::gateway()->clientToken()->conditionallyVerifyKeys($params);
+ }
+
+ /**
+ *
+ * @return string client token retrieved from server
+ */
+ public static function generateWithCustomerIdSignature()
+ {
+ return Configuration::gateway()->clientToken()->generateWithCustomerIdSignature();
+ }
+
+ /**
+ *
+ * @return string client token retrieved from server
+ */
+ public static function generateWithoutCustomerIdSignature()
+ {
+ return Configuration::gateway()->clientToken()->generateWithoutCustomerIdSignature();
+ }
+}
+class_alias('Braintree\ClientToken', 'Braintree_ClientToken');
diff --git a/lib/Braintree/ClientTokenGateway.php b/lib/Braintree/ClientTokenGateway.php
new file mode 100644
index 0000000..b2fcbf5
--- /dev/null
+++ b/lib/Braintree/ClientTokenGateway.php
@@ -0,0 +1,129 @@
+_gateway = $gateway;
+ $this->_config = $gateway->config;
+ $this->_config->assertHasAccessTokenOrKeys();
+ $this->_http = new Http($gateway->config);
+ }
+
+ public function generate($params=[])
+ {
+ if (!array_key_exists("version", $params)) {
+ $params["version"] = ClientToken::DEFAULT_VERSION;
+ }
+
+ $this->conditionallyVerifyKeys($params);
+ $generateParams = ["client_token" => $params];
+
+ return $this->_doGenerate('/client_token', $generateParams);
+ }
+
+ /**
+ * sends the generate request to the gateway
+ *
+ * @ignore
+ * @param var $url
+ * @param array $params
+ * @return string
+ */
+ public function _doGenerate($subPath, $params)
+ {
+ $fullPath = $this->_config->merchantPath() . $subPath;
+ $response = $this->_http->post($fullPath, $params);
+
+ return $this->_verifyGatewayResponse($response);
+ }
+
+ /**
+ *
+ * @param array $params
+ * @throws InvalidArgumentException
+ */
+ public function conditionallyVerifyKeys($params)
+ {
+ if (array_key_exists("customerId", $params)) {
+ Util::verifyKeys($this->generateWithCustomerIdSignature(), $params);
+ } else {
+ Util::verifyKeys($this->generateWithoutCustomerIdSignature(), $params);
+ }
+ }
+
+ /**
+ *
+ * @return mixed[]
+ */
+ public function generateWithCustomerIdSignature()
+ {
+ return [
+ "version", "customerId", "proxyMerchantId",
+ ["options" => ["makeDefault", "verifyCard", "failOnDuplicatePaymentMethod"]],
+ "merchantAccountId"];
+ }
+
+ /**
+ *
+ * @return string[]
+ */
+ public function generateWithoutCustomerIdSignature()
+ {
+ return ["version", "proxyMerchantId", "merchantAccountId"];
+ }
+
+ /**
+ * generic method for validating incoming gateway responses
+ *
+ * If the request is successful, returns a client token string.
+ * Otherwise, throws an InvalidArgumentException with the error
+ * response from the Gateway or an HTTP status code exception.
+ *
+ * @ignore
+ * @param array $response gateway response values
+ * @return string client token
+ * @throws InvalidArgumentException | HTTP status code exception
+ */
+ private function _verifyGatewayResponse($response)
+ {
+ if (isset($response['clientToken'])) {
+ return $response['clientToken']['value'];
+ } elseif (isset($response['apiErrorResponse'])) {
+ throw new InvalidArgumentException(
+ $response['apiErrorResponse']['message']
+ );
+ } else {
+ throw new Exception\Unexpected(
+ "Expected clientToken or apiErrorResponse"
+ );
+ }
+ }
+
+}
+class_alias('Braintree\ClientTokenGateway', 'Braintree_ClientTokenGateway');
diff --git a/lib/Braintree/CoinbaseAccount.php b/lib/Braintree/CoinbaseAccount.php
new file mode 100644
index 0000000..a93fc1c
--- /dev/null
+++ b/lib/Braintree/CoinbaseAccount.php
@@ -0,0 +1,110 @@
+== More information ==
+ *
+ *
+ * @package Braintree
+ * @category Resources
+ *
+ * @property-read string $customerId
+ * @property-read string $token
+ * @property-read string $userId
+ * @property-read string $userName
+ * @property-read string $userEmail
+ */
+class CoinbaseAccount extends Base
+{
+ /**
+ * factory method: returns an instance of CoinbaseAccount
+ * to the requesting method, with populated properties
+ *
+ * @ignore
+ * @return CoinbaseAccount
+ */
+ public static function factory($attributes)
+ {
+ $instance = new self();
+ $instance->_initialize($attributes);
+ return $instance;
+ }
+
+ /* instance methods */
+
+ /**
+ * returns false if default is null or false
+ *
+ * @return boolean
+ */
+ public function isDefault()
+ {
+ return $this->default;
+ }
+
+ /**
+ * sets instance properties from an array of values
+ *
+ * @access protected
+ * @param array $coinbaseAccountAttribs array of coinbaseAccount data
+ * @return void
+ */
+ protected function _initialize($coinbaseAccountAttribs)
+ {
+ // set the attributes
+ $this->_attributes = $coinbaseAccountAttribs;
+
+ $subscriptionArray = [];
+ if (isset($coinbaseAccountAttribs['subscriptions'])) {
+ foreach ($coinbaseAccountAttribs['subscriptions'] AS $subscription) {
+ $subscriptionArray[] = Subscription::factory($subscription);
+ }
+ }
+
+ $this->_set('subscriptions', $subscriptionArray);
+ }
+
+ /**
+ * create a printable representation of the object as:
+ * ClassName[property=value, property=value]
+ * @return string
+ */
+ public function __toString()
+ {
+ return __CLASS__ . '[' .
+ Util::attributesToString($this->_attributes) .']';
+ }
+
+
+ // static methods redirecting to gateway
+
+ public static function find($token)
+ {
+ return Configuration::gateway()->coinbaseAccount()->find($token);
+ }
+
+ public static function update($token, $attributes)
+ {
+ return Configuration::gateway()->coinbaseAccount()->update($token, $attributes);
+ }
+
+ public static function delete($token)
+ {
+ return Configuration::gateway()->coinbaseAccount()->delete($token);
+ }
+
+ public static function sale($token, $transactionAttribs)
+ {
+ return Configuration::gateway()->coinbaseAccount()->sale($token, $transactionAttribs);
+ }
+}
+class_alias('Braintree\CoinbaseAccount', 'Braintree_CoinbaseAccount');
diff --git a/lib/Braintree/Collection.php b/lib/Braintree/Collection.php
index 58293c3..1cbec1d 100644
--- a/lib/Braintree/Collection.php
+++ b/lib/Braintree/Collection.php
@@ -1,30 +1,31 @@
'',
- 'merchantId' => '',
- 'publicKey' => '',
- 'privateKey' => '',
- );
- /**
- *
- * @access protected
- * @static
- * @var array valid environments, used for validation
- */
- private static $_validEnvironments = array(
- 'development',
- 'sandbox',
- 'production',
- 'qa',
- );
+ public function __construct($attribs = [])
+ {
+ foreach ($attribs as $kind => $value) {
+ if ($kind == 'environment') {
+ CredentialsParser::assertValidEnvironment($value);
+ $this->_environment = $value;
+ }
+ if ($kind == 'merchantId') {
+ $this->_merchantId = $value;
+ }
+ if ($kind == 'publicKey') {
+ $this->_publicKey = $value;
+ }
+ if ($kind == 'privateKey') {
+ $this->_privateKey = $value;
+ }
+ if ($kind == 'timeout') {
+ $this->_timeout = $value;
+ }
+ if ($kind == 'acceptGzipEncoding') {
+ $this->_acceptGzipEncoding = $value;
+ }
+ }
+
+ if (isset($attribs['clientId']) || isset($attribs['accessToken'])) {
+ if (isset($attribs['environment']) || isset($attribs['merchantId']) || isset($attribs['publicKey']) || isset($attribs['privateKey'])) {
+ throw new Exception\Configuration('Cannot mix OAuth credentials (clientId, clientSecret, accessToken) with key credentials (publicKey, privateKey, environment, merchantId).');
+ }
+ $parsedCredentials = new CredentialsParser($attribs);
+
+ $this->_environment = $parsedCredentials->getEnvironment();
+ $this->_merchantId = $parsedCredentials->getMerchantId();
+ $this->_clientId = $parsedCredentials->getClientId();
+ $this->_clientSecret = $parsedCredentials->getClientSecret();
+ $this->_accessToken = $parsedCredentials->getAccessToken();
+ }
+ }
/**
* resets configuration to default
* @access public
- * @static
*/
public static function reset()
{
- self::$_cache = array (
- 'environment' => '',
- 'merchantId' => '',
- 'publicKey' => '',
- 'privateKey' => '',
- );
+ self::$global = new Configuration();
+ }
+
+ public static function gateway()
+ {
+ return new Gateway(self::$global);
+ }
+
+ public static function environment($value=null)
+ {
+ if (empty($value)) {
+ return self::$global->getEnvironment();
+ }
+ CredentialsParser::assertValidEnvironment($value);
+ self::$global->setEnvironment($value);
+ }
+
+ public static function merchantId($value=null)
+ {
+ if (empty($value)) {
+ return self::$global->getMerchantId();
+ }
+ self::$global->setMerchantId($value);
+ }
+
+ public static function publicKey($value=null)
+ {
+ if (empty($value)) {
+ return self::$global->getPublicKey();
+ }
+ self::$global->setPublicKey($value);
+ }
+
+ public static function privateKey($value=null)
+ {
+ if (empty($value)) {
+ return self::$global->getPrivateKey();
+ }
+ self::$global->setPrivateKey($value);
}
/**
- * performs sanity checks when config settings are being set
+ * Sets or gets the read timeout to use for making requests.
*
- * @ignore
- * @access protected
- * @param string $key name of config setting
- * @param string $value value to set
- * @throws InvalidArgumentException
- * @throws Braintree_Exception_Configuration
- * @static
- * @return boolean
+ * @param integer $value If provided, sets the read timeout
+ * @return integer The read timeout used for connecting to Braintree
*/
- private static function validate($key=null, $value=null)
+ public static function timeout($value=null)
{
- if (empty($key) && empty($value)) {
- throw new InvalidArgumentException('nothing to validate');
+ if (empty($value)) {
+ return self::$global->getTimeout();
}
+ self::$global->setTimeout($value);
+ }
- if ($key === 'environment' &&
- !in_array($value, self::$_validEnvironments) ) {
- throw new Braintree_Exception_Configuration('"' .
- $value . '" is not a valid environment.');
+ /**
+ * Sets or gets the SSL version to use for making requests. See
+ * https://php.net/manual/en/function.curl-setopt.php for possible
+ * CURLOPT_SSLVERSION values.
+ *
+ * @param integer $value If provided, sets the SSL version
+ * @return integer The SSL version used for connecting to Braintree
+ */
+ public static function sslVersion($value=null)
+ {
+ if (empty($value)) {
+ return self::$global->getSslVersion();
}
+ self::$global->setSslVersion($value);
+ }
- if (!isset(self::$_cache[$key])) {
- throw new Braintree_Exception_Configuration($key .
- ' is not a valid configuration setting.');
+ /**
+ * Sets or gets the proxy host to use for connecting to Braintree
+ *
+ * @param string $value If provided, sets the proxy host
+ * @return string The proxy host used for connecting to Braintree
+ */
+ public static function proxyHost($value = null)
+ {
+ if (empty($value)) {
+ return self::$global->getProxyHost();
}
+ self::$global->setProxyHost($value);
+ }
+ /**
+ * Sets or gets the port of the proxy to use for connecting to Braintree
+ *
+ * @param string $value If provided, sets the port of the proxy
+ * @return string The port of the proxy used for connecting to Braintree
+ */
+ public static function proxyPort($value = null)
+ {
if (empty($value)) {
- throw new InvalidArgumentException($key . ' cannot be empty.');
+ return self::$global->getProxyPort();
}
+ self::$global->setProxyPort($value);
+ }
- return true;
+ /**
+ * Sets or gets the proxy type to use for connecting to Braintree. This value
+ * can be any of the CURLOPT_PROXYTYPE options in PHP cURL.
+ *
+ * @param string $value If provided, sets the proxy type
+ * @return string The proxy type used for connecting to Braintree
+ */
+ public static function proxyType($value = null)
+ {
+ if (empty($value)) {
+ return self::$global->getProxyType();
+ }
+ self::$global->setProxyType($value);
}
- private static function set($key, $value)
+ /**
+ * Specifies whether or not a proxy is properly configured
+ *
+ * @return bool true if a proxy is configured properly, false if not
+ */
+ public static function isUsingProxy()
{
- // this method will raise an exception on invalid data
- self::validate($key, $value);
- // set the value in the cache
- self::$_cache[$key] = $value;
+ $proxyHost = self::$global->getProxyHost();
+ $proxyPort = self::$global->getProxyPort();
+ return !empty($proxyHost) && !empty($proxyPort);
+ }
+ public static function proxyUser($value = null)
+ {
+ if (empty($value)) {
+ return self::$global->getProxyUser();
+ }
+ self::$global->setProxyUser($value);
}
- private static function get($key)
+ public static function proxyPassword($value = null)
{
- // throw an exception if the value hasn't been set
- if (isset(self::$_cache[$key]) &&
- (empty(self::$_cache[$key]))) {
- throw new Braintree_Exception_Configuration(
- $key.' needs to be set'
- );
+ if (empty($value)) {
+ return self::$global->getProxyPassword();
}
+ self::$global->setProxyPassword($value);
+ }
+
+ /**
+ * Specified whether or not a username and password have been provided for
+ * use with an authenticated proxy
+ *
+ * @return bool true if both proxyUser and proxyPassword are present
+ */
+ public static function isAuthenticatedProxy()
+ {
+ $proxyUser = self::$global->getProxyUser();
+ $proxyPwd = self::$global->getProxyPassword();
+ return !empty($proxyUser) && !empty($proxyPwd);
+ }
- if (array_key_exists($key, self::$_cache)) {
- return self::$_cache[$key];
+ /**
+ * Specify if the HTTP client is able to decode gzipped responses.
+ *
+ * @param bool $value If true, will send an Accept-Encoding header with a gzip value. If false, will not send an Accept-Encoding header with a gzip value.
+ * @return bool true if an Accept-Encoding header with a gzip value will be sent, false if not
+ */
+ public static function acceptGzipEncoding($value = null)
+ {
+ if (is_null($value)) {
+ return self::$global->getAcceptGzipEncoding();
}
+ self::$global->setAcceptGzipEncoding($value);
+ }
- // return null by default to prevent __set from overloading
- return null;
+ public static function assertGlobalHasAccessTokenOrKeys()
+ {
+ self::$global->assertHasAccessTokenOrKeys();
}
+ public function assertHasAccessTokenOrKeys()
+ {
+ if (empty($this->_accessToken)) {
+ if (empty($this->_merchantId)) {
+ throw new Exception\Configuration('Braintree\\Configuration::merchantId needs to be set (or accessToken needs to be passed to Braintree\\Gateway).');
+ } else if (empty($this->_environment)) {
+ throw new Exception\Configuration('Braintree\\Configuration::environment needs to be set.');
+ } else if (empty($this->_publicKey)) {
+ throw new Exception\Configuration('Braintree\\Configuration::publicKey needs to be set.');
+ } else if (empty($this->_privateKey)) {
+ throw new Exception\Configuration('Braintree\\Configuration::privateKey needs to be set.');
+ }
+ }
+ }
- private static function setOrGet($name, $value = null)
+ public function assertHasClientCredentials()
{
- if (!empty($value) && is_array($value)) {
- $value = $value[0];
+ $this->assertHasClientId();
+ $this->assertHasClientSecret();
+ }
+
+ public function assertHasClientId()
+ {
+ if (empty($this->_clientId)) {
+ throw new Exception\Configuration('clientId needs to be passed to Braintree\\Gateway.');
}
- if (!empty($value)) {
- self::set($name, $value);
- } else {
- return self::get($name);
+ }
+
+ public function assertHasClientSecret()
+ {
+ if (empty($this->_clientSecret)) {
+ throw new Exception\Configuration('clientSecret needs to be passed to Braintree\\Gateway.');
}
- return true;
}
- /**#@+
- * sets or returns the property after validation
- * @access public
- * @static
- * @param string $value pass a string to set, empty to get
- * @return mixed returns true on set
+
+ public function getEnvironment()
+ {
+ return $this->_environment;
+ }
+
+ /**
+ * Do not use this method directly. Pass in the environment to the constructor.
*/
- public static function environment($value = null)
+ public function setEnvironment($value)
+ {
+ $this->_environment = $value;
+ }
+
+ public function getMerchantId()
{
- return self::setOrGet(__FUNCTION__, $value);
+ return $this->_merchantId;
}
- public static function merchantId($value = null)
+ /**
+ * Do not use this method directly. Pass in the merchantId to the constructor.
+ */
+ public function setMerchantId($value)
{
- return self::setOrGet(__FUNCTION__, $value);
+ $this->_merchantId = $value;
}
- public static function publicKey($value = null)
+ public function getPublicKey()
{
- return self::setOrGet(__FUNCTION__, $value);
+ return $this->_publicKey;
}
- public static function privateKey($value = null)
+ public function getClientId()
{
- return self::setOrGet(__FUNCTION__, $value);
+ return $this->_clientId;
}
- /**#@-*/
/**
- * returns the full merchant URL based on config values
- *
- * @access public
- * @static
- * @param none
- * @return string merchant URL
+ * Do not use this method directly. Pass in the publicKey to the constructor.
+ */
+ public function setPublicKey($value)
+ {
+ $this->_publicKey = $value;
+ }
+
+ public function getPrivateKey()
+ {
+ return $this->_privateKey;
+ }
+
+ public function getClientSecret()
+ {
+ return $this->_clientSecret;
+ }
+
+ /**
+ * Do not use this method directly. Pass in the privateKey to the constructor.
*/
- public static function merchantUrl()
+ public function setPrivateKey($value)
+ {
+ $this->_privateKey = $value;
+ }
+
+ private function setProxyHost($value)
+ {
+ $this->_proxyHost = $value;
+ }
+
+ public function getProxyHost()
+ {
+ return $this->_proxyHost;
+ }
+
+ private function setProxyPort($value)
+ {
+ $this->_proxyPort = $value;
+ }
+
+ public function getProxyPort()
+ {
+ return $this->_proxyPort;
+ }
+
+ private function setProxyType($value)
+ {
+ $this->_proxyType = $value;
+ }
+
+ public function getProxyType()
+ {
+ return $this->_proxyType;
+ }
+
+ private function setProxyUser($value)
{
- return self::baseUrl() .
- self::merchantPath();
+ $this->_proxyUser = $value;
}
+ public function getProxyUser()
+ {
+ return $this->_proxyUser;
+ }
+
+ private function setProxyPassword($value)
+ {
+ $this->_proxyPassword = $value;
+ }
+
+ public function getProxyPassword()
+ {
+ return $this->_proxyPassword;
+ }
+
+ private function setTimeout($value)
+ {
+ $this->_timeout = $value;
+ }
+
+ public function getTimeout()
+ {
+ return $this->_timeout;
+ }
+
+ private function setSslVersion($value)
+ {
+ $this->_sslVersion = $value;
+ }
+
+ private function getSslVersion()
+ {
+ return $this->_sslVersion;
+ }
+
+ public function getAcceptGzipEncoding()
+ {
+ return $this->_acceptGzipEncoding;
+ }
+
+ private function setAcceptGzipEncoding($value)
+ {
+ $this->_acceptGzipEncoding = $value;
+ }
+
+ public function getAccessToken()
+ {
+ return $this->_accessToken;
+ }
+
+ public function isAccessToken()
+ {
+ return !empty($this->_accessToken);
+ }
+
+ public function isClientCredentials()
+ {
+ return !empty($this->_clientId);
+ }
/**
* returns the base braintree gateway URL based on config values
*
* @access public
- * @static
* @param none
* @return string braintree gateway URL
*/
- public static function baseUrl()
+ public function baseUrl()
{
- return self::protocol() . '://' .
- self::serverName() . ':' .
- self::portNumber();
+ return sprintf('%s://%s:%d', $this->protocol(), $this->serverName(), $this->portNumber());
}
+ /**
+ * returns the base URL for Braintree's GraphQL endpoint based on config values
+ *
+ * @access public
+ * @param none
+ * @return string Braintree GraphQL URL
+ */
+ public function graphQLBaseUrl()
+ {
+ return sprintf('%s://%s:%d/graphql', $this->protocol(), $this->graphQLServerName(), $this->graphQLPortNumber());
+ }
+
/**
* sets the merchant path based on merchant ID
*
* @access protected
- * @static
* @param none
* @return string merchant path uri
*/
- public static function merchantPath()
+ public function merchantPath()
{
- return '/merchants/'.self::merchantId();
+ return '/merchants/' . $this->_merchantId;
}
/**
* sets the physical path for the location of the CA certs
*
* @access public
- * @static
* @param none
* @return string filepath
*/
- public static function caFile($sslPath = NULL)
+ public function caFile($sslPath = NULL)
{
$sslPath = $sslPath ? $sslPath : DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR .
'ssl' . DIRECTORY_SEPARATOR;
-
- $caPath = realpath(
- dirname(__FILE__) .
- $sslPath . 'api_braintreegateway_com.ca.crt'
- );
+ $caPath = __DIR__ . $sslPath . 'api_braintreegateway_com.ca.crt';
if (!file_exists($caPath))
{
- throw new Braintree_Exception_SSLCaFileNotFound();
+ throw new Exception\SSLCaFileNotFound();
}
return $caPath;
@@ -240,52 +506,65 @@ public static function caFile($sslPath = NULL)
* returns the port number depending on environment
*
* @access public
- * @static
* @param none
* @return int portnumber
*/
- public static function portNumber()
+ public function portNumber()
{
- if (self::sslOn()) {
+ if ($this->sslOn()) {
return 443;
}
return getenv("GATEWAY_PORT") ? getenv("GATEWAY_PORT") : 3000;
}
+ /**
+ * returns the graphql port number depending on environment
+ *
+ * @access public
+ * @param none
+ * @return int graphql portnumber
+ */
+ public function graphQLPortNumber()
+ {
+ if ($this->sslOn()) {
+ return 443;
+ }
+ return getenv("GRAPHQL_PORT") ?: 8080;
+ }
+
/**
* returns http protocol depending on environment
*
* @access public
- * @static
* @param none
* @return string http || https
*/
- public static function protocol()
+ public function protocol()
{
- return self::sslOn() ? 'https' : 'http';
+ return $this->sslOn() ? 'https' : 'http';
}
/**
* returns gateway server name depending on environment
*
* @access public
- * @static
* @param none
* @return string server domain name
*/
- public static function serverName()
+ public function serverName()
{
- switch(self::environment()) {
+ switch($this->_environment) {
case 'production':
$serverName = 'api.braintreegateway.com';
break;
case 'qa':
- $serverName = 'qa.braintreegateway.com';
+ $serverName = 'gateway.qa.braintreepayments.com';
break;
case 'sandbox':
$serverName = 'api.sandbox.braintreegateway.com';
break;
case 'development':
+ case 'integration':
default:
$serverName = 'localhost';
break;
@@ -294,18 +573,69 @@ public static function serverName()
return $serverName;
}
+ /**
+ * returns Braintree GraphQL server name depending on environment
+ *
+ * @access public
+ * @param none
+ * @return string graphql domain name
+ */
+ public function graphQLServerName()
+ {
+ switch($this->_environment) {
+ case 'production':
+ $graphQLServerName = 'payments.braintree-api.com';
+ break;
+ case 'qa':
+ $graphQLServerName = 'payments-qa.dev.braintree-api.com';
+ break;
+ case 'sandbox':
+ $graphQLServerName = 'payments.sandbox.braintree-api.com';
+ break;
+ case 'development':
+ case 'integration':
+ default:
+ $graphQLServerName = 'graphql.bt.local';
+ break;
+ }
+
+ return $graphQLServerName;
+ }
+
+ public function authUrl()
+ {
+ switch($this->_environment) {
+ case 'production':
+ $serverName = 'https://auth.venmo.com';
+ break;
+ case 'qa':
+ $serverName = 'https://auth.qa.venmo.com';
+ break;
+ case 'sandbox':
+ $serverName = 'https://auth.sandbox.venmo.com';
+ break;
+ case 'development':
+ case 'integration':
+ default:
+ $serverName = 'http://auth.venmo.dev:9292';
+ break;
+ }
+
+ return $serverName;
+ }
+
/**
* returns boolean indicating SSL is on or off for this session,
* depending on environment
*
* @access public
- * @static
* @param none
* @return boolean
*/
- public static function sslOn()
+ public function sslOn()
{
- switch(self::environment()) {
+ switch($this->_environment) {
+ case 'integration':
case 'development':
$ssl = false;
break;
@@ -326,9 +656,10 @@ public static function sslOn()
* @param string $message
*
*/
- public static function logMessage($message)
+ public function logMessage($message)
{
error_log('[Braintree] ' . $message);
}
-
}
+Configuration::reset();
+class_alias('Braintree\Configuration', 'Braintree_Configuration');
diff --git a/lib/Braintree/ConnectedMerchantPayPalStatusChanged.php b/lib/Braintree/ConnectedMerchantPayPalStatusChanged.php
new file mode 100644
index 0000000..ca4e7ef
--- /dev/null
+++ b/lib/Braintree/ConnectedMerchantPayPalStatusChanged.php
@@ -0,0 +1,37 @@
+_initialize($attributes);
+ $instance->_attributes['merchantId'] = $instance->_attributes['merchantPublicId'];
+
+ return $instance;
+ }
+
+ /**
+ * @ignore
+ */
+ protected function _initialize($attributes)
+ {
+ $this->_attributes = $attributes;
+ }
+}
+class_alias('Braintree\ConnectedMerchantPayPalStatusChanged', 'Braintree_ConnectedMerchantPayPalStatusChanged');
diff --git a/lib/Braintree/ConnectedMerchantStatusTransitioned.php b/lib/Braintree/ConnectedMerchantStatusTransitioned.php
new file mode 100644
index 0000000..4614437
--- /dev/null
+++ b/lib/Braintree/ConnectedMerchantStatusTransitioned.php
@@ -0,0 +1,37 @@
+_initialize($attributes);
+ $instance->_attributes['merchantId'] = $instance->_attributes['merchantPublicId'];
+
+ return $instance;
+ }
+
+ /**
+ * @ignore
+ */
+ protected function _initialize($attributes)
+ {
+ $this->_attributes = $attributes;
+ }
+}
+class_alias('Braintree\ConnectedMerchantStatusTransitioned', 'Braintree_ConnectedMerchantStatusTransitioned');
diff --git a/lib/Braintree/CredentialsParser.php b/lib/Braintree/CredentialsParser.php
new file mode 100644
index 0000000..034d973
--- /dev/null
+++ b/lib/Braintree/CredentialsParser.php
@@ -0,0 +1,147 @@
+ $value) {
+ if ($kind == 'clientId') {
+ $this->_clientId = $value;
+ }
+ if ($kind == 'clientSecret') {
+ $this->_clientSecret = $value;
+ }
+ if ($kind == 'accessToken') {
+ $this->_accessToken = $value;
+ }
+ }
+ $this->parse();
+ }
+
+ /**
+ *
+ * @access protected
+ * @static
+ * @var array valid environments, used for validation
+ */
+ private static $_validEnvironments = [
+ 'development',
+ 'integration',
+ 'sandbox',
+ 'production',
+ 'qa',
+ ];
+
+
+ public function parse()
+ {
+ $environments = [];
+ if (!empty($this->_clientId)) {
+ $environments[] = ['clientId', $this->_parseClientCredential('clientId', $this->_clientId, 'client_id')];
+ }
+ if (!empty($this->_clientSecret)) {
+ $environments[] = ['clientSecret', $this->_parseClientCredential('clientSecret', $this->_clientSecret, 'client_secret')];
+ }
+ if (!empty($this->_accessToken)) {
+ $environments[] = ['accessToken', $this->_parseAccessToken()];
+ }
+
+ $checkEnv = $environments[0];
+ foreach ($environments as $env) {
+ if ($env[1] !== $checkEnv[1]) {
+ throw new Exception\Configuration(
+ 'Mismatched credential environments: ' . $checkEnv[0] . ' environment is ' . $checkEnv[1] .
+ ' and ' . $env[0] . ' environment is ' . $env[1]);
+ }
+ }
+
+ self::assertValidEnvironment($checkEnv[1]);
+ $this->_environment = $checkEnv[1];
+ }
+
+ public static function assertValidEnvironment($environment) {
+ if (!in_array($environment, self::$_validEnvironments)) {
+ throw new Exception\Configuration('"' .
+ $environment . '" is not a valid environment.');
+ }
+ }
+
+ private function _parseClientCredential($credentialType, $value, $expectedValuePrefix)
+ {
+ $explodedCredential = explode('$', $value);
+ if (sizeof($explodedCredential) != 3) {
+ throw new Exception\Configuration('Incorrect ' . $credentialType . ' format. Expected: type$environment$token');
+ }
+
+ $gotValuePrefix = $explodedCredential[0];
+ $environment = $explodedCredential[1];
+ $token = $explodedCredential[2];
+
+ if ($gotValuePrefix != $expectedValuePrefix) {
+ throw new Exception\Configuration('Value passed for ' . $credentialType . ' is not a ' . $credentialType);
+ }
+
+ return $environment;
+ }
+
+ private function _parseAccessToken()
+ {
+ $accessTokenExploded = explode('$', $this->_accessToken);
+ if (sizeof($accessTokenExploded) != 4) {
+ throw new Exception\Configuration('Incorrect accessToken syntax. Expected: type$environment$merchant_id$token');
+ }
+
+ $gotValuePrefix = $accessTokenExploded[0];
+ $environment = $accessTokenExploded[1];
+ $merchantId = $accessTokenExploded[2];
+ $token = $accessTokenExploded[3];
+
+ if ($gotValuePrefix != 'access_token') {
+ throw new Exception\Configuration('Value passed for accessToken is not an accessToken');
+ }
+
+ $this->_merchantId = $merchantId;
+ return $environment;
+ }
+
+ public function getClientId()
+ {
+ return $this->_clientId;
+ }
+
+ public function getClientSecret()
+ {
+ return $this->_clientSecret;
+ }
+
+ public function getAccessToken()
+ {
+ return $this->_accessToken;
+ }
+
+ public function getEnvironment()
+ {
+ return $this->_environment;
+ }
+
+ public function getMerchantId()
+ {
+ return $this->_merchantId;
+ }
+}
+class_alias('Braintree\CredentialsParser', 'Braintree_CredentialsParser');
diff --git a/lib/Braintree/CreditCard.php b/lib/Braintree/CreditCard.php
index 74ecff3..a889545 100644
--- a/lib/Braintree/CreditCard.php
+++ b/lib/Braintree/CreditCard.php
@@ -1,40 +1,48 @@
== More information ==
*
- * For more detailed information on CreditCards, see {@link http://www.braintreepayments.com/gateway/credit-card-api http://www.braintreepaymentsolutions.com/gateway/credit-card-api}
- * For more detailed information on CreditCard verifications, see {@link http://www.braintreepayments.com/gateway/credit-card-verification-api http://www.braintreepaymentsolutions.com/gateway/credit-card-verification-api}
+ * For more detailed information on CreditCards, see {@link https://developers.braintreepayments.com/reference/response/credit-card/php https://developers.braintreepayments.com/reference/response/credit-card/php}
+ * For more detailed information on CreditCard verifications, see {@link https://developers.braintreepayments.com/reference/response/credit-card-verification/php https://developers.braintreepayments.com/reference/response/credit-card-verification/php}
*
* @package Braintree
* @category Resources
- * @copyright 2010 Braintree Payment Solutions
*
- * @property-read string $billingAddress
+ * @property-read \Braintree\Address $billingAddress
* @property-read string $bin
* @property-read string $cardType
* @property-read string $cardholderName
- * @property-read string $createdAt
+ * @property-read string $commercial
+ * @property-read \DateTime $createdAt
* @property-read string $customerId
+ * @property-read string $customerLocation
+ * @property-read string $debit
+ * @property-read boolean $default
+ * @property-read string $durbinRegulated
* @property-read string $expirationDate
* @property-read string $expirationMonth
* @property-read string $expirationYear
+ * @property-read boolean $expired
+ * @property-read boolean $healthcare
* @property-read string $imageUrl
+ * @property-read string $issuingBank
* @property-read string $last4
* @property-read string $maskedNumber
+ * @property-read string $payroll
+ * @property-read string $prepaid
+ * @property-read string $productId
+ * @property-read \Braintree\Subscription[] $subscriptions
* @property-read string $token
- * @property-read string $updatedAt
+ * @property-read string $uniqueNumberIdentifier
+ * @property-read \DateTime $updatedAt
+ * @property-read \Braintree\CreditCardVerification|null $verification
*/
-class Braintree_CreditCard extends Braintree
+class CreditCard extends Base
{
// Card Type
const AMEX = 'American Express';
@@ -42,18 +50,20 @@ class Braintree_CreditCard extends Braintree
const CHINA_UNION_PAY = 'China UnionPay';
const DINERS_CLUB_INTERNATIONAL = 'Diners Club';
const DISCOVER = 'Discover';
+ const ELO = 'Elo';
const JCB = 'JCB';
const LASER = 'Laser';
const MAESTRO = 'Maestro';
+ const UK_MAESTRO = 'UK Maestro';
const MASTER_CARD = 'MasterCard';
const SOLO = 'Solo';
const SWITCH_TYPE = 'Switch';
const VISA = 'Visa';
const UNKNOWN = 'Unknown';
- // Credit card origination location
- const INTERNATIONAL = "international";
- const US = "us";
+ // Credit card origination location
+ const INTERNATIONAL = "international";
+ const US = "us";
const PREPAID_YES = 'Yes';
const PREPAID_NO = 'No';
@@ -81,523 +91,242 @@ class Braintree_CreditCard extends Braintree
const COUNTRY_OF_ISSUANCE_UNKNOWN = "Unknown";
const ISSUING_BANK_UNKNOWN = "Unknown";
+ const PRODUCT_ID_UNKNOWN = "Unknown";
- public static function create($attribs)
- {
- Braintree_Util::verifyKeys(self::createSignature(), $attribs);
- return self::_doCreate('/payment_methods', array('credit_card' => $attribs));
- }
-
+ /* instance methods */
/**
- * attempts the create operation assuming all data will validate
- * returns a Braintree_CreditCard object instead of a Result
+ * returns false if default is null or false
*
- * @access public
- * @param array $attribs
- * @return object
- * @throws Braintree_Exception_ValidationError
+ * @return boolean
*/
- public static function createNoValidate($attribs)
+ public function isDefault()
{
- $result = self::create($attribs);
- return self::returnObjectOrThrowException(__CLASS__, $result);
+ return $this->default;
}
+
/**
- * create a customer from a TransparentRedirect operation
+ * checks whether the card is expired based on the current date
*
- * @access public
- * @param array $attribs
- * @return object
+ * @return boolean
*/
- public static function createFromTransparentRedirect($queryString)
+ public function isExpired()
{
- trigger_error("DEPRECATED: Please use Braintree_TransparentRedirectRequest::confirm", E_USER_NOTICE);
- $params = Braintree_TransparentRedirect::parseAndValidateQueryString(
- $queryString
- );
- return self::_doCreate(
- '/payment_methods/all/confirm_transparent_redirect_request',
- array('id' => $params['id'])
- );
+ return $this->expired;
}
/**
+ * checks whether the card is associated with venmo sdk
*
- * @access public
- * @param none
- * @return string
+ * @return boolean
*/
- public static function createCreditCardUrl()
+ public function isVenmoSdk()
{
- trigger_error("DEPRECATED: Please use Braintree_TransparentRedirectRequest::url", E_USER_NOTICE);
- return Braintree_Configuration::merchantUrl() .
- '/payment_methods/all/create_via_transparent_redirect_request';
+ return $this->venmoSdk;
}
/**
- * returns a ResourceCollection of expired credit cards
- * @return object ResourceCollection
+ * sets instance properties from an array of values
+ *
+ * @access protected
+ * @param array $creditCardAttribs array of creditcard data
+ * @return void
*/
- public static function expired()
+ protected function _initialize($creditCardAttribs)
{
- $response = Braintree_Http::post("/payment_methods/all/expired_ids");
- $pager = array(
- 'className' => __CLASS__,
- 'classMethod' => 'fetchExpired',
- 'methodArgs' => array()
- );
-
- return new Braintree_ResourceCollection($response, $pager);
- }
+ // set the attributes
+ $this->_attributes = $creditCardAttribs;
- public static function fetchExpired($ids)
- {
- $response = Braintree_Http::post("/payment_methods/all/expired", array('search' => array('ids' => $ids)));
+ // map each address into its own object
+ $billingAddress = isset($creditCardAttribs['billingAddress']) ?
+ Address::factory($creditCardAttribs['billingAddress']) :
+ null;
- return Braintree_Util::extractattributeasarray(
- $response['paymentMethods'],
- 'creditCard'
- );
- }
- /**
- * returns a ResourceCollection of credit cards expiring between start/end
- *
- * @return object ResourceCollection
- */
- public static function expiringBetween($startDate, $endDate)
- {
- $queryPath = '/payment_methods/all/expiring_ids?start=' . date('mY', $startDate) . '&end=' . date('mY', $endDate);
- $response = Braintree_Http::post($queryPath);
- $pager = array(
- 'className' => __CLASS__,
- 'classMethod' => 'fetchExpiring',
- 'methodArgs' => array($startDate, $endDate)
- );
-
- return new Braintree_ResourceCollection($response, $pager);
- }
+ $subscriptionArray = [];
+ if (isset($creditCardAttribs['subscriptions'])) {
+ foreach ($creditCardAttribs['subscriptions'] AS $subscription) {
+ $subscriptionArray[] = Subscription::factory($subscription);
+ }
+ }
- public static function fetchExpiring($startDate, $endDate, $ids)
- {
- $queryPath = '/payment_methods/all/expiring?start=' . date('mY', $startDate) . '&end=' . date('mY', $endDate);
- $response = Braintree_Http::post($queryPath, array('search' => array('ids' => $ids)));
+ $this->_set('subscriptions', $subscriptionArray);
+ $this->_set('billingAddress', $billingAddress);
+ $this->_set('expirationDate', $this->expirationMonth . '/' . $this->expirationYear);
+ $this->_set('maskedNumber', $this->bin . '******' . $this->last4);
- return Braintree_Util::extractAttributeAsArray(
- $response['paymentMethods'],
- 'creditCard'
- );
- }
+ if(isset($creditCardAttribs['verifications']) && count($creditCardAttribs['verifications']) > 0) {
+ $verifications = $creditCardAttribs['verifications'];
+ usort($verifications, [$this, '_compareCreatedAtOnVerifications']);
- /**
- * find a creditcard by token
- *
- * @access public
- * @param string $token credit card unique id
- * @return object Braintree_CreditCard
- * @throws Braintree_Exception_NotFound
- */
- public static function find($token)
- {
- self::_validateId($token);
- try {
- $response = Braintree_Http::get('/payment_methods/'.$token);
- return self::factory($response['creditCard']);
- } catch (Braintree_Exception_NotFound $e) {
- throw new Braintree_Exception_NotFound(
- 'credit card with token ' . $token . ' not found'
- );
+ $this->_set('verification', CreditCardVerification::factory($verifications[0]));
}
-
}
- /**
- * create a credit on the card for the passed transaction
- *
- * @access public
- * @param array $attribs
- * @return object Braintree_Result_Successful or Braintree_Result_Error
- */
- public static function credit($token, $transactionAttribs)
+ private function _compareCreatedAtOnVerifications($verificationAttrib1, $verificationAttrib2)
{
- self::_validateId($token);
- return Braintree_Transaction::credit(
- array_merge(
- $transactionAttribs,
- array('paymentMethodToken' => $token)
- )
- );
+ return ($verificationAttrib2['createdAt'] < $verificationAttrib1['createdAt']) ? -1 : 1;
}
/**
- * create a credit on this card, assuming validations will pass
+ * returns false if comparing object is not a CreditCard,
+ * or is a CreditCard with a different id
*
- * returns a Braintree_Transaction object on success
- *
- * @access public
- * @param array $attribs
- * @return object Braintree_Transaction
- * @throws Braintree_Exception_ValidationError
+ * @param object $otherCreditCard customer to compare against
+ * @return boolean
*/
- public static function creditNoValidate($token, $transactionAttribs)
+ public function isEqual($otherCreditCard)
{
- $result = self::credit($token, $transactionAttribs);
- return self::returnObjectOrThrowException('Transaction', $result);
+ return !($otherCreditCard instanceof self) ? false : $this->token === $otherCreditCard->token;
}
/**
- * create a new sale for the current card
- *
- * @param string $token
- * @param array $transactionAttribs
- * @return object Braintree_Result_Successful or Braintree_Result_Error
- * @see Braintree_Transaction::sale()
+ * create a printable representation of the object as:
+ * ClassName[property=value, property=value]
+ * @return string
*/
- public static function sale($token, $transactionAttribs)
+ public function __toString()
{
- self::_validateId($token);
- return Braintree_Transaction::sale(
- array_merge(
- $transactionAttribs,
- array('paymentMethodToken' => $token)
- )
- );
+ return __CLASS__ . '[' .
+ Util::attributesToString($this->_attributes) .']';
}
/**
- * create a new sale using this card, assuming validations will pass
- *
- * returns a Braintree_Transaction object on success
+ * factory method: returns an instance of CreditCard
+ * to the requesting method, with populated properties
*
- * @access public
- * @param array $transactionAttribs
- * @param string $token
- * @return object Braintree_Transaction
- * @throws Braintree_Exception_ValidationsFailed
- * @see Braintree_Transaction::sale()
+ * @ignore
+ * @return CreditCard
*/
- public static function saleNoValidate($token, $transactionAttribs)
+ public static function factory($attributes)
{
- $result = self::sale($token, $transactionAttribs);
- return self::returnObjectOrThrowException('Transaction', $result);
- }
+ $defaultAttributes = [
+ 'bin' => '',
+ 'expirationMonth' => '',
+ 'expirationYear' => '',
+ 'last4' => '',
+ ];
- /**
- * updates the creditcard record
- *
- * if calling this method in static context, $token
- * is the 2nd attribute. $token is not sent in object context.
- *
- * @access public
- * @param array $attributes
- * @param string $token (optional)
- * @return object Braintree_Result_Successful or Braintree_Result_Error
- */
- public static function update($token, $attributes)
- {
- Braintree_Util::verifyKeys(self::updateSignature(), $attributes);
- self::_validateId($token);
- return self::_doUpdate('put', '/payment_methods/' . $token, array('creditCard' => $attributes));
+ $instance = new self();
+ $instance->_initialize(array_merge($defaultAttributes, $attributes));
+ return $instance;
}
- /**
- * update a creditcard record, assuming validations will pass
- *
- * if calling this method in static context, $token
- * is the 2nd attribute. $token is not sent in object context.
- * returns a Braintree_CreditCard object on success
- *
- * @access public
- * @param array $attributes
- * @param string $token
- * @return object Braintree_CreditCard
- * @throws Braintree_Exception_ValidationsFailed
- */
- public static function updateNoValidate($token, $attributes)
+
+ // static methods redirecting to gateway
+
+ public static function create($attribs)
{
- $result = self::update($token, $attributes);
- return self::returnObjectOrThrowException(__CLASS__, $result);
+ return Configuration::gateway()->creditCard()->create($attribs);
}
- /**
- *
- * @access public
- * @param none
- * @return string
- */
- public static function updateCreditCardUrl()
+
+ public static function createNoValidate($attribs)
{
- trigger_error("DEPRECATED: Please use Braintree_TransparentRedirectRequest::url", E_USER_NOTICE);
- return Braintree_Configuration::merchantUrl() .
- '/payment_methods/all/update_via_transparent_redirect_request';
+ return Configuration::gateway()->creditCard()->createNoValidate($attribs);
}
- /**
- * update a customer from a TransparentRedirect operation
- *
- * @access public
- * @param array $attribs
- * @return object
- */
- public static function updateFromTransparentRedirect($queryString)
+ public static function createFromTransparentRedirect($queryString)
{
- trigger_error("DEPRECATED: Please use Braintree_TransparentRedirectRequest::confirm", E_USER_NOTICE);
- $params = Braintree_TransparentRedirect::parseAndValidateQueryString(
- $queryString
- );
- return self::_doUpdate(
- 'post',
- '/payment_methods/all/confirm_transparent_redirect_request',
- array('id' => $params['id'])
- );
+ return Configuration::gateway()->creditCard()->createFromTransparentRedirect($queryString);
}
- /* instance methods */
- /**
- * returns false if default is null or false
- *
- * @return boolean
- */
- public function isDefault()
+ public static function createCreditCardUrl()
{
- return $this->default;
+ return Configuration::gateway()->creditCard()->createCreditCardUrl();
}
- /**
- * checks whether the card is expired based on the current date
- *
- * @return boolean
- */
- public function isExpired()
+ public static function expired()
{
- return $this->expired;
+ return Configuration::gateway()->creditCard()->expired();
}
- /**
- * checks whether the card is associated with venmo sdk
- *
- * @return boolean
- */
- public function isVenmoSdk()
+ public static function fetchExpired($ids)
{
- return $this->venmoSdk;
+ return Configuration::gateway()->creditCard()->fetchExpired($ids);
}
- public static function delete($token)
+ public static function expiringBetween($startDate, $endDate)
{
- self::_validateId($token);
- Braintree_Http::delete('/payment_methods/' . $token);
- return new Braintree_Result_Successful();
+ return Configuration::gateway()->creditCard()->expiringBetween($startDate, $endDate);
}
- /**
- * sets instance properties from an array of values
- *
- * @access protected
- * @param array $creditCardAttribs array of creditcard data
- * @return none
- */
- protected function _initialize($creditCardAttribs)
+ public static function fetchExpiring($startDate, $endDate, $ids)
{
- // set the attributes
- $this->_attributes = $creditCardAttribs;
-
- // map each address into its own object
- $billingAddress = isset($creditCardAttribs['billingAddress']) ?
- Braintree_Address::factory($creditCardAttribs['billingAddress']) :
- null;
-
- $subscriptionArray = array();
- if (isset($creditCardAttribs['subscriptions'])) {
- foreach ($creditCardAttribs['subscriptions'] AS $subscription) {
- $subscriptionArray[] = Braintree_Subscription::factory($subscription);
- }
- }
-
- $this->_set('subscriptions', $subscriptionArray);
- $this->_set('billingAddress', $billingAddress);
- $this->_set('expirationDate', $this->expirationMonth . '/' . $this->expirationYear);
- $this->_set('maskedNumber', $this->bin . '******' . $this->last4);
+ return Configuration::gateway()->creditCard()->fetchExpiring($startDate, $endDate, $ids);
}
- /**
- * returns false if comparing object is not a Braintree_CreditCard,
- * or is a Braintree_CreditCard with a different id
- *
- * @param object $otherCreditCard customer to compare against
- * @return boolean
- */
- public function isEqual($otherCreditCard)
+ public static function find($token)
{
- return !($otherCreditCard instanceof Braintree_CreditCard) ? false : $this->token === $otherCreditCard->token;
+ return Configuration::gateway()->creditCard()->find($token);
}
- private static function baseOptions()
+ public static function fromNonce($nonce)
{
- return array('makeDefault', 'verificationMerchantAccountId', 'verifyCard', 'venmoSdkSession');
+ return Configuration::gateway()->creditCard()->fromNonce($nonce);
}
- private static function baseSignature($options)
+ public static function credit($token, $transactionAttribs)
{
- return array(
- 'billingAddressId', 'cardholderName', 'cvv', 'number', 'deviceSessionId',
- 'expirationDate', 'expirationMonth', 'expirationYear', 'token', 'venmoSdkPaymentMethodCode',
- 'deviceData', 'fraudMerchantId',
- array('options' => $options),
- array(
- 'billingAddress' => array(
- 'firstName',
- 'lastName',
- 'company',
- 'countryCodeAlpha2',
- 'countryCodeAlpha3',
- 'countryCodeNumeric',
- 'countryName',
- 'extendedAddress',
- 'locality',
- 'region',
- 'postalCode',
- 'streetAddress'
- ),
- ),
- );
+ return Configuration::gateway()->creditCard()->credit($token, $transactionAttribs);
}
- public static function createSignature()
+ public static function creditNoValidate($token, $transactionAttribs)
{
- $options = self::baseOptions();
- $options[] = "failOnDuplicatePaymentMethod";
- $signature = self::baseSignature($options);
- $signature[] = 'customerId';
- return $signature;
+ return Configuration::gateway()->creditCard()->creditNoValidate($token, $transactionAttribs);
}
- public static function updateSignature()
+ public static function sale($token, $transactionAttribs)
{
- $signature = self::baseSignature(self::baseOptions());
-
- $updateExistingBillingSignature = array(
- array(
- 'options' => array(
- 'updateExisting'
- )
- )
- );
-
- foreach($signature AS $key => $value) {
- if(is_array($value) and array_key_exists('billingAddress', $value)) {
- $signature[$key]['billingAddress'] = array_merge_recursive($value['billingAddress'], $updateExistingBillingSignature);
- }
- }
-
- return $signature;
+ return Configuration::gateway()->creditCard()->sale($token, $transactionAttribs);
}
- /**
- * sends the create request to the gateway
- *
- * @ignore
- * @param string $url
- * @param array $params
- * @return mixed
- */
- public static function _doCreate($url, $params)
+ public static function saleNoValidate($token, $transactionAttribs)
{
- $response = Braintree_Http::post($url, $params);
-
- return self::_verifyGatewayResponse($response);
+ return Configuration::gateway()->creditCard()->saleNoValidate($token, $transactionAttribs);
}
- /**
- * create a printable representation of the object as:
- * ClassName[property=value, property=value]
- * @return string
- */
- public function __toString()
+ public static function update($token, $attributes)
{
- return __CLASS__ . '[' .
- Braintree_Util::attributesToString($this->_attributes) .']';
+ return Configuration::gateway()->creditCard()->update($token, $attributes);
}
- /**
- * verifies that a valid credit card token is being used
- * @ignore
- * @param string $token
- * @throws InvalidArgumentException
- */
- private static function _validateId($token = null)
+ public static function updateNoValidate($token, $attributes)
{
- if (empty($token)) {
- throw new InvalidArgumentException(
- 'expected credit card id to be set'
- );
- }
- if (!preg_match('/^[0-9A-Za-z_-]+$/', $token)) {
- throw new InvalidArgumentException(
- $token . ' is an invalid credit card id.'
- );
- }
+ return Configuration::gateway()->creditCard()->updateNoValidate($token, $attributes);
}
- /**
- * sends the update request to the gateway
- *
- * @ignore
- * @param string $url
- * @param array $params
- * @return mixed
- */
- private static function _doUpdate($httpVerb, $url, $params)
+ public static function updateCreditCardUrl()
{
- $response = Braintree_Http::$httpVerb($url, $params);
- return self::_verifyGatewayResponse($response);
+ return Configuration::gateway()->creditCard()->updateCreditCardUrl();
}
- /**
- * generic method for validating incoming gateway responses
- *
- * creates a new Braintree_CreditCard object and encapsulates
- * it inside a Braintree_Result_Successful object, or
- * encapsulates a Braintree_Errors object inside a Result_Error
- * alternatively, throws an Unexpected exception if the response is invalid.
- *
- * @ignore
- * @param array $response gateway response values
- * @return object Result_Successful or Result_Error
- * @throws Braintree_Exception_Unexpected
- */
- private static function _verifyGatewayResponse($response)
+ public static function updateFromTransparentRedirect($queryString)
{
- if (isset($response['creditCard'])) {
- // return a populated instance of Braintree_Address
- return new Braintree_Result_Successful(
- self::factory($response['creditCard'])
- );
- } else if (isset($response['apiErrorResponse'])) {
- return new Braintree_Result_Error($response['apiErrorResponse']);
- } else {
- throw new Braintree_Exception_Unexpected(
- "Expected address or apiErrorResponse"
- );
- }
+ return Configuration::gateway()->creditCard()->updateFromTransparentRedirect($queryString);
}
- /**
- * factory method: returns an instance of Braintree_CreditCard
- * to the requesting method, with populated properties
- *
- * @ignore
- * @return object instance of Braintree_CreditCard
- */
- public static function factory($attributes)
+ public static function delete($token)
{
- $defaultAttributes = array(
- 'bin' => '',
- 'expirationMonth' => '',
- 'expirationYear' => '',
- 'last4' => '',
- );
-
- $instance = new self();
- $instance->_initialize(array_merge($defaultAttributes, $attributes));
- return $instance;
+ return Configuration::gateway()->creditCard()->delete($token);
+ }
+
+ /** @return array */
+ public static function allCardTypes()
+ {
+ return [
+ CreditCard::AMEX,
+ CreditCard::CARTE_BLANCHE,
+ CreditCard::CHINA_UNION_PAY,
+ CreditCard::DINERS_CLUB_INTERNATIONAL,
+ CreditCard::DISCOVER,
+ CreditCard::ELO,
+ CreditCard::JCB,
+ CreditCard::LASER,
+ CreditCard::MAESTRO,
+ CreditCard::MASTER_CARD,
+ CreditCard::SOLO,
+ CreditCard::SWITCH_TYPE,
+ CreditCard::VISA,
+ CreditCard::UNKNOWN
+ ];
}
}
+class_alias('Braintree\CreditCard', 'Braintree_CreditCard');
diff --git a/lib/Braintree/CreditCardGateway.php b/lib/Braintree/CreditCardGateway.php
new file mode 100644
index 0000000..5f8ce04
--- /dev/null
+++ b/lib/Braintree/CreditCardGateway.php
@@ -0,0 +1,486 @@
+== More information ==
+ *
+ * For more detailed information on CreditCards, see {@link https://developers.braintreepayments.com/reference/response/credit-card/php https://developers.braintreepayments.com/reference/response/credit-card/php}
+ * For more detailed information on CreditCard verifications, see {@link https://developers.braintreepayments.com/reference/response/credit-card-verification/php https://developers.braintreepayments.com/reference/response/credit-card-verification/php}
+ *
+ * @package Braintree
+ * @category Resources
+ */
+class CreditCardGateway
+{
+ private $_gateway;
+ private $_config;
+ private $_http;
+
+ public function __construct($gateway)
+ {
+ $this->_gateway = $gateway;
+ $this->_config = $gateway->config;
+ $this->_config->assertHasAccessTokenOrKeys();
+ $this->_http = new Http($gateway->config);
+ }
+
+ public function create($attribs)
+ {
+ Util::verifyKeys(self::createSignature(), $attribs);
+ return $this->_doCreate('/payment_methods', ['credit_card' => $attribs]);
+ }
+
+ /**
+ * attempts the create operation assuming all data will validate
+ * returns a CreditCard object instead of a Result
+ *
+ * @access public
+ * @param array $attribs
+ * @return CreditCard
+ * @throws Exception\ValidationError
+ */
+ public function createNoValidate($attribs)
+ {
+ $result = $this->create($attribs);
+ return Util::returnObjectOrThrowException(__CLASS__, $result);
+ }
+ /**
+ * create a customer from a TransparentRedirect operation
+ *
+ * @deprecated since version 2.3.0
+ * @access public
+ * @param array $attribs
+ * @return Result\Successful|Result\Error
+ */
+ public function createFromTransparentRedirect($queryString)
+ {
+ trigger_error("DEPRECATED: Please use TransparentRedirectRequest::confirm", E_USER_NOTICE);
+ $params = TransparentRedirect::parseAndValidateQueryString(
+ $queryString
+ );
+ return $this->_doCreate(
+ '/payment_methods/all/confirm_transparent_redirect_request',
+ ['id' => $params['id']]
+ );
+ }
+
+ /**
+ *
+ * @deprecated since version 2.3.0
+ * @access public
+ * @param none
+ * @return string
+ */
+ public function createCreditCardUrl()
+ {
+ trigger_error("DEPRECATED: Please use TransparentRedirectRequest::url", E_USER_NOTICE);
+ return $this->_config->baseUrl() . $this->_config->merchantPath().
+ '/payment_methods/all/create_via_transparent_redirect_request';
+ }
+
+ /**
+ * returns a ResourceCollection of expired credit cards
+ * @return ResourceCollection
+ */
+ public function expired()
+ {
+ $path = $this->_config->merchantPath() . '/payment_methods/all/expired_ids';
+ $response = $this->_http->post($path);
+ $pager = [
+ 'object' => $this,
+ 'method' => 'fetchExpired',
+ 'methodArgs' => []
+ ];
+
+ return new ResourceCollection($response, $pager);
+ }
+
+ public function fetchExpired($ids)
+ {
+ $path = $this->_config->merchantPath() . "/payment_methods/all/expired";
+ $response = $this->_http->post($path, ['search' => ['ids' => $ids]]);
+
+ return Util::extractattributeasarray(
+ $response['paymentMethods'],
+ 'creditCard'
+ );
+ }
+ /**
+ * returns a ResourceCollection of credit cards expiring between start/end
+ *
+ * @return ResourceCollection
+ */
+ public function expiringBetween($startDate, $endDate)
+ {
+ $queryPath = $this->_config->merchantPath() . '/payment_methods/all/expiring_ids?start=' . date('mY', $startDate) . '&end=' . date('mY', $endDate);
+ $response = $this->_http->post($queryPath);
+ $pager = [
+ 'object' => $this,
+ 'method' => 'fetchExpiring',
+ 'methodArgs' => [$startDate, $endDate]
+ ];
+
+ return new ResourceCollection($response, $pager);
+ }
+
+ public function fetchExpiring($startDate, $endDate, $ids)
+ {
+ $queryPath = $this->_config->merchantPath() . '/payment_methods/all/expiring?start=' . date('mY', $startDate) . '&end=' . date('mY', $endDate);
+ $response = $this->_http->post($queryPath, ['search' => ['ids' => $ids]]);
+
+ return Util::extractAttributeAsArray(
+ $response['paymentMethods'],
+ 'creditCard'
+ );
+ }
+
+ /**
+ * find a creditcard by token
+ *
+ * @access public
+ * @param string $token credit card unique id
+ * @return CreditCard
+ * @throws Exception\NotFound
+ */
+ public function find($token)
+ {
+ $this->_validateId($token);
+ try {
+ $path = $this->_config->merchantPath() . '/payment_methods/credit_card/' . $token;
+ $response = $this->_http->get($path);
+ return CreditCard::factory($response['creditCard']);
+ } catch (Exception\NotFound $e) {
+ throw new Exception\NotFound(
+ 'credit card with token ' . $token . ' not found'
+ );
+ }
+
+ }
+
+ /**
+ * Convert a payment method nonce to a credit card
+ *
+ * @access public
+ * @param string $nonce payment method nonce
+ * @return CreditCard
+ * @throws Exception\NotFound
+ */
+ public function fromNonce($nonce)
+ {
+ $this->_validateId($nonce, "nonce");
+ try {
+ $path = $this->_config->merchantPath() . '/payment_methods/from_nonce/' . $nonce;
+ $response = $this->_http->get($path);
+ return CreditCard::factory($response['creditCard']);
+ } catch (Exception\NotFound $e) {
+ throw new Exception\NotFound(
+ 'credit card with nonce ' . $nonce . ' locked, consumed or not found'
+ );
+ }
+
+ }
+
+ /**
+ * create a credit on the card for the passed transaction
+ *
+ * @access public
+ * @param array $attribs
+ * @return Result\Successful|Result\Error
+ */
+ public function credit($token, $transactionAttribs)
+ {
+ $this->_validateId($token);
+ return Transaction::credit(
+ array_merge(
+ $transactionAttribs,
+ ['paymentMethodToken' => $token]
+ )
+ );
+ }
+
+ /**
+ * create a credit on this card, assuming validations will pass
+ *
+ * returns a Transaction object on success
+ *
+ * @access public
+ * @param array $attribs
+ * @return Transaction
+ * @throws Exception\ValidationError
+ */
+ public function creditNoValidate($token, $transactionAttribs)
+ {
+ $result = $this->credit($token, $transactionAttribs);
+ return Util::returnObjectOrThrowException('Braintree\Transaction', $result);
+ }
+
+ /**
+ * create a new sale for the current card
+ *
+ * @param string $token
+ * @param array $transactionAttribs
+ * @return Result\Successful|Result\Error
+ * @see Transaction::sale()
+ */
+ public function sale($token, $transactionAttribs)
+ {
+ $this->_validateId($token);
+ return Transaction::sale(
+ array_merge(
+ $transactionAttribs,
+ ['paymentMethodToken' => $token]
+ )
+ );
+ }
+
+ /**
+ * create a new sale using this card, assuming validations will pass
+ *
+ * returns a Transaction object on success
+ *
+ * @access public
+ * @param array $transactionAttribs
+ * @param string $token
+ * @return Transaction
+ * @throws Exception\ValidationsFailed
+ * @see Transaction::sale()
+ */
+ public function saleNoValidate($token, $transactionAttribs)
+ {
+ $result = $this->sale($token, $transactionAttribs);
+ return Util::returnObjectOrThrowException('Braintree\Transaction', $result);
+ }
+
+ /**
+ * updates the creditcard record
+ *
+ * if calling this method in context, $token
+ * is the 2nd attribute. $token is not sent in object context.
+ *
+ * @access public
+ * @param array $attributes
+ * @param string $token (optional)
+ * @return Result\Successful|Result\Error
+ */
+ public function update($token, $attributes)
+ {
+ Util::verifyKeys(self::updateSignature(), $attributes);
+ $this->_validateId($token);
+ return $this->_doUpdate('put', '/payment_methods/credit_card/' . $token, ['creditCard' => $attributes]);
+ }
+
+ /**
+ * update a creditcard record, assuming validations will pass
+ *
+ * if calling this method in context, $token
+ * is the 2nd attribute. $token is not sent in object context.
+ * returns a CreditCard object on success
+ *
+ * @access public
+ * @param array $attributes
+ * @param string $token
+ * @return CreditCard
+ * @throws Exception\ValidationsFailed
+ */
+ public function updateNoValidate($token, $attributes)
+ {
+ $result = $this->update($token, $attributes);
+ return Util::returnObjectOrThrowException(__CLASS__, $result);
+ }
+ /**
+ *
+ * @access public
+ * @param none
+ * @return string
+ */
+ public function updateCreditCardUrl()
+ {
+ trigger_error("DEPRECATED: Please use TransparentRedirectRequest::url", E_USER_NOTICE);
+ return $this->_config->baseUrl() . $this->_config->merchantPath() .
+ '/payment_methods/all/update_via_transparent_redirect_request';
+ }
+
+ /**
+ * update a customer from a TransparentRedirect operation
+ *
+ * @deprecated since version 2.3.0
+ * @access public
+ * @param array $attribs
+ * @return object
+ */
+ public function updateFromTransparentRedirect($queryString)
+ {
+ trigger_error("DEPRECATED: Please use TransparentRedirectRequest::confirm", E_USER_NOTICE);
+ $params = TransparentRedirect::parseAndValidateQueryString(
+ $queryString
+ );
+ return $this->_doUpdate(
+ 'post',
+ '/payment_methods/all/confirm_transparent_redirect_request',
+ ['id' => $params['id']]
+ );
+ }
+
+ public function delete($token)
+ {
+ $this->_validateId($token);
+ $path = $this->_config->merchantPath() . '/payment_methods/credit_card/' . $token;
+ $this->_http->delete($path);
+ return new Result\Successful();
+ }
+
+ private static function baseOptions()
+ {
+ return ['makeDefault', 'verificationMerchantAccountId', 'verifyCard', 'verificationAmount', 'venmoSdkSession'];
+ }
+
+ private static function baseSignature($options)
+ {
+ return [
+ 'billingAddressId', 'cardholderName', 'cvv', 'number', 'deviceSessionId',
+ 'expirationDate', 'expirationMonth', 'expirationYear', 'token', 'venmoSdkPaymentMethodCode',
+ 'deviceData', 'fraudMerchantId', 'paymentMethodNonce',
+ ['options' => $options],
+ [
+ 'billingAddress' => self::billingAddressSignature()
+ ],
+ ];
+ }
+
+ public static function billingAddressSignature()
+ {
+ return [
+ 'firstName',
+ 'lastName',
+ 'company',
+ 'countryCodeAlpha2',
+ 'countryCodeAlpha3',
+ 'countryCodeNumeric',
+ 'countryName',
+ 'extendedAddress',
+ 'locality',
+ 'region',
+ 'postalCode',
+ 'streetAddress'
+ ];
+ }
+
+ public static function createSignature()
+ {
+ $options = self::baseOptions();
+ $options[] = "failOnDuplicatePaymentMethod";
+ $signature = self::baseSignature($options);
+ $signature[] = 'customerId';
+ return $signature;
+ }
+
+ public static function updateSignature()
+ {
+ $options = self::baseOptions();
+ $options[] = "failOnDuplicatePaymentMethod";
+ $signature = self::baseSignature($options);
+
+ $updateExistingBillingSignature = [
+ [
+ 'options' => [
+ 'updateExisting'
+ ]
+ ]
+ ];
+
+ foreach($signature AS $key => $value) {
+ if(is_array($value) and array_key_exists('billingAddress', $value)) {
+ $signature[$key]['billingAddress'] = array_merge_recursive($value['billingAddress'], $updateExistingBillingSignature);
+ }
+ }
+
+ return $signature;
+ }
+
+ /**
+ * sends the create request to the gateway
+ *
+ * @ignore
+ * @param string $subPath
+ * @param array $params
+ * @return mixed
+ */
+ public function _doCreate($subPath, $params)
+ {
+ $fullPath = $this->_config->merchantPath() . $subPath;
+ $response = $this->_http->post($fullPath, $params);
+
+ return $this->_verifyGatewayResponse($response);
+ }
+
+ /**
+ * verifies that a valid credit card identifier is being used
+ * @ignore
+ * @param string $identifier
+ * @param Optional $string $identifierType type of identifier supplied, default "token"
+ * @throws InvalidArgumentException
+ */
+ private function _validateId($identifier = null, $identifierType = "token")
+ {
+ if (empty($identifier)) {
+ throw new InvalidArgumentException(
+ 'expected credit card id to be set'
+ );
+ }
+ if (!preg_match('/^[0-9A-Za-z_-]+$/', $identifier)) {
+ throw new InvalidArgumentException(
+ $identifier . ' is an invalid credit card ' . $identifierType . '.'
+ );
+ }
+ }
+
+ /**
+ * sends the update request to the gateway
+ *
+ * @ignore
+ * @param string $url
+ * @param array $params
+ * @return mixed
+ */
+ private function _doUpdate($httpVerb, $subPath, $params)
+ {
+ $fullPath = $this->_config->merchantPath() . $subPath;
+ $response = $this->_http->$httpVerb($fullPath, $params);
+ return $this->_verifyGatewayResponse($response);
+ }
+
+ /**
+ * generic method for validating incoming gateway responses
+ *
+ * creates a new CreditCard object and encapsulates
+ * it inside a Result\Successful object, or
+ * encapsulates a Errors object inside a Result\Error
+ * alternatively, throws an Unexpected exception if the response is invalid
+ *
+ * @ignore
+ * @param array $response gateway response values
+ * @return Result\Successful|Result\Error
+ * @throws Exception\Unexpected
+ */
+ private function _verifyGatewayResponse($response)
+ {
+ if (isset($response['creditCard'])) {
+ // return a populated instance of Address
+ return new Result\Successful(
+ CreditCard::factory($response['creditCard'])
+ );
+ } elseif (isset($response['apiErrorResponse'])) {
+ return new Result\Error($response['apiErrorResponse']);
+ } else {
+ throw new Exception\Unexpected(
+ "Expected address or apiErrorResponse"
+ );
+ }
+ }
+}
+class_alias('Braintree\CreditCardGateway', 'Braintree_CreditCardGateway');
diff --git a/lib/Braintree/CreditCardVerification.php b/lib/Braintree/CreditCardVerification.php
index c20409d..d33bb47 100644
--- a/lib/Braintree/CreditCardVerification.php
+++ b/lib/Braintree/CreditCardVerification.php
@@ -1,5 +1,31 @@
name] = $term->toparam();
- }
- $criteria["ids"] = Braintree_CreditCardVerificationSearch::ids()->in($ids)->toparam();
- $response = Braintree_Http::post('/verifications/advanced_search', array('search' => $criteria));
+ Util::verifyKeys(self::createSignature(), $attributes);
+ return Configuration::gateway()->creditCardVerification()->create($attributes);
+ }
- return Braintree_Util::extractattributeasarray(
- $response['creditCardVerifications'],
- 'verification'
- );
+ public static function fetch($query, $ids)
+ {
+ return Configuration::gateway()->creditCardVerification()->fetch($query, $ids);
}
public static function search($query)
{
- $criteria = array();
- foreach ($query as $term) {
- $criteria[$term->name] = $term->toparam();
- }
-
- $response = Braintree_Http::post('/verifications/advanced_search_ids', array('search' => $criteria));
- $pager = array(
- 'className' => __CLASS__,
- 'classMethod' => 'fetch',
- 'methodArgs' => array($query)
- );
+ return Configuration::gateway()->creditCardVerification()->search($query);
+ }
- return new Braintree_ResourceCollection($response, $pager);
+ public static function createSignature()
+ {
+ return [
+ ['options' => ['amount', 'merchantAccountId']],
+ ['creditCard' =>
+ [
+ 'cardholderName', 'cvv', 'number',
+ 'expirationDate', 'expirationMonth', 'expirationYear',
+ ['billingAddress' => CreditCardGateway::billingAddressSignature()]
+ ]
+ ]];
}
}
+class_alias('Braintree\CreditCardVerification', 'Braintree_CreditCardVerification');
diff --git a/lib/Braintree/CreditCardVerificationGateway.php b/lib/Braintree/CreditCardVerificationGateway.php
new file mode 100644
index 0000000..869bd61
--- /dev/null
+++ b/lib/Braintree/CreditCardVerificationGateway.php
@@ -0,0 +1,74 @@
+_gateway = $gateway;
+ $this->_config = $gateway->config;
+ $this->_config->assertHasAccessTokenOrKeys();
+ $this->_http = new Http($gateway->config);
+ }
+
+ public function create($attributes)
+ {
+ $response = $this->_http->post($this->_config->merchantPath() . "/verifications", ['verification' => $attributes]);
+ return $this->_verifyGatewayResponse($response);
+ }
+
+ private function _verifyGatewayResponse($response)
+ {
+
+ if(isset($response['verification'])){
+ return new Result\Successful(
+ CreditCardVerification::factory($response['verification'])
+ );
+ } else if (isset($response['apiErrorResponse'])) {
+ return new Result\Error($response['apiErrorResponse']);
+ } else {
+ throw new Exception\Unexpected(
+ "Expected transaction or apiErrorResponse"
+ );
+ }
+ }
+
+ public function fetch($query, $ids)
+ {
+ $criteria = [];
+ foreach ($query as $term) {
+ $criteria[$term->name] = $term->toparam();
+ }
+ $criteria["ids"] = CreditCardVerificationSearch::ids()->in($ids)->toparam();
+ $path = $this->_config->merchantPath() . '/verifications/advanced_search';
+ $response = $this->_http->post($path, ['search' => $criteria]);
+
+ return Util::extractattributeasarray(
+ $response['creditCardVerifications'],
+ 'verification'
+ );
+ }
+
+ public function search($query)
+ {
+ $criteria = [];
+ foreach ($query as $term) {
+ $criteria[$term->name] = $term->toparam();
+ }
+
+ $path = $this->_config->merchantPath() . '/verifications/advanced_search_ids';
+ $response = $this->_http->post($path, ['search' => $criteria]);
+ $pager = [
+ 'object' => $this,
+ 'method' => 'fetch',
+ 'methodArgs' => [$query]
+ ];
+
+ return new ResourceCollection($response, $pager);
+ }
+}
+class_alias('Braintree\CreditCardVerificationGateway', 'Braintree_CreditCardVerificationGateway');
diff --git a/lib/Braintree/CreditCardVerificationSearch.php b/lib/Braintree/CreditCardVerificationSearch.php
index e799200..2dadff9 100644
--- a/lib/Braintree/CreditCardVerificationSearch.php
+++ b/lib/Braintree/CreditCardVerificationSearch.php
@@ -1,33 +1,56 @@
== More information ==
*
- * For more detailed information on Customers, see {@link http://www.braintreepayments.com/gateway/customer-api http://www.braintreepaymentsolutions.com/gateway/customer-api}
+ * For more detailed information on Customers, see {@link https://developers.braintreepayments.com/reference/response/customer/php https://developers.braintreepayments.com/reference/response/customer/php}
*
* @package Braintree
* @category Resources
- * @copyright 2010 Braintree Payment Solutions
*
- * @property-read array $addresses
+ * @property-read \Braintree\Address[] $addresses
+ * @property-read \Braintree\AndroidPayCard[] $androidPayCards
+ * @property-read \Braintree\AmexExpressCheckoutCard[] $amexExpressCheckoutCards
+ * @property-read \Braintree\ApplePayCard[] $applePayCards
+ * @property-read \Braintree\CoinbaseAccount[] $coinbaseAccounts
* @property-read string $company
- * @property-read string $createdAt
- * @property-read array $creditCards
+ * @property-read \DateTime $createdAt
+ * @property-read \Braintree\CreditCard[] $creditCards
* @property-read array $customFields custom fields passed with the request
* @property-read string $email
* @property-read string $fax
* @property-read string $firstName
* @property-read string $id
* @property-read string $lastName
+ * @property-read \Braintree\MasterpassCard[] $masterpassCards
+ * @property-read \Braintree\PaymentMethod[] $paymentMethods
+ * @property-read \Braintree\PayPalAccount[] $paypalAccounts
* @property-read string $phone
- * @property-read string $updatedAt
+ * @property-read \Braintree\SamsungPayCard[] $samsungPayCards
+ * @property-read \DateTime $updatedAt
+ * @property-read \Braintree\UsBankAccount[] $usBankAccounts
+ * @property-read \Braintree\VenmoAccount[] $venmoAccounts
+ * @property-read \Braintree\VisaCheckoutCard[] $visaCheckoutCards
* @property-read string $website
*/
-class Braintree_Customer extends Braintree
+class Customer extends Base
{
+ /**
+ *
+ * @return Customer[]
+ */
public static function all()
{
- $response = Braintree_Http::post('/customers/advanced_search_ids');
- $pager = array(
- 'className' => __CLASS__,
- 'classMethod' => 'fetch',
- 'methodArgs' => array(array())
- );
-
- return new Braintree_ResourceCollection($response, $pager);
+ return Configuration::gateway()->customer()->all();
}
+ /**
+ *
+ * @param string $query
+ * @param int[] $ids
+ * @return Customer|Customer[]
+ */
public static function fetch($query, $ids)
{
- $criteria = array();
- foreach ($query as $term) {
- $criteria[$term->name] = $term->toparam();
- }
- $criteria["ids"] = Braintree_CustomerSearch::ids()->in($ids)->toparam();
- $response = Braintree_Http::post('/customers/advanced_search', array('search' => $criteria));
-
- return Braintree_Util::extractattributeasarray(
- $response['customers'],
- 'customer'
- );
+ return Configuration::gateway()->customer()->fetch($query, $ids);
}
/**
- * Creates a customer using the given +attributes+. If :id is not passed,
- * the gateway will generate it.
*
- *
- * $result = Braintree_Customer::create(array(
- * 'first_name' => 'John',
- * 'last_name' => 'Smith',
- * 'company' => 'Smith Co.',
- * 'email' => 'john@smith.com',
- * 'website' => 'www.smithco.com',
- * 'fax' => '419-555-1234',
- * 'phone' => '614-555-1234'
- * ));
- * if($result->success) {
- * echo 'Created customer ' . $result->customer->id;
- * } else {
- * echo 'Could not create customer, see result->errors';
- * }
- *
- *
- * @access public
* @param array $attribs
- * @return object Result, either Successful or Error
+ * @return Result\Successful|Result\Error
*/
- public static function create($attribs = array())
+ public static function create($attribs = [])
{
- Braintree_Util::verifyKeys(self::createSignature(), $attribs);
- return self::_doCreate('/customers', array('customer' => $attribs));
+ return Configuration::gateway()->customer()->create($attribs);
}
/**
- * attempts the create operation assuming all data will validate
- * returns a Braintree_Customer object instead of a Result
*
- * @access public
* @param array $attribs
- * @return object
- * @throws Braintree_Exception_ValidationError
+ * @return Customer
*/
- public static function createNoValidate($attribs = array())
+ public static function createNoValidate($attribs = [])
{
- $result = self::create($attribs);
- return self::returnObjectOrThrowException(__CLASS__, $result);
+ return Configuration::gateway()->customer()->createNoValidate($attribs);
}
+
/**
- * create a customer from a TransparentRedirect operation
- *
- * @access public
- * @param array $attribs
- * @return object
+ * @deprecated since version 2.3.0
+ * @param string $queryString
+ * @return Result\Successful
*/
public static function createFromTransparentRedirect($queryString)
{
- trigger_error("DEPRECATED: Please use Braintree_TransparentRedirectRequest::confirm", E_USER_NOTICE);
- $params = Braintree_TransparentRedirect::parseAndValidateQueryString(
- $queryString
- );
- return self::_doCreate(
- '/customers/all/confirm_transparent_redirect_request',
- array('id' => $params['id'])
- );
+ return Configuration::gateway()->customer()->createFromTransparentRedirect($queryString);
}
/**
- *
- * @access public
- * @param none
+ * @deprecated since version 2.3.0
* @return string
*/
public static function createCustomerUrl()
{
- trigger_error("DEPRECATED: Please use Braintree_TransparentRedirectRequest::url", E_USER_NOTICE);
- return Braintree_Configuration::merchantUrl() .
- '/customers/all/create_via_transparent_redirect_request';
+ return Configuration::gateway()->customer()->createCustomerUrl();
}
-
/**
- * creates a full array signature of a valid create request
- * @return array gateway create request format
- */
- public static function createSignature()
- {
-
- $creditCardSignature = Braintree_CreditCard::createSignature();
- unset($creditCardSignature['customerId']);
- $signature = array(
- 'id', 'company', 'email', 'fax', 'firstName',
- 'lastName', 'phone', 'website', 'deviceData',
- 'deviceSessionId', 'fraudMerchantId',
- array('creditCard' => $creditCardSignature),
- array('customFields' => array('_anyKey_')),
- );
- return $signature;
- }
-
- /**
- * creates a full array signature of a valid update request
- * @return array update request format
- */
- public static function updateSignature()
- {
- $creditCardSignature = Braintree_CreditCard::updateSignature();
-
- foreach($creditCardSignature AS $key => $value) {
- if(is_array($value) and array_key_exists('options', $value)) {
- array_push($creditCardSignature[$key]['options'], 'updateExistingToken');
- }
- }
-
- $signature = array(
- 'id', 'company', 'email', 'fax', 'firstName',
- 'lastName', 'phone', 'website', 'deviceData',
- 'deviceSessionId', 'fraudMerchantId',
- array('creditCard' => $creditCardSignature),
- array('customFields' => array('_anyKey_')),
- );
- return $signature;
- }
-
-
- /**
- * find a customer by id
*
- * @access public
- * @param string id customer Id
- * @return object Braintree_Customer
- * @throws Braintree_Exception_NotFound
+ * @throws Exception\NotFound
+ * @param string $id customer id
+ * @return Customer
*/
- public static function find($id)
+ public static function find($id, $associationFilterId = null)
{
- self::_validateId($id);
- try {
- $response = Braintree_Http::get('/customers/'.$id);
- return self::factory($response['customer']);
- } catch (Braintree_Exception_NotFound $e) {
- throw new Braintree_Exception_NotFound(
- 'customer with id ' . $id . ' not found'
- );
- }
-
+ return Configuration::gateway()->customer()->find($id, $associationFilterId);
}
/**
- * credit a customer for the passed transaction
*
- * @access public
- * @param array $attribs
- * @return object Braintree_Result_Successful or Braintree_Result_Error
+ * @param int $customerId
+ * @param array $transactionAttribs
+ * @return Result\Successful|Result\Error
*/
public static function credit($customerId, $transactionAttribs)
{
- self::_validateId($customerId);
- return Braintree_Transaction::credit(
- array_merge($transactionAttribs,
- array('customerId' => $customerId)
- )
- );
+ return Configuration::gateway()->customer()->credit($customerId, $transactionAttribs);
}
/**
- * credit a customer, assuming validations will pass
*
- * returns a Braintree_Transaction object on success
- *
- * @access public
- * @param array $attribs
- * @return object Braintree_Transaction
- * @throws Braintree_Exception_ValidationError
+ * @throws Exception\ValidationError
+ * @param type $customerId
+ * @param type $transactionAttribs
+ * @return Transaction
*/
public static function creditNoValidate($customerId, $transactionAttribs)
{
- $result = self::credit($customerId, $transactionAttribs);
- return self::returnObjectOrThrowException('Braintree_Transaction', $result);
+ return Configuration::gateway()->customer()->creditNoValidate($customerId, $transactionAttribs);
}
/**
- * delete a customer by id
*
- * @param string $customerId
+ * @throws Exception on invalid id or non-200 http response code
+ * @param int $customerId
+ * @return Result\Successful
*/
public static function delete($customerId)
{
- self::_validateId($customerId);
- Braintree_Http::delete('/customers/' . $customerId);
- return new Braintree_Result_Successful();
+ return Configuration::gateway()->customer()->delete($customerId);
}
/**
- * create a new sale for a customer
*
- * @param string $customerId
+ * @param int $customerId
* @param array $transactionAttribs
- * @return object Braintree_Result_Successful or Braintree_Result_Error
- * @see Braintree_Transaction::sale()
+ * @return Transaction
*/
public static function sale($customerId, $transactionAttribs)
{
- self::_validateId($customerId);
- return Braintree_Transaction::sale(
- array_merge($transactionAttribs,
- array('customerId' => $customerId)
- )
- );
+ return Configuration::gateway()->customer()->sale($customerId, $transactionAttribs);
}
/**
- * create a new sale for a customer, assuming validations will pass
*
- * returns a Braintree_Transaction object on success
- * @access public
- * @param string $customerId
+ * @param int $customerId
* @param array $transactionAttribs
- * @return object Braintree_Transaction
- * @throws Braintree_Exception_ValidationsFailed
- * @see Braintree_Transaction::sale()
+ * @return Transaction
*/
public static function saleNoValidate($customerId, $transactionAttribs)
{
- $result = self::sale($customerId, $transactionAttribs);
- return self::returnObjectOrThrowException('Braintree_Transaction', $result);
+ return Configuration::gateway()->customer()->saleNoValidate($customerId, $transactionAttribs);
}
/**
- * Returns a ResourceCollection of customers matching the search query.
*
- * If query is a string, the search will be a basic search.
- * If query is a hash, the search will be an advanced search.
- * For more detailed information and examples, see {@link http://www.braintreepayments.com/gateway/customer-api#searching http://www.braintreepaymentsolutions.com/gateway/customer-api}
- *
- * @param mixed $query search query
- * @param array $options options such as page number
- * @return object Braintree_ResourceCollection
* @throws InvalidArgumentException
+ * @param string $query
+ * @return ResourceCollection
*/
public static function search($query)
{
- $criteria = array();
- foreach ($query as $term) {
- $criteria[$term->name] = $term->toparam();
- }
-
- $response = Braintree_Http::post('/customers/advanced_search_ids', array('search' => $criteria));
- $pager = array(
- 'className' => __CLASS__,
- 'classMethod' => 'fetch',
- 'methodArgs' => array($query)
- );
-
- return new Braintree_ResourceCollection($response, $pager);
+ return Configuration::gateway()->customer()->search($query);
}
/**
- * updates the customer record
*
- * if calling this method in static context, customerId
- * is the 2nd attribute. customerId is not sent in object context.
- *
- * @access public
+ * @throws Exception\Unexpected
+ * @param int $customerId
* @param array $attributes
- * @param string $customerId (optional)
- * @return object Braintree_Result_Successful or Braintree_Result_Error
+ * @return Result\Successful|Result\Error
*/
public static function update($customerId, $attributes)
{
- Braintree_Util::verifyKeys(self::updateSignature(), $attributes);
- self::_validateId($customerId);
- return self::_doUpdate(
- 'put',
- '/customers/' . $customerId,
- array('customer' => $attributes)
- );
+ return Configuration::gateway()->customer()->update($customerId, $attributes);
}
/**
- * update a customer record, assuming validations will pass
- *
- * if calling this method in static context, customerId
- * is the 2nd attribute. customerId is not sent in object context.
- * returns a Braintree_Customer object on success
*
- * @access public
+ * @throws Exception\Unexpected
+ * @param int $customerId
* @param array $attributes
- * @param string $customerId
- * @return object Braintree_Customer
- * @throws Braintree_Exception_ValidationsFailed
+ * @return CustomerGateway
*/
public static function updateNoValidate($customerId, $attributes)
{
- $result = self::update($customerId, $attributes);
- return self::returnObjectOrThrowException(__CLASS__, $result);
+ return Configuration::gateway()->customer()->updateNoValidate($customerId, $attributes);
}
+
/**
*
- * @access public
- * @param none
+ * @deprecated since version 2.3.0
* @return string
*/
public static function updateCustomerUrl()
{
- trigger_error("DEPRECATED: Please use Braintree_TransparentRedirectRequest::url", E_USER_NOTICE);
- return Braintree_Configuration::merchantUrl() .
- '/customers/all/update_via_transparent_redirect_request';
+ return Configuration::gateway()->customer()->updateCustomerUrl();
}
/**
- * update a customer from a TransparentRedirect operation
*
- * @access public
- * @param array $attribs
- * @return object
+ * @deprecated since version 2.3.0
+ * @param string $queryString
+ * @return Result\Successful|Result\Error
*/
public static function updateFromTransparentRedirect($queryString)
{
- trigger_error("DEPRECATED: Please use Braintree_TransparentRedirectRequest::confirm", E_USER_NOTICE);
- $params = Braintree_TransparentRedirect::parseAndValidateQueryString(
- $queryString
- );
- return self::_doUpdate(
- 'post',
- '/customers/all/confirm_transparent_redirect_request',
- array('id' => $params['id'])
- );
+ return Configuration::gateway()->customer()->updateFromTransparentRedirect($queryString);
}
/* instance methods */
@@ -395,32 +229,121 @@ public static function updateFromTransparentRedirect($queryString)
* @ignore
* @access protected
* @param array $customerAttribs array of customer data
- * @return none
*/
protected function _initialize($customerAttribs)
{
- // set the attributes
$this->_attributes = $customerAttribs;
- // map each address into its own object
- $addressArray = array();
+ $addressArray = [];
if (isset($customerAttribs['addresses'])) {
foreach ($customerAttribs['addresses'] AS $address) {
- $addressArray[] = Braintree_Address::factory($address);
+ $addressArray[] = Address::factory($address);
}
}
$this->_set('addresses', $addressArray);
- // map each creditcard into its own object
- $ccArray = array();
+ $creditCardArray = [];
if (isset($customerAttribs['creditCards'])) {
foreach ($customerAttribs['creditCards'] AS $creditCard) {
- $ccArray[] = Braintree_CreditCard::factory($creditCard);
+ $creditCardArray[] = CreditCard::factory($creditCard);
}
}
- $this->_set('creditCards', $ccArray);
+ $this->_set('creditCards', $creditCardArray);
+ $coinbaseAccountArray = [];
+ if (isset($customerAttribs['coinbaseAccounts'])) {
+ foreach ($customerAttribs['coinbaseAccounts'] AS $coinbaseAccount) {
+ $coinbaseAccountArray[] = CoinbaseAccount::factory($coinbaseAccount);
+ }
+ }
+ $this->_set('coinbaseAccounts', $coinbaseAccountArray);
+
+ $paypalAccountArray = [];
+ if (isset($customerAttribs['paypalAccounts'])) {
+ foreach ($customerAttribs['paypalAccounts'] AS $paypalAccount) {
+ $paypalAccountArray[] = PayPalAccount::factory($paypalAccount);
+ }
+ }
+ $this->_set('paypalAccounts', $paypalAccountArray);
+
+ $applePayCardArray = [];
+ if (isset($customerAttribs['applePayCards'])) {
+ foreach ($customerAttribs['applePayCards'] AS $applePayCard) {
+ $applePayCardArray[] = ApplePayCard::factory($applePayCard);
+ }
+ }
+ $this->_set('applePayCards', $applePayCardArray);
+
+ $androidPayCardArray = [];
+ if (isset($customerAttribs['androidPayCards'])) {
+ foreach ($customerAttribs['androidPayCards'] AS $androidPayCard) {
+ $androidPayCardArray[] = AndroidPayCard::factory($androidPayCard);
+ }
+ }
+ $this->_set('androidPayCards', $androidPayCardArray);
+
+ $amexExpressCheckoutCardArray = [];
+ if (isset($customerAttribs['amexExpressCheckoutCards'])) {
+ foreach ($customerAttribs['amexExpressCheckoutCards'] AS $amexExpressCheckoutCard) {
+ $amexExpressCheckoutCardArray[] = AmexExpressCheckoutCard::factory($amexExpressCheckoutCard);
+ }
+ }
+ $this->_set('amexExpressCheckoutCards', $amexExpressCheckoutCardArray);
+
+ $venmoAccountArray = array();
+ if (isset($customerAttribs['venmoAccounts'])) {
+ foreach ($customerAttribs['venmoAccounts'] AS $venmoAccount) {
+ $venmoAccountArray[] = VenmoAccount::factory($venmoAccount);
+ }
+ }
+ $this->_set('venmoAccounts', $venmoAccountArray);
+
+ $visaCheckoutCardArray = [];
+ if (isset($customerAttribs['visaCheckoutCards'])) {
+ foreach ($customerAttribs['visaCheckoutCards'] AS $visaCheckoutCard) {
+ $visaCheckoutCardArray[] = VisaCheckoutCard::factory($visaCheckoutCard);
+ }
+ }
+ $this->_set('visaCheckoutCards', $visaCheckoutCardArray);
+
+ $masterpassCardArray = [];
+ if (isset($customerAttribs['masterpassCards'])) {
+ foreach ($customerAttribs['masterpassCards'] AS $masterpassCard) {
+ $masterpassCardArray[] = MasterpassCard::factory($masterpassCard);
+ }
+ }
+ $this->_set('masterpassCards', $masterpassCardArray);
+
+ $samsungPayCardArray = [];
+ if (isset($customerAttribs['samsungPayCards'])) {
+ foreach ($customerAttribs['samsungPayCards'] AS $samsungPayCard) {
+ $samsungPayCardArray[] = SamsungPayCard::factory($samsungPayCard);
+ }
+ }
+ $this->_set('samsungPayCards', $samsungPayCardArray);
+
+ $usBankAccountArray = array();
+ if (isset($customerAttribs['usBankAccounts'])) {
+ foreach ($customerAttribs['usBankAccounts'] AS $usBankAccount) {
+ $usBankAccountArray[] = UsBankAccount::factory($usBankAccount);
+ }
+ }
+ $this->_set('usBankAccounts', $usBankAccountArray);
+
+ $this->_set('paymentMethods', array_merge(
+ $this->creditCards,
+ $this->paypalAccounts,
+ $this->applePayCards,
+ $this->coinbaseAccounts,
+ $this->androidPayCards,
+ $this->amexExpressCheckoutCards,
+ $this->venmoAccounts,
+ $this->visaCheckoutCards,
+ $this->masterpassCards,
+ $this->samsungPayCards,
+ $this->usBankAccounts
+ ));
}
/**
@@ -430,19 +353,47 @@ protected function _initialize($customerAttribs)
public function __toString()
{
return __CLASS__ . '[' .
- Braintree_Util::attributesToString($this->_attributes) .']';
+ Util::attributesToString($this->_attributes) .']';
}
/**
- * returns false if comparing object is not a Braintree_Customer,
- * or is a Braintree_Customer with a different id
+ * returns false if comparing object is not a Customer,
+ * or is a Customer with a different id
*
* @param object $otherCust customer to compare against
* @return boolean
*/
public function isEqual($otherCust)
{
- return !($otherCust instanceof Braintree_Customer) ? false : $this->id === $otherCust->id;
+ return !($otherCust instanceof Customer) ? false : $this->id === $otherCust->id;
+ }
+
+ /**
+ * returns an array containt all of the customer's payment methods
+ *
+ * @deprecated since version 3.1.0 - use the paymentMethods property directly
+ *
+ * @return array
+ */
+ public function paymentMethods()
+ {
+ return $this->paymentMethods;
+ }
+
+ /**
+ * returns the customer's default payment method
+ *
+ * @return CreditCard|PayPalAccount
+ */
+ public function defaultPaymentMethod()
+ {
+ $defaultPaymentMethods = array_filter($this->paymentMethods, 'Braintree\Customer::_defaultPaymentMethodFilter');
+ return current($defaultPaymentMethods);
+ }
+
+ public static function _defaultPaymentMethodFilter($paymentMethod)
+ {
+ return $paymentMethod->isDefault();
}
/* private class properties */
@@ -451,7 +402,7 @@ public function isEqual($otherCust)
* @access protected
* @var array registry of customer data
*/
- protected $_attributes = array(
+ protected $_attributes = [
'addresses' => '',
'company' => '',
'creditCards' => '',
@@ -464,101 +415,21 @@ public function isEqual($otherCust)
'createdAt' => '',
'updatedAt' => '',
'website' => '',
- );
-
- /**
- * sends the create request to the gateway
- *
- * @ignore
- * @param string $url
- * @param array $params
- * @return mixed
- */
- public static function _doCreate($url, $params)
- {
- $response = Braintree_Http::post($url, $params);
-
- return self::_verifyGatewayResponse($response);
- }
+ ];
/**
- * verifies that a valid customer id is being used
- * @ignore
- * @param string customer id
- * @throws InvalidArgumentException
- */
- private static function _validateId($id = null) {
- if (empty($id)) {
- throw new InvalidArgumentException(
- 'expected customer id to be set'
- );
- }
- if (!preg_match('/^[0-9A-Za-z_-]+$/', $id)) {
- throw new InvalidArgumentException(
- $id . ' is an invalid customer id.'
- );
- }
- }
-
-
- /* private class methods */
-
- /**
- * sends the update request to the gateway
- *
- * @ignore
- * @param string $url
- * @param array $params
- * @return mixed
- */
- private static function _doUpdate($httpVerb, $url, $params)
- {
- $response = Braintree_Http::$httpVerb($url, $params);
-
- return self::_verifyGatewayResponse($response);
- }
-
- /**
- * generic method for validating incoming gateway responses
- *
- * creates a new Braintree_Customer object and encapsulates
- * it inside a Braintree_Result_Successful object, or
- * encapsulates a Braintree_Errors object inside a Result_Error
- * alternatively, throws an Unexpected exception if the response is invalid.
- *
- * @ignore
- * @param array $response gateway response values
- * @return object Result_Successful or Result_Error
- * @throws Braintree_Exception_Unexpected
- */
- private static function _verifyGatewayResponse($response)
- {
- if (isset($response['customer'])) {
- // return a populated instance of Braintree_Customer
- return new Braintree_Result_Successful(
- self::factory($response['customer'])
- );
- } else if (isset($response['apiErrorResponse'])) {
- return new Braintree_Result_Error($response['apiErrorResponse']);
- } else {
- throw new Braintree_Exception_Unexpected(
- "Expected customer or apiErrorResponse"
- );
- }
- }
-
- /**
- * factory method: returns an instance of Braintree_Customer
+ * factory method: returns an instance of Customer
* to the requesting method, with populated properties
*
* @ignore
- * @return object instance of Braintree_Customer
+ * @param array $attributes
+ * @return Customer
*/
public static function factory($attributes)
{
- $instance = new self();
+ $instance = new Customer();
$instance->_initialize($attributes);
return $instance;
}
-
}
+class_alias('Braintree\Customer', 'Braintree_Customer');
diff --git a/lib/Braintree/CustomerGateway.php b/lib/Braintree/CustomerGateway.php
new file mode 100644
index 0000000..f010082
--- /dev/null
+++ b/lib/Braintree/CustomerGateway.php
@@ -0,0 +1,668 @@
+== More information ==
+ *
+ * For more detailed information on Customers, see {@link https://developers.braintreepayments.com/reference/response/customer/php https://developers.braintreepayments.com/reference/response/customer/php}
+ *
+ * @package Braintree
+ * @category Resources
+ */
+class CustomerGateway
+{
+ private $_gateway;
+ private $_config;
+ private $_http;
+
+ public function __construct($gateway)
+ {
+ $this->_gateway = $gateway;
+ $this->_config = $gateway->config;
+ $this->_config->assertHasAccessTokenOrKeys();
+ $this->_http = new Http($gateway->config);
+ }
+
+ public function all()
+ {
+ $path = $this->_config->merchantPath() . '/customers/advanced_search_ids';
+ $response = $this->_http->post($path);
+ $pager = [
+ 'object' => $this,
+ 'method' => 'fetch',
+ 'methodArgs' => [[]]
+ ];
+
+ return new ResourceCollection($response, $pager);
+ }
+
+ public function fetch($query, $ids)
+ {
+ $criteria = [];
+ foreach ($query as $term) {
+ $criteria[$term->name] = $term->toparam();
+ }
+ $criteria["ids"] = CustomerSearch::ids()->in($ids)->toparam();
+ $path = $this->_config->merchantPath() . '/customers/advanced_search';
+ $response = $this->_http->post($path, ['search' => $criteria]);
+
+ return Util::extractattributeasarray(
+ $response['customers'],
+ 'customer'
+ );
+ }
+
+ /**
+ * Creates a customer using the given +attributes+. If :id is not passed,
+ * the gateway will generate it.
+ *
+ *
+ * $result = Customer::create(array(
+ * 'first_name' => 'John',
+ * 'last_name' => 'Smith',
+ * 'company' => 'Smith Co.',
+ * 'email' => 'john@smith.com',
+ * 'website' => 'www.smithco.com',
+ * 'fax' => '419-555-1234',
+ * 'phone' => '614-555-1234'
+ * ));
+ * if($result->success) {
+ * echo 'Created customer ' . $result->customer->id;
+ * } else {
+ * echo 'Could not create customer, see result->errors';
+ * }
+ *
+ *
+ * @access public
+ * @param array $attribs
+ * @return Result\Successful|Result\Error
+ */
+ public function create($attribs = [])
+ {
+ Util::verifyKeys(self::createSignature(), $attribs);
+ return $this->_doCreate('/customers', ['customer' => $attribs]);
+ }
+
+ /**
+ * attempts the create operation assuming all data will validate
+ * returns a Customer object instead of a Result
+ *
+ * @access public
+ * @param array $attribs
+ * @return Customer
+ * @throws Exception\ValidationError
+ */
+ public function createNoValidate($attribs = [])
+ {
+ $result = $this->create($attribs);
+ return Util::returnObjectOrThrowException(__CLASS__, $result);
+ }
+ /**
+ * create a customer from a TransparentRedirect operation
+ *
+ * @deprecated since version 2.3.0
+ * @access public
+ * @param array $attribs
+ * @return Customer
+ */
+ public function createFromTransparentRedirect($queryString)
+ {
+ trigger_error("DEPRECATED: Please use TransparentRedirectRequest::confirm", E_USER_NOTICE);
+ $params = TransparentRedirect::parseAndValidateQueryString(
+ $queryString
+ );
+ return $this->_doCreate(
+ '/customers/all/confirm_transparent_redirect_request',
+ ['id' => $params['id']]
+ );
+ }
+
+ /**
+ *
+ * @deprecated since version 2.3.0
+ * @access public
+ * @param none
+ * @return string
+ */
+ public function createCustomerUrl()
+ {
+ trigger_error("DEPRECATED: Please use TransparentRedirectRequest::url", E_USER_NOTICE);
+ return $this->_config->baseUrl() . $this->_config->merchantPath() .
+ '/customers/all/create_via_transparent_redirect_request';
+ }
+
+
+ /**
+ * creates a full array signature of a valid create request
+ * @return array gateway create request format
+ */
+ public static function createSignature()
+ {
+ $creditCardSignature = CreditCardGateway::createSignature();
+ unset($creditCardSignature[array_search('customerId', $creditCardSignature)]);
+
+ $signature = [
+ 'id', 'company', 'email', 'fax', 'firstName',
+ 'lastName', 'phone', 'website', 'deviceData',
+ 'deviceSessionId', 'fraudMerchantId', 'paymentMethodNonce',
+ ['riskData' =>
+ ['customerBrowser', 'customerIp', 'customer_browser', 'customer_ip']
+ ],
+ ['creditCard' => $creditCardSignature],
+ ['customFields' => ['_anyKey_']],
+ ['options' => [
+ ['paypal' => [
+ 'payee_email',
+ 'payeeEmail',
+ 'order_id',
+ 'orderId',
+ 'custom_field',
+ 'customField',
+ 'description',
+ 'amount',
+ ['shipping' =>
+ [
+ 'firstName', 'lastName', 'company', 'countryName',
+ 'countryCodeAlpha2', 'countryCodeAlpha3', 'countryCodeNumeric',
+ 'extendedAddress', 'locality', 'postalCode', 'region',
+ 'streetAddress'],
+ ],
+ ]]
+ ]],
+ ];
+ return $signature;
+ }
+
+ /**
+ * creates a full array signature of a valid update request
+ * @return array update request format
+ */
+ public static function updateSignature()
+ {
+ $creditCardSignature = CreditCardGateway::updateSignature();
+
+ foreach($creditCardSignature AS $key => $value) {
+ if(is_array($value) and array_key_exists('options', $value)) {
+ array_push($creditCardSignature[$key]['options'], 'updateExistingToken');
+ }
+ }
+
+ $signature = [
+ 'id', 'company', 'email', 'fax', 'firstName',
+ 'lastName', 'phone', 'website', 'deviceData',
+ 'deviceSessionId', 'fraudMerchantId', 'paymentMethodNonce', 'defaultPaymentMethodToken',
+ ['creditCard' => $creditCardSignature],
+ ['customFields' => ['_anyKey_']],
+ ['options' => [
+ ['paypal' => [
+ 'payee_email',
+ 'payeeEmail',
+ 'order_id',
+ 'orderId',
+ 'custom_field',
+ 'customField',
+ 'description',
+ 'amount',
+ ['shipping' =>
+ [
+ 'firstName', 'lastName', 'company', 'countryName',
+ 'countryCodeAlpha2', 'countryCodeAlpha3', 'countryCodeNumeric',
+ 'extendedAddress', 'locality', 'postalCode', 'region',
+ 'streetAddress'],
+ ],
+ ]],
+ ]],
+ ];
+ return $signature;
+ }
+
+
+ /**
+ * find a customer by id
+ *
+ * @access public
+ * @param string id customer Id
+ * @param string associationFilterId association filter Id
+ * @return Customer|boolean The customer object or false if the request fails.
+ * @throws Exception\NotFound
+ */
+ public function find($id, $associationFilterId = null)
+ {
+ $this->_validateId($id);
+ try {
+ $queryParams = '';
+ if ($associationFilterId) {
+ $queryParams = '?association_filter_id=' . $associationFilterId;
+ }
+ $path = $this->_config->merchantPath() . '/customers/' . $id . $queryParams;
+ $response = $this->_http->get($path);
+ return Customer::factory($response['customer']);
+ } catch (Exception\NotFound $e) {
+ throw new Exception\NotFound(
+ 'customer with id ' . $id . ' not found'
+ );
+ }
+ }
+
+ /**
+ * credit a customer for the passed transaction
+ *
+ * @access public
+ * @param int $customerId
+ * @param array $transactionAttribs
+ * @return Result\Successful|Result\Error
+ */
+ public function credit($customerId, $transactionAttribs)
+ {
+ $this->_validateId($customerId);
+ return Transaction::credit(
+ array_merge($transactionAttribs,
+ ['customerId' => $customerId]
+ )
+ );
+ }
+
+ /**
+ * credit a customer, assuming validations will pass
+ *
+ * returns a Transaction object on success
+ *
+ * @access public
+ * @param int $customerId
+ * @param array $transactionAttribs
+ * @return Transaction
+ * @throws Exception\ValidationError
+ */
+ public function creditNoValidate($customerId, $transactionAttribs)
+ {
+ $result = $this->credit($customerId, $transactionAttribs);
+ return Util::returnObjectOrThrowException('Braintree\Transaction', $result);
+ }
+
+ /**
+ * delete a customer by id
+ *
+ * @param string $customerId
+ */
+ public function delete($customerId)
+ {
+ $this->_validateId($customerId);
+ $path = $this->_config->merchantPath() . '/customers/' . $customerId;
+ $this->_http->delete($path);
+ return new Result\Successful();
+ }
+
+ /**
+ * create a new sale for a customer
+ *
+ * @param string $customerId
+ * @param array $transactionAttribs
+ * @return Result\Successful|Result\Error
+ * @see Transaction::sale()
+ */
+ public function sale($customerId, $transactionAttribs)
+ {
+ $this->_validateId($customerId);
+ return Transaction::sale(
+ array_merge($transactionAttribs,
+ ['customerId' => $customerId]
+ )
+ );
+ }
+
+ /**
+ * create a new sale for a customer, assuming validations will pass
+ *
+ * returns a Transaction object on success
+ * @access public
+ * @param string $customerId
+ * @param array $transactionAttribs
+ * @return Transaction
+ * @throws Exception\ValidationsFailed
+ * @see Transaction::sale()
+ */
+ public function saleNoValidate($customerId, $transactionAttribs)
+ {
+ $result = $this->sale($customerId, $transactionAttribs);
+ return Util::returnObjectOrThrowException('Braintree\Transaction', $result);
+ }
+
+ /**
+ * Returns a ResourceCollection of customers matching the search query.
+ *
+ * If query is a string, the search will be a basic search.
+ * If query is a hash, the search will be an advanced search.
+ * For more detailed information and examples, see {@link https://developers.braintreepayments.com/reference/request/customer/search/php https://developers.braintreepayments.com/reference/request/customer/search/php}
+ *
+ * @param mixed $query search query
+ * @return ResourceCollection
+ * @throws InvalidArgumentException
+ */
+ public function search($query)
+ {
+ $criteria = [];
+ foreach ($query as $term) {
+ $result = $term->toparam();
+ if(is_null($result) || empty($result)) {
+ throw new InvalidArgumentException('Operator must be provided');
+ }
+
+ $criteria[$term->name] = $term->toparam();
+ }
+
+ $path = $this->_config->merchantPath() . '/customers/advanced_search_ids';
+ $response = $this->_http->post($path, ['search' => $criteria]);
+ $pager = [
+ 'object' => $this,
+ 'method' => 'fetch',
+ 'methodArgs' => [$query]
+ ];
+
+ return new ResourceCollection($response, $pager);
+ }
+
+ /**
+ * updates the customer record
+ *
+ * if calling this method in static context, customerId
+ * is the 2nd attribute. customerId is not sent in object context.
+ *
+ * @access public
+ * @param string $customerId (optional)
+ * @param array $attributes
+ * @return Result\Successful|Result\Error
+ */
+ public function update($customerId, $attributes)
+ {
+ Util::verifyKeys(self::updateSignature(), $attributes);
+ $this->_validateId($customerId);
+ return $this->_doUpdate(
+ 'put',
+ '/customers/' . $customerId,
+ ['customer' => $attributes]
+ );
+ }
+
+ /**
+ * update a customer record, assuming validations will pass
+ *
+ * if calling this method in static context, customerId
+ * is the 2nd attribute. customerId is not sent in object context.
+ * returns a Customer object on success
+ *
+ * @access public
+ * @param string $customerId
+ * @param array $attributes
+ * @return Customer
+ * @throws Exception\ValidationsFailed
+ */
+ public function updateNoValidate($customerId, $attributes)
+ {
+ $result = $this->update($customerId, $attributes);
+ return Util::returnObjectOrThrowException(__CLASS__, $result);
+ }
+ /**
+ *
+ * @deprecated since version 2.3.0
+ * @access public
+ * @return string
+ */
+ public function updateCustomerUrl()
+ {
+ trigger_error("DEPRECATED: Please use TransparentRedirectRequest::url", E_USER_NOTICE);
+ return $this->_config->baseUrl() . $this->_config->merchantPath() .
+ '/customers/all/update_via_transparent_redirect_request';
+ }
+
+ /**
+ * update a customer from a TransparentRedirect operation
+ *
+ * @deprecated since version 2.3.0
+ * @access public
+ * @param string $queryString
+ * @return object
+ */
+ public function updateFromTransparentRedirect($queryString)
+ {
+ trigger_error("DEPRECATED: Please use TransparentRedirectRequest::confirm", E_USER_NOTICE);
+ $params = TransparentRedirect::parseAndValidateQueryString(
+ $queryString
+ );
+ return $this->_doUpdate(
+ 'post',
+ '/customers/all/confirm_transparent_redirect_request',
+ ['id' => $params['id']]
+ );
+ }
+
+ /* instance methods */
+
+ /**
+ * sets instance properties from an array of values
+ *
+ * @ignore
+ * @access protected
+ * @param array $customerAttribs array of customer data
+ * @return void
+ */
+ protected function _initialize($customerAttribs)
+ {
+ // set the attributes
+ $this->_attributes = $customerAttribs;
+
+ // map each address into its own object
+ $addressArray = [];
+ if (isset($customerAttribs['addresses'])) {
+
+ foreach ($customerAttribs['addresses'] AS $address) {
+ $addressArray[] = Address::factory($address);
+ }
+ }
+ $this->_set('addresses', $addressArray);
+
+ // map each creditCard into its own object
+ $creditCardArray = [];
+ if (isset($customerAttribs['creditCards'])) {
+ foreach ($customerAttribs['creditCards'] AS $creditCard) {
+ $creditCardArray[] = CreditCard::factory($creditCard);
+ }
+ }
+ $this->_set('creditCards', $creditCardArray);
+
+ // map each coinbaseAccount into its own object
+ $coinbaseAccountArray = [];
+ if (isset($customerAttribs['coinbaseAccounts'])) {
+ foreach ($customerAttribs['coinbaseAccounts'] AS $coinbaseAccount) {
+ $coinbaseAccountArray[] = CoinbaseAccount::factory($coinbaseAccount);
+ }
+ }
+ $this->_set('coinbaseAccounts', $coinbaseAccountArray);
+
+ // map each paypalAccount into its own object
+ $paypalAccountArray = [];
+ if (isset($customerAttribs['paypalAccounts'])) {
+ foreach ($customerAttribs['paypalAccounts'] AS $paypalAccount) {
+ $paypalAccountArray[] = PayPalAccount::factory($paypalAccount);
+ }
+ }
+ $this->_set('paypalAccounts', $paypalAccountArray);
+
+ // map each applePayCard into its own object
+ $applePayCardArray = [];
+ if (isset($customerAttribs['applePayCards'])) {
+ foreach ($customerAttribs['applePayCards'] AS $applePayCard) {
+ $applePayCardArray[] = ApplePayCard::factory($applePayCard);
+ }
+ }
+ $this->_set('applePayCards', $applePayCardArray);
+
+ // map each androidPayCard into its own object
+ $androidPayCardArray = [];
+ if (isset($customerAttribs['androidPayCards'])) {
+ foreach ($customerAttribs['androidPayCards'] AS $androidPayCard) {
+ $androidPayCardArray[] = AndroidPayCard::factory($androidPayCard);
+ }
+ }
+ $this->_set('androidPayCards', $androidPayCardArray);
+
+ $this->_set('paymentMethods', array_merge($this->creditCards, $this->paypalAccounts, $this->applePayCards, $this->coinbaseAccounts, $this->androidPayCards));
+ }
+
+ /**
+ * returns a string representation of the customer
+ * @return string
+ */
+ public function __toString()
+ {
+ return __CLASS__ . '[' .
+ Util::attributesToString($this->_attributes) .']';
+ }
+
+ /**
+ * returns false if comparing object is not a Customer,
+ * or is a Customer with a different id
+ *
+ * @param object $otherCust customer to compare against
+ * @return boolean
+ */
+ public function isEqual($otherCust)
+ {
+ return !($otherCust instanceof Customer) ? false : $this->id === $otherCust->id;
+ }
+
+ /**
+ * returns an array containt all of the customer's payment methods
+ *
+ * @return array
+ */
+ public function paymentMethods()
+ {
+ return $this->paymentMethods;
+ }
+
+ /**
+ * returns the customer's default payment method
+ *
+ * @return CreditCard|PayPalAccount|ApplePayCard|AndroidPayCard
+ */
+ public function defaultPaymentMethod()
+ {
+ $defaultPaymentMethods = array_filter($this->paymentMethods, 'Braintree\\Customer::_defaultPaymentMethodFilter');
+ return current($defaultPaymentMethods);
+ }
+
+ public static function _defaultPaymentMethodFilter($paymentMethod)
+ {
+ return $paymentMethod->isDefault();
+ }
+
+ /* private class properties */
+
+ /**
+ * @access protected
+ * @var array registry of customer data
+ */
+ protected $_attributes = [
+ 'addresses' => '',
+ 'company' => '',
+ 'creditCards' => '',
+ 'email' => '',
+ 'fax' => '',
+ 'firstName' => '',
+ 'id' => '',
+ 'lastName' => '',
+ 'phone' => '',
+ 'createdAt' => '',
+ 'updatedAt' => '',
+ 'website' => '',
+ ];
+
+ /**
+ * sends the create request to the gateway
+ *
+ * @ignore
+ * @param string $subPath
+ * @param array $params
+ * @return mixed
+ */
+ public function _doCreate($subPath, $params)
+ {
+ $fullPath = $this->_config->merchantPath() . $subPath;
+ $response = $this->_http->post($fullPath, $params);
+
+ return $this->_verifyGatewayResponse($response);
+ }
+
+ /**
+ * verifies that a valid customer id is being used
+ * @ignore
+ * @param string customer id
+ * @throws InvalidArgumentException
+ */
+ private function _validateId($id = null) {
+ if (is_null($id)) {
+ throw new InvalidArgumentException(
+ 'expected customer id to be set'
+ );
+ }
+ if (!preg_match('/^[0-9A-Za-z_-]+$/', $id)) {
+ throw new InvalidArgumentException(
+ $id . ' is an invalid customer id.'
+ );
+ }
+ }
+
+
+ /* private class methods */
+
+ /**
+ * sends the update request to the gateway
+ *
+ * @ignore
+ * @param string $subPath
+ * @param array $params
+ * @return mixed
+ */
+ private function _doUpdate($httpVerb, $subPath, $params)
+ {
+ $fullPath = $this->_config->merchantPath() . $subPath;
+ $response = $this->_http->$httpVerb($fullPath, $params);
+
+ return $this->_verifyGatewayResponse($response);
+ }
+
+ /**
+ * generic method for validating incoming gateway responses
+ *
+ * creates a new Customer object and encapsulates
+ * it inside a Result\Successful object, or
+ * encapsulates a Errors object inside a Result\Error
+ * alternatively, throws an Unexpected exception if the response is invalid.
+ *
+ * @ignore
+ * @param array $response gateway response values
+ * @return Result\Successful|Result\Error
+ * @throws Exception\Unexpected
+ */
+ private function _verifyGatewayResponse($response)
+ {
+ if (isset($response['customer'])) {
+ // return a populated instance of Customer
+ return new Result\Successful(
+ Customer::factory($response['customer'])
+ );
+ } else if (isset($response['apiErrorResponse'])) {
+ return new Result\Error($response['apiErrorResponse']);
+ } else {
+ throw new Exception\Unexpected(
+ "Expected customer or apiErrorResponse"
+ );
+ }
+ }
+}
+class_alias('Braintree\CustomerGateway', 'Braintree_CustomerGateway');
diff --git a/lib/Braintree/CustomerSearch.php b/lib/Braintree/CustomerSearch.php
index 5de6cee..3bc734e 100644
--- a/lib/Braintree/CustomerSearch.php
+++ b/lib/Braintree/CustomerSearch.php
@@ -1,30 +1,34 @@
_set('merchantAccount',
- Braintree_MerchantAccount::factory($disbursementAttribs['merchantAccount'])
+ MerchantAccount::factory($disbursementAttribs['merchantAccount'])
);
}
}
public function transactions()
{
- $collection = Braintree_Transaction::search(array(
- Braintree_TransactionSearch::ids()->in($this->transactionIds)
- ));
+ $collection = Transaction::search([
+ TransactionSearch::ids()->in($this->transactionIds),
+ ]);
return $collection;
}
@@ -33,17 +39,28 @@ public static function factory($attributes)
public function __toString()
{
- $display = array(
+ $display = [
'id', 'merchantAccountDetails', 'exceptionMessage', 'amount',
'disbursementDate', 'followUpAction', 'retry', 'success',
- 'transactionIds'
- );
+ 'transactionIds', 'disbursementType'
+ ];
- $displayAttributes = array();
+ $displayAttributes = [];
foreach ($display AS $attrib) {
$displayAttributes[$attrib] = $this->$attrib;
}
return __CLASS__ . '[' .
- Braintree_Util::attributesToString($displayAttributes) .']';
+ Util::attributesToString($displayAttributes) .']';
+ }
+
+ public function isDebit()
+ {
+ return $this->disbursementType == Disbursement::TYPE_DEBIT;
+ }
+
+ public function isCredit()
+ {
+ return $this->disbursementType == Disbursement::TYPE_CREDIT;
}
}
+class_alias('Braintree\Disbursement', 'Braintree_Disbursement');
diff --git a/lib/Braintree/DisbursementDetails.php b/lib/Braintree/DisbursementDetails.php
index 471a163..6c5e182 100644
--- a/lib/Braintree/DisbursementDetails.php
+++ b/lib/Braintree/DisbursementDetails.php
@@ -1,31 +1,24 @@
disbursementDate);
}
}
+class_alias('Braintree\DisbursementDetails', 'Braintree_DisbursementDetails');
diff --git a/lib/Braintree/Discount.php b/lib/Braintree/Discount.php
index 0c8e12e..efa3d72 100644
--- a/lib/Braintree/Discount.php
+++ b/lib/Braintree/Discount.php
@@ -1,22 +1,35 @@
$response['discounts']);
-
- return Braintree_Util::extractAttributeAsArray(
- $discounts,
- 'discount'
- );
- }
+namespace Braintree;
+/**
+ * @property-read string $amount
+ * @property-read \DateTime $createdAt
+ * @property-read int|null $currentBillingCycle
+ * @property-read string $description
+ * @property-read string $id
+ * @property-read string|null $kind
+ * @property-read string $merchantId
+ * @property-read string $name
+ * @property-read boolean $neverExpires
+ * @property-read int|null $numberOfBillingCycles
+ * @property-read int|null $quantity
+ * @property-read \DateTime $updatedAt
+ */
+class Discount extends Modification
+{
public static function factory($attributes)
{
$instance = new self();
$instance->_initialize($attributes);
return $instance;
}
+
+
+ // static methods redirecting to gateway
+
+ public static function all()
+ {
+ return Configuration::gateway()->discount()->all();
+ }
}
+class_alias('Braintree\Discount', 'Braintree_Discount');
diff --git a/lib/Braintree/DiscountGateway.php b/lib/Braintree/DiscountGateway.php
new file mode 100644
index 0000000..d26672a
--- /dev/null
+++ b/lib/Braintree/DiscountGateway.php
@@ -0,0 +1,31 @@
+_gateway = $gateway;
+ $this->_config = $gateway->config;
+ $this->_config->assertHasAccessTokenOrKeys();
+ $this->_http = new Http($gateway->config);
+ }
+
+ public function all()
+ {
+ $path = $this->_config->merchantPath() . '/discounts';
+ $response = $this->_http->get($path);
+
+ $discounts = ["discount" => $response['discounts']];
+
+ return Util::extractAttributeAsArray(
+ $discounts,
+ 'discount'
+ );
+ }
+}
+class_alias('Braintree\DiscountGateway', 'Braintree_DiscountGateway');
diff --git a/lib/Braintree/Dispute.php b/lib/Braintree/Dispute.php
new file mode 100644
index 0000000..b2baa9e
--- /dev/null
+++ b/lib/Braintree/Dispute.php
@@ -0,0 +1,191 @@
+_attributes = $disputeAttribs;
+
+ if (isset($disputeAttribs['transaction'])) {
+ $transactionDetails = new Dispute\TransactionDetails($disputeAttribs['transaction']);
+ $this->_set('transactionDetails', $transactionDetails);
+ $this->_set('transaction', $transactionDetails);
+ }
+
+ if (isset($disputeAttribs['evidence'])) {
+ $evidenceArray = array_map(function($evidence) {
+ return new Dispute\EvidenceDetails($evidence);
+ }, $disputeAttribs['evidence']);
+ $this->_set('evidence', $evidenceArray);
+ }
+
+ if (isset($disputeAttribs['statusHistory'])) {
+ $statusHistoryArray = array_map(function($statusHistory) {
+ return new Dispute\StatusHistoryDetails($statusHistory);
+ }, $disputeAttribs['statusHistory']);
+ $this->_set('statusHistory', $statusHistoryArray);
+ }
+
+ if (isset($disputeAttribs['transaction'])) {
+ $this->_set('transaction',
+ new Dispute\TransactionDetails($disputeAttribs['transaction'])
+ );
+ }
+ }
+
+ public static function factory($attributes)
+ {
+ $instance = new self();
+ $instance->_initialize($attributes);
+ return $instance;
+ }
+
+ public function __toString()
+ {
+ $display = [
+ 'amount', 'reason', 'status',
+ 'replyByDate', 'receivedDate', 'currencyIsoCode'
+ ];
+
+ $displayAttributes = [];
+ foreach ($display AS $attrib) {
+ $displayAttributes[$attrib] = $this->$attrib;
+ }
+ return __CLASS__ . '[' .
+ Util::attributesToString($displayAttributes) .']';
+ }
+
+ /**
+ * Accepts a dispute, given a dispute ID
+ *
+ * @param string $id
+ */
+ public static function accept($id)
+ {
+ return Configuration::gateway()->dispute()->accept($id);
+ }
+
+ /**
+ * Adds file evidence to a dispute, given a dispute ID and a document ID
+ *
+ * @param string $disputeId
+ * @param string $documentIdOrRequest
+ */
+ public static function addFileEvidence($disputeId, $documentIdOrRequest)
+ {
+ return Configuration::gateway()->dispute()->addFileEvidence($disputeId, $documentIdOrRequest);
+ }
+
+ /**
+ * Adds text evidence to a dispute, given a dispute ID and content
+ *
+ * @param string $id
+ * @param string $contentOrRequest
+ */
+ public static function addTextEvidence($id, $contentOrRequest)
+ {
+ return Configuration::gateway()->dispute()->addTextEvidence($id, $contentOrRequest);
+ }
+
+ /**
+ * Finalize a dispute, given a dispute ID
+ *
+ * @param string $id
+ */
+ public static function finalize($id)
+ {
+ return Configuration::gateway()->dispute()->finalize($id);
+ }
+
+ /**
+ * Find a dispute, given a dispute ID
+ *
+ * @param string $id
+ */
+ public static function find($id)
+ {
+ return Configuration::gateway()->dispute()->find($id);
+ }
+
+ /**
+ * Remove evidence from a dispute, given a dispute ID and evidence ID
+ *
+ * @param string $disputeId
+ * @param string $evidenceId
+ */
+ public static function removeEvidence($disputeId, $evidenceId)
+ {
+ return Configuration::gateway()->dispute()->removeEvidence($disputeId, $evidenceId);
+ }
+
+ /**
+ * Search for Disputes, given a DisputeSearch query
+ *
+ * @param DisputeSearch $query
+ */
+ public static function search($query)
+ {
+ return Configuration::gateway()->dispute()->search($query);
+ }
+}
+class_alias('Braintree\Dispute', 'Braintree_Dispute');
diff --git a/lib/Braintree/Dispute/EvidenceDetails.php b/lib/Braintree/Dispute/EvidenceDetails.php
new file mode 100644
index 0000000..19d3c0a
--- /dev/null
+++ b/lib/Braintree/Dispute/EvidenceDetails.php
@@ -0,0 +1,31 @@
+_gateway = $gateway;
+ $this->_config = $gateway->config;
+ $this->_config->assertHasAccessTokenOrKeys();
+ $this->_http = new Http($gateway->config);
+ }
+
+ /* public class methods */
+
+ /**
+ * Accepts a dispute, given a dispute ID
+ *
+ * @param string $id
+ */
+ public function accept($id)
+ {
+ try {
+ if (trim($id) == "") {
+ throw new Exception\NotFound();
+ }
+
+ $path = $this->_config->merchantPath() . '/disputes/' . $id . '/accept';
+ $response = $this->_http->put($path);
+
+ if (isset($response['apiErrorResponse'])) {
+ return new Result\Error($response['apiErrorResponse']);
+ }
+
+ return new Result\Successful();
+ } catch (Exception\NotFound $e) {
+ throw new Exception\NotFound('dispute with id "' . $id . '" not found');
+ }
+ }
+
+ /**
+ * Adds file evidence to a dispute, given a dispute ID and a document ID
+ *
+ * @param string $disputeId
+ * @param string $documentIdOrRequest
+ */
+ public function addFileEvidence($disputeId, $documentIdOrRequest)
+ {
+ $request = is_array($documentIdOrRequest) ? $documentIdOrRequest : ['documentId' => $documentIdOrRequest];
+
+ if (trim($disputeId) == "") {
+ throw new Exception\NotFound('dispute with id "' . $disputeId . '" not found');
+ }
+
+ if (trim($request['documentId']) == "") {
+ throw new Exception\NotFound('document with id "' . $request['documentId'] . '" not found');
+ }
+
+ try {
+ if (array_key_exists('category', $request)) {
+ if (trim($request['category']) == "") {
+ throw new InvalidArgumentException('category cannot be blank');
+ }
+ }
+
+ $request['document_upload_id'] = $request['documentId'];
+ unset($request['documentId']);
+
+ $path = $this->_config->merchantPath() . '/disputes/' . $disputeId . '/evidence';
+ $response = $this->_http->post($path, ['evidence' => $request]);
+
+ if (isset($response['apiErrorResponse'])) {
+ return new Result\Error($response['apiErrorResponse']);
+ }
+
+ if (isset($response['evidence'])) {
+ $evidence = new Dispute\EvidenceDetails($response['evidence']);
+ return new Result\Successful($evidence);
+ }
+ } catch (Exception\NotFound $e) {
+ throw new Exception\NotFound('dispute with id "' . $disputeId . '" not found');
+ }
+ }
+
+ /**
+ * Adds text evidence to a dispute, given a dispute ID and content
+ *
+ * @param string $id
+ * @param string $content
+ */
+ public function addTextEvidence($id, $contentOrRequest)
+ {
+ $request = is_array($contentOrRequest) ? $contentOrRequest : ['content' => $contentOrRequest];
+ if (trim($request['content']) == "") {
+ throw new InvalidArgumentException('content cannot be blank');
+ }
+
+ try {
+ $evidence = [
+ 'comments' => $request['content'],
+ ];
+
+ if (trim($id) == "") {
+ throw new Exception\NotFound();
+ }
+
+ if (array_key_exists('tag', $request)) {
+ $evidence['category'] = $request['tag'];
+ }
+
+ if (array_key_exists('category', $request)) {
+ if (trim($request['category']) == "") {
+ throw new InvalidArgumentException('category cannot be blank');
+ }
+ $evidence['category'] = $request['category'];
+ }
+
+ if (array_key_exists('sequenceNumber', $request)) {
+ if (trim($request['sequenceNumber']) == "") {
+ throw new InvalidArgumentException('sequenceNumber cannot be blank');
+ } else if ((string)(int)($request['sequenceNumber']) != $request['sequenceNumber']) {
+ throw new InvalidArgumentException('sequenceNumber must be an integer');
+ }
+ $evidence['sequenceNumber'] = (int)$request['sequenceNumber'];
+ }
+
+ $path = $this->_config->merchantPath() . '/disputes/' . $id . '/evidence';
+ $response = $this->_http->post($path, [
+ 'evidence' => $evidence
+ ]);
+
+ if (isset($response['apiErrorResponse'])) {
+ return new Result\Error($response['apiErrorResponse']);
+ }
+
+ if (isset($response['evidence'])) {
+ $evidence = new Dispute\EvidenceDetails($response['evidence']);
+ return new Result\Successful($evidence);
+ }
+ } catch (Exception\NotFound $e) {
+ throw new Exception\NotFound('dispute with id "' . $id . '" not found');
+ }
+ }
+
+ /**
+ * Finalize a dispute, given a dispute ID
+ *
+ * @param string $id
+ */
+ public function finalize($id)
+ {
+ try {
+ if (trim($id) == "") {
+ throw new Exception\NotFound();
+ }
+
+ $path = $this->_config->merchantPath() . '/disputes/' . $id . '/finalize';
+ $response = $this->_http->put($path);
+
+ if (isset($response['apiErrorResponse'])) {
+ return new Result\Error($response['apiErrorResponse']);
+ }
+
+ return new Result\Successful();
+ } catch (Exception\NotFound $e) {
+ throw new Exception\NotFound('dispute with id "' . $id . '" not found');
+ }
+ }
+
+ /**
+ * Find a dispute, given a dispute ID
+ *
+ * @param string $id
+ */
+ public function find($id)
+ {
+ if (trim($id) == "") {
+ throw new Exception\NotFound('dispute with id "' . $id . '" not found');
+ }
+
+ try {
+ $path = $this->_config->merchantPath() . '/disputes/' . $id;
+ $response = $this->_http->get($path);
+ return Dispute::factory($response['dispute']);
+ } catch (Exception\NotFound $e) {
+ throw new Exception\NotFound('dispute with id "' . $id . '" not found');
+ }
+ }
+
+ /**
+ * Remove evidence from a dispute, given a dispute ID and evidence ID
+ *
+ * @param string $disputeId
+ * @param string $evidenceId
+ */
+ public function removeEvidence($disputeId, $evidenceId)
+ {
+ try {
+ if (trim($disputeId) == "" || trim($evidenceId) == "") {
+ throw new Exception\NotFound();
+ }
+
+ $path = $this->_config->merchantPath() . '/disputes/' . $disputeId . '/evidence/' . $evidenceId;
+ $response = $this->_http->delete($path);
+
+ if (isset($response['apiErrorResponse'])) {
+ return new Result\Error($response['apiErrorResponse']);
+ }
+
+ return new Result\Successful();
+ } catch (Exception\NotFound $e) {
+ throw new Exception\NotFound('evidence with id "' . $evidenceId . '" for dispute with id "' . $disputeId . '" not found');
+ }
+ }
+
+ /**
+ * Search for Disputes, given a DisputeSearch query
+ *
+ * @param DisputeSearch $query
+ */
+ public function search($query)
+ {
+ $criteria = [];
+ foreach ($query as $term) {
+ $criteria[$term->name] = $term->toparam();
+ }
+ $pager = [
+ 'object' => $this,
+ 'method' => 'fetchDisputes',
+ 'query' => $criteria
+ ];
+ return new PaginatedCollection($pager);
+ }
+
+ public function fetchDisputes($query, $page)
+ {
+ $response = $this->_http->post($this->_config->merchantPath() . '/disputes/advanced_search?page=' . $page, [
+ 'search' => $query
+ ]);
+ $body = $response['disputes'];
+ $disputes = Util::extractattributeasarray($body, 'dispute');
+ $totalItems = $body['totalItems'][0];
+ $pageSize = $body['pageSize'][0];
+ return new PaginatedResult($totalItems, $pageSize, $disputes);
+ }
+}
+class_alias('Braintree\DisputeGateway', 'Braintree_DisputeGateway');
diff --git a/lib/Braintree/DisputeSearch.php b/lib/Braintree/DisputeSearch.php
new file mode 100644
index 0000000..b97c463
--- /dev/null
+++ b/lib/Braintree/DisputeSearch.php
@@ -0,0 +1,90 @@
+ Braintree\DocumentUpload::EVIDENCE_DOCUMENT,
+ * "file" => $pngFile
+ * ]);
+ *
+ * For more information on DocumentUploads, see https://developers.braintreepayments.com/reference/request/document_upload/create
+ *
+ * @property-read string $contentType
+ * @property-read \DateTime $expiresAt
+ * @property-read string $id
+ * @property-read string $kind
+ * @property-read string $name
+ * @property-read int $size
+ */
+class DocumentUpload extends Base
+{
+ /* DocumentUpload Kind */
+ const EVIDENCE_DOCUMENT = "evidence_document";
+
+ protected function _initialize($documentUploadAttribs)
+ {
+ $this->_attributes = $documentUploadAttribs;
+ }
+
+ /**
+ * Creates a DocumentUpload object
+ * @param kind The kind of document
+ * @param file The open file to upload
+ * @throws InvalidArgumentException if the params are not expected
+ */
+ public static function create($params)
+ {
+ return Configuration::gateway()->documentUpload()->create($params);
+ }
+
+ public static function factory($attributes)
+ {
+ $instance = new self();
+ $instance->_initialize($attributes);
+ return $instance;
+ }
+}
+class_alias('Braintree\DocumentUpload', 'Braintree_DocumentUpload');
diff --git a/lib/Braintree/DocumentUploadGateway.php b/lib/Braintree/DocumentUploadGateway.php
new file mode 100644
index 0000000..2839968
--- /dev/null
+++ b/lib/Braintree/DocumentUploadGateway.php
@@ -0,0 +1,81 @@
+_gateway = $gateway;
+ $this->_config = $gateway->config;
+ $this->_config->assertHasAccessTokenOrKeys();
+ $this->_http = new Http($gateway->config);
+ }
+
+ /* public class methods */
+
+ /**
+ * Accepts a dispute, given a dispute ID
+ *
+ * @param string $id
+ */
+ public function create($params)
+ {
+ Util::verifyKeys(self::createSignature(), $params);
+
+ $file = $params['file'];
+
+ if (!is_resource($file)) {
+ throw new InvalidArgumentException('file must be a stream resource');
+ }
+
+ $payload = [
+ 'document_upload[kind]' => $params['kind']
+ ];
+ $path = $this->_config->merchantPath() . '/document_uploads/';
+ $response = $this->_http->postMultipart($path, $payload, $file);
+
+ if (isset($response['apiErrorResponse'])) {
+ return new Result\Error($response['apiErrorResponse']);
+ }
+
+ if (isset($response['documentUpload'])) {
+ $documentUpload = DocumentUpload::factory($response['documentUpload']);
+ return new Result\Successful($documentUpload);
+ }
+ }
+
+ public static function createSignature()
+ {
+ return [
+ 'file', 'kind'
+ ];
+ }
+}
+class_alias('Braintree\DocumentUploadGateway', 'Braintree_DocumentUploadGateway');
diff --git a/lib/Braintree/EndsWithNode.php b/lib/Braintree/EndsWithNode.php
new file mode 100644
index 0000000..edccec2
--- /dev/null
+++ b/lib/Braintree/EndsWithNode.php
@@ -0,0 +1,23 @@
+name = $name;
+ $this->searchTerms = [];
+ }
+
+ public function endsWith($value)
+ {
+ $this->searchTerms["ends_with"] = strval($value);
+ return $this;
+ }
+
+ public function toParam()
+ {
+ return $this->searchTerms;
+ }
+}
+class_alias('Braintree\EndsWithNode', 'Braintree_EndsWithNode');
diff --git a/lib/Braintree/EqualityNode.php b/lib/Braintree/EqualityNode.php
index 68c10b5..d91e1eb 100644
--- a/lib/Braintree/EqualityNode.php
+++ b/lib/Braintree/EqualityNode.php
@@ -1,6 +1,7 @@
_errors =
- new Braintree_Error_ValidationErrorCollection($errorData);
+ new ValidationErrorCollection($errorData);
}
+ /**
+ * Return count of items in collection
+ * Implements countable
+ *
+ * @return integer
+ */
+ public function count()
+ {
+ return $this->deepSize();
+ }
/**
* Returns all of the validation errors at all levels of nesting in a single, flat array.
@@ -77,10 +81,10 @@ public function onHtmlField($field)
$pieces = preg_split("/[\[\]]+/", $field, 0, PREG_SPLIT_NO_EMPTY);
$errors = $this;
foreach(array_slice($pieces, 0, -1) as $key) {
- $errors = $errors->forKey(Braintree_Util::delimiterToCamelCase($key));
- if (!isset($errors)) { return array(); }
+ $errors = $errors->forKey(Util::delimiterToCamelCase($key));
+ if (!isset($errors)) { return []; }
}
- $finalKey = Braintree_Util::delimiterToCamelCase(end($pieces));
+ $finalKey = Util::delimiterToCamelCase(end($pieces));
return $errors->onAttribute($finalKey);
}
@@ -88,7 +92,7 @@ public function onHtmlField($field)
* Returns the errors at the given nesting level (see forKey) in a single, flat array:
*
*
- * $result = Braintree_Customer::create(...);
+ * $result = Customer::create(...);
* $customerErrors = $result->errors->forKey('customer')->shallowAll();
*
*/
@@ -116,3 +120,4 @@ public function __toString()
return sprintf('%s', $this->_errors);
}
}
+class_alias('Braintree\Error\ErrorCollection', 'Braintree_Error_ErrorCollection');
diff --git a/lib/Braintree/Error/Validation.php b/lib/Braintree/Error/Validation.php
index 8253ae6..79a4cc1 100644
--- a/lib/Braintree/Error/Validation.php
+++ b/lib/Braintree/Error/Validation.php
@@ -1,11 +1,7 @@
== More information ==
*
- * For more detailed information on Validation errors, see {@link http://www.braintreepayments.com/gateway/validation-errors http://www.braintreepaymentsolutions.com/gateway/validation-errors}
+ * For more detailed information on Validation errors, see {@link https://developers.braintreepayments.com/reference/general/validation-errors/overview/php https://developers.braintreepayments.com/reference/general/validation-errors/overview/php}
*
* @package Braintree
* @subpackage Error
- * @copyright 2010 Braintree Payment Solutions
*
* @property-read string $attribute
* @property-read string $code
* @property-read string $message
*/
-class Braintree_Error_Validation
+class Validation
{
- private $_attribute;
- private $_code;
- private $_message;
+ private $_attribute;
+ private $_code;
+ private $_message;
/**
* @ignore
@@ -42,13 +37,13 @@ public function __construct($attributes)
* @ignore
* @access protected
* @param array $attributes array of properties to set - single level
- * @return none
+ * @return void
*/
private function _initializeFromArray($attributes)
{
foreach($attributes AS $name => $value) {
$varName = "_$name";
- $this->$varName = Braintree_Util::delimiterToCamelCase($value, '_');
+ $this->$varName = Util::delimiterToCamelCase($value, '_');
}
}
@@ -62,3 +57,4 @@ public function __get($name)
return isset($this->$varName) ? $this->$varName : null;
}
}
+class_alias('Braintree\Error\Validation', 'Braintree_Error_Validation');
diff --git a/lib/Braintree/Error/ValidationErrorCollection.php b/lib/Braintree/Error/ValidationErrorCollection.php
index 5125c90..ff3683e 100644
--- a/lib/Braintree/Error/ValidationErrorCollection.php
+++ b/lib/Braintree/Error/ValidationErrorCollection.php
@@ -1,30 +1,25 @@
== More information ==
*
- * For more detailed information on Validation errors, see {@link http://www.braintreepayments.com/gateway/validation-errors http://www.braintreepaymentsolutions.com/gateway/validation-errors}
+ * For more detailed information on Validation errors, see {@link https://developers.braintreepayments.com/reference/general/validation-errors/overview/php https://developers.braintreepayments.com/reference/general/validation-errors/overview/php}
*
* @package Braintree
* @subpackage Error
- * @copyright 2010 Braintree Payment Solutions
*
* @property-read array $errors
* @property-read array $nested
*/
-class Braintree_Error_ValidationErrorCollection extends Braintree_Collection
+class ValidationErrorCollection extends Collection
{
- private $_errors = array();
- private $_nested = array();
+ private $_errors = [];
+ private $_nested = [];
/**
* @ignore
@@ -35,17 +30,17 @@ public function __construct($data)
// map errors to new collections recursively
if ($key == 'errors') {
foreach ($errorData AS $error) {
- $this->_errors[] = new Braintree_Error_Validation($error);
+ $this->_errors[] = new Validation($error);
}
} else {
- $this->_nested[$key] = new Braintree_Error_ValidationErrorCollection($errorData);
+ $this->_nested[$key] = new ValidationErrorCollection($errorData);
}
}
public function deepAll()
{
- $validationErrors = array_merge(array(), $this->_errors);
+ $validationErrors = array_merge([], $this->_errors);
foreach($this->_nested as $nestedErrors)
{
$validationErrors = array_merge($validationErrors, $nestedErrors->deepAll());
@@ -75,7 +70,7 @@ public function forKey($key)
public function onAttribute($attribute)
{
- $matches = array();
+ $matches = [];
foreach ($this->_errors AS $key => $error) {
if($error->attribute == $attribute) {
$matches[] = $error;
@@ -105,7 +100,7 @@ public function __get($name)
*/
public function __toString()
{
- $output = array();
+ $output = [];
// TODO: implement scope
if (!empty($this->_errors)) {
@@ -133,3 +128,4 @@ private function _inspect($errors, $scope = null)
return $eOutput;
}
}
+class_alias('Braintree\Error\ValidationErrorCollection', 'Braintree_Error_ValidationErrorCollection');
diff --git a/lib/Braintree/EuropeBankAccount.php b/lib/Braintree/EuropeBankAccount.php
new file mode 100644
index 0000000..b661e70
--- /dev/null
+++ b/lib/Braintree/EuropeBankAccount.php
@@ -0,0 +1,67 @@
+== More information ==
+ *
+ * See {@link https://developers.braintreepayments.com/javascript+php}
+ *
+ * @package Braintree
+ * @category Resources
+ *
+ * @property-read string $account-holder-name
+ * @property-read string $bic
+ * @property-read string $customerId
+ * @property-read string $default
+ * @property-read string $image-url
+ * @property-read string $mandate-reference-number
+ * @property-read string $masked-iban
+ * @property-read string $token
+ */
+class EuropeBankAccount extends Base
+{
+
+ /* instance methods */
+ /**
+ * returns false if default is null or false
+ *
+ * @return boolean
+ */
+ public function isDefault()
+ {
+ return $this->default;
+ }
+
+ /**
+ * factory method: returns an instance of EuropeBankAccount
+ * to the requesting method, with populated properties
+ *
+ * @ignore
+ * @return EuropeBankAccount
+ */
+ public static function factory($attributes)
+ {
+ $defaultAttributes = [
+ ];
+
+ $instance = new self();
+ $instance->_initialize(array_merge($defaultAttributes, $attributes));
+ return $instance;
+ }
+
+ /**
+ * sets instance properties from an array of values
+ *
+ * @access protected
+ * @param array $europeBankAccountAttribs array of EuropeBankAccount properties
+ * @return void
+ */
+ protected function _initialize($europeBankAccountAttribs)
+ {
+ $this->_attributes = $europeBankAccountAttribs;
+ }
+}
+class_alias('Braintree\EuropeBankAccount', 'Braintree_EuropeBankAccount');
diff --git a/lib/Braintree/Exception.php b/lib/Braintree/Exception.php
index 40ec119..58d0805 100644
--- a/lib/Braintree/Exception.php
+++ b/lib/Braintree/Exception.php
@@ -1,20 +1,13 @@
_initialize($attributes);
+ return $instance;
+ }
+
+ protected function _initialize($attributes)
+ {
+ $this->_attributes = $attributes;
+ }
+
+ /**
+ * returns a string representation of the facilitated details
+ * @return string
+ */
+ public function __toString()
+ {
+ return __CLASS__ . '[' .
+ Util::attributesToString($this->_attributes) .']';
+ }
+
+}
+class_alias('Braintree\FacilitatedDetails', 'Braintree_FacilitatedDetails');
diff --git a/lib/Braintree/FacilitatorDetails.php b/lib/Braintree/FacilitatorDetails.php
new file mode 100644
index 0000000..d84ce21
--- /dev/null
+++ b/lib/Braintree/FacilitatorDetails.php
@@ -0,0 +1,35 @@
+_initialize($attributes);
+
+ return $instance;
+ }
+
+ protected function _initialize($attributes)
+ {
+ $this->_attributes = $attributes;
+ }
+
+ /**
+ * returns a string representation of the facilitator details
+ * @return string
+ */
+ public function __toString()
+ {
+ return __CLASS__ . '[' .
+ Util::attributesToString($this->_attributes) .']';
+ }
+
+}
+class_alias('Braintree\FacilitatorDetails', 'Braintree_FacilitatorDetails');
diff --git a/lib/Braintree/Gateway.php b/lib/Braintree/Gateway.php
new file mode 100644
index 0000000..701a599
--- /dev/null
+++ b/lib/Braintree/Gateway.php
@@ -0,0 +1,278 @@
+config = $config;
+ }
+
+ /**
+ *
+ * @return AddOnGateway
+ */
+ public function addOn()
+ {
+ return new AddOnGateway($this);
+ }
+
+ /**
+ *
+ * @return AddressGateway
+ */
+ public function address()
+ {
+ return new AddressGateway($this);
+ }
+
+ /**
+ *
+ * @return ApplePayGateway
+ */
+ public function applePay()
+ {
+ return new ApplePayGateway($this);
+ }
+
+ /**
+ *
+ * @return ClientTokenGateway
+ */
+ public function clientToken()
+ {
+ return new ClientTokenGateway($this);
+ }
+
+ /**
+ *
+ * @return CreditCardGateway
+ */
+ public function creditCard()
+ {
+ return new CreditCardGateway($this);
+ }
+
+ /**
+ *
+ * @return CreditCardVerificationGateway
+ */
+ public function creditCardVerification()
+ {
+ return new CreditCardVerificationGateway($this);
+ }
+
+ /**
+ *
+ * @return CustomerGateway
+ */
+ public function customer()
+ {
+ return new CustomerGateway($this);
+ }
+
+ /**
+ *
+ * @return DiscountGateway
+ */
+ public function discount()
+ {
+ return new DiscountGateway($this);
+ }
+
+ /**
+ *
+ * @return DisputeGateway
+ */
+ public function dispute()
+ {
+ return new DisputeGateway($this);
+ }
+
+ /**
+ *
+ * @return DocumentUploadGateway
+ */
+ public function documentUpload()
+ {
+ return new DocumentUploadGateway($this);
+ }
+
+ /**
+ *
+ * @return MerchantGateway
+ */
+ public function merchant()
+ {
+ return new MerchantGateway($this);
+ }
+
+ /**
+ *
+ * @return MerchantAccountGateway
+ */
+ public function merchantAccount()
+ {
+ return new MerchantAccountGateway($this);
+ }
+
+ /**
+ *
+ * @return OAuthGateway
+ */
+ public function oauth()
+ {
+ return new OAuthGateway($this);
+ }
+
+ /**
+ *
+ * @return PaymentMethodGateway
+ */
+ public function paymentMethod()
+ {
+ return new PaymentMethodGateway($this);
+ }
+
+ /**
+ *
+ * @return PaymentMethodNonceGateway
+ */
+ public function paymentMethodNonce()
+ {
+ return new PaymentMethodNonceGateway($this);
+ }
+
+ /**
+ *
+ * @return PayPalAccountGateway
+ */
+ public function payPalAccount()
+ {
+ return new PayPalAccountGateway($this);
+ }
+
+ /**
+ *
+ * @return PlanGateway
+ */
+ public function plan()
+ {
+ return new PlanGateway($this);
+ }
+
+ /**
+ *
+ * @return SettlementBatchSummaryGateway
+ */
+ public function settlementBatchSummary()
+ {
+ return new SettlementBatchSummaryGateway($this);
+ }
+
+ /**
+ *
+ * @return SubscriptionGateway
+ */
+ public function subscription()
+ {
+ return new SubscriptionGateway($this);
+ }
+
+ /**
+ *
+ * @return TestingGateway
+ */
+ public function testing()
+ {
+ return new TestingGateway($this);
+ }
+
+ /**
+ *
+ * @return TransactionGateway
+ */
+ public function transaction()
+ {
+ return new TransactionGateway($this);
+ }
+
+ /**
+ *
+ * @return TransactionLineItemGateway
+ */
+ public function transactionLineItem()
+ {
+ return new TransactionLineItemGateway($this);
+ }
+
+ /**
+ *
+ * @return TransparentRedirectGateway
+ */
+ public function transparentRedirect()
+ {
+ return new TransparentRedirectGateway($this);
+ }
+
+ /**
+ *
+ * @return UsBankAccountGateway
+ */
+ public function usBankAccount()
+ {
+ return new UsBankAccountGateway($this);
+ }
+
+ /**
+ *
+ * @return UsBankAccountVerificationGateway
+ */
+ public function usBankAccountVerification()
+ {
+ return new UsBankAccountVerificationGateway($this);
+ }
+
+ /**
+ *
+ * @return IdealPaymentGateway
+ */
+ public function idealPayment()
+ {
+ return new IdealPaymentGateway($this);
+ }
+
+ /**
+ *
+ * @return WebhookNotificationGateway
+ */
+ public function webhookNotification()
+ {
+ return new WebhookNotificationGateway($this);
+ }
+
+ /**
+ *
+ * @return WebhookTestingGateway
+ */
+ public function webhookTesting()
+ {
+ return new WebhookTestingGateway($this);
+ }
+}
+class_alias('Braintree\Gateway', 'Braintree_Gateway');
diff --git a/lib/Braintree/GrantedPaymentInstrumentUpdate.php b/lib/Braintree/GrantedPaymentInstrumentUpdate.php
new file mode 100644
index 0000000..551b0c8
--- /dev/null
+++ b/lib/Braintree/GrantedPaymentInstrumentUpdate.php
@@ -0,0 +1,73 @@
+== More information ==
+ *
+ *
+ * @package Braintree
+ * @category Resources
+ *
+ * @property-read string $grantOwnerMerchantId
+ * @property-read string $grantRecipientMerchantId
+ * @property-read string $paymentMethodNonce
+ * @property-read string $token
+ * @property-read string $updatedFields
+ */
+class GrantedPaymentInstrumentUpdate extends Base
+{
+ /**
+ * factory method: returns an instance of GrantedPaymentInstrumentUpdate
+ * to the requesting method, with populated properties
+ *
+ * @ignore
+ * @return GrantedPaymentInstrumentUpdate
+ */
+ public static function factory($attributes)
+ {
+ $instance = new self();
+ $instance->_initialize($attributes);
+ return $instance;
+ }
+
+ /* instance methods */
+
+ /**
+ * sets instance properties from an array of values
+ *
+ * @access protected
+ * @param array $GrantedPaymentInstrumentAttribs array of grantedPaymentInstrumentUpdate data
+ * @return void
+ */
+ protected function _initialize($grantedPaymentInstrumentUpdateAttribs)
+ {
+ // set the attributes
+ $this->_attributes = $grantedPaymentInstrumentUpdateAttribs;
+
+ $paymentMethodNonce = isset($grantedPaymentInstrumentUpdateAttribs['paymentMethodNonce']) ?
+ GrantedPaymentInstrumentUpdate::factory($grantedPaymentInstrumentUpdateAttribs['paymentMethodNonce']) :
+ null;
+ $this->_set('paymentMethodNonce', $paymentMethodNonce);
+ }
+
+ /**
+ * create a printable representation of the object as:
+ * ClassName[property=value, property=value]
+ * @return string
+ */
+ public function __toString()
+ {
+ return __CLASS__ . '[' .
+ Util::attributesToString($this->_attributes) . ']';
+ }
+}
+class_alias('Braintree\GrantedPaymentInstrumentUpdate', 'Braintree_GrantedPaymentInstrumentUpdate');
diff --git a/lib/Braintree/GraphQL.php b/lib/Braintree/GraphQL.php
new file mode 100644
index 0000000..488d6fe
--- /dev/null
+++ b/lib/Braintree/GraphQL.php
@@ -0,0 +1,44 @@
+ $definition];
+ if ($variables) {
+ $graphQLRequest["variables"] = $variables;
+ }
+
+ $response = $this->_doUrlRequest('POST', $this->_config->graphQLBaseUrl(), json_encode($graphQLRequest), null, $this->graphQLHeaders());
+
+ $result = json_decode($response["body"], true);
+ Util::throwGraphQLResponseException($result);
+
+ return $result;
+ }
+}
+
+class_alias('Braintree\GraphQL', 'Braintree_GraphQL');
diff --git a/lib/Braintree/Http.php b/lib/Braintree/Http.php
index fb05985..616be51 100644
--- a/lib/Braintree/Http.php
+++ b/lib/Braintree/Http.php
@@ -1,104 +1,273 @@
_config = $config;
+ }
+
+ public function delete($path, $params = null)
{
- $response = self::_doRequest('DELETE', $path);
- if($response['status'] === 200) {
+ $response = $this->_doRequest('DELETE', $path, $this->_buildXml($params));
+ $responseCode = $response['status'];
+ if ($responseCode === 200 || $responseCode === 204) {
return true;
+ } else if ($responseCode === 422) {
+ return Xml::buildArrayFromXml($response['body']);
+ } else {
+ Util::throwStatusCodeException($response['status']);
+ }
+ }
+
+ public function get($path)
+ {
+ $response = $this->_doRequest('GET', $path);
+ if ($response['status'] === 200) {
+ return Xml::buildArrayFromXml($response['body']);
} else {
- Braintree_Util::throwStatusCodeException($response['status']);
+ Util::throwStatusCodeException($response['status']);
}
}
- public static function get($path)
+ public function post($path, $params = null)
{
- $response = self::_doRequest('GET', $path);
- if($response['status'] === 200) {
- return Braintree_Xml::buildArrayFromXml($response['body']);
+ $response = $this->_doRequest('POST', $path, $this->_buildXml($params));
+ $responseCode = $response['status'];
+ if ($responseCode === 200 || $responseCode === 201 || $responseCode === 422 || $responseCode == 400) {
+ return Xml::buildArrayFromXml($response['body']);
} else {
- Braintree_Util::throwStatusCodeException($response['status']);
+ Util::throwStatusCodeException($responseCode);
}
}
- public static function post($path, $params = null)
+ public function postMultipart($path, $params, $file)
{
- $response = self::_doRequest('POST', $path, self::_buildXml($params));
+ $headers = [
+ 'User-Agent: Braintree PHP Library ' . Version::get(),
+ 'X-ApiVersion: ' . Configuration::API_VERSION
+ ];
+ $response = $this->_doRequest('POST', $path, $params, $file, $headers);
$responseCode = $response['status'];
- if($responseCode === 200 || $responseCode === 201 || $responseCode === 422) {
- return Braintree_Xml::buildArrayFromXml($response['body']);
+ if ($responseCode === 200 || $responseCode === 201 || $responseCode === 422 || $responseCode == 400) {
+ return Xml::buildArrayFromXml($response['body']);
} else {
- Braintree_Util::throwStatusCodeException($responseCode);
+ Util::throwStatusCodeException($responseCode);
}
}
- public static function put($path, $params = null)
+ public function put($path, $params = null)
{
- $response = self::_doRequest('PUT', $path, self::_buildXml($params));
+ $response = $this->_doRequest('PUT', $path, $this->_buildXml($params));
$responseCode = $response['status'];
- if($responseCode === 200 || $responseCode === 201 || $responseCode === 422) {
- return Braintree_Xml::buildArrayFromXml($response['body']);
+ if ($responseCode === 200 || $responseCode === 201 || $responseCode === 422 || $responseCode == 400) {
+ return Xml::buildArrayFromXml($response['body']);
} else {
- Braintree_Util::throwStatusCodeException($responseCode);
+ Util::throwStatusCodeException($responseCode);
}
}
- private static function _buildXml($params)
+ private function _buildXml($params)
{
- return empty($params) ? null : Braintree_Xml::buildXmlFromArray($params);
+ return empty($params) ? null : Xml::buildXmlFromArray($params);
}
- private static function _doRequest($httpVerb, $path, $requestBody = null)
+ private function _getHeaders()
{
- return self::_doUrlRequest($httpVerb, Braintree_Configuration::merchantUrl() . $path, $requestBody);
+ return [
+ 'Accept: application/xml',
+ ];
}
- public static function _doUrlRequest($httpVerb, $url, $requestBody = null)
+ private function _getAuthorization()
+ {
+ if ($this->_useClientCredentials) {
+ return [
+ 'user' => $this->_config->getClientId(),
+ 'password' => $this->_config->getClientSecret(),
+ ];
+ } else if ($this->_config->isAccessToken()) {
+ return [
+ 'token' => $this->_config->getAccessToken(),
+ ];
+ } else {
+ return [
+ 'user' => $this->_config->getPublicKey(),
+ 'password' => $this->_config->getPrivateKey(),
+ ];
+ }
+ }
+
+ public function useClientCredentials()
+ {
+ $this->_useClientCredentials = true;
+ }
+
+ private function _doRequest($httpVerb, $path, $requestBody = null, $file = null, $headers = null)
+ {
+ return $this->_doUrlRequest($httpVerb, $this->_config->baseUrl() . $path, $requestBody, $file, $headers);
+ }
+
+ public function _doUrlRequest($httpVerb, $url, $requestBody = null, $file = null, $customHeaders = null)
{
$curl = curl_init();
- curl_setopt($curl, CURLOPT_TIMEOUT, 60);
+ curl_setopt($curl, CURLOPT_TIMEOUT, $this->_config->timeout());
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $httpVerb);
curl_setopt($curl, CURLOPT_URL, $url);
- curl_setopt($curl, CURLOPT_ENCODING, 'gzip');
- curl_setopt($curl, CURLOPT_HTTPHEADER, array(
- 'Accept: application/xml',
- 'Content-Type: application/xml',
- 'User-Agent: Braintree PHP Library ' . Braintree_Version::get(),
- 'X-ApiVersion: ' . Braintree_Configuration::API_VERSION
- ));
- curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
- curl_setopt($curl, CURLOPT_USERPWD, Braintree_Configuration::publicKey() . ':' . Braintree_Configuration::privateKey());
- // curl_setopt($curl, CURLOPT_VERBOSE, true);
- if (Braintree_Configuration::sslOn()) {
+
+ if ($this->_config->acceptGzipEncoding()) {
+ curl_setopt($curl, CURLOPT_ENCODING, 'gzip');
+ }
+ if ($this->_config->sslVersion()) {
+ curl_setopt($curl, CURLOPT_SSLVERSION, $this->_config->sslVersion());
+ }
+
+ $headers = [];
+ if ($customHeaders) {
+ $headers = $customHeaders;
+ } else {
+ $headers = $this->_getHeaders($curl);
+ $headers[] = 'User-Agent: Braintree PHP Library ' . Version::get();
+ $headers[] = 'X-ApiVersion: ' . Configuration::API_VERSION;
+ $headers[] = 'Content-Type: application/xml';
+ }
+
+ $authorization = $this->_getAuthorization();
+ if (isset($authorization['user'])) {
+ curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
+ curl_setopt($curl, CURLOPT_USERPWD, $authorization['user'] . ':' . $authorization['password']);
+ } else if (isset($authorization['token'])) {
+ $headers[] = 'Authorization: Bearer ' . $authorization['token'];
+ }
+
+ if ($this->_config->sslOn()) {
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2);
- curl_setopt($curl, CURLOPT_CAINFO, Braintree_Configuration::caFile());
+ curl_setopt($curl, CURLOPT_CAINFO, $this->getCaFile());
}
- if(!empty($requestBody)) {
+ if (!empty($file)) {
+ $boundary = "---------------------" . md5(mt_rand() . microtime());
+ $headers[] = "Content-Type: multipart/form-data; boundary={$boundary}";
+ $this->prepareMultipart($curl, $requestBody, $file, $boundary);
+ } else if (!empty($requestBody)) {
curl_setopt($curl, CURLOPT_POSTFIELDS, $requestBody);
}
+ if ($this->_config->isUsingProxy()) {
+ $proxyHost = $this->_config->getProxyHost();
+ $proxyPort = $this->_config->getProxyPort();
+ $proxyType = $this->_config->getProxyType();
+ $proxyUser = $this->_config->getProxyUser();
+ $proxyPwd= $this->_config->getProxyPassword();
+ curl_setopt($curl, CURLOPT_PROXY, $proxyHost . ':' . $proxyPort);
+ if (!empty($proxyType)) {
+ curl_setopt($curl, CURLOPT_PROXYTYPE, $proxyType);
+ }
+ if ($this->_config->isAuthenticatedProxy()) {
+ curl_setopt($curl, CURLOPT_PROXYUSERPWD, $proxyUser . ':' . $proxyPwd);
+ }
+ }
+
+ curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($curl);
$httpStatus = curl_getinfo($curl, CURLINFO_HTTP_CODE);
+ $error_code = curl_errno($curl);
+ $error = curl_error($curl);
+
+ if ($error_code == 28 && $httpStatus == 0) {
+ throw new Exception\Timeout();
+ }
+
curl_close($curl);
- if (Braintree_Configuration::sslOn()) {
+ if ($this->_config->sslOn()) {
if ($httpStatus == 0) {
- throw new Braintree_Exception_SSLCertificate();
+ throw new Exception\SSLCertificate($error, $error_code);
+ }
+ } else if ($error_code) {
+ throw new Exception\Connection($error, $error_code);
+ }
+
+ return ['status' => $httpStatus, 'body' => $response];
+ }
+
+ function prepareMultipart($ch, $requestBody, $file, $boundary) {
+ $disallow = ["\0", "\"", "\r", "\n"];
+ $fileInfo = new finfo(FILEINFO_MIME_TYPE);
+ $filePath = stream_get_meta_data($file)['uri'];
+ $data = file_get_contents($filePath);
+ $mimeType = $fileInfo->buffer($data);
+
+ // build normal parameters
+ foreach ($requestBody as $k => $v) {
+ $k = str_replace($disallow, "_", $k);
+ $body[] = implode("\r\n", [
+ "Content-Disposition: form-data; name=\"{$k}\"",
+ "",
+ filter_var($v),
+ ]);
+ }
+
+ // build file parameter
+ $splitFilePath = explode(DIRECTORY_SEPARATOR, $filePath);
+ $filePath = end($splitFilePath);
+ $filePath = str_replace($disallow, "_", $filePath);
+ $body[] = implode("\r\n", [
+ "Content-Disposition: form-data; name=\"file\"; filename=\"{$filePath}\"",
+ "Content-Type: {$mimeType}",
+ "",
+ $data,
+ ]);
+
+ // add boundary for each parameters
+ array_walk($body, function (&$part) use ($boundary) {
+ $part = "--{$boundary}\r\n{$part}";
+ });
+
+ // add final boundary
+ $body[] = "--{$boundary}--";
+ $body[] = "";
+
+ // set options
+ return curl_setopt_array($ch, [
+ CURLOPT_POST => true,
+ CURLOPT_POSTFIELDS => implode("\r\n", $body)
+ ]);
+ }
+
+ private function getCaFile()
+ {
+ static $memo;
+
+ if ($memo === null) {
+ $caFile = $this->_config->caFile();
+
+ if (substr($caFile, 0, 7) !== 'phar://') {
+ return $caFile;
}
+
+ $extractedCaFile = sys_get_temp_dir() . '/api_braintreegateway_com.ca.crt';
+
+ if (!file_exists($extractedCaFile) || sha1_file($extractedCaFile) != sha1_file($caFile)) {
+ if (!copy($caFile, $extractedCaFile)) {
+ throw new Exception\SSLCaFileNotFound();
+ }
+ }
+ $memo = $extractedCaFile;
}
- return array('status' => $httpStatus, 'body' => $response);
+
+ return $memo;
}
}
+class_alias('Braintree\Http', 'Braintree_Http');
diff --git a/lib/Braintree/IbanBankAccount.php b/lib/Braintree/IbanBankAccount.php
new file mode 100644
index 0000000..8e53563
--- /dev/null
+++ b/lib/Braintree/IbanBankAccount.php
@@ -0,0 +1,57 @@
+_attributes) . ']';
+ }
+
+ /**
+ * sets instance properties from an array of values
+ *
+ * @ignore
+ * @access protected
+ * @param array $ibanAttribs array of ibanBankAccount data
+ * @return void
+ */
+ protected function _initialize($ibanAttribs)
+ {
+ // set the attributes
+ $this->_attributes = $ibanAttribs;
+ }
+
+ /**
+ * factory method: returns an instance of IbanBankAccount
+ * to the requesting method, with populated properties
+ * @ignore
+ * @return IbanBankAccount
+ */
+ public static function factory($attributes)
+ {
+ $instance = new self();
+ $instance->_initialize($attributes);
+ return $instance;
+ }
+}
+class_alias('Braintree\IbanBankAccount', 'Braintree_IbanBankAccount');
diff --git a/lib/Braintree/IdealPayment.php b/lib/Braintree/IdealPayment.php
new file mode 100644
index 0000000..2ada135
--- /dev/null
+++ b/lib/Braintree/IdealPayment.php
@@ -0,0 +1,92 @@
+== More information ==
+ *
+ *
+ * @package Braintree
+ * @category Resources
+ *
+ * @property-read string $id
+ * @property-read string $idealTransactionId
+ * @property-read string $currency
+ * @property-read string $amount
+ * @property-read string $status
+ * @property-read string $orderId
+ * @property-read string $issuer
+ * @property-read string $ibanBankAccount
+ */
+class IdealPayment extends Base
+{
+ /**
+ * factory method: returns an instance of IdealPayment
+ * to the requesting method, with populated properties
+ *
+ * @ignore
+ * @return IdealPayment
+ */
+ public static function factory($attributes)
+ {
+ $instance = new self();
+ $instance->_initialize($attributes);
+ return $instance;
+ }
+
+ /* instance methods */
+
+ /**
+ * sets instance properties from an array of values
+ *
+ * @access protected
+ * @param array $idealPaymentAttribs array of idealPayment data
+ * @return void
+ */
+ protected function _initialize($idealPaymentAttribs)
+ {
+ // set the attributes
+ $this->_attributes = $idealPaymentAttribs;
+
+ $ibanBankAccount = isset($idealPaymentAttribs['ibanBankAccount']) ?
+ IbanBankAccount::factory($idealPaymentAttribs['ibanBankAccount']) :
+ null;
+ $this->_set('ibanBankAccount', $ibanBankAccount);
+ }
+
+ /**
+ * create a printable representation of the object as:
+ * ClassName[property=value, property=value]
+ * @return string
+ */
+ public function __toString()
+ {
+ return __CLASS__ . '[' .
+ Util::attributesToString($this->_attributes) . ']';
+ }
+
+
+ // static methods redirecting to gateway
+
+ public static function find($idealPaymentId)
+ {
+ return Configuration::gateway()->idealPayment()->find($idealPaymentId);
+ }
+
+ public static function sale($idealPaymentId, $transactionAttribs)
+ {
+ $transactionAttribs['options'] = [
+ 'submitForSettlement' => true
+ ];
+ return Configuration::gateway()->idealPayment()->sale($idealPaymentId, $transactionAttribs);
+ }
+}
+class_alias('Braintree\IdealPayment', 'Braintree_IdealPayment');
diff --git a/lib/Braintree/IdealPaymentGateway.php b/lib/Braintree/IdealPaymentGateway.php
new file mode 100644
index 0000000..258bc46
--- /dev/null
+++ b/lib/Braintree/IdealPaymentGateway.php
@@ -0,0 +1,104 @@
+== More information ==
+ *
+ *
+ * @package Braintree
+ * @category Resources
+ */
+class IdealPaymentGateway
+{
+ private $_gateway;
+ private $_config;
+ private $_http;
+
+ public function __construct($gateway)
+ {
+ $this->_gateway = $gateway;
+ $this->_config = $gateway->config;
+ $this->_config->assertHasAccessTokenOrKeys();
+ $this->_http = new Http($gateway->config);
+ }
+
+ /**
+ * find an IdealPayment by id
+ *
+ * @access public
+ * @param string $idealPaymentId
+ * @return IdealPayment
+ * @throws Exception\NotFound
+ */
+ public function find($idealPaymentId)
+ {
+ try {
+ $path = $this->_config->merchantPath() . '/ideal_payments/' . $idealPaymentId;
+ $response = $this->_http->get($path);
+ return IdealPayment::factory($response['idealPayment']);
+ } catch (Exception\NotFound $e) {
+ throw new Exception\NotFound(
+ 'iDEAL Payment with id ' . $idealPaymentId . ' not found'
+ );
+ }
+ }
+
+ /**
+ * create a new sale for the current IdealPayment
+ *
+ * @param string $idealPaymentId
+ * @param array $transactionAttribs
+ * @return Result\Successful|Result\Error
+ * @see Transaction::sale()
+ */
+ public function sale($idealPaymentId, $transactionAttribs)
+ {
+ return Transaction::sale(
+ array_merge(
+ $transactionAttribs,
+ ['paymentMethodNonce' => $idealPaymentId]
+ )
+ );
+ }
+
+ /**
+ * generic method for validating incoming gateway responses
+ *
+ * creates a new IdealPayment object and encapsulates
+ * it inside a Result\Successful object, or
+ * encapsulates a Errors object inside a Result\Error
+ * alternatively, throws an Unexpected exception if the response is invalid.
+ *
+ * @ignore
+ * @param array $response gateway response values
+ * @return Result\Successful|Result\Error
+ * @throws Exception\Unexpected
+ */
+ private function _verifyGatewayResponse($response)
+ {
+ if (isset($response['idealPayment'])) {
+ // return a populated instance of IdealPayment
+ return new Result\Successful(
+ IdealPayment::factory($response['idealPayment'])
+ );
+ } else if (isset($response['apiErrorResponse'])) {
+ return new Result\Error($response['apiErrorResponse']);
+ } else {
+ throw new Exception\Unexpected(
+ 'Expected Ideal Payment or apiErrorResponse'
+ );
+ }
+ }
+}
+class_alias('Braintree\IdealPaymentGateway', 'Braintree_IdealPaymentGateway');
diff --git a/lib/Braintree/Instance.php b/lib/Braintree/Instance.php
index c048254..ba0d272 100644
--- a/lib/Braintree/Instance.php
+++ b/lib/Braintree/Instance.php
@@ -1,25 +1,18 @@
_attributes);
- return get_class($this) .'['.$objOutput.']';
+ $objOutput = Util::implodeAssociativeArray($this->_attributes);
+ return get_class($this) .'[' . $objOutput . ']';
}
/**
* initializes instance properties from the keys/values of an array
* @ignore
* @access protected
* @param $aAttribs array of properties to set - single level
- * @return none
+ * @return void
*/
private function _initializeFromArray($attributes)
{
@@ -79,3 +71,4 @@ private function _initializeFromArray($attributes)
}
}
+class_alias('Braintree\Instance', 'Braintree_Instance');
diff --git a/lib/Braintree/IsNode.php b/lib/Braintree/IsNode.php
index 321fbb3..6db01b0 100644
--- a/lib/Braintree/IsNode.php
+++ b/lib/Braintree/IsNode.php
@@ -1,21 +1,24 @@
name = $name;
- $this->searchTerms = array();
+ $this->searchTerms = [];
}
- function is($value)
+ public function is($value)
{
$this->searchTerms['is'] = strval($value);
+
return $this;
}
- function toParam()
+ public function toParam()
{
return $this->searchTerms;
}
}
+class_alias('Braintree\IsNode', 'Braintree_IsNode');
diff --git a/lib/Braintree/KeyValueNode.php b/lib/Braintree/KeyValueNode.php
index ec0cb94..1fd0139 100644
--- a/lib/Braintree/KeyValueNode.php
+++ b/lib/Braintree/KeyValueNode.php
@@ -1,22 +1,23 @@
name = $name;
$this->searchTerm = True;
-
}
- function is($value)
+ public function is($value)
{
$this->searchTerm = $value;
return $this;
}
- function toParam()
+ public function toParam()
{
return $this->searchTerm;
}
}
+class_alias('Braintree\KeyValueNode', 'Braintree_KeyValueNode');
diff --git a/lib/Braintree/LocalPaymentCompleted.php b/lib/Braintree/LocalPaymentCompleted.php
new file mode 100644
index 0000000..07b7360
--- /dev/null
+++ b/lib/Braintree/LocalPaymentCompleted.php
@@ -0,0 +1,68 @@
+== More information ==
+ *
+ *
+ * @package Braintree
+ * @category Resources
+ *
+ * @property-read string $grantOwnerMerchantId
+ * @property-read string $grantRecipientMerchantId
+ * @property-read string $paymentMethodNonce
+ * @property-read string $token
+ * @property-read string $updatedFields
+ */
+class LocalPaymentCompleted extends Base
+{
+ /**
+ * factory method: returns an instance of GrantedPaymentInstrumentUpdate
+ * to the requesting method, with populated properties
+ *
+ * @ignore
+ * @return LocalPaymentCompleted
+ */
+ public static function factory($attributes)
+ {
+ $instance = new self();
+ $instance->_initialize($attributes);
+ return $instance;
+ }
+
+ /* instance methods */
+
+ /**
+ * sets instance properties from an array of values
+ *
+ * @access protected
+ * @param array $LocalPaymentCompletedAttribs array of localPaymentCompleted data
+ * @return void
+ */
+ protected function _initialize($localPaymentCompletedAttribs)
+ {
+ // set the attributes
+ $this->_attributes = $localPaymentCompletedAttribs;
+ }
+
+ /**
+ * create a printable representation of the object as:
+ * ClassName[property=value, property=value]
+ * @return string
+ */
+ public function __toString()
+ {
+ return __CLASS__ . '[' .
+ Util::attributesToString($this->_attributes) . ']';
+ }
+}
+class_alias('Braintree\LocalPaymentCompleted', 'Braintree_LocalPaymentCompleted');
diff --git a/lib/Braintree/MasterpassCard.php b/lib/Braintree/MasterpassCard.php
new file mode 100644
index 0000000..a7fdef2
--- /dev/null
+++ b/lib/Braintree/MasterpassCard.php
@@ -0,0 +1,141 @@
+== More information ==
+ *
+ * For more detailed information on CreditCard verifications, see {@link https://developers.braintreepayments.com/reference/response/credit-card-verification/php https://developers.braintreepayments.com/reference/response/credit-card-verification/php}
+ *
+ * @package Braintree
+ * @category Resources
+ *
+ * @property-read \Braintree\Address $billingAddress
+ * @property-read string $bin
+ * @property-read string $cardType
+ * @property-read string $cardholderName
+ * @property-read string $commercial
+ * @property-read string $countryOfIssuance
+ * @property-read \DateTime $createdAt
+ * @property-read string $customerId
+ * @property-read string $customerLocation
+ * @property-read string $debit
+ * @property-read boolean $default
+ * @property-read string $durbinRegulated
+ * @property-read string $expirationDate
+ * @property-read string $expirationMonth
+ * @property-read string $expirationYear
+ * @property-read boolean $expired
+ * @property-read string $healthcare
+ * @property-read string $imageUrl
+ * @property-read string $issuingBank
+ * @property-read string $last4
+ * @property-read string $maskedNumber
+ * @property-read string $payroll
+ * @property-read string $prepaid
+ * @property-read string $productId
+ * @property-read \Braintree\Subscription[] $subscriptions
+ * @property-read string $token
+ * @property-read string $uniqueNumberIdentifier
+ * @property-read \DateTime $updatedAt
+ */
+class MasterpassCard extends Base
+{
+ /* instance methods */
+ /**
+ * returns false if default is null or false
+ *
+ * @return boolean
+ */
+ public function isDefault()
+ {
+ return $this->default;
+ }
+
+ /**
+ * checks whether the card is expired based on the current date
+ *
+ * @return boolean
+ */
+ public function isExpired()
+ {
+ return $this->expired;
+ }
+
+ /**
+ * sets instance properties from an array of values
+ *
+ * @access protected
+ * @param array $creditCardAttribs array of creditcard data
+ * @return void
+ */
+ protected function _initialize($creditCardAttribs)
+ {
+ // set the attributes
+ $this->_attributes = $creditCardAttribs;
+
+ // map each address into its own object
+ $billingAddress = isset($creditCardAttribs['billingAddress']) ?
+ Address::factory($creditCardAttribs['billingAddress']) :
+ null;
+
+ $subscriptionArray = [];
+ if (isset($creditCardAttribs['subscriptions'])) {
+ foreach ($creditCardAttribs['subscriptions'] AS $subscription) {
+ $subscriptionArray[] = Subscription::factory($subscription);
+ }
+ }
+
+ $this->_set('subscriptions', $subscriptionArray);
+ $this->_set('billingAddress', $billingAddress);
+ $this->_set('expirationDate', $this->expirationMonth . '/' . $this->expirationYear);
+ $this->_set('maskedNumber', $this->bin . '******' . $this->last4);
+ }
+
+ /**
+ * returns false if comparing object is not a CreditCard,
+ * or is a CreditCard with a different id
+ *
+ * @param object $otherCreditCard customer to compare against
+ * @return boolean
+ */
+ public function isEqual($otherMasterpassCard)
+ {
+ return !($otherMasterpassCard instanceof self) ? false : $this->token === $otherMasterpassCard->token;
+ }
+
+ /**
+ * create a printable representation of the object as:
+ * ClassName[property=value, property=value]
+ * @return string
+ */
+ public function __toString()
+ {
+ return __CLASS__ . '[' .
+ Util::attributesToString($this->_attributes) .']';
+ }
+
+ /**
+ * factory method: returns an instance of CreditCard
+ * to the requesting method, with populated properties
+ *
+ * @ignore
+ * @return MasterpassCard
+ */
+ public static function factory($attributes)
+ {
+ $defaultAttributes = [
+ 'bin' => '',
+ 'expirationMonth' => '',
+ 'expirationYear' => '',
+ 'last4' => '',
+ ];
+
+ $instance = new self();
+ $instance->_initialize(array_merge($defaultAttributes, $attributes));
+ return $instance;
+ }
+}
+class_alias('Braintree\MasterpassCard', 'Braintree_MasterpassCard');
diff --git a/lib/Braintree/Merchant.php b/lib/Braintree/Merchant.php
new file mode 100644
index 0000000..09d8bf4
--- /dev/null
+++ b/lib/Braintree/Merchant.php
@@ -0,0 +1,36 @@
+_attributes = $attribs;
+
+ $merchantAccountArray = [];
+ if (isset($attribs['merchantAccounts'])) {
+ foreach ($attribs['merchantAccounts'] AS $merchantAccount) {
+ $merchantAccountArray[] = MerchantAccount::factory($merchantAccount);
+ }
+ }
+ $this->_set('merchantAccounts', $merchantAccountArray);
+ }
+
+ public static function factory($attributes)
+ {
+ $instance = new self();
+ $instance->_initialize($attributes);
+ return $instance;
+ }
+
+ /**
+ * returns a string representation of the merchant
+ * @return string
+ */
+ public function __toString()
+ {
+ return __CLASS__ . '[' .
+ Util::attributesToString($this->_attributes) .']';
+ }
+}
+class_alias('Braintree\Merchant', 'Braintree_Merchant');
diff --git a/lib/Braintree/MerchantAccount.php b/lib/Braintree/MerchantAccount.php
index 7c9aaf9..0797e23 100644
--- a/lib/Braintree/MerchantAccount.php
+++ b/lib/Braintree/MerchantAccount.php
@@ -1,6 +1,17 @@
$attribs));
- }
-
- public static function find($merchant_account_id)
- {
- try {
- $response = Braintree_Http::get('/merchant_accounts/' . $merchant_account_id);
- return self::factory($response['merchantAccount']);
- } catch (Braintree_Exception_NotFound $e) {
- throw new Braintree_Exception_NotFound('merchant account with id ' . $merchant_account_id . ' not found');
- }
- }
-
- public static function update($merchant_account_id, $attributes)
- {
- Braintree_Util::verifyKeys(self::updateSignature(), $attributes);
- return self::_doUpdate('/merchant_accounts/' . $merchant_account_id . '/update_via_api', array('merchant_account' => $attributes));
- }
-
- public static function detectSignature($attribs)
- {
- if (isset($attribs['applicantDetails'])) {
- trigger_error("DEPRECATED: Passing applicantDetails to create is deprecated. Please use individual, business, and funding", E_USER_NOTICE);
- return self::createDeprecatedSignature();
- } else {
- return self::createSignature();
- }
- }
-
- public static function updateSignature()
- {
- $signature = self::createSignature();
- unset($signature['tosAccepted']);
- return $signature;
- }
-
- public static function createSignature()
- {
- $addressSignature = array('streetAddress', 'postalCode', 'locality', 'region');
- $individualSignature = array(
- 'firstName',
- 'lastName',
- 'email',
- 'phone',
- 'dateOfBirth',
- 'ssn',
- array('address' => $addressSignature)
- );
-
- $businessSignature = array(
- 'dbaName',
- 'legalName',
- 'taxId',
- array('address' => $addressSignature)
- );
-
- $fundingSignature = array(
- 'routingNumber',
- 'accountNumber',
- 'destination',
- 'email',
- 'mobilePhone'
- );
-
- return array(
- 'id',
- 'tosAccepted',
- 'masterMerchantAccountId',
- array('individual' => $individualSignature),
- array('funding' => $fundingSignature),
- array('business' => $businessSignature)
- );
- }
-
- public static function createDeprecatedSignature()
- {
- $applicantDetailsAddressSignature = array('streetAddress', 'postalCode', 'locality', 'region');
- $applicantDetailsSignature = array(
- 'companyName',
- 'firstName',
- 'lastName',
- 'email',
- 'phone',
- 'dateOfBirth',
- 'ssn',
- 'taxId',
- 'routingNumber',
- 'accountNumber',
- array('address' => $applicantDetailsAddressSignature)
- );
-
- return array(
- array('applicantDetails' => $applicantDetailsSignature),
- 'id',
- 'tosAccepted',
- 'masterMerchantAccountId'
- );
- }
-
- public static function _doCreate($url, $params)
- {
- $response = Braintree_Http::post($url, $params);
-
- return self::_verifyGatewayResponse($response);
- }
-
- private static function _doUpdate($url, $params)
- {
- $response = Braintree_Http::put($url, $params);
-
- return self::_verifyGatewayResponse($response);
- }
-
- private static function _verifyGatewayResponse($response)
- {
- if (isset($response['merchantAccount'])) {
- // return a populated instance of Braintree_merchantAccount
- return new Braintree_Result_Successful(
- self::factory($response['merchantAccount'])
- );
- } else if (isset($response['apiErrorResponse'])) {
- return new Braintree_Result_Error($response['apiErrorResponse']);
- } else {
- throw new Braintree_Exception_Unexpected(
- "Expected merchant account or apiErrorResponse"
- );
- }
- }
-
public static function factory($attributes)
{
$instance = new self();
@@ -155,22 +34,41 @@ protected function _initialize($merchantAccountAttribs)
if (isset($merchantAccountAttribs['individual'])) {
$individual = $merchantAccountAttribs['individual'];
- $this->_set('individualDetails', Braintree_MerchantAccount_IndividualDetails::Factory($individual));
+ $this->_set('individualDetails', MerchantAccount\IndividualDetails::Factory($individual));
}
if (isset($merchantAccountAttribs['business'])) {
$business = $merchantAccountAttribs['business'];
- $this->_set('businessDetails', Braintree_MerchantAccount_BusinessDetails::Factory($business));
+ $this->_set('businessDetails', MerchantAccount\BusinessDetails::Factory($business));
}
if (isset($merchantAccountAttribs['funding'])) {
$funding = $merchantAccountAttribs['funding'];
- $this->_set('fundingDetails', new Braintree_MerchantAccount_FundingDetails($funding));
+ $this->_set('fundingDetails', new MerchantAccount\FundingDetails($funding));
}
if (isset($merchantAccountAttribs['masterMerchantAccount'])) {
$masterMerchantAccount = $merchantAccountAttribs['masterMerchantAccount'];
- $this->_set('masterMerchantAccount', Braintree_MerchantAccount::Factory($masterMerchantAccount));
+ $this->_set('masterMerchantAccount', self::Factory($masterMerchantAccount));
}
}
+
+
+ // static methods redirecting to gateway
+
+ public static function create($attribs)
+ {
+ return Configuration::gateway()->merchantAccount()->create($attribs);
+ }
+
+ public static function find($merchant_account_id)
+ {
+ return Configuration::gateway()->merchantAccount()->find($merchant_account_id);
+ }
+
+ public static function update($merchant_account_id, $attributes)
+ {
+ return Configuration::gateway()->merchantAccount()->update($merchant_account_id, $attributes);
+ }
}
+class_alias('Braintree\MerchantAccount', 'Braintree_MerchantAccount');
diff --git a/lib/Braintree/MerchantAccount/AddressDetails.php b/lib/Braintree/MerchantAccount/AddressDetails.php
index 15349f6..c4caf9a 100644
--- a/lib/Braintree/MerchantAccount/AddressDetails.php
+++ b/lib/Braintree/MerchantAccount/AddressDetails.php
@@ -1,5 +1,10 @@
_attributes = $businessAttribs;
if (isset($businessAttribs['address'])) {
- $this->_set('addressDetails', new Braintree_MerchantAccount_AddressDetails($businessAttribs['address']));
+ $this->_set('addressDetails', new AddressDetails($businessAttribs['address']));
}
}
@@ -17,3 +20,4 @@ public static function factory($attributes)
return $instance;
}
}
+class_alias('Braintree\MerchantAccount\BusinessDetails', 'Braintree_MerchantAccount_BusinessDetails');
diff --git a/lib/Braintree/MerchantAccount/FundingDetails.php b/lib/Braintree/MerchantAccount/FundingDetails.php
index f33e595..6fac0b5 100644
--- a/lib/Braintree/MerchantAccount/FundingDetails.php
+++ b/lib/Braintree/MerchantAccount/FundingDetails.php
@@ -1,6 +1,10 @@
_attributes = $individualAttribs;
if (isset($individualAttribs['address'])) {
- $this->_set('addressDetails', new Braintree_MerchantAccount_AddressDetails($individualAttribs['address']));
+ $this->_set('addressDetails', new AddressDetails($individualAttribs['address']));
}
}
@@ -17,3 +20,4 @@ public static function factory($attributes)
return $instance;
}
}
+class_alias('Braintree\MerchantAccount\IndividualDetails', 'Braintree_MerchantAccount_IndividualDetails');
diff --git a/lib/Braintree/MerchantAccountGateway.php b/lib/Braintree/MerchantAccountGateway.php
new file mode 100644
index 0000000..a9704c8
--- /dev/null
+++ b/lib/Braintree/MerchantAccountGateway.php
@@ -0,0 +1,182 @@
+_gateway = $gateway;
+ $this->_config = $gateway->config;
+ $this->_config->assertHasAccessTokenOrKeys();
+ $this->_http = new Http($gateway->config);
+ }
+
+ public function create($attribs)
+ {
+ Util::verifyKeys(self::detectSignature($attribs), $attribs);
+ return $this->_doCreate('/merchant_accounts/create_via_api', ['merchant_account' => $attribs]);
+ }
+
+ public function find($merchant_account_id)
+ {
+ try {
+ $path = $this->_config->merchantPath() . '/merchant_accounts/' . $merchant_account_id;
+ $response = $this->_http->get($path);
+ return MerchantAccount::factory($response['merchantAccount']);
+ } catch (Exception\NotFound $e) {
+ throw new Exception\NotFound('merchant account with id ' . $merchant_account_id . ' not found');
+ }
+ }
+
+ public function update($merchant_account_id, $attributes)
+ {
+ Util::verifyKeys(self::updateSignature(), $attributes);
+ return $this->_doUpdate('/merchant_accounts/' . $merchant_account_id . '/update_via_api', ['merchant_account' => $attributes]);
+ }
+
+ public static function detectSignature($attribs)
+ {
+ if (isset($attribs['applicantDetails'])) {
+ trigger_error("DEPRECATED: Passing applicantDetails to create is deprecated. Please use individual, business, and funding", E_USER_NOTICE);
+ return self::createDeprecatedSignature();
+ } else {
+ return self::createSignature();
+ }
+ }
+
+ public static function updateSignature()
+ {
+ $signature = self::createSignature();
+ unset($signature['tosAccepted']);
+ return $signature;
+ }
+
+ public function createForCurrency($attribs)
+ {
+ $response = $this->_http->post($this->_config->merchantPath() . '/merchant_accounts/create_for_currency', ['merchant_account' => $attribs]);
+ return $this->_verifyGatewayResponse($response);
+ }
+
+ public function all()
+ {
+ $pager = [
+ 'object' => $this,
+ 'method' => 'fetchMerchantAccounts',
+ ];
+ return new PaginatedCollection($pager);
+ }
+
+ public function fetchMerchantAccounts($page)
+ {
+ $response = $this->_http->get($this->_config->merchantPath() . '/merchant_accounts?page=' . $page);
+ $body = $response['merchantAccounts'];
+ $merchantAccounts = Util::extractattributeasarray($body, 'merchantAccount');
+ $totalItems = $body['totalItems'][0];
+ $pageSize = $body['pageSize'][0];
+ return new PaginatedResult($totalItems, $pageSize, $merchantAccounts);
+ }
+
+ public static function createSignature()
+ {
+ $addressSignature = ['streetAddress', 'postalCode', 'locality', 'region'];
+ $individualSignature = [
+ 'firstName',
+ 'lastName',
+ 'email',
+ 'phone',
+ 'dateOfBirth',
+ 'ssn',
+ ['address' => $addressSignature]
+ ];
+
+ $businessSignature = [
+ 'dbaName',
+ 'legalName',
+ 'taxId',
+ ['address' => $addressSignature]
+ ];
+
+ $fundingSignature = [
+ 'routingNumber',
+ 'accountNumber',
+ 'destination',
+ 'email',
+ 'mobilePhone',
+ 'descriptor',
+ ];
+
+ return [
+ 'id',
+ 'tosAccepted',
+ 'masterMerchantAccountId',
+ ['individual' => $individualSignature],
+ ['funding' => $fundingSignature],
+ ['business' => $businessSignature]
+ ];
+ }
+
+ public static function createDeprecatedSignature()
+ {
+ $applicantDetailsAddressSignature = ['streetAddress', 'postalCode', 'locality', 'region'];
+ $applicantDetailsSignature = [
+ 'companyName',
+ 'firstName',
+ 'lastName',
+ 'email',
+ 'phone',
+ 'dateOfBirth',
+ 'ssn',
+ 'taxId',
+ 'routingNumber',
+ 'accountNumber',
+ ['address' => $applicantDetailsAddressSignature]
+ ];
+
+ return [
+ ['applicantDetails' => $applicantDetailsSignature],
+ 'id',
+ 'tosAccepted',
+ 'masterMerchantAccountId'
+ ];
+ }
+
+ public function _doCreate($subPath, $params)
+ {
+ $fullPath = $this->_config->merchantPath() . $subPath;
+ $response = $this->_http->post($fullPath, $params);
+
+ return $this->_verifyGatewayResponse($response);
+ }
+
+ private function _doUpdate($subPath, $params)
+ {
+ $fullPath = $this->_config->merchantPath() . $subPath;
+ $response = $this->_http->put($fullPath, $params);
+
+ return $this->_verifyGatewayResponse($response);
+ }
+
+ private function _verifyGatewayResponse($response)
+ {
+ if (isset($response['response'])) {
+ $response = $response['response'];
+ }
+ if (isset($response['merchantAccount'])) {
+ // return a populated instance of merchantAccount
+ return new Result\Successful(
+ MerchantAccount::factory($response['merchantAccount'])
+ );
+ } else if (isset($response['apiErrorResponse'])) {
+ return new Result\Error($response['apiErrorResponse']);
+ } else {
+ throw new Exception\Unexpected(
+ "Expected merchant account or apiErrorResponse"
+ );
+ }
+ }
+}
+class_alias('Braintree\MerchantAccountGateway', 'Braintree_MerchantAccountGateway');
diff --git a/lib/Braintree/MerchantGateway.php b/lib/Braintree/MerchantGateway.php
new file mode 100644
index 0000000..d628e88
--- /dev/null
+++ b/lib/Braintree/MerchantGateway.php
@@ -0,0 +1,42 @@
+_gateway = $gateway;
+ $this->_config = $gateway->config;
+ $this->_config->assertHasClientCredentials();
+ $this->_http = new Http($gateway->config);
+ $this->_http->useClientCredentials();
+ }
+
+ public function create($attribs)
+ {
+ $response = $this->_http->post('/merchants/create_via_api', ['merchant' => $attribs]);
+ return $this->_verifyGatewayResponse($response);
+ }
+
+ private function _verifyGatewayResponse($response)
+ {
+ if (isset($response['response']['merchant'])) {
+ // return a populated instance of merchant
+ return new Result\Successful([
+ Merchant::factory($response['response']['merchant']),
+ OAuthCredentials::factory($response['response']['credentials']),
+ ]);
+ } else if (isset($response['apiErrorResponse'])) {
+ return new Result\Error($response['apiErrorResponse']);
+ } else {
+ throw new Exception\Unexpected(
+ "Expected merchant or apiErrorResponse"
+ );
+ }
+ }
+}
+class_alias('Braintree\MerchantGateway', 'Braintree_MerchantGateway');
diff --git a/lib/Braintree/Modification.php b/lib/Braintree/Modification.php
index 7563b57..e1d32b2 100644
--- a/lib/Braintree/Modification.php
+++ b/lib/Braintree/Modification.php
@@ -1,17 +1,11 @@
_attributes = $attributes;
-
- $addOnArray = array();
- if (isset($attributes['addOns'])) {
- foreach ($attributes['addOns'] AS $addOn) {
- $addOnArray[] = Braintree_addOn::factory($addOn);
- }
- }
- $this->_attributes['addOns'] = $addOnArray;
}
public static function factory($attributes)
@@ -20,4 +14,9 @@ public static function factory($attributes)
$instance->_initialize($attributes);
return $instance;
}
+
+ public function __toString() {
+ return get_called_class() . '[' . Util::attributesToString($this->_attributes) . ']';
+ }
}
+class_alias('Braintree\Modification', 'Braintree_Modification');
diff --git a/lib/Braintree/MultipleValueNode.php b/lib/Braintree/MultipleValueNode.php
index 3100e4d..397c610 100644
--- a/lib/Braintree/MultipleValueNode.php
+++ b/lib/Braintree/MultipleValueNode.php
@@ -1,15 +1,18 @@
name = $name;
- $this->items = array();
+ $this->items = [];
$this->allowedValues = $allowedValues;
}
- function in($values)
+ public function in($values)
{
$bad_values = array_diff($values, $this->allowedValues);
if (count($this->allowedValues) > 0 && count($bad_values) > 0) {
@@ -25,13 +28,14 @@ function in($values)
return $this;
}
- function is($value)
+ public function is($value)
{
- return $this->in(array($value));
+ return $this->in([$value]);
}
- function toParam()
+ public function toParam()
{
return $this->items;
}
}
+class_alias('Braintree\MultipleValueNode', 'Braintree_MultipleValueNode');
diff --git a/lib/Braintree/MultipleValueOrTextNode.php b/lib/Braintree/MultipleValueOrTextNode.php
index e0faca4..c98ee95 100644
--- a/lib/Braintree/MultipleValueOrTextNode.php
+++ b/lib/Braintree/MultipleValueOrTextNode.php
@@ -1,45 +1,47 @@
textNode = new Braintree_TextNode($name);
+ $this->textNode = new TextNode($name);
}
- function contains($value)
+ public function contains($value)
{
$this->textNode->contains($value);
return $this;
}
- function endsWith($value)
+ public function endsWith($value)
{
$this->textNode->endsWith($value);
return $this;
}
- function is($value)
+ public function is($value)
{
$this->textNode->is($value);
return $this;
}
- function isNot($value)
+ public function isNot($value)
{
$this->textNode->isNot($value);
return $this;
}
- function startsWith($value)
+ public function startsWith($value)
{
$this->textNode->startsWith($value);
return $this;
}
- function toParam()
+ public function toParam()
{
return array_merge(parent::toParam(), $this->textNode->toParam());
}
}
+class_alias('Braintree\MultipleValueOrTextNode', 'Braintree_MultipleValueOrTextNode');
diff --git a/lib/Braintree/OAuthAccessRevocation.php b/lib/Braintree/OAuthAccessRevocation.php
new file mode 100644
index 0000000..62a2897
--- /dev/null
+++ b/lib/Braintree/OAuthAccessRevocation.php
@@ -0,0 +1,32 @@
+_initialize($attributes);
+
+ return $instance;
+ }
+
+ /**
+ * @ignore
+ */
+ protected function _initialize($attributes)
+ {
+ $this->_attributes = $attributes;
+ }
+}
+class_alias('Braintree\OAuthAccessRevocation', 'Braintree_OAuthAccessRevocation');
diff --git a/lib/Braintree/OAuthCredentials.php b/lib/Braintree/OAuthCredentials.php
new file mode 100644
index 0000000..60de5fb
--- /dev/null
+++ b/lib/Braintree/OAuthCredentials.php
@@ -0,0 +1,34 @@
+_attributes = $attribs;
+ }
+
+ public static function factory($attributes)
+ {
+ $instance = new self();
+ $instance->_initialize($attributes);
+ return $instance;
+ }
+
+ /**
+ * returns a string representation of the access token
+ * @return string
+ */
+ public function __toString()
+ {
+ return __CLASS__ . '[' .
+ Util::attributesToString($this->_attributes) .']';
+ }
+}
+class_alias('Braintree\OAuthCredentials', 'Braintree_OAuthCredentials');
diff --git a/lib/Braintree/OAuthGateway.php b/lib/Braintree/OAuthGateway.php
new file mode 100644
index 0000000..4e2d1b2
--- /dev/null
+++ b/lib/Braintree/OAuthGateway.php
@@ -0,0 +1,124 @@
+_gateway = $gateway;
+ $this->_config = $gateway->config;
+ $this->_http = new Http($gateway->config);
+ $this->_http->useClientCredentials();
+
+ $this->_config->assertHasClientCredentials();
+ }
+
+ public function createTokenFromCode($params)
+ {
+ $params['grantType'] = "authorization_code";
+ return $this->_createToken($params);
+ }
+
+ public function createTokenFromRefreshToken($params)
+ {
+ $params['grantType'] = "refresh_token";
+ return $this->_createToken($params);
+ }
+
+ public function revokeAccessToken($accessToken)
+ {
+ $params = ['token' => $accessToken];
+ $response = $this->_http->post('/oauth/revoke_access_token', $params);
+ return $this->_verifyGatewayResponse($response);
+ }
+
+ private function _createToken($params)
+ {
+ $params = ['credentials' => $params];
+ $response = $this->_http->post('/oauth/access_tokens', $params);
+ return $this->_verifyGatewayResponse($response);
+ }
+
+ private function _verifyGatewayResponse($response)
+ {
+ if (isset($response['credentials'])) {
+ $result = new Result\Successful(
+ OAuthCredentials::factory($response['credentials'])
+ );
+ return $this->_mapSuccess($result);
+ } else if (isset($response['result'])) {
+ $result = new Result\Successful(
+ OAuthResult::factory($response['result'])
+ );
+ return $this->_mapAccessTokenRevokeSuccess($result);
+ } else if (isset($response['apiErrorResponse'])) {
+ $result = new Result\Error($response['apiErrorResponse']);
+ return $this->_mapError($result);
+ } else {
+ throw new Exception\Unexpected(
+ "Expected credentials or apiErrorResponse"
+ );
+ }
+ }
+
+ public function _mapError($result)
+ {
+ $error = $result->errors->deepAll()[0];
+
+ if ($error->code == Error\Codes::OAUTH_INVALID_GRANT) {
+ $result->error = 'invalid_grant';
+ } else if ($error->code == Error\Codes::OAUTH_INVALID_CREDENTIALS) {
+ $result->error = 'invalid_credentials';
+ } else if ($error->code == Error\Codes::OAUTH_INVALID_SCOPE) {
+ $result->error = 'invalid_scope';
+ }
+ $result->errorDescription = explode(': ', $error->message)[1];
+ return $result;
+ }
+
+ public function _mapAccessTokenRevokeSuccess($result)
+ {
+ $result->revocationResult = $result->success;
+ return $result;
+ }
+
+ public function _mapSuccess($result)
+ {
+ $credentials = $result->credentials;
+ $result->accessToken = $credentials->accessToken;
+ $result->refreshToken = $credentials->refreshToken;
+ $result->tokenType = $credentials->tokenType;
+ $result->expiresAt = $credentials->expiresAt;
+ return $result;
+ }
+
+ public function connectUrl($params = [])
+ {
+ $query = Util::camelCaseToDelimiterArray($params, '_');
+ $query['client_id'] = $this->_config->getClientId();
+ $queryString = preg_replace('/\%5B\d+\%5D/', '%5B%5D', http_build_query($query));
+
+ return $this->_config->baseUrl() . '/oauth/connect?' . $queryString;
+ }
+
+ /**
+ * @deprecated since version 3.26.1
+ */
+ public function computeSignature($url)
+ {
+ $key = hash('sha256', $this->_config->getClientSecret(), true);
+ return hash_hmac('sha256', $url, $key);
+ }
+}
+class_alias('Braintree\OAuthGateway', 'Braintree_OAuthGateway');
diff --git a/lib/Braintree/OAuthResult.php b/lib/Braintree/OAuthResult.php
new file mode 100644
index 0000000..9c3e4e6
--- /dev/null
+++ b/lib/Braintree/OAuthResult.php
@@ -0,0 +1,34 @@
+_attributes = $attribs;
+ }
+
+ public static function factory($attributes)
+ {
+ $instance = new self();
+ $instance->_initialize($attributes);
+ return $instance;
+ }
+
+ /**
+ * returns a string representation of the result
+ * @return string
+ */
+ public function __toString()
+ {
+ return __CLASS__ . '[' .
+ Util::attributesToString($this->_attributes) .']';
+ }
+}
+class_alias('Braintree\OAuthResult', 'Braintree_OAuthResult');
diff --git a/lib/Braintree/PaginatedCollection.php b/lib/Braintree/PaginatedCollection.php
new file mode 100644
index 0000000..9deef04
--- /dev/null
+++ b/lib/Braintree/PaginatedCollection.php
@@ -0,0 +1,120 @@
+
+ * $result = MerchantAccount::all();
+ *
+ * foreach($result as $merchantAccount) {
+ * print_r($merchantAccount->status);
+ * }
+ *
+ *
+ * @package Braintree
+ * @subpackage Utility
+ */
+class PaginatedCollection implements Iterator
+{
+ private $_pager;
+ private $_pageSize;
+ private $_currentPage;
+ private $_index;
+ private $_totalItems;
+ private $_items;
+
+ /**
+ * set up the paginated collection
+ *
+ * expects an array of an object and method to call on it
+ *
+ * @param array $pager
+ */
+ public function __construct($pager)
+ {
+ $this->_pager = $pager;
+ $this->_pageSize = 0;
+ $this->_currentPage = 0;
+ $this->_totalItems = 0;
+ $this->_index = 0;
+ }
+
+ /**
+ * returns the current item when iterating with foreach
+ */
+ public function current()
+ {
+ return $this->_items[($this->_index % $this->_pageSize)];
+ }
+
+ public function key()
+ {
+ return null;
+ }
+
+ /**
+ * advances to the next item in the collection when iterating with foreach
+ */
+ public function next()
+ {
+ ++$this->_index;
+ }
+
+ /**
+ * rewinds the collection to the first item when iterating with foreach
+ */
+ public function rewind()
+ {
+ $this->_index = 0;
+ $this->_currentPage = 0;
+ $this->_pageSize = 0;
+ $this->_totalItems = 0;
+ $this->_items = [];
+ }
+
+ /**
+ * returns whether the current item is valid when iterating with foreach
+ */
+ public function valid()
+ {
+ if ($this->_currentPage == 0 || $this->_index % $this->_pageSize == 0 && $this->_index < $this->_totalItems)
+ {
+ $this->_getNextPage();
+ }
+
+ return $this->_index < $this->_totalItems;
+ }
+
+ private function _getNextPage()
+ {
+ $this->_currentPage++;
+ $object = $this->_pager['object'];
+ $method = $this->_pager['method'];
+
+ if (isset($this->_pager['query'])) {
+ $query = $this->_pager['query'];
+ $result = call_user_func(
+ [$object, $method],
+ $query,
+ $this->_currentPage
+ );
+ } else {
+ $result = call_user_func(
+ [$object, $method],
+ $this->_currentPage
+ );
+ }
+
+ $this->_totalItems= $result->getTotalItems();
+ $this->_pageSize = $result->getPageSize();
+ $this->_items = $result->getCurrentPage();
+ }
+}
+class_alias('Braintree\PaginatedCollection', 'Braintree_PaginatedCollection');
diff --git a/lib/Braintree/PaginatedResult.php b/lib/Braintree/PaginatedResult.php
new file mode 100644
index 0000000..9b383ab
--- /dev/null
+++ b/lib/Braintree/PaginatedResult.php
@@ -0,0 +1,32 @@
+_totalItems = $totalItems;
+ $this->_pageSize = $pageSize;
+ $this->_currentPage = $currentPage;
+ }
+
+ public function getTotalItems()
+ {
+ return $this->_totalItems;
+ }
+
+ public function getPageSize()
+ {
+ return $this->_pageSize;
+ }
+
+ public function getCurrentPage()
+ {
+ return $this->_currentPage;
+ }
+}
+class_alias('Braintree\PaginatedResult', 'Braintree_PaginatedResult');
diff --git a/lib/Braintree/PartialMatchNode.php b/lib/Braintree/PartialMatchNode.php
index 076204a..5d8dc91 100644
--- a/lib/Braintree/PartialMatchNode.php
+++ b/lib/Braintree/PartialMatchNode.php
@@ -1,16 +1,18 @@
searchTerms["starts_with"] = strval($value);
return $this;
}
- function endsWith($value)
+ public function endsWith($value)
{
$this->searchTerms["ends_with"] = strval($value);
return $this;
}
}
+class_alias('Braintree\PartialMatchNode', 'Braintree_PartialMatchNode');
diff --git a/lib/Braintree/PartnerMerchant.php b/lib/Braintree/PartnerMerchant.php
index 48673e7..d5ac152 100644
--- a/lib/Braintree/PartnerMerchant.php
+++ b/lib/Braintree/PartnerMerchant.php
@@ -1,29 +1,23 @@
_attributes = $attributes;
}
}
+class_alias('Braintree\PartnerMerchant', 'Braintree_PartnerMerchant');
diff --git a/lib/Braintree/PayPalAccount.php b/lib/Braintree/PayPalAccount.php
new file mode 100644
index 0000000..7cfbf97
--- /dev/null
+++ b/lib/Braintree/PayPalAccount.php
@@ -0,0 +1,115 @@
+== More information ==
+ *
+ *
+ * @package Braintree
+ * @category Resources
+ *
+ * @property-read string $billingAgreementId
+ * @property-read \DateTime $createdAt
+ * @property-read string $customerId
+ * @property-read boolean $default
+ * @property-read string $email
+ * @property-read string $imageUrl
+ * @property-read string $payerId
+ * @property-read \Braintree\Subscription[] $subscriptions
+ * @property-read string $token
+ * @property-read \DateTime $updatedAt
+ */
+class PayPalAccount extends Base
+{
+ /**
+ * factory method: returns an instance of PayPalAccount
+ * to the requesting method, with populated properties
+ *
+ * @ignore
+ * @return PayPalAccount
+ */
+ public static function factory($attributes)
+ {
+ $instance = new self();
+ $instance->_initialize($attributes);
+ return $instance;
+ }
+
+ /* instance methods */
+
+ /**
+ * returns false if default is null or false
+ *
+ * @return boolean
+ */
+ public function isDefault()
+ {
+ return $this->default;
+ }
+
+ /**
+ * sets instance properties from an array of values
+ *
+ * @access protected
+ * @param array $paypalAccountAttribs array of paypalAccount data
+ * @return void
+ */
+ protected function _initialize($paypalAccountAttribs)
+ {
+ // set the attributes
+ $this->_attributes = $paypalAccountAttribs;
+
+ $subscriptionArray = [];
+ if (isset($paypalAccountAttribs['subscriptions'])) {
+ foreach ($paypalAccountAttribs['subscriptions'] AS $subscription) {
+ $subscriptionArray[] = Subscription::factory($subscription);
+ }
+ }
+
+ $this->_set('subscriptions', $subscriptionArray);
+ }
+
+ /**
+ * create a printable representation of the object as:
+ * ClassName[property=value, property=value]
+ * @return string
+ */
+ public function __toString()
+ {
+ return __CLASS__ . '[' .
+ Util::attributesToString($this->_attributes) . ']';
+ }
+
+
+ // static methods redirecting to gateway
+
+ public static function find($token)
+ {
+ return Configuration::gateway()->payPalAccount()->find($token);
+ }
+
+ public static function update($token, $attributes)
+ {
+ return Configuration::gateway()->payPalAccount()->update($token, $attributes);
+ }
+
+ public static function delete($token)
+ {
+ return Configuration::gateway()->payPalAccount()->delete($token);
+ }
+
+ public static function sale($token, $transactionAttribs)
+ {
+ return Configuration::gateway()->payPalAccount()->sale($token, $transactionAttribs);
+ }
+}
+class_alias('Braintree\PayPalAccount', 'Braintree_PayPalAccount');
diff --git a/lib/Braintree/PayPalAccountGateway.php b/lib/Braintree/PayPalAccountGateway.php
new file mode 100644
index 0000000..9302a17
--- /dev/null
+++ b/lib/Braintree/PayPalAccountGateway.php
@@ -0,0 +1,178 @@
+== More information ==
+ *
+ *
+ * @package Braintree
+ * @category Resources
+ */
+class PayPalAccountGateway
+{
+ private $_gateway;
+ private $_config;
+ private $_http;
+
+ public function __construct($gateway)
+ {
+ $this->_gateway = $gateway;
+ $this->_config = $gateway->config;
+ $this->_config->assertHasAccessTokenOrKeys();
+ $this->_http = new Http($gateway->config);
+ }
+
+
+ /**
+ * find a paypalAccount by token
+ *
+ * @access public
+ * @param string $token paypal accountunique id
+ * @return PayPalAccount
+ * @throws Exception\NotFound
+ */
+ public function find($token)
+ {
+ $this->_validateId($token);
+ try {
+ $path = $this->_config->merchantPath() . '/payment_methods/paypal_account/' . $token;
+ $response = $this->_http->get($path);
+ return PayPalAccount::factory($response['paypalAccount']);
+ } catch (Exception\NotFound $e) {
+ throw new Exception\NotFound(
+ 'paypal account with token ' . $token . ' not found'
+ );
+ }
+
+ }
+
+ /**
+ * updates the paypalAccount record
+ *
+ * if calling this method in context, $token
+ * is the 2nd attribute. $token is not sent in object context.
+ *
+ * @access public
+ * @param array $attributes
+ * @param string $token (optional)
+ * @return Result\Successful or Result\Error
+ */
+ public function update($token, $attributes)
+ {
+ Util::verifyKeys(self::updateSignature(), $attributes);
+ $this->_validateId($token);
+ return $this->_doUpdate('put', '/payment_methods/paypal_account/' . $token, ['paypalAccount' => $attributes]);
+ }
+
+ public function delete($token)
+ {
+ $this->_validateId($token);
+ $path = $this->_config->merchantPath() . '/payment_methods/paypal_account/' . $token;
+ $this->_http->delete($path);
+ return new Result\Successful();
+ }
+
+ /**
+ * create a new sale for the current PayPal account
+ *
+ * @param string $token
+ * @param array $transactionAttribs
+ * @return Result\Successful|Result\Error
+ * @see Transaction::sale()
+ */
+ public function sale($token, $transactionAttribs)
+ {
+ $this->_validateId($token);
+ return Transaction::sale(
+ array_merge(
+ $transactionAttribs,
+ ['paymentMethodToken' => $token]
+ )
+ );
+ }
+
+ public static function updateSignature()
+ {
+ return [
+ 'token',
+ ['options' => ['makeDefault']]
+ ];
+ }
+
+ /**
+ * sends the update request to the gateway
+ *
+ * @ignore
+ * @param string $subPath
+ * @param array $params
+ * @return mixed
+ */
+ private function _doUpdate($httpVerb, $subPath, $params)
+ {
+ $fullPath = $this->_config->merchantPath() . $subPath;
+ $response = $this->_http->$httpVerb($fullPath, $params);
+ return $this->_verifyGatewayResponse($response);
+ }
+
+ /**
+ * generic method for validating incoming gateway responses
+ *
+ * creates a new PayPalAccount object and encapsulates
+ * it inside a Result\Successful object, or
+ * encapsulates a Errors object inside a Result\Error
+ * alternatively, throws an Unexpected exception if the response is invalid.
+ *
+ * @ignore
+ * @param array $response gateway response values
+ * @return Result\Successful|Result\Error
+ * @throws Exception\Unexpected
+ */
+ private function _verifyGatewayResponse($response)
+ {
+ if (isset($response['paypalAccount'])) {
+ // return a populated instance of PayPalAccount
+ return new Result\Successful(
+ PayPalAccount::factory($response['paypalAccount'])
+ );
+ } else if (isset($response['apiErrorResponse'])) {
+ return new Result\Error($response['apiErrorResponse']);
+ } else {
+ throw new Exception\Unexpected(
+ 'Expected paypal account or apiErrorResponse'
+ );
+ }
+ }
+
+ /**
+ * verifies that a valid paypal account identifier is being used
+ * @ignore
+ * @param string $identifier
+ * @param Optional $string $identifierType type of identifier supplied, default 'token'
+ * @throws InvalidArgumentException
+ */
+ private function _validateId($identifier = null, $identifierType = 'token')
+ {
+ if (empty($identifier)) {
+ throw new InvalidArgumentException(
+ 'expected paypal account id to be set'
+ );
+ }
+ if (!preg_match('/^[0-9A-Za-z_-]+$/', $identifier)) {
+ throw new InvalidArgumentException(
+ $identifier . ' is an invalid paypal account ' . $identifierType . '.'
+ );
+ }
+ }
+}
+class_alias('Braintree\PayPalAccountGateway', 'Braintree_PayPalAccountGateway');
diff --git a/lib/Braintree/PaymentInstrumentType.php b/lib/Braintree/PaymentInstrumentType.php
new file mode 100644
index 0000000..8218e42
--- /dev/null
+++ b/lib/Braintree/PaymentInstrumentType.php
@@ -0,0 +1,19 @@
+== More information ==
+ *
+ *
+ * @package Braintree
+ * @category Resources
+ */
+class PaymentMethod extends Base
+{
+ // static methods redirecting to gateway
+
+ public static function create($attribs)
+ {
+ return Configuration::gateway()->paymentMethod()->create($attribs);
+ }
+
+ public static function find($token)
+ {
+ return Configuration::gateway()->paymentMethod()->find($token);
+ }
+
+ public static function update($token, $attribs)
+ {
+ return Configuration::gateway()->paymentMethod()->update($token, $attribs);
+ }
+
+ public static function delete($token, $options=[])
+ {
+ return Configuration::gateway()->paymentMethod()->delete($token, $options);
+ }
+}
+class_alias('Braintree\PaymentMethod', 'Braintree_PaymentMethod');
diff --git a/lib/Braintree/PaymentMethodGateway.php b/lib/Braintree/PaymentMethodGateway.php
new file mode 100644
index 0000000..df3dd58
--- /dev/null
+++ b/lib/Braintree/PaymentMethodGateway.php
@@ -0,0 +1,320 @@
+== More information ==
+ *
+ *
+ * @package Braintree
+ * @category Resources
+ */
+class PaymentMethodGateway
+{
+ private $_gateway;
+ private $_config;
+ private $_http;
+
+ public function __construct($gateway)
+ {
+ $this->_gateway = $gateway;
+ $this->_config = $gateway->config;
+ $this->_config->assertHasAccessTokenOrKeys();
+ $this->_http = new Http($gateway->config);
+ }
+
+
+ public function create($attribs)
+ {
+ Util::verifyKeys(self::createSignature(), $attribs);
+ return $this->_doCreate('/payment_methods', ['payment_method' => $attribs]);
+ }
+
+ /**
+ * find a PaymentMethod by token
+ *
+ * @param string $token payment method unique id
+ * @return CreditCard|PayPalAccount
+ * @throws Exception\NotFound
+ */
+ public function find($token)
+ {
+ $this->_validateId($token);
+ try {
+ $path = $this->_config->merchantPath() . '/payment_methods/any/' . $token;
+ $response = $this->_http->get($path);
+ return PaymentMethodParser::parsePaymentMethod($response);
+ } catch (Exception\NotFound $e) {
+ throw new Exception\NotFound(
+ 'payment method with token ' . $token . ' not found'
+ );
+ }
+ }
+
+ public function update($token, $attribs)
+ {
+ Util::verifyKeys(self::updateSignature(), $attribs);
+ return $this->_doUpdate('/payment_methods/any/' . $token, ['payment_method' => $attribs]);
+ }
+
+ public function delete($token, $options=[])
+ {
+ Util::verifyKeys(self::deleteSignature(), $options);
+ $this->_validateId($token);
+ $queryString = "";
+ if (!empty($options)) {
+ $queryString = "?" . http_build_query(Util::camelCaseToDelimiterArray($options, '_'));
+ }
+ return $this->_doDelete('/payment_methods/any/' . $token . $queryString);
+ }
+
+ public function grant($sharedPaymentMethodToken, $attribs=[])
+ {
+ if (is_bool($attribs) === true) {
+ $attribs = ['allow_vaulting' => $attribs];
+ }
+ $options = [ 'shared_payment_method_token' => $sharedPaymentMethodToken ];
+
+ return $this->_doGrant(
+ '/payment_methods/grant',
+ [
+ 'payment_method' => array_merge($attribs, $options)
+ ]
+ );
+ }
+
+ public function revoke($sharedPaymentMethodToken)
+ {
+ return $this->_doRevoke(
+ '/payment_methods/revoke',
+ [
+ 'payment_method' => [
+ 'shared_payment_method_token' => $sharedPaymentMethodToken
+ ]
+ ]
+ );
+ }
+
+ private static function baseSignature()
+ {
+ $billingAddressSignature = AddressGateway::createSignature();
+ $optionsSignature = [
+ 'failOnDuplicatePaymentMethod',
+ 'makeDefault',
+ 'verificationMerchantAccountId',
+ 'verifyCard',
+ 'verificationAmount',
+ 'usBankAccountVerificationMethod',
+ ['paypal' => [
+ 'payee_email',
+ 'payeeEmail',
+ 'order_id',
+ 'orderId',
+ 'custom_field',
+ 'customField',
+ 'description',
+ 'amount',
+ ['shipping' =>
+ [
+ 'firstName', 'lastName', 'company', 'countryName',
+ 'countryCodeAlpha2', 'countryCodeAlpha3', 'countryCodeNumeric',
+ 'extendedAddress', 'locality', 'postalCode', 'region',
+ 'streetAddress'],
+ ],
+ ]],
+ ];
+ return [
+ 'billingAddressId',
+ 'cardholderName',
+ 'cvv',
+ 'deviceData',
+ 'expirationDate',
+ 'expirationMonth',
+ 'expirationYear',
+ 'number',
+ 'paymentMethodNonce',
+ 'token',
+ ['options' => $optionsSignature],
+ ['billingAddress' => $billingAddressSignature]
+ ];
+ }
+
+ public static function createSignature()
+ {
+ $signature = array_merge(self::baseSignature(), ['customerId', 'paypalRefreshToken', 'paypalVaultWithoutUpgrade']);
+ return $signature;
+ }
+
+ public static function updateSignature()
+ {
+ $billingAddressSignature = AddressGateway::updateSignature();
+ array_push($billingAddressSignature, [
+ 'options' => [
+ 'updateExisting'
+ ]
+ ]);
+ $signature = array_merge(self::baseSignature(), [
+ 'deviceSessionId',
+ 'venmoSdkPaymentMethodCode',
+ 'fraudMerchantId',
+ ['billingAddress' => $billingAddressSignature]
+ ]);
+ return $signature;
+ }
+
+ private static function deleteSignature()
+ {
+ return ['revokeAllGrants'];
+ }
+
+ /**
+ * sends the create request to the gateway
+ *
+ * @ignore
+ * @param string $subPath
+ * @param array $params
+ * @return mixed
+ */
+ public function _doCreate($subPath, $params)
+ {
+ $fullPath = $this->_config->merchantPath() . $subPath;
+ $response = $this->_http->post($fullPath, $params);
+
+ return $this->_verifyGatewayResponse($response);
+ }
+
+ public function _doGrant($subPath, $params)
+ {
+ $fullPath = $this->_config->merchantPath() . $subPath;
+ $response = $this->_http->post($fullPath, $params);
+
+ return $this->_verifyGrantResponse($response);
+ }
+
+ public function _doRevoke($subPath, $params)
+ {
+ $fullPath = $this->_config->merchantPath() . $subPath;
+ $response = $this->_http->post($fullPath, $params);
+
+ return $this->_verifyRevokeResponse($response);
+ }
+
+ /**
+ * sends the update request to the gateway
+ *
+ * @ignore
+ * @param string $subPath
+ * @param array $params
+ * @return mixed
+ */
+ public function _doUpdate($subPath, $params)
+ {
+ $fullPath = $this->_config->merchantPath() . $subPath;
+ $response = $this->_http->put($fullPath, $params);
+
+ return $this->_verifyGatewayResponse($response);
+ }
+
+
+ /**
+ * sends the delete request to the gateway
+ *
+ * @ignore
+ * @param string $subPath
+ * @return mixed
+ */
+ public function _doDelete($subPath)
+ {
+ $fullPath = $this->_config->merchantPath() . $subPath;
+ $this->_http->delete($fullPath);
+ return new Result\Successful();
+ }
+
+ /**
+ * generic method for validating incoming gateway responses
+ *
+ * creates a new CreditCard or PayPalAccount object
+ * and encapsulates it inside a Result\Successful object, or
+ * encapsulates a Errors object inside a Result\Error
+ * alternatively, throws an Unexpected exception if the response is invalid.
+ *
+ * @ignore
+ * @param array $response gateway response values
+ * @return Result\Successful|Result\Error
+ * @throws Exception\Unexpected
+ */
+ private function _verifyGatewayResponse($response)
+ {
+ if (isset($response['apiErrorResponse'])) {
+ return new Result\Error($response['apiErrorResponse']);
+ } else if (($response)) {
+ return new Result\Successful(
+ PaymentMethodParser::parsePaymentMethod($response),
+ 'paymentMethod'
+ );
+ } else {
+ throw new Exception\Unexpected(
+ 'Expected payment method or apiErrorResponse'
+ );
+ }
+ }
+
+ private function _verifyGrantResponse($response) {
+ if (isset($response['apiErrorResponse'])) {
+ return new Result\Error($response['apiErrorResponse']);
+ } else if (isset($response['paymentMethodNonce'])) {
+ return new Result\Successful(
+ PaymentMethodNonce::factory($response['paymentMethodNonce']),
+ 'paymentMethodNonce'
+ );
+ } else {
+ throw new Exception\Unexpected(
+ 'Expected paymentMethodNonce or apiErrorResponse'
+ );
+ }
+ }
+
+ private function _verifyRevokeResponse($response) {
+ if (isset($response['apiErrorResponse'])) {
+ return new Result\Error($response['apiErrorResponse']);
+ } else if (isset($response['success'])) {
+ return new Result\Successful();
+ } else {
+ throw new Exception\Unexpected(
+ 'Expected success or apiErrorResponse'
+ );
+ }
+ }
+
+ /**
+ * verifies that a valid payment method identifier is being used
+ * @ignore
+ * @param string $identifier
+ * @param Optional $string $identifierType type of identifier supplied, default 'token'
+ * @throws InvalidArgumentException
+ */
+ private function _validateId($identifier = null, $identifierType = 'token')
+ {
+ if (empty($identifier)) {
+ throw new InvalidArgumentException(
+ 'expected payment method id to be set'
+ );
+ }
+ if (!preg_match('/^[0-9A-Za-z_-]+$/', $identifier)) {
+ throw new InvalidArgumentException(
+ $identifier . ' is an invalid payment method ' . $identifierType . '.'
+ );
+ }
+ }
+}
+class_alias('Braintree\PaymentMethodGateway', 'Braintree_PaymentMethodGateway');
diff --git a/lib/Braintree/PaymentMethodNonce.php b/lib/Braintree/PaymentMethodNonce.php
new file mode 100644
index 0000000..4501fa9
--- /dev/null
+++ b/lib/Braintree/PaymentMethodNonce.php
@@ -0,0 +1,62 @@
+== More information ==
+ *
+ *
+ * @package Braintree
+ * @category Resources
+ *
+ * @property-read \Braintree\BinData $binData
+ * @property-read boolean $default
+ * @property-read string $nonce
+ * @property-read \Braintree\ThreeDSecureInfo $threeDSecureInfo
+ * @property-read string $type
+ */
+class PaymentMethodNonce extends Base
+{
+ // static methods redirecting to gateway
+
+ public static function create($token)
+ {
+ return Configuration::gateway()->paymentMethodNonce()->create($token);
+ }
+
+ public static function find($nonce)
+ {
+ return Configuration::gateway()->paymentMethodNonce()->find($nonce);
+ }
+
+ public static function factory($attributes)
+ {
+ $instance = new self();
+ $instance->_initialize($attributes);
+ return $instance;
+ }
+
+ protected function _initialize($nonceAttributes)
+ {
+ $this->_attributes = $nonceAttributes;
+ $this->_set('nonce', $nonceAttributes['nonce']);
+ $this->_set('type', $nonceAttributes['type']);
+
+ if(isset($nonceAttributes['threeDSecureInfo'])) {
+ $this->_set('threeDSecureInfo', ThreeDSecureInfo::factory($nonceAttributes['threeDSecureInfo']));
+ }
+
+ if(isset($nonceAttributes['binData'])) {
+ $this->_set('binData', BinData::factory($nonceAttributes['binData']));
+ }
+ }
+}
+class_alias('Braintree\PaymentMethodNonce', 'Braintree_PaymentMethodNonce');
diff --git a/lib/Braintree/PaymentMethodNonceGateway.php b/lib/Braintree/PaymentMethodNonceGateway.php
new file mode 100644
index 0000000..40ad8af
--- /dev/null
+++ b/lib/Braintree/PaymentMethodNonceGateway.php
@@ -0,0 +1,64 @@
+== More information ==
+ *
+ *
+ * @package Braintree
+ * @category Resources
+ */
+class PaymentMethodNonceGateway
+{
+ private $_gateway;
+ private $_config;
+ private $_http;
+
+ public function __construct($gateway)
+ {
+ $this->_gateway = $gateway;
+ $this->_config = $gateway->config;
+ $this->_http = new Http($gateway->config);
+ }
+
+
+ public function create($token)
+ {
+ $subPath = '/payment_methods/' . $token . '/nonces';
+ $fullPath = $this->_config->merchantPath() . $subPath;
+ $response = $this->_http->post($fullPath);
+
+ return new Result\Successful(
+ PaymentMethodNonce::factory($response['paymentMethodNonce']),
+ "paymentMethodNonce"
+ );
+ }
+
+ /**
+ * @access public
+ *
+ */
+ public function find($nonce)
+ {
+ try {
+ $path = $this->_config->merchantPath() . '/payment_method_nonces/' . $nonce;
+ $response = $this->_http->get($path);
+ return PaymentMethodNonce::factory($response['paymentMethodNonce']);
+ } catch (Exception\NotFound $e) {
+ throw new Exception\NotFound(
+ 'payment method nonce with id ' . $nonce . ' not found'
+ );
+ }
+
+ }
+}
+class_alias('Braintree\PaymentMethodNonceGateway', 'Braintree_PaymentMethodNonceGateway');
diff --git a/lib/Braintree/PaymentMethodParser.php b/lib/Braintree/PaymentMethodParser.php
new file mode 100644
index 0000000..fea4af7
--- /dev/null
+++ b/lib/Braintree/PaymentMethodParser.php
@@ -0,0 +1,58 @@
+== More information ==
+ *
+ *
+ * @package Braintree
+ * @category Resources
+ *
+ */
+class PaymentMethodParser
+{
+ public static function parsePaymentMethod($response)
+ {
+ if (isset($response['creditCard'])) {
+ return CreditCard::factory($response['creditCard']);
+ } else if (isset($response['paypalAccount'])) {
+ return PayPalAccount::factory($response['paypalAccount']);
+ } else if (isset($response['coinbaseAccount'])) {
+ return CoinbaseAccount::factory($response['coinbaseAccount']);
+ } else if (isset($response['applePayCard'])) {
+ return ApplePayCard::factory($response['applePayCard']);
+ } else if (isset($response['androidPayCard'])) {
+ return AndroidPayCard::factory($response['androidPayCard']);
+ } else if (isset($response['amexExpressCheckoutCard'])) {
+ return AmexExpressCheckoutCard::factory($response['amexExpressCheckoutCard']);
+ } else if (isset($response['europeBankAccount'])) {
+ return EuropeBankAccount::factory($response['europeBankAccount']);
+ } else if (isset($response['usBankAccount'])) {
+ return UsBankAccount::factory($response['usBankAccount']);
+ } else if (isset($response['venmoAccount'])) {
+ return VenmoAccount::factory($response['venmoAccount']);
+ } else if (isset($response['visaCheckoutCard'])) {
+ return VisaCheckoutCard::factory($response['visaCheckoutCard']);
+ } else if (isset($response['masterpassCard'])) {
+ return MasterpassCard::factory($response['masterpassCard']);
+ } else if (isset($response['samsungPayCard'])) {
+ return SamsungPayCard::factory($response['samsungPayCard']);
+ } else if (is_array($response)) {
+ return UnknownPaymentMethod::factory($response);
+ } else {
+ throw new Exception\Unexpected(
+ 'Expected payment method'
+ );
+ }
+ }
+}
+class_alias('Braintree\PaymentMethodParser', 'Braintree_PaymentMethodParser');
diff --git a/lib/Braintree/Plan.php b/lib/Braintree/Plan.php
index b0fb3d0..a65b15a 100644
--- a/lib/Braintree/Plan.php
+++ b/lib/Braintree/Plan.php
@@ -1,21 +1,25 @@
$response['plans']);
- } else {
- $plans = array("plan" => array());
- }
-
- return Braintree_Util::extractAttributeAsArray(
- $plans,
- 'plan'
- );
- }
+namespace Braintree;
+/**
+ * @property-read \Braintree\Addon[] $addOns
+ * @property-read string $id
+ * @property-read int|null $billingDayOfMonth
+ * @property-read int $billingFrequency
+ * @property-read \DateTime $createdAt
+ * @property-read string $currencyIsoCode
+ * @property-read string|null $description
+ * @property-read \Braintree\Discount[] $discounts
+ * @property-read string $name
+ * @property-read int|null $numberOfBillingCycles
+ * @property-read string $price
+ * @property-read int|null $trialDuration
+ * @property-read string|null $trialDurationUnit
+ * @property-read boolean $trialPeriod
+ * @property-read \DateTime $updatedAt
+ */
+class Plan extends Base
+{
public static function factory($attributes)
{
$instance = new self();
@@ -28,28 +32,37 @@ protected function _initialize($attributes)
{
$this->_attributes = $attributes;
- $addOnArray = array();
+ $addOnArray = [];
if (isset($attributes['addOns'])) {
foreach ($attributes['addOns'] AS $addOn) {
- $addOnArray[] = Braintree_AddOn::factory($addOn);
+ $addOnArray[] = AddOn::factory($addOn);
}
}
$this->_attributes['addOns'] = $addOnArray;
- $discountArray = array();
+ $discountArray = [];
if (isset($attributes['discounts'])) {
foreach ($attributes['discounts'] AS $discount) {
- $discountArray[] = Braintree_Discount::factory($discount);
+ $discountArray[] = Discount::factory($discount);
}
}
$this->_attributes['discounts'] = $discountArray;
- $planArray = array();
+ $planArray = [];
if (isset($attributes['plans'])) {
foreach ($attributes['plans'] AS $plan) {
- $planArray[] = Braintree_Plan::factory($plan);
+ $planArray[] = self::factory($plan);
}
}
$this->_attributes['plans'] = $planArray;
}
+
+
+ // static methods redirecting to gateway
+
+ public static function all()
+ {
+ return Configuration::gateway()->plan()->all();
+ }
}
+class_alias('Braintree\Plan', 'Braintree_Plan');
diff --git a/lib/Braintree/PlanGateway.php b/lib/Braintree/PlanGateway.php
new file mode 100644
index 0000000..22beaee
--- /dev/null
+++ b/lib/Braintree/PlanGateway.php
@@ -0,0 +1,34 @@
+_gateway = $gateway;
+ $this->_config = $gateway->config;
+ $this->_config->assertHasAccessTokenOrKeys();
+ $this->_http = new Http($gateway->config);
+ }
+
+ public function all()
+ {
+ $path = $this->_config->merchantPath() . '/plans';
+ $response = $this->_http->get($path);
+ if (key_exists('plans', $response)){
+ $plans = ["plan" => $response['plans']];
+ } else {
+ $plans = ["plan" => []];
+ }
+
+ return Util::extractAttributeAsArray(
+ $plans,
+ 'plan'
+ );
+ }
+}
+class_alias('Braintree\PlanGateway', 'Braintree_PlanGateway');
diff --git a/lib/Braintree/ProcessorResponseTypes.php b/lib/Braintree/ProcessorResponseTypes.php
new file mode 100644
index 0000000..746343f
--- /dev/null
+++ b/lib/Braintree/ProcessorResponseTypes.php
@@ -0,0 +1,15 @@
+name = $name;
- $this->searchTerms = array();
+ $this->searchTerms = [];
}
- function greaterThanOrEqualTo($value)
+ public function greaterThanOrEqualTo($value)
{
$this->searchTerms['min'] = $value;
return $this;
}
- function lessThanOrEqualTo($value)
+ public function lessThanOrEqualTo($value)
{
$this->searchTerms['max'] = $value;
return $this;
}
- function is($value)
+ public function is($value)
{
$this->searchTerms['is'] = $value;
return $this;
}
- function between($min, $max)
+ public function between($min, $max)
{
return $this->greaterThanOrEqualTo($min)->lessThanOrEqualTo($max);
}
- function toParam()
+ public function toParam()
{
return $this->searchTerms;
}
}
+class_alias('Braintree\RangeNode', 'Braintree_RangeNode');
diff --git a/lib/Braintree/ResourceCollection.php b/lib/Braintree/ResourceCollection.php
index dc09cf1..2a5dd4d 100644
--- a/lib/Braintree/ResourceCollection.php
+++ b/lib/Braintree/ResourceCollection.php
@@ -1,20 +1,17 @@
- * $result = Braintree_Customer::all();
+ * $result = Customer::all();
*
* foreach($result as $transaction) {
* print_r($transaction->id);
@@ -23,12 +20,12 @@
*
* @package Braintree
* @subpackage Utility
- * @copyright 2010 Braintree Payment Solutions
*/
-class Braintree_ResourceCollection implements Iterator
+class ResourceCollection implements Iterator
{
- private $_index;
private $_batchIndex;
+ private $_ids;
+ private $_index;
private $_items;
private $_pageSize;
private $_pager;
@@ -38,8 +35,8 @@ class Braintree_ResourceCollection implements Iterator
*
* expects an array of attributes with literal keys
*
- * @param array $attributes
- * @param array $pagerAttribs
+ * @param array $response
+ * @param array $pager
*/
public function __construct($response, $pager)
{
@@ -64,7 +61,7 @@ public function current()
public function firstItem()
{
$ids = $this->_ids;
- $page = $this->_getPage(array($ids[0]));
+ $page = $this->_getPage([$ids[0]]);
return $page[0];
}
@@ -82,7 +79,7 @@ public function next()
}
/**
- * rewinds thtestIterateOverResultse collection to the first item when iterating with foreach
+ * rewinds the testIterateOverResults collection to the first item when iterating with foreach
*/
public function rewind()
{
@@ -115,7 +112,7 @@ private function _getNextPage()
{
if (empty($this->_ids))
{
- $this->_items = array();
+ $this->_items = [];
}
else
{
@@ -128,21 +125,32 @@ private function _getNextPage()
/**
* requests the next page of results for the collection
*
- * @return none
+ * @return void
*/
private function _getPage($ids)
{
- $className = $this->_pager['className'];
- $classMethod = $this->_pager['classMethod'];
- $methodArgs = array();
+ $object = $this->_pager['object'];
+ $method = $this->_pager['method'];
+ $methodArgs = [];
foreach ($this->_pager['methodArgs'] as $arg) {
array_push($methodArgs, $arg);
}
array_push($methodArgs, $ids);
return call_user_func_array(
- array($className, $classMethod),
+ [$object, $method],
$methodArgs
);
}
+
+ /**
+ * returns all IDs in the collection
+ *
+ * @return array
+ */
+ public function getIds()
+ {
+ return $this->_ids;
+ }
}
+class_alias('Braintree\ResourceCollection', 'Braintree_ResourceCollection');
diff --git a/lib/Braintree/Result/CreditCardVerification.php b/lib/Braintree/Result/CreditCardVerification.php
index 68bc75a..f0f91b3 100644
--- a/lib/Braintree/Result/CreditCardVerification.php
+++ b/lib/Braintree/Result/CreditCardVerification.php
@@ -1,11 +1,8 @@
_initializeFromArray($attributes);
}
+
/**
* initializes instance properties from the keys/values of an array
* @ignore
* @access protected
* @param $aAttribs array of properties to set - single level
- * @return none
+ * @return void
*/
private function _initializeFromArray($attributes)
{
+ if(isset($attributes['riskData']))
+ {
+ $attributes['riskData'] = RiskData::factory($attributes['riskData']);
+ }
+
$this->_attributes = $attributes;
foreach($attributes AS $name => $value) {
$varName = "_$name";
$this->$varName = $value;
- // $this->$varName = Braintree_Util::delimiterToCamelCase($value, '_');
}
}
+
/**
- *
* @ignore
*/
public function __get($name)
@@ -81,6 +84,17 @@ public function __get($name)
public function __toString()
{
return __CLASS__ . '[' .
- Braintree_Util::attributesToString($this->_attributes) .']';
+ Util::attributesToString($this->_attributes) . ']';
+ }
+
+ public static function allStatuses()
+ {
+ return [
+ CreditCardVerification::FAILED,
+ CreditCardVerification::GATEWAY_REJECTED,
+ CreditCardVerification::PROCESSOR_DECLINED,
+ CreditCardVerification::VERIFIED
+ ];
}
}
+class_alias('Braintree\Result\CreditCardVerification', 'Braintree_Result_CreditCardVerification');
diff --git a/lib/Braintree/Result/Error.php b/lib/Braintree/Result/Error.php
index 94f6bce..c0d8ba7 100644
--- a/lib/Braintree/Result/Error.php
+++ b/lib/Braintree/Result/Error.php
@@ -1,11 +1,12 @@
- * $result = Braintree_Transaction::void('abc123');
+ * $result = Transaction::void('abc123');
* if ($result->success) {
* // Successful Result
* } else {
- * // Braintree_Result_Error
+ * // Result\Error
* }
*
*
* @package Braintree
* @subpackage Result
- * @copyright 2010 Braintree Payment Solutions
*
* @property-read array $params original passed params
- * @property-read object $errors Braintree_Error_ErrorCollection
- * @property-read object $creditCardVerification credit card verification data
+ * @property-read \Braintree\Error\ErrorCollection $errors
+ * @property-read \Braintree\Result\CreditCardVerification $creditCardVerification credit card verification data
*/
-class Braintree_Result_Error extends Braintree
+class Error extends Base
{
- /**
- *
- * @var boolean always false
+ /**
+ * @var bool always false
*/
public $success = false;
@@ -54,10 +53,10 @@ public function valueForHtmlField($field)
$pieces = preg_split("/[\[\]]+/", $field, 0, PREG_SPLIT_NO_EMPTY);
$params = $this->params;
foreach(array_slice($pieces, 0, -1) as $key) {
- $params = $params[Braintree_Util::delimiterToCamelCase($key)];
+ $params = $params[Util::delimiterToCamelCase($key)];
}
if ($key != 'custom_fields') {
- $finalKey = Braintree_Util::delimiterToCamelCase(end($pieces));
+ $finalKey = Util::delimiterToCamelCase(end($pieces));
} else {
$finalKey = end($pieces);
}
@@ -73,45 +72,52 @@ public function valueForHtmlField($field)
public function __construct($response)
{
$this->_attributes = $response;
- $this->_set('errors', new Braintree_Error_ErrorCollection($response['errors']));
+ $this->_set('errors', new ErrorCollection($response['errors']));
if(isset($response['verification'])) {
- $this->_set('creditCardVerification', new Braintree_Result_CreditCardVerification($response['verification']));
+ $this->_set('creditCardVerification', new CreditCardVerification($response['verification']));
} else {
$this->_set('creditCardVerification', null);
}
if(isset($response['transaction'])) {
- $this->_set('transaction', Braintree_Transaction::factory($response['transaction']));
+ $this->_set('transaction', Transaction::factory($response['transaction']));
} else {
$this->_set('transaction', null);
}
if(isset($response['subscription'])) {
- $this->_set('subscription', Braintree_Subscription::factory($response['subscription']));
+ $this->_set('subscription', Subscription::factory($response['subscription']));
} else {
$this->_set('subscription', null);
}
if(isset($response['merchantAccount'])) {
- $this->_set('merchantAccount', Braintree_MerchantAccount::factory($response['merchantAccount']));
+ $this->_set('merchantAccount', MerchantAccount::factory($response['merchantAccount']));
} else {
$this->_set('merchantAccount', null);
}
+
+ if(isset($response['verification'])) {
+ $this->_set('verification', new CreditCardVerification($response['verification']));
+ } else {
+ $this->_set('verification', null);
+ }
}
/**
* create a printable representation of the object as:
* ClassName[property=value, property=value]
* @ignore
- * @return var
+ * @return string
*/
public function __toString()
{
- $output = Braintree_Util::attributesToString($this->_attributes);
+ $output = Util::attributesToString($this->_attributes);
if (isset($this->_creditCardVerification)) {
$output .= sprintf('%s', $this->_creditCardVerification);
}
- return __CLASS__ .'['.$output.']';
+ return __CLASS__ .'[' . $output . ']';
}
}
+class_alias('Braintree\Result\Error', 'Braintree_Result_Error');
diff --git a/lib/Braintree/Result/Successful.php b/lib/Braintree/Result/Successful.php
index 8b7d16c..69f087c 100644
--- a/lib/Braintree/Result/Successful.php
+++ b/lib/Braintree/Result/Successful.php
@@ -1,11 +1,8 @@
customer like so:
*
*
- * $result = Braintree_Customer::create(array('first_name' => "John"));
+ * $result = Customer::create(array('first_name' => "John"));
* if ($result->success) {
- * // Braintree_Result_Successful
+ * // Successful
* echo "Created customer {$result->customer->id}";
* } else {
- * // Braintree_Result_Error
+ * // Error
* }
*
*
*
* @package Braintree
* @subpackage Result
- * @copyright 2010 Braintree Payment Solutions
*/
-class Braintree_Result_Successful extends Braintree_Instance
+class Successful extends Instance
{
/**
*
@@ -42,28 +38,33 @@ class Braintree_Result_Successful extends Braintree_Instance
*
* @var string stores the internal name of the object providing access to
*/
- private $_returnObjectName;
+ private $_returnObjectNames;
/**
* @ignore
- * @param string $classToReturn name of class to instantiate
+ * @param array|null $objsToReturn
+ * @param array|null $propertyNames
*/
- public function __construct($objToReturn = null)
+ public function __construct($objsToReturn = [], $propertyNames = [])
{
- if(!empty($objToReturn)) {
- // get a lowercase direct name for the property
- $property = Braintree_Util::cleanClassName(
- get_class($objToReturn)
- );
+ // Sanitize arguments (preserves backwards compatibility)
+ if (!is_array($objsToReturn)) { $objsToReturn = [$objsToReturn]; }
+ if (!is_array($propertyNames)) { $propertyNames = [$propertyNames]; }
+
+ $objects = $this->_mapPropertyNamesToObjsToReturn($propertyNames, $objsToReturn);
+ $this->_attributes = [];
+ $this->_returnObjectNames = [];
+
+ foreach ($objects as $propertyName => $objToReturn) {
+
// save the name for indirect access
- $this->_returnObjectName = $property;
+ array_push($this->_returnObjectNames, $propertyName);
// create the property!
- $this->$property = $objToReturn;
+ $this->$propertyName = $objToReturn;
}
}
-
/**
*
* @ignore
@@ -71,8 +72,21 @@ public function __construct($objToReturn = null)
*/
public function __toString()
{
- $returnObject = $this->_returnObjectName;
- return __CLASS__ . '['.$this->$returnObject->__toString().']';
+ $objects = [];
+ foreach ($this->_returnObjectNames as $returnObjectName) {
+ array_push($objects, $returnObjectName);
+ }
+ return __CLASS__ . '[' . implode(', ', $objects) . ']';
}
+ private function _mapPropertyNamesToObjsToReturn($propertyNames, $objsToReturn) {
+ if(count($objsToReturn) != count($propertyNames)) {
+ $propertyNames = [];
+ foreach ($objsToReturn as $obj) {
+ array_push($propertyNames, Util::cleanClassName(get_class($obj)));
+ }
+ }
+ return array_combine($propertyNames, $objsToReturn);
+ }
}
+class_alias('Braintree\Result\Successful', 'Braintree_Result_Successful');
diff --git a/lib/Braintree/Result/UsBankAccountVerification.php b/lib/Braintree/Result/UsBankAccountVerification.php
new file mode 100644
index 0000000..3c94c9b
--- /dev/null
+++ b/lib/Braintree/Result/UsBankAccountVerification.php
@@ -0,0 +1,112 @@
+_initializeFromArray($attributes);
+
+ $usBankAccount = isset($attributes['usBankAccount']) ?
+ UsBankAccount::factory($attributes['usBankAccount']) :
+ null;
+ $this->usBankAccount = $usBankAccount;
+ }
+
+ /**
+ * initializes instance properties from the keys/values of an array
+ * @ignore
+ * @access protected
+ * @param $aAttribs array of properties to set - single level
+ * @return void
+ */
+ private function _initializeFromArray($attributes)
+ {
+ $this->_attributes = $attributes;
+ foreach($attributes AS $name => $value) {
+ $varName = "_$name";
+ $this->$varName = $value;
+ }
+ }
+
+ /**
+ * @ignore
+ */
+ public function __get($name)
+ {
+ $varName = "_$name";
+ return isset($this->$varName) ? $this->$varName : null;
+ }
+
+ /**
+ * returns a string representation of the customer
+ * @return string
+ */
+ public function __toString()
+ {
+ return __CLASS__ . '[' .
+ Util::attributesToString($this->_attributes) . ']';
+ }
+
+ public static function allStatuses()
+ {
+ return [
+ UsBankAccountVerification::FAILED,
+ UsBankAccountVerification::GATEWAY_REJECTED,
+ UsBankAccountVerification::PROCESSOR_DECLINED,
+ UsBankAccountVerification::VERIFIED,
+ UsBankAccountVerification::PENDING,
+ ];
+ }
+
+ public static function allVerificationMethods()
+ {
+ return [
+ UsBankAccountVerification::TOKENIZED_CHECK,
+ UsBankAccountVerification::NETWORK_CHECK,
+ UsBankAccountVerification::INDEPENDENT_CHECK,
+ UsBankAccountVerification::MICRO_TRANSFERS,
+ ];
+ }
+}
+class_alias('Braintree\Result\UsBankAccountVerification', 'Braintree_Result_UsBankAccountVerification');
diff --git a/lib/Braintree/RevokedPaymentMethodMetadata.php b/lib/Braintree/RevokedPaymentMethodMetadata.php
new file mode 100644
index 0000000..85a2036
--- /dev/null
+++ b/lib/Braintree/RevokedPaymentMethodMetadata.php
@@ -0,0 +1,53 @@
+== More information ==
+ *
+ *
+ * @package Braintree
+ * @category Resources
+ *
+ * @property-read string $customerId
+ * @property-read string $token
+ * @property-read string $revokedPaymentMethod
+ */
+class RevokedPaymentMethodMetadata extends Base
+{
+ /**
+ * factory method: returns an instance of RevokedPaymentMethodMetadata
+ * to the requesting method, with populated properties
+ *
+ * @ignore
+ * @return RevokedPaymentMethodMetadata
+ */
+ public static function factory($attributes)
+ {
+ $instance = new self();
+ $instance->revokedPaymentMethod = PaymentMethodParser::parsePaymentMethod($attributes);
+ $instance->customerId = $instance->revokedPaymentMethod->customerId;
+ $instance->token = $instance->revokedPaymentMethod->token;
+ return $instance;
+ }
+
+ /**
+ * create a printable representation of the object as:
+ * ClassName[property=value, property=value]
+ * @return string
+ */
+ public function __toString()
+ {
+ return __CLASS__ . '[' .
+ Util::attributesToString($this->_attributes) . ']';
+ }
+}
+class_alias('Braintree\RevokedPaymentMethodMetadata', 'Braintree_RevokedPaymentMethodMetadata');
diff --git a/lib/Braintree/RiskData.php b/lib/Braintree/RiskData.php
new file mode 100644
index 0000000..11552e1
--- /dev/null
+++ b/lib/Braintree/RiskData.php
@@ -0,0 +1,35 @@
+_initialize($attributes);
+
+ return $instance;
+ }
+
+ protected function _initialize($attributes)
+ {
+ $this->_attributes = $attributes;
+ }
+
+ /**
+ * returns a string representation of the risk data
+ * @return string
+ */
+ public function __toString()
+ {
+ return __CLASS__ . '[' .
+ Util::attributesToString($this->_attributes) .']';
+ }
+
+}
+class_alias('Braintree\RiskData', 'Braintree_RiskData');
diff --git a/lib/Braintree/SamsungPayCard.php b/lib/Braintree/SamsungPayCard.php
new file mode 100644
index 0000000..f774c98
--- /dev/null
+++ b/lib/Braintree/SamsungPayCard.php
@@ -0,0 +1,138 @@
+default;
+ }
+
+ /**
+ * checks whether the card is expired based on the current date
+ *
+ * @return boolean
+ */
+ public function isExpired()
+ {
+ return $this->expired;
+ }
+
+ /**
+ * sets instance properties from an array of values
+ *
+ * @access protected
+ * @param array $creditCardAttribs array of creditcard data
+ * @return void
+ */
+ protected function _initialize($creditCardAttribs)
+ {
+ // set the attributes
+ $this->_attributes = $creditCardAttribs;
+
+ // map each address into its own object
+ $billingAddress = isset($creditCardAttribs['billingAddress']) ?
+ Address::factory($creditCardAttribs['billingAddress']) :
+ null;
+
+ $subscriptionArray = [];
+ if (isset($creditCardAttribs['subscriptions'])) {
+ foreach ($creditCardAttribs['subscriptions'] AS $subscription) {
+ $subscriptionArray[] = Subscription::factory($subscription);
+ }
+ }
+
+ $this->_set('subscriptions', $subscriptionArray);
+ $this->_set('billingAddress', $billingAddress);
+ $this->_set('expirationDate', $this->expirationMonth . '/' . $this->expirationYear);
+ $this->_set('maskedNumber', $this->bin . '******' . $this->last4);
+ }
+
+ /**
+ * returns false if comparing object is not a SamsungPayCard,
+ * or is a SamsungPayCard with a different id
+ *
+ * @param object $otherSamsungPayCard customer to compare against
+ * @return boolean
+ */
+ public function isEqual($otherSamsungPayCard)
+ {
+ return !($otherSamsungPayCard instanceof self) ? false : $this->token === $otherSamsungPayCard->token;
+ }
+
+ /**
+ * create a printable representation of the object as:
+ * ClassName[property=value, property=value]
+ * @return string
+ */
+ public function __toString()
+ {
+ return __CLASS__ . '[' .
+ Util::attributesToString($this->_attributes) .']';
+ }
+
+ /**
+ * factory method: returns an instance of SamsungPayCard
+ * to the requesting method, with populated properties
+ *
+ * @ignore
+ * @return SamsungPayCard
+ */
+ public static function factory($attributes)
+ {
+ $defaultAttributes = [
+ 'bin' => '',
+ 'expirationMonth' => '',
+ 'expirationYear' => '',
+ 'last4' => '',
+ ];
+
+ $instance = new self();
+ $instance->_initialize(array_merge($defaultAttributes, $attributes));
+ return $instance;
+ }
+}
+class_alias('Braintree\SamsungPayCard', 'Braintree_SamsungPayCard');
diff --git a/lib/Braintree/SettlementBatchSummary.php b/lib/Braintree/SettlementBatchSummary.php
index 98591a7..2906f20 100644
--- a/lib/Braintree/SettlementBatchSummary.php
+++ b/lib/Braintree/SettlementBatchSummary.php
@@ -1,57 +1,16 @@
$settlement_date);
- if (isset($groupByCustomField))
- {
- $criteria['group_by_custom_field'] = $groupByCustomField;
- }
- $params = array('settlement_batch_summary' => $criteria);
- $response = Braintree_Http::post('/settlement_batch_summary', $params);
-
- if (isset($groupByCustomField))
- {
- $response['settlementBatchSummary']['records'] = self::_underscoreCustomField(
- $groupByCustomField,
- $response['settlementBatchSummary']['records']
- );
- }
-
- return self::_verifyGatewayResponse($response);
- }
-
- private static function _underscoreCustomField($groupByCustomField, $records)
- {
- $updatedRecords = array();
-
- foreach ($records as $record)
- {
- $camelized = Braintree_Util::delimiterToCamelCase($groupByCustomField);
- $record[$groupByCustomField] = $record[$camelized];
- unset($record[$camelized]);
- $updatedRecords[] = $record;
- }
-
- return $updatedRecords;
- }
-
- private static function _verifyGatewayResponse($response)
- {
- if (isset($response['settlementBatchSummary'])) {
- return new Braintree_Result_Successful(
- self::factory($response['settlementBatchSummary'])
- );
- } else if (isset($response['apiErrorResponse'])) {
- return new Braintree_Result_Error($response['apiErrorResponse']);
- } else {
- throw new Braintree_Exception_Unexpected(
- "Expected settlementBatchSummary or apiErrorResponse"
- );
- }
- }
+namespace Braintree;
+/**
+ * @property-read array $records
+ */
+class SettlementBatchSummary extends Base
+{
+ /**
+ *
+ * @param array $attributes
+ * @return SettlementBatchSummary
+ */
public static function factory($attributes)
{
$instance = new self();
@@ -61,6 +20,7 @@ public static function factory($attributes)
/**
* @ignore
+ * @param array $attributes
*/
protected function _initialize($attributes)
{
@@ -71,4 +31,18 @@ public function records()
{
return $this->_attributes['records'];
}
+
+
+ /**
+ * static method redirecting to gateway
+ *
+ * @param string $settlement_date Date YYYY-MM-DD
+ * @param string $groupByCustomField
+ * @return Result\Successful|Result\Error
+ */
+ public static function generate($settlement_date, $groupByCustomField = NULL)
+ {
+ return Configuration::gateway()->settlementBatchSummary()->generate($settlement_date, $groupByCustomField);
+ }
}
+class_alias('Braintree\SettlementBatchSummary', 'Braintree_SettlementBatchSummary');
diff --git a/lib/Braintree/SettlementBatchSummaryGateway.php b/lib/Braintree/SettlementBatchSummaryGateway.php
new file mode 100644
index 0000000..e70db87
--- /dev/null
+++ b/lib/Braintree/SettlementBatchSummaryGateway.php
@@ -0,0 +1,106 @@
+_gateway = $gateway;
+ $this->_config = $gateway->config;
+ $this->_config->assertHasAccessTokenOrKeys();
+ $this->_http = new Http($gateway->config);
+ }
+
+ /**
+ *
+ * @param string $settlement_date
+ * @param string $groupByCustomField
+ * @return SettlementBatchSummary|Result\Error
+ */
+ public function generate($settlement_date, $groupByCustomField = NULL)
+ {
+ $criteria = ['settlement_date' => $settlement_date];
+ if (isset($groupByCustomField))
+ {
+ $criteria['group_by_custom_field'] = $groupByCustomField;
+ }
+ $params = ['settlement_batch_summary' => $criteria];
+ $path = $this->_config->merchantPath() . '/settlement_batch_summary';
+ $response = $this->_http->post($path, $params);
+
+ if (isset($groupByCustomField))
+ {
+ $response['settlementBatchSummary']['records'] = $this->_underscoreCustomField(
+ $groupByCustomField,
+ $response['settlementBatchSummary']['records']
+ );
+ }
+
+ return $this->_verifyGatewayResponse($response);
+ }
+
+ /**
+ *
+ * @param string $groupByCustomField
+ * @param array $records
+ * @return array
+ */
+ private function _underscoreCustomField($groupByCustomField, $records)
+ {
+ $updatedRecords = [];
+
+ foreach ($records as $record)
+ {
+ $camelized = Util::delimiterToCamelCase($groupByCustomField);
+ $record[$groupByCustomField] = $record[$camelized];
+ unset($record[$camelized]);
+ $updatedRecords[] = $record;
+ }
+
+ return $updatedRecords;
+ }
+
+ /**
+ *
+ * @param array $response
+ * @return Result\Successful|Result\Error
+ * @throws Exception\Unexpected
+ */
+ private function _verifyGatewayResponse($response)
+ {
+ if (isset($response['settlementBatchSummary'])) {
+ return new Result\Successful(
+ SettlementBatchSummary::factory($response['settlementBatchSummary'])
+ );
+ } else if (isset($response['apiErrorResponse'])) {
+ return new Result\Error($response['apiErrorResponse']);
+ } else {
+ throw new Exception\Unexpected(
+ "Expected settlementBatchSummary or apiErrorResponse"
+ );
+ }
+ }
+}
+class_alias('Braintree\SettlementBatchSummaryGateway', 'Braintree_SettlementBatchSummaryGateway');
diff --git a/lib/Braintree/SignatureService.php b/lib/Braintree/SignatureService.php
new file mode 100644
index 0000000..5e12369
--- /dev/null
+++ b/lib/Braintree/SignatureService.php
@@ -0,0 +1,24 @@
+key = $key;
+ $this->digest = $digest;
+ }
+
+ public function sign($payload)
+ {
+ return $this->hash($payload) . "|" . $payload;
+ }
+
+ public function hash($data)
+ {
+ return call_user_func($this->digest, $this->key, $data);
+ }
+
+}
+class_alias('Braintree\SignatureService', 'Braintree_SignatureService');
diff --git a/lib/Braintree/Subscription.php b/lib/Braintree/Subscription.php
index 12bbb54..8530b36 100644
--- a/lib/Braintree/Subscription.php
+++ b/lib/Braintree/Subscription.php
@@ -1,17 +1,49 @@
== More information ==
*
- * For more detailed information on Subscriptions, see {@link http://www.braintreepayments.com/gateway/subscription-api http://www.braintreepaymentsolutions.com/gateway/subscription-api}
+ * For more detailed information on Subscriptions, see {@link https://developers.braintreepayments.com/reference/response/subscription/php https://developers.braintreepayments.com/reference/response/subscription/php}
*
* PHP Version 5
*
* @package Braintree
- * @copyright 2010 Braintree Payment Solutions
+ *
+ * @property-read \Braintree\Addon[] $addOns
+ * @property-read string $balance
+ * @property-read int $billingDayOfMonth
+ * @property-read \DateTime $billingPeriodEndDate
+ * @property-read \DateTime $billingPeriodStartDate
+ * @property-read \DateTime $createdAt
+ * @property-read int $currentBillingCycle
+ * @property-read int|null $daysPastDue
+ * @property-read string|null $description
+ * @property-read \Braintree\Descriptor|null $descriptor
+ * @property-read \Braintree\Discount[] $discounts
+ * @property-read int $failureCount
+ * @property-read \DateTime $firstBillingDate
+ * @property-read string $id
+ * @property-read string $merchantAccountId
+ * @property-read boolean $neverExpires
+ * @property-read string $nextBillingPeriodAmount
+ * @property-read \DateTime $nextBillingDate
+ * @property-read int|null $numberOfBillingCycles
+ * @property-read \DateTime|null $paidThroughDate
+ * @property-read string $paymentMethodToken
+ * @property-read string $planId
+ * @property-read string $price
+ * @property-read string $status
+ * @property-read \Braintree\Subscription\StatusDetails[] $statusHistory
+ * @property-read \Braintree\Transaction[] $transactions
+ * @property-read int $trialDuration
+ * @property-read string $trialDurationUnit
+ * @property-read boolean $trialPeriod
+ * @property-read \DateTime $updatedAt
*/
-class Braintree_Subscription extends Braintree
+class Subscription extends Base
{
const ACTIVE = 'Active';
const CANCELED = 'Canceled';
@@ -19,12 +51,10 @@ class Braintree_Subscription extends Braintree
const PAST_DUE = 'Past Due';
const PENDING = 'Pending';
- public static function create($attributes)
- {
- Braintree_Util::verifyKeys(self::_createSignature(), $attributes);
- $response = Braintree_Http::post('/subscriptions', array('subscription' => $attributes));
- return self::_verifyGatewayResponse($response);
- }
+ // Subscription Sources
+ const API = 'api';
+ const CONTROL_PANEL = 'control_panel';
+ const RECURRING = 'recurring';
/**
* @ignore
@@ -37,138 +67,6 @@ public static function factory($attributes)
return $instance;
}
- public static function find($id)
- {
- self::_validateId($id);
-
- try {
- $response = Braintree_Http::get('/subscriptions/' . $id);
- return self::factory($response['subscription']);
- } catch (Braintree_Exception_NotFound $e) {
- throw new Braintree_Exception_NotFound('subscription with id ' . $id . ' not found');
- }
-
- }
-
- public static function search($query)
- {
- $criteria = array();
- foreach ($query as $term) {
- $criteria[$term->name] = $term->toparam();
- }
-
-
- $response = Braintree_Http::post('/subscriptions/advanced_search_ids', array('search' => $criteria));
- $pager = array(
- 'className' => __CLASS__,
- 'classMethod' => 'fetch',
- 'methodArgs' => array($query)
- );
-
- return new Braintree_ResourceCollection($response, $pager);
- }
-
- public static function fetch($query, $ids)
- {
- $criteria = array();
- foreach ($query as $term) {
- $criteria[$term->name] = $term->toparam();
- }
- $criteria["ids"] = Braintree_SubscriptionSearch::ids()->in($ids)->toparam();
- $response = Braintree_Http::post('/subscriptions/advanced_search', array('search' => $criteria));
-
- return Braintree_Util::extractAttributeAsArray(
- $response['subscriptions'],
- 'subscription'
- );
- }
-
- public static function update($subscriptionId, $attributes)
- {
- Braintree_Util::verifyKeys(self::_updateSignature(), $attributes);
- $response = Braintree_Http::put(
- '/subscriptions/' . $subscriptionId,
- array('subscription' => $attributes)
- );
- return self::_verifyGatewayResponse($response);
- }
-
- public static function retryCharge($subscriptionId, $amount = null)
- {
- $transaction_params = array('type' => Braintree_Transaction::SALE,
- 'subscriptionId' => $subscriptionId);
- if (isset($amount)) {
- $transaction_params['amount'] = $amount;
- }
-
- $response = Braintree_Http::post(
- '/transactions',
- array('transaction' => $transaction_params));
- return self::_verifyGatewayResponse($response);
- }
-
- public static function cancel($subscriptionId)
- {
- $response = Braintree_Http::put('/subscriptions/' . $subscriptionId . '/cancel');
- return self::_verifyGatewayResponse($response);
- }
-
- private static function _createSignature()
- {
- return array_merge(
- array(
- 'billingDayOfMonth',
- 'firstBillingDate',
- 'id',
- 'merchantAccountId',
- 'neverExpires',
- 'numberOfBillingCycles',
- 'paymentMethodToken',
- 'planId',
- 'price',
- 'trialDuration',
- 'trialDurationUnit',
- 'trialPeriod',
- array('descriptor' => array('name', 'phone')),
- array('options' => array('doNotInheritAddOnsOrDiscounts', 'startImmediately')),
- ),
- self::_addOnDiscountSignature()
- );
- }
-
- private static function _updateSignature()
- {
- return array_merge(
- array(
- 'merchantAccountId', 'numberOfBillingCycles', 'paymentMethodToken', 'planId',
- 'id', 'neverExpires', 'price',
- array('descriptor' => array('name', 'phone')),
- array('options' => array('prorateCharges', 'replaceAllAddOnsAndDiscounts', 'revertSubscriptionOnProrationFailure')),
- ),
- self::_addOnDiscountSignature()
- );
- }
-
- private static function _addOnDiscountSignature()
- {
- return array(
- array(
- 'addOns' => array(
- array('add' => array('amount', 'inheritedFromId', 'neverExpires', 'numberOfBillingCycles', 'quantity')),
- array('update' => array('amount', 'existingId', 'neverExpires', 'numberOfBillingCycles', 'quantity')),
- array('remove' => array('_anyKey_')),
- )
- ),
- array(
- 'discounts' => array(
- array('add' => array('amount', 'inheritedFromId', 'neverExpires', 'numberOfBillingCycles', 'quantity')),
- array('update' => array('amount', 'existingId', 'neverExpires', 'numberOfBillingCycles', 'quantity')),
- array('remove' => array('_anyKey_')),
- )
- )
- );
- }
-
/**
* @ignore
*/
@@ -176,81 +74,102 @@ protected function _initialize($attributes)
{
$this->_attributes = $attributes;
- $addOnArray = array();
+ $addOnArray = [];
if (isset($attributes['addOns'])) {
foreach ($attributes['addOns'] AS $addOn) {
- $addOnArray[] = Braintree_AddOn::factory($addOn);
+ $addOnArray[] = AddOn::factory($addOn);
}
}
$this->_attributes['addOns'] = $addOnArray;
- $discountArray = array();
+ $discountArray = [];
if (isset($attributes['discounts'])) {
foreach ($attributes['discounts'] AS $discount) {
- $discountArray[] = Braintree_Discount::factory($discount);
+ $discountArray[] = Discount::factory($discount);
}
}
$this->_attributes['discounts'] = $discountArray;
if (isset($attributes['descriptor'])) {
- $this->_set('descriptor', new Braintree_Descriptor($attributes['descriptor']));
+ $this->_set('descriptor', new Descriptor($attributes['descriptor']));
}
- $transactionArray = array();
+ if (isset($attributes['description'])) {
+ $this->_set('description', $attributes['description']);
+ }
+
+ $statusHistory = [];
+ if (isset($attributes['statusHistory'])) {
+ foreach ($attributes['statusHistory'] AS $history) {
+ $statusHistory[] = new Subscription\StatusDetails($history);
+ }
+ }
+ $this->_attributes['statusHistory'] = $statusHistory;
+
+ $transactionArray = [];
if (isset($attributes['transactions'])) {
foreach ($attributes['transactions'] AS $transaction) {
- $transactionArray[] = Braintree_Transaction::factory($transaction);
+ $transactionArray[] = Transaction::factory($transaction);
}
}
$this->_attributes['transactions'] = $transactionArray;
}
/**
- * @ignore
+ * returns a string representation of the customer
+ * @return string
*/
- private static function _validateId($id = null) {
- if (empty($id)) {
- throw new InvalidArgumentException(
- 'expected subscription id to be set'
- );
- }
- if (!preg_match('/^[0-9A-Za-z_-]+$/', $id)) {
- throw new InvalidArgumentException(
- $id . ' is an invalid subscription id.'
- );
+ public function __toString()
+ {
+ $excludedAttributes = ['statusHistory'];
+
+ $displayAttributes = [];
+ foreach($this->_attributes as $key => $val) {
+ if (!in_array($key, $excludedAttributes)) {
+ $displayAttributes[$key] = $val;
+ }
}
+
+ return __CLASS__ . '[' .
+ Util::attributesToString($displayAttributes) .']';
}
- /**
- * @ignore
- */
- private static function _verifyGatewayResponse($response)
+
+
+ // static methods redirecting to gateway
+
+ public static function create($attributes)
{
- if (isset($response['subscription'])) {
- return new Braintree_Result_Successful(
- self::factory($response['subscription'])
- );
- } else if (isset($response['transaction'])) {
- // return a populated instance of Braintree_Transaction, for subscription retryCharge
- return new Braintree_Result_Successful(
- Braintree_Transaction::factory($response['transaction'])
- );
- } else if (isset($response['apiErrorResponse'])) {
- return new Braintree_Result_Error($response['apiErrorResponse']);
- } else {
- throw new Braintree_Exception_Unexpected(
- "Expected subscription, transaction, or apiErrorResponse"
- );
- }
+ return Configuration::gateway()->subscription()->create($attributes);
}
- /**
- * returns a string representation of the customer
- * @return string
- */
- public function __toString()
+ public static function find($id)
{
- return __CLASS__ . '[' .
- Braintree_Util::attributesToString($this->_attributes) .']';
+ return Configuration::gateway()->subscription()->find($id);
+ }
+
+ public static function search($query)
+ {
+ return Configuration::gateway()->subscription()->search($query);
}
+ public static function fetch($query, $ids)
+ {
+ return Configuration::gateway()->subscription()->fetch($query, $ids);
+ }
+
+ public static function update($subscriptionId, $attributes)
+ {
+ return Configuration::gateway()->subscription()->update($subscriptionId, $attributes);
+ }
+
+ public static function retryCharge($subscriptionId, $amount = null, $submitForSettlement = false)
+ {
+ return Configuration::gateway()->subscription()->retryCharge($subscriptionId, $amount, $submitForSettlement);
+ }
+
+ public static function cancel($subscriptionId)
+ {
+ return Configuration::gateway()->subscription()->cancel($subscriptionId);
+ }
}
+class_alias('Braintree\Subscription', 'Braintree_Subscription');
diff --git a/lib/Braintree/Subscription/StatusDetails.php b/lib/Braintree/Subscription/StatusDetails.php
new file mode 100644
index 0000000..9f588ce
--- /dev/null
+++ b/lib/Braintree/Subscription/StatusDetails.php
@@ -0,0 +1,24 @@
+== More information ==
+ *
+ * For more detailed information on Subscriptions, see {@link https://developers.braintreepayments.com/reference/response/subscription/php https://developers.braintreepayments.com/reference/response/subscription/php}
+ *
+ * PHP Version 5
+ *
+ * @package Braintree
+ */
+class SubscriptionGateway
+{
+ private $_gateway;
+ private $_config;
+ private $_http;
+
+ public function __construct($gateway)
+ {
+ $this->_gateway = $gateway;
+ $this->_config = $gateway->config;
+ $this->_config->assertHasAccessTokenOrKeys();
+ $this->_http = new Http($gateway->config);
+ }
+
+ public function create($attributes)
+ {
+ Util::verifyKeys(self::_createSignature(), $attributes);
+ $path = $this->_config->merchantPath() . '/subscriptions';
+ $response = $this->_http->post($path, ['subscription' => $attributes]);
+ return $this->_verifyGatewayResponse($response);
+ }
+
+ public function find($id)
+ {
+ $this->_validateId($id);
+
+ try {
+ $path = $this->_config->merchantPath() . '/subscriptions/' . $id;
+ $response = $this->_http->get($path);
+ return Subscription::factory($response['subscription']);
+ } catch (Exception\NotFound $e) {
+ throw new Exception\NotFound('subscription with id ' . $id . ' not found');
+ }
+
+ }
+
+ public function search($query)
+ {
+ $criteria = [];
+ foreach ($query as $term) {
+ $criteria[$term->name] = $term->toparam();
+ }
+
+
+ $path = $this->_config->merchantPath() . '/subscriptions/advanced_search_ids';
+ $response = $this->_http->post($path, ['search' => $criteria]);
+ $pager = [
+ 'object' => $this,
+ 'method' => 'fetch',
+ 'methodArgs' => [$query]
+ ];
+
+ return new ResourceCollection($response, $pager);
+ }
+
+ public function fetch($query, $ids)
+ {
+ $criteria = [];
+ foreach ($query as $term) {
+ $criteria[$term->name] = $term->toparam();
+ }
+ $criteria["ids"] = SubscriptionSearch::ids()->in($ids)->toparam();
+ $path = $this->_config->merchantPath() . '/subscriptions/advanced_search';
+ $response = $this->_http->post($path, ['search' => $criteria]);
+
+ return Util::extractAttributeAsArray(
+ $response['subscriptions'],
+ 'subscription'
+ );
+ }
+
+ public function update($subscriptionId, $attributes)
+ {
+ Util::verifyKeys(self::_updateSignature(), $attributes);
+ $path = $this->_config->merchantPath() . '/subscriptions/' . $subscriptionId;
+ $response = $this->_http->put($path, ['subscription' => $attributes]);
+ return $this->_verifyGatewayResponse($response);
+ }
+
+ public function retryCharge($subscriptionId, $amount = null, $submitForSettlement = false)
+ {
+ $transaction_params = ['type' => Transaction::SALE,
+ 'subscriptionId' => $subscriptionId];
+ if (isset($amount)) {
+ $transaction_params['amount'] = $amount;
+ }
+ if ($submitForSettlement) {
+ $transaction_params['options'] = ['submitForSettlement' => $submitForSettlement];
+ }
+
+ $path = $this->_config->merchantPath() . '/transactions';
+ $response = $this->_http->post($path, ['transaction' => $transaction_params]);
+ return $this->_verifyGatewayResponse($response);
+ }
+
+ public function cancel($subscriptionId)
+ {
+ $path = $this->_config->merchantPath() . '/subscriptions/' . $subscriptionId . '/cancel';
+ $response = $this->_http->put($path);
+ return $this->_verifyGatewayResponse($response);
+ }
+
+ private static function _createSignature()
+ {
+ return array_merge(
+ [
+ 'billingDayOfMonth',
+ 'firstBillingDate',
+ 'createdAt',
+ 'updatedAt',
+ 'id',
+ 'merchantAccountId',
+ 'neverExpires',
+ 'numberOfBillingCycles',
+ 'paymentMethodToken',
+ 'paymentMethodNonce',
+ 'planId',
+ 'price',
+ 'trialDuration',
+ 'trialDurationUnit',
+ 'trialPeriod',
+ ['descriptor' => ['name', 'phone', 'url']],
+ ['options' => [
+ 'doNotInheritAddOnsOrDiscounts',
+ 'startImmediately',
+ ['paypal' => ['description']]
+ ]],
+ ],
+ self::_addOnDiscountSignature()
+ );
+ }
+
+ private static function _updateSignature()
+ {
+ return array_merge(
+ [
+ 'merchantAccountId', 'numberOfBillingCycles', 'paymentMethodToken', 'planId',
+ 'paymentMethodNonce', 'id', 'neverExpires', 'price',
+ ['descriptor' => ['name', 'phone', 'url']],
+ ['options' => [
+ 'prorateCharges',
+ 'replaceAllAddOnsAndDiscounts',
+ 'revertSubscriptionOnProrationFailure',
+ ['paypal' => ['description']]
+ ]],
+ ],
+ self::_addOnDiscountSignature()
+ );
+ }
+
+ private static function _addOnDiscountSignature()
+ {
+ return [
+ [
+ 'addOns' => [
+ ['add' => ['amount', 'inheritedFromId', 'neverExpires', 'numberOfBillingCycles', 'quantity']],
+ ['update' => ['amount', 'existingId', 'neverExpires', 'numberOfBillingCycles', 'quantity']],
+ ['remove' => ['_anyKey_']],
+ ]
+ ],
+ [
+ 'discounts' => [
+ ['add' => ['amount', 'inheritedFromId', 'neverExpires', 'numberOfBillingCycles', 'quantity']],
+ ['update' => ['amount', 'existingId', 'neverExpires', 'numberOfBillingCycles', 'quantity']],
+ ['remove' => ['_anyKey_']],
+ ]
+ ]
+ ];
+ }
+
+ /**
+ * @ignore
+ */
+ private function _validateId($id = null) {
+ if (empty($id)) {
+ throw new InvalidArgumentException(
+ 'expected subscription id to be set'
+ );
+ }
+ if (!preg_match('/^[0-9A-Za-z_-]+$/', $id)) {
+ throw new InvalidArgumentException(
+ $id . ' is an invalid subscription id.'
+ );
+ }
+ }
+
+ /**
+ * @ignore
+ */
+ private function _verifyGatewayResponse($response)
+ {
+ if (isset($response['subscription'])) {
+ return new Result\Successful(
+ Subscription::factory($response['subscription'])
+ );
+ } else if (isset($response['transaction'])) {
+ // return a populated instance of Transaction, for subscription retryCharge
+ return new Result\Successful(
+ Transaction::factory($response['transaction'])
+ );
+ } else if (isset($response['apiErrorResponse'])) {
+ return new Result\Error($response['apiErrorResponse']);
+ } else {
+ throw new Exception\Unexpected(
+ "Expected subscription, transaction, or apiErrorResponse"
+ );
+ }
+ }
+}
+class_alias('Braintree\SubscriptionGateway', 'Braintree_SubscriptionGateway');
diff --git a/lib/Braintree/SubscriptionSearch.php b/lib/Braintree/SubscriptionSearch.php
index 01cba1a..ba2a5ef 100644
--- a/lib/Braintree/SubscriptionSearch.php
+++ b/lib/Braintree/SubscriptionSearch.php
@@ -1,64 +1,72 @@
'378734493671000',
'Discover' => '6011000990139424',
'MasterCard' => '5105105105105100',
'Visa' => '4000111111111115',
- );
+ ];
+
+ public static $amexPayWithPoints = [
+ 'Success' => "371260714673002",
+ 'IneligibleCard' => "378267515471109",
+ 'InsufficientPoints' => "371544868764018",
+ ];
+ public static $disputes = [
+ 'Chargeback' => '4023898493988028',
+ ];
public static function getAll()
{
return array_merge(
self::$amExes,
self::$discoverCards,
+ self::$eloCards,
self::$masterCards,
self::$visas
);
}
}
+class_alias('Braintree\Test\CreditCardNumbers', 'Braintree_Test_CreditCardNumbers');
diff --git a/lib/Braintree/Test/MerchantAccount.php b/lib/Braintree/Test/MerchantAccount.php
index 85a7774..cc00c67 100644
--- a/lib/Braintree/Test/MerchantAccount.php
+++ b/lib/Braintree/Test/MerchantAccount.php
@@ -1,12 +1,13 @@
testing()->settle($transactionId);
+ }
+
+ /**
+ * settlement confirm a transaction by id in sandbox
+ *
+ * @param string $id transaction id
+ * @param Configuration $config gateway config
+ * @return Transaction
+ */
+ public static function settlementConfirm($transactionId)
+ {
+ return Configuration::gateway()->testing()->settlementConfirm($transactionId);
+ }
+
+ /**
+ * settlement decline a transaction by id in sandbox
+ *
+ * @param string $id transaction id
+ * @param Configuration $config gateway config
+ * @return Transaction
+ */
+ public static function settlementDecline($transactionId)
+ {
+ return Configuration::gateway()->testing()->settlementDecline($transactionId);
+ }
+
+ /**
+ * settlement pending a transaction by id in sandbox
+ *
+ * @param string $id transaction id
+ * @param Configuration $config gateway config
+ * @return Transaction
+ */
+ public static function settlementPending($transactionId)
+ {
+ return Configuration::gateway()->testing()->settlementPending($transactionId);
+ }
+}
+class_alias('Braintree\Test\Transaction', 'Braintree_Test_Transaction');
diff --git a/lib/Braintree/Test/TransactionAmounts.php b/lib/Braintree/Test/TransactionAmounts.php
index 7dc1a5b..9258d72 100644
--- a/lib/Braintree/Test/TransactionAmounts.php
+++ b/lib/Braintree/Test/TransactionAmounts.php
@@ -1,11 +1,5 @@
_gateway = $gateway;
+ $this->_config = $gateway->config;
+ $this->_http = new Http($this->_config);
+ }
+
+ public function settle($transactionId)
+ {
+ return self::_doTestRequest('/settle', $transactionId);
+ }
+
+ public function settlementPending($transactionId)
+ {
+ return self::_doTestRequest('/settlement_pending', $transactionId);
+ }
+
+ public function settlementConfirm($transactionId)
+ {
+ return self::_doTestRequest('/settlement_confirm', $transactionId);
+ }
+
+ public function settlementDecline($transactionId)
+ {
+ return self::_doTestRequest('/settlement_decline', $transactionId);
+ }
+
+ private function _doTestRequest($testPath, $transactionId)
+ {
+ self::_checkEnvironment();
+ $path = $this->_config->merchantPath() . '/transactions/' . $transactionId . $testPath;
+ $response = $this->_http->put($path);
+ return Transaction::factory($response['transaction']);
+ }
+
+ private function _checkEnvironment()
+ {
+ if (Configuration::$global->getEnvironment() === 'production') {
+ throw new Exception\TestOperationPerformedInProduction();
+ }
+ }
+}
+class_alias('Braintree\TestingGateway', 'Braintree_TestingGateway');
diff --git a/lib/Braintree/TextNode.php b/lib/Braintree/TextNode.php
index f193d1d..9f932a3 100644
--- a/lib/Braintree/TextNode.php
+++ b/lib/Braintree/TextNode.php
@@ -1,10 +1,12 @@
searchTerms["contains"] = strval($value);
return $this;
}
}
+class_alias('Braintree\TextNode', 'Braintree_TextNode');
diff --git a/lib/Braintree/ThreeDSecureInfo.php b/lib/Braintree/ThreeDSecureInfo.php
new file mode 100644
index 0000000..f7abae2
--- /dev/null
+++ b/lib/Braintree/ThreeDSecureInfo.php
@@ -0,0 +1,35 @@
+_initialize($attributes);
+
+ return $instance;
+ }
+
+ protected function _initialize($attributes)
+ {
+ $this->_attributes = $attributes;
+ }
+
+ /**
+ * returns a string representation of the three d secure info
+ * @return string
+ */
+ public function __toString()
+ {
+ return __CLASS__ . '[' .
+ Util::attributesToString($this->_attributes) .']';
+ }
+
+}
+class_alias('Braintree\ThreeDSecureInfo', 'Braintree_ThreeDSecureInfo');
diff --git a/lib/Braintree/Transaction.php b/lib/Braintree/Transaction.php
index eb9babf..e3cd6b6 100644
--- a/lib/Braintree/Transaction.php
+++ b/lib/Braintree/Transaction.php
@@ -1,13 +1,8 @@
Minimalistic example:
*
- * Braintree_Transaction::saleNoValidate(array(
+ * Transaction::saleNoValidate(array(
* 'amount' => '100.00',
* 'creditCard' => array(
* 'number' => '5105105105105100',
@@ -26,7 +21,7 @@
*
* Full example:
*
- * Braintree_Transaction::saleNoValidate(array(
+ * Transaction::saleNoValidate(array(
* 'amount' => '100.00',
* 'orderId' => '123',
* 'channel' => 'MyShoppingCardProvider',
@@ -42,7 +37,7 @@
* 'id' => 'customer_123',
* 'firstName' => 'Dan',
* 'lastName' => 'Smith',
- * 'company' => 'Braintree Payment Solutions',
+ * 'company' => 'Braintree',
* 'email' => 'dan@example.com',
* 'phone' => '419-555-1234',
* 'fax' => '419-555-1235',
@@ -82,7 +77,7 @@
* a transaction can be stored in the vault by setting
* transaction[options][storeInVault] to true.
*
- * $transaction = Braintree_Transaction::saleNoValidate(array(
+ * $transaction = Transaction::saleNoValidate(array(
* 'customer' => array(
* 'firstName' => 'Adam',
* 'lastName' => 'Williams'
@@ -105,7 +100,7 @@
* To also store the billing address in the vault, pass the
* addBillingAddressToPaymentMethod option.
*
- * Braintree_Transaction.saleNoValidate(array(
+ * Transaction.saleNoValidate(array(
* ...
* 'options' => array(
* 'storeInVault' => true
@@ -126,7 +121,7 @@
* $transaction[options][submitForSettlement] to true.
*
*
- * $transaction = Braintree_Transaction::saleNoValidate(array(
+ * $transaction = Transaction::saleNoValidate(array(
* 'amount' => '100.00',
* 'creditCard' => array(
* 'number' => '5105105105105100',
@@ -140,35 +135,83 @@
*
* == More information ==
*
- * For more detailed information on Transactions, see {@link http://www.braintreepayments.com/gateway/transaction-api http://www.braintreepaymentsolutions.com/gateway/transaction-api}
+ * For more detailed information on Transactions, see {@link https://developers.braintreepayments.com/reference/response/transaction/php https://developers.braintreepayments.com/reference/response/transaction/php}
*
* @package Braintree
* @category Resources
- * @copyright 2010 Braintree Payment Solutions
*
*
+ * @property-read \Braintree\AddOn[] $addons
+ * @property-read string $additionalProcessorResponse raw response from processor
+ * @property-read string $amount transaction amount
+ * @property-read \Braintree\AmexExpressCheckoutCardDetails $amexExpressCheckoutCardDetails transaction Amex Express Checkout card info
+ * @property-read \Braintree\AndroidPayCardDetails $androidPayCardDetails transaction Android Pay card info
+ * @property-read \Braintree\ApplePayCardDetails $applePayCardDetails transaction Apple Pay card info
+ * @property-read \Braintree\AuthorizationAdjustment[] $authorizationAdjustments populated when a transaction has authorization adjustments created when submitted for settlement
+ * @property-read \DateTime $authorizationExpiresAt DateTime authorization will expire
* @property-read string $avsErrorResponseCode
* @property-read string $avsPostalCodeResponseCode
* @property-read string $avsStreetAddressResponseCode
+ * @property-read \Braintree\Transaction\AddressDetails $billingDetails transaction billing address
+ * @property-read string $channel
+ * @property-read \Braintree\CoinbaseDetails $coinbaseDetails transaction Coinbase account info
+ * @property-read \DateTime $createdAt transaction created DateTime
+ * @property-read \Braintree\CreditCardDetails $creditCardDetails transaction credit card info
+ * @property-read string $currencyIsoCode
+ * @property-read array $customFields custom fields passed with the request
+ * @property-read \Braintree\Transaction\CustomerDetails $customerDetails transaction customer info
* @property-read string $cvvResponseCode
+ * @property-read \Braintree\Descriptor $descriptor
+ * @property-read Braintree\DisbursementDetails $disbursementDetails populated when transaction is disbursed
+ * @property-read string $discountAmount
+ * @property-read \Braintree\Discount[] $discounts
+ * @property-read \Braintree\Dispute[] $disputes populated when transaction is disputed
+ * @property-read string $escrowStatus
+ * @property-read \Braintree\FacilitatedDetails $facilitatedDetails
+ * @property-read \Braintree\FacilitatorDetails $facilitatorDetails
+ * @property-read string $gatewayRejectionReason
* @property-read string $id transaction id
- * @property-read string $amount transaction amount
- * @property-read object $billingDetails transaction billing address
- * @property-read string $createdAt transaction created timestamp
- * @property-read object $creditCardDetails transaction credit card info
- * @property-read object $customerDetails transaction customer info
- * @property-read array $customFields custom fields passed with the request
+ * @property-read \Braintree\IdealPayment $idealPaymentDetails transaction Ideal Payment info
+ * @property-read \Braintree\TransactionLineItem[] $lineItems
+ * @property-read \Braintree\MasterpassCardDetails $masterpassCardDetails transaction Masterpass card info
+ * @property-read string $merchantAccountId
+ * @property-read string $networkTransactionId
+ * @property-read string $orderId
+ * @property-read string $paymentInstrumentType
+ * @property-read \Braintree\PayPalDetails $paypalDetails transaction paypal account info
+ * @property-read string $planId
+ * @property-read string $processorAuthorizationCode
* @property-read string $processorResponseCode gateway response code
- * @property-read object $shippingDetails transaction shipping address
+ * @property-read string $processorResponseText
+ * @property-read string $processorResponseType
+ * @property-read string $processorSettlementResponseCode
+ * @property-read string $processorSettlementResponseText
+ * @property-read string $purchaseOrderNumber
+ * @property-read mixed $reccuring
+ * @property-read mixed $refundIds
+ * @property-read string $refundedTransactionId
+ * @property-read \Braintree\RiskData $riskData
+ * @property-read \Braintree\SamsungPayCardDetails $samsungPayCardDetails transaction Samsung Pay card info
+ * @property-read string $serviceFeeAmount
+ * @property-read string $settlementBatchId
+ * @property-read string $shippingAmount
+ * @property-read \Braintree\Transaction\AddressDetails $shippingDetails transaction shipping address
* @property-read string $status transaction status
- * @property-read array $statusHistory array of StatusDetails objects
+ * @property-read \Braintree\Transaction\StatusDetails[] $statusHistory array of StatusDetails objects
+ * @property-read \Braintree\Transaction\SubscriptionDetails $subscriptionDetails
+ * @property-read string $subscriptionId
+ * @property-read string $taxAmount
+ * @property-read string $taxExcempt
+ * @property-read \Braintree\ThreeDSecureInfo $threeDSecureInfo
* @property-read string $type transaction type
- * @property-read string $updatedAt transaction updated timestamp
- * @property-read object $disbursementDetails populated when transaction is disbursed
+ * @property-read \DateTime $updatedAt transaction updated DateTime
+ * @property-read \Braintree\VenmoAccount $venmoAccountDetails transaction Venmo Account info
+ * @property-read \Braintree\VisaCheckoutCardDetails $visaCheckoutCardDetails transaction Visa Checkout card info
+ * @property-read string $voiceReferralName
*
*/
-final class Braintree_Transaction extends Braintree
+class Transaction extends Base
{
// Transaction Status
const AUTHORIZATION_EXPIRED = 'authorization_expired';
@@ -182,6 +225,9 @@ final class Braintree_Transaction extends Braintree
const SUBMITTED_FOR_SETTLEMENT = 'submitted_for_settlement';
const VOIDED = 'voided';
const UNRECOGNIZED = 'unrecognized';
+ const SETTLEMENT_DECLINED = 'settlement_declined';
+ const SETTLEMENT_PENDING = 'settlement_pending';
+ const SETTLEMENT_CONFIRMED = 'settlement_confirmed';
// Transaction Escrow Status
const ESCROW_HOLD_PENDING = 'hold_pending';
@@ -204,335 +250,130 @@ final class Braintree_Transaction extends Braintree
const RECURRING = 'recurring';
// Gateway Rejection Reason
- const AVS = 'avs';
- const AVS_AND_CVV = 'avs_and_cvv';
- const CVV = 'cvv';
- const DUPLICATE = 'duplicate';
- const FRAUD = 'fraud';
-
- public static function cloneTransaction($transactionId, $attribs)
- {
- Braintree_Util::verifyKeys(self::cloneSignature(), $attribs);
- return self::_doCreate('/transactions/' . $transactionId . '/clone', array('transactionClone' => $attribs));
- }
-
- /**
- * @ignore
- * @access public
- * @param array $attribs
- * @return object
- */
- private static function create($attribs)
- {
- Braintree_Util::verifyKeys(self::createSignature(), $attribs);
- return self::_doCreate('/transactions', array('transaction' => $attribs));
- }
+ const AVS = 'avs';
+ const AVS_AND_CVV = 'avs_and_cvv';
+ const CVV = 'cvv';
+ const DUPLICATE = 'duplicate';
+ const FRAUD = 'fraud';
+ const THREE_D_SECURE = 'three_d_secure';
+ const APPLICATION_INCOMPLETE = 'application_incomplete';
+
+ // Industry Types
+ const LODGING_INDUSTRY = 'lodging';
+ const TRAVEL_AND_CRUISE_INDUSTRY = 'travel_cruise';
+ const TRAVEL_AND_FLIGHT_INDUSTRY = 'travel_flight';
/**
+ * sets instance properties from an array of values
*
* @ignore
- * @access public
- * @param array $attribs
- * @return object
- * @throws Braintree_Exception_ValidationError
- */
- private static function createNoValidate($attribs)
- {
- $result = self::create($attribs);
- return self::returnObjectOrThrowException(__CLASS__, $result);
- }
- /**
- *
- * @access public
- * @param array $attribs
- * @return object
- */
- public static function createFromTransparentRedirect($queryString)
- {
- trigger_error("DEPRECATED: Please use Braintree_TransparentRedirectRequest::confirm", E_USER_NOTICE);
- $params = Braintree_TransparentRedirect::parseAndValidateQueryString(
- $queryString
- );
- return self::_doCreate(
- '/transactions/all/confirm_transparent_redirect_request',
- array('id' => $params['id'])
- );
- }
- /**
- *
- * @access public
- * @param none
- * @return string
- */
- public static function createTransactionUrl()
- {
- trigger_error("DEPRECATED: Please use Braintree_TransparentRedirectRequest::url", E_USER_NOTICE);
- return Braintree_Configuration::merchantUrl() .
- '/transactions/all/create_via_transparent_redirect_request';
- }
-
- public static function cloneSignature()
- {
- return array('amount', 'channel', array('options' => array('submitForSettlement')));
- }
-
- /**
- * creates a full array signature of a valid gateway request
- * @return array gateway request signature format
- */
- public static function createSignature()
- {
- return array(
- 'amount', 'customerId', 'merchantAccountId', 'orderId', 'channel', 'paymentMethodToken', 'deviceSessionId',
- 'purchaseOrderNumber', 'recurring', 'shippingAddressId', 'taxAmount', 'taxExempt', 'type', 'venmoSdkPaymentMethodCode',
- 'serviceFeeAmount', 'deviceData', 'fraudMerchantId', 'billingAddressId',
- array('creditCard' =>
- array('token', 'cardholderName', 'cvv', 'expirationDate', 'expirationMonth', 'expirationYear', 'number'),
- ),
- array('customer' =>
- array(
- 'id', 'company', 'email', 'fax', 'firstName',
- 'lastName', 'phone', 'website'),
- ),
- array('billing' =>
- array(
- 'firstName', 'lastName', 'company', 'countryName',
- 'countryCodeAlpha2', 'countryCodeAlpha3', 'countryCodeNumeric',
- 'extendedAddress', 'locality', 'postalCode', 'region',
- 'streetAddress'),
- ),
- array('shipping' =>
- array(
- 'firstName', 'lastName', 'company', 'countryName',
- 'countryCodeAlpha2', 'countryCodeAlpha3', 'countryCodeNumeric',
- 'extendedAddress', 'locality', 'postalCode', 'region',
- 'streetAddress'),
- ),
- array('options' =>
- array(
- 'holdInEscrow',
- 'storeInVault',
- 'storeInVaultOnSuccess',
- 'submitForSettlement',
- 'addBillingAddressToPaymentMethod',
- 'venmoSdkSession',
- 'storeShippingAddressInVault'),
- ),
- array('customFields' => array('_anyKey_')
- ),
- array('descriptor' => array('name', 'phone'))
- );
- }
-
- /**
- *
- * @access public
- * @param array $attribs
- * @return object
- */
- public static function credit($attribs)
- {
- return self::create(array_merge($attribs, array('type' => Braintree_Transaction::CREDIT)));
- }
-
- /**
- *
- * @access public
- * @param array $attribs
- * @return object
- * @throws Braintree_Exception_ValidationError
+ * @access protected
+ * @param array $transactionAttribs array of transaction data
+ * @return void
*/
- public static function creditNoValidate($attribs)
+ protected function _initialize($transactionAttribs)
{
- $result = self::credit($attribs);
- return self::returnObjectOrThrowException(__CLASS__, $result);
- }
-
+ $this->_attributes = $transactionAttribs;
- /**
- * @access public
- *
- */
- public static function find($id)
- {
- self::_validateId($id);
- try {
- $response = Braintree_Http::get('/transactions/'.$id);
- return self::factory($response['transaction']);
- } catch (Braintree_Exception_NotFound $e) {
- throw new Braintree_Exception_NotFound(
- 'transaction with id ' . $id . ' not found'
+ if (isset($transactionAttribs['applePay'])) {
+ $this->_set('applePayCardDetails',
+ new Transaction\ApplePayCardDetails(
+ $transactionAttribs['applePay']
+ )
);
}
- }
- /**
- * new sale
- * @param array $attribs
- * @return array
- */
- public static function sale($attribs)
- {
- return self::create(array_merge(array('type' => Braintree_Transaction::SALE), $attribs));
- }
-
- /**
- * roughly equivalent to the ruby bang method
- * @access public
- * @param array $attribs
- * @return array
- * @throws Braintree_Exception_ValidationsFailed
- */
- public static function saleNoValidate($attribs)
- {
- $result = self::sale($attribs);
- return self::returnObjectOrThrowException(__CLASS__, $result);
- }
-
- /**
- * Returns a ResourceCollection of transactions matching the search query.
- *
- * If query is a string, the search will be a basic search.
- * If query is a hash, the search will be an advanced search.
- * For more detailed information and examples, see {@link http://www.braintreepayments.com/gateway/transaction-api#searching http://www.braintreepaymentsolutions.com/gateway/transaction-api}
- *
- * @param mixed $query search query
- * @param array $options options such as page number
- * @return object Braintree_ResourceCollection
- * @throws InvalidArgumentException
- */
- public static function search($query)
- {
- $criteria = array();
- foreach ($query as $term) {
- $criteria[$term->name] = $term->toparam();
+ if (isset($transactionAttribs['androidPayCard'])) {
+ $this->_set('androidPayCardDetails',
+ new Transaction\AndroidPayCardDetails(
+ $transactionAttribs['androidPayCard']
+ )
+ );
}
- $response = Braintree_Http::post('/transactions/advanced_search_ids', array('search' => $criteria));
- if (array_key_exists('searchResults', $response)) {
- $pager = array(
- 'className' => __CLASS__,
- 'classMethod' => 'fetch',
- 'methodArgs' => array($query)
- );
-
- return new Braintree_ResourceCollection($response, $pager);
- } else {
- throw new Braintree_Exception_DownForMaintenance();
+ if (isset($transactionAttribs['masterpassCard'])) {
+ $this->_set('masterpassCardDetails',
+ new Transaction\MasterpassCardDetails(
+ $transactionAttribs['masterpassCard']
+ )
+ );
}
- }
- public static function fetch($query, $ids)
- {
- $criteria = array();
- foreach ($query as $term) {
- $criteria[$term->name] = $term->toparam();
+ if (isset($transactionAttribs['visaCheckoutCard'])) {
+ $this->_set('visaCheckoutCardDetails',
+ new Transaction\VisaCheckoutCardDetails(
+ $transactionAttribs['visaCheckoutCard']
+ )
+ );
}
- $criteria["ids"] = Braintree_TransactionSearch::ids()->in($ids)->toparam();
- $response = Braintree_Http::post('/transactions/advanced_search', array('search' => $criteria));
-
- return Braintree_Util::extractattributeasarray(
- $response['creditCardTransactions'],
- 'transaction'
- );
- }
-
- /**
- * void a transaction by id
- *
- * @param string $id transaction id
- * @return object Braintree_Result_Successful|Braintree_Result_Error
- */
- public static function void($transactionId)
- {
- self::_validateId($transactionId);
-
- $response = Braintree_Http::put('/transactions/'. $transactionId . '/void');
- return self::_verifyGatewayResponse($response);
- }
- /**
- *
- */
- public static function voidNoValidate($transactionId)
- {
- $result = self::void($transactionId);
- return self::returnObjectOrThrowException(__CLASS__, $result);
- }
-
- public static function submitForSettlement($transactionId, $amount = null)
- {
- self::_validateId($transactionId);
- $response = Braintree_Http::put(
- '/transactions/'. $transactionId . '/submit_for_settlement',
- array( 'transaction' => array( 'amount' => $amount))
- );
- return self::_verifyGatewayResponse($response);
- }
-
- public static function submitForSettlementNoValidate($transactionId, $amount = null)
- {
- $result = self::submitForSettlement($transactionId, $amount);
- return self::returnObjectOrThrowException(__CLASS__, $result);
- }
-
- public static function holdInEscrow($transactionId)
- {
- self::_validateId($transactionId);
-
- $response = Braintree_Http::put(
- '/transactions/' . $transactionId . '/hold_in_escrow',
- array()
- );
- return self::_verifyGatewayResponse($response);
- }
+ if (isset($transactionAttribs['samsungPayCard'])) {
+ $this->_set('samsungPayCardDetails',
+ new Transaction\SamsungPayCardDetails(
+ $transactionAttribs['samsungPayCard']
+ )
+ );
+ }
- public static function releaseFromEscrow($transactionId)
- {
- self::_validateId($transactionId);
+ if (isset($transactionAttribs['amexExpressCheckoutCard'])) {
+ $this->_set('amexExpressCheckoutCardDetails',
+ new Transaction\AmexExpressCheckoutCardDetails(
+ $transactionAttribs['amexExpressCheckoutCard']
+ )
+ );
+ }
- $response = Braintree_Http::put(
- '/transactions/' . $transactionId . '/release_from_escrow',
- array()
- );
- return self::_verifyGatewayResponse($response);
- }
+ if (isset($transactionAttribs['venmoAccount'])) {
+ $this->_set('venmoAccountDetails',
+ new Transaction\VenmoAccountDetails(
+ $transactionAttribs['venmoAccount']
+ )
+ );
+ }
- public static function cancelRelease($transactionId)
- {
- self::_validateId($transactionId);
+ if (isset($transactionAttribs['creditCard'])) {
+ $this->_set('creditCardDetails',
+ new Transaction\CreditCardDetails(
+ $transactionAttribs['creditCard']
+ )
+ );
+ }
- $response = Braintree_Http::put(
- '/transactions/' . $transactionId . '/cancel_release',
- array()
- );
- return self::_verifyGatewayResponse($response);
- }
+ if (isset($transactionAttribs['coinbaseAccount'])) {
+ $this->_set('coinbaseDetails',
+ new Transaction\CoinbaseDetails(
+ $transactionAttribs['coinbaseAccount']
+ )
+ );
+ }
+ if (isset($transactionAttribs['usBankAccount'])) {
+ $this->_set('usBankAccount',
+ new Transaction\UsBankAccountDetails(
+ $transactionAttribs['usBankAccount']
+ )
+ );
+ }
- /**
- * sets instance properties from an array of values
- *
- * @ignore
- * @access protected
- * @param array $transactionAttribs array of transaction data
- * @return none
- */
- protected function _initialize($transactionAttribs)
- {
- $this->_attributes = $transactionAttribs;
+ if (isset($transactionAttribs['idealPayment'])) {
+ $this->_set('idealPayment',
+ new Transaction\IdealPaymentDetails(
+ $transactionAttribs['idealPayment']
+ )
+ );
+ }
- if (isset($transactionAttribs['creditCard'])) {
- $this->_set('creditCardDetails',
- new Braintree_Transaction_CreditCardDetails(
- $transactionAttribs['creditCard']
+ if (isset($transactionAttribs['paypal'])) {
+ $this->_set('paypalDetails',
+ new Transaction\PayPalDetails(
+ $transactionAttribs['paypal']
)
);
}
if (isset($transactionAttribs['customer'])) {
$this->_set('customerDetails',
- new Braintree_Transaction_CustomerDetails(
+ new Transaction\CustomerDetails(
$transactionAttribs['customer']
)
);
@@ -540,7 +381,7 @@ protected function _initialize($transactionAttribs)
if (isset($transactionAttribs['billing'])) {
$this->_set('billingDetails',
- new Braintree_Transaction_AddressDetails(
+ new Transaction\AddressDetails(
$transactionAttribs['billing']
)
);
@@ -548,7 +389,7 @@ protected function _initialize($transactionAttribs)
if (isset($transactionAttribs['shipping'])) {
$this->_set('shippingDetails',
- new Braintree_Transaction_AddressDetails(
+ new Transaction\AddressDetails(
$transactionAttribs['shipping']
)
);
@@ -556,7 +397,7 @@ protected function _initialize($transactionAttribs)
if (isset($transactionAttribs['subscription'])) {
$this->_set('subscriptionDetails',
- new Braintree_Transaction_SubscriptionDetails(
+ new Transaction\SubscriptionDetails(
$transactionAttribs['subscription']
)
);
@@ -564,7 +405,7 @@ protected function _initialize($transactionAttribs)
if (isset($transactionAttribs['descriptor'])) {
$this->_set('descriptor',
- new Braintree_Descriptor(
+ new Descriptor(
$transactionAttribs['descriptor']
)
);
@@ -572,34 +413,65 @@ protected function _initialize($transactionAttribs)
if (isset($transactionAttribs['disbursementDetails'])) {
$this->_set('disbursementDetails',
- new Braintree_DisbursementDetails($transactionAttribs['disbursementDetails'])
+ new DisbursementDetails($transactionAttribs['disbursementDetails'])
);
}
- $statusHistory = array();
+ $disputes = [];
+ if (isset($transactionAttribs['disputes'])) {
+ foreach ($transactionAttribs['disputes'] AS $dispute) {
+ $disputes[] = Dispute::factory($dispute);
+ }
+ }
+
+ $this->_set('disputes', $disputes);
+
+ $statusHistory = [];
if (isset($transactionAttribs['statusHistory'])) {
foreach ($transactionAttribs['statusHistory'] AS $history) {
- $statusHistory[] = new Braintree_Transaction_StatusDetails($history);
+ $statusHistory[] = new Transaction\StatusDetails($history);
}
}
$this->_set('statusHistory', $statusHistory);
- $addOnArray = array();
+ $addOnArray = [];
if (isset($transactionAttribs['addOns'])) {
foreach ($transactionAttribs['addOns'] AS $addOn) {
- $addOnArray[] = Braintree_AddOn::factory($addOn);
+ $addOnArray[] = AddOn::factory($addOn);
}
}
$this->_set('addOns', $addOnArray);
- $discountArray = array();
+ $discountArray = [];
if (isset($transactionAttribs['discounts'])) {
foreach ($transactionAttribs['discounts'] AS $discount) {
- $discountArray[] = Braintree_Discount::factory($discount);
+ $discountArray[] = Discount::factory($discount);
}
}
$this->_set('discounts', $discountArray);
+
+ $authorizationAdjustments = [];
+ if (isset($transactionAttribs['authorizationAdjustments'])) {
+ foreach ($transactionAttribs['authorizationAdjustments'] AS $authorizationAdjustment) {
+ $authorizationAdjustments[] = AuthorizationAdjustment::factory($authorizationAdjustment);
+ }
+ }
+
+ $this->_set('authorizationAdjustments', $authorizationAdjustments);
+
+ if(isset($transactionAttribs['riskData'])) {
+ $this->_set('riskData', RiskData::factory($transactionAttribs['riskData']));
+ }
+ if(isset($transactionAttribs['threeDSecureInfo'])) {
+ $this->_set('threeDSecureInfo', ThreeDSecureInfo::factory($transactionAttribs['threeDSecureInfo']));
+ }
+ if(isset($transactionAttribs['facilitatedDetails'])) {
+ $this->_set('facilitatedDetails', FacilitatedDetails::factory($transactionAttribs['facilitatedDetails']));
+ }
+ if(isset($transactionAttribs['facilitatorDetails'])) {
+ $this->_set('facilitatorDetails', FacilitatorDetails::factory($transactionAttribs['facilitatorDetails']));
+ }
}
/**
@@ -609,24 +481,17 @@ protected function _initialize($transactionAttribs)
public function __toString()
{
// array of attributes to print
- $display = array(
+ $display = [
'id', 'type', 'amount', 'status',
'createdAt', 'creditCardDetails', 'customerDetails'
- );
+ ];
- $displayAttributes = array();
+ $displayAttributes = [];
foreach ($display AS $attrib) {
$displayAttributes[$attrib] = $this->$attrib;
}
return __CLASS__ . '[' .
- Braintree_Util::attributesToString($displayAttributes) .']';
- }
-
- public static function refund($transactionId, $amount = null)
- {
- $params = array('transaction' => array('amount' => $amount));
- $response = Braintree_Http::post('/transactions/' . $transactionId . '/refund', $params);
- return self::_verifyGatewayResponse($response);
+ Util::attributesToString($displayAttributes) .']';
}
public function isEqual($otherTx)
@@ -641,10 +506,11 @@ public function vaultCreditCard()
return null;
}
else {
- return Braintree_CreditCard::find($token);
+ return CreditCard::find($token);
}
}
+ /** @return void|Braintree\Customer */
public function vaultCustomer()
{
$customerId = $this->customerDetails->id;
@@ -652,91 +518,135 @@ public function vaultCustomer()
return null;
}
else {
- return Braintree_Customer::find($customerId);
+ return Customer::find($customerId);
}
}
+ /** @return boolean */
public function isDisbursed() {
return $this->disbursementDetails->isValid();
}
+ /** @return line items */
+ public function lineItems() {
+ return Configuration::gateway()->transactionLineItem()->findAll($this->id);
+ }
+
/**
- * sends the create request to the gateway
+ * factory method: returns an instance of Transaction
+ * to the requesting method, with populated properties
*
* @ignore
- * @param var $url
- * @param array $params
- * @return mixed
+ * @return Transaction
*/
- public static function _doCreate($url, $params)
+ public static function factory($attributes)
{
- $response = Braintree_Http::post($url, $params);
+ $instance = new self();
+ $instance->_initialize($attributes);
+ return $instance;
+ }
+
- return self::_verifyGatewayResponse($response);
+ // static methods redirecting to gateway
+
+ public static function cloneTransaction($transactionId, $attribs)
+ {
+ return Configuration::gateway()->transaction()->cloneTransaction($transactionId, $attribs);
}
- /**
- * verifies that a valid transaction id is being used
- * @ignore
- * @param string transaction id
- * @throws InvalidArgumentException
- */
- private static function _validateId($id = null) {
- if (empty($id)) {
- throw new InvalidArgumentException(
- 'expected transaction id to be set'
- );
- }
- if (!preg_match('/^[0-9a-z]+$/', $id)) {
- throw new InvalidArgumentException(
- $id . ' is an invalid transaction id.'
- );
- }
+ public static function createFromTransparentRedirect($queryString)
+ {
+ return Configuration::gateway()->transaction()->createFromTransparentRedirect($queryString);
}
+ public static function createTransactionUrl()
+ {
+ return Configuration::gateway()->transaction()->createTransactionUrl();
+ }
- /* private class methods */
+ public static function credit($attribs)
+ {
+ return Configuration::gateway()->transaction()->credit($attribs);
+ }
- /**
- * generic method for validating incoming gateway responses
- *
- * creates a new Braintree_Transaction object and encapsulates
- * it inside a Braintree_Result_Successful object, or
- * encapsulates a Braintree_Errors object inside a Result_Error
- * alternatively, throws an Unexpected exception if the response is invalid.
- *
- * @ignore
- * @param array $response gateway response values
- * @return object Result_Successful or Result_Error
- * @throws Braintree_Exception_Unexpected
- */
- private static function _verifyGatewayResponse($response)
+ public static function creditNoValidate($attribs)
{
- if (isset($response['transaction'])) {
- // return a populated instance of Braintree_Transaction
- return new Braintree_Result_Successful(
- self::factory($response['transaction'])
- );
- } else if (isset($response['apiErrorResponse'])) {
- return new Braintree_Result_Error($response['apiErrorResponse']);
- } else {
- throw new Braintree_Exception_Unexpected(
- "Expected transaction or apiErrorResponse"
- );
- }
+ return Configuration::gateway()->transaction()->creditNoValidate($attribs);
}
- /**
- * factory method: returns an instance of Braintree_Transaction
- * to the requesting method, with populated properties
- *
- * @ignore
- * @return object instance of Braintree_Transaction
- */
- public static function factory($attributes)
+ public static function find($id)
{
- $instance = new self();
- $instance->_initialize($attributes);
- return $instance;
+ return Configuration::gateway()->transaction()->find($id);
+ }
+
+ public static function sale($attribs)
+ {
+ return Configuration::gateway()->transaction()->sale($attribs);
+ }
+
+ public static function saleNoValidate($attribs)
+ {
+ return Configuration::gateway()->transaction()->saleNoValidate($attribs);
+ }
+
+ public static function search($query)
+ {
+ return Configuration::gateway()->transaction()->search($query);
+ }
+
+ public static function fetch($query, $ids)
+ {
+ return Configuration::gateway()->transaction()->fetch($query, $ids);
+ }
+
+ public static function void($transactionId)
+ {
+ return Configuration::gateway()->transaction()->void($transactionId);
+ }
+
+ public static function voidNoValidate($transactionId)
+ {
+ return Configuration::gateway()->transaction()->voidNoValidate($transactionId);
+ }
+
+ public static function submitForSettlement($transactionId, $amount = null, $attribs = [])
+ {
+ return Configuration::gateway()->transaction()->submitForSettlement($transactionId, $amount, $attribs);
+ }
+
+ public static function submitForSettlementNoValidate($transactionId, $amount = null, $attribs = [])
+ {
+ return Configuration::gateway()->transaction()->submitForSettlementNoValidate($transactionId, $amount, $attribs);
+ }
+
+ public static function updateDetails($transactionId, $attribs = [])
+ {
+ return Configuration::gateway()->transaction()->updateDetails($transactionId, $attribs);
+ }
+
+ public static function submitForPartialSettlement($transactionId, $amount, $attribs = [])
+ {
+ return Configuration::gateway()->transaction()->submitForPartialSettlement($transactionId, $amount, $attribs);
+ }
+
+ public static function holdInEscrow($transactionId)
+ {
+ return Configuration::gateway()->transaction()->holdInEscrow($transactionId);
+ }
+
+ public static function releaseFromEscrow($transactionId)
+ {
+ return Configuration::gateway()->transaction()->releaseFromEscrow($transactionId);
+ }
+
+ public static function cancelRelease($transactionId)
+ {
+ return Configuration::gateway()->transaction()->cancelRelease($transactionId);
+ }
+
+ public static function refund($transactionId, $amount = null)
+ {
+ return Configuration::gateway()->transaction()->refund($transactionId, $amount);
}
}
+class_alias('Braintree\Transaction', 'Braintree_Transaction');
diff --git a/lib/Braintree/Transaction/AddressDetails.php b/lib/Braintree/Transaction/AddressDetails.php
index dc89573..597a208 100644
--- a/lib/Braintree/Transaction/AddressDetails.php
+++ b/lib/Braintree/Transaction/AddressDetails.php
@@ -1,11 +1,7 @@
_attributes['cardType'] = $this->virtualCardType;
+ $this->_attributes['last4'] = $this->virtualCardLast4;
+ }
+}
+class_alias('Braintree\Transaction\AndroidPayCardDetails', 'Braintree_Transaction_AndroidPayCardDetails');
diff --git a/lib/Braintree/Transaction/ApplePayCardDetails.php b/lib/Braintree/Transaction/ApplePayCardDetails.php
new file mode 100644
index 0000000..f0a2be4
--- /dev/null
+++ b/lib/Braintree/Transaction/ApplePayCardDetails.php
@@ -0,0 +1,39 @@
+_attributes['expirationDate'] = $this->expirationMonth . '/' . $this->expirationYear;
+ $this->_attributes['maskedNumber'] = $this->bin . '******' . $this->last4;
+
+ }
+}
+class_alias('Braintree\Transaction\MasterpassCardDetails', 'Braintree_Transaction_MasterpassCardDetails');
diff --git a/lib/Braintree/Transaction/PayPalDetails.php b/lib/Braintree/Transaction/PayPalDetails.php
new file mode 100644
index 0000000..4c13a42
--- /dev/null
+++ b/lib/Braintree/Transaction/PayPalDetails.php
@@ -0,0 +1,51 @@
+_attributes['expirationDate'] = $this->expirationMonth . '/' . $this->expirationYear;
+ $this->_attributes['maskedNumber'] = $this->bin . '******' . $this->last4;
+
+ }
+}
+class_alias('Braintree\Transaction\SamsungPayCardDetails', 'Braintree_Transaction_SamsungPayCardDetails');
diff --git a/lib/Braintree/Transaction/StatusDetails.php b/lib/Braintree/Transaction/StatusDetails.php
index c105ced..b8e1f8f 100644
--- a/lib/Braintree/Transaction/StatusDetails.php
+++ b/lib/Braintree/Transaction/StatusDetails.php
@@ -1,25 +1,21 @@
achMandate = $achMandate;
+ }
+}
+class_alias('Braintree\Transaction\UsBankAccountDetails', 'Braintree_Transaction_UsBankAccountDetails');
diff --git a/lib/Braintree/Transaction/VenmoAccountDetails.php b/lib/Braintree/Transaction/VenmoAccountDetails.php
new file mode 100644
index 0000000..b48baed
--- /dev/null
+++ b/lib/Braintree/Transaction/VenmoAccountDetails.php
@@ -0,0 +1,38 @@
+_attributes['expirationDate'] = $this->expirationMonth . '/' . $this->expirationYear;
+ $this->_attributes['maskedNumber'] = $this->bin . '******' . $this->last4;
+
+ }
+}
+class_alias('Braintree\Transaction\VisaCheckoutCardDetails', 'Braintree_Transaction_VisaCheckoutCardDetails');
diff --git a/lib/Braintree/TransactionGateway.php b/lib/Braintree/TransactionGateway.php
new file mode 100644
index 0000000..4f5e32f
--- /dev/null
+++ b/lib/Braintree/TransactionGateway.php
@@ -0,0 +1,585 @@
+== More information ==
+ *
+ * For more detailed information on Transactions, see {@link https://developers.braintreepayments.com/reference/response/transaction/php https://developers.braintreepayments.com/reference/response/transaction/php}
+ *
+ * @package Braintree
+ * @category Resources
+ */
+
+class TransactionGateway
+{
+ private $_gateway;
+ private $_config;
+ private $_http;
+
+ public function __construct($gateway)
+ {
+ $this->_gateway = $gateway;
+ $this->_config = $gateway->config;
+ $this->_config->assertHasAccessTokenOrKeys();
+ $this->_http = new Http($gateway->config);
+ }
+
+ public function cloneTransaction($transactionId, $attribs)
+ {
+ Util::verifyKeys(self::cloneSignature(), $attribs);
+ return $this->_doCreate('/transactions/' . $transactionId . '/clone', ['transactionClone' => $attribs]);
+ }
+
+ /**
+ * @ignore
+ * @access private
+ * @param array $attribs
+ * @return Result\Successful|Result\Error
+ */
+ private function create($attribs)
+ {
+ Util::verifyKeys(self::createSignature(), $attribs);
+ return $this->_doCreate('/transactions', ['transaction' => $attribs]);
+ }
+
+ /**
+ * @ignore
+ * @access private
+ * @param array $attribs
+ * @return object
+ * @throws Exception\ValidationError
+ */
+ private function createNoValidate($attribs)
+ {
+ $result = $this->create($attribs);
+ return Util::returnObjectOrThrowException(__CLASS__, $result);
+ }
+ /**
+ *
+ * @deprecated since version 2.3.0
+ * @access public
+ * @param array $attribs
+ * @return object
+ */
+ public function createFromTransparentRedirect($queryString)
+ {
+ trigger_error("DEPRECATED: Please use TransparentRedirectRequest::confirm", E_USER_NOTICE);
+ $params = TransparentRedirect::parseAndValidateQueryString(
+ $queryString
+ );
+ return $this->_doCreate(
+ '/transactions/all/confirm_transparent_redirect_request',
+ ['id' => $params['id']]
+ );
+ }
+ /**
+ *
+ * @deprecated since version 2.3.0
+ * @access public
+ * @param none
+ * @return string
+ */
+ public function createTransactionUrl()
+ {
+ trigger_error("DEPRECATED: Please use TransparentRedirectRequest::url", E_USER_NOTICE);
+ return $this->_config->baseUrl() . $this->_config->merchantPath() .
+ '/transactions/all/create_via_transparent_redirect_request';
+ }
+
+ public static function cloneSignature()
+ {
+ return ['amount', 'channel', ['options' => ['submitForSettlement']]];
+ }
+
+ /**
+ * creates a full array signature of a valid gateway request
+ * @return array gateway request signature format
+ */
+ public static function createSignature()
+ {
+ return [
+ 'amount',
+ 'billingAddressId',
+ 'channel',
+ 'customerId',
+ 'deviceData',
+ 'deviceSessionId',
+ 'fraudMerchantId',
+ 'merchantAccountId',
+ 'orderId',
+ 'paymentMethodNonce',
+ 'paymentMethodToken',
+ 'purchaseOrderNumber',
+ 'recurring',
+ 'serviceFeeAmount',
+ 'sharedPaymentMethodToken',
+ 'sharedPaymentMethodNonce',
+ 'sharedCustomerId',
+ 'sharedShippingAddressId',
+ 'sharedBillingAddressId',
+ 'shippingAddressId',
+ 'taxAmount',
+ 'taxExempt',
+ 'threeDSecureToken',
+ 'transactionSource',
+ 'type',
+ 'venmoSdkPaymentMethodCode',
+ 'shippingAmount',
+ 'discountAmount',
+ 'shipsFromPostalCode',
+ ['riskData' =>
+ ['customerBrowser', 'customerIp', 'customer_browser', 'customer_ip']
+ ],
+ ['creditCard' =>
+ ['token', 'cardholderName', 'cvv', 'expirationDate', 'expirationMonth', 'expirationYear', 'number'],
+ ],
+ ['customer' =>
+ [
+ 'id', 'company', 'email', 'fax', 'firstName',
+ 'lastName', 'phone', 'website'],
+ ],
+ ['billing' =>
+ [
+ 'firstName', 'lastName', 'company', 'countryName',
+ 'countryCodeAlpha2', 'countryCodeAlpha3', 'countryCodeNumeric',
+ 'extendedAddress', 'locality', 'postalCode', 'region',
+ 'streetAddress'],
+ ],
+ ['shipping' =>
+ [
+ 'firstName', 'lastName', 'company', 'countryName',
+ 'countryCodeAlpha2', 'countryCodeAlpha3', 'countryCodeNumeric',
+ 'extendedAddress', 'locality', 'postalCode', 'region',
+ 'streetAddress'],
+ ],
+ ['threeDSecurePassThru' =>
+ [
+ 'eciFlag',
+ 'cavv',
+ 'xid'],
+ ],
+ ['options' =>
+ [
+ 'holdInEscrow',
+ 'storeInVault',
+ 'storeInVaultOnSuccess',
+ 'submitForSettlement',
+ 'addBillingAddressToPaymentMethod',
+ 'venmoSdkSession',
+ 'storeShippingAddressInVault',
+ 'payeeId',
+ 'payeeEmail',
+ 'skipAdvancedFraudChecking',
+ 'skipAvs',
+ 'skipCvv',
+ ['threeDSecure' =>
+ ['required']
+ ],
+ # TODO: Snake case version included for backwards compatiblity. Remove in the next major version
+ ['three_d_secure' =>
+ ['required']
+ ],
+ ['paypal' =>
+ [
+ 'payeeId',
+ 'payeeEmail',
+ 'customField',
+ 'description',
+ ['supplementaryData' => ['_anyKey_']],
+ ]
+ ],
+ ['amexRewards' =>
+ [
+ 'requestId',
+ 'points',
+ 'currencyAmount',
+ 'currencyIsoCode'
+ ]
+ ],
+ ['venmo' =>
+ [
+ # TODO: Snake case version included for backwards compatiblity. Remove in the next major version
+ 'profile_id',
+ 'profileId'
+ ]
+ ]
+ ],
+ ],
+ ['customFields' => ['_anyKey_']],
+ ['descriptor' => ['name', 'phone', 'url']],
+ ['paypalAccount' => ['payeeId', 'payeeEmail', 'payerId', 'paymentId']],
+ # TODO: Snake case version included for backwards compatiblity. Remove in the next major version
+ ['apple_pay_card' => ['number', 'cardholder_name', 'cryptogram', 'expiration_month', 'expiration_year', 'eci_indicator']],
+
+ ['applePayCard' => ['number', 'cardholderName', 'cryptogram', 'expirationMonth', 'expirationYear', 'eciIndicator']],
+ ['industry' =>
+ ['industryType',
+ ['data' =>
+ [
+ 'folioNumber',
+ 'checkInDate',
+ 'checkOutDate',
+ 'travelPackage',
+ 'departureDate',
+ 'lodgingCheckInDate',
+ 'lodgingCheckOutDate',
+ 'lodgingName',
+ 'roomRate',
+ 'passengerFirstName',
+ 'passengerLastName',
+ 'passengerMiddleInitial',
+ 'passengerTitle',
+ 'issuedDate',
+ 'travelAgencyName',
+ 'travelAgencyCode',
+ 'ticketNumber',
+ 'issuingCarrierCode',
+ 'customerCode',
+ 'fareAmount',
+ 'feeAmount',
+ 'taxAmount',
+ 'restrictedTicket',
+ ['legs' =>
+ [
+ 'conjunctionTicket',
+ 'exchangeTicket',
+ 'couponNumber',
+ 'serviceClass',
+ 'carrierCode',
+ 'fareBasisCode',
+ 'flightNumber',
+ 'departureDate',
+ 'departureAirportCode',
+ 'departureTime',
+ 'arrivalAirportCode',
+ 'arrivalTime',
+ 'stopoverPermitted',
+ 'fareAmount',
+ 'feeAmount',
+ 'taxAmount',
+ 'endorsementOrRestrictions'
+ ]
+ ]
+ ]
+ ]
+ ]
+ ],
+ ['lineItems' => ['quantity', 'name', 'description', 'kind', 'unitAmount', 'unitTaxAmount', 'totalAmount', 'discountAmount', 'taxAmount', 'unitOfMeasure', 'productCode', 'commodityCode', 'url']],
+ ['externalVault' =>
+ ['status' , 'previousNetworkTransactionId'],
+ ]
+ ];
+ }
+
+ public static function submitForSettlementSignature()
+ {
+ return ['orderId', ['descriptor' => ['name', 'phone', 'url']]];
+ }
+
+ public static function updateDetailsSignature()
+ {
+ return ['amount', 'orderId', ['descriptor' => ['name', 'phone', 'url']]];
+ }
+
+ public static function refundSignature()
+ {
+ return ['amount', 'orderId'];
+ }
+
+ /**
+ *
+ * @access public
+ * @param array $attribs
+ * @return Result\Successful|Result\Error
+ */
+ public function credit($attribs)
+ {
+ return $this->create(array_merge($attribs, ['type' => Transaction::CREDIT]));
+ }
+
+ /**
+ *
+ * @access public
+ * @param array $attribs
+ * @return Result\Successful|Result\Error
+ * @throws Exception\ValidationError
+ */
+ public function creditNoValidate($attribs)
+ {
+ $result = $this->credit($attribs);
+ return Util::returnObjectOrThrowException(__CLASS__, $result);
+ }
+
+ /**
+ * @access public
+ * @param string id
+ * @return Transaction
+ */
+ public function find($id)
+ {
+ $this->_validateId($id);
+ try {
+ $path = $this->_config->merchantPath() . '/transactions/' . $id;
+ $response = $this->_http->get($path);
+ return Transaction::factory($response['transaction']);
+ } catch (Exception\NotFound $e) {
+ throw new Exception\NotFound(
+ 'transaction with id ' . $id . ' not found'
+ );
+ }
+ }
+ /**
+ * new sale
+ * @param array $attribs
+ * @return Result\Successful|Result\Error
+ */
+ public function sale($attribs)
+ {
+ return $this->create(array_merge(['type' => Transaction::SALE], $attribs));
+ }
+
+ /**
+ * roughly equivalent to the ruby bang method
+ * @access public
+ * @param array $attribs
+ * @return array
+ * @throws Exception\ValidationsFailed
+ */
+ public function saleNoValidate($attribs)
+ {
+ $result = $this->sale($attribs);
+ return Util::returnObjectOrThrowException(__CLASS__, $result);
+ }
+
+ /**
+ * Returns a ResourceCollection of transactions matching the search query.
+ *
+ * If query is a string, the search will be a basic search.
+ * If query is a hash, the search will be an advanced search.
+ * For more detailed information and examples, see {@link https://developers.braintreepayments.com/reference/request/transaction/search/php https://developers.braintreepayments.com/reference/request/transaction/search/php}
+ *
+ * @param mixed $query search query
+ * @param array $options options such as page number
+ * @return ResourceCollection
+ * @throws InvalidArgumentException
+ */
+ public function search($query)
+ {
+ $criteria = [];
+ foreach ($query as $term) {
+ $criteria[$term->name] = $term->toparam();
+ }
+
+ $path = $this->_config->merchantPath() . '/transactions/advanced_search_ids';
+ $response = $this->_http->post($path, ['search' => $criteria]);
+ if (array_key_exists('searchResults', $response)) {
+ $pager = [
+ 'object' => $this,
+ 'method' => 'fetch',
+ 'methodArgs' => [$query]
+ ];
+
+ return new ResourceCollection($response, $pager);
+ } else {
+ throw new Exception\DownForMaintenance();
+ }
+ }
+
+ public function fetch($query, $ids)
+ {
+ $criteria = [];
+ foreach ($query as $term) {
+ $criteria[$term->name] = $term->toparam();
+ }
+ $criteria["ids"] = TransactionSearch::ids()->in($ids)->toparam();
+ $path = $this->_config->merchantPath() . '/transactions/advanced_search';
+ $response = $this->_http->post($path, ['search' => $criteria]);
+
+ if (array_key_exists('creditCardTransactions', $response)) {
+ return Util::extractattributeasarray(
+ $response['creditCardTransactions'],
+ 'transaction'
+ );
+ } else {
+ throw new Exception\DownForMaintenance();
+ }
+ }
+
+ /**
+ * void a transaction by id
+ *
+ * @param string $id transaction id
+ * @return Result\Successful|Result\Error
+ */
+ public function void($transactionId)
+ {
+ $this->_validateId($transactionId);
+
+ $path = $this->_config->merchantPath() . '/transactions/'. $transactionId . '/void';
+ $response = $this->_http->put($path);
+ return $this->_verifyGatewayResponse($response);
+ }
+ /**
+ *
+ */
+ public function voidNoValidate($transactionId)
+ {
+ $result = $this->void($transactionId);
+ return Util::returnObjectOrThrowException(__CLASS__, $result);
+ }
+
+ public function submitForSettlement($transactionId, $amount = null, $attribs = [])
+ {
+ $this->_validateId($transactionId);
+ Util::verifyKeys(self::submitForSettlementSignature(), $attribs);
+ $attribs['amount'] = $amount;
+
+ $path = $this->_config->merchantPath() . '/transactions/'. $transactionId . '/submit_for_settlement';
+ $response = $this->_http->put($path, ['transaction' => $attribs]);
+ return $this->_verifyGatewayResponse($response);
+ }
+
+ public function submitForSettlementNoValidate($transactionId, $amount = null, $attribs = [])
+ {
+ $result = $this->submitForSettlement($transactionId, $amount, $attribs);
+ return Util::returnObjectOrThrowException(__CLASS__, $result);
+ }
+
+ public function updateDetails($transactionId, $attribs = [])
+ {
+ $this->_validateId($transactionId);
+ Util::verifyKeys(self::updateDetailsSignature(), $attribs);
+
+ $path = $this->_config->merchantPath() . '/transactions/'. $transactionId . '/update_details';
+ $response = $this->_http->put($path, ['transaction' => $attribs]);
+ return $this->_verifyGatewayResponse($response);
+ }
+
+ public function submitForPartialSettlement($transactionId, $amount, $attribs = [])
+ {
+ $this->_validateId($transactionId);
+ Util::verifyKeys(self::submitForSettlementSignature(), $attribs);
+ $attribs['amount'] = $amount;
+
+ $path = $this->_config->merchantPath() . '/transactions/'. $transactionId . '/submit_for_partial_settlement';
+ $response = $this->_http->post($path, ['transaction' => $attribs]);
+ return $this->_verifyGatewayResponse($response);
+ }
+
+ public function holdInEscrow($transactionId)
+ {
+ $this->_validateId($transactionId);
+
+ $path = $this->_config->merchantPath() . '/transactions/' . $transactionId . '/hold_in_escrow';
+ $response = $this->_http->put($path, []);
+ return $this->_verifyGatewayResponse($response);
+ }
+
+ public function releaseFromEscrow($transactionId)
+ {
+ $this->_validateId($transactionId);
+
+ $path = $this->_config->merchantPath() . '/transactions/' . $transactionId . '/release_from_escrow';
+ $response = $this->_http->put($path, []);
+ return $this->_verifyGatewayResponse($response);
+ }
+
+ public function cancelRelease($transactionId)
+ {
+ $this->_validateId($transactionId);
+
+ $path = $this->_config->merchantPath() . '/transactions/' . $transactionId . '/cancel_release';
+ $response = $this->_http->put($path, []);
+ return $this->_verifyGatewayResponse($response);
+ }
+
+ public function refund($transactionId, $amount_or_options = null)
+ {
+ self::_validateId($transactionId);
+
+ if(gettype($amount_or_options) == "array") {
+ $options = $amount_or_options;
+ } else {
+ $options = [
+ "amount" => $amount_or_options
+ ];
+ }
+ Util::verifyKeys(self::refundSignature(), $options);
+
+ $params = ['transaction' => $options];
+ $path = $this->_config->merchantPath() . '/transactions/' . $transactionId . '/refund';
+ $response = $this->_http->post($path, $params);
+ return $this->_verifyGatewayResponse($response);
+ }
+
+ /**
+ * sends the create request to the gateway
+ *
+ * @ignore
+ * @param var $subPath
+ * @param array $params
+ * @return Result\Successful|Result\Error
+ */
+ public function _doCreate($subPath, $params)
+ {
+ $fullPath = $this->_config->merchantPath() . $subPath;
+ $response = $this->_http->post($fullPath, $params);
+
+ return $this->_verifyGatewayResponse($response);
+ }
+
+ /**
+ * verifies that a valid transaction id is being used
+ * @ignore
+ * @param string transaction id
+ * @throws InvalidArgumentException
+ */
+ private function _validateId($id = null) {
+ if (empty($id)) {
+ throw new InvalidArgumentException(
+ 'expected transaction id to be set'
+ );
+ }
+ if (!preg_match('/^[0-9a-z]+$/', $id)) {
+ throw new InvalidArgumentException(
+ $id . ' is an invalid transaction id.'
+ );
+ }
+ }
+
+ /**
+ * generic method for validating incoming gateway responses
+ *
+ * creates a new Transaction object and encapsulates
+ * it inside a Result\Successful object, or
+ * encapsulates a Errors object inside a Result\Error
+ * alternatively, throws an Unexpected exception if the response is invalid.
+ *
+ * @ignore
+ * @param array $response gateway response values
+ * @return Result\Successful|Result\Error
+ * @throws Exception\Unexpected
+ */
+ private function _verifyGatewayResponse($response)
+ {
+ if (isset($response['transaction'])) {
+ // return a populated instance of Transaction
+ return new Result\Successful(
+ Transaction::factory($response['transaction'])
+ );
+ } else if (isset($response['apiErrorResponse'])) {
+ return new Result\Error($response['apiErrorResponse']);
+ } else {
+ throw new Exception\Unexpected(
+ "Expected transaction or apiErrorResponse"
+ );
+ }
+ }
+}
+class_alias('Braintree\TransactionGateway', 'Braintree_TransactionGateway');
diff --git a/lib/Braintree/TransactionLineItem.php b/lib/Braintree/TransactionLineItem.php
new file mode 100644
index 0000000..69aa69f
--- /dev/null
+++ b/lib/Braintree/TransactionLineItem.php
@@ -0,0 +1,55 @@
+transactionLineItem()->findAll($transactionId);
+ }
+}
+class_alias('Braintree\TransactionLineItem', 'Braintree_TransactionLineItem');
+class_alias('Braintree\TransactionLineItem', 'Braintree\Transaction\LineItem');
+class_alias('Braintree\TransactionLineItem', 'Braintree_Transaction_LineItem');
diff --git a/lib/Braintree/TransactionLineItemGateway.php b/lib/Braintree/TransactionLineItemGateway.php
new file mode 100644
index 0000000..7b1db2f
--- /dev/null
+++ b/lib/Braintree/TransactionLineItemGateway.php
@@ -0,0 +1,67 @@
+_gateway = $gateway;
+ $this->_config = $gateway->config;
+ $this->_config->assertHasAccessTokenOrKeys();
+ $this->_http = new Http($gateway->config);
+ }
+
+ /**
+ * @access public
+ * @param string id
+ * @return Transaction
+ */
+ public function findAll($id)
+ {
+ $this->_validateId($id);
+ try {
+ $path = $this->_config->merchantPath() . '/transactions/' . $id . '/line_items';
+ $response = $this->_http->get($path);
+
+ $lineItems = [];
+ if (isset($response['lineItems'])) {
+ foreach ($response['lineItems'] AS $lineItem) {
+ $lineItems[] = new TransactionLineItem($lineItem);
+ }
+ }
+ return $lineItems;
+ } catch (Exception\NotFound $e) {
+ throw new Exception\NotFound('transaction line items with id ' . $id . ' not found');
+ }
+ }
+
+ /**
+ * verifies that a valid transaction id is being used
+ * @ignore
+ * @param string transaction id
+ * @throws InvalidArgumentException
+ */
+ private function _validateId($id = null) {
+ if (empty($id)) {
+ throw new InvalidArgumentException('expected transaction id to be set');
+ }
+ if (!preg_match('/^[0-9a-z]+$/', $id)) {
+ throw new InvalidArgumentException($id . ' is an invalid transaction id.');
+ }
+ }
+}
+class_alias('Braintree\TransactionLineItemGateway', 'Braintree_TransactionLineItemGateway');
diff --git a/lib/Braintree/TransactionSearch.php b/lib/Braintree/TransactionSearch.php
index 487d781..5271b0b 100644
--- a/lib/Braintree/TransactionSearch.php
+++ b/lib/Braintree/TransactionSearch.php
@@ -1,125 +1,131 @@
- * $trData = Braintree_TransparentRedirect::createCustomerData(array(
+ * $trData = TransparentRedirect::createCustomerData(array(
* 'redirectUrl => 'http://example.com/redirect_back_to_merchant_site',
* ));
*
@@ -32,7 +25,7 @@
* amount, include the amount in the trData.
*
*
- * $trData = Braintree_TransparentRedirect::transactionData(array(
+ * $trData = TransparentRedirect::transactionData(array(
* 'redirectUrl' => 'http://example.com/complete_transaction',
* 'transaction' => array('amount' => '100.00'),
* ));
@@ -41,9 +34,8 @@
*
* @package Braintree
* @category Resources
- * @copyright 2010 Braintree Payment Solutions
*/
-class Braintree_TransparentRedirect
+class TransparentRedirect
{
// Request Kinds
const CREATE_TRANSACTION = 'create_transaction';
@@ -52,276 +44,57 @@ class Braintree_TransparentRedirect
const CREATE_CUSTOMER = 'create_customer';
const UPDATE_CUSTOMER = 'update_customer';
- /**
- *
- * @ignore
- */
- private static $_transparentRedirectKeys = 'redirectUrl';
- private static $_createCustomerSignature;
- private static $_updateCustomerSignature;
- private static $_transactionSignature;
- private static $_createCreditCardSignature;
- private static $_updateCreditCardSignature;
-
-
/**
* @ignore
* don't permit an explicit call of the constructor!
- * (like $t = new Braintree_TransparentRedirect())
+ * (like $t = new TransparentRedirect())
*/
protected function __construct()
{
}
- /**
- * create signatures for different call types
- * @ignore
- */
- public static function init()
- {
- self::$_createCustomerSignature = array(
- self::$_transparentRedirectKeys,
- array('customer' => Braintree_Customer::createSignature()),
- );
- self::$_updateCustomerSignature = array(
- self::$_transparentRedirectKeys,
- 'customerId',
- array('customer' => Braintree_Customer::updateSignature()),
- );
- self::$_transactionSignature = array(
- self::$_transparentRedirectKeys,
- array('transaction' => Braintree_Transaction::createSignature()),
- );
- self::$_createCreditCardSignature = array(
- self::$_transparentRedirectKeys,
- array('creditCard' => Braintree_CreditCard::createSignature()),
- );
- self::$_updateCreditCardSignature = array(
- self::$_transparentRedirectKeys,
- 'paymentMethodToken',
- array('creditCard' => Braintree_CreditCard::updateSignature()),
- );
- }
+ // static methods redirecting to gateway
public static function confirm($queryString)
{
- $params = Braintree_TransparentRedirect::parseAndValidateQueryString(
- $queryString
- );
- $confirmationKlasses = array(
- Braintree_TransparentRedirect::CREATE_TRANSACTION => 'Braintree_Transaction',
- Braintree_TransparentRedirect::CREATE_CUSTOMER => 'Braintree_Customer',
- Braintree_TransparentRedirect::UPDATE_CUSTOMER => 'Braintree_Customer',
- Braintree_TransparentRedirect::CREATE_PAYMENT_METHOD => 'Braintree_CreditCard',
- Braintree_TransparentRedirect::UPDATE_PAYMENT_METHOD => 'Braintree_CreditCard'
- );
- return call_user_func(array($confirmationKlasses[$params["kind"]], '_doCreate'),
- '/transparent_redirect_requests/' . $params['id'] . '/confirm',
- array()
- );
+ return Configuration::gateway()->transparentRedirect()->confirm($queryString);
}
- /**
- * returns the trData string for creating a credit card,
- * @param array $params
- * @return string
- */
public static function createCreditCardData($params)
{
- Braintree_Util::verifyKeys(
- self::$_createCreditCardSignature,
- $params
- );
- $params["kind"] = Braintree_TransparentRedirect::CREATE_PAYMENT_METHOD;
- return self::_data($params);
+ return Configuration::gateway()->transparentRedirect()->createCreditCardData($params);
}
- /**
- * returns the trData string for creating a customer.
- * @param array $params
- * @return string
- */
public static function createCustomerData($params)
{
- Braintree_Util::verifyKeys(
- self::$_createCustomerSignature,
- $params
- );
- $params["kind"] = Braintree_TransparentRedirect::CREATE_CUSTOMER;
- return self::_data($params);
-
+ return Configuration::gateway()->transparentRedirect()->createCustomerData($params);
}
public static function url()
{
- return Braintree_Configuration::merchantUrl() . "/transparent_redirect_requests";
+ return Configuration::gateway()->transparentRedirect()->url();
}
- /**
- * returns the trData string for creating a transaction
- * @param array $params
- * @return string
- */
public static function transactionData($params)
{
- Braintree_Util::verifyKeys(
- self::$_transactionSignature,
- $params
- );
- $params["kind"] = Braintree_TransparentRedirect::CREATE_TRANSACTION;
- $transactionType = isset($params['transaction']['type']) ?
- $params['transaction']['type'] :
- null;
- if ($transactionType != Braintree_Transaction::SALE && $transactionType != Braintree_Transaction::CREDIT) {
- throw new InvalidArgumentException(
- 'expected transaction[type] of sale or credit, was: ' .
- $transactionType
- );
- }
-
- return self::_data($params);
+ return Configuration::gateway()->transparentRedirect()->transactionData($params);
}
- /**
- * Returns the trData string for updating a credit card.
- *
- * The paymentMethodToken of the credit card to update is required.
- *
- *
- * $trData = Braintree_TransparentRedirect::updateCreditCardData(array(
- * 'redirectUrl' => 'http://example.com/redirect_here',
- * 'paymentMethodToken' => 'token123',
- * ));
- *
- *
- * @param array $params
- * @return string
- */
public static function updateCreditCardData($params)
{
- Braintree_Util::verifyKeys(
- self::$_updateCreditCardSignature,
- $params
- );
- if (!isset($params['paymentMethodToken'])) {
- throw new InvalidArgumentException(
- 'expected params to contain paymentMethodToken.'
- );
- }
- $params["kind"] = Braintree_TransparentRedirect::UPDATE_PAYMENT_METHOD;
- return self::_data($params);
+ return Configuration::gateway()->transparentRedirect()->updateCreditCardData($params);
}
- /**
- * Returns the trData string for updating a customer.
- *
- * The customerId of the customer to update is required.
- *
- *
- * $trData = Braintree_TransparentRedirect::updateCustomerData(array(
- * 'redirectUrl' => 'http://example.com/redirect_here',
- * 'customerId' => 'customer123',
- * ));
- *
- *
- * @param array $params
- * @return string
- */
public static function updateCustomerData($params)
{
- Braintree_Util::verifyKeys(
- self::$_updateCustomerSignature,
- $params
- );
- if (!isset($params['customerId'])) {
- throw new InvalidArgumentException(
- 'expected params to contain customerId of customer to update'
- );
- }
- $params["kind"] = Braintree_TransparentRedirect::UPDATE_CUSTOMER;
- return self::_data($params);
+ return Configuration::gateway()->transparentRedirect()->updateCustomerData($params);
}
public static function parseAndValidateQueryString($queryString)
{
- // parse the params into an array
- parse_str($queryString, $params);
- // remove the hash
- $queryStringWithoutHash = null;
- if(preg_match('/^(.*)&hash=[a-f0-9]+$/', $queryString, $match)) {
- $queryStringWithoutHash = $match[1];
- }
-
- if($params['http_status'] != '200') {
- $message = null;
- if(array_key_exists('bt_message', $params)) {
- $message = $params['bt_message'];
- }
- Braintree_Util::throwStatusCodeException($params['http_status'], $message);
- }
-
- // recreate the hash and compare it
- if(self::_hash($queryStringWithoutHash) == $params['hash']) {
- return $params;
- } else {
- throw new Braintree_Exception_ForgedQueryString();
- }
- }
-
-
- /**
- *
- * @ignore
- */
- private static function _data($params)
- {
- if (!isset($params['redirectUrl'])) {
- throw new InvalidArgumentException(
- 'expected params to contain redirectUrl'
- );
- }
- $params = self::_underscoreKeys($params);
- $now = new DateTime('now', new DateTimeZone('UTC'));
- $trDataParams = array_merge($params,
- array(
- 'api_version' => Braintree_Configuration::API_VERSION,
- 'public_key' => Braintree_Configuration::publicKey(),
- 'time' => $now->format('YmdHis'),
- )
- );
- ksort($trDataParams);
- $trDataSegment = http_build_query($trDataParams, null, '&');
- $trDataHash = self::_hash($trDataSegment);
- return "$trDataHash|$trDataSegment";
- }
-
- private static function _underscoreKeys($array)
- {
- foreach($array as $key=>$value)
- {
- $newKey = Braintree_Util::camelCaseToDelimiter($key, '_');
- unset($array[$key]);
- if (is_array($value))
- {
- $array[$newKey] = self::_underscoreKeys($value);
- }
- else
- {
- $array[$newKey] = $value;
- }
- }
- return $array;
- }
-
- /**
- * @ignore
- */
- private static function _hash($string)
- {
- return Braintree_Digest::hexDigest($string);
+ return Configuration::gateway()->transparentRedirect()->parseAndValidateQueryString($queryString);
}
-
}
-Braintree_TransparentRedirect::init();
+class_alias('Braintree\TransparentRedirect', 'Braintree_TransparentRedirect');
diff --git a/lib/Braintree/TransparentRedirectGateway.php b/lib/Braintree/TransparentRedirectGateway.php
new file mode 100644
index 0000000..3308af0
--- /dev/null
+++ b/lib/Braintree/TransparentRedirectGateway.php
@@ -0,0 +1,289 @@
+_gateway = $gateway;
+ $this->_config = $gateway->config;
+ $this->_config->assertHasAccessTokenOrKeys();
+ }
+
+ /**
+ *
+ * @ignore
+ */
+ private static $_transparentRedirectKeys = 'redirectUrl';
+ private static $_createCustomerSignature;
+ private static $_updateCustomerSignature;
+ private static $_transactionSignature;
+ private static $_createCreditCardSignature;
+ private static $_updateCreditCardSignature;
+
+ /**
+ * create signatures for different call types
+ * @ignore
+ */
+ public static function init()
+ {
+
+ self::$_createCustomerSignature = [
+ self::$_transparentRedirectKeys,
+ ['customer' => CustomerGateway::createSignature()],
+ ];
+ self::$_updateCustomerSignature = [
+ self::$_transparentRedirectKeys,
+ 'customerId',
+ ['customer' => CustomerGateway::updateSignature()],
+ ];
+ self::$_transactionSignature = [
+ self::$_transparentRedirectKeys,
+ ['transaction' => TransactionGateway::createSignature()],
+ ];
+ self::$_createCreditCardSignature = [
+ self::$_transparentRedirectKeys,
+ ['creditCard' => CreditCardGateway::createSignature()],
+ ];
+ self::$_updateCreditCardSignature = [
+ self::$_transparentRedirectKeys,
+ 'paymentMethodToken',
+ ['creditCard' => CreditCardGateway::updateSignature()],
+ ];
+ }
+
+ public function confirm($queryString)
+ {
+ $params = TransparentRedirect::parseAndValidateQueryString(
+ $queryString
+ );
+ $confirmationKlasses = [
+ TransparentRedirect::CREATE_TRANSACTION => 'Braintree\TransactionGateway',
+ TransparentRedirect::CREATE_CUSTOMER => 'Braintree\CustomerGateway',
+ TransparentRedirect::UPDATE_CUSTOMER => 'Braintree\CustomerGateway',
+ TransparentRedirect::CREATE_PAYMENT_METHOD => 'Braintree\CreditCardGateway',
+ TransparentRedirect::UPDATE_PAYMENT_METHOD => 'Braintree\CreditCardGateway',
+ ];
+ $confirmationGateway = new $confirmationKlasses[$params["kind"]]($this->_gateway);
+ return $confirmationGateway->_doCreate('/transparent_redirect_requests/' . $params['id'] . '/confirm', []);
+ }
+
+ /**
+ * returns the trData string for creating a credit card,
+ * @param array $params
+ * @return string
+ */
+ public function createCreditCardData($params)
+ {
+ Util::verifyKeys(
+ self::$_createCreditCardSignature,
+ $params
+ );
+ $params["kind"] = TransparentRedirect::CREATE_PAYMENT_METHOD;
+ return $this->_data($params);
+ }
+
+ /**
+ * returns the trData string for creating a customer.
+ * @param array $params
+ * @return string
+ */
+ public function createCustomerData($params)
+ {
+ Util::verifyKeys(
+ self::$_createCustomerSignature,
+ $params
+ );
+ $params["kind"] = TransparentRedirect::CREATE_CUSTOMER;
+ return $this->_data($params);
+
+ }
+
+ public function url()
+ {
+ return $this->_config->baseUrl() . $this->_config->merchantPath() . '/transparent_redirect_requests';
+ }
+
+ /**
+ * returns the trData string for creating a transaction
+ * @param array $params
+ * @return string
+ */
+ public function transactionData($params)
+ {
+ Util::verifyKeys(
+ self::$_transactionSignature,
+ $params
+ );
+ $params["kind"] = TransparentRedirect::CREATE_TRANSACTION;
+ $transactionType = isset($params['transaction']['type']) ?
+ $params['transaction']['type'] :
+ null;
+ if ($transactionType != Transaction::SALE && $transactionType != Transaction::CREDIT) {
+ throw new InvalidArgumentException(
+ 'expected transaction[type] of sale or credit, was: ' .
+ $transactionType
+ );
+ }
+
+ return $this->_data($params);
+ }
+
+ /**
+ * Returns the trData string for updating a credit card.
+ *
+ * The paymentMethodToken of the credit card to update is required.
+ *
+ *
+ * $trData = TransparentRedirect::updateCreditCardData(array(
+ * 'redirectUrl' => 'http://example.com/redirect_here',
+ * 'paymentMethodToken' => 'token123',
+ * ));
+ *
+ *
+ * @param array $params
+ * @return string
+ */
+ public function updateCreditCardData($params)
+ {
+ Util::verifyKeys(
+ self::$_updateCreditCardSignature,
+ $params
+ );
+ if (!isset($params['paymentMethodToken'])) {
+ throw new InvalidArgumentException(
+ 'expected params to contain paymentMethodToken.'
+ );
+ }
+ $params["kind"] = TransparentRedirect::UPDATE_PAYMENT_METHOD;
+ return $this->_data($params);
+ }
+
+ /**
+ * Returns the trData string for updating a customer.
+ *
+ * The customerId of the customer to update is required.
+ *
+ *
+ * $trData = TransparentRedirect::updateCustomerData(array(
+ * 'redirectUrl' => 'http://example.com/redirect_here',
+ * 'customerId' => 'customer123',
+ * ));
+ *
+ *
+ * @param array $params
+ * @return string
+ */
+ public function updateCustomerData($params)
+ {
+ Util::verifyKeys(
+ self::$_updateCustomerSignature,
+ $params
+ );
+ if (!isset($params['customerId'])) {
+ throw new InvalidArgumentException(
+ 'expected params to contain customerId of customer to update'
+ );
+ }
+ $params["kind"] = TransparentRedirect::UPDATE_CUSTOMER;
+ return $this->_data($params);
+ }
+
+ public function parseAndValidateQueryString($queryString)
+ {
+ // parse the params into an array
+ parse_str($queryString, $params);
+ // remove the hash
+ $queryStringWithoutHash = null;
+ if (preg_match('/^(.*)&hash=[a-f0-9]+$/', $queryString, $match)) {
+ $queryStringWithoutHash = $match[1];
+ }
+
+ if($params['http_status'] != '200') {
+ $message = null;
+ if(array_key_exists('bt_message', $params)) {
+ $message = $params['bt_message'];
+ }
+ Util::throwStatusCodeException(isset($params['http_status']) ? $params['http_status'] : null, $message);
+ }
+
+ // recreate the hash and compare it
+ if ($this->_hash($queryStringWithoutHash) == $params['hash']) {
+ return $params;
+ } else {
+ throw new Exception\ForgedQueryString();
+ }
+ }
+
+
+ /**
+ *
+ * @ignore
+ */
+ private function _data($params)
+ {
+ if (!isset($params['redirectUrl'])) {
+ throw new InvalidArgumentException(
+ 'expected params to contain redirectUrl'
+ );
+ }
+ $params = $this->_underscoreKeys($params);
+ $now = new DateTime('now', new DateTimeZone('UTC'));
+ $trDataParams = array_merge($params,
+ [
+ 'api_version' => Configuration::API_VERSION,
+ 'public_key' => $this->_config->publicKey(),
+ 'time' => $now->format('YmdHis'),
+ ]
+ );
+ ksort($trDataParams);
+ $urlEncodedData = http_build_query($trDataParams, null, "&");
+ $signatureService = new SignatureService(
+ $this->_config->privateKey(),
+ "Braintree\Digest::hexDigestSha1"
+ );
+ return $signatureService->sign($urlEncodedData);
+ }
+
+ private function _underscoreKeys($array)
+ {
+ foreach($array as $key=>$value)
+ {
+ $newKey = Util::camelCaseToDelimiter($key, '_');
+ unset($array[$key]);
+ if (is_array($value))
+ {
+ $array[$newKey] = $this->_underscoreKeys($value);
+ }
+ else
+ {
+ $array[$newKey] = $value;
+ }
+ }
+ return $array;
+ }
+
+ /**
+ * @ignore
+ */
+ private function _hash($string)
+ {
+ return Digest::hexDigestSha1($this->_config->privateKey(), $string);
+ }
+}
+TransparentRedirectGateway::init();
+class_alias('Braintree\TransparentRedirectGateway', 'Braintree_TransparentRedirectGateway');
diff --git a/lib/Braintree/UnknownPaymentMethod.php b/lib/Braintree/UnknownPaymentMethod.php
new file mode 100644
index 0000000..74e7289
--- /dev/null
+++ b/lib/Braintree/UnknownPaymentMethod.php
@@ -0,0 +1,69 @@
+== More information ==
+ *
+ *
+ * @package Braintree
+ * @category Resources
+ *
+ * @property-read string $token
+ * @property-read string $imageUrl
+ */
+class UnknownPaymentMethod extends Base
+{
+
+
+ /**
+ * factory method: returns an instance of UnknownPaymentMethod
+ * to the requesting method, with populated properties
+ *
+ * @ignore
+ * @return UnknownPaymentMethod
+ */
+ public static function factory($attributes)
+ {
+ $instance = new self();
+ $values = array_values($attributes);
+ $instance->_initialize(array_shift($values));
+ return $instance;
+ }
+
+ /* instance methods */
+
+ /**
+ * returns false if default is null or false
+ *
+ * @return boolean
+ */
+ public function isDefault()
+ {
+ return $this->default;
+ }
+
+ /**
+ * sets instance properties from an array of values
+ *
+ * @access protected
+ * @param array $unknownPaymentMethodAttribs array of unknownPaymentMethod data
+ * @return void
+ */
+ protected function _initialize($unknownPaymentMethodAttribs)
+ {
+ // set the attributes
+ $this->imageUrl = 'https://assets.braintreegateway.com/payment_method_logo/unknown.png';
+ $this->_attributes = $unknownPaymentMethodAttribs;
+ }
+
+}
+class_alias('Braintree\UnknownPaymentMethod', 'Braintree_UnknownPaymentMethod');
diff --git a/lib/Braintree/UsBankAccount.php b/lib/Braintree/UsBankAccount.php
new file mode 100644
index 0000000..4ebc420
--- /dev/null
+++ b/lib/Braintree/UsBankAccount.php
@@ -0,0 +1,117 @@
+== More information ==
+ *
+ *
+ * @package Braintree
+ * @category Resources
+ *
+ * @property-read string $customerId
+ * @property-read string $email
+ * @property-read string $token
+ * @property-read string $imageUrl
+ * @property-read string $routingNumber
+ * @property-read string $accountType
+ * @property-read string $accountHolderName
+ * @property-read string $last4
+ * @property-read string $bankName
+ * @property-read string $achMandate
+ * @property-read boolean $default
+ * @property-read boolean $verified
+ */
+class UsBankAccount extends Base
+{
+ /**
+ * factory method: returns an instance of UsBankAccount
+ * to the requesting method, with populated properties
+ *
+ * @ignore
+ * @return UsBankAccount
+ */
+ public static function factory($attributes)
+ {
+ $instance = new self();
+ $instance->_initialize($attributes);
+ return $instance;
+ }
+
+ /* instance methods */
+
+ /**
+ * sets instance properties from an array of values
+ *
+ * @access protected
+ * @param array $usBankAccountAttribs array of usBankAccount data
+ * @return void
+ */
+ protected function _initialize($usBankAccountAttribs)
+ {
+ // set the attributes
+ $this->_attributes = $usBankAccountAttribs;
+
+ $achMandate = isset($usBankAccountAttribs['achMandate']) ?
+ AchMandate::factory($usBankAccountAttribs['achMandate']) :
+ null;
+ $this->_set('achMandate', $achMandate);
+
+ if (isset($usBankAccountAttribs['verifications'])) {
+ $verification_records = $usBankAccountAttribs['verifications'];
+
+ $verifications = array();
+ for ($i = 0; $i < count($verification_records); $i++) {
+ $verifications[$i] = UsBankAccountVerification::factory($verification_records[$i]);
+ }
+ $this->_set('verifications', $verifications);
+ } else {
+ $this->_set('verifications', null);
+ }
+ }
+
+ /**
+ * create a printable representation of the object as:
+ * ClassName[property=value, property=value]
+ * @return string
+ */
+ public function __toString()
+ {
+ return __CLASS__ . '[' .
+ Util::attributesToString($this->_attributes) . ']';
+ }
+
+ /**
+ * returns false if default is null or false
+ *
+ * @return boolean
+ */
+ public function isDefault()
+ {
+ return $this->default;
+ }
+
+ // static methods redirecting to gateway
+
+ public static function find($token)
+ {
+ return Configuration::gateway()->usBankAccount()->find($token);
+ }
+
+ public static function sale($token, $transactionAttribs)
+ {
+ $transactionAttribs['options'] = [
+ 'submitForSettlement' => true
+ ];
+ return Configuration::gateway()->usBankAccount()->sale($token, $transactionAttribs);
+ }
+}
+class_alias('Braintree\UsBankAccount', 'Braintree_UsBankAccount');
diff --git a/lib/Braintree/UsBankAccountGateway.php b/lib/Braintree/UsBankAccountGateway.php
new file mode 100644
index 0000000..6e3393c
--- /dev/null
+++ b/lib/Braintree/UsBankAccountGateway.php
@@ -0,0 +1,106 @@
+== More information ==
+ *
+ *
+ * @package Braintree
+ * @category Resources
+ */
+class UsBankAccountGateway
+{
+ private $_gateway;
+ private $_config;
+ private $_http;
+
+ public function __construct($gateway)
+ {
+ $this->_gateway = $gateway;
+ $this->_config = $gateway->config;
+ $this->_config->assertHasAccessTokenOrKeys();
+ $this->_http = new Http($gateway->config);
+ }
+
+
+ /**
+ * find a usBankAccount by token
+ *
+ * @access public
+ * @param string $token paypal accountunique id
+ * @return UsBankAccount
+ * @throws Exception\NotFound
+ */
+ public function find($token)
+ {
+ try {
+ $path = $this->_config->merchantPath() . '/payment_methods/us_bank_account/' . $token;
+ $response = $this->_http->get($path);
+ return UsBankAccount::factory($response['usBankAccount']);
+ } catch (Exception\NotFound $e) {
+ throw new Exception\NotFound(
+ 'US bank account with token ' . $token . ' not found'
+ );
+ }
+
+ }
+
+ /**
+ * create a new sale for the current UsBank account
+ *
+ * @param string $token
+ * @param array $transactionAttribs
+ * @return Result\Successful|Result\Error
+ * @see Transaction::sale()
+ */
+ public function sale($token, $transactionAttribs)
+ {
+ return Transaction::sale(
+ array_merge(
+ $transactionAttribs,
+ ['paymentMethodToken' => $token]
+ )
+ );
+ }
+
+ /**
+ * generic method for validating incoming gateway responses
+ *
+ * creates a new UsBankAccount object and encapsulates
+ * it inside a Result\Successful object, or
+ * encapsulates a Errors object inside a Result\Error
+ * alternatively, throws an Unexpected exception if the response is invalid.
+ *
+ * @ignore
+ * @param array $response gateway response values
+ * @return Result\Successful|Result\Error
+ * @throws Exception\Unexpected
+ */
+ private function _verifyGatewayResponse($response)
+ {
+ if (isset($response['usBankAccount'])) {
+ // return a populated instance of UsBankAccount
+ return new Result\Successful(
+ UsBankAccount::factory($response['usBankAccount'])
+ );
+ } else if (isset($response['apiErrorResponse'])) {
+ return new Result\Error($response['apiErrorResponse']);
+ } else {
+ throw new Exception\Unexpected(
+ 'Expected US bank account or apiErrorResponse'
+ );
+ }
+ }
+}
+class_alias('Braintree\UsBankAccountGateway', 'Braintree_UsBankAccountGateway');
diff --git a/lib/Braintree/UsBankAccountVerification.php b/lib/Braintree/UsBankAccountVerification.php
new file mode 100644
index 0000000..a062a55
--- /dev/null
+++ b/lib/Braintree/UsBankAccountVerification.php
@@ -0,0 +1,102 @@
+== More information ==
+ *
+ *
+ * @package Braintree
+ * @category Resources
+ *
+ */
+class UsBankAccountVerification extends Result\UsBankAccountVerification
+{
+ /**
+ * factory method: returns an instance of UsBankAccountVerification
+ * to the requesting method, with populated properties
+ *
+ * @ignore
+ * @return UsBankAccountVerification
+ */
+ public static function factory($attributes)
+ {
+ $instance = new self($attributes);
+ $instance->_initialize($attributes);
+ return $instance;
+ }
+
+ /* instance methods */
+
+ /**
+ * sets instance properties from an array of values
+ *
+ * @access protected
+ * @param array $usBankAccountVerificationAttribs array of usBankAccountVerification data
+ * @return void
+ */
+ protected function _initialize($usBankAccountVerificationAttribs)
+ {
+ // set the attributes
+ $this->_attributes = $usBankAccountVerificationAttribs;
+ }
+
+ /**
+ * create a printable representation of the object as:
+ * ClassName[property=value, property=value]
+ * @return string
+ */
+ public function __toString()
+ {
+ return __CLASS__ . '[' . Util::attributesToString($this->_attributes) . ']';
+ }
+
+
+ // static methods redirecting to gateway
+
+ /**
+ * finds a US bank account verification
+ *
+ * @access public
+ * @param string $token unique id
+ * @return UsBankAccountVerification
+ */
+ public static function find($token)
+ {
+ return Configuration::gateway()->usBankAccountVerification()->find($token);
+ }
+
+ /**
+ * Returns a ResourceCollection of US bank account verifications matching the search query.
+ *
+ * @access public
+ * @param mixed $query search query
+ * @return ResourceCollection
+ */
+ public static function search($query)
+ {
+ return Configuration::gateway()->usBankAccountVerification()->search($query);
+ }
+
+ /**
+ * Returns a ResourceCollection of US bank account verifications matching the search query.
+ *
+ * @access public
+ * @param string $token unique id
+ * @param array $amounts micro transfer amounts
+ * @return ResourceCollection
+ */
+ public static function confirmMicroTransferAmounts($token, $amounts)
+ {
+ return Configuration::gateway()->usBankAccountVerification()->confirmMicroTransferAmounts($token, $amounts);
+ }
+}
+class_alias('Braintree\UsBankAccountVerification', 'Braintree_UsBankAccountVerification');
diff --git a/lib/Braintree/UsBankAccountVerificationGateway.php b/lib/Braintree/UsBankAccountVerificationGateway.php
new file mode 100644
index 0000000..31397ad
--- /dev/null
+++ b/lib/Braintree/UsBankAccountVerificationGateway.php
@@ -0,0 +1,129 @@
+== More information ==
+ *
+ *
+ * @package Braintree
+ * @category Resources
+ */
+class UsBankAccountVerificationGateway
+{
+ private $_gateway;
+ private $_config;
+ private $_http;
+
+ public function __construct($gateway)
+ {
+ $this->_gateway = $gateway;
+ $this->_config = $gateway->config;
+ $this->_config->assertHasAccessTokenOrKeys();
+ $this->_http = new Http($gateway->config);
+ }
+
+ /**
+ * find a usBankAccountVerification by token
+ *
+ * @access public
+ * @param string $token unique id
+ * @return UsBankAccountVerification
+ * @throws Exception\NotFound
+ */
+ public function find($token)
+ {
+ try {
+ $path = $this->_config->merchantPath() . '/us_bank_account_verifications/' . $token;
+ $response = $this->_http->get($path);
+ return UsBankAccountVerification::factory($response['usBankAccountVerification']);
+ } catch (Exception\NotFound $e) {
+ throw new Exception\NotFound(
+ 'US bank account with token ' . $token . ' not found'
+ );
+ }
+ }
+
+ public function search($query)
+ {
+ $criteria = [];
+ foreach ($query as $term) {
+ $criteria[$term->name] = $term->toparam();
+ }
+
+ $path = $this->_config->merchantPath() . '/us_bank_account_verifications/advanced_search_ids';
+ $response = $this->_http->post($path, ['search' => $criteria]);
+ $pager = [
+ 'object' => $this,
+ 'method' => 'fetch',
+ 'methodArgs' => [$query]
+ ];
+
+ return new ResourceCollection($response, $pager);
+ }
+
+ /**
+ * complete micro transfer verification by confirming the transfer amounts
+ *
+ * @access public
+ * @param string $token unique id
+ * @param array $amounts amounts deposited in micro transfer
+ * @return UsBankAccountVerification
+ * @throws Exception\Unexpected
+ */
+ public function confirmMicroTransferAmounts($token, $amounts)
+ {
+ try {
+ $path = $this->_config->merchantPath() . '/us_bank_account_verifications/' . $token . '/confirm_micro_transfer_amounts';
+ $response = $this->_http->put($path, [
+ "us_bank_account_verification" => ["deposit_amounts" => $amounts]
+ ]);
+ return $this->_verifyGatewayResponse($response);
+ } catch (Exception\Unexpected $e) {
+ throw new Exception\Unexpected(
+ 'Unexpected exception.'
+ );
+ }
+ }
+
+ /**
+ * generic method for validating incoming gateway responses
+ *
+ * creates a new UsBankAccountVerification object and encapsulates
+ * it inside a Result\Successful object, or
+ * encapsulates a Errors object inside a Result\Error
+ * alternatively, throws an Unexpected exception if the response is invalid.
+ *
+ * @ignore
+ * @param array $response gateway response values
+ * @return Result\Successful|Result\Error
+ * @throws Exception\Unexpected
+ */
+ private function _verifyGatewayResponse($response)
+ {
+ if (isset($response['apiErrorResponse'])) {
+ return new Result\Error($response['apiErrorResponse']);
+ } else if (isset($response['usBankAccountVerification'])) {
+ // return a populated instance of UsBankAccountVerification
+ return new Result\Successful(
+ UsBankAccountVerification::factory($response['usBankAccountVerification'])
+ );
+ } else {
+ throw new Exception\Unexpected(
+ 'Expected US bank account or apiErrorResponse'
+ );
+ }
+ }
+}
+
+class_alias('Braintree\UsBankAccountVerificationGateway', 'Braintree_UsBankAccountVerificationGateway');
diff --git a/lib/Braintree/UsBankAccountVerificationSearch.php b/lib/Braintree/UsBankAccountVerificationSearch.php
new file mode 100644
index 0000000..a6336dc
--- /dev/null
+++ b/lib/Braintree/UsBankAccountVerificationSearch.php
@@ -0,0 +1,64 @@
+success) {
+ return $resultObj->$resultObjName;
+ } else {
+ throw new Exception\ValidationsFailed();
}
}
/**
- * removes the Braintree_ header from a classname
+ * removes the header from a classname
*
- * @param string $name Braintree_ClassName
- * @return camelCased classname minus Braintree_ header
+ * @param string $name ClassName
+ * @return camelCased classname minus header
*/
public static function cleanClassName($name)
{
- $classNamesToResponseKeys = array(
- 'CreditCard' => 'creditCard',
- 'Customer' => 'customer',
- 'Subscription' => 'subscription',
- 'Transaction' => 'transaction',
- 'CreditCardVerification' => 'verification',
- 'AddOn' => 'addOn',
- 'Discount' => 'discount',
- 'Plan' => 'plan',
- 'Address' => 'address',
- 'SettlementBatchSummary' => 'settlementBatchSummary',
- 'MerchantAccount' => 'merchantAccount'
- );
+ $classNamesToResponseKeys = [
+ 'Braintree\CreditCard' => 'creditCard',
+ 'Braintree_CreditCard' => 'creditCard',
+ 'Braintree\CreditCardGateway' => 'creditCard',
+ 'Braintree_CreditCardGateway' => 'creditCard',
+ 'Braintree\Customer' => 'customer',
+ 'Braintree_Customer' => 'customer',
+ 'Braintree\CustomerGateway' => 'customer',
+ 'Braintree_CustomerGateway' => 'customer',
+ 'Braintree\Subscription' => 'subscription',
+ 'Braintree_Subscription' => 'subscription',
+ 'Braintree\SubscriptionGateway' => 'subscription',
+ 'Braintree_SubscriptionGateway' => 'subscription',
+ 'Braintree\Transaction' => 'transaction',
+ 'Braintree_Transaction' => 'transaction',
+ 'Braintree\TransactionGateway' => 'transaction',
+ 'Braintree_TransactionGateway' => 'transaction',
+ 'Braintree\CreditCardVerification' => 'verification',
+ 'Braintree_CreditCardVerification' => 'verification',
+ 'Braintree\CreditCardVerificationGateway' => 'verification',
+ 'Braintree_CreditCardVerificationGateway' => 'verification',
+ 'Braintree\AddOn' => 'addOn',
+ 'Braintree_AddOn' => 'addOn',
+ 'Braintree\AddOnGateway' => 'addOn',
+ 'Braintree_AddOnGateway' => 'addOn',
+ 'Braintree\Discount' => 'discount',
+ 'Braintree_Discount' => 'discount',
+ 'Braintree\DiscountGateway' => 'discount',
+ 'Braintree_DiscountGateway' => 'discount',
+ 'Braintree\Dispute' => 'dispute',
+ 'Braintree_Dispute' => 'dispute',
+ 'Braintree\Dispute\EvidenceDetails' => 'evidence',
+ 'Braintree_Dispute_EvidenceDetails' => 'evidence',
+ 'Braintree\DocumentUpload' => 'documentUpload',
+ 'Braintree_DocumentUpload' => 'doumentUpload',
+ 'Braintree\Plan' => 'plan',
+ 'Braintree_Plan' => 'plan',
+ 'Braintree\PlanGateway' => 'plan',
+ 'Braintree_PlanGateway' => 'plan',
+ 'Braintree\Address' => 'address',
+ 'Braintree_Address' => 'address',
+ 'Braintree\AddressGateway' => 'address',
+ 'Braintree_AddressGateway' => 'address',
+ 'Braintree\SettlementBatchSummary' => 'settlementBatchSummary',
+ 'Braintree_SettlementBatchSummary' => 'settlementBatchSummary',
+ 'Braintree\SettlementBatchSummaryGateway' => 'settlementBatchSummary',
+ 'Braintree_SettlementBatchSummaryGateway' => 'settlementBatchSummary',
+ 'Braintree\Merchant' => 'merchant',
+ 'Braintree_Merchant' => 'merchant',
+ 'Braintree\MerchantGateway' => 'merchant',
+ 'Braintree_MerchantGateway' => 'merchant',
+ 'Braintree\MerchantAccount' => 'merchantAccount',
+ 'Braintree_MerchantAccount' => 'merchantAccount',
+ 'Braintree\MerchantAccountGateway' => 'merchantAccount',
+ 'Braintree_MerchantAccountGateway' => 'merchantAccount',
+ 'Braintree\OAuthCredentials' => 'credentials',
+ 'Braintree_OAuthCredentials' => 'credentials',
+ 'Braintree\OAuthResult' => 'result',
+ 'Braintree_OAuthResult' => 'result',
+ 'Braintree\PayPalAccount' => 'paypalAccount',
+ 'Braintree_PayPalAccount' => 'paypalAccount',
+ 'Braintree\PayPalAccountGateway' => 'paypalAccount',
+ 'Braintree_PayPalAccountGateway' => 'paypalAccount',
+ 'Braintree\UsBankAccountVerification' => 'usBankAccountVerification',
+ 'Braintree_UsBankAccountVerification' => 'usBankAccountVerification',
+ ];
- $name = str_replace('Braintree_', '', $name);
return $classNamesToResponseKeys[$name];
}
/**
*
* @param string $name className
- * @return string Braintree_ClassName
+ * @return string ClassName
*/
public static function buildClassName($name)
{
- $responseKeysToClassNames = array(
- 'creditCard' => 'CreditCard',
- 'customer' => 'Customer',
- 'subscription' => 'Subscription',
- 'transaction' => 'Transaction',
- 'verification' => 'CreditCardVerification',
- 'addOn' => 'AddOn',
- 'discount' => 'Discount',
- 'plan' => 'Plan',
- 'address' => 'Address',
- 'settlementBatchSummary' => 'SettlementBatchSummary',
- 'merchantAccount' => 'MerchantAccount'
- );
+ $responseKeysToClassNames = [
+ 'creditCard' => 'Braintree\CreditCard',
+ 'customer' => 'Braintree\Customer',
+ 'dispute' => 'Braintree\Dispute',
+ 'documentUpload' => 'Braintree\DocumentUpload',
+ 'subscription' => 'Braintree\Subscription',
+ 'transaction' => 'Braintree\Transaction',
+ 'verification' => 'Braintree\CreditCardVerification',
+ 'addOn' => 'Braintree\AddOn',
+ 'discount' => 'Braintree\Discount',
+ 'plan' => 'Braintree\Plan',
+ 'address' => 'Braintree\Address',
+ 'settlementBatchSummary' => 'Braintree\SettlementBatchSummary',
+ 'merchantAccount' => 'Braintree\MerchantAccount',
+ ];
- return 'Braintree_' . $responseKeysToClassNames[$name];
+ return (string) $responseKeysToClassNames[$name];
}
/**
@@ -134,16 +247,16 @@ public static function buildClassName($name)
*
* @access public
* @param string $string
+ * @param null|string $delimiter
* @return string modified string
*/
public static function delimiterToCamelCase($string, $delimiter = '[\-\_]')
{
- // php doesn't garbage collect functions created by create_function()
- // so use a static variable to avoid adding a new function to memory
- // every time this function is called.
static $callback = null;
if ($callback === null) {
- $callback = create_function('$matches', 'return strtoupper($matches[1]);');
+ $callback = function ($matches) {
+ return strtoupper($matches[1]);
+ };
}
return preg_replace_callback('/' . $delimiter . '(\w)/', $callback, $string);
@@ -166,20 +279,60 @@ public static function delimiterToUnderscore($string)
* find capitals and convert to delimiter + lowercase
*
* @access public
- * @param var $string
- * @return var modified string
+ * @param string $string
+ * @param null|string $delimiter
+ * @return string modified string
*/
public static function camelCaseToDelimiter($string, $delimiter = '-')
{
- // php doesn't garbage collect functions created by create_function()
- // so use a static variable to avoid adding a new function to memory
- // every time this function is called.
- static $callbacks = array();
- if (!isset($callbacks[$delimiter])) {
- $callbacks[$delimiter] = create_function('$matches', "return '$delimiter' . strtolower(\$matches[1]);");
+ return strtolower(preg_replace('/([A-Z])/', "$delimiter\\1", $string));
+ }
+
+ public static function delimiterToCamelCaseArray($array, $delimiter = '[\-\_]')
+ {
+ $converted = [];
+ foreach ($array as $key => $value) {
+ if (is_string($key)) {
+ $key = self::delimiterToCamelCase($key, $delimiter);
+ }
+
+ if (is_array($value)) {
+ // Make an exception for custom fields, which must be underscore (can't be
+ // camelCase).
+ if ($key === 'customFields') {
+ $value = self::delimiterToUnderscoreArray($value);
+ } else {
+ $value = self::delimiterToCamelCaseArray($value, $delimiter);
+ }
+ }
+ $converted[$key] = $value;
}
+ return $converted;
+ }
+
+ public static function camelCaseToDelimiterArray($array, $delimiter = '-')
+ {
+ $converted = [];
+ foreach ($array as $key => $value) {
+ if (is_string($key)) {
+ $key = self::camelCaseToDelimiter($key, $delimiter);
+ }
+ if (is_array($value)) {
+ $value = self::camelCaseToDelimiterArray($value, $delimiter);
+ }
+ $converted[$key] = $value;
+ }
+ return $converted;
+ }
- return preg_replace_callback('/([A-Z])/', $callbacks[$delimiter], $string);
+ public static function delimiterToUnderscoreArray($array)
+ {
+ $converted = [];
+ foreach ($array as $key => $value) {
+ $key = self::delimiterToUnderscore($key);
+ $converted[$key] = $value;
+ }
+ return $converted;
}
/**
@@ -187,24 +340,27 @@ public static function camelCaseToDelimiter($string, $delimiter = '-')
* @param array $array associative array to implode
* @param string $separator (optional, defaults to =)
* @param string $glue (optional, defaults to ', ')
+ * @return bool
*/
public static function implodeAssociativeArray($array, $separator = '=', $glue = ', ')
{
// build a new array with joined keys and values
$tmpArray = null;
foreach ($array AS $key => $value) {
- $tmpArray[] = $key . $separator . $value;
-
+ if ($value instanceof DateTime) {
+ $value = $value->format('r');
+ }
+ $tmpArray[] = $key . $separator . $value;
}
// implode and return the new array
return (is_array($tmpArray)) ? implode($glue, $tmpArray) : false;
}
public static function attributesToString($attributes) {
- $printableAttribs = array();
+ $printableAttribs = [];
foreach ($attributes AS $key => $value) {
if (is_array($value)) {
- $pAttrib = Braintree_Util::attributesToString($value);
+ $pAttrib = self::attributesToString($value);
} else if ($value instanceof DateTime) {
$pAttrib = $value->format(DateTime::RFC850);
} else {
@@ -212,7 +368,7 @@ public static function attributesToString($attributes) {
}
$printableAttribs[$key] = sprintf('%s', $pAttrib);
}
- return Braintree_Util::implodeAssociativeArray($printableAttribs);
+ return self::implodeAssociativeArray($printableAttribs);
}
/**
@@ -234,7 +390,7 @@ public static function verifyKeys($signature, $attributes)
if(!empty($invalidKeys)) {
asort($invalidKeys);
$sortedList = join(', ', $invalidKeys);
- throw new InvalidArgumentException('invalid keys: '. $sortedList);
+ throw new InvalidArgumentException('invalid keys: ' . $sortedList);
}
}
/**
@@ -245,7 +401,7 @@ public static function verifyKeys($signature, $attributes)
*/
private static function _flattenArray($keys, $namespace = null)
{
- $flattenedArray = array();
+ $flattenedArray = [];
foreach($keys AS $key) {
if(is_array($key)) {
$theKeys = array_keys($key);
@@ -264,7 +420,7 @@ private static function _flattenArray($keys, $namespace = null)
private static function _flattenUserKeys($keys, $namespace = null)
{
- $flattenedArray = array();
+ $flattenedArray = [];
foreach($keys AS $key => $value) {
$fullKey = empty($namespace) ? $key : $namespace;
@@ -306,3 +462,4 @@ private static function _removeWildcardKeys($validKeys, $invalidKeys)
return $invalidKeys;
}
}
+class_alias('Braintree\Util', 'Braintree_Util');
diff --git a/lib/Braintree/VenmoAccount.php b/lib/Braintree/VenmoAccount.php
new file mode 100644
index 0000000..91f0da3
--- /dev/null
+++ b/lib/Braintree/VenmoAccount.php
@@ -0,0 +1,75 @@
+== More information ==
+ *
+ * See {@link https://developers.braintreepayments.com/javascript+php}
+ *
+ * @package Braintree
+ * @category Resources
+ *
+ * @property-read \DateTime $createdAt
+ * @property-read string $customerId
+ * @property-read boolean $default
+ * @property-read string $imageUrl
+ * @property-read string $sourceDescription
+ * @property-read \Braintree\Subscription[] $subscriptions
+ * @property-read string $token
+ * @property-read \DateTime $updatedAt
+ * @property-read string $username
+ * @property-read string $venmoUserId
+ */
+class VenmoAccount extends Base
+{
+ /* instance methods */
+ /**
+ * returns false if default is null or false
+ *
+ * @return boolean
+ */
+ public function isDefault()
+ {
+ return $this->default;
+ }
+
+ /**
+ * factory method: returns an instance of VenmoAccount
+ * to the requesting method, with populated properties
+ *
+ * @ignore
+ * @return VenmoAccount
+ */
+ public static function factory($attributes)
+ {
+
+ $instance = new self();
+ $instance->_initialize($attributes);
+ return $instance;
+ }
+
+ /**
+ * sets instance properties from an array of values
+ *
+ * @access protected
+ * @param array $venmoAccountAttribs array of Venmo account properties
+ * @return void
+ */
+ protected function _initialize($venmoAccountAttribs)
+ {
+ $this->_attributes = $venmoAccountAttribs;
+
+ $subscriptionArray = array();
+ if (isset($venmoAccountAttribs['subscriptions'])) {
+ foreach ($venmoAccountAttribs['subscriptions'] AS $subscription) {
+ $subscriptionArray[] = Subscription::factory($subscription);
+ }
+ }
+
+ $this->_set('subscriptions', $subscriptionArray);
+ }
+}
+class_alias('Braintree\VenmoAccount', 'Braintree_VenmoAccount');
diff --git a/lib/Braintree/Version.php b/lib/Braintree/Version.php
index 5a04005..35eb484 100644
--- a/lib/Braintree/Version.php
+++ b/lib/Braintree/Version.php
@@ -1,23 +1,17 @@
== More information ==
+ *
+ * For more detailed information on CreditCard verifications, see {@link https://developers.braintreepayments.com/reference/response/credit-card-verification/php https://developers.braintreepayments.com/reference/response/credit-card-verification/php}
+ *
+ * @package Braintree
+ * @category Resources
+ *
+ * @property-read \Braintree\Address $billingAddress
+ * @property-read string $bin
+ * @property-read string $callId
+ * @property-read string $cardType
+ * @property-read string $cardholderName
+ * @property-read string $commercial
+ * @property-read string $countryOfIssuance
+ * @property-read \DateTime $createdAt
+ * @property-read string $customerId
+ * @property-read string $customerLocation
+ * @property-read string $debit
+ * @property-read boolean $default
+ * @property-read string $durbinRegulated
+ * @property-read string $expirationDate
+ * @property-read string $expirationMonth
+ * @property-read string $expirationYear
+ * @property-read boolean $expired
+ * @property-read string $healthcare
+ * @property-read string $imageUrl
+ * @property-read string $issuingBank
+ * @property-read string $last4
+ * @property-read string $maskedNumber
+ * @property-read string $payroll
+ * @property-read string $prepaid
+ * @property-read string $productId
+ * @property-read \Braintree\Subscription[] $subscriptions
+ * @property-read string $token
+ * @property-read string $uniqueNumberIdentifier
+ * @property-read \DateTime $updatedAt
+ */
+class VisaCheckoutCard extends Base
+{
+ /* instance methods */
+ /**
+ * returns false if default is null or false
+ *
+ * @return boolean
+ */
+ public function isDefault()
+ {
+ return $this->default;
+ }
+
+ /**
+ * checks whether the card is expired based on the current date
+ *
+ * @return boolean
+ */
+ public function isExpired()
+ {
+ return $this->expired;
+ }
+
+ /**
+ * sets instance properties from an array of values
+ *
+ * @access protected
+ * @param array $creditCardAttribs array of creditcard data
+ * @return void
+ */
+ protected function _initialize($creditCardAttribs)
+ {
+ // set the attributes
+ $this->_attributes = $creditCardAttribs;
+
+ // map each address into its own object
+ $billingAddress = isset($creditCardAttribs['billingAddress']) ?
+ Address::factory($creditCardAttribs['billingAddress']) :
+ null;
+
+ $subscriptionArray = [];
+ if (isset($creditCardAttribs['subscriptions'])) {
+ foreach ($creditCardAttribs['subscriptions'] AS $subscription) {
+ $subscriptionArray[] = Subscription::factory($subscription);
+ }
+ }
+
+ $this->_set('subscriptions', $subscriptionArray);
+ $this->_set('billingAddress', $billingAddress);
+ $this->_set('expirationDate', $this->expirationMonth . '/' . $this->expirationYear);
+ $this->_set('maskedNumber', $this->bin . '******' . $this->last4);
+
+ if(isset($creditCardAttribs['verifications']) && count($creditCardAttribs['verifications']) > 0) {
+ $verifications = $creditCardAttribs['verifications'];
+ usort($verifications, [$this, '_compareCreatedAtOnVerifications']);
+
+ $this->_set('verification', CreditCardVerification::factory($verifications[0]));
+ }
+ }
+
+ private function _compareCreatedAtOnVerifications($verificationAttrib1, $verificationAttrib2)
+ {
+ return ($verificationAttrib2['createdAt'] < $verificationAttrib1['createdAt']) ? -1 : 1;
+ }
+
+ /**
+ * returns false if comparing object is not a VisaCheckoutCard,
+ * or is a VisaCheckoutCard with a different id
+ *
+ * @param object $otherVisaCheckoutCard customer to compare against
+ * @return boolean
+ */
+ public function isEqual($otherVisaCheckoutCard)
+ {
+ return !($otherVisaCheckoutCard instanceof self) ? false : $this->token === $otherVisaCheckoutCard->token;
+ }
+
+ /**
+ * create a printable representation of the object as:
+ * ClassName[property=value, property=value]
+ * @return string
+ */
+ public function __toString()
+ {
+ return __CLASS__ . '[' .
+ Util::attributesToString($this->_attributes) .']';
+ }
+
+ /**
+ * factory method: returns an instance of VisaCheckoutCard
+ * to the requesting method, with populated properties
+ *
+ * @ignore
+ * @return VisaCheckoutCard
+ */
+ public static function factory($attributes)
+ {
+ $defaultAttributes = [
+ 'bin' => '',
+ 'expirationMonth' => '',
+ 'expirationYear' => '',
+ 'last4' => '',
+ ];
+
+ $instance = new self();
+ $instance->_initialize(array_merge($defaultAttributes, $attributes));
+ return $instance;
+ }
+}
+class_alias('Braintree\VisaCheckoutCard', 'Braintree_VisaCheckoutCard');
diff --git a/lib/Braintree/WebhookNotification.php b/lib/Braintree/WebhookNotification.php
index 873211d..c669dc8 100644
--- a/lib/Braintree/WebhookNotification.php
+++ b/lib/Braintree/WebhookNotification.php
@@ -1,5 +1,7 @@
webhookNotification()->parse($signature, $payload);
}
- public static function verify($challenge)
- {
- $publicKey = Braintree_Configuration::publicKey();
- $digest = Braintree_Digest::hexDigest($challenge);
- return "{$publicKey}|{$digest}";
+ public static function verify($challenge) {
+ return Configuration::gateway()->webhookNotification()->verify($challenge);
}
public static function factory($attributes)
@@ -40,34 +49,14 @@ public static function factory($attributes)
return $instance;
}
- private static function _matchingSignature($signaturePairs)
- {
- foreach ($signaturePairs as $pair)
- {
- $components = preg_split("/\|/", $pair);
- if ($components[0] == Braintree_Configuration::publicKey()) {
- return $components[1];
- }
- }
-
- return null;
- }
-
- private static function _validateSignature($signature, $payload)
- {
- $signaturePairs = preg_split("/&/", $signature);
- $matchingSignature = self::_matchingSignature($signaturePairs);
-
- $payloadSignature = Braintree_Digest::hexDigest($payload);
- if (!Braintree_Digest::secureCompare($matchingSignature, $payloadSignature)) {
- throw new Braintree_Exception_InvalidSignature("webhook notification signature invalid");
- }
- }
-
protected function _initialize($attributes)
{
$this->_attributes = $attributes;
+ if (!isset($attributes['sourceMerchantId'])) {
+ $this->_set('sourceMerchantId', null);
+ }
+
if (isset($attributes['subject']['apiErrorResponse'])) {
$wrapperNode = $attributes['subject']['apiErrorResponse'];
} else {
@@ -75,28 +64,65 @@ protected function _initialize($attributes)
}
if (isset($wrapperNode['subscription'])) {
- $this->_set('subscription', Braintree_Subscription::factory($attributes['subject']['subscription']));
+ $this->_set('subscription', Subscription::factory($attributes['subject']['subscription']));
}
if (isset($wrapperNode['merchantAccount'])) {
- $this->_set('merchantAccount', Braintree_MerchantAccount::factory($wrapperNode['merchantAccount']));
+ $this->_set('merchantAccount', MerchantAccount::factory($wrapperNode['merchantAccount']));
}
if (isset($wrapperNode['transaction'])) {
- $this->_set('transaction', Braintree_Transaction::factory($wrapperNode['transaction']));
+ $this->_set('transaction', Transaction::factory($wrapperNode['transaction']));
}
if (isset($wrapperNode['disbursement'])) {
- $this->_set('disbursement', Braintree_Disbursement::factory($wrapperNode['disbursement']));
+ $this->_set('disbursement', Disbursement::factory($wrapperNode['disbursement']));
}
if (isset($wrapperNode['partnerMerchant'])) {
- $this->_set('partnerMerchant', Braintree_PartnerMerchant::factory($wrapperNode['partnerMerchant']));
+ $this->_set('partnerMerchant', PartnerMerchant::factory($wrapperNode['partnerMerchant']));
+ }
+
+ if (isset($wrapperNode['oauthApplicationRevocation'])) {
+ $this->_set('oauthAccessRevocation', OAuthAccessRevocation::factory($wrapperNode['oauthApplicationRevocation']));
+ }
+
+ if (isset($wrapperNode['connectedMerchantStatusTransitioned'])) {
+ $this->_set('connectedMerchantStatusTransitioned', ConnectedMerchantStatusTransitioned::factory($wrapperNode['connectedMerchantStatusTransitioned']));
+ }
+
+ if (isset($wrapperNode['connectedMerchantPaypalStatusChanged'])) {
+ $this->_set('connectedMerchantPayPalStatusChanged', ConnectedMerchantPayPalStatusChanged::factory($wrapperNode['connectedMerchantPaypalStatusChanged']));
+ }
+
+ if (isset($wrapperNode['dispute'])) {
+ $this->_set('dispute', Dispute::factory($wrapperNode['dispute']));
+ }
+
+ if (isset($wrapperNode['accountUpdaterDailyReport'])) {
+ $this->_set('accountUpdaterDailyReport', AccountUpdaterDailyReport::factory($wrapperNode['accountUpdaterDailyReport']));
+ }
+
+ if (isset($wrapperNode['idealPayment'])) {
+ $this->_set('idealPayment', IdealPayment::factory($wrapperNode['idealPayment']));
+ }
+
+ if (isset($wrapperNode['grantedPaymentInstrumentUpdate'])) {
+ $this->_set('grantedPaymentInstrumentUpdate', GrantedPaymentInstrumentUpdate::factory($wrapperNode['grantedPaymentInstrumentUpdate']));
+ }
+
+ if ($attributes['kind'] == self::GRANTED_PAYMENT_METHOD_REVOKED) {
+ $this->_set('revokedPaymentMethodMetadata', RevokedPaymentMethodMetadata::factory($wrapperNode));
+ }
+
+ if (isset($wrapperNode['localPayment'])) {
+ $this->_set('localPaymentCompleted', LocalPaymentCompleted::factory($wrapperNode['localPayment']));
}
if (isset($wrapperNode['errors'])) {
- $this->_set('errors', new Braintree_Error_ValidationErrorCollection($wrapperNode['errors']));
+ $this->_set('errors', new Error\ValidationErrorCollection($wrapperNode['errors']));
$this->_set('message', $wrapperNode['message']);
}
}
}
+class_alias('Braintree\WebhookNotification', 'Braintree_WebhookNotification');
diff --git a/lib/Braintree/WebhookNotificationGateway.php b/lib/Braintree/WebhookNotificationGateway.php
new file mode 100644
index 0000000..17ef2bb
--- /dev/null
+++ b/lib/Braintree/WebhookNotificationGateway.php
@@ -0,0 +1,77 @@
+config = $gateway->config;
+ $this->config->assertHasAccessTokenOrKeys();
+ }
+
+ public function parse($signature, $payload)
+ {
+ if (is_null($signature)) {
+ throw new Exception\InvalidSignature("signature cannot be null");
+ }
+
+ if (is_null($payload)) {
+ throw new Exception\InvalidSignature("payload cannot be null");
+ }
+
+ if (preg_match("/[^A-Za-z0-9+=\/\n]/", $payload) === 1) {
+ throw new Exception\InvalidSignature("payload contains illegal characters");
+ }
+
+ self::_validateSignature($signature, $payload);
+
+ $xml = base64_decode($payload);
+ $attributes = Xml::buildArrayFromXml($xml);
+ return WebhookNotification::factory($attributes['notification']);
+ }
+
+ public function verify($challenge)
+ {
+ if (!preg_match('/^[a-f0-9]{20,32}$/', $challenge)) {
+ throw new Exception\InvalidChallenge("challenge contains non-hex characters");
+ }
+ $publicKey = $this->config->getPublicKey();
+ $digest = Digest::hexDigestSha1($this->config->getPrivateKey(), $challenge);
+ return "{$publicKey}|{$digest}";
+ }
+
+ private function _payloadMatches($signature, $payload)
+ {
+ $payloadSignature = Digest::hexDigestSha1($this->config->getPrivateKey(), $payload);
+ return Digest::secureCompare($signature, $payloadSignature);
+ }
+
+ private function _validateSignature($signatureString, $payload)
+ {
+ $signaturePairs = preg_split("/&/", $signatureString);
+ $signature = self::_matchingSignature($signaturePairs);
+ if (!$signature) {
+ throw new Exception\InvalidSignature("no matching public key");
+ }
+
+ if (!(self::_payloadMatches($signature, $payload) || self::_payloadMatches($signature, $payload . "\n"))) {
+ throw new Exception\InvalidSignature("signature does not match payload - one has been modified");
+ }
+ }
+
+ private function _matchingSignature($signaturePairs)
+ {
+ foreach ($signaturePairs as $pair)
+ {
+ $components = preg_split("/\|/", $pair);
+ if ($components[0] == $this->config->getPublicKey()) {
+ return $components[1];
+ }
+ }
+
+ return null;
+ }
+}
+
+class_alias('Braintree\WebhookNotificationGateway', 'Braintree_WebhookNotificationGateway');
diff --git a/lib/Braintree/WebhookTesting.php b/lib/Braintree/WebhookTesting.php
index f8d74da..350591c 100644
--- a/lib/Braintree/WebhookTesting.php
+++ b/lib/Braintree/WebhookTesting.php
@@ -1,217 +1,11 @@
$signature,
- 'payload' => $payload
- );
- }
-
- private static function _sampleXml($kind, $id)
- {
- switch ($kind) {
- case Braintree_WebhookNotification::SUB_MERCHANT_ACCOUNT_APPROVED:
- $subjectXml = self::_merchantAccountApprovedSampleXml($id);
- break;
- case Braintree_WebhookNotification::SUB_MERCHANT_ACCOUNT_DECLINED:
- $subjectXml = self::_merchantAccountDeclinedSampleXml($id);
- break;
- case Braintree_WebhookNotification::TRANSACTION_DISBURSED:
- $subjectXml = self::_transactionDisbursedSampleXml($id);
- break;
- case Braintree_WebhookNotification::DISBURSEMENT_EXCEPTION:
- $subjectXml = self::_disbursementExceptionSampleXml($id);
- break;
- case Braintree_WebhookNotification::DISBURSEMENT:
- $subjectXml = self::_disbursementSampleXml($id);
- break;
- case Braintree_WebhookNotification::PARTNER_MERCHANT_CONNECTED:
- $subjectXml = self::_partnerMerchantConnectedSampleXml($id);
- break;
- case Braintree_WebhookNotification::PARTNER_MERCHANT_DISCONNECTED:
- $subjectXml = self::_partnerMerchantDisconnectedSampleXml($id);
- break;
- case Braintree_WebhookNotification::PARTNER_MERCHANT_DECLINED:
- $subjectXml = self::_partnerMerchantDeclinedSampleXml($id);
- break;
- default:
- $subjectXml = self::_subscriptionSampleXml($id);
- break;
- }
- $timestamp = self::_timestamp();
- return "
-
- {$timestamp}
- {$kind}
- {$subjectXml}
-
- ";
- }
-
- private static function _merchantAccountApprovedSampleXml($id)
- {
- return "
-
- {$id}
-
- master_ma_for_{$id}
- active
-
- active
-
- ";
- }
-
- private static function _merchantAccountDeclinedSampleXml($id)
- {
- return "
-
- Credit score is too low
-
-
-
-
-
- 82621
- Credit score is too low
- base
-
-
-
-
-
- {$id}
- suspended
-
- master_ma_for_{$id}
- suspended
-
-
-
- ";
- }
-
- private static function _transactionDisbursedSampleXml($id)
- {
- return "
-
- ${id}
- 100
-
- 2013-07-09
-
-
- ";
- }
-
- private static function _disbursementExceptionSampleXml($id)
- {
- return "
-
- ${id}
-
- - asdfg
- - qwert
-
- false
- false
-
- merchant_account_token
- USD
- false
- active
-
- 100.00
- 2014-02-10
- bank_rejected
- update_funding_information
-
- ";
- }
-
- private static function _disbursementSampleXml($id)
- {
- return "
-
- ${id}
-
- - asdfg
- - qwert
-
- true
- false
-
- merchant_account_token
- USD
- false
- active
-
- 100.00
- 2014-02-10
-
-
-
- ";
- }
-
- private static function _subscriptionSampleXml($id)
- {
- return "
-
- {$id}
-
-
-
-
-
-
-
- ";
- }
-
- private static function _partnerMerchantConnectedSampleXml($id)
- {
- return "
-
- public_id
- public_key
- private_key
- abc123
- cse_key
-
- ";
- }
+namespace Braintree;
- private static function _partnerMerchantDisconnectedSampleXml($id)
- {
- return "
-
- abc123
-
- ";
- }
-
- private static function _partnerMerchantDeclinedSampleXml($id)
- {
- return "
-
- abc123
-
- ";
- }
-
- private static function _timestamp()
+class WebhookTesting
+{
+ public static function sampleNotification($kind, $id, $sourceMerchantId = null)
{
- $originalZone = date_default_timezone_get();
- date_default_timezone_set('UTC');
- $timestamp = strftime('%Y-%m-%dT%TZ');
- date_default_timezone_set($originalZone);
-
- return $timestamp;
+ return Configuration::gateway()->webhookTesting()->sampleNotification($kind, $id, $sourceMerchantId);
}
}
+class_alias('Braintree\WebhookTesting', 'Braintree_WebhookTesting');
diff --git a/lib/Braintree/WebhookTestingGateway.php b/lib/Braintree/WebhookTestingGateway.php
new file mode 100644
index 0000000..ce675c9
--- /dev/null
+++ b/lib/Braintree/WebhookTestingGateway.php
@@ -0,0 +1,553 @@
+config = $gateway->config;
+ $this->config->assertHasAccessTokenOrKeys();
+ }
+
+ public function sampleNotification($kind, $id, $sourceMerchantId = null)
+ {
+ $xml = self::_sampleXml($kind, $id, $sourceMerchantId);
+ $payload = base64_encode($xml) . "\n";
+ $signature = $this->config->getPublicKey() . "|" . Digest::hexDigestSha1($this->config->getPrivateKey(), $payload);
+
+ return [
+ 'bt_signature' => $signature,
+ 'bt_payload' => $payload
+ ];
+ }
+
+ private static function _sampleXml($kind, $id, $sourceMerchantId)
+ {
+ switch ($kind) {
+ case WebhookNotification::SUB_MERCHANT_ACCOUNT_APPROVED:
+ $subjectXml = self::_merchantAccountApprovedSampleXml($id);
+ break;
+ case WebhookNotification::SUB_MERCHANT_ACCOUNT_DECLINED:
+ $subjectXml = self::_merchantAccountDeclinedSampleXml($id);
+ break;
+ case WebhookNotification::TRANSACTION_DISBURSED:
+ $subjectXml = self::_transactionDisbursedSampleXml($id);
+ break;
+ case WebhookNotification::TRANSACTION_SETTLED:
+ $subjectXml = self::_transactionSettledSampleXml($id);
+ break;
+ case WebhookNotification::TRANSACTION_SETTLEMENT_DECLINED:
+ $subjectXml = self::_transactionSettlementDeclinedSampleXml($id);
+ break;
+ case WebhookNotification::DISBURSEMENT_EXCEPTION:
+ $subjectXml = self::_disbursementExceptionSampleXml($id);
+ break;
+ case WebhookNotification::DISBURSEMENT:
+ $subjectXml = self::_disbursementSampleXml($id);
+ break;
+ case WebhookNotification::PARTNER_MERCHANT_CONNECTED:
+ $subjectXml = self::_partnerMerchantConnectedSampleXml($id);
+ break;
+ case WebhookNotification::PARTNER_MERCHANT_DISCONNECTED:
+ $subjectXml = self::_partnerMerchantDisconnectedSampleXml($id);
+ break;
+ case WebhookNotification::PARTNER_MERCHANT_DECLINED:
+ $subjectXml = self::_partnerMerchantDeclinedSampleXml($id);
+ break;
+ case WebhookNotification::OAUTH_ACCESS_REVOKED:
+ $subjectXml = self::_oauthAccessRevocationSampleXml($id);
+ break;
+ case WebhookNotification::CONNECTED_MERCHANT_STATUS_TRANSITIONED:
+ $subjectXml = self::_connectedMerchantStatusTransitionedSampleXml($id);
+ break;
+ case WebhookNotification::CONNECTED_MERCHANT_PAYPAL_STATUS_CHANGED:
+ $subjectXml = self::_connectedMerchantPayPalStatusChangedSampleXml($id);
+ break;
+ case WebhookNotification::DISPUTE_OPENED:
+ $subjectXml = self::_disputeOpenedSampleXml($id);
+ break;
+ case WebhookNotification::DISPUTE_LOST:
+ $subjectXml = self::_disputeLostSampleXml($id);
+ break;
+ case WebhookNotification::DISPUTE_WON:
+ $subjectXml = self::_disputeWonSampleXml($id);
+ break;
+ case WebhookNotification::SUBSCRIPTION_CHARGED_SUCCESSFULLY:
+ $subjectXml = self::_subscriptionChargedSuccessfullySampleXml($id);
+ break;
+ case WebhookNotification::SUBSCRIPTION_CHARGED_UNSUCCESSFULLY:
+ $subjectXml = self::_subscriptionChargedUnsuccessfullySampleXml($id);
+ break;
+ case WebhookNotification::CHECK:
+ $subjectXml = self::_checkSampleXml();
+ break;
+ case WebhookNotification::ACCOUNT_UPDATER_DAILY_REPORT:
+ $subjectXml = self::_accountUpdaterDailyReportSampleXml($id);
+ break;
+ case WebhookNotification::IDEAL_PAYMENT_COMPLETE:
+ $subjectXml = self::_idealPaymentCompleteSampleXml($id);
+ break;
+ case WebhookNotification::IDEAL_PAYMENT_FAILED:
+ $subjectXml = self::_idealPaymentFailedSampleXml($id);
+ break;
+ case WebhookNotification::GRANTED_PAYMENT_INSTRUMENT_UPDATE:
+ $subjectXml = self::_grantedPaymentInstrumentUpdateSampleXml();
+ break;
+ case WebhookNotification::LOCAL_PAYMENT_COMPLETED:
+ $subjectXml = self::_localPaymentCompletedSampleXml();
+ break;
+ default:
+ $subjectXml = self::_subscriptionSampleXml($id);
+ break;
+ }
+ $timestamp = self::_timestamp();
+
+ $sourceMerchantIdXml = '';
+ if (!is_null($sourceMerchantId)) {
+ $sourceMerchantIdXml = "{$sourceMerchantId}";
+ }
+
+ return "
+
+ {$timestamp}
+ {$kind}
+ {$sourceMerchantIdXml}
+ {$subjectXml}
+
+ ";
+ }
+
+ private static function _merchantAccountApprovedSampleXml($id)
+ {
+ return "
+
+ {$id}
+
+ master_ma_for_{$id}
+ active
+
+ active
+
+ ";
+ }
+
+ private static function _merchantAccountDeclinedSampleXml($id)
+ {
+ return "
+
+ Credit score is too low
+
+
+
+
+
+ 82621
+ Credit score is too low
+ base
+
+
+
+
+
+ {$id}
+ suspended
+
+ master_ma_for_{$id}
+ suspended
+
+
+
+ ";
+ }
+
+ private static function _transactionDisbursedSampleXml($id)
+ {
+ return "
+
+ ${id}
+ 100
+
+ 2013-07-09
+
+
+ ";
+ }
+
+ private static function _transactionSettledSampleXml($id)
+ {
+ return "
+
+ ${id}
+ settled
+ sale
+ USD
+ 100.00
+ ogaotkivejpfayqfeaimuktty
+ us_bank_account
+
+ 123456789
+ 1234
+ checking
+ Dan Schulman
+
+
+ ";
+ }
+
+ private static function _transactionSettlementDeclinedSampleXml($id)
+ {
+ return "
+
+ ${id}
+ settlement_declined
+ sale
+ USD
+ 100.00
+ ogaotkivejpfayqfeaimuktty
+ us_bank_account
+
+ 123456789
+ 1234
+ checking
+ Dan Schulman
+
+
+ ";
+ }
+
+ private static function _disbursementExceptionSampleXml($id)
+ {
+ return "
+
+ ${id}
+
+ - asdfg
+ - qwert
+
+ false
+ false
+
+ merchant_account_token
+ USD
+ false
+ active
+
+ 100.00
+ 2014-02-10
+ bank_rejected
+ update_funding_information
+
+ ";
+ }
+
+ private static function _disbursementSampleXml($id)
+ {
+ return "
+
+ ${id}
+
+ - asdfg
+ - qwert
+
+ true
+ false
+
+ merchant_account_token
+ USD
+ false
+ active
+
+ 100.00
+ 2014-02-10
+
+
+
+ ";
+ }
+
+ private static function _disputeOpenedSampleXml($id)
+ {
+ return "
+
+ 250.00
+ 250.0
+ 245.00
+ USD
+ 2014-03-01
+ 2014-03-21
+ chargeback
+ open
+ fraud
+ ${id}
+
+ ${id}
+ 250.00
+
+ 2014-03-21
+
+ ";
+ }
+
+ private static function _disputeLostSampleXml($id)
+ {
+ return "
+
+ 250.00
+ 250.0
+ 245.00
+ USD
+ 2014-03-01
+ 2014-03-21
+ chargeback
+ lost
+ fraud
+ ${id}
+
+ ${id}
+ 250.00
+ 2020-02-10
+
+ 2014-03-21
+
+ ";
+ }
+
+ private static function _disputeWonSampleXml($id)
+ {
+ return "
+
+ 250.00
+ 250.0
+ 245.00
+ USD
+ 2014-03-01
+ 2014-03-21
+ chargeback
+ won
+ fraud
+ ${id}
+
+ ${id}
+ 250.00
+
+ 2014-03-21
+ 2014-03-22
+
+ ";
+ }
+
+ private static function _subscriptionSampleXml($id)
+ {
+ return "
+
+ {$id}
+
+
+
+
+
+
+
+ ";
+ }
+
+ private static function _subscriptionChargedSuccessfullySampleXml($id)
+ {
+ return "
+
+ {$id}
+ 2016-03-21
+ 2017-03-31
+
+
+ {$id}
+ submitted_for_settlement
+ 49.99
+
+
+
+
+
+
+
+ ";
+ }
+
+ private static function _subscriptionChargedUnsuccessfullySampleXml($id)
+ {
+ return "
+
+ {$id}
+ 2016-03-21
+ 2017-03-31
+
+
+ {$id}
+ failed
+ 49.99
+
+
+
+
+
+
+
+ ";
+ }
+
+ private static function _checkSampleXml()
+ {
+ return "
+ true
+ ";
+ }
+
+ private static function _partnerMerchantConnectedSampleXml($id)
+ {
+ return "
+
+ public_id
+ public_key
+ private_key
+ abc123
+ cse_key
+
+ ";
+ }
+
+ private static function _partnerMerchantDisconnectedSampleXml($id)
+ {
+ return "
+
+ abc123
+
+ ";
+ }
+
+ private static function _partnerMerchantDeclinedSampleXml($id)
+ {
+ return "
+
+ abc123
+
+ ";
+ }
+
+ private static function _oauthAccessRevocationSampleXml($id)
+ {
+ return "
+
+ {$id}
+ oauth_application_client_id
+
+ ";
+ }
+
+ private static function _accountUpdaterDailyReportSampleXml($id)
+ {
+ return "
+
+ 2016-01-14
+ link-to-csv-report
+
+ ";
+ }
+
+ private static function _connectedMerchantStatusTransitionedSampleXml($id)
+ {
+ return "
+
+ {$id}
+ new_status
+ oauth_application_client_id
+
+ ";
+ }
+
+ private static function _connectedMerchantPayPalStatusChangedSampleXml($id)
+ {
+ return "
+
+ {$id}
+ link
+ oauth_application_client_id
+
+ ";
+ }
+
+ private static function _idealPaymentCompleteSampleXml($id)
+ {
+ return "
+
+ {$id}
+ COMPLETE
+ ABCISSUER
+ ORDERABC
+ EUR
+ 10.00
+ 2016-11-29T23:27:34.547Z
+ https://example.com
+ 1234567890
+
+ ";
+ }
+
+ private static function _idealPaymentFailedSampleXml($id)
+ {
+ return "
+
+ {$id}
+ FAILED
+ ABCISSUER
+ ORDERABC
+ EUR
+ 10.00
+ 2016-11-29T23:27:34.547Z
+ https://example.com
+ 1234567890
+
+ ";
+ }
+
+ private static function _grantedPaymentInstrumentUpdateSampleXml()
+ {
+ return "
+
+ vczo7jqrpwrsi2px
+ cf0i8wgarszuy6hc
+
+ ee257d98-de40-47e8-96b3-a6954ea7a9a4
+ false
+ false
+
+ abc123z
+
+ - expiration-month
+ - expiration-year
+
+
+ ";
+ }
+
+ private static function _localPaymentCompletedSampleXml()
+ {
+ return "
+
+ a-payment-id
+ a-payer-id
+
+ ";
+ }
+
+ private static function _timestamp()
+ {
+ $originalZone = date_default_timezone_get();
+ date_default_timezone_set('UTC');
+ $timestamp = strftime('%Y-%m-%dT%TZ');
+ date_default_timezone_set($originalZone);
+
+ return $timestamp;
+ }
+}
+class_alias('Braintree\WebhookTestingGateway', 'Braintree_WebhookTestingGateway');
diff --git a/lib/Braintree/Xml.php b/lib/Braintree/Xml.php
index a6e5119..e938c40 100644
--- a/lib/Braintree/Xml.php
+++ b/lib/Braintree/Xml.php
@@ -1,17 +1,12 @@
openMemory();
@@ -33,7 +37,7 @@ public static function arrayToXml($aData)
$aKeys = array_keys($aData);
$rootElementName = $aKeys[0];
// open the root element
- $writer->startElement(Braintree_Util::camelCaseToDelimiter($rootElementName));
+ $writer->startElement($rootElementName);
// create the body
self::_createElementsFromArray($writer, $aData[$rootElementName], $rootElementName);
@@ -52,7 +56,7 @@ public static function arrayToXml($aData)
* @static
* @param object $writer XMLWriter object
* @param array $aData contains attributes and values
- * @return none
+ * @return void
*/
private static function _createElementsFromArray(&$writer, $aData)
{
@@ -64,9 +68,7 @@ private static function _createElementsFromArray(&$writer, $aData)
}
return;
}
- foreach ($aData AS $index => $element) {
- // convert the style back to gateway format
- $elementName = Braintree_Util::camelCaseToDelimiter($index, '-');
+ foreach ($aData AS $elementName => $element) {
// handle child elements
$writer->startElement($elementName);
if (is_array($element)) {
@@ -104,28 +106,30 @@ private static function _createElementsFromArray(&$writer, $aData)
private static function _generateXmlAttribute($value)
{
if ($value instanceof DateTime) {
- return array('type', 'datetime', self::_dateTimeToXmlTimestamp($value));
+ return ['type', 'datetime', self::_dateTimeToXmlTimestamp($value)];
}
if (is_int($value)) {
- return array('type', 'integer', $value);
+ return ['type', 'integer', $value];
}
if (is_bool($value)) {
- return array('type', 'boolean', ($value ? 'true' : 'false'));
+ return ['type', 'boolean', ($value ? 'true' : 'false')];
}
if ($value === NULL) {
- return array('nil', 'true', $value);
+ return ['nil', 'true', $value];
}
}
/**
* converts datetime back to xml schema format
* @access protected
* @param object $dateTime
- * @return var XML schema formatted timestamp
+ * @return string XML schema formatted timestamp
*/
private static function _dateTimeToXmlTimestamp($dateTime)
{
- $dateTime->setTimeZone(new DateTimeZone('UTC'));
- return ($dateTime->format('Y-m-d\TH:i:s') . 'Z');
+ $dateTimeForUTC = clone $dateTime;
+
+ $dateTimeForUTC->setTimeZone(new DateTimeZone('UTC'));
+ return ($dateTimeForUTC->format('Y-m-d\TH:i:s') . 'Z');
}
private static function _castDateTime($string)
@@ -142,3 +146,4 @@ private static function _castDateTime($string)
}
}
}
+class_alias('Braintree\Xml\Generator', 'Braintree_Xml_Generator');
diff --git a/lib/Braintree/Xml/Parser.php b/lib/Braintree/Xml/Parser.php
index e4fea27..1c0d05e 100644
--- a/lib/Braintree/Xml/Parser.php
+++ b/lib/Braintree/Xml/Parser.php
@@ -1,179 +1,140 @@
getName());
- $type = $iterator->attributes()->type;
-
- self::$_xmlRoot = $iterator->getName();
- self::$_responseType = $type;
+ $document = new DOMDocument('1.0', 'UTF-8');
+ $document->loadXML($xml);
- // return the mapped array with the root element as the header
- return array($xmlRoot => self::_iteratorToArray($iterator));
+ $root = $document->documentElement->nodeName;
+ return Util::delimiterToCamelCaseArray([
+ $root => self::_nodeToValue($document->childNodes->item(0)),
+ ]);
}
/**
- * processes SimpleXMLIterator objects recursively
+ * Converts a node to an array of values or nodes
*
- * @access protected
- * @param object $iterator
- * @return array xml converted to array
+ * @param DOMNode @node
+ * @return mixed
*/
- private static function _iteratorToArray($iterator)
+ private static function _nodeToArray($node)
{
- $xmlArray = array();
- $value = null;
-
- // rewind the iterator and check if the position is valid
- // if not, return the string it contains
- $iterator->rewind();
- if (!$iterator->valid()) {
- return self::_typecastXmlValue($iterator);
+ $type = null;
+ if ($node instanceof DOMElement) {
+ $type = $node->getAttribute('type');
}
- for ($iterator->rewind(); $iterator->valid(); $iterator->next()) {
-
- $tmpArray = null;
- $value = null;
-
- // get the attribute type string for use in conditions below
- $attributeType = $iterator->attributes()->type;
-
- // extract the parent element via xpath query
- $parentElement = $iterator->xpath($iterator->key() . '/..');
- if ($parentElement[0] instanceof SimpleXMLIterator) {
- $parentElement = $parentElement[0];
- $parentKey = Braintree_Util::delimiterToCamelCase($parentElement->getName());
- } else {
- $parentElement = null;
- }
-
-
- if ($parentKey == "customFields") {
- $key = Braintree_Util::delimiterToUnderscore($iterator->key());
- } else {
- $key = Braintree_Util::delimiterToCamelCase($iterator->key());
- }
-
- // process children recursively
- if ($iterator->hasChildren()) {
- // return the child elements
- $value = self::_iteratorToArray($iterator->current());
- // if the element is an array type,
- // use numeric keys to allow multiple values
- if ($attributeType != 'array') {
- $tmpArray[$key] = $value;
+ switch($type) {
+ case 'array':
+ $array = [];
+ foreach ($node->childNodes as $child) {
+ $value = self::_nodeToValue($child);
+ if ($value !== null) {
+ $array[] = $value;
}
- } else {
- // cast values according to attributes
- $tmpArray[$key] = self::_typecastXmlValue($iterator->current());
}
-
- // set the output string
- $output = isset($value) ? $value : $tmpArray[$key];
-
- // determine if there are multiple tags of this name at the same level
- if (isset($parentElement) &&
- ($parentElement->attributes()->type == 'collection') &&
- $iterator->hasChildren()) {
- $xmlArray[$key][] = $output;
- continue;
+ return $array;
+ case 'collection':
+ $collection = [];
+ foreach ($node->childNodes as $child) {
+ $value = self::_nodetoValue($child);
+ if ($value !== null) {
+ if (!isset($collection[$child->nodeName])) {
+ $collection[$child->nodeName] = [];
+ }
+ $collection[$child->nodeName][] = self::_nodeToValue($child);
+ }
}
-
- // if the element was an array type, output to a numbered key
- // otherwise, use the element name
- if ($attributeType == 'array') {
- $xmlArray[] = $output;
+ return $collection;
+ default:
+ $values = [];
+ if ($node->childNodes->length === 1 && $node->childNodes->item(0) instanceof DOMText) {
+ return $node->childNodes->item(0)->nodeValue;
} else {
- $xmlArray[$key] = $output;
+ foreach ($node->childNodes as $child) {
+ if (!$child instanceof DOMText) {
+ $values[$child->nodeName] = self::_nodeToValue($child);
+ }
+ }
+ return $values;
}
}
-
- return $xmlArray;
}
/**
- * typecast xml value based on attributes
- * @param object $valueObj SimpleXMLElement
- * @return mixed value for placing into array
+ * Converts a node to a PHP value
+ *
+ * @param DOMNode $node
+ * @return mixed
*/
- private static function _typecastXmlValue($valueObj)
+ private static function _nodeToValue($node)
{
- // get the element attributes
- $attribs = $valueObj->attributes();
- // the element is null, so jump out here
- if (isset($attribs->nil) && $attribs->nil) {
- return null;
- }
- // switch on the type attribute
- // switch works even if $attribs->type isn't set
- switch ($attribs->type) {
- case 'datetime':
- return self::_timestampToUTC((string) $valueObj);
- break;
- case 'date':
- return new DateTime((string)$valueObj);
- break;
- case 'integer':
- return (int) $valueObj;
- break;
- case 'boolean':
- $value = (string) $valueObj;
- // look for a number inside the string
- if(is_numeric($value)) {
- return (bool) $value;
- } else {
- // look for the string "true", return false in all other cases
- return ($value != "true") ? FALSE : TRUE;
- }
- break;
- case 'array':
- return array();
- default:
- return (string) $valueObj;
+ $type = null;
+ if ($node instanceof DOMElement) {
+ $type = $node->getAttribute('type');
}
+ switch($type) {
+ case 'datetime':
+ return self::_timestampToUTC((string) $node->nodeValue);
+ case 'date':
+ return new DateTime((string) $node->nodeValue);
+ case 'integer':
+ return (int) $node->nodeValue;
+ case 'boolean':
+ $value = (string) $node->nodeValue;
+ if(is_numeric($value)) {
+ return (bool) $value;
+ } else {
+ return ($value !== "true") ? false : true;
+ }
+ case 'array':
+ case 'collection':
+ return self::_nodeToArray($node);
+ default:
+ if ($node->hasChildNodes()) {
+ return self::_nodeToArray($node);
+ } elseif (trim($node->nodeValue) === '') {
+ return null;
+ } else {
+ return $node->nodeValue;
+ }
+ }
}
+
/**
- * convert xml timestamps into DateTime
+ * Converts XML timestamps into DateTime instances
+ *
* @param string $timestamp
- * @return string UTC formatted datetime string
+ * @return DateTime
*/
private static function _timestampToUTC($timestamp)
{
$tz = new DateTimeZone('UTC');
- // strangely DateTime requires an explicit set below
- // to show the proper time zone
$dateTime = new DateTime($timestamp, $tz);
$dateTime->setTimezone($tz);
return $dateTime;
}
}
+class_alias('Braintree\Xml\Parser', 'Braintree_Xml_Parser');
diff --git a/lib/autoload.php b/lib/autoload.php
new file mode 100644
index 0000000..1b5edc0
--- /dev/null
+++ b/lib/autoload.php
@@ -0,0 +1,21 @@
+ true|false,
- * "error_message" => "Error message",
- * "transaction_id" => "XXX",
- *
- * //If the payment is captured in this method, return a "captured_payment" array with the following information about the payment
- * "captured_payment" => ["is_success"=>true|false, "error_message" => "error message", "transaction_id" => "xxx", "amount" => 20]
- * ]
- * @since 1.0
- * @return void
- */
+ * Enqueue scripts.
+ *
+ * @since 1.4.0
+ * @return array
+ */
+ public function scripts() {
+ $scripts = [];
+
+ if( $settings = $this->get_plugin_settings() ) {
+ $scripts = [
+ [
+ 'handle' => 'braintree_client',
+ 'src' => 'https://js.braintreegateway.com/web/3.43.0/js/client.min.js',
+ 'version' => $this->_version,
+ 'deps' => [],
+ 'enqueue' => [
+ [$this, 'aft_enabled']
+ ]
+ ],
+ [
+ 'handle' => 'braintree_data_collector',
+ 'src' => 'https://js.braintreegateway.com/web/3.43.0/js/data-collector.min.js',
+ 'version' => $this->_version,
+ 'deps' => [],
+ 'enqueue' => [
+ [$this, 'aft_enabled']
+ ]
+ ],
+ [
+ 'handle' => 'braintree_data_processing',
+ 'src' => $this->get_base_url() . '/../assets/js/braintree-data-processing.js',
+ 'version' => $this->_version,
+ 'deps' => ['jquery'],
+ 'strings' => [
+ 'bt_magic' => $settings['tokenization-key'],
+ 'bt_field' => $this->get_device_data_field_value()
+ ],
+ 'enqueue' => [
+ [$this, 'aft_enabled']
+ ]
+ ],
+ ];
+ }
+
+ return array_merge(parent::scripts(), $scripts);
+ }
+
+ /**
+ * After form has been submitted, send CC details to Braintree and ensure the card is going to work
+ * If not, void the validation result (processed elsewhere) and have the submit the form again
+ *
+ * @param $feed - Current configured payment feed
+ * @param $submission_data - Contains form field data submitted by the user as well as payment information (i.e. payment amount, setup fee, line items, etc...)
+ * @param $form - Current form array containing all form settings
+ * @param $entry - Current entry array containing entry information (i.e data submitted by users). NOTE: the entry hasn't been saved to the database at this point, so this $entry object does not have the "ID" property and is only a memory representation of the entry.
+ * @return array - Return an $authorization array in the following format:
+ * [
+ * "is_authorized" => true|false,
+ * "error_message" => "Error message",
+ * "transaction_id" => "XXX",
+ *
+ * //If the payment is captured in this method, return a "captured_payment" array with the following information about the payment
+ * "captured_payment" => ["is_success"=>true|false, "error_message" => "error message", "transaction_id" => "xxx", "amount" => 20]
+ * ]
+ * @since 1.0
+ * @return void
+ */
public function authorize( $feed, $submission_data, $form, $entry ) {
// Prepare authorization response payload
@@ -85,6 +133,9 @@ public function authorize( $feed, $submission_data, $form, $entry ) {
$card_number = str_replace( array( '-', ' ' ), '', $submission_data['card_number'] );
// Prepare Braintree payload
+ $namePieces = explode(' ', $submission_data['card_name']);
+ $lastName = array_pop($namePieces);
+ $firstName = implode(' ', $namePieces);
$args = array(
'amount' => $submission_data['payment_amount'],
'creditCard' => array(
@@ -94,43 +145,56 @@ public function authorize( $feed, $submission_data, $form, $entry ) {
'cvv' => $submission_data['card_security_code']
),
'customer' => array(
- 'firstName' => $submission_data['card_name']
- ),
+ 'lastName' => $lastName,
+ 'firstName' => $firstName,
+ 'email' => $submission_data['email']
+ ),
'billing' => array(
- 'firstName' => $submission_data['card_name'],
+ 'lastName' => $lastName,
+ 'firstName' => $firstName,
'streetAddress' => $submission_data['address'],
'locality' => $submission_data['city'],
'postalCode' => $submission_data['zip']
- )
+ )
);
try {
// Configure Braintree environment
- Braintree_Configuration::environment( strtolower( $settings['environment'] ) );
- Braintree_Configuration::merchantId( $settings['merchant-id']);
- Braintree_Configuration::publicKey( $settings['public-key'] );
- Braintree_Configuration::privateKey( $settings['private-key'] );
+ Braintree\Configuration::environment( strtolower( $settings['environment'] ) );
+ Braintree\Configuration::merchantId( $settings['merchant-id']);
+ Braintree\Configuration::publicKey( $settings['public-key'] );
+ Braintree\Configuration::privateKey( $settings['private-key'] );
- // Set to auto settlemt if applicable
+ // Set to auto settlement if applicable
if( $settings['settlement'] == 'Yes' ) {
$args['options']['submitForSettlement'] = 'true';
}
+ if ($feed['meta']['taxExempt'] == 1) {
+ $args['taxExempt'] = 'true';
+ }
+
+ if ($feed['meta']['enableAFT'] == 1 && !empty($submission_data['device_data'])) {
+ $args['deviceData'] = $submission_data['device_data'];
+ }
+ GFCommon::log_debug('Braintree Transaction Args: ' . print_r( $args, true ));
+
// Send transaction to Braintree
- $result = Braintree_Transaction::sale( $args );
+ $result = Braintree\Transaction::sale( $args );
+ GFCommon::log_debug('Braintree Transaction Sale Result: ' . print_r( $result, true ));
// Update response to reflect successful payment
if( $result->success == '1' ) {
$authorization['is_authorized'] = true;
$authorization['error_message'] = '';
- $authorization['transaction_id'] = $result->transaction->_attributes['id'];
+ $authorization['transaction_id'] = $result->transaction->id;
$authorization['captured_payment'] = array(
'is_success' => true,
- 'transaction_id' => $result->transaction->_attributes['id'],
- 'amount' => $result->transaction->_attributes['amount'],
+ 'transaction_id' => $result->transaction->id,
+ 'amount' => $result->transaction->amount,
'error_message' => '',
'payment_method' => 'Credit Card'
);
@@ -140,15 +204,16 @@ public function authorize( $feed, $submission_data, $form, $entry ) {
// Append gateway response text to error message if it exists. If it doesn't exist, a more hardcore
// failure has occured and it won't do the user any good to see it other than a general error message
- if( isset( $result->_attributes['transaction']->_attributes['processorResponseText'] ) ) {
- $authorization['error_message'] .= sprintf( '. Your bank said: %s.', $result->_attributes['transaction']->_attributes['processorResponseText'] );
+ if( isset( $result->transaction->processorResponseText ) ) {
+ $authorization['error_message'] .= sprintf( '. Your bank said: %s.', $result->transaction->processorResponseText );
}
}
}
catch( Exception $e ) {
- // Do nothing with exception object, just fallback to generic failure
+ // Log exception object message, then fallback to generic failure
+ GFCommon::log_debug('Braintree Exception: ' . print_r( $e->getMessage(), true ));
}
return $authorization;
@@ -160,58 +225,280 @@ public function authorize( $feed, $submission_data, $form, $entry ) {
}
/**
- * Create and display feed settings fields.
- *
- * @since 1.0
- * @return void
- */
+ * Override this method to add integration code to the payment processor in order to create a subscription. This method is executed during the form validation process and allows
+ * the form submission process to fail with a validation error if there is anything wrong when creating the subscription.
+ *
+ * @param $feed - Current configured payment feed
+ * @param $submission_data - Contains form field data submitted by the user as well as payment information (i.e. payment amount, setup fee, line items, etc...)
+ * @param $form - Current form array containing all form settings
+ * @param $entry - Current entry array containing entry information (i.e data submitted by users). NOTE: the entry hasn't been saved to the database at this point, so this $entry object does not have the 'ID' property and is only a memory representation of the entry.
+ *
+ * @since 1.2.0
+ * @return array - Return an $subscription array in the following format:
+ * [
+ * 'is_success'=>true|false,
+ * 'error_message' => 'error message',
+ * 'subscription_id' => 'xxx',
+ * 'amount' => 10
+ *
+ * //To implement an initial/setup fee for gateways that don't support setup fees as part of subscriptions, manually capture the funds for the setup fee as a separate transaction and send that payment
+ * //information in the following 'captured_payment' array
+ * 'captured_payment' => ['name' => 'Setup Fee', 'is_success'=>true|false, 'error_message' => 'error message', 'transaction_id' => 'xxx', 'amount' => 20]
+ * ]
+ */
+ public function subscribe( $feed, $submission_data, $form, $entry ) {
+
+ // Prepare authorization response payload
+ $authorization = [
+ 'is_authorized' => false,
+ 'error_message' => apply_filters( 'gform_braintree_credit_card_failure_message', __( 'Your card could not be billed. Please ensure the details you entered are correct and try again.', 'gravity-forms-braintree' ) ),
+ 'subscription_id' => '',
+ 'amount' => '',
+ 'captured_payment' => [
+ 'is_success' => false,
+ 'error_message' => '',
+ 'subscription_id' => '',
+ 'amount' => $submission_data['payment_amount']
+ ]
+ ];
+
+ if( $settings = $this->get_plugin_settings() ) {
+
+ // Sanitize card number, removing dashes and spaces
+ $card_number = str_replace(array('-', ' '), '', $submission_data['card_number']);
+
+ // Prepare Braintree payload
+ $namePieces = explode(' ', $submission_data['card_name']);
+ $args = [
+ 'creditCard' => [
+ 'billingAddress' => [
+ 'countryName' => $submission_data['country'],
+ 'streetAddress' => $submission_data['address'],
+ 'locality' => $submission_data['city'],
+ 'region' => $submission_data['state'],
+ 'postalCode' => $submission_data['zip']
+ ],
+ 'number' => $card_number,
+ 'expirationDate' => sprintf('%s/%s', $submission_data['card_expiration_date'][0], $submission_data['card_expiration_date'][1]),
+ 'cardholderName' => $submission_data['card_name'],
+ 'cvv' => $submission_data['card_security_code']
+ ],
+ 'email' => $submission_data['email'],
+ 'lastName' => array_pop($namePieces),
+ 'firstName' => implode(' ', $namePieces)
+ ];
+
+ try {
+ // Configure Braintree environment
+ Braintree\Configuration::environment(strtolower($settings['environment']));
+ Braintree\Configuration::merchantId($settings['merchant-id']);
+ Braintree\Configuration::publicKey($settings['public-key']);
+ Braintree\Configuration::privateKey($settings['private-key']);
+
+ $plans = Braintree\Plan::all();
+
+ // See if there is a plan with a matching dollar value.
+ $thePlan = null;
+ if (count($plans) == 1) {
+ $thePlan = $plans[0];
+ } else if (count($plans) > 1) {
+ foreach ($plans as $plan) {
+ if ((float)$submission_data['payment_amount'] == (float)$plan->price) {
+ $thePlan = $plan;
+ break;
+ }
+ }
+ if (empty($thePlan)) {
+ $thePlan = $plans[0];
+ }
+ } else {
+ $authorization['error_message'] = apply_filters('gform_braintree_no_plans_failure_message', __('No subscription plans are available.', 'gravity-forms-braintree'));
+ }
+
+ if (!empty($thePlan)) {
+ $collection = Braintree\Customer::search([
+ Braintree\CustomerSearch::email()->is($args['email'])
+ ]);
+
+ if ($collection->maximumCount() > 0) {
+ foreach ($collection as $customer) {
+ $result = $customer;
+ }
+ } else {
+ if ($feed['meta']['enableAFT'] == 1 && !empty($submission_data['device_data'])) {
+ $args['deviceData'] = $submission_data['device_data'];
+ }
+
+ $result = Braintree\Customer::create($args);
+ }
+
+ if (!empty($result)) {
+ if (get_class($result) != 'Braintree\Customer') {
+ $result = $result->customer;
+ }
+
+ $subscription = [
+ 'paymentMethodToken' => $result->creditCards[0]->token,
+ 'planId' => $thePlan->id
+ ];
+
+ if (!empty($submission_data['first_bill_date'])) {
+ $dateTime = new DateTime($submission_data['first_bill_date']);
+ $subscription['firstBillingDate'] = $dateTime;
+ }
+
+ if ((float)$submission_data['payment_amount'] != (float)$plan->price) {
+ $subscription['price'] = (float)$submission_data['payment_amount'];
+ }
+
+ $subscriptionResult = Braintree\Subscription::create($subscription);
+
+ if ($subscriptionResult->success == true) {
+
+ $authorization['is_success'] = true;
+ $authorization['error_message'] = '';
+ $authorization['subscription_id'] = $subscriptionResult->subscription->id;
+ $authorization['amount'] = $subscriptionResult->subscription->price;
+
+ $authorization['captured_payment'] = [
+ 'is_success' => true,
+ 'subscription_id' => $subscriptionResult->subscription->id,
+ 'transaction_id' => $subscriptionResult->subscription->transactions[0]->id,
+ 'amount' => $subscriptionResult->subscription->price,
+ 'error_message' => ''
+ ];
+
+ }
+ } else {
+ $authorization['error_message'] = apply_filters('gform_braintree_customer_create_failure_message', __('Failed to create a customer.', 'gravity-forms-braintree'));
+ }
+ } else {
+ $authorization['error_message'] = apply_filters('gform_braintree_no_plan_message', __('No subscription plan found.', 'gravity-forms-braintree'));
+ }
+
+ } catch (Exception $e) {
+ // Log exception object message, then fallback to generic failure
+ GFCommon::log_debug('Braintree Exception: ' . print_r( $e->getMessage(), true ));
+ }
+ }
+
+ return $authorization;
+ }
+
+ /**
+ * Create and display feed settings fields.
+ *
+ * @since 1.0
+ * @return array
+ */
public function feed_settings_fields () {
// Get defaults from GFPaymentAddOn
$settings = parent::feed_settings_fields();
- // Remove billing information
- //$settings = $this->remove_field( 'billingInformation', $settings );
-
// Remove options
$settings = $this->remove_field( 'options', $settings );
- // Remove the subscription option from transaction type dropdown
$transaction_type = $this->get_field( 'transactionType', $settings );
- foreach( $transaction_type['choices'] as $index => $choice ) {
- if( $choice['value'] == 'subscription' ) {
- unset( $transaction_type['choices'][$index] );
- }
- }
-
$settings = $this->replace_field( 'transactionType', $transaction_type, $settings );
+ // Add tax exempt field to feed settings.
+ $fields = array(
+ array(
+ 'label' => 'Tax Exempt',
+ 'type' => 'checkbox',
+ 'name' => 'taxExempt',
+ 'choices' => array(
+ array(
+ 'label' => 'Enabled',
+ 'name' => 'taxExempt'
+ )
+ )
+ ),
+ array(
+ 'label' => 'Enable Advanced Fraud Tools',
+ 'type' => 'checkbox',
+ 'name' => 'enableAFT',
+ 'tooltip' => esc_html__( 'Advanced Fraud Tools
Add a hidden field as the first field in the form and set it below for Device Data.', 'simplefeedaddon' ),
+ 'choices' => array(
+ array(
+ 'label' => 'Enabled',
+ 'name' => 'enableAFT'
+ )
+ )
+ )
+ );
+ $settings = $this->add_field_after( 'transactionType', $fields, $settings );
+
// Return sanitized settings
return $settings;
}
/**
- * Create and display plugin settings fields. These are settings for Braintree in particular, not a feed
- *
- * @since 1.0
- * @return void
- */
+ * Update billing fields.
+ *
+ * @since 1.2.1
+ * @return array
+ */
+ public function billing_info_fields() {
+ $default_settings = parent::billing_info_fields();
+
+ $default_settings[] = array( 'name' => 'first_bill_date', 'label' => __( 'First Billing Date', 'gravityforms' ), 'required' => false );
+ $default_settings[] = array( 'name' => 'device_data', 'label' => __( 'Device Data', 'gravityforms' ), 'required' => false );
+
+ return $default_settings;
+ }
+
+ /**
+ * Check if the enableAFT box is checked for script enqueueing.
+ *
+ * @param $form
+ * @since 1.4.0
+ * @return bool
+ */
+ public function aft_enabled( $form ) {
+ if ($form && $this->has_feed( $form['id'] )) {
+ $feed = $this->get_feed($form['id']);
+
+ return !empty($feed['meta']) && !empty($feed['meta']['enableAFT']);
+ }
+
+ return false;
+ }
+
+ /**
+ * Get the name of the device data field.
+ *
+ * @since 1.4.0
+ * @return string
+ */
+ public function get_device_data_field_value( ) {
+ // @todo Need to get the setting of the field name that device_data is set to.
+ return 'input_11';
+ }
+
+ /**
+ * Create and display plugin settings fields. These are settings for Braintree in particular, not a feed
+ *
+ * @since 1.0
+ * @return array
+ */
public function plugin_settings_fields () {
return array(
- array(
- 'title' => 'Account Settings',
- 'fields' => array(
- array(
- 'name' => 'merchant-id',
- 'tooltip' => 'Your Braintree Merchant ID',
- 'label' => 'Merchant ID',
- 'type' => 'text',
- 'class' => 'medium'
- ),
+ array(
+ 'title' => 'Account Settings',
+ 'fields' => array(
+ array(
+ 'name' => 'merchant-id',
+ 'tooltip' => 'Your Braintree Merchant ID',
+ 'label' => 'Merchant ID',
+ 'type' => 'text',
+ 'class' => 'medium'
+ ),
array(
'name' => 'public-key',
'tooltip' => 'Your Braintree Account Public Key',
@@ -225,9 +512,16 @@ public function plugin_settings_fields () {
'label' => 'Private Key',
'type' => 'text',
'class' => 'medium'
+ ),
+ array(
+ 'name' => 'tokenization-key',
+ 'tooltip' => 'Your Braintree Account Tokenization Key',
+ 'label' => 'Tokenization Key',
+ 'type' => 'text',
+ 'class' => 'medium'
)
- )
- ),
+ )
+ ),
array(
'title' => 'Payment Settings',
'fields' => array(
@@ -271,17 +565,17 @@ public function plugin_settings_fields () {
)
)
- );
+ );
}
/**
- * Helper function to determine if all Braintree settings have been set.
- * Does not check if they are correct, only that they have been set, IE not null
- * @param @settings Plugin settings to check if valid
- * @since 1.0
- * @return void
- */
+ * Helper function to determine if all Braintree settings have been set.
+ * Does not check if they are correct, only that they have been set, IE not null
+ * @param @settings Plugin settings to check if valid
+ * @since 1.0
+ * @return boolean
+ */
public function settings_are_valid ( $settings ) {
if( empty( $settings ) ) {
@@ -299,11 +593,11 @@ public function settings_are_valid ( $settings ) {
}
/**
- * Get plugin settings
- *
- * @since 1.0
- * @return void
- */
+ * Get plugin settings
+ *
+ * @since 1.0
+ * @return array|boolean
+ */
public function get_plugin_settings () {
$settings = parent::get_plugin_settings();
diff --git a/lib/ssl/api_braintreegateway_com.ca.crt b/lib/ssl/api_braintreegateway_com.ca.crt
index e142318..7dcf4af 100644
--- a/lib/ssl/api_braintreegateway_com.ca.crt
+++ b/lib/ssl/api_braintreegateway_com.ca.crt
@@ -1,68 +1,4 @@
-----BEGIN CERTIFICATE-----
-MIIDAjCCAmsCEH3Z/gfPqB63EHln+6eJNMYwDQYJKoZIhvcNAQEFBQAwgcExCzAJ
-BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xh
-c3MgMyBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcy
-MTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3Jp
-emVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMB4X
-DTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVTMRcw
-FQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMg
-UHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEo
-YykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5
-MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEB
-AQUAA4GNADCBiQKBgQDMXtERXVxp0KvTuWpMmR9ZmDCOFoUgRm1HP9SFIIThbbP4
-pO0M8RcPO/mn+SXXwc+EY/J8Y8+iR/LGWzOOZEAEaMGAuWQcRXfH2G71lSk8UOg0
-13gfqLptQ5GVj0VXXn7F+8qkBOvqlzdUMG+7AUcyM83cV5tkaWH4mx0ciU9cZwID
-AQABMA0GCSqGSIb3DQEBBQUAA4GBAFFNzb5cy5gZnBWyATl4Lk0PZ3BwmcYQWpSk
-U01UbSuvDV1Ai2TT1+7eVmGSX6bEHRBhNtMsJzzoKQm5EWR0zLVznxxIqbxhAe7i
-F6YM40AIOw7n60RzKprxaZLvcRTDOaxxp5EJb+RxBrO6WVcmeQD2+A2iMzAo1KpY
-oJ2daZH9
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHKMQsw
-CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl
-cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu
-LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT
-aWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp
-dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD
-VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT
-aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ
-bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu
-IENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg
-LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMu6nFL8eB8aHm8b
-N3O9+MlrlBIwT/A2R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1EUGO+i2t
-KmFZpGcmTNDovFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGu
-kxUccLwgTS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBm
-CC+Vk7+qRy+oRpfwEuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJ
-Xwzw3sJ2zq/3avL6QaaiMxTJ5Xpj055iN9WFZZ4O5lMkdBteHRJTW8cs54NJOxWu
-imi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAERSWwauSCPc/L8my/uRan2Te
-2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5fj267Cz3qWhMe
-DGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoAWii/gt/4uhMdUIaC
-/Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8SGhJouPtmmRQURVyu565p
-F4ErWjfJXir0xuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGt
-TxzhT5yvDwyd93gN2PQ1VoDat20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ==
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIDhDCCAwqgAwIBAgIQL4D+I4wOIg9IZxIokYesszAKBggqhkjOPQQDAzCByjEL
-MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW
-ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2ln
-biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp
-U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y
-aXR5IC0gRzQwHhcNMDcxMTA1MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCByjELMAkG
-A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJp
-U2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwg
-SW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2ln
-biBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5
-IC0gRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASnVnp8Utpkmw4tXNherJI9/gHm
-GUo9FANL+mAnINmDiWn6VMaaGF5VKmTeBvaNSjutEDxlPZCIBIngMGGzrl0Bp3ve
-fLK+ymVhAIau2o970ImtTR1ZmkGxvEeA3J5iw/mjgbIwga8wDwYDVR0TAQH/BAUw
-AwEB/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJ
-aW1hZ2UvZ2lmMCEwHzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYj
-aHR0cDovL2xvZ28udmVyaXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFLMW
-kf3upm7ktS5Jj4d4gYDs5bG1MAoGCCqGSM49BAMDA2gAMGUCMGYhDBgmYFo4e1ZC
-4Kf8NoRRkSAsdk1DPcQdhCPQrNZ8NQbOzWm9kA3bbEhCHQ6qQgIxAJw9SDkjOVga
-FRJZap7v1VmyHVIsmXHNxynfGyphe3HR3vPA5Q06Sqotp9iGKt0uEA==
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCB
yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp
@@ -91,20 +27,6 @@ WE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiCKm0oHw0LxOXnGiYZ
hnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
-MIICPDCCAaUCEDyRMcsf9tAbDpq40ES/Er4wDQYJKoZIhvcNAQEFBQAwXzELMAkG
-A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz
-cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2
-MDEyOTAwMDAwMFoXDTI4MDgwMjIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV
-BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt
-YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN
-ADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE
-BarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is
-I19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G
-CSqGSIb3DQEBBQUAA4GBABByUqkFFBkyCEHwxWsKzH4PIRnN5GfcX6kb5sroc50i
-2JhucwNhkcV8sEVAbkSdjbCxlnRhLQ2pRdKkkirWmnWXbj9T/UWZYB2oK0z5XqcJ
-2HUw19JlYD1n1khVdWk/kfVIC0dpImmClr7JyDiGSnoscxlIaU5rfGW/D/xwzoiQ
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBI
MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x
FzAVBgNVBAMTDlNlY3VyZVRydXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIz
@@ -150,35 +72,71 @@ LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI
4uJEvlz36hz1
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
-MIIFbDCCA1SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBHMQswCQYDVQQGEwJVUzEW
-MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVy
-c2FsIENBIDIwHhcNMDQwMzA0MDUwMDAwWhcNMjkwMzA0MDUwMDAwWjBHMQswCQYD
-VQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1
-c3QgVW5pdmVyc2FsIENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
-AQCzVFLByT7y2dyxUxpZKeexw0Uo5dfR7cXFS6GqdHtXr0om/Nj1XqduGdt0DE81
-WzILAePb63p3NeqqWuDW6KFXlPCQo3RWlEQwAx5cTiuFJnSCegx2oG9NzkEtoBUG
-FF+3Qs17j1hhNNwqCPkuwwGmIkQcTAeC5lvO0Ep8BNMZcyfwqph/Lq9O64ceJHdq
-XbboW0W63MOhBW9Wjo8QJqVJwy7XQYci4E+GymC16qFjwAGXEHm9ADwSbSsVsaxL
-se4YuU6W3Nx2/zu+z18DwPw76L5GG//aQMJS9/7jOvdqdzXQ2o3rXhhqMcceujwb
-KNZrVMaqW9eiLBsZzKIC9ptZvTdrhrVtgrrY6slWvKk2WP0+GfPtDCapkzj4T8Fd
-IgbQl+rhrcZV4IErKIM6+vR7IVEAvlI4zs1meaj0gVbi0IMJR1FbUGrP20gaXT73
-y/Zl92zxlfgCOzJWgjl6W70viRu/obTo/3+NjN8D8WBOWBFM66M/ECuDmgFz2ZRt
-hAAnZqzwcEAJQpKtT5MNYQlRJNiS1QuUYbKHsu3/mjX/hVTK7URDrBs8FmtISgoc
-QIgfksILAAX/8sgCSqSqqcyZlpwvWOB94b67B9xfBHJcMTTD7F8t4D1kkCLm0ey4
-Lt1ZrtmhN79UNdxzMk+MBB4zsslG8dhcyFVQyWi9qLo2CQIDAQABo2MwYTAPBgNV
-HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAfBgNV
-HSMEGDAWgBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAOBgNVHQ8BAf8EBAMCAYYwDQYJ
-KoZIhvcNAQEFBQADggIBAGbBxiPz2eAubl/oz66wsCVNK/g7WJtAJDday6sWSf+z
-dXkzoS9tcBc0kf5nfo/sm+VegqlVHy/c1FEHEv6sFj4sNcZj/NwQ6w2jqtB8zNHQ
-L1EuxBRa3ugZ4T7GzKQp5y6EqgYweHZUcyiYWTjgAA1i00J9IZ+uPTqM1fp3DRgr
-Fg5fNuH8KrUwJM/gYwx7WBr+mbpCErGR9Hxo4sjoryzqyX6uuyo9DRXcNJW2GHSo
-ag/HtPQTxORb7QrSpJdMKu0vbBKJPfEncKpqA1Ihn0CoZ1Dy81of398j9tx4TuaY
-T1U6U+Pv8vSfx3zYWK8pIpe44L2RLrB27FcRz+8pRPPphXpgY+RdM4kX2TGq2tbz
-GDVyz4crL2MjhF2EjD9XoIj8mZEoJmmZ1I+XRL6O1UixpCgp8RW04eWe3fiPpm8m
-1wk8OhwRDqZsN/etRIcsKMfYdIKz0G9KV7s1KSegi+ghp4dkNl3M2Basx7InQJJV
-OCiNUW7dFGdTbHFcJoRNdVq2fmBWqU2t+5sel/MN2dKXVHfaPRK34B7vCAas+YWH
-6aLcr34YEoP9VhdBLtUpgn2Z9DH2canPLAEnpQW5qrJITirvn5NSUZU8UnOOVkwX
-QMAJKOSLakhT2+zNVVXxxvjpoixMptEmX36vWkzaH6byHCx+rgIW0lbQL1dTR+iS
+MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBl
+MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv
+b3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzExMTEwMDAwMDAwWjBlMQswCQYDVQQG
+EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl
+cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwggEi
+MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7c
+JpSIqvTO9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYP
+mDI2dsze3Tyoou9q+yHyUmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+
+wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4
+VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpyoeb6pNnVFzF1roV9Iq4/
+AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whfGHdPAgMB
+AAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW
+BBRF66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYun
+pyGd823IDzANBgkqhkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRC
+dWKuh+vy1dneVrOfzM4UKLkNl2BcEkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTf
+fwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38FnSbNd67IJKusm7Xi+fT8r87cm
+NW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i8b5QZ7dsvfPx
+H2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe
++o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
+MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
+QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT
+MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
+b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG
+9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB
+CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97
+nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt
+43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P
+T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4
+gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO
+BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR
+TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw
+DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr
+hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg
+06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF
+PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls
+YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
+CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs
+MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
+ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL
+MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
+LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug
+RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm
++9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW
+PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM
+xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB
+Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3
+hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg
+EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF
+MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA
+FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec
+nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z
+eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF
+hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2
+Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe
+vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep
++OkuE6N36B9K
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFaDCCA1CgAwIBAgIBATANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJVUzEW
@@ -212,27 +170,6 @@ DF4JbAiXfKM9fJP/P6EUp8+1Xevb2xzEdt+Iub1FBZUbrvxGakyvSOPOrg/Sfuvm
bJxPgWp6ZKy7PtXny3YuxadIwVyQD8vIP/rmMuGNG2+k5o7Y+SlIis5z/iw=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
-MIIDZjCCAk6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBEMQswCQYDVQQGEwJVUzEW
-MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3QgR2xvYmFs
-IENBIDIwHhcNMDQwMzA0MDUwMDAwWhcNMTkwMzA0MDUwMDAwWjBEMQswCQYDVQQG
-EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3Qg
-R2xvYmFsIENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDvPE1A
-PRDfO1MA4Wf+lGAVPoWI8YkNkMgoI5kF6CsgncbzYEbYwbLVjDHZ3CB5JIG/NTL8
-Y2nbsSpr7iFY8gjpeMtvy/wWUsiRxP89c96xPqfCfWbB9X5SJBri1WeR0IIQ13hL
-TytCOb1kLUCgsBDTOEhGiKEMuzozKmKY+wCdE1l/bztyqu6mD4b5BWHqZ38MN5aL
-5mkWRxHCJ1kDs6ZgwiFAVvqgx306E+PsV8ez1q6diYD3Aecs9pYrEw15LNnA5IZ7
-S4wMcoKK+xfNAGw6EzywhIdLFnopsk/bHdQL82Y3vdj2V7teJHq4PIu5+pIaGoSe
-2HSPqht/XvT+RSIhAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE
-FHE4NvICMVNHK266ZUapEBVYIAUJMB8GA1UdIwQYMBaAFHE4NvICMVNHK266ZUap
-EBVYIAUJMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQUFAAOCAQEAA/e1K6td
-EPx7srJerJsOflN4WT5CBP51o62sgU7XAotexC3IUnbHLB/8gTKY0UvGkpMzNTEv
-/NgdRN3ggX+d6YvhZJFiCzkIjKx0nVnZellSlxG5FntvRdOW2TF9AjYPnDtuzywN
-A0ZF66D0f0hExghAzN4bcLUprbqLOzRldRtxIR0sFAqwlpW41uryZfspuk/qkZN0
-abby/+Ea0AzRdoXLiiW9l14sbxWZJue2Kf8i7MkCx1YAzUm5s2x7UwQa4qjJqhIF
-I8LO57sEAszAR6LkxCkvW0VXiVHuPOtSCP8HNR6fNWpHSlaY0VqFH4z1Ir+rzoPz
-4iIprn2DQKi6bA==
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT
MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i
YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG
@@ -253,99 +190,52 @@ hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV
5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
-MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBI
-MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x
-FzAVBgNVBAMTDlNlY3VyZVRydXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIz
-MTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAeBgNVBAoTF1NlY3VyZVRydXN0IENv
-cnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCCASIwDQYJKoZIhvcN
-AQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQXOZEz
-Zum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO
-0gMdA+9tDWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIao
-wW8xQmxSPmjL8xk037uHGFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj
-7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b01k/unK8RCSc43Oz969XL0Imnal0ugBS
-8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmHursCAwEAAaOBnTCBmjAT
-BgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB
-/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCeg
-JYYjaHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGC
-NxUBBAMCAQAwDQYJKoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt3
-6Z3q059c4EVlew3KW+JwULKUBRSuSceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/
-3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHfmbx8IVQr5Fiiu1cprp6poxkm
-D5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZnMUFdAvnZyPS
-CPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR
-3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE=
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIICiDCCAg2gAwIBAgIQNfwmXNmET8k9Jj1Xm67XVjAKBggqhkjOPQQDAzCBhDEL
-MAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjE4MDYGA1UECxMvKGMp
-IDIwMDcgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAi
-BgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMjAeFw0wNzExMDUwMDAw
-MDBaFw0zODAxMTgyMzU5NTlaMIGEMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhh
-d3RlLCBJbmMuMTgwNgYDVQQLEy8oYykgMjAwNyB0aGF3dGUsIEluYy4gLSBGb3Ig
-YXV0aG9yaXplZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9v
-dCBDQSAtIEcyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEotWcgnuVnfFSeIf+iha/
-BebfowJPDQfGAFG6DAJSLSKkQjnE/o/qycG+1E3/n3qe4rF8mq2nhglzh9HnmuN6
-papu+7qzcMBniKI11KOasf2twu8x+qi58/sIxpHR+ymVo0IwQDAPBgNVHRMBAf8E
-BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUmtgAMADna3+FGO6Lts6K
-DPgR4bswCgYIKoZIzj0EAwMDaQAwZgIxAN344FdHW6fmCsO99YCKlzUNG4k8VIZ3
-KMqh9HneteY4sPBlcIx/AlTCv//YoT7ZzwIxAMSNlPzcU9LcnXgWHxUzI1NS41ox
-XZ3Krr0TKUQNJ1uo52icEvdYPy5yAlejj6EULg==
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIEIDCCAwigAwIBAgIQNE7VVyDV7exJ9C/ON9srbTANBgkqhkiG9w0BAQUFADCB
-qTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf
-Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw
-MDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNV
-BAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMDYxMTE3MDAwMDAwWhcNMzYw
-NzE2MjM1OTU5WjCBqTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5j
-LjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYG
-A1UECxMvKGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl
-IG9ubHkxHzAdBgNVBAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwggEiMA0GCSqG
-SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCsoPD7gFnUnMekz52hWXMJEEUMDSxuaPFs
-W0hoSVk3/AszGcJ3f8wQLZU0HObrTQmnHNK4yZc2AreJ1CRfBsDMRJSUjQJib+ta
-3RGNKJpchJAQeg29dGYvajig4tVUROsdB58Hum/u6f1OCyn1PoSgAfGcq/gcfomk
-6KHYcWUNo1F77rzSImANuVud37r8UVsLr5iy6S7pBOhih94ryNdOwUxkHt3Ph1i6
-Sk/KaAcdHJ1KxtUvkcx8cXIcxcBn6zL9yZJclNqFwJu/U30rCfSMnZEfl2pSy94J
-NqR32HuHUETVPm4pafs5SSYeCaWAe0At6+gnhcn+Yf1+5nyXHdWdAgMBAAGjQjBA
-MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBR7W0XP
-r87Lev0xkhpqtvNG61dIUDANBgkqhkiG9w0BAQUFAAOCAQEAeRHAS7ORtvzw6WfU
-DW5FvlXok9LOAz/t2iWwHVfLHjp2oEzsUHboZHIMpKnxuIvW1oeEuzLlQRHAd9mz
-YJ3rG9XRbkREqaYB7FViHXe4XI5ISXycO1cRrK1zN44veFyQaEfZYGDm/Ac9IiAX
-xPcW6cTYcvnIc3zfFi8VqT79aie2oetaupgf1eNNZAqdE8hhuvU5HIe6uL17In/2
-/qxAeeWsEG89jxt5dovEN7MhGITlNgDrYyCZuen+MwS7QcjBAvlEYyCegc5C09Y/
-LHbTY5xZ3Y+m4Q6gLkH3LpVHz7z9M/P2C2F+fpErgUfCJzDupxBdN49cOSvkBPB7
-jVaMaA==
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIICiDCCAg2gAwIBAgIQNfwmXNmET8k9Jj1Xm67XVjAKBggqhkjOPQQDAzCBhDEL
-MAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjE4MDYGA1UECxMvKGMp
-IDIwMDcgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAi
-BgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMjAeFw0wNzExMDUwMDAw
-MDBaFw0zODAxMTgyMzU5NTlaMIGEMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhh
-d3RlLCBJbmMuMTgwNgYDVQQLEy8oYykgMjAwNyB0aGF3dGUsIEluYy4gLSBGb3Ig
-YXV0aG9yaXplZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9v
-dCBDQSAtIEcyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEotWcgnuVnfFSeIf+iha/
-BebfowJPDQfGAFG6DAJSLSKkQjnE/o/qycG+1E3/n3qe4rF8mq2nhglzh9HnmuN6
-papu+7qzcMBniKI11KOasf2twu8x+qi58/sIxpHR+ymVo0IwQDAPBgNVHRMBAf8E
-BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUmtgAMADna3+FGO6Lts6K
-DPgR4bswCgYIKoZIzj0EAwMDaQAwZgIxAN344FdHW6fmCsO99YCKlzUNG4k8VIZ3
-KMqh9HneteY4sPBlcIx/AlTCv//YoT7ZzwIxAMSNlPzcU9LcnXgWHxUzI1NS41ox
-XZ3Krr0TKUQNJ1uo52icEvdYPy5yAlejj6EULg==
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0
-IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz
-BgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9y
-aXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG
-9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAwMTk1NFoXDTE5MDYy
-NjAwMTk1NFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29y
-azEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs
-YXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRw
-Oi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNl
-cnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDOOnHK5avIWZJV16vY
-dA757tn2VUdZZUcOBVXc65g2PFxTXdMwzzjsvUGJ7SVCCSRrCl6zfN1SLUzm1NZ9
-WlmpZdRJEy0kTRxQb7XBhVQ7/nHk01xC+YDgkRoKWzk2Z/M/VXwbP7RfZHM047QS
-v4dk+NoS/zcnwbNDu+97bi5p9wIDAQABMA0GCSqGSIb3DQEBBQUAA4GBADt/UG9v
-UJSZSWI4OB9L+KXIPqeCgfYrx+jFzug6EILLGACOTb2oWH+heQC1u+mNr0HZDzTu
-IYEZoDJJKPTEjlbVUjP9UNV+mWwD5MlM/Mtsq2azSiGM5bUMMj4QssxsodyamEwC
-W/POuZ6lcg5Ktz885hZo+L7tdEy8W9ViH0Pd
+MIIEuTCCA6GgAwIBAgIQQBrEZCGzEyEDDrvkEhrFHTANBgkqhkiG9w0BAQsFADCB
+vTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
+ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwOCBWZXJp
+U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MTgwNgYDVQQDEy9W
+ZXJpU2lnbiBVbml2ZXJzYWwgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe
+Fw0wODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIG9MQswCQYDVQQGEwJVUzEX
+MBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0
+IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9y
+IGF1dGhvcml6ZWQgdXNlIG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNh
+bCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEAx2E3XrEBNNti1xWb/1hajCMj1mCOkdeQmIN65lgZOIzF
+9uVkhbSicfvtvbnazU0AtMgtc6XHaXGVHzk8skQHnOgO+k1KxCHfKWGPMiJhgsWH
+H26MfF8WIFFE0XBPV+rjHOPMee5Y2A7Cs0WTwCznmhcrewA3ekEzeOEz4vMQGn+H
+LL729fdC4uW/h2KJXwBL38Xd5HVEMkE6HnFuacsLdUYI0crSK5XQz/u5QGtkjFdN
+/BMReYTtXlT2NJ8IAfMQJQYXStrxHXpma5hgZqTZ79IugvHw7wnqRMkVauIDbjPT
+rJ9VAMf2CGqUuV/c4DPxhGD5WycRtPwW8rtWaoAljQIDAQABo4GyMIGvMA8GA1Ud
+EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMG0GCCsGAQUFBwEMBGEwX6FdoFsw
+WTBXMFUWCWltYWdlL2dpZjAhMB8wBwYFKw4DAhoEFI/l0xqGrI2Oa8PPgGrUSBgs
+exkuMCUWI2h0dHA6Ly9sb2dvLnZlcmlzaWduLmNvbS92c2xvZ28uZ2lmMB0GA1Ud
+DgQWBBS2d/ppSEefUxLVwuoHMnYH0ZcHGTANBgkqhkiG9w0BAQsFAAOCAQEASvj4
+sAPmLGd75JR3Y8xuTPl9Dg3cyLk1uXBPY/ok+myDjEedO2Pzmvl2MpWRsXe8rJq+
+seQxIcaBlVZaDrHC1LGmWazxY8u4TB1ZkErvkBYoH1quEPuBUDgMbMzxPcP1Y+Oz
+4yHJJDnp/RVmRvQbEdBNc6N9Rvk97ahfYtTxP/jgdFcrGJ2BtMQo2pSXpXDrrB2+
+BxHw1dvd5Yzw1TKwg+ZX4o+/vqGqvz0dtdQ46tewXDpPaj+PwGZsY6rp2aQW9IHR
+lRQOfc2VNNnSj3BzgXucfr2YYdhFh5iQxeuGMMY1v/D/w1WIg0vvBZIGcfK4mJO3
+7M2CYfE45k+XmCpajQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh
+MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH
+MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT
+MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
+b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG
+9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI
+2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx
+1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ
+q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz
+tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ
+vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP
+BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV
+5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY
+1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4
+NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG
+Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91
+8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe
+pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl
+MrY=
-----END CERTIFICATE-----
diff --git a/readme.txt b/readme.txt
index 05daa4d..2c6ff23 100644
--- a/readme.txt
+++ b/readme.txt
@@ -3,8 +3,8 @@ Contributors: Plugify, hello@lukerollans.me
Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=hello%40plugify%2eio&lc=GB&item_name=Plugin%20Development%20Donation¤cy_code=USD
Tags: credit card,braintree,gravity form,payment
Requires at least: 3.8
-Tested up to: 3.9
-Stable tag: 1.1.1
+Tested up to: 5.1.1
+Stable tag: 1.4.0
License: GPLv2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
@@ -61,6 +61,19 @@ No filters are currently available for this pre-release version
== Changelog ==
+= 1.4.0 =
+* Add Braintree's Advanced Fraud Tools integration.
+
+= 1.3.1 =
+* Fix bug with tax exempt flag.
+
+= 1.3.0 =
+* Update Braintree SDK files to 3.39.0 version.
+
+= 1.2.0 =
+* Update Braintree SDK files to the latest version.
+* Add ability to use subscriptions.
+
= 1.1.1 =
* Dashes and spaces are now removed from credit card number before sending to Braintree