From eb093d0cb60eca81ac81e053e681d071b1264d4d Mon Sep 17 00:00:00 2001
From: drastik <jwjoshuawalker@gmail.com>
Date: Fri, 10 Aug 2012 05:05:47 -0700
Subject: [PATCH] Several changes.  See README

---
 CRM/Core/Payment/Stripe.php                 |  86 +++++---
 README.txt                                  |  37 +++-
 civicrm_stripe.sql                          |   2 +-
 civicrm_templates/CRM/Core/BillingBlock.tpl | 221 ++++++++++++++++++++
 4 files changed, 305 insertions(+), 41 deletions(-)
 create mode 100644 civicrm_templates/CRM/Core/BillingBlock.tpl

diff --git a/CRM/Core/Payment/Stripe.php b/CRM/Core/Payment/Stripe.php
index b3121459..d51bc6a3 100644
--- a/CRM/Core/Payment/Stripe.php
+++ b/CRM/Core/Payment/Stripe.php
@@ -59,10 +59,14 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
   function checkConfig() {
     $config = CRM_Core_Config::singleton();
     $error = array();
-    
+
     if (empty($this->_paymentProcessor['user_name'])) {
       $error[] = ts('The "Secret Key" is not set in the Stripe Payment Processor settings.');
     }
+    
+    if (empty($this->_paymentProcessor['password'])) {
+      $error[] = ts('The "Publishable Key" is not set in the Stripe Payment Processor settings.');
+    }
  
     if (!empty($error)) {
       return implode('<p>', $error);
@@ -85,12 +89,6 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
     //Include Stripe library & Set API credentials.
     require_once("stripe-php/lib/Stripe.php");
     Stripe::setApiKey($this->_paymentProcessor['user_name']);
-    
-    $cc_name = $params['first_name'] . " ";
-    if (strlen($params['middle_name']) > 0) {
-      $cc_name .= $params['middle_name'] . " ";
-    }
-    $cc_name .= $params['last_name'];
 
     //Stripe amount required in cents.
     $amount = $params['amount'] * 100;
@@ -98,12 +96,27 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
     $amount = number_format($amount, 0, '', '');
 
     //Check for existing customer, create new otherwise.
-    $stripe_customer_id = "";
     $email = $params['email'];
-    $customer_query = "SELECT id FROM civicrm_stripe_customers WHERE email = '$email'";
-    $customer_query_res = CRM_Core_DAO::singleValueQuery($customer_query);
-    
+    $customer_query = CRM_Core_DAO::singleValueQuery("SELECT id FROM civicrm_stripe_customers WHERE email = '$email'");
+
+    //Use Stripe.js instead of raw card details.
+    if(isset($params['stripe_token'])) {
+      $card_details = $params['stripe_token'];
+    } else {
+      CRM_Core_Error::fatal(ts('Stripe.js token was not passed!  Have you turned on the CiviCRM-Stripe CMS module?'));
+    }
+
+    /****
+     * If for some reason you cannot use Stripe.js and you are aware of PCI Compliance issues, here is the alternative to Stripe.js:
+     ****/ 
     //Prepare Card details in advance to use for new Stripe Customer object if we need.
+/*   
+    $cc_name = $params['first_name'] . " ";
+    if (strlen($params['middle_name']) > 0) {
+      $cc_name .= $params['middle_name'] . " ";
+    }
+    $cc_name .= $params['last_name'];
+    
     $card_details = array(
   	  'number' => $params['credit_card_number'], 
   	  'exp_month' => $params['month'], 
@@ -113,28 +126,26 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
       'address_line1' => $params['street_address'],
       'address_state' => $params['state_province'],
       'address_zip' => $params['postal_code'],
-      //'address_country' => $params['country']
     );
+    */
     
     //Create a new Customer in Stripe
-    if(!isset($customer_query_res)) {
+    if(!isset($customer_query)) {
       $stripe_customer = Stripe_Customer::create(array(
-  		'description' => 'Donor from CiviCRM',
+  		'description' => 'Payment from CiviCRM',
   		'card' => $card_details,
         'email' => $email,
       ));
       
       //Store the relationship between CiviCRM's email address for the Contact & Stripe's Customer ID
       if(isset($stripe_customer)) {
-        $stripe_customer_id = $stripe_customer->id;
-        CRM_Core_DAO::executeQuery("INSERT INTO civicrm_stripe_customers (email, id) VALUES ('$email', '$stripe_customer_id')");
+        CRM_Core_DAO::executeQuery("INSERT INTO civicrm_stripe_customers (email, id) VALUES ('$email', '$stripe_customer->id')");
       } else {
         CRM_Core_Error::fatal(ts('There was an error saving new customer within Stripe.  Is Stripe down?'));
       }
     } else {
-      $stripe_customer = Stripe_Customer::retrieve($customer_query_res);
+      $stripe_customer = Stripe_Customer::retrieve($customer_query);
       if(!empty($stripe_customer)) {
-        $stripe_customer_id = $customer_query_res;
         $stripe_customer->card = $card_details;
         $stripe_customer->save();
       } else {
@@ -147,9 +158,8 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
         //Somehow a customer ID saved in the system no longer pairs with a Customer within Stripe.  (Perhaps deleted using Stripe interface?) 
         //Store the relationship between CiviCRM's email address for the Contact & Stripe's Customer ID
         if(isset($stripe_customer)) {
-          $stripe_customer_id = $stripe_customer->id;
           CRM_Core_DAO::executeQuery("DELETE FROM civicrm_stripe_customers WHERE email = '$email'");
-          CRM_Core_DAO::executeQuery("INSERT INTO civicrm_stripe_customers (email, id) VALUES ('$email', '$stripe_customer_id')");
+          CRM_Core_DAO::executeQuery("INSERT INTO civicrm_stripe_customers (email, id) VALUES ('$email', '$stripe_customer->id')");
         } else {
           CRM_Core_Error::fatal(ts('There was an error saving new customer within Stripe.  Is Stripe down?'));
         }
@@ -164,15 +174,15 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
     );
 
     //Use Stripe Customer if we have a valid one.  Otherwise just use the card.
-    if(!empty($stripe_customer_id)) {
-      $stripe_charge['customer'] = $stripe_customer_id;
+    if(!empty($stripe_customer->id)) {
+      $stripe_charge['customer'] = $stripe_customer->id;
     } else {
       $stripe_charge['card'] = $card_details;
     }
     
     //Handle recurring payments in doRecurPayment().
     if (CRM_Utils_Array::value('is_recur', $params) && $params['contributionRecurID']) {
-      return $this->doRecurPayment($params, $amount, $stripe_customer, $card_details);
+      return $this->doRecurPayment($params, $amount, $stripe_customer);
     }
        
     //Fire away!
@@ -182,15 +192,14 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
     return $params;
   }
   
-  function doRecurPayment(&$params, $amount, $stripe_customer, $card_details) {
+  function doRecurPayment(&$params, $amount, $stripe_customer) {
     $frequency = $params['frequency_unit'];
     $installments = $params['installments'];
     $plan_id = "$frequency-$amount";
     
-    $stripe_plan_query = "SELECT plan_id FROM civicrm_stripe_plans WHERE plan_id = '$plan_id'";
-    $stripe_plan_query_res = CRM_Core_DAO::singleValueQuery($stripe_plan_query);
-    
-    if(!isset($stripe_plan_query_res)) {
+    $stripe_plan_query = CRM_Core_DAO::singleValueQuery("SELECT plan_id FROM civicrm_stripe_plans WHERE plan_id = '$plan_id'");
+
+    if(!isset($stripe_plan_query)) {
       $formatted_amount =  "$" . number_format(($amount / 100), 2);
       //Create a new Plan
       $stripe_plan = Stripe_Plan::create(array( 
@@ -199,19 +208,28 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
       	"name" => "CiviCRM $frequency" . 'ly ' . $formatted_amount,
       	"currency" => "usd",
       	"id" => $plan_id));
-      $new_plan_insert = "INSERT INTO civicrm_stripe_plans (plan_id) VALUES ('$plan_id')";
-      CRM_Core_DAO::executeQuery($new_plan_insert);
+      CRM_Core_DAO::executeQuery("INSERT INTO civicrm_stripe_plans (plan_id) VALUES ('$plan_id')");
     }
     
     //Attach the Subscription to the Stripe Customer
-    $stripe_response = $stripe_customer->updateSubscription(array('prorate' => FALSE, 'plan' => $plan_id, 'card' => $card_details));
+    $stripe_response = $stripe_customer->updateSubscription(array('prorate' => FALSE, 'plan' => $plan_id));
     
+    $existing_subscription_query = CRM_Core_DAO::singleValueQuery("SELECT invoice_id FROM civicrm_stripe_subscriptions WHERE customer_id = '$stripe_customer->id'");
+    if(!empty($existing_subscription_query)) {
+      //Cancel existing Recurring Contribution in CiviCRM
+      $cancel_date = date("Y-m-d H:i:s");
+      CRM_Core_DAO::executeQuery("UPDATE civicrm_contribution_recur SET cancel_date = '$cancel_date', contribution_status_id = '3' WHERE invoice_id = '$existing_subscription_query'");
+      //Delete the Stripe Subscription from our cron watch list.
+      CRM_Core_DAO::executeQuery("DELETE FROM civicrm_stripe_subscriptions WHERE invoice_id = '$existing_subscription_query'");
+    }
+
     //Calculate timestamp for the last installment
     $end_time = strtotime("+$installments $frequency");
-    $new_subscription_insert = "INSERT INTO civicrm_stripe_subscriptions (customer_id, plan_id, end_time) VALUES ('$stripe_customer->id', '$plan_id', '$end_time')";
-    CRM_Core_DAO::executeQuery($new_subscription_insert);
+    $invoice_id = $params['invoiceID'];
+    CRM_Core_DAO::executeQuery("INSERT INTO civicrm_stripe_subscriptions (customer_id, invoice_id, end_time) VALUES ('$stripe_customer->id', '$invoice_id', '$end_time')");
     
-    $params['trxn_id'] = $plan_id . ' ' . $stripe_response->start;
+    $trxn_id = $stripe_customer->id . '-' . $end_time;
+    $params['trxn_id'] = $trxn_id;
     
     return $params;
   }
diff --git a/README.txt b/README.txt
index 39b5aeab..1fab60f0 100644
--- a/README.txt
+++ b/README.txt
@@ -1,13 +1,38 @@
+------------
+Important Note:
+
+This version is for CiviCRM 4.1 and prior.
+It will work for CiviCRM 4.2+ but there will be a new version to utilize all the new features surrounding Payment Processors in CiviCRM 4.2.
+This currently includes everything you need minus a cron file to cancel recurring contributions.  Do not allow recurring just yet!
+
+You also need a corresponding module for your CMS.  Here is where the modules can be found:
+Drupal:  git clone --recursive --branch master http://git.drupal.org/sandbox/drastik/1719796.git civicrm_stripe
+Joomla:  TBD
+WordPress:  TBD 
+
+------------
+
 Installing Stripe as a payment processor in CiviCRM 4.x
 
-Folder structure is left in tact, but there is only 1 file and this is where it goes:  
-Stripe.php in civicrm/CRM/Core/Payment/Stripe.php
+Folder structure is left in tact.  
+Place Stripe.php in civicrm/CRM/Core/Payment/Stripe.php
+
+Place civicrm_templates folder anywhere and inform CiviCRM of your "Custom Templates" location in this admin page:  site.com/civicrm/admin/setting/path
 
 Copy Stripe's PHP library folder 'stripe-php' to civicrm/packages/stripe-php  
 You can get Stripe's PHP library here: https://github.com/stripe/stripe-php
 
-Run the included SQL file "civicrm_stripe.sql" to:  
+Run the included SQL file "civicrm_stripe.sql" to handle the DB-related needs.  It will:  
 Insert Stripe into civicrm_payment_processor_type (makes it available as an option within CiviCRM's payment processor settings)
-Create table civicrm_stripe_customers  
-Create table civicrm_stripe_plans
-Create table civicrm_stripe_subscriptions
\ No newline at end of file
+It will create the required tables:
+civicrm_stripe_customers
+civicrm_stripe_plans
+civicrm_stripe_subscriptions
+
+------------
+Note:
+
+This will be packaged as a "CiviCRM Extension" shortly for an alternative installation method.
+In <CiviCRM 4.2, you will need to create a cron job in order for recurring contributions to be properly ended.
+
+------------
\ No newline at end of file
diff --git a/civicrm_stripe.sql b/civicrm_stripe.sql
index 08876861..75cc2b9d 100644
--- a/civicrm_stripe.sql
+++ b/civicrm_stripe.sql
@@ -29,7 +29,7 @@ CREATE TABLE IF NOT EXISTS `civicrm_stripe_plans` (
 
 CREATE TABLE IF NOT EXISTS `civicrm_stripe_subscriptions` (
   `customer_id` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
-  `plan_id` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
+  `invoice_id` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
   `end_time` int(11) NOT NULL DEFAULT '0',
   KEY `end_time` (`end_time`)
 ) DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
diff --git a/civicrm_templates/CRM/Core/BillingBlock.tpl b/civicrm_templates/CRM/Core/BillingBlock.tpl
new file mode 100644
index 00000000..96a007b2
--- /dev/null
+++ b/civicrm_templates/CRM/Core/BillingBlock.tpl
@@ -0,0 +1,221 @@
+{*
+ +--------------------------------------------------------------------+
+ | CiviCRM version 4.1                                                |
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC (c) 2004-2011                                |
+ +--------------------------------------------------------------------+
+ | This file is a part of CiviCRM.                                    |
+ |                                                                    |
+ | CiviCRM is free software; you can copy, modify, and distribute it  |
+ | under the terms of the GNU Affero General Public License           |
+ | Version 3, 19 November 2007 and the CiviCRM Licensing Exception.   |
+ |                                                                    |
+ | CiviCRM is distributed in the hope that it will be useful, but     |
+ | WITHOUT ANY WARRANTY; without even the implied warranty of         |
+ | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.               |
+ | See the GNU Affero General Public License for more details.        |
+ |                                                                    |
+ | You should have received a copy of the GNU Affero General Public   |
+ | License and the CiviCRM Licensing Exception along                  |
+ | with this program; if not, contact CiviCRM LLC                     |
+ | at info[AT]civicrm[DOT]org. If you have questions about the        |
+ | GNU Affero General Public License or the licensing of CiviCRM,     |
+ | see the CiviCRM license FAQ at http://civicrm.org/licensing        |
+ +--------------------------------------------------------------------+
+*}
+{if $form.credit_card_number or $form.bank_account_number}
+<!-- START Stripe -->
+  {if $paymentProcessor.payment_processor_type == 'Stripe'}
+
+    <script type="text/javascript" src="https://js.stripe.com/v1/"></script>
+  	<script type="text/javascript">
+  	var stripe_publishable_key = '{$paymentProcessor.password}';
+  	
+	{literal}
+	  cj(function() {
+		cj(document).ready(function(){
+		  //Identify the payment form.  Don't reference by form#id since it changes between payment pages (Contribution / Event / etc).
+		  cj("#crm-container>form").addClass('stripe-payment-form');
+		  Stripe.setPublishableKey(stripe_publishable_key);
+		  cj("form.stripe-payment-form").submit(function(event) {
+    	    // disable the submit button to prevent repeated clicks
+    	  	cj('form.stripe-payment-form input.form-submit').attr("disabled", "disabled");
+
+    	 	Stripe.createToken({
+        	  number: cj('#credit_card_number').val(),
+        	  cvc: cj('#cvv2').val(),
+        	  exp_month: cj('#credit_card_exp_date\\[M\\]').val(),
+        	  exp_year: cj('#credit_card_exp_date\\[Y\\]').val()
+    	  	}, stripeResponseHandler);
+
+    	  	// prevent the form from submitting with the default action
+    	  	return false;
+  		  });
+		});
+		
+		//Response from Stripe.createToken.
+		function stripeResponseHandler(status, response) {		  
+		  if (response.error) {
+        	// show the errors on the form
+			cj("form.stripe-payment-form").prepend('<div class="messages crm-error">'
+				+'<strong>Payment Error Response:</strong>'
+	   			  +'<ul id="errorList">'
+	         		+'<li>Error: ' + response.error.message + '</li>'
+	   			  +'</ul>'
+   				+'</div>');
+
+        	cj('form.stripe-payment-form input.form-submit').removeAttr("disabled");
+
+    	  } else {
+        	var token = response['id'];
+        	// Update form with the token & submit
+        	cj("input#stripe-token").val(token);
+        	cj("form.stripe-payment-form").get(0).submit();
+    	  }
+		}
+	  });
+    {/literal}
+    </script>
+  {/if}
+<!-- END Stripe -->
+    <div id="payment_information">
+        <fieldset class="billing_mode-group {if $paymentProcessor.payment_type & 2}direct_debit_info-group{else}credit_card_info-group{/if}">
+            <legend>
+               {if $paymentProcessor.payment_type & 2}
+                    {ts}Direct Debit Information{/ts}
+               {else}
+                   {ts}Credit Card Information{/ts}
+               {/if}
+            </legend> 
+            {if $paymentProcessor.billing_mode & 2 and !$hidePayPalExpress }
+            <div class="crm-section no-label paypal_button_info-section">	
+			    <div class="content description">
+			        {ts}If you have a PayPal account, you can click the PayPal button to continue. Otherwise, fill in the credit card and billing information on this form and click <strong>Continue</strong> at the bottom of the page.{/ts}
+				</div>
+			</div>
+			 <div class="crm-section no-label {$form.$expressButtonName.name}-section">	
+			    <div class="content description">
+			        {$form.$expressButtonName.html}
+			        <div class="description">Save time. Checkout securely. Pay without sharing your financial information. </div>
+				</div>
+			</div>
+            {/if} 
+
+            {if $paymentProcessor.billing_mode & 1}
+                <div class="crm-section billing_mode-section {if $paymentProcessor.payment_type & 2}direct_debit_info-section{else}credit_card_info-section{/if}">
+                   {if $paymentProcessor.payment_type & 2}
+                        <div class="crm-section {$form.account_holder.name}-section">	
+							<div class="label">{$form.account_holder.label}</div>
+                            <div class="content">{$form.account_holder.html}</div>
+                            <div class="clear"></div> 
+                        </div>
+                        <div class="crm-section {$form.bank_account_number.name}-section">	
+							<div class="label">{$form.bank_account_number.label}</div>
+                            <div class="content">{$form.bank_account_number.html}</div>
+                            <div class="clear"></div> 
+                        </div>
+                        <div class="crm-section {$form.bank_identification_number.name}-section">	
+							<div class="label">{$form.bank_identification_number.label}</div>
+                            <div class="content">{$form.bank_identification_number.html}</div>
+                            <div class="clear"></div> 
+                        </div>
+                        <div class="crm-section {$form.bank_name.name}-section">	
+							<div class="label">{$form.bank_name.label}</div>
+                            <div class="content">{$form.bank_name.html}</div>
+                            <div class="clear"></div> 
+                        </div>
+                   {else}
+                		<div class="crm-section {$form.credit_card_type.name}-section">	
+							<div class="label">{$form.credit_card_type.label}</div>
+                			<div class="content">{$form.credit_card_type.html}</div>
+                			<div class="clear"></div> 
+                		</div>
+                		<div class="crm-section {$form.credit_card_number.name}-section">	
+							<div class="label">{$form.credit_card_number.label}</div>
+                			<div class="content">{$form.credit_card_number.html}
+                				<div class="description">{ts}Enter numbers only, no spaces or dashes.{/ts}</div>
+                			</div>
+                			<div class="clear"></div> 
+                		</div>
+                		<div class="crm-section {$form.cvv2.name}-section">	
+							<div class="label">{$form.cvv2.label}</div>
+                			<div class="content">
+                				{$form.cvv2.html}
+                				<img src="{$config->resourceBase}i/mini_cvv2.gif" alt="{ts}Security Code Location on Credit Card{/ts}" style="vertical-align: text-bottom;" />
+                				<div class="description">{ts}Usually the last 3-4 digits in the signature area on the back of the card.{/ts}</div>
+                			</div>
+                			<div class="clear"></div> 
+                		</div>
+                		<div class="crm-section {$form.credit_card_exp_date.name}-section">	
+							<div class="label">{$form.credit_card_exp_date.label}</div>
+                			<div class="content">{$form.credit_card_exp_date.html}</div>
+                			<div class="clear"></div> 
+                		</div>
+                    {/if}
+                </div>
+                </fieldset>
+
+                <fieldset class="billing_name_address-group">
+                	<legend>{ts}Billing Name and Address{/ts}</legend>
+                    <div class="crm-section billing_name_address-section">
+                        <div class="crm-section billingNameInfo-section">	
+                        	<div class="content description">
+                        	  {if $paymentProcessor.payment_type & 2}
+                        	     {ts}Enter the name of the account holder, and the corresponding billing address.{/ts}
+                        	  {else}
+                        	     {ts}Enter the name as shown on your credit or debit card, and the billing address for this card.{/ts}
+                        	  {/if}
+                        	</div>
+                        </div>
+                        <div class="crm-section {$form.billing_first_name.name}-section">	
+							<div class="label">{$form.billing_first_name.label}</div>
+                            <div class="content">{$form.billing_first_name.html}</div>
+                            <div class="clear"></div> 
+                        </div>
+                        <div class="crm-section {$form.billing_middle_name.name}-section">	
+							<div class="label">{$form.billing_middle_name.label}</div>
+                            <div class="content">{$form.billing_middle_name.html}</div>
+                            <div class="clear"></div> 
+                        </div>
+                        <div class="crm-section {$form.billing_last_name.name}-section">	
+							<div class="label">{$form.billing_last_name.label}</div>
+                            <div class="content">{$form.billing_last_name.html}</div>
+                            <div class="clear"></div> 
+                        </div>
+                        {assign var=n value=billing_street_address-$bltID}
+                        <div class="crm-section {$form.$n.name}-section">	
+							<div class="label">{$form.$n.label}</div>
+                            <div class="content">{$form.$n.html}</div>
+                            <div class="clear"></div> 
+                        </div>
+                        {assign var=n value=billing_city-$bltID}
+                        <div class="crm-section {$form.$n.name}-section">	
+							<div class="label">{$form.$n.label}</div>
+                            <div class="content">{$form.$n.html}</div>
+                            <div class="clear"></div> 
+                        </div>
+                        {assign var=n value=billing_country_id-$bltID}
+                        <div class="crm-section {$form.$n.name}-section">	
+							<div class="label">{$form.$n.label}</div>
+                            <div class="content">{$form.$n.html|crmReplace:class:big}</div>
+                            <div class="clear"></div> 
+                        </div>
+                        {assign var=n value=billing_state_province_id-$bltID}
+                        <div class="crm-section {$form.$n.name}-section">	
+							<div class="label">{$form.$n.label}</div>
+                            <div class="content">{$form.$n.html|crmReplace:class:big}</div>
+                            <div class="clear"></div> 
+                        </div>
+                        {assign var=n value=billing_postal_code-$bltID}
+                        <div class="crm-section {$form.$n.name}-section">	
+							<div class="label">{$form.$n.label}</div>
+                            <div class="content">{$form.$n.html}</div>
+                            <div class="clear"></div> 
+                        </div>
+                    </div>
+                </fieldset>
+            {else}
+                </fieldset>
+            {/if}
+    </div>
+{/if}
\ No newline at end of file
-- 
GitLab