diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..62e7820 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +# Created by .ignore support plugin (hsz.mobi) +.idea \ No newline at end of file diff --git a/README.md b/README.md index 991f90f..42ea532 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,28 @@ # Integrate Gravity Forms with Braintree -Braintree Payments is a payment gateway provider owned by eBAY Inc, which allows you to proces credit card payments without the need for a bank merchant account and full PCI-compliance. No sensitive data such as credit card numbers are stored on your server, Braintree takes care of everything. - -#### If you have found this plugin useful, consider taking a moment to rate it, or perhaps even a small donation. +Braintree Payments is a payment gateway provider owned by PayPal which allows you to process credit card payments without the need for a bank merchant account and full PCI-compliance. No sensitive data such as credit card numbers are stored on your server, Braintree takes care of everything. ## Installation -1. Upload the `gravity-forms-braintree` folder to the `/wp-content/plugins/` directory. -2. Activate the plugin through the 'Plugins' menu in WordPress. -3. Navigate to the Form you wish to setup with a Braintree feed. -4. Under Form Settings, choose the Braintree option. +### Automatic installation -## Features +Automatic installation is the easiest option as WordPress handles the file transfers itself and you don't need to leave your web browser. To do an automatic install of Gravity Forms Braintree Payments, log in to your WordPress dashboard, navigate to the Plugins menu and click Add New. + +In the search field type Gravity Forms Braintree Payments and click Search Plugins. Once you've found our plugin (make sure it says "by Angell EYE") you can view details about it such as the the rating and description. Most importantly, of course, you can install it by simply clicking Install Now. + +### Manual Installation -* Seamlessly integrates your Gravity Forms credit card forms with Braintree Payments -* 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 +1. Unzip the files and upload the folder into your plugins folder (/wp-content/plugins/) overwriting older versions if they exist +2. Activate the plugin in your WordPress admin area. -## Subscriptions -The plugin does not currently support Braintree Subscriptions. Keep a look out for it in a future version +### Usage + +1. Navigate to the Form you wish to setup with a Braintree feed. +2. Under Form Settings, choose the Braintree option. + +## Features -## 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. +* Seamlessly integrates your Gravity Forms credit card forms with Braintree Payments. +* 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. \ No newline at end of file diff --git a/angelleye-gravity-forms-braintree.php b/angelleye-gravity-forms-braintree.php new file mode 100644 index 0000000..18e1b33 --- /dev/null +++ b/angelleye-gravity-forms-braintree.php @@ -0,0 +1,339 @@ +setPHP('7.2'); + $checker->setRequiredClasses(['GFForms' => 'The Gravity Forms plugin is required in order to run Gravity Forms Braintree Payments.']); + $checker->setRequiredExtensions(['xmlwriter', 'openssl', 'dom', 'hash', 'curl']); + $checker->setRequiredPlugins(['gravityforms/gravityforms.php'=>['min_version'=>'2.4', 'install_link'=>'https://rocketgenius.pxf.io/c/1331556/445235/7938', 'name'=>'Gravity Forms']]); + //$checker->setDeactivatePlugins([self::$plugin_base_file]); + if($checker->check()===true) { + $this->init(); + } + } + + public function init() + { + $path = trailingslashit( dirname( __FILE__ ) ); + + // Ensure Gravity Forms (payment addon framework) is installed and good to go + if( is_callable( array( 'GFForms', 'include_payment_addon_framework' ) ) ) { + + // Bootstrap payment addon framework + GFForms::include_payment_addon_framework(); + GFForms::include_addon_framework(); + + // Require Braintree Payments core + if(!class_exists('Braintree')) { + require_once $path . 'lib/Braintree.php'; + } + + // Require plugin entry point + require_once $path . 'includes/angelleye-gravity-braintree-helper.php'; + require_once $path . 'lib/class.plugify-gform-braintree.php'; + require_once $path . 'includes/class-angelleye-gravity-braintree-ach-field.php'; + require_once $path . 'includes/class-angelleye-gravity-braintree-ach-toggle-field.php'; + require_once $path . 'lib/angelleye-gravity-forms-payment-logger.php'; + require_once $path . 'includes/angelleye-gravity-braintree-field-mapping.php'; + require_once $path . 'includes/class-angelleye-gravity-braintree-creditcard.php'; + require_once $path . 'includes/class-angelleye-gravity-braintree-reports.php'; + + /** + * Required functions + */ + if (!function_exists('angelleye_queue_update')) { + require_once( 'includes/angelleye-functions.php' ); + } + + // Fire off entry point + new Plugify_GForm_Braintree(); + new AngelleyeGravityBraintreeFieldMapping(); + + /** + * Register the ACH form field and Payment Method toggle field + */ + GF_Fields::register( new Angelleye_Gravity_Braintree_ACH_Field() ); + GF_Fields::register( new Angelleye_Gravity_Braintree_ACH_Toggle_Field() ); + GF_Fields::register( new Angelleye_Gravity_Braintree_CreditCard_Field() ); + AngellEYE_GForm_Braintree_Payment_Logger::instance(); + } + } + + public static function isBraintreeFeedActive() + { + global $wpdb; + $addon_feed_table_name = $wpdb->prefix . 'gf_addon_feed'; + $is_active = $wpdb->get_var("select is_active from ".$addon_feed_table_name." where addon_slug='gravity-forms-braintree' and is_active=1"); + + return $is_active=='1'; + } + + /** + * Get Payment summary html. + * + * @return void + */ + public function gform_payment_preview_html() { + + $status = false; + $preview_html = ''; + $extra_fees_enable = false; + if( !empty( $_POST['nonce'] ) && wp_verify_nonce($_POST['nonce'],'preview-payment-nonce') ) { + $card_type = !empty( $_POST['card_type'] ) ? $_POST['card_type'] : ''; + $form_id = !empty( $_POST['form_id'] ) ? $_POST['form_id'] : ''; + $form_data = !empty( $_POST['form_data'] ) ? $_POST['form_data'] : []; + + $extra_fees = angelleye_get_extra_fees( $form_id ); + $extra_fees_label = !empty( $extra_fees['title'] ) ? $extra_fees['title'] : ''; + + if( empty( $extra_fees['is_fees_enable'] ) ) { + + if( $card_type === 'ACH' ) { + + wp_send_json([ + 'status' => true, + 'extra_fees_enable' => false, + 'html' => '' + ]); + } else { + + wp_send_json([ + 'status' => false, + 'html' => '' + ]); + } + } + + $status = true; + $extra_fees_enable = true; + + $product_fields = get_product_fields_by_form_id( $form_id ); + $products = []; + if( !empty( $product_fields['products'] ) && is_array( $product_fields['products'] ) ) { + + foreach ( $product_fields['products'] as $product_field ) { + $id = !empty( $product_field['id'] ) ? $product_field['id'] : ''; + $label = !empty( $product_field['label'] ) ? $product_field['label'] : ''; + $product_group = !empty( $product_field['group'] ) ? $product_field['group'] : ''; + $price_id = !empty( $product_field['price_id'] ) ? $product_field['price_id'] : ''; + $quantity_id = !empty( $product_field['quantity_id'] ) ? $product_field['quantity_id'] : ''; + + $product_price = 0; + $product_qty = 1; + if( !empty( $form_data ) && is_array( $form_data ) ) { + foreach ( $form_data as $data ) { + if( !empty( $data['name'] ) && $data['name'] == $price_id ) { + $product_price = !empty( $data['value'] ) ? get_price_without_formatter( $data['value'] ) : ''; + $label = !empty( $data['value'] ) ? get_selected_product_label( $data['value'], $label ) : ''; + } elseif ( !empty( $data['name'] ) && $data['name'] == $quantity_id ) { + $product_qty = !empty( $data['value'] ) ? $data['value'] : 1; + } + } + } + + $products[] = [ + 'id' => $id, + 'label' => $label, + 'price' => $product_price, + 'quantity' => $product_qty, + ]; + } + } + + $cart_prices = get_gfb_prices([ + 'form_id' => $form_id, + 'products' => $products, + 'card_type' => $card_type, + ]); + + $extra_fee_amount = !empty( $cart_prices['extra_fee_amount'] ) ? $cart_prices['extra_fee_amount'] : '0'; + ob_start(); + ?> +
| + | + | + | + |
|---|---|---|---|
| + | + | + | + |
| + | + | ||
| + | + | ||
| + | + | ||
| "),t=0;t<7;t+=1)g+=" | "+C.i18n[o].dayOfWeekShort[(t+C.dayOfWeekStart)%7]+" | ";g+="
|---|---|
| "+f+" | ")),g+=''+u+" | ",p.getDay()===C.dayOfWeekStartPrev&&(g+="
'+response.message+'
'+message+'
'); +} + +setTimeout(function() { + function manageExtraFeesFields( is_enable = false ) { + let extraFeesFields = document.querySelectorAll('input.extra-fees-input'); + if( undefined !== extraFeesFields && extraFeesFields.length > 0 ) { + extraFeesFields.forEach(function ( field ){ + if(is_enable) { + field.setAttribute('readonly', 'readonly'); + } else { + field.removeAttribute('readonly'); + } + }); + } + } + let disableExtraFee = document.getElementById('disable_extra_fees'); + if( undefined !== disableExtraFee && null !== disableExtraFee ) { + manageExtraFeesFields(disableExtraFee.checked); + disableExtraFee.addEventListener('click', function (e) { + manageExtraFeesFields(disableExtraFee.checked); + }); + } +}, 500); diff --git a/assets/js/scripts.js b/assets/js/scripts.js index e1fcb85..6145dea 100644 --- a/assets/js/scripts.js +++ b/assets/js/scripts.js @@ -148,4 +148,4 @@ jQuery( function($) { }); -}); +}); \ No newline at end of file diff --git a/gravity-forms-braintree.php b/gravity-forms-braintree.php deleted file mode 100644 index 45534e9..0000000 --- a/gravity-forms-braintree.php +++ /dev/null @@ -1,35 +0,0 @@ - diff --git a/includes/angelleye-functions.php b/includes/angelleye-functions.php new file mode 100644 index 0000000..c0e4489 --- /dev/null +++ b/includes/angelleye-functions.php @@ -0,0 +1,91 @@ +file = $file; + $plugin->file_id = $file_id; + $plugin->product_id = $product_id; + + $angelleye_queued_updates[] = $plugin; + } + +} + + +/** + * Load installer for the AngellEYE Updater. + * @return $api Object + */ +if (!class_exists('AngellEYE_Updater') && !function_exists('angell_updater_install')) { + + function angell_updater_install($api, $action, $args) { + $download_url = AEU_ZIP_URL; + + if ('plugin_information' != $action || + false !== $api || + !isset($args->slug) || + 'angelleye-updater' != $args->slug + ) + return $api; + + $api = new stdClass(); + $api->name = 'AngellEYE Updater'; + $api->version = ''; + $api->download_link = esc_url($download_url); + return $api; + } + + add_filter('plugins_api', 'angell_updater_install', 10, 3); +} + +/** + * AngellEYE Installation Prompts + */ +if (!class_exists('AngellEYE_Updater') && !function_exists('angell_updater_notice')) { + + /** + * Display a notice if the "AngellEYE Updater" plugin hasn't been installed. + * @return void + */ + function angell_updater_notice() { + $active_plugins = apply_filters('active_plugins', get_option('active_plugins')); + if (in_array('angelleye-updater/angelleye-updater.php', $active_plugins)) + return; + + $slug = 'angelleye-updater'; + $install_url = wp_nonce_url(self_admin_url('update.php?action=install-plugin&plugin=' . $slug), 'install-plugin_' . $slug); + $activate_url = 'plugins.php?action=activate&plugin=' . urlencode('angelleye-updater/angelleye-updater.php') . '&plugin_status=all&paged=1&s&_wpnonce=' . urlencode(wp_create_nonce('activate-plugin_angelleye-updater/angelleye-updater.php')); + + $message = 'Install the Angell EYE Updater plugin to get updates for your Angell EYE plugins.'; + $is_downloaded = false; + $plugins = array_keys(get_plugins()); + foreach ($plugins as $plugin) { + if (strpos($plugin, 'angelleye-updater.php') !== false) { + $is_downloaded = true; + $message = ' Activate the Angell EYE Updater plugin to get updates for your Angell EYE plugins.'; + } + } + echo '' . $message . '
File Name: %s', 'angelleye-gravity-forms-braintree'), $_GET['report']); ?>
+Start Date: %s', 'angelleye-gravity-forms-braintree'), $start_date); ?>
+End Date: %s', 'angelleye-gravity-forms-braintree'), $end_date); ?>
+ +Merchant Account ID: %s', 'angelleye-gravity-forms-braintree'), $merchant_id); ?>
+ +| + + | + + |
|---|
'.sprintf( __( "%s report file is not exists."), esc_html( $_GET['report'] )).'
'; + } + } + + /** + * Display braintree reports filter and listing. + * + * @return void + */ + public function display_reports_html() { + + $merchant_accounts = $this->merchant_account_choices(); + $report_files = $this->get_transaction_report_files([ + 'paged' => !empty( $_GET['paged'] ) ? $_GET['paged'] : 1, + ]); + $report_records = !empty( $report_files['records']) ? $report_files['records'] : []; + ?> +| + | + | + | + | + |
|---|---|---|---|---|
| + | + | + | + | + + | +
| + | ||||
';print_r($get_form); die;
+
+$braintree_mapping = isset($get_form['braintree_fields_mapping'])?$get_form['braintree_fields_mapping']:[];
+
+function agreegateAllFields($main_arr, $parent_key = ''){
+ $final_fields = [];
+ foreach ($main_arr as $main_key => $single_field){
+ if(is_array($single_field))
+ $final_fields[$main_key] = agreegateAllFields($single_field, $main_key);
+ else {
+ $final_fields[($parent_key != '' ? $parent_key . '.' : '') . $main_key] = $single_field;
+ }
+ }
+
+ return $final_fields;
+}
+
+$total_braintree_fields = agreegateAllFields($braintree_fields);
+//print_r($total_braintree_fields);
+
+$gravity_fields = $get_form['fields'];
+$ignore_type_fields = ['creditcard'];
+
+$final_gravity_fields = [];
+foreach ($gravity_fields as $gravity_field) {
+ if(in_array($gravity_field->type, $ignore_type_fields))
+ continue;
+ if(is_array($gravity_field['inputs']) && count($gravity_field['inputs'])){
+ foreach ($gravity_field['inputs'] as $single_input)
+ $final_gravity_fields[$single_input['id']] = $single_input['label'].' ('.$gravity_field['label'].')';
+ }else
+ $final_gravity_fields[$gravity_field['id']] = $gravity_field['label'];
+}
+//print_r($gravity_fields);
+?>
+ Braintree Field Mapping
+
+ 'settings',
+ 'subview' => 'gravity-forms-braintree',
+ 'id' => $form_id
+ ], menu_page_url('gf_edit_forms', false));
+
+ echo " Please make sure to configure the Braintree feed to process the payments.
";
+}?>
+
+
+ Here you can map individual Gravity form fields to Braintree fields so that they will show up in the Braintree transaction details.
+
+The field names on the left are currently available in Braintree. Simply select the Gravity form field from the drop-down that you would like to pass to the matching field in Braintree transaction details.
+If you do not see a Braintree field available for your Gravity form field, you may create custom fields within your Braintree account, and then add these custom fields at the bottom of this field mapping section.
+For more information, see our documentation.
+
+
- * $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 Customer[]
*/
- public static function create($attribs = array())
+ public static function all()
{
- Braintree_Util::verifyKeys(self::createSignature(), $attribs);
- return self::_doCreate('/customers', array('customer' => $attribs));
+ return Configuration::gateway()->customer()->all();
}
/**
- * 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
+ * @param array $query
+ * @param int[] $ids
+ * @return Customer|Customer[]
*/
- public static function createNoValidate($attribs = array())
+ public static function fetch($query, $ids)
{
- $result = self::create($attribs);
- return self::returnObjectOrThrowException(__CLASS__, $result);
+ return Configuration::gateway()->customer()->fetch($query, $ids);
}
+
/**
- * create a customer from a TransparentRedirect operation
*
- * @access public
* @param array $attribs
- * @return object
+ * @return Result\Successful|Result\Error
*/
- public static function createFromTransparentRedirect($queryString)
+ public static function create($attribs = [])
{
- 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()->create($attribs);
}
/**
*
- * @access public
- * @param none
- * @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';
- }
-
-
- /**
- * 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
+ * @param array $attribs
+ * @return Customer
*/
- public static function updateSignature()
+ public static function createNoValidate($attribs = [])
{
- $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;
+ return Configuration::gateway()->customer()->createNoValidate($attribs);
}
-
/**
- * 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 array $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);
- }
- /**
- *
- * @access public
- * @param none
- * @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';
- }
-
- /**
- * update a customer from a TransparentRedirect operation
- *
- * @access public
- * @param array $attribs
- * @return object
- */
- 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()->updateNoValidate($customerId, $attributes);
}
/* instance methods */
@@ -395,170 +189,173 @@ 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);
+ foreach ($customerAttribs['addresses'] as $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);
+ foreach ($customerAttribs['creditCards'] as $creditCard) {
+ $creditCardArray[] = CreditCard::factory($creditCard);
+ }
+ }
+ $this->_set('creditCards', $creditCardArray);
+
+ $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);
+
+ $googlePayCardArray = [];
+ if (isset($customerAttribs['androidPayCards'])) {
+ foreach ($customerAttribs['androidPayCards'] as $googlePayCard) {
+ $googlePayCardArray[] = GooglePayCard::factory($googlePayCard);
+ }
+ }
+ $this->_set('googlePayCards', $googlePayCardArray);
+
+ $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);
+
+ $samsungPayCardArray = [];
+ if (isset($customerAttribs['samsungPayCards'])) {
+ foreach ($customerAttribs['samsungPayCards'] as $samsungPayCard) {
+ $samsungPayCardArray[] = SamsungPayCard::factory($samsungPayCard);
}
}
- $this->_set('creditCards', $ccArray);
+ $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->googlePayCards,
+ $this->venmoAccounts,
+ $this->visaCheckoutCards,
+ $this->samsungPayCards,
+ $this->usBankAccounts
+ ));
+
+ $customFields = [];
+ if (isset($customerAttribs['customFields'])) {
+ $customFields = $customerAttribs['customFields'];
+ }
+ $this->_set('customFields', $customFields);
}
/**
* returns a string representation of the customer
* @return string
*/
- public function __toString()
+ 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;
}
- /* private class properties */
-
/**
- * @access protected
- * @var array registry of customer data
- */
- protected $_attributes = array(
- 'addresses' => '',
- 'company' => '',
- 'creditCards' => '',
- 'email' => '',
- 'fax' => '',
- 'firstName' => '',
- 'id' => '',
- 'lastName' => '',
- 'phone' => '',
- 'createdAt' => '',
- 'updatedAt' => '',
- 'website' => '',
- );
-
- /**
- * sends the create request to the gateway
+ * returns the customer's default payment method
*
- * @ignore
- * @param string $url
- * @param array $params
- * @return mixed
+ * @return CreditCard|PayPalAccount
*/
- public static function _doCreate($url, $params)
+ public function defaultPaymentMethod()
{
- $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.'
- );
- }
+ $defaultPaymentMethods = array_filter($this->paymentMethods, 'Braintree\Customer::_defaultPaymentMethodFilter');
+ return current($defaultPaymentMethods);
}
-
- /* 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)
+ public static function _defaultPaymentMethodFilter($paymentMethod)
{
- $response = Braintree_Http::$httpVerb($url, $params);
-
- return self::_verifyGatewayResponse($response);
+ return $paymentMethod->isDefault();
}
+ /* private class properties */
+
/**
- * 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
+ * @access protected
+ * @var array registry of customer data
*/
- 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"
- );
- }
- }
+ protected $_attributes = [
+ 'addresses' => '',
+ 'company' => '',
+ 'creditCards' => '',
+ 'email' => '',
+ 'fax' => '',
+ 'firstName' => '',
+ 'id' => '',
+ 'lastName' => '',
+ 'phone' => '',
+ 'taxIdentifiers' => '',
+ 'createdAt' => '',
+ 'updatedAt' => '',
+ 'website' => '',
+ ];
/**
- * 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;
}
-
}
diff --git a/lib/Braintree/CustomerGateway.php b/lib/Braintree/CustomerGateway.php
new file mode 100644
index 0000000..ac30430
--- /dev/null
+++ b/lib/Braintree/CustomerGateway.php
@@ -0,0 +1,607 @@
+== More information ==
+ *
+ // phpcs:ignore Generic.Files.LineLength
+ * 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);
+ }
+
+ /**
+ * 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', 'paymentMethodNonce',
+ ['riskData' =>
+ ['customerBrowser', 'customerIp']
+ ],
+ ['creditCard' => $creditCardSignature],
+ ['customFields' => ['_anyKey_']],
+ ['taxIdentifiers' =>
+ ['countryCode', 'identifier']
+ ],
+ ['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',
+ 'paymentMethodNonce', 'defaultPaymentMethodToken',
+ ['creditCard' => $creditCardSignature],
+ ['customFields' => ['_anyKey_']],
+ ['taxIdentifiers' =>
+ ['countryCode', 'identifier']
+ ],
+ ['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.
+ // phpcs:ignore Generic.Files.LineLength
+ * 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);
+ }
+
+ /* 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 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 from gateway response to googlePayCard objects
+ $googlePayCardArray = [];
+ if (isset($customerAttribs['androidPayCards'])) {
+ foreach ($customerAttribs['androidPayCards'] as $googlePayCard) {
+ $googlePayCardArray[] = GooglePayCard::factory($googlePayCard);
+ }
+ }
+ $this->_set('googlePayCards', $googlePayCardArray);
+
+ $paymentMethodsArray = array_merge(
+ $this->creditCards,
+ $this->paypalAccounts,
+ $this->applePayCards,
+ $this->googlePayCards
+ );
+ $this->_set('paymentMethods', $paymentMethodsArray);
+ }
+
+ /**
+ * 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|GooglePayCard
+ */
+ public function defaultPaymentMethod()
+ {
+ // phpcs:ignore Generic.Files.LineLength
+ $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'])
+ );
+ } elseif (isset($response['apiErrorResponse'])) {
+ return new Result\Error($response['apiErrorResponse']);
+ } else {
+ throw new Exception\Unexpected(
+ "Expected customer or apiErrorResponse"
+ );
+ }
+ }
+}
diff --git a/lib/Braintree/CustomerSearch.php b/lib/Braintree/CustomerSearch.php
index 5de6cee..08bdf27 100644
--- a/lib/Braintree/CustomerSearch.php
+++ b/lib/Braintree/CustomerSearch.php
@@ -1,30 +1,106 @@
merchantAccountDetails = $disbursementAttribs['merchantAccount'];
if (isset($disbursementAttribs['merchantAccount'])) {
- $this->_set('merchantAccount',
- Braintree_MerchantAccount::factory($disbursementAttribs['merchantAccount'])
+ $this->_set(
+ '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;
}
@@ -31,19 +39,29 @@ public static function factory($attributes)
return $instance;
}
- public function __toString()
+ public function __toString()
{
- $display = array(
+ $display = [
'id', 'merchantAccountDetails', 'exceptionMessage', 'amount',
'disbursementDate', 'followUpAction', 'retry', 'success',
- 'transactionIds'
- );
+ 'transactionIds', 'disbursementType'
+ ];
- $displayAttributes = array();
- foreach ($display AS $attrib) {
+ $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;
}
}
diff --git a/lib/Braintree/DisbursementDetails.php b/lib/Braintree/DisbursementDetails.php
index 471a163..4befb00 100644
--- a/lib/Braintree/DisbursementDetails.php
+++ b/lib/Braintree/DisbursementDetails.php
@@ -1,31 +1,25 @@
disbursementDate);
}
}
diff --git a/lib/Braintree/Discount.php b/lib/Braintree/Discount.php
index 0c8e12e..8c0d53a 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();
+ }
}
diff --git a/lib/Braintree/DiscountGateway.php b/lib/Braintree/DiscountGateway.php
new file mode 100644
index 0000000..5382495
--- /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'
+ );
+ }
+}
diff --git a/lib/Braintree/Dispute.php b/lib/Braintree/Dispute.php
new file mode 100644
index 0000000..a8aaf13
--- /dev/null
+++ b/lib/Braintree/Dispute.php
@@ -0,0 +1,221 @@
+_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['paypalMessages'])) {
+ $paypalMessagesArray = array_map(function ($paypalMessages) {
+ return new Dispute\PayPalMessageDetails($paypalMessages);
+ }, $disputeAttribs['paypalMessages']);
+ $this->_set('paypalMessages', $paypalMessagesArray);
+ }
+
+ 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
+ * // phpcs:ignore Generic.Files.LineLength
+ * @param string|mixed $contentOrRequest If a string, $contentOrRequest is the text-based content for the dispute evidence.
+ * Alternatively, the second argument can also be an array containing:
+ * string $content The text-based content for the dispute evidence, and
+ * string $category The category for this piece of evidence
+ * Note: (optional) string $tag parameter is deprecated, use $category instead.
+ *
+ * Example: https://developers.braintreepayments.com/reference/request/dispute/add-text-evidence/php#submitting-categorized-evidence
+ */
+ 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);
+ }
+
+ /** @return array */
+ public static function allChargebackProtectionLevelTypes()
+ {
+ return [
+ Dispute::EFFORTLESS,
+ Dispute::STANDARD,
+ Dispute::NOT_PROTECTED
+ ];
+ }
+}
diff --git a/lib/Braintree/Dispute/EvidenceDetails.php b/lib/Braintree/Dispute/EvidenceDetails.php
new file mode 100644
index 0000000..096925e
--- /dev/null
+++ b/lib/Braintree/Dispute/EvidenceDetails.php
@@ -0,0 +1,30 @@
+_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)) {
+ trigger_error('$tag is deprecated, use $category instead', E_USER_DEPRECATED);
+ $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');
+ } elseif ((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) {
+ $message = 'evidence with id "' . $evidenceId . '" for dispute with id "' . $disputeId . '" not found';
+ throw new Exception\NotFound($message);
+ }
+ }
+
+ /**
+ * Search for Disputes, given a DisputeSearch query
+ *
+ * @param array $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);
+ }
+}
diff --git a/lib/Braintree/DisputeSearch.php b/lib/Braintree/DisputeSearch.php
new file mode 100644
index 0000000..7ac3c34
--- /dev/null
+++ b/lib/Braintree/DisputeSearch.php
@@ -0,0 +1,96 @@
+ 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;
+ }
+}
diff --git a/lib/Braintree/DocumentUploadGateway.php b/lib/Braintree/DocumentUploadGateway.php
new file mode 100644
index 0000000..1bff87f
--- /dev/null
+++ b/lib/Braintree/DocumentUploadGateway.php
@@ -0,0 +1,80 @@
+_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'
+ ];
+ }
+}
diff --git a/lib/Braintree/EndsWithNode.php b/lib/Braintree/EndsWithNode.php
new file mode 100644
index 0000000..de962d9
--- /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;
+ }
+}
diff --git a/lib/Braintree/EqualityNode.php b/lib/Braintree/EqualityNode.php
index 68c10b5..b14675e 100644
--- a/lib/Braintree/EqualityNode.php
+++ b/lib/Braintree/EqualityNode.php
@@ -1,8 +1,10 @@
searchTerms['is_not'] = strval($value);
return $this;
diff --git a/lib/Braintree/Error/Codes.php b/lib/Braintree/Error/Codes.php
index 6f18bc8..81706e1 100644
--- a/lib/Braintree/Error/Codes.php
+++ b/lib/Braintree/Error/Codes.php
@@ -1,12 +1,6 @@
_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.
@@ -76,11 +83,13 @@ 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(); }
+ foreach (array_slice($pieces, 0, -1) as $key) {
+ $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 +97,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();
*
*/
@@ -101,7 +110,7 @@ public function shallowAll()
*
* @ignore
*/
- public function __get($name)
+ public function __get($name)
{
$varName = "_$name";
return isset($this->$varName) ? $this->$varName : null;
@@ -111,8 +120,19 @@ public function __get($name)
*
* @ignore
*/
- public function __toString()
+ public function __toString()
{
return sprintf('%s', $this->_errors);
}
+
+ /**
+ * Implementation of JsonSerializable
+ *
+ * @ignore
+ * @return array
+ */
+ public function jsonSerialize()
+ {
+ return $this->_errors->deepAll();
+ }
}
diff --git a/lib/Braintree/Error/Validation.php b/lib/Braintree/Error/Validation.php
index 8253ae6..6dee0b7 100644
--- a/lib/Braintree/Error/Validation.php
+++ b/lib/Braintree/Error/Validation.php
@@ -1,11 +1,8 @@
== 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}
+ * // phpcs:ignore Generic.Files.LineLength
+ * 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
* @param array $attributes
*/
- public function __construct($attributes)
+ public function __construct($attributes)
{
$this->_initializeFromArray($attributes);
}
@@ -42,13 +39,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) {
+ foreach ($attributes as $name => $value) {
$varName = "_$name";
- $this->$varName = Braintree_Util::delimiterToCamelCase($value, '_');
+ $this->$varName = Util::delimiterToCamelCase($value, '_');
}
}
@@ -56,7 +53,7 @@ private function _initializeFromArray($attributes)
*
* @ignore
*/
- public function __get($name)
+ public function __get($name)
{
$varName = "_$name";
return isset($this->$varName) ? $this->$varName : null;
diff --git a/lib/Braintree/Error/ValidationErrorCollection.php b/lib/Braintree/Error/ValidationErrorCollection.php
index 5125c90..d4c8bf3 100644
--- a/lib/Braintree/Error/ValidationErrorCollection.php
+++ b/lib/Braintree/Error/ValidationErrorCollection.php
@@ -1,53 +1,49 @@
== 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}
+ * // phpcs:ignore Generic.Files.LineLength
+ * 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
*/
- public function __construct($data)
+ public function __construct($data)
{
- foreach($data AS $key => $errorData)
+ foreach ($data as $key => $errorData) {
// map errors to new collections recursively
if ($key == 'errors') {
- foreach ($errorData AS $error) {
- $this->_errors[] = new Braintree_Error_Validation($error);
+ foreach ($errorData as $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);
- foreach($this->_nested as $nestedErrors)
- {
+ $validationErrors = array_merge([], $this->_errors);
+ foreach ($this->_nested as $nestedErrors) {
$validationErrors = array_merge($validationErrors, $nestedErrors->deepAll());
}
return $validationErrors;
@@ -56,8 +52,7 @@ public function deepAll()
public function deepSize()
{
$total = sizeof($this->_errors);
- foreach($this->_nested as $_nestedErrors)
- {
+ foreach ($this->_nested as $_nestedErrors) {
$total = $total + $_nestedErrors->deepSize();
}
return $total;
@@ -75,11 +70,11 @@ public function forKey($key)
public function onAttribute($attribute)
{
- $matches = array();
- foreach ($this->_errors AS $key => $error) {
- if($error->attribute == $attribute) {
- $matches[] = $error;
- }
+ $matches = [];
+ foreach ($this->_errors as $key => $error) {
+ if ($error->attribute == $attribute) {
+ $matches[] = $error;
+ }
}
return $matches;
}
@@ -94,7 +89,7 @@ public function shallowAll()
*
* @ignore
*/
- public function __get($name)
+ public function __get($name)
{
$varName = "_$name";
return isset($this->$varName) ? $this->$varName : null;
@@ -105,14 +100,13 @@ public function __get($name)
*/
public function __toString()
{
- $output = array();
+ $output = [];
- // TODO: implement scope
if (!empty($this->_errors)) {
$output[] = $this->_inspect($this->_errors);
}
if (!empty($this->_nested)) {
- foreach ($this->_nested AS $key => $values) {
+ foreach ($this->_nested as $key => $values) {
$output[] = $this->_inspect($this->_nested);
}
}
@@ -125,8 +119,11 @@ public function __toString()
private function _inspect($errors, $scope = null)
{
$eOutput = '[' . __CLASS__ . '/errors:[';
- foreach($errors AS $error => $errorObj) {
- $outputErrs[] = "({$errorObj->error['code']} {$errorObj->error['message']})";
+ $outputErrs = [];
+ foreach ($errors as $error => $errorObj) {
+ if (is_array($errorObj->error)) {
+ $outputErrs[] = "({$errorObj->error['code']} {$errorObj->error['message']})";
+ }
}
$eOutput .= join(', ', $outputErrs) . ']]';
diff --git a/lib/Braintree/Exception.php b/lib/Braintree/Exception.php
index 40ec119..d844eaf 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) . ']';
+ }
+}
diff --git a/lib/Braintree/FacilitatorDetails.php b/lib/Braintree/FacilitatorDetails.php
new file mode 100644
index 0000000..86c1b60
--- /dev/null
+++ b/lib/Braintree/FacilitatorDetails.php
@@ -0,0 +1,34 @@
+_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) . ']';
+ }
+}
diff --git a/lib/Braintree/Gateway.php b/lib/Braintree/Gateway.php
new file mode 100644
index 0000000..d11176c
--- /dev/null
+++ b/lib/Braintree/Gateway.php
@@ -0,0 +1,267 @@
+config = $config;
+ $this->graphQLClient = new GraphQLClient($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 UsBankAccountGateway
+ */
+ public function usBankAccount()
+ {
+ return new UsBankAccountGateway($this);
+ }
+
+ /**
+ *
+ * @return UsBankAccountVerificationGateway
+ */
+ public function usBankAccountVerification()
+ {
+ return new UsBankAccountVerificationGateway($this);
+ }
+
+ /**
+ *
+ * @return WebhookNotificationGateway
+ */
+ public function webhookNotification()
+ {
+ return new WebhookNotificationGateway($this);
+ }
+
+ /**
+ *
+ * @return WebhookTestingGateway
+ */
+ public function webhookTesting()
+ {
+ return new WebhookTestingGateway($this);
+ }
+}
diff --git a/lib/Braintree/GooglePayCard.php b/lib/Braintree/GooglePayCard.php
new file mode 100644
index 0000000..0a3621b
--- /dev/null
+++ b/lib/Braintree/GooglePayCard.php
@@ -0,0 +1,91 @@
+== More information ==
+ *
+ * See {@link https://developers.braintreepayments.com/javascript+php}
- * $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,37 +39,58 @@ 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
* @return string string representation of the object's structure
*/
- public function __toString()
- {
- $returnObject = $this->_returnObjectName;
- return __CLASS__ . '['.$this->$returnObject->__toString().']';
- }
+ public function __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);
+ }
}
diff --git a/lib/Braintree/Result/UsBankAccountVerification.php b/lib/Braintree/Result/UsBankAccountVerification.php
new file mode 100644
index 0000000..73bc9d6
--- /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
- * Braintree_Transaction::saleNoValidate(array(
+ * Transaction::saleNoValidate(array(
* 'amount' => '100.00',
* 'creditCard' => array(
* 'number' => '5105105105105100',
@@ -26,7 +23,7 @@
*
* Full example:
*
- * Braintree_Transaction::saleNoValidate(array(
+ * Transaction::saleNoValidate(array(
* 'amount' => '100.00',
* 'orderId' => '123',
* 'channel' => 'MyShoppingCardProvider',
@@ -42,7 +39,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 +79,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 +102,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 +123,7 @@
* $transaction[options][submitForSettlement] to true.
*
*
- * $transaction = Braintree_Transaction::saleNoValidate(array(
+ * $transaction = Transaction::saleNoValidate(array(
* 'amount' => '100.00',
* 'creditCard' => array(
* 'number' => '5105105105105100',
@@ -140,35 +137,88 @@
*
* == 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\Transaction\GooglePayCardDetails $googlePayCardDetails transaction Google Pay card info
+ * @property-read \Braintree\Transaction\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 \DateTime $createdAt transaction created DateTime
+ * @property-read \Braintree\Transaction\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 $graphQLId transaction graphQLId
* @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\TransactionLineItem[] $lineItems
+ * @property-read string $merchantAccountId
+ * @property-read string $networkTransactionId
+ * @property-read string $orderId
+ * @property-read string $acquirerReferenceNumber
+ * @property-read string $paymentInstrumentType
+ * @property-read \Braintree\Transaction\PayPalDetails $paypalDetails transaction paypal account info
+ * @property-read \Braintree\Transaction\PayPalHereDetails $paypalHereDetails
+ * @property-read \Braintree\Transaction\LocalPaymentDetails $localPaymentDetails transaction local payment info
+ * @property-read string $planId
+ * @property-read string $processedWithNetworkToken
+ * @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 $productSku
+ * @property-read string $purchaseOrderNumber
+ * @property-read mixed $reccuring
+ * @property-read mixed $refundIds
+ * @property-read string $refundedTransactionId
+ * @property-read string $retrievalReferenceNumber
+ * @property-read \Braintree\RiskData $riskData
+ * @property-read \Braintree\Transaction\SamsungPayCardDetails $samsungPayCardDetails transaction Samsung Pay card info
+ * @property-read string $scaExemptionRequested
+ * @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\Transaction\VisaCheckoutCardDetails $visaCheckoutCardDetails transaction Visa Checkout card info
+ * @property-read string $voiceReferralName
*
*/
+// phpcs:enable Generic.Files.LineLength
-final class Braintree_Transaction extends Braintree
+class Transaction extends Base
{
// Transaction Status
const AUTHORIZATION_EXPIRED = 'authorization_expired';
@@ -182,6 +232,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,429 +257,259 @@ 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 RISK_THRESHOLD = 'risk_threshold';
+ const THREE_D_SECURE = 'three_d_secure';
+ const TOKEN_ISSUANCE = 'token_issuance';
+ 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';
+
+ // Additional Charge Types
+ const RESTAURANT = 'lodging';
+ const GIFT_SHOP = 'gift_shop';
+ const MINI_BAR = 'mini_bar';
+ const TELEPHONE = 'telephone';
+ const LAUNDRY = 'laundry';
+ const OTHER = 'other';
/**
+ * 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();
+ // Rename androidPayCard from API responses to GooglePayCard
+ if (isset($transactionAttribs['androidPayCard'])) {
+ $this->_set(
+ 'googlePayCardDetails',
+ new Transaction\GooglePayCardDetails(
+ $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['visaCheckoutCard'])) {
+ $this->_set(
+ 'visaCheckoutCardDetails',
+ new Transaction\VisaCheckoutCardDetails(
+ $transactionAttribs['visaCheckoutCard']
+ )
+ );
}
- }
- public static function fetch($query, $ids)
- {
- $criteria = array();
- foreach ($query as $term) {
- $criteria[$term->name] = $term->toparam();
+ if (isset($transactionAttribs['samsungPayCard'])) {
+ $this->_set(
+ 'samsungPayCardDetails',
+ new Transaction\SamsungPayCardDetails(
+ $transactionAttribs['samsungPayCard']
+ )
+ );
}
- $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);
- }
-
- public static function releaseFromEscrow($transactionId)
- {
- self::_validateId($transactionId);
-
- $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['usBankAccount'])) {
+ $this->_set(
+ 'usBankAccount',
+ new Transaction\UsBankAccountDetails(
+ $transactionAttribs['usBankAccount']
+ )
+ );
+ }
+ if (isset($transactionAttribs['paypal'])) {
+ $this->_set(
+ 'paypalDetails',
+ new Transaction\PayPalDetails(
+ $transactionAttribs['paypal']
+ )
+ );
+ }
- /**
- * 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['paypalHere'])) {
+ $this->_set(
+ 'paypalHereDetails',
+ new Transaction\PayPalHereDetails(
+ $transactionAttribs['paypalHere']
+ )
+ );
+ }
- if (isset($transactionAttribs['creditCard'])) {
- $this->_set('creditCardDetails',
- new Braintree_Transaction_CreditCardDetails(
- $transactionAttribs['creditCard']
+ if (isset($transactionAttribs['localPayment'])) {
+ $this->_set(
+ 'localPaymentDetails',
+ new Transaction\LocalPaymentDetails(
+ $transactionAttribs['localPayment']
)
);
}
if (isset($transactionAttribs['customer'])) {
- $this->_set('customerDetails',
- new Braintree_Transaction_CustomerDetails(
+ $this->_set(
+ 'customerDetails',
+ new Transaction\CustomerDetails(
$transactionAttribs['customer']
)
);
}
if (isset($transactionAttribs['billing'])) {
- $this->_set('billingDetails',
- new Braintree_Transaction_AddressDetails(
+ $this->_set(
+ 'billingDetails',
+ new Transaction\AddressDetails(
$transactionAttribs['billing']
)
);
}
if (isset($transactionAttribs['shipping'])) {
- $this->_set('shippingDetails',
- new Braintree_Transaction_AddressDetails(
+ $this->_set(
+ 'shippingDetails',
+ new Transaction\AddressDetails(
$transactionAttribs['shipping']
)
);
}
if (isset($transactionAttribs['subscription'])) {
- $this->_set('subscriptionDetails',
- new Braintree_Transaction_SubscriptionDetails(
+ $this->_set(
+ 'subscriptionDetails',
+ new Transaction\SubscriptionDetails(
$transactionAttribs['subscription']
)
);
}
if (isset($transactionAttribs['descriptor'])) {
- $this->_set('descriptor',
- new Braintree_Descriptor(
+ $this->_set(
+ 'descriptor',
+ new Descriptor(
$transactionAttribs['descriptor']
)
);
}
if (isset($transactionAttribs['disbursementDetails'])) {
- $this->_set('disbursementDetails',
- new Braintree_DisbursementDetails($transactionAttribs['disbursementDetails'])
+ $this->_set(
+ '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);
+ foreach ($transactionAttribs['statusHistory'] as $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);
+ foreach ($transactionAttribs['addOns'] as $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);
+ foreach ($transactionAttribs['discounts'] as $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']));
+ }
}
/**
* returns a string representation of the transaction
* @return string
*/
- public function __toString()
+ public function __toString()
{
// array of attributes to print
- $display = array(
+ $display = [
'id', 'type', 'amount', 'status',
'createdAt', 'creditCardDetails', 'customerDetails'
- );
+ ];
- $displayAttributes = array();
- foreach ($display AS $attrib) {
+ $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)
@@ -639,104 +522,155 @@ public function vaultCreditCard()
$token = $this->creditCardDetails->token;
if (empty($token)) {
return null;
- }
- else {
- return Braintree_CreditCard::find($token);
+ } else {
+ return CreditCard::find($token);
}
}
+ /** @return null|\Braintree\Customer */
public function vaultCustomer()
{
$customerId = $this->customerDetails->id;
if (empty($customerId)) {
return null;
- }
- else {
- return Braintree_Customer::find($customerId);
+ } else {
+ return Customer::find($customerId);
}
}
- public function isDisbursed() {
+ /** @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);
-
- return self::_verifyGatewayResponse($response);
+ $instance = new self();
+ $instance->_initialize($attributes);
+ return $instance;
}
/**
- * verifies that a valid transaction id is being used
- * @ignore
- * @param string transaction id
- * @throws InvalidArgumentException
+ * adjustAuthorization: It is a static method to invoke
+ * method in the TransactionGateway
+ *
+ * @param string transactionId
+ * @param string amount
*/
- 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 adjustAuthorization($transactionId, $amount)
+ {
+ return Configuration::gateway()->transaction()->adjustAuthorization($transactionId, $amount);
}
+ // static methods redirecting to gateway
- /* private class methods */
+ public static function cloneTransaction($transactionId, $attribs)
+ {
+ return Configuration::gateway()->transaction()->cloneTransaction($transactionId, $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 createTransactionUrl()
{
- 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()->createTransactionUrl();
}
- /**
- * 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 credit($attribs)
{
- $instance = new self();
- $instance->_initialize($attributes);
- return $instance;
+ return Configuration::gateway()->transaction()->credit($attribs);
+ }
+
+ public static function creditNoValidate($attribs)
+ {
+ return Configuration::gateway()->transaction()->creditNoValidate($attribs);
+ }
+
+ public static function find($id)
+ {
+ 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 = [])
+ {
+ // phpcs:ignore Generic.Files.LineLength
+ 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);
}
}
diff --git a/lib/Braintree/Transaction/AddressDetails.php b/lib/Braintree/Transaction/AddressDetails.php
index dc89573..e59c296 100644
--- a/lib/Braintree/Transaction/AddressDetails.php
+++ b/lib/Braintree/Transaction/AddressDetails.php
@@ -1,11 +1,8 @@
_attributes['expirationDate'] = $this->expirationMonth . '/' . $this->expirationYear;
$this->_attributes['maskedNumber'] = $this->bin . '******' . $this->last4;
-
}
}
diff --git a/lib/Braintree/Transaction/CustomerDetails.php b/lib/Braintree/Transaction/CustomerDetails.php
index b5151b3..8841fca 100644
--- a/lib/Braintree/Transaction/CustomerDetails.php
+++ b/lib/Braintree/Transaction/CustomerDetails.php
@@ -1,18 +1,15 @@
_attributes['cardType'] = $this->virtualCardType;
+ $this->_attributes['last4'] = $this->virtualCardLast4;
+ }
+}
diff --git a/lib/Braintree/Transaction/LocalPaymentDetails.php b/lib/Braintree/Transaction/LocalPaymentDetails.php
new file mode 100644
index 0000000..acd343a
--- /dev/null
+++ b/lib/Braintree/Transaction/LocalPaymentDetails.php
@@ -0,0 +1,45 @@
+_attributes['expirationDate'] = $this->expirationMonth . '/' . $this->expirationYear;
+ $this->_attributes['maskedNumber'] = $this->bin . '******' . $this->last4;
+ }
+}
diff --git a/lib/Braintree/Transaction/StatusDetails.php b/lib/Braintree/Transaction/StatusDetails.php
index c105ced..c6df5b4 100644
--- a/lib/Braintree/Transaction/StatusDetails.php
+++ b/lib/Braintree/Transaction/StatusDetails.php
@@ -1,25 +1,21 @@
achMandate = $achMandate;
+ }
+}
diff --git a/lib/Braintree/Transaction/VenmoAccountDetails.php b/lib/Braintree/Transaction/VenmoAccountDetails.php
new file mode 100644
index 0000000..bd00e39
--- /dev/null
+++ b/lib/Braintree/Transaction/VenmoAccountDetails.php
@@ -0,0 +1,39 @@
+_attributes['expirationDate'] = $this->expirationMonth . '/' . $this->expirationYear;
+ $this->_attributes['maskedNumber'] = $this->bin . '******' . $this->last4;
+ }
+}
diff --git a/lib/Braintree/TransactionGateway.php b/lib/Braintree/TransactionGateway.php
new file mode 100644
index 0000000..2ba16a2
--- /dev/null
+++ b/lib/Braintree/TransactionGateway.php
@@ -0,0 +1,663 @@
+== More information ==
+ *
+ * // phpcs:ignore Generic.Files.LineLength
+ * 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);
+ $attribs = Util::replaceKey($attribs, 'googlePayCard', 'androidPayCard');
+ 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);
+ }
+
+ 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',
+ 'merchantAccountId',
+ 'orderId',
+ 'paymentMethodNonce',
+ 'paymentMethodToken',
+ 'productSku',
+ 'purchaseOrderNumber',
+ 'recurring',
+ 'serviceFeeAmount',
+ 'sharedPaymentMethodToken',
+ 'sharedPaymentMethodNonce',
+ 'sharedCustomerId',
+ 'sharedShippingAddressId',
+ 'sharedBillingAddressId',
+ 'shippingAddressId',
+ 'taxAmount',
+ 'taxExempt',
+ 'threeDSecureToken',
+ 'threeDSecureAuthenticationId',
+ 'transactionSource',
+ 'type',
+ 'venmoSdkPaymentMethodCode',
+ 'scaExemption',
+ 'shippingAmount',
+ 'discountAmount',
+ 'shipsFromPostalCode',
+ ['riskData' =>
+ [
+ 'customerBrowser', 'customerIp', 'customerDeviceId',
+ 'customerLocationZip', 'customerTenure'],
+ ],
+ ['creditCard' =>
+ [
+ 'token',
+ 'cardholderName',
+ 'cvv',
+ 'expirationDate',
+ 'expirationMonth',
+ 'expirationYear',
+ 'number',
+ ['paymentReaderCardDetails' => ['encryptedCardData', 'keySerialNumber']],
+ ],
+ ],
+ ['customer' =>
+ [
+ 'id', 'company', 'email', 'fax', 'firstName',
+ 'lastName', 'phone', 'website'],
+ ],
+ ['billing' =>
+ [
+ 'firstName', 'lastName', 'company', 'countryName',
+ 'countryCodeAlpha2', 'countryCodeAlpha3', 'countryCodeNumeric',
+ 'extendedAddress', 'locality', 'phoneNumber', 'postalCode', 'region',
+ 'streetAddress'],
+ ],
+ ['shipping' =>
+ [
+ 'firstName', 'lastName', 'company', 'countryName',
+ 'countryCodeAlpha2', 'countryCodeAlpha3', 'countryCodeNumeric',
+ 'extendedAddress', 'locality', 'phoneNumber', 'postalCode', 'region',
+ 'shippingMethod', 'streetAddress'],
+ ],
+ ['threeDSecurePassThru' =>
+ [
+ 'eciFlag',
+ 'cavv',
+ 'xid',
+ 'threeDSecureVersion',
+ 'authenticationResponse',
+ 'directoryResponse',
+ 'cavvAlgorithm',
+ 'dsTransactionId'],
+ ],
+ ['options' =>
+ [
+ 'holdInEscrow',
+ 'storeInVault',
+ 'storeInVaultOnSuccess',
+ 'submitForSettlement',
+ 'addBillingAddressToPaymentMethod',
+ 'venmoSdkSession',
+ 'storeShippingAddressInVault',
+ 'payeeId',
+ 'payeeEmail',
+ 'skipAdvancedFraudChecking',
+ 'skipAvs',
+ 'skipCvv',
+ ['creditCard' =>
+ ['accountType']
+ ],
+ ['threeDSecure' =>
+ ['required']
+ ],
+ ['paypal' =>
+ [
+ 'payeeId',
+ 'payeeEmail',
+ 'customField',
+ 'description',
+ ['supplementaryData' => ['_anyKey_']],
+ ]
+ ],
+ ['amexRewards' =>
+ [
+ 'requestId',
+ 'points',
+ 'currencyAmount',
+ 'currencyIsoCode'
+ ]
+ ],
+ ['venmo' =>
+ [
+ 'profileId'
+ ]
+ ]
+ ],
+ ],
+ ['customFields' => ['_anyKey_']],
+ ['descriptor' => ['name', 'phone', 'url']],
+ ['paypalAccount' => ['payeeId', 'payeeEmail', 'payerId', 'paymentId']],
+ ['applePayCard' =>
+ [
+ 'cardholderName',
+ 'cryptogram',
+ 'eciIndicator',
+ 'expirationMonth',
+ 'expirationYear',
+ 'number'
+ ]
+ ],
+ ['industry' =>
+ ['industryType',
+ ['data' =>
+ [
+ 'folioNumber',
+ 'checkInDate',
+ 'checkOutDate',
+ 'travelPackage',
+ 'departureDate',
+ 'lodgingCheckInDate',
+ 'lodgingCheckOutDate',
+ 'lodgingName',
+ 'roomRate',
+ 'roomTax',
+ 'passengerFirstName',
+ 'passengerLastName',
+ 'passengerMiddleInitial',
+ 'passengerTitle',
+ 'issuedDate',
+ 'travelAgencyName',
+ 'travelAgencyCode',
+ 'ticketNumber',
+ 'issuingCarrierCode',
+ 'customerCode',
+ 'fareAmount',
+ 'feeAmount',
+ 'taxAmount',
+ 'restrictedTicket',
+ 'noShow',
+ 'advancedDeposit',
+ 'fireSafe',
+ 'propertyPhone',
+ ['legs' =>
+ [
+ 'conjunctionTicket',
+ 'exchangeTicket',
+ 'couponNumber',
+ 'serviceClass',
+ 'carrierCode',
+ 'fareBasisCode',
+ 'flightNumber',
+ 'departureDate',
+ 'departureAirportCode',
+ 'departureTime',
+ 'arrivalAirportCode',
+ 'arrivalTime',
+ 'stopoverPermitted',
+ 'fareAmount',
+ 'feeAmount',
+ 'taxAmount',
+ 'endorsementOrRestrictions'
+ ]
+ ],
+ ['additionalCharges' =>
+ [
+ 'kind',
+ 'amount'
+ ]
+ ]
+ ]
+ ]
+ ]
+ ],
+ ['lineItems' =>
+ [
+ 'commodityCode',
+ 'description',
+ 'discountAmount',
+ 'kind',
+ 'name',
+ 'productCode',
+ 'quantity',
+ 'taxAmount',
+ 'totalAmount',
+ 'unitAmount',
+ 'unitOfMeasure',
+ 'unitTaxAmount',
+ 'url'
+ ]
+ ],
+ ['externalVault' =>
+ ['status' , 'previousNetworkTransactionId'],
+ ],
+ ['googlePayCard' =>
+ [
+ 'cryptogram',
+ 'eciIndicator',
+ 'expirationMonth',
+ 'expirationYear',
+ 'googleTransactionId',
+ 'number',
+ 'sourceCardLastFour',
+ 'sourceCardType'
+ ]
+ ],
+ ['installments' => ['count']]
+ ];
+ }
+
+ public static function submitForSettlementSignature()
+ {
+ return ['orderId', ['descriptor' => ['name', 'phone', 'url']],
+ 'purchaseOrderNumber',
+ 'taxAmount',
+ 'taxExempt',
+ 'shippingAmount',
+ 'discountAmount',
+ 'shipsFromPostalCode',
+ ['lineItems' =>
+ [
+ 'commodityCode',
+ 'description',
+ 'discountAmount',
+ 'kind',
+ 'name',
+ 'productCode',
+ 'quantity',
+ 'taxAmount',
+ 'totalAmount',
+ 'unitAmount',
+ 'unitOfMeasure',
+ 'unitTaxAmount',
+ 'url'
+ ]
+ ],
+ ];
+ }
+
+ public static function updateDetailsSignature()
+ {
+ return ['amount', 'orderId', ['descriptor' => ['name', 'phone', 'url']]];
+ }
+
+ public static function refundSignature()
+ {
+ return [
+ 'amount',
+ 'merchantAccountId',
+ '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 (Note: $recurring param is deprecated. Use $transactionSource instead)
+ * @return Result\Successful|Result\Error
+ */
+ public function sale($attribs)
+ {
+ if (array_key_exists('recurring', $attribs)) {
+ trigger_error('$recurring is deprecated, use $transactionSource instead', E_USER_DEPRECATED);
+ }
+ 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.
+ * // phpcs:ignore Generic.Files.LineLength
+ * 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\RequestTimeout();
+ }
+ }
+
+ 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\RequestTimeout();
+ }
+ }
+
+ /**
+ * Adjusts the authorization amount of a transaction
+ *
+ * @access public
+ * @param string $transactionId
+ * @param string amount
+ *
+ * @return Result\Successful|Result\Error
+ * @throws Exception\Unexpected
+ */
+ public function adjustAuthorization($transactionId, $amount)
+ {
+ self::_validateId($transactionId);
+ $params = ['amount' => $amount];
+ $path = $this->_config->merchantPath() . '/transactions/' . $transactionId . '/adjust_authorization';
+ $response = $this->_http->put($path, ['transaction' => $params]);
+ return $this->_verifyGatewayResponse($response);
+ }
+
+ /**
+ * 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'
+ );
+ }
+ }
+
+ /**
+ * 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'])
+ );
+ } elseif (isset($response['apiErrorResponse'])) {
+ return new Result\Error($response['apiErrorResponse']);
+ } else {
+ throw new Exception\Unexpected(
+ "Expected transaction or apiErrorResponse"
+ );
+ }
+ }
+}
diff --git a/lib/Braintree/TransactionLineItem.php b/lib/Braintree/TransactionLineItem.php
new file mode 100644
index 0000000..fa392f2
--- /dev/null
+++ b/lib/Braintree/TransactionLineItem.php
@@ -0,0 +1,53 @@
+transactionLineItem()->findAll($transactionId);
+ }
+}
diff --git a/lib/Braintree/TransactionLineItemGateway.php b/lib/Braintree/TransactionLineItemGateway.php
new file mode 100644
index 0000000..6d6d066
--- /dev/null
+++ b/lib/Braintree/TransactionLineItemGateway.php
@@ -0,0 +1,68 @@
+_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.');
+ }
+ }
+}
diff --git a/lib/Braintree/TransactionSearch.php b/lib/Braintree/TransactionSearch.php
index 487d781..ee3e50d 100644
--- a/lib/Braintree/TransactionSearch.php
+++ b/lib/Braintree/TransactionSearch.php
@@ -1,125 +1,306 @@
- * $trData = Braintree_TransparentRedirect::createCustomerData(array(
- * 'redirectUrl => 'http://example.com/redirect_back_to_merchant_site',
- * ));
- *
- *
- * In addition to the redirectUrl, any data that needs to be protected
- * from user tampering should be included in the trData.
- * For example, to prevent the user from tampering with the transaction
- * amount, include the amount in the trData.
- *
- *
- * $trData = Braintree_TransparentRedirect::transactionData(array(
- * 'redirectUrl' => 'http://example.com/complete_transaction',
- * 'transaction' => array('amount' => '100.00'),
- * ));
- *
- *
- *
- * @package Braintree
- * @category Resources
- * @copyright 2010 Braintree Payment Solutions
- */
-class Braintree_TransparentRedirect
-{
- // Request Kinds
- const CREATE_TRANSACTION = 'create_transaction';
- const CREATE_PAYMENT_METHOD = 'create_payment_method';
- const UPDATE_PAYMENT_METHOD = 'update_payment_method';
- 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())
- */
- 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()),
- );
- }
-
- 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()
- );
- }
-
- /**
- * 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);
- }
-
- /**
- * 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);
-
- }
-
- public static function url()
- {
- return Braintree_Configuration::merchantUrl() . "/transparent_redirect_requests";
- }
-
- /**
- * 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);
- }
-
- /**
- * 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);
- }
-
- /**
- * 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);
- }
-
- 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);
- }
-
-}
-Braintree_TransparentRedirect::init();
diff --git a/lib/Braintree/UnknownPaymentMethod.php b/lib/Braintree/UnknownPaymentMethod.php
new file mode 100644
index 0000000..dd754b5
--- /dev/null
+++ b/lib/Braintree/UnknownPaymentMethod.php
@@ -0,0 +1,68 @@
+== 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;
+ }
+}
diff --git a/lib/Braintree/UsBankAccount.php b/lib/Braintree/UsBankAccount.php
new file mode 100644
index 0000000..0fb362f
--- /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);
+ }
+}
diff --git a/lib/Braintree/UsBankAccountGateway.php b/lib/Braintree/UsBankAccountGateway.php
new file mode 100644
index 0000000..3c8aef2
--- /dev/null
+++ b/lib/Braintree/UsBankAccountGateway.php
@@ -0,0 +1,105 @@
+== 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'])
+ );
+ } elseif (isset($response['apiErrorResponse'])) {
+ return new Result\Error($response['apiErrorResponse']);
+ } else {
+ throw new Exception\Unexpected(
+ 'Expected US bank account or apiErrorResponse'
+ );
+ }
+ }
+}
diff --git a/lib/Braintree/UsBankAccountVerification.php b/lib/Braintree/UsBankAccountVerification.php
new file mode 100644
index 0000000..8a9a554
--- /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);
+ }
+}
diff --git a/lib/Braintree/UsBankAccountVerificationGateway.php b/lib/Braintree/UsBankAccountVerificationGateway.php
new file mode 100644
index 0000000..918f12b
--- /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 {
+ // phpcs:ignore Generic.Files.LineLength
+ $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']);
+ } elseif (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'
+ );
+ }
+ }
+}
diff --git a/lib/Braintree/UsBankAccountVerificationSearch.php b/lib/Braintree/UsBankAccountVerificationSearch.php
new file mode 100644
index 0000000..a3c22e9
--- /dev/null
+++ b/lib/Braintree/UsBankAccountVerificationSearch.php
@@ -0,0 +1,72 @@
+success) {
+ return $resultObj->$resultObjName;
+ } else {
+ throw new Exception\ValidationsFailed();
+ }
+ }
+
+ /**
+ * 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'
- );
-
- $name = str_replace('Braintree_', '', $name);
+ $classNamesToResponseKeys = [
+ 'Braintree\CreditCard' => 'creditCard',
+ 'Braintree\CreditCardGateway' => 'creditCard',
+ 'Braintree\Customer' => 'customer',
+ 'Braintree\CustomerGateway' => 'customer',
+ 'Braintree\Subscription' => 'subscription',
+ 'Braintree\SubscriptionGateway' => 'subscription',
+ 'Braintree\Transaction' => 'transaction',
+ 'Braintree\TransactionGateway' => 'transaction',
+ 'Braintree\CreditCardVerification' => 'verification',
+ 'Braintree\CreditCardVerificationGateway' => 'verification',
+ 'Braintree\AddOn' => 'addOn',
+ 'Braintree\AddOnGateway' => 'addOn',
+ 'Braintree\Discount' => 'discount',
+ 'Braintree\DiscountGateway' => 'discount',
+ 'Braintree\Dispute' => 'dispute',
+ 'Braintree\Dispute\EvidenceDetails' => 'evidence',
+ 'Braintree\DocumentUpload' => 'documentUpload',
+ 'Braintree\Plan' => 'plan',
+ 'Braintree\PlanGateway' => 'plan',
+ 'Braintree\Address' => 'address',
+ 'Braintree\AddressGateway' => 'address',
+ 'Braintree\SettlementBatchSummary' => 'settlementBatchSummary',
+ 'Braintree\SettlementBatchSummaryGateway' => 'settlementBatchSummary',
+ 'Braintree\Merchant' => 'merchant',
+ 'Braintree\MerchantGateway' => 'merchant',
+ 'Braintree\MerchantAccount' => 'merchantAccount',
+ 'Braintree\MerchantAccountGateway' => 'merchantAccount',
+ 'Braintree\OAuthCredentials' => 'credentials',
+ 'Braintree\OAuthResult' => 'result',
+ 'Braintree\PayPalAccount' => 'paypalAccount',
+ 'Braintree\PayPalAccountGateway' => 'paypalAccount',
+ 'Braintree\UsBankAccountVerification' => 'usBankAccountVerification',
+ ];
+
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'
- );
-
- return 'Braintree_' . $responseKeysToClassNames[$name];
+ $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 (string) $responseKeysToClassNames[$name];
}
/**
@@ -134,16 +218,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 +250,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,32 +311,36 @@ 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;
-
+ foreach ($array as $key => $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();
- foreach ($attributes AS $key => $value) {
+ public static function attributesToString($attributes)
+ {
+ $printableAttribs = [];
+ foreach ($attributes as $key => $value) {
if (is_array($value)) {
- $pAttrib = Braintree_Util::attributesToString($value);
- } else if ($value instanceof DateTime) {
+ $pAttrib = self::attributesToString($value);
+ } elseif ($value instanceof DateTime) {
$pAttrib = $value->format(DateTime::RFC850);
} else {
$pAttrib = $value;
}
$printableAttribs[$key] = sprintf('%s', $pAttrib);
}
- return Braintree_Util::implodeAssociativeArray($printableAttribs);
+ return self::implodeAssociativeArray($printableAttribs);
}
/**
@@ -231,12 +359,29 @@ public static function verifyKeys($signature, $attributes)
$invalidKeys = array_diff($userKeys, $validKeys);
$invalidKeys = self::_removeWildcardKeys($validKeys, $invalidKeys);
- if(!empty($invalidKeys)) {
+ if (!empty($invalidKeys)) {
asort($invalidKeys);
$sortedList = join(', ', $invalidKeys);
- throw new InvalidArgumentException('invalid keys: '. $sortedList);
+ throw new InvalidArgumentException('invalid keys: ' . $sortedList);
}
}
+
+ /**
+ * replaces the value of a key in an array
+ * @param $array
+ * @param string $oldKey
+ * @param string $newKey
+ * @return array
+ */
+ public static function replaceKey($array, $oldKey, $newKey)
+ {
+ if (array_key_exists($oldKey, $array)) {
+ $array[$newKey] = $array[$oldKey];
+ unset($array[$oldKey]);
+ }
+ return $array;
+ }
+
/**
* flattens a numerically indexed nested array to a single level
* @param array $keys
@@ -245,9 +390,9 @@ public static function verifyKeys($signature, $attributes)
*/
private static function _flattenArray($keys, $namespace = null)
{
- $flattenedArray = array();
- foreach($keys AS $key) {
- if(is_array($key)) {
+ $flattenedArray = [];
+ foreach ($keys as $key) {
+ if (is_array($key)) {
$theKeys = array_keys($key);
$theValues = array_values($key);
$scope = $theKeys[0];
@@ -264,25 +409,25 @@ private static function _flattenArray($keys, $namespace = null)
private static function _flattenUserKeys($keys, $namespace = null)
{
- $flattenedArray = array();
-
- foreach($keys AS $key => $value) {
- $fullKey = empty($namespace) ? $key : $namespace;
- if (!is_numeric($key) && $namespace != null) {
- $fullKey .= '[' . $key . ']';
- }
- if (is_numeric($key) && is_string($value)) {
- $fullKey .= '[' . $value . ']';
- }
- if(is_array($value)) {
- $more = self::_flattenUserKeys($value, $fullKey);
- $flattenedArray = array_merge($flattenedArray, $more);
- } else {
- $flattenedArray[] = $fullKey;
- }
- }
- sort($flattenedArray);
- return $flattenedArray;
+ $flattenedArray = [];
+
+ foreach ($keys as $key => $value) {
+ $fullKey = empty($namespace) ? $key : $namespace;
+ if (!is_numeric($key) && $namespace != null) {
+ $fullKey .= '[' . $key . ']';
+ }
+ if (is_numeric($key) && is_string($value)) {
+ $fullKey .= '[' . $value . ']';
+ }
+ if (is_array($value)) {
+ $more = self::_flattenUserKeys($value, $fullKey);
+ $flattenedArray = array_merge($flattenedArray, $more);
+ } else {
+ $flattenedArray[] = $fullKey;
+ }
+ }
+ sort($flattenedArray);
+ return $flattenedArray;
}
/**
@@ -293,10 +438,10 @@ private static function _flattenUserKeys($keys, $namespace = null)
*/
private static function _removeWildcardKeys($validKeys, $invalidKeys)
{
- foreach($validKeys AS $key) {
+ foreach ($validKeys as $key) {
if (stristr($key, '[_anyKey_]')) {
$wildcardKey = str_replace('[_anyKey_]', '', $key);
- foreach ($invalidKeys AS $index => $invalidKey) {
+ foreach ($invalidKeys as $index => $invalidKey) {
if (stristr($invalidKey, $wildcardKey)) {
unset($invalidKeys[$index]);
}
diff --git a/lib/Braintree/VenmoAccount.php b/lib/Braintree/VenmoAccount.php
new file mode 100644
index 0000000..bb24ced
--- /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);
+ }
+}
diff --git a/lib/Braintree/Version.php b/lib/Braintree/Version.php
index 5a04005..42b5d43 100644
--- a/lib/Braintree/Version.php
+++ b/lib/Braintree/Version.php
@@ -1,30 +1,25 @@
== More information ==
+ *
+ * // phpcs:ignore Generic.Files.LineLength
+ * 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;
+ }
+}
diff --git a/lib/Braintree/WebhookNotification.php b/lib/Braintree/WebhookNotification.php
index 873211d..326ba3a 100644
--- a/lib/Braintree/WebhookNotification.php
+++ b/lib/Braintree/WebhookNotification.php
@@ -1,6 +1,32 @@
webhookNotification()->parse($signature, $payload);
}
public static function verify($challenge)
{
- $publicKey = Braintree_Configuration::publicKey();
- $digest = Braintree_Digest::hexDigest($challenge);
- return "{$publicKey}|{$digest}";
+ return Configuration::gateway()->webhookNotification()->verify($challenge);
}
public static function factory($attributes)
@@ -40,34 +58,15 @@ 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)
{
+ // phpcs:disable Generic.Files.LineLength
$this->_attributes = $attributes;
+ if (!isset($attributes['sourceMerchantId'])) {
+ $this->_set('sourceMerchantId', null);
+ }
+
if (isset($attributes['subject']['apiErrorResponse'])) {
$wrapperNode = $attributes['subject']['apiErrorResponse'];
} else {
@@ -75,28 +74,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['grantedPaymentInstrumentUpdate'])) {
+ $this->_set('grantedPaymentInstrumentUpdate', GrantedPaymentInstrumentUpdate::factory($wrapperNode['grantedPaymentInstrumentUpdate']));
+ }
+
+ if (in_array($attributes['kind'], [self::GRANTED_PAYMENT_METHOD_REVOKED, self::PAYMENT_METHOD_REVOKED_BY_CUSTOMER])) {
+ $this->_set('revokedPaymentMethodMetadata', RevokedPaymentMethodMetadata::factory($wrapperNode));
+ }
+
+ if (isset($wrapperNode['localPayment'])) {
+ $this->_set('localPaymentCompleted', LocalPaymentCompleted::factory($wrapperNode['localPayment']));
+ }
+
+ if (isset($wrapperNode['localPaymentReversed'])) {
+ $this->_set('localPaymentReversed', LocalPaymentReversed::factory($wrapperNode['localPaymentReversed']));
}
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']);
}
+ // phpcs:enable Generic.Files.LineLength
}
}
diff --git a/lib/Braintree/WebhookNotificationGateway.php b/lib/Braintree/WebhookNotificationGateway.php
new file mode 100644
index 0000000..22bf07b
--- /dev/null
+++ b/lib/Braintree/WebhookNotificationGateway.php
@@ -0,0 +1,75 @@
+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;
+ }
+}
diff --git a/lib/Braintree/WebhookTesting.php b/lib/Braintree/WebhookTesting.php
index f8d74da..5a473f4 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
-
- ";
- }
+namespace Braintree;
- 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
-
- ";
- }
-
- 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);
}
}
diff --git a/lib/Braintree/WebhookTestingGateway.php b/lib/Braintree/WebhookTestingGateway.php
new file mode 100644
index 0000000..cecf61e
--- /dev/null
+++ b/lib/Braintree/WebhookTestingGateway.php
@@ -0,0 +1,723 @@
+config = $gateway->config;
+ $this->config->assertHasAccessTokenOrKeys();
+ }
+
+ public function sampleNotification($kind, $id, $sourceMerchantId = null)
+ {
+ $xml = self::_sampleXml($kind, $id, $sourceMerchantId);
+ $payload = base64_encode($xml) . "\n";
+ $publicKey = $this->config->getPublicKey();
+ $sha = Digest::hexDigestSha1($this->config->getPrivateKey(), $payload);
+ $signature = $publicKey . "|" . $sha;
+
+ 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::DISPUTE_ACCEPTED:
+ $subjectXml = self::_disputeAcceptedSampleXml($id);
+ break;
+ case WebhookNotification::DISPUTE_DISPUTED:
+ $subjectXml = self::_disputeDisputedSampleXml($id);
+ break;
+ case WebhookNotification::DISPUTE_EXPIRED:
+ $subjectXml = self::_disputeExpiredSampleXml($id);
+ break;
+ case WebhookNotification::SUBSCRIPTION_CHARGED_SUCCESSFULLY:
+ $subjectXml = self::_subscriptionChargedSuccessfullySampleXml($id);
+ break;
+ case WebhookNotification::SUBSCRIPTION_CHARGED_UNSUCCESSFULLY:
+ $subjectXml = self::_subscriptionChargedUnsuccessfullySampleXml($id);
+ break;
+ case WebhookNotification::SUBSCRIPTION_EXPIRED:
+ $subjectXml = self::_subscriptionExpiredSampleXml($id);
+ break;
+ case WebhookNotification::SUBSCRIPTION_CANCELED:
+ $subjectXml = self::_subscriptionCanceledSampleXml($id);
+ break;
+ case WebhookNotification::SUBSCRIPTION_WENT_PAST_DUE:
+ $subjectXml = self::_subscriptionWentPastDueSampleXml($id);
+ break;
+ case WebhookNotification::CHECK:
+ $subjectXml = self::_checkSampleXml();
+ break;
+ case WebhookNotification::ACCOUNT_UPDATER_DAILY_REPORT:
+ $subjectXml = self::_accountUpdaterDailyReportSampleXml($id);
+ break;
+ case WebhookNotification::GRANTOR_UPDATED_GRANTED_PAYMENT_METHOD:
+ $subjectXml = self::_grantedPaymentInstrumentUpdateSampleXml();
+ break;
+ case WebhookNotification::RECIPIENT_UPDATED_GRANTED_PAYMENT_METHOD:
+ $subjectXml = self::_grantedPaymentInstrumentUpdateSampleXml();
+ break;
+ case WebhookNotification::GRANTED_PAYMENT_METHOD_REVOKED:
+ $subjectXml = self::_grantedPaymentMethodRevokedXml($id);
+ break;
+ case WebhookNotification::PAYMENT_METHOD_REVOKED_BY_CUSTOMER:
+ $subjectXml = self::_paymentMethodRevokedByCustomerSampleXml($id);
+ break;
+ case WebhookNotification::LOCAL_PAYMENT_COMPLETED:
+ $subjectXml = self::_localPaymentCompletedSampleXml();
+ break;
+ case WebhookNotification::LOCAL_PAYMENT_REVERSED:
+ $subjectXml = self::_localPaymentReversedSampleXml();
+ 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 _disputeAcceptedSampleXml($id)
+ {
+ return "
+
+ 250.00
+ 250.0
+ 245.00
+ USD
+ 2014-03-01
+ 2014-03-21
+ chargeback
+ accepted
+ fraud
+ ${id}
+
+ ${id}
+ 250.00
+
+ 2014-03-21
+
+ ";
+ }
+
+ private static function _disputeDisputedSampleXml($id)
+ {
+ return "
+
+ 250.00
+ 250.0
+ 245.00
+ USD
+ 2014-03-01
+ 2014-03-21
+ chargeback
+ disputed
+ fraud
+ ${id}
+
+ ${id}
+ 250.00
+
+ 2014-03-21
+
+ ";
+ }
+
+ private static function _disputeExpiredSampleXml($id)
+ {
+ return "
+
+ 250.00
+ 250.0
+ 245.00
+ USD
+ 2014-03-01
+ 2014-03-21
+ chargeback
+ expired
+ fraud
+ ${id}
+
+ ${id}
+ 250.00
+
+ 2014-03-21
+
+ ";
+ }
+
+ private static function _subscriptionSampleXml($id)
+ {
+ return "
+
+ {$id}
+ Active
+
+
+
+
+
+
+
+ ";
+ }
+
+ private static function _subscriptionChargedSuccessfullySampleXml($id)
+ {
+ return "
+
+ {$id}
+ Active
+ 2016-03-21
+ 2017-03-31
+
+
+ {$id}
+ submitted_for_settlement
+ 49.99
+
+
+
+
+
+
+
+ ";
+ }
+
+ private static function _subscriptionChargedUnsuccessfullySampleXml($id)
+ {
+ return "
+
+ {$id}
+ Active
+ 2016-03-21
+ 2017-03-31
+
+
+ {$id}
+ failed
+ 49.99
+
+
+
+
+
+
+
+ ";
+ }
+
+ private static function _subscriptionExpiredSampleXml($id)
+ {
+ return "
+
+ {$id}
+ Expired
+
+
+
+
+
+
+
+ ";
+ }
+
+ private static function _subscriptionCanceledSampleXml($id)
+ {
+ return "
+
+ {$id}
+ Canceled
+
+
+
+
+
+
+
+ ";
+ }
+
+ private static function _subscriptionWentPastDueSampleXml($id)
+ {
+ return "
+
+ {$id}
+ Past Due
+
+
+
+
+
+
+
+ ";
+ }
+
+ 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 _grantedPaymentInstrumentUpdateSampleXml()
+ {
+ return "
+
+ vczo7jqrpwrsi2px
+ cf0i8wgarszuy6hc
+
+ ee257d98-de40-47e8-96b3-a6954ea7a9a4
+ false
+ false
+
+ abc123z
+
+ - expiration-month
+ - expiration-year
+
+
+ ";
+ }
+
+ private static function _grantedPaymentMethodRevokedXml($id)
+ {
+ return "
+
+ 2018-10-11T21:28:37Z
+ 2018-10-11T21:28:37Z
+ true
+ https://assets.braintreegateway.com/payment_method_logo/venmo.png?environment=test
+ {$id}
+ Venmo Account: venmojoe
+ venmojoe
+ 456
+
+ venmo_customer_id
+ cGF5bWVudG1ldGhvZF92ZW5tb2FjY291bnQ
+
+ ";
+ }
+
+ private static function _paymentMethodRevokedByCustomerSampleXml($id)
+ {
+ return "
+
+ a-billing-agreement-id
+ 2019-01-01T12:00:00Z
+ a-customer-id
+ true
+ name@email.com
+ cGF5bWVudG1ldGhvZF9jaDZieXNz
+ https://assets.braintreegateway.com/payment_method_logo/paypal.png?environment=test
+
+ {$id}
+ 2019-01-02T12:00:00Z
+
+ a-payer-id
+
+
+ 2019-01-02T12:00:00Z
+
+ ";
+ }
+
+ private static function _localPaymentCompletedSampleXml()
+ {
+ return "
+
+ a-payment-id
+ a-payer-id
+ ee257d98-de40-47e8-96b3-a6954ea7a9a4
+
+ 1
+ authorizing
+ 10.00
+ order1234
+
+
+ ";
+ }
+
+ private static function _localPaymentReversedSampleXml()
+ {
+ return "
+
+ a-payment-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;
+ }
+}
diff --git a/lib/Braintree/Xml.php b/lib/Braintree/Xml.php
index a6e5119..ee04924 100644
--- a/lib/Braintree/Xml.php
+++ b/lib/Braintree/Xml.php
@@ -1,24 +1,18 @@
openMemory();
@@ -33,7 +34,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 +53,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)
{
@@ -62,23 +63,20 @@ private static function _createElementsFromArray(&$writer, $aData)
} else {
$writer->text($aData);
}
- return;
+ 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)) {
if (array_key_exists(0, $element) || empty($element)) {
$writer->writeAttribute('type', 'array');
- foreach ($element AS $ignored => $itemInArray) {
+ foreach ($element as $ignored => $itemInArray) {
$writer->startElement('item');
self::_createElementsFromArray($writer, $itemInArray);
$writer->endElement();
}
- }
- else {
+ } else {
self::_createElementsFromArray($writer, $element);
}
} else {
@@ -103,39 +101,45 @@ private static function _createElementsFromArray(&$writer, $aData)
*/
private static function _generateXmlAttribute($value)
{
- if ($value instanceof DateTime) {
- return array('type', 'datetime', self::_dateTimeToXmlTimestamp($value));
+ if ($value instanceof DateTime || is_a($value, 'DateTimeImmutable')) {
+ return ['type', 'datetime', self::_convertDateTimeObjectToXmlTimestamp($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);
+ if ($value === null) {
+ 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)
+ private static function _convertDateTimeObjectToXmlTimestamp($dateTime)
{
- $dateTime->setTimeZone(new DateTimeZone('UTC'));
- return ($dateTime->format('Y-m-d\TH:i:s') . 'Z');
+ if (is_a($dateTime, 'DateTimeImmutable')) {
+ $dateTimeForUTC = DateTime::createFromImmutable($dateTime);
+ } else {
+ $dateTimeForUTC = clone $dateTime;
+ }
+
+ $dateTimeForUTC->setTimeZone(new DateTimeZone('UTC'));
+ return ($dateTimeForUTC->format('Y-m-d\TH:i:s') . 'Z');
}
private static function _castDateTime($string)
{
try {
if (empty($string)) {
- return false;
+ return false;
}
$dateTime = new DateTime($string);
- return self::_dateTimeToXmlTimestamp($dateTime);
+ return self::_convertDateTimeObjectToXmlTimestamp($dateTime);
} catch (Exception $e) {
// not a datetime
return false;
diff --git a/lib/Braintree/Xml/Parser.php b/lib/Braintree/Xml/Parser.php
index e4fea27..0614c79 100644
--- a/lib/Braintree/Xml/Parser.php
+++ b/lib/Braintree/Xml/Parser.php
@@ -1,177 +1,138 @@
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;
+ }
+ }
+ 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);
+ }
+ }
+ return $collection;
+ default:
+ $values = [];
+ if ($node->childNodes->length === 1 && $node->childNodes->item(0) instanceof DOMText) {
+ return $node->childNodes->item(0)->nodeValue;
+ } else {
+ foreach ($node->childNodes as $child) {
+ if (!$child instanceof DOMText) {
+ $values[$child->nodeName] = self::_nodeToValue($child);
+ }
+ }
+ return $values;
}
- } 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;
- }
-
- // if the element was an array type, output to a numbered key
- // otherwise, use the element name
- if ($attributeType == 'array') {
- $xmlArray[] = $output;
- } else {
- $xmlArray[$key] = $output;
- }
}
-
- 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;
+ $type = null;
+ if ($node instanceof DOMElement) {
+ $type = $node->getAttribute('type');
}
- // switch on the type attribute
- // switch works even if $attribs->type isn't set
- switch ($attribs->type) {
+
+ switch ($type) {
case 'datetime':
- return self::_timestampToUTC((string) $valueObj);
- break;
+ return self::_timestampToUTC((string) $node->nodeValue);
case 'date':
- return new DateTime((string)$valueObj);
- break;
+ return new DateTime((string) $node->nodeValue);
case 'integer':
- return (int) $valueObj;
- break;
+ return (int) $node->nodeValue;
case 'boolean':
- $value = (string) $valueObj;
- // look for a number inside the string
- if(is_numeric($value)) {
+ $value = (string) $node->nodeValue;
+ if (is_numeric($value)) {
return (bool) $value;
} else {
- // look for the string "true", return false in all other cases
- return ($value != "true") ? FALSE : TRUE;
+ return ($value !== "true") ? false : true;
}
- break;
case 'array':
- return array();
+ case 'collection':
+ return self::_nodeToArray($node);
default:
- return (string) $valueObj;
+ 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;
diff --git a/lib/angelleye-gravity-forms-payment-logger.php b/lib/angelleye-gravity-forms-payment-logger.php
new file mode 100644
index 0000000..8cd032a
--- /dev/null
+++ b/lib/angelleye-gravity-forms-payment-logger.php
@@ -0,0 +1,113 @@
+api_url = 'https://gtctgyk7fh.execute-api.us-east-2.amazonaws.com/default/PayPalPaymentsTracker';
+ $this->api_key = 'srGiuJFpDO4W7YCDXF56g2c9nT1JhlURVGqYD7oa';
+ $this->allow_method = array('Braintree');
+ add_action('angelleye_gravity_forms_response_data', array($this, 'own_angelleye_gravity_forms_response_data'), 10, 6);
+ }
+
+ public function own_angelleye_gravity_forms_response_data($result_data, $request_data, $product_id = 1, $sandbox = false, $is_nvp = true, $payment_method = 'express_checkout') {
+ $request_param = array();
+ if (isset($result_data) && is_array($result_data) && !empty($result_data['CURL_ERROR'])) {
+ return $result_data;
+ } else {
+ $result = $result_data;
+ $request = $request_data;
+ if ($payment_method == 'braintree') {
+ $request['METHOD'] = 'Braintree';
+ }
+ if (isset($request['METHOD']) && !empty($request['METHOD']) && in_array($request['METHOD'], $this->allow_method)) {
+ $opt_in_log = get_option('angelleye_send_opt_in_logging_details', 'no');
+ $request_param['site_url'] = '';
+ if ($opt_in_log == 'yes') {
+ $request_param['site_url'] = get_bloginfo('url');
+ }
+ $request_param['type'] = $request['METHOD'];
+ $request_param['mode'] = ($sandbox) ? 'sandbox' : 'live';
+ $request_param['product_id'] = $product_id;
+ if ($request['METHOD'] == 'Braintree') {
+ if ($result->success) {
+ $request_param['status'] = 'Success';
+ } else {
+ $request_param['status'] = 'Failure';
+ }
+ if ($opt_in_log == 'yes') {
+ if (isset($result->transaction->statusHistory[0]->user) && !empty($result->transaction->statusHistory[0]->user)) {
+ $request_param['merchant_id'] = $result->transaction->statusHistory[0]->user;
+ }
+ }
+ $request_param['correlation_id'] = '';
+ $request_param['transaction_id'] = isset($result->transaction->id) ? $result->transaction->id : '';
+ $request_param['amount'] = isset($result->transaction->amount) ? $result->transaction->amount : '0.00';
+ $this->angelleye_tpv_request($request_param);
+ }
+ }
+ }
+ return $result_data;
+ }
+
+ public function angelleye_tpv_request($request_param) {
+ try {
+ $payment_type = $request_param['type'];
+ $amount = $request_param['amount'];
+ $status = $request_param['status'];
+ $site_url = $request_param['site_url'];
+ $payment_mode = $request_param['mode'];
+ $merchant_id = @$request_param['merchant_id'];
+ $correlation_id = $request_param['correlation_id'];
+ $transaction_id = $request_param['transaction_id'];
+ $product_id = $request_param['product_id'];
+ $params = [
+ "product_id" => $product_id,
+ "type" => $payment_type,
+ "amount" => $amount,
+ "status" => $status,
+ "site_url" => $site_url,
+ "mode" => $payment_mode,
+ "merchant_id" => $merchant_id,
+ "correlation_id" => $correlation_id,
+ "transaction_id" => $transaction_id
+ ];
+ $params = apply_filters('angelleye_log_params', $params);
+ $post_args = array(
+ 'headers' => array(
+ 'Content-Type' => 'application/json; charset=utf-8',
+ 'x-api-key' => $this->api_key
+ ),
+ 'body' => json_encode($params),
+ 'method' => 'POST',
+ 'data_format' => 'body',
+ );
+ $response = wp_remote_post($this->api_url, $post_args);
+ if (is_wp_error($response)) {
+ $error_message = $response->get_error_message();
+ error_log(print_r($error_message, true));
+ return false;
+ } else {
+ $body = json_decode(wp_remote_retrieve_body($response), true);
+ if ($body['status']) {
+ return true;
+ }
+ }
+ return false;
+ } catch (Exception $ex) {
+
+ }
+ }
+
+}
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 @@
+", $item['id'] );
+
+ return $img;
+
+ }
+
+}
+
+?>
diff --git a/lib/class.plugify-gform-braintree.php b/lib/class.plugify-gform-braintree.php
index 5128597..24a3a15 100644
--- a/lib/class.plugify-gform-braintree.php
+++ b/lib/class.plugify-gform-braintree.php
@@ -4,319 +4,1677 @@
final class Plugify_GForm_Braintree extends GFPaymentAddOn {
- protected $_version = '1.0';
-
- protected $_min_gravityforms_version = '1.8.7.16';
- protected $_slug = 'gravity-forms-braintree';
- protected $_path = 'gravity-forms-braintree/lib/class.plugify-gform-braintree.php';
- protected $_full_path = __FILE__;
- protected $_title = 'Braintree';
- protected $_short_title = 'Braintree';
- protected $_requires_credit_card = true;
- protected $_supports_callbacks = false;
- protected $_enable_rg_autoupgrade = true;
-
- /**
- * Class constructor. Send __construct call to parent
- * @since 1.0
- * @return void
- */
- public function __construct () {
-
- // Build parent
- parent::__construct();
+ protected $_version = '5.1.1';
+ protected $_min_gravityforms_version = '1.8.7.16';
+ protected $_slug = 'gravity-forms-braintree';
+ protected $_path = 'gravity-forms-braintree/lib/class.plugify-gform-braintree.php';
+ protected $_full_path = __FILE__;
+ protected $_title = 'Braintree';
+ protected $_short_title = 'Braintree';
+ protected $_requires_credit_card = true;
+ protected $_supports_callbacks = true;
+ protected $_enable_rg_autoupgrade = true;
+ protected $is_payment_gateway = true;
+ protected $current_feed = true;
+ protected $selected_payment_method = 'braintree_credit_card';
- }
+ /**
+ * Class constructor. Send __construct call to parent
+ * @since 1.0
+ * @return void
+ */
+ public function __construct() {
- /**
- * Override init_frontend to assign front end based filters and actions required for operation
- *
- * @since 1.0
- * @return void
- */
- public function init_frontend () {
+ add_action('wp_ajax_angelleye_gform_braintree_adismiss_notice', array($this, 'angelleye_gform_braintree_adismiss_notice'), 10);
+ add_action('admin_notices', array($this, 'angelleye_gform_braintree_display_push_notification'), 10);
- // init_frontend on GFPaymentAddOn
- parent::init_frontend();
+ add_action('admin_enqueue_scripts', array($this, 'enqueue_styles_css'), 10);
+ add_action('admin_enqueue_scripts', array($this, 'enqueue_scripts_js'), 10);
+ add_filter('gform_noconflict_scripts', [$this, 'include_angelleye_braintree_script_noconflict']);
+ add_filter('gform_noconflict_styles', [$this, 'include_angelleye_braintree_style_noconflict']);
+ add_filter('angelleye_braintree_parameter', [$this,'manage_braintree_request_parameter'], 10, 4);
- }
+ // Build parent
+ parent::__construct();
+ }
- /**
- * 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
- $authorization = array(
- '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' ) ),
- 'transaction_id' => '',
- 'captured_payment' => array(
- 'is_success' => false,
- 'error_message' => '',
- 'transaction_id' => '',
- 'amount' => $submission_data['payment_amount']
- )
- );
-
-
- // Perform capture in this function. For this version, we won't authorize and then capture later
- // at least, not in this version
- 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
- $args = array(
- 'amount' => $submission_data['payment_amount'],
- 'creditCard' => array(
- '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']
- ),
- 'customer' => array(
- 'firstName' => $submission_data['card_name']
- ),
- 'billing' => array(
- 'firstName' => $submission_data['card_name'],
- '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'] );
-
- // Set to auto settlemt if applicable
- if( $settings['settlement'] == 'Yes' ) {
- $args['options']['submitForSettlement'] = 'true';
- }
-
- // Send transaction to Braintree
- $result = Braintree_Transaction::sale( $args );
-
- // 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['captured_payment'] = array(
- 'is_success' => true,
- 'transaction_id' => $result->transaction->_attributes['id'],
- 'amount' => $result->transaction->_attributes['amount'],
- 'error_message' => '',
- 'payment_method' => 'Credit Card'
- );
-
- }
- else {
-
- // 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'] );
- }
-
- }
+ /**
+ * Override credit card field check, so that we can return true when someone has ach form
+ * If any user will have any form then same payment gateway class will be used
+ * @param array $form
+ *
+ * @return bool
+ */
+ public function has_credit_card_field($form) {
+ if (isset($form['fields'])) {
+ foreach ($form['fields'] as $single_field) {
+ if ($single_field->type == 'creditcard' || $single_field->type == 'braintree_ach' || $single_field->type == 'braintree_credit_card') {
+ return true;
+ }
+ }
+ }
+ return $this->get_credit_card_field($form) !== false;
+ }
- }
- catch( Exception $e ) {
- // Do nothing with exception object, just fallback to generic failure
- }
+ /**
+ * Override default message for Gravity Form Braintree Feeds
+ * @return string
+ */
+ public function requires_credit_card_message() {
+ $url = add_query_arg(array('view' => null, 'subview' => null));
- return $authorization;
+ return sprintf(esc_html__("You must add a Credit Card/ACH Payment field to your form before creating a feed. Let's go %sadd one%s!", 'gravityforms'), "", '');
+ }
- }
+ /**
+ * Override init_frontend to assign front end based filters and actions required for operation
+ *
+ * @since 1.0
+ * @return void
+ */
+ public function init_frontend() {
- return false;
+ // init_frontend on GFPaymentAddOn
+ parent::init_frontend();
+ }
- }
+ /**
+ * Init the Braintree configuration and return gateway for transactions, etc.
+ * @return bool|\Braintree\Gateway
+ * @throws \Braintree\Exception\Configuration
+ */
+ public function getBraintreeGateway() {
+ $settings = $this->get_plugin_settings();
+ if (!$settings)
+ return false;
- /**
- * Create and display feed settings fields.
- *
- * @since 1.0
- * @return void
- */
- public function feed_settings_fields () {
+ // Configure Braintree environment
+ $braintree_config = new \Braintree\Configuration([
+ 'environment' => strtolower($settings['environment']),
+ 'merchantId' => $settings['merchant-id'],
+ 'publicKey' => $settings['public-key'],
+ 'privateKey' => $settings['private-key']
+ ]);
- // Get defaults from GFPaymentAddOn
- $settings = parent::feed_settings_fields();
+ $braintree_config->timeout(60);
- // Remove billing information
- //$settings = $this->remove_field( 'billingInformation', $settings );
+ $gateway = new Braintree\Gateway($braintree_config);
+ return $gateway;
+ }
- // Remove options
- $settings = $this->remove_field( 'options', $settings );
+ /**
+ * ACH Payment authorization
+ * @param $feed
+ * @param $submission_data
+ * @param $form
+ * @param $entry
+ *
+ * @return array|bool
+ * @throws \Braintree\Exception\Configuration
+ */
+ public function ach_authorize($feed, $submission_data, $form, $entry) {
+ $this->log_debug("Braintree_ACH_Authorize::START");
+ $gateway = $this->getBraintreeGateway();
+ if ($gateway) {
+ try {
+ // Prepare authorization response payload
+ $authorization = array(
+ 'is_authorized' => false,
+ 'error_message' => apply_filters('gform_braintree_credit_card_failure_message', __('We are unable to authorize the bank account, Please try again.', 'gravity-forms-braintree')),
+ 'transaction_id' => '',
+ 'captured_payment' => array(
+ 'is_success' => false,
+ 'error_message' => '',
+ 'transaction_id' => '',
+ 'amount' => $submission_data['payment_amount']
+ )
+ );
- // Remove the subscription option from transaction type dropdown
- $transaction_type = $this->get_field( 'transactionType', $settings );
+ $ach_device_corelation = rgpost('ach_device_corelation');
+ $ach_token = rgpost('ach_token');
+ $payment_amount = number_format($submission_data['payment_amount'], 2, '.', '');
- foreach( $transaction_type['choices'] as $index => $choice ) {
- if( $choice['value'] == 'subscription' ) {
- unset( $transaction_type['choices'][$index] );
- }
- }
+ $settings = $this->get_plugin_settings();
+ $response = getAngelleyeBraintreePaymentFields($form);
+ $braintree_ach_field = $response['braintree_ach'];
+ /* $account_number = rgpost( 'input_' . $braintree_ach_field->id . '_1' );
+ $account_type = rgpost( 'input_' . $braintree_ach_field->id . '_2' );
+ $routing_number = rgpost( 'input_' . $braintree_ach_field->id . '_3' ); */
+ $account_holder_name = rgpost('input_' . $braintree_ach_field->id . '_4');
- $settings = $this->replace_field( 'transactionType', $transaction_type, $settings );
+ $account_holder_name = explode(' ', $account_holder_name);
+ /**
+ * Create customer in Braintree
+ */
+ $customer_request = [
+ 'firstName' => @$account_holder_name[0],
+ 'lastName' => end($account_holder_name),
+ ];
+ $this->log_debug("Braintree_ACH_Customer::create REQUEST => " . print_r($customer_request, 1));
+ $customer_result = $gateway->customer()->create($customer_request);
+ $this->log_debug("Braintree_ACH_Customer::create RESPONSE => " . print_r($customer_result, 1));
- // Return sanitized settings
- return $settings;
+ if ($customer_result->success) {
+ $payment_method_request = [
+ 'customerId' => $customer_result->customer->id,
+ 'paymentMethodNonce' => $ach_token,
+ 'options' => [
+ 'usBankAccountVerificationMethod' => Braintree\Result\UsBankAccountVerification::NETWORK_CHECK
+ ]
+ ];
- }
+ $this->log_debug("Braintree_ACH_PaymentRequest::create REQUEST => " . print_r($payment_method_request, 1));
+ $payment_method_response = $gateway->paymentMethod()->create($payment_method_request);
+ $this->log_debug("Braintree_ACH_PaymentRequest::create RESPONSE => " . print_r($payment_method_response, 1));
- /**
- * Create and display plugin settings fields. These are settings for Braintree in particular, not a feed
- *
- * @since 1.0
- * @return void
- */
- 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(
- 'name' => 'public-key',
- 'tooltip' => 'Your Braintree Account Public Key',
- 'label' => 'Public Key',
- 'type' => 'text',
- 'class' => 'medium'
- ),
- array(
- 'name' => 'private-key',
- 'tooltip' => 'Your Braintree Account Private Key',
- 'label' => 'Private Key',
- 'type' => 'text',
- 'class' => 'medium'
- )
- )
- ),
- array(
- 'title' => 'Payment Settings',
- 'fields' => array(
- array(
- 'name' => 'settlement',
- 'tooltip' => 'Choosing \'Yes\' will tell Braintree to automatically submit your transactions for settlement upon receipt',
- 'label' => 'Automatic Settlement Submission',
- 'type' => 'radio',
- 'choices' => array(
- array(
- 'label' => 'Yes',
- 'name' => 'yes'
- ),
- array(
- 'label' => 'No',
- 'name' => 'no'
- )
- )
- )
- )
- ),
- array(
- 'title' => 'Environment Settings',
- 'fields' => array(
- array(
- 'name' => 'environment',
- 'tooltip' => 'Do you want to process test payments or real payments?',
- 'label' => 'API Endpoint',
- 'type' => 'radio',
- 'choices' => array(
- array(
- 'label' => 'Sandbox',
- 'name' => 'sandbox'
- ),
- array(
- 'label' => 'Production',
- 'name' => 'production'
- )
- )
- )
- )
- )
-
- );
+ if (isset($payment_method_response->paymentMethod->token)) {
- }
+ $sale_request = [
+ 'amount' => $payment_amount,
+ 'paymentMethodToken' => $payment_method_response->paymentMethod->token,
+ 'deviceData' => $ach_device_corelation,
+ 'options' => [
+ 'submitForSettlement' => true
+ ]
+ ];
- /**
- * 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
- */
- public function settings_are_valid ( $settings ) {
-
- if( empty( $settings ) ) {
- return false;
- }
+ $sale_request = apply_filters('angelleye_braintree_parameter', $sale_request, $submission_data, $form, $entry);
- foreach( $settings as $setting ) {
- if( '' == $setting ) {
- return false;
- }
- }
+ $this->log_debug("Braintree_ACH_Transaction::sale REQUEST => " . print_r($sale_request, 1));
+ $sale_response = $gateway->transaction()->sale($sale_request);
+ $this->log_debug("Braintree_ACH_Transaction::sale RESPONSE => " . print_r($sale_response, 1));
- return true;
+ do_action('angelleye_braintree_transaction_response', $sale_response, $submission_data, $form, $entry );
- }
+ if ($sale_response->success) {
+ do_action('angelleye_gravity_forms_response_data', $sale_response, $submission_data, '16', ( strtolower($settings['environment']) == 'sandbox' ) ? true : false, false, 'braintree_ach');
+ $authorization['is_authorized'] = true;
+ $authorization['error_message'] = '';
+ $authorization['transaction_id'] = $sale_response->transaction->id;
- /**
- * Get plugin settings
- *
- * @since 1.0
- * @return void
- */
- public function get_plugin_settings () {
+ $authorization['captured_payment'] = array(
+ 'is_success' => true,
+ 'transaction_id' => $sale_response->transaction->id,
+ 'amount' => $sale_response->transaction->amount,
+ 'error_message' => '',
+ 'payment_method' => 'Braintree ACH'
+ );
- $settings = parent::get_plugin_settings();
+ $this->log_debug("Braintree_ACH::SUCCESS");
+ } else {
- if( $this->settings_are_valid( $settings ) ) {
- return $settings;
- }
- else {
- return false;
+ $processorResponseText = !empty( $sale_response->message ) ? $sale_response->message : '';
+ if( !empty( $sale_response->transaction->processorResponseText ) && strtolower( $sale_response->transaction->processorResponseText ) !== 'unavailable' ) {
+ $processorResponseText = $sale_response->transaction->processorResponseText;
+ }
+
+ if ( !empty( $processorResponseText ) ) {
+ $authorization['error_message'] = sprintf('Your bank did not authorized the transaction: %s.', $processorResponseText);
+ } else {
+ $authorization['error_message'] = sprintf('Your bank declined the transaction, please try again or contact bank.');
+ }
+ $this->log_debug("Braintree_ACH::FAILED_ERROR");
+ }
+ } else {
+ $authorization['error_message'] = __('We are unable to authorize bank account, This may have happened due to expired token, please try again.', 'gravity-forms-braintree');
+ }
+ } else {
+ $authorization['error_message'] = __('Unable to proceed with the transaction due to invalid name.', 'gravity-forms-braintree');
+ }
+ } catch (Exception $exception) {
+ $this->log_debug("Braintree_ACH::EXCEPTION: " . $exception->getTraceAsString());
+ $exception['error_message'] = __('An internal error occurred, Please try later. ERROR: ' . $exception->getMessage());
+ }
+ return $authorization;
+ }
+
+ $this->log_debug("Braintree_ACH::FAILED");
+ return false;
+ }
+
+ /**
+ * Gets the payment validation result.
+ *
+ * @since Unknown
+ * @access public
+ *
+ * @used-by GFPaymentAddOn::validation()
+ *
+ * @param array $validation_result Contains the form validation results.
+ * @param array $authorization_result Contains the form authorization results.
+ *
+ * @return array The validation result for the credit card field.
+ */
+ public function get_validation_result($validation_result, $authorization_result) {
+
+ $credit_card_page = 0;
+ if ($this->selected_payment_method == 'braintree_ach') {
+ foreach ($validation_result['form']['fields'] as &$field) {
+ if ($field->type == 'braintree_ach') {
+ $field->failed_validation = true;
+ $field->validation_message = $authorization_result['error_message'];
+ $credit_card_page = $field->pageNumber;
+ break;
+ }
+ }
+ } elseif ( $this->selected_payment_method === 'braintree_credit_card') {
+ foreach ($validation_result['form']['fields'] as &$field) {
+ if ($field->type == 'braintree_credit_card') {
+ $field->failed_validation = true;
+ $field->validation_message = $authorization_result['error_message'];
+ $credit_card_page = $field->pageNumber;
+ break;
+ }
+ }
+ } else {
+ foreach ($validation_result['form']['fields'] as &$field) {
+ if ($field->type == 'creditcard') {
+ $field->failed_validation = true;
+ $field->validation_message = $authorization_result['error_message'];
+ $credit_card_page = $field->pageNumber;
+ break;
+ }
+ }
+ }
+ $validation_result['credit_card_page'] = $credit_card_page;
+ $validation_result['is_valid'] = false;
+
+ return $validation_result;
+ }
+
+ /**
+ * Braintree credit card Payment authorization
+ * @param $feed
+ * @param $submission_data
+ * @param $form
+ * @param $entry
+ *
+ * @return array|bool
+ * @throws \Braintree\Exception\Configuration
+ */
+ public function braintree_cc_authorize($feed, $submission_data, $form, $entry) {
+ try {
+ $settings = $this->get_plugin_settings();
+ $gateway = $this->getBraintreeGateway();
+ if ($gateway) {
+ $authorization = array(
+ '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')),
+ 'transaction_id' => '',
+ 'captured_payment' => array(
+ 'is_success' => false,
+ 'error_message' => '',
+ 'transaction_id' => '',
+ 'amount' => $submission_data['payment_amount']
+ )
+ );
+ if (empty($_POST['payment_method_nonce'])) {
+ return $authorization;
+ }
+ $args = array(
+ 'amount' => $submission_data['payment_amount'],
+ 'paymentMethodNonce' => $_POST['payment_method_nonce']
+ );
+
+ if ($settings['settlement'] == 'Yes') {
+ $args['options']['submitForSettlement'] = 'true';
+ }
+
+ $args = apply_filters('angelleye_braintree_parameter', $args, $submission_data, $form, $entry);
+
+ $this->log_debug("Braintree_CC_Transaction::sale REQUEST => " . print_r($args, 1));
+
+ $result = $gateway->transaction()->sale($args);
+
+ $this->log_debug("Braintree_CC_Transaction::sale RESPONSE => " . print_r($result, 1));
+
+ do_action('angelleye_braintree_transaction_response', $result, $submission_data, $form, $entry );
+
+ if ($result->success) {
+ do_action('angelleye_gravity_forms_response_data', $result, $submission_data, '16', (strtolower($settings['environment']) == 'sandbox') ? true : false, false, 'braintree');
+ $authorization['is_authorized'] = true;
+ $authorization['error_message'] = '';
+ $authorization['transaction_id'] = $result->transaction->id;
+ $authorization['captured_payment'] = array(
+ 'is_success' => true,
+ 'transaction_id' => $result->transaction->id,
+ 'amount' => $result->transaction->amount,
+ 'error_message' => '',
+ 'payment_method' => 'Credit Card'
+ );
+ } else {
+
+ $processorResponseText = !empty( $result->message ) ? $result->message : '';
+ if( !empty( $result->transaction->processorResponseText ) && strtolower( $result->transaction->processorResponseText ) !== 'unavailable' ) {
+ $processorResponseText = $result->transaction->processorResponseText;
+ }
+
+ if ( !empty( $processorResponseText ) ) {
+ $authorization['error_message'] .= sprintf('. Your bank said: %s.', $processorResponseText);
+ }
+ }
+ }
+ } catch (Exception $e) {
+ // Do nothing with exception object, just fallback to generic failure
+ }
+ return $authorization;
+ }
+
+ /**
+ * 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) {
+ $this->selected_payment_method = getAngelleyeBraintreePaymentMethod($form);
+ if ($this->selected_payment_method == 'braintree_ach') {
+ return $this->ach_authorize($feed, $submission_data, $form, $entry);
+ }
+ if ($this->selected_payment_method == 'braintree_credit_card') {
+ return $this->braintree_cc_authorize($feed, $submission_data, $form, $entry);
+ }
+ // Prepare authorization response payload
+ $authorization = array(
+ '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')),
+ 'transaction_id' => '',
+ 'captured_payment' => array(
+ 'is_success' => false,
+ 'error_message' => '',
+ 'transaction_id' => '',
+ 'amount' => $submission_data['payment_amount']
+ )
+ );
+ // Perform capture in this function. For this version, we won't authorize and then capture later
+ // at least, not in this version
+ 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
+ $args = array(
+ 'amount' => $submission_data['payment_amount'],
+ 'creditCard' => array(
+ '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']
+ )
+ );
+
+ try {
+ $gateway = $this->getBraintreeGateway();
+ if ($gateway) {
+ // Set to auto settlemt if applicable
+ if ($settings['settlement'] == 'Yes') {
+ $args['options']['submitForSettlement'] = 'true';
+ }
+
+ $args = apply_filters('angelleye_braintree_parameter', $args, $submission_data, $form, $entry);
+
+ $this->log_debug("Braintree_Transaction::sale REQUEST => " . print_r($args, 1));
+
+ // Send transaction to Braintree
+ $result = $gateway->transaction()->sale($args);
+
+ $this->log_debug("Braintree_Transaction::sale RESPONSE => " . print_r($result, 1));
+
+ do_action('angelleye_braintree_transaction_response', $result, $submission_data, $form, $entry );
+
+ // Update response to reflect successful payment
+ if ($result->success) {
+ do_action('angelleye_gravity_forms_response_data', $result, $submission_data, '16', (strtolower($settings['environment']) == 'sandbox') ? true : false, false, 'braintree');
+ $authorization['is_authorized'] = true;
+ $authorization['error_message'] = '';
+ $authorization['transaction_id'] = $result->transaction->id;
+ $authorization['captured_payment'] = array(
+ 'is_success' => true,
+ 'transaction_id' => $result->transaction->id,
+ 'amount' => $result->transaction->amount,
+ 'error_message' => '',
+ 'payment_method' => 'Credit Card'
+ );
+ } else {
+ // 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
+
+ $processorResponseText = !empty( $result->message ) ? $result->message : '';
+ if( !empty( $result->transaction->processorResponseText ) && strtolower( $result->transaction->processorResponseText ) !== 'unavailable' ) {
+ $processorResponseText = $result->transaction->processorResponseText;
+ }
+
+ if ( !empty( $processorResponseText ) ) {
+ $authorization['error_message'] .= sprintf('. Your bank said: %s.', $processorResponseText);
+ }
+ }
+ }
+ } catch (Exception $e) {
+ // Do nothing with exception object, just fallback to generic failure
+ }
+ return $authorization;
+ }
+
+ return false;
+ }
+
+ public function process_capture($authorization, $feed, $submission_data, $form, $entry) {
+
+ do_action('gform_braintree_post_capture', rgar($authorization, 'is_authorized'), rgars($authorization, 'captured_payment/amount'), $entry, $form, $this->_args_for_deprecated_hooks['config'], $this->_args_for_deprecated_hooks['aim_response']);
+
+ return parent::process_capture($authorization, $feed, $submission_data, $form, $entry);
+ }
+
+ /**
+ * Braintree 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 array $feed Current configured payment feed.
+ * @param array $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 array $form Current form array containing all form settings.
+ * @param array $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 $subscription array
+ * @type bool $is_success If the subscription is successful.
+ * @type string $error_message The error message, if applicable.
+ * @type string $subscription_id The subscription ID.
+ * @type int $amount The subscription amount.
+ * @type array $captured_payment {
+ * If payment is captured, an additional array is created.
+ * @type bool $is_success If the payment capture is successful.
+ * @type string $error_message The error message, if any.
+ * @type string $transaction_id The transaction ID of the captured payment.
+ * @type int $amount The amount of the captured payment, if successful.
+ * }
+ *
+ * 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' => XX
+ * ]
+ * }
+ * @throws \Braintree\Exception\Configuration
+ */
+ public function subscribe($feed, $submission_data, $form, $entry) {
+ $authorization = array(
+ 'is_authorized' => false,
+ 'is_success' => 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.', 'angelleye-gravity-forms-braintree')),
+ );
+ if (empty($_POST['payment_method_nonce'])) {
+ return $authorization;
+ }
+ $settings = $this->get_plugin_settings();
+ $gateway = $this->getBraintreeGateway();
+ if ($gateway) {
+ $args = array(
+ 'amount' => $submission_data['payment_amount'],
+ 'creditCard' => array(
+ 'number' => !empty($submission_data['card_number']) ? str_replace(array('-', ' '), '', $submission_data['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']
+ )
+ );
+ $args = apply_filters('angelleye_braintree_parameter', $args, $submission_data, $form, $entry);
+
+ $this->log_debug("Braintree_subscribe_Transaction::sale REQUEST => " . print_r($args, 1));
+
+ $customerArgs = !empty($args['customer']) ? $args['customer'] : array();
+ $customer_id = $this->get_customer_id($customerArgs);
+ $paymentMethod = $gateway->paymentMethod()->create([
+ 'customerId' => $customer_id,
+ 'paymentMethodNonce' => $_POST['payment_method_nonce']
+ ]);
+ $fee_amount = !empty($submission_data['setup_fee']) ? $submission_data['setup_fee'] : 0;
+ $setup_fee_result = true;
+ if (!empty($fee_amount) && $fee_amount > 0) {
+ $feeArgs = array(
+ 'amount' => $fee_amount,
+ 'paymentMethodToken' => $paymentMethod->paymentMethod->token,
+ );
+ if ($settings['settlement'] == 'Yes') {
+ $feeArgs['options']['submitForSettlement'] = 'true';
+ }
+ $feeArgs = apply_filters('angelleye_braintree_parameter', $feeArgs, $submission_data, $form, $entry);
+
+ $this->log_debug("Braintree_Feed_subscribe_Transaction::sale REQUEST => " . print_r($feeArgs, 1));
+
+ $feeResult = $gateway->transaction()->sale($feeArgs);
+
+ $this->log_debug("Braintree_Feed_subscribe_Transaction::sale RESPONSE => " . print_r($feeResult, 1));
+
+ do_action('angelleye_braintree_transaction_response', $feeResult, $submission_data, $form, $entry );
+
+ if ($feeResult->success) {
+ $authorization['captured_payment'] = array(
+ 'is_success' => true,
+ 'transaction_id' => $feeResult->transaction->id,
+ 'amount' => $feeResult->transaction->amount,
+ 'error_message' => '',
+ 'payment_method' => 'Credit Card'
+ );
+ } else {
+ $setup_fee_result = false;
+
+ $processorResponseText = !empty( $result->message ) ? $result->message : '';
+ if( !empty( $result->transaction->processorResponseText ) && strtolower( $result->transaction->processorResponseText ) !== 'unavailable' ) {
+ $processorResponseText = $result->transaction->processorResponseText;
+ }
+ if ( !empty( $processorResponseText ) ) {
+ $authorization['error_message'] .= sprintf('. Your bank said: %s.', $processorResponseText);
+ }
+ }
+ }
+ if ($setup_fee_result) {
+ try {
+ $subscriptionArgs = array(
+ 'paymentMethodToken' => $paymentMethod->paymentMethod->token,
+ 'planId' => !empty($feed['meta']['subscriptionPlan']) ? $feed['meta']['subscriptionPlan'] : '',
+ 'price' => $submission_data['payment_amount'],
+ );
+ if ($feed['meta']['recurringTimes'] == 0) {
+ $subscriptionArgs['neverExpires'] = true;
+ } else {
+ $subscriptionArgs['numberOfBillingCycles'] = $feed['meta']['recurringTimes'];
+ }
+ if (!empty($feed['meta']['trial_enabled'])) {
+ $subscriptionArgs['trialDuration'] = '';
+ $subscriptionArgs['trialDurationUnit'] = '';
+ $subscriptionArgs['trialPeriod'] = true;
+ } else {
+ $subscriptionArgs['firstBillingDate'] = '';
+ }
+ $subscriptionArgs = apply_filters('angelleye_gravity_braintree_subscription_args', $subscriptionArgs);
+ $subscription = $gateway->subscription()->create($subscriptionArgs);
+ if ($subscription->success) {
+ $authorization['is_authorized'] = true;
+ $authorization['is_success'] = true;
+ $authorization['error_message'] = '';
+ $authorization['paymentMethodToken'] = $subscription->subscription->paymentMethodToken;
+ $authorization['subscription_id'] = $subscription->subscription->id;
+ $authorization['amount'] = $subscription->subscription->price;
+ $authorization['subscription_trial_amount'] = $subscription->subscription->price;
+ $authorization['subscription_start_date'] = $subscription->subscription->firstBillingDate->date;
+ }
+ } catch (Exception $e) {
+
+ }
+ }
+ }
+ return $authorization;
+ }
+
+ /**
+ * Braintree override this method to add integration code to the payment processor in order to cancel a subscription.
+ *
+ * This method is executed when a subscription is canceled from the braintree Payment Gateway.
+ *
+ * @param array $entry Current entry array containing entry information (i.e data submitted by users).
+ * @param array $feed Current configured payment feed.
+ *
+ * @return bool Returns true if the subscription was cancelled successfully and false otherwise.
+ *
+ * @throws \Braintree\Exception\Configuration
+ */
+ public function cancel($entry, $feed) {
+ $gateway = $this->getBraintreeGateway();
+ if ($gateway) {
+ $result = $gateway->subscription()->cancel($entry['transaction_id']);
+ if ($result->success) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public function is_payment_gateway($entry_id) {
+
+ if ($this->is_payment_gateway) {
+ return true;
+ }
+
+ $gateway = gform_get_meta($entry_id, 'payment_gateway');
+
+ return in_array($gateway, array('Braintree', $this->_slug));
+ }
+
+ /**
+ * Create and display feed settings fields.
+ *
+ * @since 1.0
+ * @return void
+ */
+ 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] );
+ //}
+ //}
+
+ $transactionType = '';
+ foreach ($settings as $index => $setting) {
+ if (!empty($setting['dependency']['field']) && $setting['dependency']['field'] == 'transactionType') {
+ $transactionType = !empty($setting['dependency']['values'][0]) ? $setting['dependency']['values'][0] : '';
+ }
+ }
+
+ if ((!empty($_POST['_gaddon_setting_transactionType']) && $_POST['_gaddon_setting_transactionType'] == 'subscription') || ( empty($_POST['_gaddon_setting_transactionType']) && !empty($transactionType) && $transactionType == 'subscription')) {
+ $form_page_link = add_query_arg([
+ 'id' => !empty($_REQUEST['id']) ? $_REQUEST['id'] : '',
+ ], menu_page_url('gf_edit_forms', false));
+ $transaction_type['description'] = sprintf(__('When building your subscription form, make sure to use the %sBraintree CC%s field instead of the basic Credit Card field.', 'angelleye-gravity-forms-braintree'), '', '');
+ }
+
+ $settings = $this->replace_field('transactionType', $transaction_type, $settings);
+
+ $settings = parent::remove_field('trial', $settings);
+
+ $createBraintreePlanUrl = $this->merchant_url('plans/new');
+ $api_settings_field = array(
+ array(
+ 'name' => 'braintree_trial',
+ 'label' => esc_html__('Trial', 'angelleye-gravity-forms-braintree'),
+ 'type' => 'braintree_trial',
+ 'hidden' => '',
+ 'tooltip' => ''
+ ),
+ array(
+ 'name' => 'subscriptionPlan',
+ 'label' => esc_html__('Plan', 'angelleye-gravity-forms-braintree'),
+ 'type' => 'select',
+ 'choices' => $this->get_plans(),
+ 'required' => true,
+ 'tooltip' => sprintf(__('Plugin will fetch and display the subscription plans. Create the %splan%s in your Braintree account.', 'angelleye-gravity-forms-braintree'), '', ''),
+ ),
+ );
+
+ $settings = $this->add_field_after('setupFee', $api_settings_field, $settings);
+
+ if( !empty( $settings ) && is_array( $settings ) ) {
+
+ $temp_settings = [];
+
+ $extra_fee_settings = [
+ 'title' => esc_html__( 'Extra Fee Settings (%)', 'angelleye-gravity-forms-braintree' ),
+ 'fields' => [
+ [
+ 'name' => 'override_extra_fees',
+ 'type' => 'toggle',
+ 'label' => esc_html__( 'Override Global Settings', 'angelleye-gravity-forms-braintree' ),
+ 'default_value' => false,
+ 'tooltip' => '' . __( 'Override Global Settings', 'angelleye-gravity-forms-braintree' ) . '' . __( 'If you would like this specific form/feed to use different values than what are configured in the global Braintree settings, you may override them here.', 'angelleye-gravity-forms-braintree' ),
+ ],
+ [
+ 'name' => 'extra_fee_label',
+ 'type' => 'text',
+ 'label' => esc_html__( 'Extra Fee Label', 'angelleye-gravity-forms-braintree' ),
+ 'tooltip' => '',
+ 'required' => false,
+ 'default_value' => esc_html__( 'Convenience Fee', 'angelleye-gravity-forms-braintree'),
+ 'placeholder' => esc_html__( 'Convenience Fee', 'angelleye-gravity-forms-braintree'),
+ 'class' => 'extra-fees-input',
+ 'dependency' => [
+ 'live' => true,
+ 'fields' => [
+ [
+ 'field' => 'override_extra_fees',
+ ]
+ ]
+ ]
+ ],
+ [
+ 'name' => 'credit_card_fees',
+ 'type' => 'text',
+ 'input_type' => 'number',
+ 'label' => esc_html__( 'Credit Card', 'angelleye-gravity-forms-braintree' ),
+ 'tooltip' => '',
+ 'required' => false,
+ 'default_value' => '0.00',
+ 'placeholder' => '0.00',
+ 'min' => '0',
+ 'class' => 'extra-fees-input',
+ 'dependency' => [
+ 'live' => true,
+ 'fields' => [
+ [
+ 'field' => 'override_extra_fees',
+ ]
+ ],
+ ],
+ ],
+ [
+ 'name' => 'debit_card_fees',
+ 'type' => 'text',
+ 'input_type' => 'number',
+ 'label' => esc_html__( 'Debit Card', 'angelleye-gravity-forms-braintree' ),
+ 'tooltip' => '',
+ 'required' => false,
+ 'default_value' => '0.00',
+ 'placeholder' => '0.00',
+ 'min' => '0',
+ 'class' => 'extra-fees-input',
+ 'dependency' => [
+ 'live' => true,
+ 'fields' => [
+ [
+ 'field' => 'override_extra_fees',
+ ]
+ ],
+ ],
+ ],
+ [
+ 'name' => 'ach_fees',
+ 'type' => 'text',
+ 'input_type' => 'number',
+ 'label' => esc_html__( 'ACH', 'angelleye-gravity-forms-braintree' ),
+ 'tooltip' => '',
+ 'required' => false,
+ 'default_value' => '0.00',
+ 'placeholder' => '0.00',
+ 'min' => '0',
+ 'class' => 'extra-fees-input',
+ 'dependency' => [
+ 'live' => true,
+ 'fields' => [
+ [
+ 'field' => 'override_extra_fees',
+ ]
+ ],
+ ],
+ ],
+ [
+ 'name' => 'paypal_fees',
+ 'type' => 'text',
+ 'input_type' => 'number',
+ 'label' => esc_html__( 'PayPal', 'angelleye-gravity-forms-braintree' ),
+ 'tooltip' => '',
+ 'required' => false,
+ 'default_value' => '0.00',
+ 'placeholder' => '0.00',
+ 'min' => '0',
+ 'class' => 'extra-fees-input',
+ 'dependency' => [
+ 'live' => true,
+ 'fields' => [
+ [
+ 'field' => 'override_extra_fees',
+ ]
+ ],
+ ],
+ ],
+ [
+ 'name' => 'venmo_fees',
+ 'type' => 'text',
+ 'input_type' => 'number',
+ 'label' => esc_html__( 'Venmo', 'angelleye-gravity-forms-braintree' ),
+ 'tooltip' => '',
+ 'required' => false,
+ 'default_value' => '0.00',
+ 'placeholder' => '0.00',
+ 'min' => '0',
+ 'class' => 'extra-fees-input',
+ 'dependency' => [
+ 'live' => true,
+ 'fields' => [
+ [
+ 'field' => 'override_extra_fees',
+ ]
+ ],
+ ],
+ ],
+ /*[
+ 'name' => 'google_pay_fees',
+ 'type' => 'text',
+ 'input_type' => 'number',
+ 'label' => esc_html__( 'Google Pay', 'angelleye-gravity-forms-braintree' ),
+ 'tooltip' => '',
+ 'required' => false,
+ 'default_value' => '0.00',
+ 'placeholder' => '0.00',
+ 'min' => '0',
+ 'class' => 'extra-fees-input',
+ 'dependency' => [
+ 'live' => true,
+ 'fields' => [
+ [
+ 'field' => 'override_extra_fees',
+ ]
+ ],
+ ],
+ ],
+ [
+ 'name' => 'apple_pay_fees',
+ 'type' => 'text',
+ 'input_type' => 'number',
+ 'label' => esc_html__( 'Apple Pay', 'angelleye-gravity-forms-braintree' ),
+ 'tooltip' => '',
+ 'required' => false,
+ 'default_value' => '0.00',
+ 'placeholder' => '0.00',
+ 'min' => '0',
+ 'class' => 'extra-fees-input',
+ 'dependency' => [
+ 'live' => true,
+ 'fields' => [
+ [
+ 'field' => 'override_extra_fees',
+ ]
+ ],
+ ],
+ ],*/
+ [
+ 'name' => 'disable_extra_fees',
+ 'type' => 'checkbox',
+ 'label' => '',
+ 'tooltip' => '',
+ 'required' => false,
+ 'min' => 0,
+ 'choices' => [
+ [
+ 'name' => 'disable_extra_fees',
+ 'label' => esc_html__( 'Disable Extra Fee', 'angelleye-gravity-forms-braintree' ),
+ ]
+ ],
+ 'dependency' => [
+ 'live' => true,
+ 'fields' => [
+ [
+ 'field' => 'override_extra_fees',
+ ]
+ ],
+ ],
+ ],
+ ],
+ ];
+
+ $merchant_settings = [
+ 'title' => esc_html__( 'Merchant Account Settings', 'angelleye-gravity-forms-braintree' ),
+ 'fields' => [
+ [
+ 'name' => 'sub_merchant_account_id',
+ 'label' => esc_html__( 'Merchant Account ID', 'angelleye-gravity-forms-braintree' ),
+ 'type' => 'select',
+ 'choices' => $this->merchant_account_choices(),
+ 'required' => false,
+ 'default_value' => '',
+ 'tooltip' => esc_html__('By default the payment will be processed by your primary Braintree merchant account. If you have multiple merchant accounts configured, you can specify which one this form should pay to here.', 'angelleye-gravity-forms-braintree'),
+ ]
+ ]
+ ];
+
+ $payment_methods_settings = [
+ 'title' => esc_html__( 'Payment Method Settings', 'angelleye-gravity-forms-braintree' ),
+ 'fields' => [
+ [
+ 'name' => 'braintree_payment_methods',
+ 'label' => esc_html__( 'Payment Methods', 'angelleye-gravity-forms-braintree' ),
+ 'type' => 'checkbox',
+ 'choices' => $this->get_payment_method_choices(),
+ 'required' => false,
+ 'default_value' => '',
+ 'tooltip' => esc_html__('By default the Braintree Credit Card payment method in your form. Enable multiple payment methods.', 'angelleye-gravity-forms-braintree'),
+ ],
+ /*[
+ 'name' => 'google_pay_merchant_id',
+ 'type' => 'text',
+ 'label' => esc_html__( 'Google Pay Merchant ID', 'angelleye-gravity-forms-braintree' ),
+ 'tooltip' => '',
+ 'required' => false,
+ 'dependency' => [
+ 'live' => true,
+ 'fields' => [
+ [
+ 'field' => 'braintree_payment_methods',
+ 'values' => ['google_pay'],
+ ]
+ ],
+ ],
+ ],*/
+ ]
+ ];
+
+ foreach ( $settings as $setting ) {
+
+ if( !empty( $setting ) && $setting['title'] === 'Other Settings' ) {
+ $temp_settings[] = $merchant_settings;
+ $temp_settings[] = $extra_fee_settings;
+ $temp_settings[] = $payment_methods_settings;
+ }
+
+ $temp_settings[] = $setting;
+ }
+
+ $settings = $temp_settings;
+ }
+
+ // Return sanitized settings
+ return $settings;
+ }
+
+ public function merchant_url($tab = 'plans') {
+
+ $settings = $this->get_plugin_settings();
+
+ $braintreeUrl = '#';
+ if (!empty($settings['merchant-id'])) {
+ $environment = !empty($settings['environment']) ? strtolower($settings['environment']) : 'sandbox';
+ $environmentUrl = ( $environment == 'sandbox' ) ? 'sandbox.' : '';
+
+ $braintree_config = new \Braintree\Configuration([
+ 'environment' => strtolower($settings['environment']),
+ 'merchantId' => $settings['merchant-id'],
+ 'publicKey' => $settings['public-key'],
+ 'privateKey' => $settings['private-key']
+ ]);
+
+ $merchantPath = $braintree_config->merchantPath();
+ $braintreeUrl = "https://{$environmentUrl}braintreegateway.com{$merchantPath}/{$tab}";
+ }
+
+ return $braintreeUrl;
+ }
+
+ /**
+ * This function is callback for braintree_trial setting field.
+ *
+ * @param array $field Settings fields
+ * @param bool $echo Display or return
+ *
+ * @return string $html
+ *
+ * @throws \Braintree\Exception\Configuration
+ */
+ public function settings_braintree_trial($field, $echo = true) {
+
+ $braintreePlans = $this->merchant_url();
+ $html = sprintf(__('Select your product trial form %sBraintree Plans%s', 'angelleye-gravity-forms-braintree'), '', '');
+
+ if ($echo) {
+ echo $html;
+ }
+
+ return $html;
+ }
+
+ /**
+ * Create and display plugin settings fields. These are settings for Braintree in particular, not a feed
+ *
+ * @since 1.0
+ * @return void
+ */
+ 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(
+ 'name' => 'public-key',
+ 'tooltip' => 'Your Braintree Account Public Key',
+ 'label' => 'Public Key',
+ 'type' => 'text',
+ 'class' => 'medium'
+ ),
+ array(
+ 'name' => 'private-key',
+ 'tooltip' => 'Your Braintree Account Private Key',
+ 'label' => 'Private Key',
+ 'type' => 'text',
+ 'class' => 'medium'
+ )
+ )
+ ),
+ array(
+ 'title' => 'Braintree ACH Settings',
+ 'fields' => array(
+ array(
+ 'name' => 'tokenization-key',
+ 'tooltip' => 'Your Braintree Tokenization Key',
+ 'label' => 'Tokenization Key',
+ 'type' => 'text',
+ 'class' => 'medium'
+ ),
+ array(
+ 'name' => 'business-name',
+ 'tooltip' => 'For all ACH transactions, you are required to collect a mandate or “proof of authorization” from the customer to prove that you have their explicit permission to debit their bank account. We will put your business name in authorization text',
+ 'label' => 'Business name',
+ 'type' => 'text',
+ 'class' => 'medium'
+ )
+ )
+ ),
+ array(
+ 'title' => 'Payment Settings',
+ 'fields' => array(
+ array(
+ 'name' => 'settlement',
+ 'tooltip' => 'Choosing \'Yes\' will tell Braintree to automatically submit your transactions for settlement upon receipt',
+ 'label' => 'Automatic Settlement Submission',
+ 'type' => 'radio',
+ 'choices' => array(
+ array(
+ 'label' => 'Yes',
+ 'name' => 'yes'
+ ),
+ array(
+ 'label' => 'No',
+ 'name' => 'no'
+ )
+ )
+ )
+ )
+ ),
+ array(
+ 'title' => 'Environment Settings',
+ 'fields' => array(
+ array(
+ 'name' => 'environment',
+ 'tooltip' => 'Do you want to process test payments or real payments?',
+ 'label' => 'API Endpoint',
+ 'type' => 'radio',
+ 'choices' => array(
+ array(
+ 'label' => 'Sandbox',
+ 'name' => 'sandbox'
+ ),
+ array(
+ 'label' => 'Production',
+ 'name' => 'production'
+ )
+ )
+ )
+ )
+ ),
+ array(
+ 'title' => esc_html__( 'Extra Fee Settings', 'angelleye-gravity-forms-braintree' ),
+ 'fields' => array(
+ array(
+ 'name' => 'enable_extra_fees',
+ 'type' => 'toggle',
+ 'label' => esc_html__( 'Enable Extra Fee (%)', 'angelleye-gravity-forms-braintree' ),
+ 'default_value' => false,
+ 'tooltip' => '' . __( 'Extra Fee', 'angelleye-gravity-forms-braintree' ) . '' . __( 'Enable this and set a percentage (%) to collect a convenience fee on credit card, debit card and/or ACH payments.', 'angelleye-gravity-forms-braintree' ),
+ ),
+ array(
+ 'name' => 'extra_fee_label',
+ 'type' => 'text',
+ 'label' => esc_html__( 'Extra Fee Label', 'angelleye-gravity-forms-braintree' ),
+ 'tooltip' => '',
+ 'required' => false,
+ 'default_value' => esc_html__( 'Convenience Fee', 'angelleye-gravity-forms-braintree'),
+ 'placeholder' => esc_html__( 'Convenience Fee', 'angelleye-gravity-forms-braintree'),
+ 'class' => 'extra-fees-input',
+ 'dependency' => array(
+ 'live' => true,
+ 'fields' => array(
+ array(
+ 'field' => 'enable_extra_fees',
+ ),
+ ),
+ ),
+ ),
+ array(
+ 'name' => 'credit_card_fees',
+ 'type' => 'text',
+ 'input_type' => 'number',
+ 'label' => esc_html__( 'Credit Card', 'angelleye-gravity-forms-braintree' ),
+ 'tooltip' => '',
+ 'required' => false,
+ 'default_value' => '0.00',
+ 'placeholder' => '0.00',
+ 'min' => '0',
+ 'class' => 'extra-fees-input',
+ 'dependency' => array(
+ 'live' => true,
+ 'fields' => array(
+ array(
+ 'field' => 'enable_extra_fees',
+ ),
+ ),
+ ),
+ ),
+ array(
+ 'name' => 'debit_card_fees',
+ 'type' => 'text',
+ 'input_type' => 'number',
+ 'label' => esc_html__( 'Debit Card', 'angelleye-gravity-forms-braintree' ),
+ 'tooltip' => '',
+ 'required' => false,
+ 'default_value' => '0.00',
+ 'placeholder' => '0.00',
+ 'min' => '0',
+ 'class' => 'extra-fees-input',
+ 'dependency' => array(
+ 'live' => true,
+ 'fields' => array(
+ array(
+ 'field' => 'enable_extra_fees',
+ ),
+ ),
+ ),
+ ),
+ array(
+ 'name' => 'ach_fees',
+ 'type' => 'text',
+ 'input_type' => 'number',
+ 'label' => esc_html__( 'ACH', 'angelleye-gravity-forms-braintree' ),
+ 'tooltip' => '',
+ 'required' => false,
+ 'default_value' => '0.00',
+ 'placeholder' => '0.00',
+ 'min' => '0',
+ 'class' => 'extra-fees-input',
+ 'dependency' => array(
+ 'live' => true,
+ 'fields' => array(
+ array(
+ 'field' => 'enable_extra_fees',
+ ),
+ ),
+ ),
+ ),
+ array(
+ 'name' => 'paypal_fees',
+ 'type' => 'text',
+ 'input_type' => 'number',
+ 'label' => esc_html__( 'PayPal', 'angelleye-gravity-forms-braintree' ),
+ 'tooltip' => '',
+ 'required' => false,
+ 'default_value' => '0.00',
+ 'placeholder' => '0.00',
+ 'min' => '0',
+ 'class' => 'extra-fees-input',
+ 'dependency' => array(
+ 'live' => true,
+ 'fields' => array(
+ array(
+ 'field' => 'enable_extra_fees',
+ ),
+ ),
+ ),
+ ),
+ array(
+ 'name' => 'venmo_fees',
+ 'type' => 'text',
+ 'input_type' => 'number',
+ 'label' => esc_html__( 'Venmo', 'angelleye-gravity-forms-braintree' ),
+ 'tooltip' => '',
+ 'required' => false,
+ 'default_value' => '0.00',
+ 'placeholder' => '0.00',
+ 'min' => '0',
+ 'class' => 'extra-fees-input',
+ 'dependency' => array(
+ 'live' => true,
+ 'fields' => array(
+ array(
+ 'field' => 'enable_extra_fees',
+ ),
+ ),
+ ),
+ ),
+ /*array(
+ 'name' => 'google_pay_fees',
+ 'type' => 'text',
+ 'input_type' => 'number',
+ 'label' => esc_html__( 'Google Pay', 'angelleye-gravity-forms-braintree' ),
+ 'tooltip' => '',
+ 'required' => false,
+ 'default_value' => '0.00',
+ 'placeholder' => '0.00',
+ 'min' => '0',
+ 'class' => 'extra-fees-input',
+ 'dependency' => array(
+ 'live' => true,
+ 'fields' => array(
+ array(
+ 'field' => 'enable_extra_fees',
+ ),
+ ),
+ ),
+ ),
+ array(
+ 'name' => 'apple_pay_fees',
+ 'type' => 'text',
+ 'input_type' => 'number',
+ 'label' => esc_html__( 'Apple Pay', 'angelleye-gravity-forms-braintree' ),
+ 'tooltip' => '',
+ 'required' => false,
+ 'default_value' => '0.00',
+ 'placeholder' => '0.00',
+ 'min' => '0',
+ 'class' => 'extra-fees-input',
+ 'dependency' => array(
+ 'live' => true,
+ 'fields' => array(
+ array(
+ 'field' => 'enable_extra_fees',
+ ),
+ ),
+ ),
+ ),*/
+ )
+ )
+ );
+ }
+
+ /**
+ * 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
+ */
+ public function settings_are_valid($settings) {
+ if (empty($settings)) {
+ return false;
+ }
+ if (!empty($settings['merchant-id']) && !empty($settings['public-key']) && !empty($settings['public-key'])) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Get plugin settings
+ *
+ * @since 1.0
+ * @return void
+ */
+ public function get_plugin_settings() {
+
+ $settings = parent::get_plugin_settings();
+
+ if ($this->settings_are_valid($settings)) {
+ return $settings;
+ } else {
+ return false;
+ }
+ }
+
+ public function angelleye_gform_braintree_display_push_notification() {
+ global $current_user;
+ $user_id = $current_user->ID;
+ if (false === ( $response = get_transient('angelleye_gravity_braintree_push_notification_result') )) {
+ $response = $this->angelleye_get_push_notifications();
+ if (is_object($response)) {
+ set_transient('angelleye_gravity_braintree_push_notification_result', $response, 12 * HOUR_IN_SECONDS);
+ }
+ }
+ if (is_object($response)) {
+ foreach ($response->data as $key => $response_data) {
+ if (!get_user_meta($user_id, $response_data->id)) {
+ $this->angelleye_display_push_notification($response_data);
+ }
+ }
+ }
+ }
+
+ public function angelleye_get_push_notifications() {
+ $args = array(
+ 'plugin_name' => 'angelleye-gravity-forms-braintree',
+ );
+ $api_url = PAYPAL_FOR_WOOCOMMERCE_PUSH_NOTIFICATION_WEB_URL . '?Wordpress_Plugin_Notification_Sender';
+ $api_url .= '&action=angelleye_get_plugin_notification';
+ $request = wp_remote_post($api_url, array(
+ 'method' => 'POST',
+ 'timeout' => 45,
+ 'redirection' => 5,
+ 'httpversion' => '1.0',
+ 'blocking' => true,
+ 'headers' => array('user-agent' => 'AngellEYE'),
+ 'body' => $args,
+ 'cookies' => array(),
+ 'sslverify' => false
+ ));
+ if (is_wp_error($request) or wp_remote_retrieve_response_code($request) != 200) {
+ return false;
+ }
+ if ($request != '') {
+ $response = json_decode(wp_remote_retrieve_body($request));
+ } else {
+ $response = false;
+ }
+ return $response;
+ }
+
+ public function angelleye_display_push_notification($response_data) {
+ echo '';
+ }
+
+ public function angelleye_gform_braintree_adismiss_notice() {
+ global $current_user;
+ $user_id = $current_user->ID;
+ if (!empty($_POST['action']) && $_POST['action'] == 'angelleye_gform_braintree_adismiss_notice') {
+ add_user_meta($user_id, wc_clean($_POST['data']), 'true', true);
+ wp_send_json_success();
+ }
+ }
+
+ public function include_angelleye_braintree_style_noconflict($styles) {
+ $styles[] = 'gravity-forms-braintree-admin-css';
+ return $styles;
+ }
+
+ public function include_angelleye_braintree_script_noconflict($scripts) {
+ $scripts[] = 'gravity-forms-braintree-admin';
+ return $scripts;
+ }
+
+ public function enqueue_scripts_js() {
+
+ $is_report = !empty( $_GET['page'] ) && $_GET['page'] === 'gf_transaction_reports';
+
+ if (GFForms::is_gravity_page() || $is_report) {
+ wp_enqueue_script('datetimepicker-admin', GRAVITY_FORMS_BRAINTREE_ASSET_URL . 'assets/js/datetimepicker.min.js', array('jquery'), $this->_version, false);
+ wp_enqueue_script('gravity-forms-braintree-admin', GRAVITY_FORMS_BRAINTREE_ASSET_URL . 'assets/js/gravity-forms-braintree-admin.js', array('jquery'), $this->_version, false);
+ wp_localize_script('gravity-forms-braintree-admin', 'GFBraintreeObj',[
+ 'report_confirm' => __('Are you sure you want to delete this report file.','angelleye-gravity-forms-braintree'),
+ 'start_date_required' => __('Start date is required field','angelleye-gravity-forms-braintree'),
+ 'end_date_required' => __('End date is required field','angelleye-gravity-forms-braintree'),
+ ]);
+ }
+ }
+
+ public function enqueue_styles_css() {
+ $is_report = !empty( $_GET['page'] ) && $_GET['page'] === 'gf_transaction_reports';
+ if (GFForms::is_gravity_page() || $is_report) {
+ wp_enqueue_style('datetimepicker-admin-css', GRAVITY_FORMS_BRAINTREE_ASSET_URL . 'assets/css/datetimepicker.min.css', array(), $this->_version, 'all');
+ wp_enqueue_style('gravity-forms-braintree-admin-css', GRAVITY_FORMS_BRAINTREE_ASSET_URL . 'assets/css/gravity-forms-braintree-admin.css', array(), $this->_version, 'all');
+ }
+ }
+
+ /**
+ * Load the Braintree JS and Custom JS in frontend
+ * @return array
+ */
+ public function scripts() {
+ $translation_array = [];
+ $settings = $this->get_plugin_settings();
+ if ($settings !== false) {
+ $translation_array['ajax_url'] = admin_url( 'admin-ajax.php' );
+ $translation_array['ach_bt_nonce'] = wp_create_nonce('preview-payment-nonce');
+ $translation_array['ach_bt_token'] = @$settings['tokenization-key'];
+ $translation_array['ach_business_name'] = @$settings['business-name'];
+ $translation_array['is_admin'] = is_admin();
+ }
+
+ $scripts = array(
+ array(
+ 'handle' => 'angelleye-gravity-form-braintree-client',
+ 'src' => 'https://js.braintreegateway.com/web/3.61.0/js/client.min.js',
+ 'version' => $this->_version,
+ 'deps' => array('jquery'),
+ 'in_footer' => false,
+ 'callback' => array($this, 'localize_scripts'),
+ 'enqueue' => array(
+ array('field_types' => array('braintree_ach','braintree_credit_card'))
+ )
+ ),
+ array(
+ 'handle' => 'angelleye-gravity-form-braintree-data-collector',
+ 'src' => 'https://js.braintreegateway.com/web/3.61.0/js/data-collector.min.js',
+ 'version' => $this->_version,
+ 'deps' => array(),
+ 'in_footer' => false,
+ 'callback' => array($this, 'localize_scripts'),
+ 'enqueue' => array(
+ array('field_types' => array('braintree_ach','braintree_credit_card'))
+ )
+ ),
+ array(
+ 'handle' => 'angelleye-gravity-form-braintree-usbankaccount',
+ 'src' => 'https://js.braintreegateway.com/web/3.61.0/js/us-bank-account.min.js',
+ 'version' => $this->_version,
+ 'deps' => array(),
+ 'in_footer' => false,
+ 'callback' => array($this, 'localize_scripts'),
+ 'enqueue' => array(
+ array('field_types' => array('braintree_ach','braintree_credit_card'))
+ )
+ ),
+ array(
+ 'handle' => 'angelleye_gravity_form_braintree_ach_handler',
+ 'src' => GRAVITY_FORMS_BRAINTREE_ASSET_URL . 'assets/js/angelleye-braintree-ach-cc.js',
+ 'version' => $this->_version,
+ 'deps' => array('jquery', 'angelleye-gravity-form-braintree-client', 'angelleye-gravity-form-braintree-data-collector',
+ 'angelleye-gravity-form-braintree-usbankaccount'),
+ 'in_footer' => false,
+ 'callback' => array($this, 'localize_scripts'),
+ 'strings' => $translation_array,
+ 'enqueue' => array(
+// array(
+// 'admin_page' => array( 'form_settings' ),
+// 'tab' => 'simpleaddon'
+// )
+ array('field_types' => array('braintree_ach','braintree_credit_card'))
+ )
+ ),
+ );
+
+ return array_merge(parent::scripts(), $scripts);
+ }
+
+ /**
+ * Override default billing cycles intervals for subscription plan.
+ *
+ * @return array $billing_cycles
+ */
+ public function supported_billing_intervals() {
+ $billing_cycles = array(
+ 'month' => array('label' => esc_html__('month(s)', 'angelleye-gravity-forms-braintree'), 'min' => 1, 'max' => 24)
+ );
+
+ return $billing_cycles;
+ }
+
+ /**
+ * Get all Braintree plans using Braintree payment Gateway settings.
+ *
+ * @return array $plans
+ *
+ * @throws \Braintree\Exception\Configuration
+ */
+ public function get_plans() {
+ try {
+ $gateway = $this->getBraintreeGateway();
+ if ($gateway) {
+ $plan_lists = $gateway->plan()->all();
+ $plans = array(array(
+ 'label' => __('Select a plan', 'angelleye-gravity-forms-braintree'),
+ 'value' => '',
+ ));
+ if (!empty($plan_lists)) {
+ foreach ($plan_lists as $plan) {
+ $plans[] = array(
+ 'label' => $plan->name,
+ 'value' => $plan->id,
+ );
+ }
+ }
+ return $plans;
+ }
+ } catch (Exception $ex) {
+
+ }
+ }
+
+ /**
+ * Get customer id using customer email address.
+ *
+ * If customer email address already exists in braintree customer lists then provide customer id,
+ * Otherwise create a new customer using customer details and then provide a customer id.
+ *
+ * @param array $args
+ *
+ * @return int|string $customer_id Customer id.
+ *
+ * @throws \Braintree\Exception\Configuration
+ */
+ public function get_customer_id($args) {
+ //check if customer detail is empty or not array then return.
+ if (empty($args) || !is_array($args)) {
+ return '';
+ }
+ $email = !empty($args['email']) ? $args['email'] : '';
+ $gateway = $this->getBraintreeGateway();
+ if ($gateway) {
+ //search customer using email address
+ $collections = $gateway->customer()->search([
+ Braintree\CustomerSearch::email()->is($email)
+ ]);
+ $customer_id = 0;
+ foreach ($collections as $key => $collection) {
+ if (!empty($collection->id)) {
+ $customer_id = $collection->id;
+ }
+ }
+ //check $customer_id is empty then create a new customer.
+ if (empty($customer_id)) {
+ $customer = $gateway->customer()->create($args);
+ if (!empty($customer->customer->id)) {
+ $customer_id = $customer->customer->id;
+ }
+ }
+ return $customer_id;
+ }
+ }
+
+ /**
+ * Manage Braintree request parameters.
+ *
+ * @param array $request_args Get request arguments.
+ * @param array $data Get data.
+ * @param array $form Get form.
+ * @param array $entry Get form entry
+ * @return array $request_args
+ */
+ public function manage_braintree_request_parameter( $request_args, $data, $form, $entry ) {
+
+ $payment_feed = $this->get_payment_feed([], $form);
+ $feed_meta = !empty( $payment_feed['meta'] ) ? $payment_feed['meta'] : '';
+
+ if( !empty( $feed_meta['sub_merchant_account_id'] ) ) {
+ $request_args['merchantAccountId'] = $feed_meta['sub_merchant_account_id'];
+ }
+
+ return $request_args;
+ }
+
+ /**
+ * Get all sub merchant accounts using primary account.
+ *
+ * @return array $merchant_accounts
+ */
+ public function merchant_account_choices() {
+
+ $merchant_accounts = [
+ [
+ 'label' => esc_html__( 'Select Merchant Account ID', 'angelleye-gravity-forms-braintree' ),
+ 'value' => ''
+ ]
+ ];
+
+ try {
+
+ $gateway = $this->getBraintreeGateway();
+
+ if ( ! empty( $gateway ) ) {
+
+ $subMerchantAccounts = $gateway->merchantAccount()->all();
+
+ foreach ( $subMerchantAccounts as $account ) {
+
+ $account_id = !empty( $account->id ) ? $account->id : '';
+ $account_currency = !empty( $account->currencyIsoCode ) ? $account->currencyIsoCode : '';
+ $merchant_accounts[] = [
+ 'label' => sprintf('%s - [%s]', $account_id, $account_currency),
+ 'value' => $account_id
+ ];
+ }
+ }
+ } catch (Exception $exception) {
+
+ }
+
+ return $merchant_accounts;
+ }
+
+ public function get_payment_method_choices() {
+
+ $payment_methods = [];
+
+ $get_payment_methods = get_braintree_payment_methods();
+
+ if( !empty( $get_payment_methods ) && is_array( $get_payment_methods ) ) {
+
+ foreach ( $get_payment_methods as $key => $get_payment_method ) {
+ $payment_methods[] = [
+ 'label' => $get_payment_method,
+ 'name' => $key
+ ];
+ }
}
+ return $payment_methods;
}
-
}
-
-?>
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..0cf3a6a 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1,66 +1,161 @@
-=== Gravity Forms Braintree Add-On ===
-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
-License: GPLv2 or later
-License URI: http://www.gnu.org/licenses/gpl-2.0.html
+=== Gravity Forms Braintree Payments ===
+Contributors: angelleye, Plugify, hello@lukerollans.me, gravityplus
+Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=9CQZZGGMF78VY&source=url
+Tags: gravity form, gravity forms, credit card, credit cards, payment, payments, braintree
+Requires at least: 5.0
+Tested up to: 6.5.4
+Stable tag: 5.1.1
+License: GPLv3
+License URI: https://www.gnu.org/licenses/gpl-3.0.html
Allow your customers to purchase goods and services through Gravity Forms via Braintree Payments
== Description ==
-Braintree Payments is a payment gateway provider owned by eBAY Inc, which allows you to proces credit card payments without the need for a bank merchant account and full PCI-compliance. No sensitive data such as credit card numbers are stored on your server, Braintree takes care of everything.
+Braintree Payments is a payment gateway provider owned by PayPal which allows you to process credit card payments without the need for a bank merchant account and full PCI-compliance. No sensitive data such as credit card numbers are stored on your server, Braintree takes care of everything.
> Requires at least WordPress 3.8 and Gravity Forms 1.8
There are just a few simple steps to begin leveraging your Braintree Payments account:
-1. Install Gravity Forms Braintree Add-On
-2. Go to the Form Settings page for the form you wish to create a Braintree feed on
+1. Install Gravity Forms Braintree Payments.
+2. Go to the Form Settings page for the form you wish to create a Braintree feed on.
3. You will be prompted to configure your Braintree settings. Click the link provided to do so.
4. Once you have configured your Braintree settings, return to the Form Settings page and follow the prompts.
= Features =
-* Seamlessly integrates your Gravity Forms credit card forms with Braintree Payments
-* 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
-
-If you have found this plugin useful, consider taking a moment to rate it, or perhaps even a small donation.
+* Seamlessly integrates your Gravity Forms credit card forms with Braintree Payments.
+* 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.
== Installation ==
-1. Upload the `gravity-forms-braintree` folder to the `/wp-content/plugins/` directory.
-2. Activate the plugin through the 'Plugins' menu in WordPress.
-3. Navigate to the Form you wish to setup with a Braintree feed.
-4. Under Form Settings, choose the Braintree option.
+= Automatic installation =
+
+Automatic installation is the easiest option as WordPress handles the file transfers itself and you don't need to leave your web browser. To do an automatic install of Gravity Forms Braintree Payments, log in to your WordPress dashboard, navigate to the Plugins menu and click Add New.
+
+In the search field type Gravity Forms Braintree Payments and click Search Plugins. Once you've found our plugin (make sure it says "by Angell EYE") you can view details about it such as the the rating and description. Most importantly, of course, you can install it by simply clicking Install Now.
+
+= Manual Installation =
+
+1. Unzip the files and upload the folder into your plugins folder (/wp-content/plugins/) overwriting older versions if they exist
+2. Activate the plugin in your WordPress admin area.
+
+= Usage =
+
+1. Navigate to the Form you wish to setup with a Braintree feed.
+2. Under Form Settings, choose the Braintree option.
== Frequently asked questions ==
= What type of Braintree payments can be accepted? =
-For this early version, only one off payments can be accepted. Subscriptions will be available in version 1.1
-
-= Can I use conditional logic? EG, I only want to register a user if the Braintree payment was successful =
-In version 1.0, no. This is planned for version 1.2, coming very soon
+* For this early version, only one off payments can be accepted.
= Does this plugin support Braintree subscriptions? =
-Not currently, no. This will be released very shortly in version 1.1
-
-= Available filters and actions =
-No filters are currently available for this pre-release version
+* Not yet. This will be added based on future demand.
== Screenshots ==
-1. You will be initially greeted with the empty feed page, prompting you to configure your Braintree settings.
-2. Page for configuring your Braintree settings, such as Merchant ID
-3. Configuring a Gravity Forms Braintree feed
-4. List of active feeds
+1. Drop a credit card field collection directly into any Gravity Form.
+2. Easily configure your Braintree settings, allowing for quick and efficient setup.
+3. Quickly and easily configure payment feeds under Form Settings of any Gravity Form.
+4. List of active feeds on the current form.
== Changelog ==
+= 5.1.1 - 06.20.2024 =
+* Fix - Braintree drop-in load on input value change
+
+= 5.1.0 - 06.19.2024 =
+* Feature - Added global and form level settings for PayPal and venmo Extra fees.
+* Feature - Added form level settings for Enable/Disable payment methods (PayPal and Venmo). Default is Credit Card.
+* Check the compatibility with latest WordPress version 6.5.4
+
+= 5.0.1 - 01.24.2024 =
+* Fix - Minor bug fixes
+
+= 5.0.0 - 01.22.2024 =
+* Feature - Braintree Merchant Account ID Compatibility for each Braintree payment forms. ([GFB-54](https://github.com/angelleye/gravity-forms-braintree/pull/46))
+* Feature - Add global and form level settings for Extra fees. ([GFB-50](https://github.com/angelleye/gravity-forms-braintree/pull/47))
+* Feature - Collect Extra fees based on Card Type: Debit, Credit or ACH.
+* Feature - Generate and export Braintree transaction reports. ([GFB-56](https://github.com/angelleye/gravity-forms-braintree/pull/48))
+* Check the compatibility with latest WordPress version 6.4.2
+
+= 4.0.7 - 04.18.2023 =
+* Fix - Resolved Braintree payment error while using credit card field. ([GFB-46](https://github.com/angelleye/gravity-forms-braintree/pull/45))
+
+= 4.0.6 - 04.18.2023 =
+* Fix - Adjustments to resolve theme conflict. ([GFB-40](https://github.com/angelleye/gravity-forms-braintree/pull/44))
+
+= 4.0.5 - 01.26.2022 =
+* Feature - Added Gravity Forms version 2.5.16 capability. ([GFB-42](https://github.com/angelleye/gravity-forms-braintree/pull/39))
+
+= 4.0.4 - 01.17.2022 =
+* Fix - Resolved PHP Fatal error. ([GFB-41](https://github.com/angelleye/gravity-forms-braintree/pull/38))
+
+= 4.0.3 - 01.10.2022 =
+* Fix - Resolved PHP Fatal error. ([GFB-37](https://github.com/angelleye/gravity-forms-braintree/pull/37))
+
+= 4.0.2 - 07.12.2021 =
+* Tweak - Updates Update Braintree SDK. ([GFB-37](https://github.com/angelleye/gravity-forms-braintree/pull/33))
+
+= 4.0.1 - 03.16.2021 =
+* Tweak - Adding label for Braintree CC while setting up Subscription method ([GFB-36](https://github.com/angelleye/gravity-forms-braintree/pull/31))
+
+= 4.0 - 03.01.2021 =
+* Feature - Added Braintree Subscription ([GFB-31](https://github.com/angelleye/gravity-forms-braintree/pull/30))
+
+= 3.1.2 - 05.14.2020 =
+* Fix - Resolved Braintree ACH/CC form validation issuw with multiple Payment Methods ([GFB-27](https://github.com/angelleye/gravity-forms-braintree/pull/28))
+
+= 3.1.1 - 05.14.2020 =
+* Feature - Added Braintree ACH Direct Debit + CC compatibility with custom radio fields and conditions ([GFB-25](https://github.com/angelleye/gravity-forms-braintree/pull/26))
+* Feature - Pass custom field mapping data with ACH Direct Debit payments ([GFB-24](https://github.com/angelleye/gravity-forms-braintree/pull/27))
+
+= 3.1.0 - 05.13.2020 =
+* Feature - Added Braintree ACH Direct Debit Payment Gateway ([GFB-17](https://github.com/angelleye/gravity-forms-braintree/pull/25))
+* Feature - Added custom plugin requirement checker to validate server configuration ([GFB-22](https://github.com/angelleye/gravity-forms-braintree/pull/24))
+
+= 3.0.2 - 05.04.2020 =
+* Fix - Resolved the issue with PHP Version comparison ([GFB-21](https://github.com/angelleye/gravity-forms-braintree/pull/23))
+
+= 3.0.1 - 04.28.2020 =
+* Fix - Compatibility issue with PayPal for WooCommerce in loading Braintree library ([GFB-18](https://github.com/angelleye/gravity-forms-braintree/pull/22))
+
+= 3.0.0 - 04.28.2020 =
+* Upgrade - Braintree Library Upgraded from 3.36.0 to 5.0.0 ([GFB-18](https://github.com/angelleye/gravity-forms-braintree/pull/21))
+* Tweak - Support Gravity Form No Conflict Mode issue with Braintree script loading in backend. ([GFB-19](https://github.com/angelleye/gravity-forms-braintree/pull/20))
+
+= 2.2.2 - 12.30.2019 =
+* Tweak - Adjustment to Updater plugin notice dismissible. ([GFB-16](https://github.com/angelleye/gravity-forms-braintree/pull/17))
+
+= 2.2.0 = 11.20.2019 =
+* Verification - WordPress 5.3 compatibility.
+
+= 2.2.0 = 10.16.2019 =
+* Feature - Adds Braintree field mapping capability. ([GFB-12](https://github.com/angelleye/gravity-forms-braintree/pull/14)) ([GFB-15](https://github.com/angelleye/gravity-forms-braintree/pull/16))
+* Tweak - Adds a notice if you try to activate the Braintree Payments extension without Gravity Forms active.
+
+= 2.1.3 - 07.23.2019 =
+* Tweak - Update push notification system sync interval time. ([GFB-9](https://github.com/angelleye/gravity-forms-braintree/pull/11))
+
+= 2.1.2 - 07.09.2019 =
+* Tweak - Minor adjustments to API request.
+
+= 2.1.1 - 05.31.2019 =
+* Feature - Adds AE notification system. ([GFB-8](https://github.com/angelleye/gravity-forms-braintree/pull/10))
+* Tweak - Adds text domain. ([GFB-7](https://github.com/angelleye/gravity-forms-braintree/pull/9))
+
+= 2.1.0 =
+* Feature - Adds AE Updater compatibility for future notices and automated updates. [GFB-4] ([GFB-5](https://github.com/angelleye/gravity-forms-braintree/pull/8))
+
+= 2.0.0 =
+* Fix - Updates Braintree Payments SDK and resolves failures with latest version of Gravity Forms. ([#1](https://github.com/angelleye/gravity-forms-braintree/issues/1))
+
+= 1.1.2 =
+* Internal maintenance release. Version 1.2 is coming soon and it's going to be big!
+
= 1.1.1 =
* Dashes and spaces are now removed from credit card number before sending to Braintree
@@ -80,8 +175,4 @@ No filters are currently available for this pre-release version
* Most of plugin functionality
= 0.1 =
-* Initial version of the plugin
-
-== Upgrade notice ==
-
-IMPORTANT! Version 1.0 is a complete overhaul from the previous version. Your existing feeds will not work. Please make sure you check all your feeds and ensure they function correctly.
+* Initial version of the plugin
\ No newline at end of file
diff --git a/templates/view-order-summary.php b/templates/view-order-summary.php
new file mode 100644
index 0000000..1159ecf
--- /dev/null
+++ b/templates/view-order-summary.php
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/view-pricing-fields-html.php b/templates/view-pricing-fields-html.php
new file mode 100644
index 0000000..7ecb501
--- /dev/null
+++ b/templates/view-pricing-fields-html.php
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+