Commit 116560e7 authored by mattwire's avatar mattwire

Merge branch 'webform_detect' into 'master'

Bugfixes - detect webform correctly, fix stripe customer creation in test mode

See merge request extensions/stripe!23
parents cc384463 e9d7a603
......@@ -381,12 +381,13 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
*
* @param array $params
* Assoc array of input parameters for this transaction.
*
* @param string $component
*
* @return array
* Result array
*
* @throws \CRM_Core_Exception
* @throws \CiviCRM_API3_Exception
* @throws \Civi\Payment\Exception\PaymentProcessorException
*/
public function doPayment(&$params, $component = 'contribute') {
......
......@@ -13,7 +13,7 @@ class CRM_Stripe_Customer {
public static function find($params) {
$requiredParams = ['is_live', 'processor_id'];
foreach ($requiredParams as $required) {
if (empty($required)) {
if (empty($params[$required])) {
throw new \Civi\Payment\Exception\PaymentProcessorException('Stripe Customer (find): Missing required parameter: ' . $required);
}
}
......@@ -32,6 +32,60 @@ class CRM_Stripe_Customer {
WHERE contact_id = %1 AND is_live = %2 AND processor_id = %3", $queryParams);
}
/**
* Find the details (contact_id, is_live, processor_id) for an existing Stripe customer in the CiviCRM database
*
* @param string $stripeCustomerId
*
* @return array|null
*/
public static function getParamsForCustomerId($stripeCustomerId) {
$queryParams = [
1 => [$stripeCustomerId, 'String'],
];
$dao = CRM_Core_DAO::executeQuery("SELECT contact_id, is_live, processor_id
FROM civicrm_stripe_customers
WHERE id = %1", $queryParams);
$dao->fetch();
return [
'contact_id' => $dao->contact_id,
'is_live' => $dao->is_live,
'processor_id' => $dao->processor_id,
];
}
/**
* Find the details (contact_id, is_live, processor_id) for an existing Stripe customer in the CiviCRM database
*
* @param string $stripeCustomerId
*
* @return array|null
*/
public static function getAll($isLive, $processorId, $options = []) {
$queryParams = [
1 => [$isLive ? 1 : 0, 'Boolean'],
2 => [$processorId, 'Integer'],
];
$limitClause = '';
if ($limit = CRM_Utils_Array::value('limit', $options)) {
$limitClause = "LIMIT $limit";
if ($offset = CRM_Utils_Array::value('offset', $options)) {
$limitClause .= " OFFSET $offset";
}
}
$customerIds = [];
$dao = CRM_Core_DAO::executeQuery("SELECT id
FROM civicrm_stripe_customers
WHERE is_live = %1 AND processor_id = %2 {$limitClause}", $queryParams);
while ($dao->fetch()) {
$customerIds[] = $dao->id;
}
return $customerIds;
}
/**
* Add a new Stripe customer to the CiviCRM database
*
......@@ -42,7 +96,7 @@ class CRM_Stripe_Customer {
public static function add($params) {
$requiredParams = ['contact_id', 'customer_id', 'is_live', 'processor_id'];
foreach ($requiredParams as $required) {
if (empty($required)) {
if (empty($params[$required])) {
throw new \Civi\Payment\Exception\PaymentProcessorException('Stripe Customer (add): Missing required parameter: ' . $required);
}
}
......@@ -50,7 +104,7 @@ class CRM_Stripe_Customer {
$queryParams = [
1 => [$params['contact_id'], 'String'],
2 => [$params['customer_id'], 'String'],
3 => [$params['is_live'], 'Boolean'],
3 => [$params['is_live'] ? 1 : 0, 'Boolean'],
4 => [$params['processor_id'], 'Integer'],
];
CRM_Core_DAO::executeQuery("INSERT INTO civicrm_stripe_customers
......@@ -69,7 +123,7 @@ class CRM_Stripe_Customer {
$requiredParams = ['contact_id', 'card_token', 'is_live', 'processor_id'];
// $optionalParams = ['email'];
foreach ($requiredParams as $required) {
if (empty($required)) {
if (empty($params[$required])) {
throw new \Civi\Payment\Exception\PaymentProcessorException('Stripe Customer (create): Missing required parameter: ' . $required);
}
}
......@@ -115,20 +169,34 @@ class CRM_Stripe_Customer {
* @throws \Civi\Payment\Exception\PaymentProcessorException
*/
public static function delete($params) {
$requiredParams = ['contact_id', 'is_live', 'processor_id'];
$requiredParams = ['is_live', 'processor_id'];
foreach ($requiredParams as $required) {
if (empty($required)) {
if (empty($params[$required])) {
throw new \Civi\Payment\Exception\PaymentProcessorException('Stripe Customer (delete): Missing required parameter: ' . $required);
}
}
if (empty($params['contact_id']) && empty($params['id'])) {
throw new \Civi\Payment\Exception\PaymentProcessorException('Stripe Customer (delete): Missing required parameter: contact_id or id');
}
$queryParams = [
1 => [$params['contact_id'], 'String'],
2 => [$params['is_live'], 'Boolean'],
3 => [$params['processor_id'], 'Integer'],
];
$sql = "DELETE FROM civicrm_stripe_customers
if (!empty($params['id'])) {
$queryParams = [
1 => [$params['id'], 'String'],
2 => [$params['is_live'] ? 1 : 0, 'Boolean'],
3 => [$params['processor_id'], 'Integer'],
];
$sql = "DELETE FROM civicrm_stripe_customers
WHERE id = %1 AND is_live = %2 AND processor_id = %3";
}
else {
$queryParams = [
1 => [$params['contact_id'], 'String'],
2 => [$params['is_live'] ? 1 : 0, 'Boolean'],
3 => [$params['processor_id'], 'Integer'],
];
$sql = "DELETE FROM civicrm_stripe_customers
WHERE contact_id = %1 AND is_live = %2 AND processor_id = %3";
}
CRM_Core_DAO::executeQuery($sql, $queryParams);
}
......
......@@ -25,7 +25,23 @@ class CRM_Stripe_Utils_Check_Webhook {
$webhook_path = str_replace('NN', $pp['id'], $webhook_path);
\Stripe\Stripe::setApiKey($sk);
$webhooks = \Stripe\WebhookEndpoint::all(["limit" => 100]);
try {
$webhooks = \Stripe\WebhookEndpoint::all(["limit" => 100]);
}
catch (\Stripe\Error\Authentication $e) {
$messages[] = new CRM_Utils_Check_Message(
'stripe_webhook',
E::ts('The %1 (%2) Payment Processor has an invalid API key', [
1 => $pp['name'],
2 => $pp['id'],
]),
E::ts('Stripe - API Key'),
\Psr\Log\LogLevel::ERROR,
'fa-money'
);
continue;
}
if (empty($webhooks->data)) {
$messages[] = new CRM_Utils_Check_Message(
......
......@@ -5,6 +5,8 @@
*
*/
use CRM_Stripe_ExtensionUtil as E;
/**
* StripeCustomer.Get API specification
*
......@@ -79,6 +81,41 @@ function civicrm_api3_stripe_customer_get($params) {
return civicrm_api3_create_success($results);
}
/**
* StripeCustomer.Get API specification
*
* @param array $spec description of fields supported by this API call
* @return void
* @see http://wiki.civicrm.org/confluence/display/CRMDOC/API+Architecture+Standards
*/
function _civicrm_api3_stripe_customer_delete_spec(&$spec) {
$spec['id']['title'] = ts("Stripe Customer ID");
$spec['id']['type'] = CRM_Utils_Type::T_STRING;
$spec['contact_id']['title'] = ts("CiviCRM Contact ID");
$spec['contact_id']['type'] = CRM_Utils_Type::T_INT;
$spec['is_live']['title'] = ts("Is live processor");
$spec['is_live']['type'] = CRM_Utils_Type::T_BOOLEAN;
$spec['is_live']['api.required'] = TRUE;
$spec['processor_id']['title'] = ts("Payment Processor ID");
$spec['processor_id']['type'] = CRM_Utils_Type::T_INT;
$spec['processor_id']['api.required'] = TRUE;
}
/**
* StripeCustomer.delete API
* This api will delete a stripe customer from CiviCRM
*
* @param array $params
* @see civicrm_api3_create_success
*
* @throws \Civi\Payment\Exception\PaymentProcessorException
* @return array
*/
function civicrm_api3_stripe_customer_delete($params) {
CRM_Stripe_Customer::delete($params);
return civicrm_api3_create_success([]);
}
/**
* Stripe.Customer.Updatecontactids API
* This api will update the civicrm_stripe_customers table and add contact IDs for all known email addresses
......@@ -153,3 +190,85 @@ function civicrm_api3_stripe_customer_updatecontactids($params) {
return civicrm_api3_create_success($counts);
}
function _civicrm_api3_stripe_customer_updatestripemetadata_spec(&$spec) {
$spec['id']['title'] = E::ts("Stripe Customer ID");
$spec['id']['description'] = E::ts('If set only this customer will be updated, otherwise we try and update ALL customers');
$spec['id']['type'] = CRM_Utils_Type::T_STRING;
$spec['id']['api.required'] = FALSE;
$spec['dryrun']['api.required'] = TRUE;
$spec['dryrun']['type'] = CRM_Utils_Type::T_BOOLEAN;
$spec['is_live']['api.required'] = FALSE;
$spec['is_live']['type'] = CRM_Utils_Type::T_BOOLEAN;
$spec['processor_id']['api.required'] = FALSE;
$spec['processor_id']['type'] = CRM_Utils_Type::T_INT;
}
/**
* This allows us to update the metadata held by stripe about our CiviCRM payments
* Older versions of stripe extension did not set anything useful in stripe except email
* Now we set a description including the name + metadata holding contact id.
*
* @param $params
*
* @return array
* @throws \CiviCRM_API3_Exception
* @throws \Civi\Payment\Exception\PaymentProcessorException
*/
function civicrm_api3_stripe_customer_updatestripemetadata($params) {
if (!isset($params['dryrun'])) {
throw new CiviCRM_API3_Exception('Missing required parameter dryrun');
}
// Check params
if (empty($params['id'])) {
// We're doing an update on all stripe customers
if (!isset($params['is_live']) || !isset($params['processor_id'])) {
throw new CiviCRM_API3_Exception('Missing required parameters is_live and/or processor_id when using without a customer id');
}
$customerIds = CRM_Stripe_Customer::getAll($params['is_live'], $params['processor_id'], $params['options']);
}
else {
$customerIds = [$params['id']];
}
foreach ($customerIds as $customerId) {
$customerParams = CRM_Stripe_Customer::getParamsForCustomerId($customerId);
if (empty($customerParams['contact_id'])) {
throw new CiviCRM_API3_Exception('Could not find contact ID for stripe customer: ' . $customerId);
}
$paymentProcessor = \Civi\Payment\System::singleton()
->getById($customerParams['processor_id']);
$paymentProcessor->setAPIParams();
// Get the stripe customer from stripe
try {
$stripeCustomer = \Stripe\Customer::retrieve($customerId);
} catch (Exception $e) {
$err = CRM_Core_Payment_Stripe::parseStripeException('retrieve_customer', $e, FALSE);
$errorMessage = CRM_Core_Payment_Stripe::handleErrorNotification($err, NULL);
throw new \Civi\Payment\Exception\PaymentProcessorException('Failed to retrieve Stripe Customer: ' . $errorMessage);
}
// Get the contact display name
$contactDisplayName = civicrm_api3('Contact', 'getvalue', [
'return' => 'display_name',
'id' => $customerParams['contact_id'],
]);
// Currently we set the description and metadata
$stripeCustomerParams = [
'description' => $contactDisplayName . ' (CiviCRM)',
'metadata' => ['civicrm_contact_id' => $customerParams['contact_id']],
];
// Update the stripe customer object at stripe
if (!$params['dryrun']) {
\Stripe\Customer::update($customerId, $stripeCustomerParams);
$results[] = $stripeCustomerParams;
}
else {
$results[] = $stripeCustomerParams;
}
}
return civicrm_api3_create_success($results, $params);
}
......@@ -20,5 +20,6 @@ The api commands are:
### Additionally for upgrading:
* `StripeCustomer.updatecontactids` - Used to migrate civicrm_stripe_customer table to match on contact_id instead of email address.
* `StripeCustomer.updatestripemetadata` - Used to update stripe customers that were created using an older version of the extension (adds name to description and contact_id as a metadata field).
* `StripeSubscription.updatetransactionids` - Used to migrate civicrm_stripe_subscriptions to use recurring contributions directly.
* `StripeSubscription.copytrxnidtoprocessorid` - Used to copy trxn_id to processor_id in civicrm_contribution_recur table so we can use cancelSubscription. Hopefully this won't be needed in future versions of CiviCRM if we can pass more sensible values to the cancelSubscription function.
\ No newline at end of file
## Release 5.4
* Fix drupal webform detection so it doesn't generate a false positive if we also have a webform on the same page.
* Fix Stripe create customer in test mode.
* Fix offline (live) event payments for Wordpress.
* Add StripeCustomer.updatestripemetadata API.
* Add a system check for invalid API key.
* Add StripeCustomer.delete to delete a customer from CiviCRM.
## Release 5.3.2
* Fix retrieving email receipt parameter on stripe IPN which stopped contributions from being marked as completed.
* Fix webhook check for wordpress so we don't get false positives when everything is configured ok.
......
......@@ -12,9 +12,9 @@
<author>Matthew Wire (MJW Consulting)</author>
<email>mjw@mjwconsult.co.uk</email>
</maintainer>
<releaseDate>2019-03-13</releaseDate>
<version>5.3.2</version>
<develStage>stable</develStage>
<releaseDate>2019-03-20</releaseDate>
<version>5.4.dev</version>
<develStage>beta</develStage>
<compatibility>
<ver>5.10</ver>
</compatibility>
......
......@@ -73,8 +73,9 @@ CRM.$(function($) {
$( document ).ajaxComplete(function( event, xhr, settings ) {
// /civicrm/payment/form? occurs when a payproc is selected on page
// /civicrm/contact/view/participant occurs when payproc is first loaded on event credit card payment
if ((settings.url.match("/civicrm/payment/form?"))
|| (settings.url.match("/civicrm/contact/view/participant?"))) {
// On wordpress these are urlencoded
if ((settings.url.match("civicrm(\/|%2F)payment(\/|%2F)form") != null)
|| (settings.url.match("civicrm(\/|\%2F)contact(\/|\%2F)view(\/|\%2F)participant") != null)) {
// See if there is a payment processor selector on this form
// (e.g. an offline credit card contribution page).
if ($('#payment_processor_id').length > 0) {
......@@ -185,7 +186,7 @@ CRM.$(function($) {
}
});
var isWebform = getIsWebform();
var isWebform = getIsWebform($form);
// For CiviCRM Webforms.
if (isWebform) {
......@@ -222,7 +223,7 @@ CRM.$(function($) {
return false;
}
var isWebform = getIsWebform();
var isWebform = getIsWebform($form);
// Handle multiple payment options and Stripe not being chosen.
if (isWebform) {
......@@ -341,17 +342,19 @@ CRM.$(function($) {
}
}
function getIsWebform() {
return $('.webform-client-form').length;
function getIsWebform(form) {
// Pass in the billingForm object
// If the form has the webform-client-form class then it's a drupal webform!
return form.hasClass('webform-client-form');
}
function getBillingForm() {
// If we have a stripe billing form on the page
var $billingForm = $('input#stripe-pub-key').closest('form');
if (!$billingForm.length && getIsWebform()) {
//if (!$billingForm.length && getIsWebform()) {
// If we are in a webform
$billingForm = $('.webform-client-form');
}
// $billingForm = $('.webform-client-form');
//}
if (!$billingForm.length) {
// If we have multiple payment processors to select and stripe is not currently loaded
$billingForm = $('input[name=hidden_processor]').closest('form');
......@@ -361,7 +364,7 @@ CRM.$(function($) {
function getBillingSubmit() {
$form = getBillingForm();
var isWebform = getIsWebform();
var isWebform = getIsWebform($form);
if (isWebform) {
$submit = $form.find('[type="submit"].webform-submit');
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment