*/
class Wp_Stripe_Plaid_Public {
/**
* The ID of this plugin.
*
* @since 1.0.0
* @access private
* @var string $plugin_name The ID of this plugin.
*/
private $plugin_name;
/**
* The version of this plugin.
*
* @since 1.0.0
* @access private
* @var string $version The current version of this plugin.
*/
private $version;
/**
* The plugin settings.
*
* @since 1.0.0
* @access private
* @var string $settings The current settings of this plugin.
*/
private $settings;
/**
* The plugin settings.
*
* @since 1.0.0
* @access private
* @var string $user_message holds message(s) for user if they don't have Stripe or Plaid creds.
*/
private $user_message = array();
/**
* Holds array of customers.
*
* @var array
*/
private $stripe_customers = array();
/**
* Holds secret key.
*
* @var [type]
*/
private $stripe_key;
/**
* Initialize the class and set its properties.
*
* @since 1.0.0
* @param string $plugin_name The name of the plugin.
* @param string $version The version of this plugin.
*/
public function __construct( $plugin_name, $version ) {
$this->plugin_name = $plugin_name;
$this->version = $version;
$this->settings = get_option( 'stripe_plaid_settings' );
$this->set_stripe_key();
add_shortcode( 'wp_stripe_plaid', array( $this, 'render_form' ) );
}
/**
* Gets all Stripe customers.
* Recursively calls the customers endpoint if more than 100 customers exist.
*
* @param [type] $start_after
* @return void
*/
public function get_all_stripe_customers( $start_after ) {
$args = [
'limit' => 100,
];
if ( $start_after ) {
$args['starting_after'] = $start_after;
}
\Stripe\Stripe::setApiKey( $this->stripe_key );
$customers = \Stripe\Customer::all( $args );
// Add stripe customer results to $stripe_customers.
$this->stripe_customers = array_merge( $this->stripe_customers, $customers->data );
if ( $customers->has_more ) {
$last_customer = end( $customers->data );
$this->get_all_stripe_customers( $last_customer->id );
}
}
/**
* Sets the Stripe key based on ENV
*
* @return void
*/
public function set_stripe_key() {
$this->stripe_key = ( $this->settings['sp_environment'] === 'live' || $this->settings['sp_environment'] === 'development' ) ? $this->settings['stripe_live_api_key'] : $this->settings['stripe_test_api_key'];
}
/**
* Register the stylesheets for the public-facing side of the site.
*
* @since 1.0.0
*/
public function enqueue_styles() {
wp_register_style( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'css/wp-stripe-plaid-public.css', array(), $this->version, 'all' );
}
/**
* Register the JavaScript for the public-facing side of the site.
*
* @since 1.0.0
*/
public function enqueue_scripts() {
wp_register_script( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'js/wp-stripe-plaid-public.js', array( 'jquery', 'stripe_plaid' ), $this->version, true );
wp_register_script( 'stripe_plaid', 'https://cdn.plaid.com/link/v2/stable/link-initialize.js', array(), null, true );
wp_localize_script($this->plugin_name, 'ajax_object', array( 'ajax_url' => admin_url('admin-ajax.php'), 'ajax_nonce' => wp_create_nonce('stripe_plaid_nonce') ) );
}
/**
* Logic to check for keys.
*
* @return void
*/
public function has_creds(){
// check for test creds if in test mode, otherwise live key
if ( $this->settings['sp_environment'] === "test" ) {
if ( !strlen( trim( $this->settings['stripe_test_api_key'] ) ) ) {
$this->user_message[] = 'Missing Stripe test API Key';
}
} else {
if ( !strlen( trim( $this->settings['stripe_live_api_key'] ) ) ) {
$this->user_message[] = 'Missing Stripe live API Key';
}
}
// Plaid keys.
if ( !strlen( trim( $this->settings['plaid_client_id'] ) ) ) {
$this->user_message[] = 'Missing Plaid client ID';
}
if ( !strlen( trim( $this->settings['plaid_secret'] ) ) ) {
$this->user_message[] = 'Missing Plaid Secret';
}
if ( !strlen( trim( $this->settings['plaid_public_key'] ) ) ) {
$this->user_message[] = 'Missing Plaid public key';
}
}
/**
* Renders the payment form
*
* @return void
*/
public function render_form() {
if ( defined( 'REST_REQUEST' ) || is_admin() ) {
return;
}
$this->has_creds();
$show_form = ( is_user_logged_in() && $this->settings['form_auth'] === 'private' || $this->settings['form_auth'] === 'public' ) ? true : false;
if ( ! $show_form ) {
ob_start();
printf( '
', wp_login_url( get_the_permalink() ) );
return ob_get_clean();
} else {
wp_enqueue_script( $this->plugin_name );
wp_enqueue_script( 'stripe_plaid' );
wp_enqueue_style( $this->plugin_name );
if ( empty( $this->user_message ) ) {
Wp_Stripe_Plaid_Public::get_all_stripe_customers( false );
if ( $this->settings['sp_environment'] === 'live' ) {
$env = 'production';
} elseif ( $this->settings['sp_environment'] === 'test' ) {
$env = 'sandbox';
} else {
$env = 'development';
}
// Do we have a param with the amount?
$amount = ( isset( $_GET['amount'] ) ) ? (float) $_GET['amount'] : '';
if ( is_user_logged_in() ) {
$user = wp_get_current_user();
}
ob_start();
?>
user_message as $message ) {
echo '- ' . $message . '
';
}
}
}
}
/**
* Gets a customer from the $this->stripe_customers by email or returns false.
*
* @param string $email
* @return mixed
*/
public function get_customer_by_email( $email ) {
foreach ($this->stripe_customers as $customer) {
if ( $customer->email === $email ) {
return $customer;
}
}
return false;
}
/**
* Calls Stripe after connecting to Plaid
*
* @param int $amount
* @param string $currency
* @param string $token
* @param string $description
* @param string $email
* @return void
*/
public function call_stripe( $amount, $currency, $token, $description, $email ) {
\Stripe\Stripe::setApiKey( $this->stripe_key );
$return = array( 'error' => false );
$desc = 'Charged as an unauthenticated user';
$customer = false;
$stripe_customer_id = false;
// If they are logged in, create some defaults.
if ( is_user_logged_in() ) {
$meta_key = '_lb_ach_' . $this->settings['sp_environment'] . '_customer';
$current_user = wp_get_current_user();
$email = $current_user->user_email;
$wp_user = $current_user->user_login;
$desc = 'WordPress User: ' . $current_user->user_login;
$stripe_customer_id = get_user_meta( $current_user->ID, $meta_key, true );
// If we have a Stripe ID but no customer object, TRY to get customer by ID.
if ( $stripe_customer_id ) {
try {
$customer = \Stripe\Customer::retrieve( $stripe_customer_id );
} catch ( \Stripe\Error\Base $e ) {
$customer = false;
}
}
// Stripe will return deleted customers. We do not want these.
if ( isset( $customer->deleted ) ) {
$customer = false;
}
}
// Check with Stripe to see if we have a customer with this email.
if ( ! $customer ) {
$customer = $this->get_customer_by_email( $email );
if ( $customer ) {
$stripe_customer_id = $customer->id;
}
}
// If we still have not identified a customer, create one.
if ( ! $customer ) {
// Create a Customer.
$customer = \Stripe\Customer::create(array(
'email' => $email,
'source' => $token,
'description' => $desc,
));
$stripe_customer_id = $customer->id;
// Add to user's meta.
update_user_meta($current_user->ID, $meta_key, $stripe_customer_id);
}
// Figure out if the user is using a stored bank account or a new bank account by comparing bank account fingerprints.
$token_data = \Stripe\Token::retrieve( $token );
$this_bank_account = $token_data['bank_account'];
$cust_banks = $customer['sources']['data'];
foreach ( $cust_banks as $bank ) {
if ( $bank['fingerprint'] == $this_bank_account['fingerprint'] ) {
$source = $bank['id'];
}
}
// If this bank is not an existing one, we'll add it.
if ( $source === false ) {
$new_source = $customer->sources->create( array( 'source' => $token ) );
$source = $new_source['id'];
}
// Try to authorize the bank.
$charge_args = array(
'amount' => $amount,
'currency' => 'usd',
'description' => $description,
'customer' => $stripe_customer_id,
'source' => $source,
);
/**
* Let's Charge.
*/
try {
$charge = \Stripe\Charge::create( $charge_args );
} catch(\Stripe\Error\Card $e) {
// Since it's a decline, \Stripe\Error\Card will be caught.
$return = $e->getJsonBody();
} catch (\Stripe\Error\RateLimit $e) {
// Too many requests made to the API too quickly.
$return = $e->getJsonBody();
} catch (\Stripe\Error\InvalidRequest $e) {
// Invalid parameters were supplied to Stripe's API.
$return = $e->getJsonBody();
} catch (\Stripe\Error\Authentication $e) {
// Authentication with Stripe's API failed.
$return = $e->getJsonBody();
} catch (\Stripe\Error\ApiConnection $e) {
// Network communication with Stripe failed.
$return = $e->getJsonBody();
} catch (\Stripe\Error\Base $e) {
// Display a very generic error to the user, and maybe send.
$return = $e->getJsonBody();
} catch (Exception $e) {
// Something else happened, completely unrelated to Stripe.
$return = $e->getJsonBody();
}
// log error if there is any.
if ( $return['error'] && $this->settings['log'] === 'on' ) {
$message = 'DESCRIPTION: ' . $description . ' CHARGE: ' . $amount . ' TYPE: ' . $return['error']['type'] . ' PARAM: ' . $return['error']['param'] . ' MESSAGE: ' . $return['error']['message'];
Wp_Stripe_Plaid_Public::write_error( $message );
}
return $return;
}
/**
* Enables Plaid link
*
* @return void
*/
public function call_plaid(){
check_ajax_referer('stripe_plaid_nonce', 'nonce');
$env_setting = $this->settings['sp_environment'];
$env = ( $this->settings['sp_environment'] === 'live' ) ? 'production' : 'sandbox';
if ( $env_setting === 'live' ) {
$env = 'production';
} elseif ( $env_setting === 'development' ) {
$env = 'development';
} else {
$env = 'sandbox';
}
$headers[] = 'Content-Type: application/json';
$params = array(
'client_id' => $this->settings['plaid_client_id'],
'secret' => $this->settings['plaid_secret'],
'public_token' => $_POST['public_token'],
);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://" . $env . ".plaid.com/item/public_token/exchange");
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($params));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
curl_setopt($ch, CURLOPT_TIMEOUT, 80);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
if(!$result = curl_exec($ch)) {
trigger_error(curl_error($ch));
}
curl_close($ch);
$jsonParsed = json_decode($result);
$btok_params = array(
'client_id' => $this->settings['plaid_client_id'],
'secret' => $this->settings['plaid_secret'],
'access_token' => $jsonParsed->access_token,
'account_id' => $_POST['account_id']
);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://" . $env . ".plaid.com/processor/stripe/bank_account_token/create");
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($btok_params));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
curl_setopt($ch, CURLOPT_TIMEOUT, 80);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
if(!$result = curl_exec($ch)) {
trigger_error(curl_error($ch));
}
curl_close($ch);
$btoken = json_decode($result);
$charge = $this->call_stripe( $_POST['amount'], 'USD', $btoken->stripe_bank_account_token, $_POST['description'], $_POST['email'] );
wp_send_json( $charge );
wp_die();
}
/**
* Writes an error to the error log.
*
* @param string $message
* @return void
*/
static function write_error( $message ) {
$ts = date( '[ m.d.Y | H:i:s e ]' );
$message = $ts . " - " . $message . "\n" ;
error_log( $message, 3, WP_STRIPE_PLAID_PATH . 'errorlog.log');
}
}