Skip to content
Snippets Groups Projects
Commit 2e06edb4 authored by mattwire's avatar mattwire
Browse files

Refactor error handling and handle case where customer exists in CiviCRM but not in stripe

parent 9fa92ebd
Branches
Tags
No related merge requests found
......@@ -103,17 +103,38 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
* Check if return from stripeCatchErrors was an error object
* that should be passed back to original api caller.
*
* @param $stripeReturn
* @param array $err
* The return from a call to stripeCatchErrors
*
* @return bool
*
*/
public function isErrorReturn($stripeReturn) {
if (is_object($stripeReturn) && get_class($stripeReturn) == 'CRM_Core_Error') {
public function isErrorReturn($err) {
if (!empty($err['is_error'])) {
return TRUE;
}
return FALSE;
else {
return FALSE;
}
}
/**
* Handle an error from Stripe API and notify the user
*
* @param array $err
* @param string $bounceURL
*
* @return string errorMessage (or statusbounce if URL is specified)
*/
public function handleErrorNotification($err, $bounceURL = NULL) {
$errorMessage = 'Oops! Looks like there was an error. Payment Response: <br />' .
'Type: ' . $err['type'] . '<br />' .
'Code: ' . $err['code'] . '<br />' .
'Message: ' . $err['message'] . '<br />';
if ($bounceURL) {
CRM_Core_Error::statusBounce($errorMessage, $bounceURL);
}
return $errorMessage;
}
/**
......@@ -132,7 +153,6 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
* @throws \CiviCRM_API3_Exception
*/
public function stripeCatchErrors($op = 'create_customer', $stripe_params, $params, $ignores = array()) {
$error_url = $params['stripe_error_url'];
$return = FALSE;
// Check for errors before trying to submit.
try {
......@@ -184,18 +204,13 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
}
$this->logStripeException($op, $e);
$error_message = '';
// Since it's a decline, Stripe_CardError will be caught
$body = $e->getJsonBody();
$err = $body['error'];
if (!isset($err['code'])) {
$err['code'] = null;
// A "fake" error code
$err['code'] = 9000;
}
//$error_message .= 'Status is: ' . $e->getHttpStatus() . "<br />";
////$error_message .= 'Param is: ' . $err['param'] . "<br />";
$error_message .= 'Type: ' . $err['type'] . '<br />';
$error_message .= 'Code: ' . $err['code'] . '<br />';
$error_message .= 'Message: ' . $err['message'] . '<br />';
if (is_a($e, 'Stripe_CardError')) {
civicrm_api3('Note', 'create', array(
......@@ -207,23 +222,9 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
));
}
if (isset($error_url)) {
// Redirect to first page of form and present error.
CRM_Core_Error::statusBounce("Oops! Looks like there was an error. Payment Response:
<br /> {$error_message}", $error_url);
}
else {
// Don't have return url - return error object to api
$core_err = CRM_Core_Error::singleton();
$message = 'Oops! Looks like there was an error. Payment Response: <br />' . $error_message;
if ($err['code']) {
$core_err->push($err['code'], 0, NULL, $message);
}
else {
$core_err->push(9000, 0, NULL, 'Unknown Error: ' . $message);
}
return $core_err;
}
// Flag to detect error return
$err['is_error'] = TRUE;
return $err;
}
return $return;
......@@ -423,13 +424,13 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
if (!(array_key_exists('qfKey', $params))) {
// Probably not called from a civicrm form (e.g. webform) -
// will return error object to original api caller.
$params['stripe_error_url'] = $error_url = null;
$params['stripe_error_url'] = NULL;
}
else {
$qfKey = $params['qfKey'];
$parsed_url = parse_url($params['entryURL']);
$url_path = substr($parsed_url['path'], 1);
$params['stripe_error_url'] = $error_url = CRM_Utils_System::url($url_path,
$params['stripe_error_url'] = CRM_Utils_System::url($url_path,
$parsed_url['query'] . "&_qf_Main_display=1&qfKey={$qfKey}", FALSE, NULL, FALSE);
}
......@@ -465,16 +466,24 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
$stripeCustomerId = CRM_Stripe_Customer::find($customerParams);
// Customer not in civicrm_stripe database. Create a new Customer in Stripe.
// Customer not in civicrm database. Create a new Customer in Stripe.
if (!isset($stripeCustomerId)) {
$stripe_customer = CRM_Stripe_Customer::create($customerParams, $this);
$stripeCustomer = CRM_Stripe_Customer::create($customerParams, $this);
}
else {
// Customer was found in civicrm_stripe database, fetch from Stripe.
$stripe_customer = $this->stripeCatchErrors('retrieve_customer', $stripeCustomerId, $params);
if (!empty($stripe_customer)) {
if ($this->isErrorReturn($stripe_customer)) {
return $stripe_customer;
// Customer was found in civicrm database, fetch from Stripe.
$stripeCustomer = $this->stripeCatchErrors('retrieve_customer', $stripeCustomerId, $params);
if (!empty($stripeCustomer)) {
if ($this->isErrorReturn($stripeCustomer)) {
if (($stripeCustomer['type'] == 'invalid_request_error') && ($stripeCustomer['code'] == 'resource_missing')) {
// Customer doesn't exist, create a new one
$stripeCustomer = CRM_Stripe_Customer::create($customerParams, $this);
}
if ($this->isErrorReturn($stripeCustomer)) {
// We still failed to create a customer
self::handleErrorNotification($stripeCustomer, $params['stripe_error_url']);
return $stripeCustomer;
}
}
// Avoid the 'use same token twice' issue while still using latest card.
if (!empty($params['is_secondary_financial_transaction'])) {
......@@ -483,9 +492,10 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
// Don't update customer again or we will get "token_already_used" error from Stripe.
}
else {
$stripe_customer->card = $card_token;
$response = $this->stripeCatchErrors('save', $stripe_customer, $params);
$stripeCustomer->card = $card_token;
$response = $this->stripeCatchErrors('save', $stripeCustomer, $params);
if (isset($response) && $this->isErrorReturn($response)) {
self::handleErrorNotification($response, $params['stripe_error_url']);
return $response;
}
}
......@@ -494,7 +504,7 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
// Customer was found in civicrm_stripe database, but not in Stripe.
// Delete existing customer record from CiviCRM and create a new customer
CRM_Stripe_Customer::delete($customerParams);
$stripe_customer = CRM_Stripe_Customer::create($customerParams, $this);
$stripeCustomer = CRM_Stripe_Customer::create($customerParams, $this);
}
}
......@@ -511,8 +521,8 @@ 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($stripeCustomer->id)) {
$stripe_charge['customer'] = $stripeCustomer->id;
}
else {
$stripe_charge['card'] = $card_token;
......@@ -520,32 +530,34 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
// Handle recurring payments in doRecurPayment().
if (CRM_Utils_Array::value('is_recur', $params) && $params['contributionRecurID']) {
return $this->doRecurPayment($params, $amount, $stripe_customer);
return $this->doRecurPayment($params, $amount, $stripeCustomer);
}
// Fire away! Check for errors before trying to submit.
$stripe_response = $this->stripeCatchErrors('charge', $stripe_charge, $params);
if (!empty($stripe_response)) {
if ($this->isErrorReturn($stripe_response)) {
return $stripe_response;
$stripeCharge = $this->stripeCatchErrors('charge', $stripe_charge, $params);
if (!empty($stripeCharge)) {
if ($this->isErrorReturn($stripeCharge)) {
self::handleErrorNotification($stripeCharge, $params['stripe_error_url']);
return $stripeCharge;
}
// Success! Return some values for CiviCRM.
$params['trxn_id'] = $stripe_response->id;
$params['trxn_id'] = $stripeCharge->id;
// Return fees & net amount for Civi reporting.
// Uses new Balance Trasaction object.
$balance_transaction = $this->stripeCatchErrors('retrieve_balance_transaction', $stripe_response->balance_transaction, $params);
if (!empty($balance_transaction)) {
if ($this->isErrorReturn($balance_transaction)) {
return $balance_transaction;
$balanceTransaction = $this->stripeCatchErrors('retrieve_balance_transaction', $stripeCharge->balance_transaction, $params);
if (!empty($balanceTransaction)) {
if ($this->isErrorReturn($balanceTransaction)) {
self::handleErrorNotification($balanceTransaction, $params['stripe_error_url']);
return $balanceTransaction;
}
$params['fee_amount'] = $balance_transaction->fee / 100;
$params['net_amount'] = $balance_transaction->net / 100;
$params['fee_amount'] = $balanceTransaction->fee / 100;
$params['net_amount'] = $balanceTransaction->net / 100;
}
}
else {
// There was no response from Stripe on the create charge command.
if (isset($error_url)) {
CRM_Core_Error::statusBounce('Stripe transaction response not received! Check the Logs section of your stripe.com account.', $error_url);
if (isset($params['stripe_error_url'])) {
CRM_Core_Error::statusBounce('Stripe transaction response not received! Check the Logs section of your stripe.com account.', $params['stripe_error_url']);
}
else {
// Don't have return url - return error object to api
......@@ -566,7 +578,7 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
* Assoc array of input parameters for this transaction.
* @param int $amount
* Transaction amount in USD cents.
* @param object $stripe_customer
* @param object $stripeCustomer
* Stripe customer object generated by Stripe API.
*
* @return array
......@@ -574,7 +586,7 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
*
* @throws \CiviCRM_API3_Exception
*/
public function doRecurPayment(&$params, $amount, $stripe_customer) {
public function doRecurPayment(&$params, $amount, $stripeCustomer) {
// Get recurring contrib properties.
$frequency = $params['frequency_unit'];
$frequency_interval = (empty($params['frequency_interval']) ? 1 : $params['frequency_interval']);
......@@ -636,8 +648,8 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
}
}
// Apply the disount through a negative balance.
$stripe_customer->account_balance = -$discount_in_cents;
$stripe_customer->save();
$stripeCustomer->account_balance = -$discount_in_cents;
$stripeCustomer->save();
}
// Tying a plan to a membership (or priceset->membership) makes it possible
......@@ -727,14 +739,14 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
'prorate' => FALSE,
'plan' => $plan_id,
);
$stripe_response = $stripe_customer->subscriptions->create($cust_sub_params);
$subscription_id = $stripe_response->id;
$stripeSubscription = $stripeCustomer->subscriptions->create($cust_sub_params);
$subscription_id = $stripeSubscription->id;
$recuring_contribution_id = $params['contributionRecurID'];
// Prepare escaped query params.
$query_params = array(
1 => array($subscription_id, 'String'),
2 => array($stripe_customer->id, 'String'),
2 => array($stripeCustomer->id, 'String'),
3 => array($recuring_contribution_id, 'String'),
4 => array($this->_paymentProcessor['id'], 'Integer'),
);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment