Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • extensions/stripe
  • madhavi/stripe
  • wmortada/stripe
  • jamie/stripe
  • michaelmcandrew/stripe
  • bgm/stripe
  • noah/stripe
  • laryn/stripe
  • phani/stripe
  • JonGold/stripe
  • scardinius/stripe
  • varshith/stripe
  • naomi/stripe
  • jhoskins98/stripe
  • artfulrobot/stripe
  • jitendra/stripe
  • justinfreeman/stripe
  • revati_gawas/stripe
  • capo/stripe
  • pradeep/stripe
  • partners/ixiam/stripe
  • homotechsual/stripe
  • Edselopez/stripe
  • goron/stripe
  • tapash/stripe
  • petednz/stripe
  • kartik1000/stripe
  • ananelson/stripe
  • Samuele.Masetto/stripe
  • sluc23/stripe
  • aaron/stripe
  • DaveD/stripe
  • konadave/stripe
  • partners/coopsymbiotic/stripe
  • kurund/stripe
  • AllenShaw/stripe
  • awestbha/stripe
  • mathavan/stripe
  • BjoernE/stripe
  • alietz/stripe
  • seamuslee/stripe
  • damo-civi/stripe
  • dmunio/stripe
  • ufundo/stripe
44 results
Show changes
Commits on Source (27)
Showing
with 1109 additions and 1211 deletions
......@@ -3,18 +3,20 @@
* https://civicrm.org/licensing
*/
use CRM_Stripe_ExtensionUtil as E;
/**
* Class CRM_Core_Payment_Stripe
*/
class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
use CRM_Core_Payment_StripeTrait;
use CRM_Core_Payment_MJWTrait;
/**
*
* @var string
*/
const API_VERSION = '2019-05-16';
const API_VERSION = '2019-09-09';
/**
* Mode of operation: live or test.
......@@ -31,13 +33,14 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
*
* @param string $mode
* The mode of operation: live or test.
* @param array $paymentProcessor
*
* @return void
*/
public function __construct($mode, &$paymentProcessor) {
public function __construct($mode, $paymentProcessor) {
$this->_mode = $mode;
$this->_paymentProcessor = $paymentProcessor;
$this->_processorName = ts('Stripe');
$this->_processorName = E::SHORT_NAME;
}
/**
......@@ -46,7 +49,7 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
* @return string
*/
public static function getSecretKey($paymentProcessor) {
return trim(CRM_Utils_Array::value('user_name', $paymentProcessor));
return trim(CRM_Utils_Array::value('password', $paymentProcessor));
}
/**
......@@ -55,7 +58,7 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
* @return string
*/
public static function getPublicKey($paymentProcessor) {
return trim(CRM_Utils_Array::value('password', $paymentProcessor));
return trim(CRM_Utils_Array::value('user_name', $paymentProcessor));
}
/**
......@@ -105,7 +108,7 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
* The error message if any.
*/
public function checkConfig() {
$error = array();
$error = [];
if (!empty($error)) {
return implode('<p>', $error);
......@@ -120,7 +123,9 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
* @return bool
*/
public function supportsBackOffice() {
return TRUE;
// @fixme Make this work again with stripe elements / 6.0
return FALSE;
// return TRUE;
}
/**
......@@ -131,8 +136,21 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
return FALSE;
}
public function supportsRecurring() {
return TRUE;
}
/**
* Does this payment processor support refund?
*
* @return bool
*/
public function supportsRefund() {
return TRUE;
}
/**
* We can configure a start date for a smartdebit mandate
* Can we set a future recur start date? Stripe allows this but we don't (yet) support it.
* @return bool
*/
public function supportsFutureRecurStartDate() {
......@@ -173,14 +191,8 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
*
* @return string errorMessage (or statusbounce if URL is specified)
*/
public static function handleErrorNotification($err, $bounceURL = NULL) {
$debugMsg = $err['type'] . ' ' . $err['code'] . ' ' . $err['message'];
Civi::log()->debug('Stripe Payment Error: ' . $debugMsg);
if ($bounceURL) {
CRM_Core_Error::statusBounce($err['message'], $bounceURL, 'Payment Error');
}
return $debugMsg;
public function handleErrorNotification($err, $bounceURL = NULL) {
return self::handleError("{$err['type']} {$err['code']}", $err['message'], $bounceURL);
}
/**
......@@ -215,9 +227,6 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
public function createPlan($params, $amount) {
$currency = strtolower($params['currencyID']);
$planId = "every-{$params['frequency_interval']}-{$params['frequency_unit']}-{$amount}-" . $currency;
if (isset($params['membership_type_tag'])) {
$planId = $params['membership_type_tag'] . $planId;
}
if ($this->_paymentProcessor['is_test']) {
$planId .= '-test';
......@@ -236,19 +245,19 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
if ($this->_paymentProcessor['is_test']) {
$productName .= '-test';
}
$product = \Stripe\Product::create(array(
$product = \Stripe\Product::create([
"name" => $productName,
"type" => "service"
));
]);
// Create a new Plan.
$stripePlan = array(
$stripePlan = [
'amount' => $amount,
'interval' => $params['frequency_unit'],
'product' => $product->id,
'currency' => $currency,
'id' => $planId,
'interval_count' => $params['frequency_interval'],
);
];
$plan = \Stripe\Plan::create($stripePlan);
}
}
......@@ -261,15 +270,7 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
* @return array
*/
public function getPaymentFormFields() {
return array(
'credit_card_type',
'credit_card_number',
'cvv2',
'credit_card_exp_date',
'stripe_token',
'stripe_pub_key',
'stripe_id',
);
return [];
}
/**
......@@ -281,86 +282,7 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
* field metadata
*/
public function getPaymentFormFieldsMetadata() {
$creditCardType = array('' => ts('- select -')) + CRM_Contribute_PseudoConstant::creditCard();
return array(
'credit_card_number' => array(
'htmlType' => 'text',
'name' => 'credit_card_number',
'title' => ts('Card Number'),
'cc_field' => TRUE,
'attributes' => array(
'size' => 20,
'maxlength' => 20,
'autocomplete' => 'off',
),
'is_required' => TRUE,
),
'cvv2' => array(
'htmlType' => 'text',
'name' => 'cvv2',
'title' => ts('Security Code'),
'cc_field' => TRUE,
'attributes' => array(
'size' => 5,
'maxlength' => 10,
'autocomplete' => 'off',
),
'is_required' => TRUE,
),
'credit_card_exp_date' => array(
'htmlType' => 'date',
'name' => 'credit_card_exp_date',
'title' => ts('Expiration Date'),
'cc_field' => TRUE,
'attributes' => CRM_Core_SelectValues::date('creditCard'),
'is_required' => TRUE,
'month_field' => 'credit_card_exp_date_M',
'year_field' => 'credit_card_exp_date_Y',
'extra' => ['class' => 'crm-form-select'],
),
'credit_card_type' => array(
'htmlType' => 'select',
'name' => 'credit_card_type',
'title' => ts('Card Type'),
'cc_field' => TRUE,
'attributes' => $creditCardType,
'is_required' => FALSE,
),
'stripe_token' => array(
'htmlType' => 'hidden',
'name' => 'stripe_token',
'title' => 'Stripe Token',
'attributes' => array(
'id' => 'stripe-token',
'class' => 'payproc-metadata',
),
'cc_field' => TRUE,
'is_required' => TRUE,
),
'stripe_id' => array(
'htmlType' => 'hidden',
'name' => 'stripe_id',
'title' => 'Stripe ID',
'attributes' => array(
'id' => 'stripe-id',
'class' => 'payproc-metadata',
),
'cc_field' => TRUE,
'is_required' => TRUE,
),
'stripe_pub_key' => array(
'htmlType' => 'hidden',
'name' => 'stripe_pub_key',
'title' => 'Stripe Public Key',
'attributes' => array(
'id' => 'stripe-pub-key',
'class' => 'payproc-metadata',
),
'cc_field' => TRUE,
'is_required' => TRUE,
),
);
return [];
}
/**
......@@ -394,14 +316,26 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
* @param \CRM_Core_Form $form
*/
public function buildForm(&$form) {
// Set default values
$paymentProcessorId = CRM_Utils_Array::value('id', $form->_paymentProcessor);
$publishableKey = CRM_Core_Payment_Stripe::getPublicKeyById($paymentProcessorId);
$defaults = [
'stripe_id' => $paymentProcessorId,
'stripe_pub_key' => $publishableKey,
$jsVars = [
'id' => $form->_paymentProcessor['id'],
'currency' => $this->getDefaultCurrencyForForm($form),
'billingAddressID' => CRM_Core_BAO_LocationType::getBilling(),
'publishableKey' => CRM_Core_Payment_Stripe::getPublicKeyById($form->_paymentProcessor['id']),
'jsDebug' => TRUE,
];
$form->setDefaults($defaults);
\Civi::resources()->addVars(E::SHORT_NAME, $jsVars);
// Assign to smarty so we can add via Card.tpl for drupal webform because addVars doesn't work in that context
$form->assign('stripeJSVars', $jsVars);
// Add help and javascript
CRM_Core_Region::instance('billing-block')->add(
['template' => 'CRM/Core/Payment/Stripe/Card.tpl', 'weight' => -1]);
// Add CSS via region (it won't load on drupal webform if added via \Civi::resources()->addStyleFile)
CRM_Core_Region::instance('billing-block')->add([
'styleUrl' => \Civi::resources()->getUrl(E::LONG_NAME, 'css/elements.css'),
'weight' => -1,
]);
\Civi::resources()->addScriptFile(E::LONG_NAME, 'js/civicrm_stripe.js');
}
/**
......@@ -422,22 +356,24 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
* @throws \Civi\Payment\Exception\PaymentProcessorException
*/
public function doPayment(&$params, $component = 'contribute') {
if (array_key_exists('credit_card_number', $params)) {
$cc = $params['credit_card_number'];
if (!empty($cc) && substr($cc, 0, 8) != '00000000') {
Civi::log()->debug(ts('ALERT! Unmasked credit card received in back end. Please report this error to the site administrator.'));
}
$params = $this->beginDoPayment($params);
// Get the passed in paymentIntent
if(!empty(CRM_Utils_Array::value('paymentIntentID', $params))) {
$paymentIntentID = CRM_Utils_Array::value('paymentIntentID', $params);
}
elseif (CRM_Core_Session::singleton()->get('stripePaymentIntent')) {
// @fixme Hack for contributionpages - see https://github.com/civicrm/civicrm-core/pull/15252
$paymentIntentID = CRM_Core_Session::singleton()->get('stripePaymentIntent');
$params['paymentIntentID'] = $paymentIntentID;
}
else {
CRM_Core_Error::statusBounce(E::ts('Unable to complete payment! Missing paymentIntent ID.'));
Civi::log()->debug('paymentIntentID not found. $params: ' . print_r($params, TRUE));
}
$completedStatusId = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Completed');
$pendingStatusId = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Pending');
// If we have a $0 amount, skip call to processor and set payment_status to Completed.
if (empty($params['amount'])) {
$params['payment_status_id'] = $completedStatusId;
return $params;
}
$this->setAPIParams();
// Get proper entry URL for returning on error.
......@@ -448,162 +384,135 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
}
else {
$qfKey = $params['qfKey'];
$parsed_url = parse_url($params['entryURL']);
$url_path = substr($parsed_url['path'], 1);
$params['stripe_error_url'] = CRM_Utils_System::url($url_path,
$parsed_url['query'] . "&_qf_Main_display=1&qfKey={$qfKey}", FALSE, NULL, FALSE);
$parsedUrl = parse_url($params['entryURL']);
$urlPath = substr($parsedUrl['path'], 1);
$query = $parsedUrl['query'];
if (strpos($query, '_qf_Main_display=1') === FALSE) {
$query .= '&_qf_Main_display=1';
}
if (strpos($query, 'qfKey=') === FALSE) {
$query .= "&qfKey={$qfKey}";
}
$params['stripe_error_url'] = CRM_Utils_System::url($urlPath, $query, FALSE, NULL, FALSE);
}
$amount = self::getAmount($params);
// Use Stripe.js instead of raw card details.
if (!empty($params['stripe_token'])) {
$card_token = $params['stripe_token'];
}
else if(!empty(CRM_Utils_Array::value('stripe_token', $_POST, NULL))) {
$card_token = CRM_Utils_Array::value('stripe_token', $_POST, NULL);
}
else {
CRM_Core_Error::statusBounce(ts('Unable to complete payment! Please this to the site administrator with a description of what you were trying to do.'));
Civi::log()->debug('Stripe.js token was not passed! Report this message to the site administrator. $params: ' . print_r($params, TRUE));
}
$contactId = $this->getContactId($params);
$email = $this->getBillingEmail($params, $contactId);
// See if we already have a stripe customer
$customerParams = [
'contact_id' => $contactId,
'card_token' => $card_token,
'processor_id' => $this->_paymentProcessor['id'],
'email' => $email,
// Include this to allow redirect within session on payment failure
'stripe_error_url' => $params['stripe_error_url'],
];
// Get the Stripe Customer:
// 1. Look for an existing customer.
// 2. If no customer (or a deleted customer found), create a new one.
// 3. If existing customer found, update the metadata that Stripe holds for this customer.
$stripeCustomerId = CRM_Stripe_Customer::find($customerParams);
// Customer not in civicrm database. Create a new Customer in Stripe.
if (!isset($stripeCustomerId)) {
$stripeCustomer = CRM_Stripe_Customer::create($customerParams);
$stripeCustomer = CRM_Stripe_Customer::create($customerParams, $this);
}
else {
// Customer was found in civicrm database, fetch from Stripe.
$deleteCustomer = FALSE;
try {
$stripeCustomer = \Stripe\Customer::retrieve($stripeCustomerId);
}
catch (Exception $e) {
} catch (Exception $e) {
$err = self::parseStripeException('retrieve_customer', $e, FALSE);
if (($err['type'] == 'invalid_request_error') && ($err['code'] == 'resource_missing')) {
$deleteCustomer = TRUE;
}
$errorMessage = self::handleErrorNotification($err, $params['stripe_error_url']);
throw new \Civi\Payment\Exception\PaymentProcessorException('Failed to create Stripe Charge: ' . $errorMessage);
$errorMessage = $this->handleErrorNotification($err, $params['stripe_error_url']);
throw new \Civi\Payment\Exception\PaymentProcessorException('Failed to retrieve Stripe Customer: ' . $errorMessage);
}
if ($deleteCustomer || $stripeCustomer->isDeleted()) {
if ($stripeCustomer->isDeleted()) {
// Customer doesn't exist, create a new one
CRM_Stripe_Customer::delete($customerParams);
try {
$stripeCustomer = CRM_Stripe_Customer::create($customerParams);
}
catch (Exception $e) {
$stripeCustomer = CRM_Stripe_Customer::create($customerParams, $this);
} catch (Exception $e) {
// We still failed to create a customer
$errorMessage = self::handleErrorNotification($stripeCustomer, $params['stripe_error_url']);
$errorMessage = $this->handleErrorNotification($stripeCustomer, $params['stripe_error_url']);
throw new \Civi\Payment\Exception\PaymentProcessorException('Failed to create Stripe Customer: ' . $errorMessage);
}
}
$stripeCustomer->card = $card_token;
try {
$stripeCustomer->save();
}
catch (Exception $e) {
$err = self::parseStripeException('update_customer', $e, TRUE);
if (($err['type'] == 'invalid_request_error') && ($err['code'] == 'token_already_used')) {
// This error is ok, we've already used the token during create_customer
}
else {
$errorMessage = self::handleErrorNotification($err, $params['stripe_error_url']);
throw new \Civi\Payment\Exception\PaymentProcessorException('Failed to update Stripe Customer: ' . $errorMessage);
}
else {
CRM_Stripe_Customer::updateMetadata($customerParams, $this, $stripeCustomer->id);
}
}
// Prepare the charge array, minus Customer/Card details.
if (empty($params['description'])) {
$params['description'] = ts('Backend Stripe contribution');
}
// Handle recurring payments in doRecurPayment().
if (CRM_Utils_Array::value('is_recur', $params) && $params['contributionRecurID']) {
// We set payment status as pending because the IPN will set it as completed / failed
$params['payment_status_id'] = $pendingStatusId;
return $this->doRecurPayment($params, $amount, $stripeCustomer);
$params['description'] = E::ts('Contribution: %1', [1 => $this->getPaymentProcessorLabel()]);
}
// Stripe charge.
$stripeChargeParams = [
'amount' => $amount,
'currency' => strtolower($params['currencyID']),
'description' => $params['description'] . ' # Invoice ID: ' . CRM_Utils_Array::value('invoiceID', $params),
$contactContribution = $this->getContactId($params) . '-' . ($this->getContributionId($params) ?: 'XX');
$intentParams = [
'customer' => $stripeCustomer->id,
'description' => "{$params['description']} {$contactContribution} #" . CRM_Utils_Array::value('invoiceID', $params),
'statement_descriptor_suffix' => "{$contactContribution} " . substr($params['description'],0,7),
];
$intentParams['statement_descriptor'] = substr("{$contactContribution} " . $params['description'], 0, 22);
$intentMetadata = [
'amount_to_capture' => $this->getAmount($params),
];
// Use Stripe Customer if we have a valid one. Otherwise just use the card.
if (!empty($stripeCustomer->id)) {
$stripeChargeParams['customer'] = $stripeCustomer->id;
}
else {
$stripeChargeParams['card'] = $card_token;
}
// This is where we actually charge the customer
try {
$stripeCharge = \Stripe\Charge::create($stripeChargeParams);
\Stripe\PaymentIntent::update($paymentIntentID, $intentParams);
$intent = \Stripe\PaymentIntent::retrieve($paymentIntentID);
$intent->customer = $stripeCustomer->id;
switch ($intent->status) {
case 'requires_confirmation':
$intent->confirm();
case 'requires_capture':
$intent->capture($intentMetadata);
break;
}
}
catch (Exception $e) {
$err = self::parseStripeException('charge_create', $e, FALSE);
if ($e instanceof \Stripe\Error\Card) {
if ($this->getContributionId($params)) {
civicrm_api3('Note', 'create', [
'entity_id' => $this->getContributionId($params),
'contact_id' => $this->getContactId($params),
'subject' => $err['type'],
'note' => $err['code'],
'entity_table' => 'civicrm_contribution',
]);
}
}
$errorMessage = self::handleErrorNotification($err, $params['stripe_error_url']);
throw new \Civi\Payment\Exception\PaymentProcessorException('Failed to create Stripe Charge: ' . $errorMessage);
$this->handleError($e->getCode(), $e->getMessage(), $params['stripe_error_url']);
}
// Handle recurring payments in doRecurPayment().
if (CRM_Utils_Array::value('is_recur', $params) && $params['contributionRecurID']) {
// This is where we save the customer card
// @todo For a recurring payment we have to save the card. For a single payment we'd like to develop the
// save card functionality but should not save by default as the customer has not agreed.
$paymentMethod = \Stripe\PaymentMethod::retrieve($intent->payment_method);
$paymentMethod->attach(['customer' => $stripeCustomer->id]);
// We set payment status as pending because the IPN will set it as completed / failed
$params['payment_status_id'] = $pendingStatusId;
return $this->doRecurPayment($params, $amount, $stripeCustomer, $paymentMethod);
}
// Return fees & net amount for Civi reporting.
$stripeCharge = $intent->charges->data[0];
try {
$stripeBalanceTransaction = \Stripe\BalanceTransaction::retrieve($stripeCharge->balance_transaction);
}
catch (Exception $e) {
$err = self::parseStripeException('retrieve_balance_transaction', $e, FALSE);
$errorMessage = self::handleErrorNotification($err, $params['stripe_error_url']);
$errorMessage = $this->handleErrorNotification($err, $params['stripe_error_url']);
throw new \Civi\Payment\Exception\PaymentProcessorException('Failed to retrieve Stripe Balance Transaction: ' . $errorMessage);
}
// Success!
// Set the desired contribution status which will be set later (do not set on the contribution here!)
$params['contribution_status_id'] = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Completed');
// For contribution workflow we have a contributionId so we can set parameters directly.
// For events/membership workflow we have to return the parameters and they might get set...
$newParams['trxn_id'] = $stripeCharge->id;
$newParams['payment_status_id'] = $completedStatusId;
// For a single charge there is no stripe invoice.
$this->setPaymentProcessorOrderID($stripeCharge->id);
$this->setPaymentProcessorTrxnID($stripeCharge->id);
$newParams['fee_amount'] = $stripeBalanceTransaction->fee / 100;
$newParams['net_amount'] = $stripeBalanceTransaction->net / 100;
if ($this->getContributionId($params)) {
$newParams['id'] = $this->getContributionId($params);
civicrm_api3('Contribution', 'create', $newParams);
unset($newParams['id']);
}
$params = array_merge($params, $newParams);
return $params;
return $this->endDoPayment($params, $newParams);
}
/**
......@@ -614,8 +523,9 @@ 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 $stripeCustomer
* @param \Stripe\Customer $stripeCustomer
* Stripe customer object generated by Stripe API.
* @param \Stripe\PaymentMethod $stripePaymentMethod
*
* @return array
* The result in a nice formatted array (or an error object).
......@@ -623,7 +533,7 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
* @throws \CiviCRM_API3_Exception
* @throws \CRM_Core_Exception
*/
public function doRecurPayment(&$params, $amount, $stripeCustomer) {
public function doRecurPayment($params, $amount, $stripeCustomer, $stripePaymentMethod) {
$requiredParams = ['contributionRecurID', 'frequency_unit'];
foreach ($requiredParams as $required) {
if (!isset($params[$required])) {
......@@ -635,8 +545,6 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
// Make sure frequency_interval is set (default to 1 if not)
empty($params['frequency_interval']) ? $params['frequency_interval'] = 1 : NULL;
$amount = $this->deprecatedHandleCiviDiscount($params, $amount, $stripeCustomer);
// Create the stripe plan
$planId = self::createPlan($params, $amount);
......@@ -644,16 +552,18 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
$subscriptionParams = [
'prorate' => FALSE,
'plan' => $planId,
'default_payment_method' => $stripePaymentMethod,
];
// Create the stripe subscription for the customer
$stripeSubscription = $stripeCustomer->subscriptions->create($subscriptionParams);
$this->setPaymentProcessorSubscriptionID($stripeSubscription->id);
$recurParams = [
'id' => $params['contributionRecurID'],
'trxn_id' => $stripeSubscription->id,
'trxn_id' => $this->getPaymentProcessorSubscriptionID(),
// FIXME processor_id is deprecated as it is not guaranteed to be unique, but currently (CiviCRM 5.9)
// it is required by cancelSubscription (where it is called subscription_id)
'processor_id' => $stripeSubscription->id,
'processor_id' => $this->getPaymentProcessorSubscriptionID(),
'auto_renew' => 1,
'cycle_day' => date('d'),
'next_sched_contribution_date' => $this->calculateNextScheduledDate($params),
......@@ -665,6 +575,7 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
}
if ($params['installments']) {
$recurParams['end_date'] = $this->calculateEndDate($params);
$recurParams['installments'] = $params['installments'];
}
}
......@@ -672,9 +583,43 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
CRM_Stripe_Hook::updateRecurringContribution($recurParams);
// Update the recurring payment
civicrm_api3('ContributionRecur', 'create', $recurParams);
// Update the contribution status
return $params;
// Set the orderID (trxn_id) to the invoice ID
// The IPN will change it to the charge_id
$this->setPaymentProcessorOrderID($stripeSubscription->latest_invoice);
return $this->endDoPayment($params);
}
/**
* Submit a refund payment
*
* @param array $params
* Assoc array of input parameters for this transaction.
*
* @throws \Civi\Payment\Exception\PaymentProcessorException
*/
public function doRefund(&$params) {
$requiredParams = ['charge_id', 'payment_processor_id'];
foreach ($requiredParams as $required) {
if (!isset($params[$required])) {
$message = 'Stripe doRefund: Missing mandatory parameter: ' . $required;
Civi::log()->error($message);
Throw new \Civi\Payment\Exception\PaymentProcessorException($message);
}
}
$refundParams = [
'charge' => $params['charge_id'],
];
if (!empty($params['amount'])) {
$refundParams['amount'] = $this->getAmount($params);
}
try {
$refund = \Stripe\Refund::create($refundParams);
}
catch (Exception $e) {
$this->handleError($e->getCode(), $e->getMessage());
Throw new \Civi\Payment\Exception\PaymentProcessorException($e->getMessage());
}
}
/**
......@@ -770,74 +715,6 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
return $endDate->format('Ymd');
}
/**
* @deprecated This belongs in a separate extension / hook as it's non-standard CiviCRM behaviour
*
* This adds some support for CiviDiscount on recurring contributions and changes the default behavior to discounting
* only the first of a recurring contribution set instead of all. (Intro offer) The Stripe procedure for discounting the
* first payment of subscription entails creating a negative invoice item or negative balance first,
* then creating the subscription at 100% full price. The customers first Stripe invoice will reflect the
* discount. Subsequent invoices will be at the full undiscounted amount.
* NB: Civi currently won't send a $0 charge to a payproc extension, but it should in this case. If the discount is
* the cost of initial payment, we still send the whole discount (or giftcard) as a negative balance.
* Consider not selling giftards greater than your least expensive auto-renew membership until we can override this.
*
* @param $params
* @param $amount
* @param $stripeCustomer
*
* @return float|int
* @throws \CiviCRM_API3_Exception
*/
public function deprecatedHandleCiviDiscount(&$params, $amount, $stripeCustomer) {
if (!empty($params['discountcode'])) {
$discount_code = $params['discountcode'];
$discount_object = civicrm_api3('DiscountCode', 'get', array(
'sequential' => 1,
'return' => "amount,amount_type",
'code' => $discount_code,
));
// amount_types: 1 = percentage, 2 = fixed, 3 = giftcard
if ((!empty($discount_object['values'][0]['amount'])) && (!empty($discount_object['values'][0]['amount_type']))) {
$discount_type = $discount_object['values'][0]['amount_type'];
if ( $discount_type == 1 ) {
// Discount is a percentage. Avoid ugly math and just get the full price using price_ param.
foreach($params as $key=>$value){
if("price_" == substr($key,0,6)){
$price_param = $key;
$price_field_id = substr($key,strrpos($key,'_') + 1);
}
}
if (!empty($params[$price_param])) {
$priceFieldValue = civicrm_api3('PriceFieldValue', 'get', array(
'sequential' => 1,
'return' => "amount",
'id' => $params[$price_param],
'price_field_id' => $price_field_id,
));
}
if (!empty($priceFieldValue['values'][0]['amount'])) {
$priceset_amount = $priceFieldValue['values'][0]['amount'];
$full_price = $priceset_amount * 100;
$discount_in_cents = $full_price - $amount;
// Set amount to full price.
$amount = $full_price;
}
} else if ( $discount_type >= 2 ) {
// discount is fixed or a giftcard. (may be > amount).
$discount_amount = $discount_object['values'][0]['amount'];
$discount_in_cents = $discount_amount * 100;
// Set amount to full price.
$amount = $amount + $discount_in_cents;
}
}
// Apply the disount through a negative balance.
$stripeCustomer->account_balance = -$discount_in_cents;
$stripeCustomer->save();
}
return $amount;
}
/**
* Default payment instrument validation.
*
......@@ -863,15 +740,15 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
$contributionRecurId = $this->getRecurringContributionId($params);
try {
$contributionRecur = civicrm_api3('ContributionRecur', 'getsingle', array(
$contributionRecur = civicrm_api3('ContributionRecur', 'getsingle', [
'id' => $contributionRecurId,
));
]);
}
catch (Exception $e) {
return FALSE;
}
if (empty($contributionRecur['trxn_id'])) {
CRM_Core_Session::setStatus(ts('The recurring contribution cannot be cancelled (No reference (trxn_id) found).'), 'Smart Debit', 'error');
CRM_Core_Session::setStatus(E::ts('The recurring contribution cannot be cancelled (No reference (trxn_id) found).'), 'Smart Debit', 'error');
return FALSE;
}
......
This diff is collapsed.
<?php
/**
* Shared payment IPN functions that should one day be migrated to CiviCRM core
* Version: 20190304
*/
trait CRM_Core_Payment_StripeIPNTrait {
/**
* @var array Payment processor
*/
private $_paymentProcessor;
/**
* Get the payment processor
* The $_GET['processor_id'] value is set by CRM_Core_Payment::handlePaymentMethod.
*/
protected function getPaymentProcessor() {
$paymentProcessorId = (int) CRM_Utils_Array::value('processor_id', $_GET);
if (empty($paymentProcessorId)) {
$this->exception('Failed to get payment processor id');
}
try {
$this->_paymentProcessor = \Civi\Payment\System::singleton()->getById($paymentProcessorId)->getPaymentProcessor();
}
catch(Exception $e) {
$this->exception('Failed to get payment processor');
}
}
/**
* Mark a contribution as cancelled and update related entities
*
* @param array $params [ 'id' -> contribution_id, 'payment_processor_id' -> payment_processor_id]
*
* @return bool
* @throws \CiviCRM_API3_Exception
*/
protected function canceltransaction($params) {
return $this->incompletetransaction($params, 'cancel');
}
/**
* Mark a contribution as failed and update related entities
*
* @param array $params [ 'id' -> contribution_id, 'payment_processor_id' -> payment_processor_id]
*
* @return bool
* @throws \CiviCRM_API3_Exception
*/
protected function failtransaction($params) {
return $this->incompletetransaction($params, 'fail');
}
/**
* Handler for failtransaction and canceltransaction - do not call directly
*
* @param array $params
* @param string $mode
*
* @return bool
* @throws \CiviCRM_API3_Exception
*/
protected function incompletetransaction($params, $mode) {
$requiredParams = ['id', 'payment_processor_id'];
foreach ($requiredParams as $required) {
if (!isset($params[$required])) {
$this->exception('canceltransaction: Missing mandatory parameter: ' . $required);
}
}
if (isset($params['payment_processor_id'])) {
$input['payment_processor_id'] = $params['payment_processor_id'];
}
$contribution = new CRM_Contribute_BAO_Contribution();
$contribution->id = $params['id'];
if (!$contribution->find(TRUE)) {
throw new CiviCRM_API3_Exception('A valid contribution ID is required', 'invalid_data');
}
if (!$contribution->loadRelatedObjects($input, $ids, TRUE)) {
throw new CiviCRM_API3_Exception('failed to load related objects');
}
$input['trxn_id'] = !empty($params['trxn_id']) ? $params['trxn_id'] : $contribution->trxn_id;
if (!empty($params['fee_amount'])) {
$input['fee_amount'] = $params['fee_amount'];
}
$objects['contribution'] = &$contribution;
$objects = array_merge($objects, $contribution->_relatedObjects);
$transaction = new CRM_Core_Transaction();
switch ($mode) {
case 'cancel':
return $this->cancelled($objects, $transaction);
case 'fail':
return $this->failed($objects, $transaction);
default:
throw new CiviCRM_API3_Exception('Unknown incomplete transaction type: ' . $mode);
}
}
}
<?php
/**
* Shared payment functions that should one day be migrated to CiviCRM core
*/
trait CRM_Core_Payment_StripeTrait {
/**********************
* Version 20190313
*********************/
/**
* Get the billing email address
*
* @param array $params
* @param int $contactId
*
* @return string|NULL
*/
protected function getBillingEmail($params, $contactId) {
$billingLocationId = CRM_Core_BAO_LocationType::getBilling();
$emailAddress = CRM_Utils_Array::value("email-{$billingLocationId}", $params,
CRM_Utils_Array::value('email-Primary', $params,
CRM_Utils_Array::value('email', $params, NULL)));
if (empty($emailAddress) && !empty($contactId)) {
// Try and retrieve an email address from Contact ID
try {
$emailAddress = civicrm_api3('Email', 'getvalue', array(
'contact_id' => $contactId,
'return' => ['email'],
));
}
catch (CiviCRM_API3_Exception $e) {
return NULL;
}
}
return $emailAddress;
}
/**
* Get the contact id
*
* @param array $params
*
* @return int ContactID
*/
protected function getContactId($params) {
// contactID is set by: membership payment workflow
$contactId = CRM_Utils_Array::value('contactID', $params,
CRM_Utils_Array::value('contact_id', $params,
CRM_Utils_Array::value('cms_contactID', $params,
CRM_Utils_Array::value('cid', $params, NULL
))));
if (!empty($contactId)) {
return $contactId;
}
// FIXME: Ref: https://lab.civicrm.org/extensions/stripe/issues/16
// The problem is that when registering for a paid event, civicrm does not pass in the
// contact id to the payment processor (civicrm version 5.3). So, I had to patch your
// getContactId to check the session for a contact id. It's a hack and probably should be fixed in core.
// The code below is exactly what CiviEvent does, but does not pass it through to the next function.
$session = CRM_Core_Session::singleton();
return $session->get('transaction.userID', NULL);
}
/**
* Get the contribution ID
*
* @param $params
*
* @return mixed
*/
protected function getContributionId($params) {
/*
* contributionID is set in the contribution workflow
* We do NOT have a contribution ID for event and membership payments as they are created after payment!
* See: https://github.com/civicrm/civicrm-core/pull/13763 (for events)
*/
return CRM_Utils_Array::value('contributionID', $params);
}
/**
* Get the recurring contribution ID from parameters passed in to cancelSubscription
* Historical the data passed to cancelSubscription is pretty poor and doesn't include much!
*
* @param array $params
*
* @return int|null
*/
protected function getRecurringContributionId($params) {
// Not yet passed, but could be added via core PR
$contributionRecurId = CRM_Utils_Array::value('contribution_recur_id', $params);
if (!empty($contributionRecurId)) {
return $contributionRecurId;
}
// Not yet passed, but could be added via core PR
$contributionId = CRM_Utils_Array::value('contribution_id', $params);
try {
return civicrm_api3('Contribution', 'getvalue', ['id' => $contributionId, 'return' => 'contribution_recur_id']);
}
catch (Exception $e) {
$subscriptionId = CRM_Utils_Array::value('subscriptionId', $params);
if (!empty($subscriptionId)) {
try {
return civicrm_api3('ContributionRecur', 'getvalue', ['processor_id' => $subscriptionId, 'return' => 'id']);
}
catch (Exception $e) {
return NULL;
}
}
return NULL;
}
}
/**
*
* @param array $params ['name' => payment instrument name]
*
* @return int|null
* @throws \CiviCRM_API3_Exception
*/
public static function createPaymentInstrument($params) {
$mandatoryParams = ['name'];
foreach ($mandatoryParams as $value) {
if (empty($params[$value])) {
Civi::log()->error('createPaymentInstrument: Missing mandatory parameter: ' . $value);
return NULL;
}
}
// Create a Payment Instrument
// See if we already have this type
$paymentInstrument = civicrm_api3('OptionValue', 'get', array(
'option_group_id' => "payment_instrument",
'name' => $params['name'],
));
if (empty($paymentInstrument['count'])) {
// Otherwise create it
try {
$financialAccount = civicrm_api3('FinancialAccount', 'getsingle', [
'financial_account_type_id' => "Asset",
'name' => "Payment Processor Account",
]);
}
catch (Exception $e) {
$financialAccount = civicrm_api3('FinancialAccount', 'getsingle', [
'financial_account_type_id' => "Asset",
'name' => "Payment Processor Account",
'options' => ['limit' => 1, 'sort' => "id ASC"],
]);
}
$paymentParams = [
'option_group_id' => "payment_instrument",
'name' => $params['name'],
'description' => $params['name'],
'financial_account_id' => $financialAccount['id'],
];
$paymentInstrument = civicrm_api3('OptionValue', 'create', $paymentParams);
$paymentInstrumentId = $paymentInstrument['values'][$paymentInstrument['id']]['value'];
}
else {
$paymentInstrumentId = $paymentInstrument['id'];
}
return $paymentInstrumentId;
}
}
<?php
/**
* https://civicrm.org/licensing
*/
/**
* Class CRM_Stripe_AJAX
*/
class CRM_Stripe_AJAX {
/**
* Generate the paymentIntent for civicrm_stripe.js
*
* @throws \CRM_Core_Exception
* @throws \CiviCRM_API3_Exception
*/
public static function confirmPayment() {
$paymentMethodID = CRM_Utils_Request::retrieveValue('payment_method_id', 'String', NULL, TRUE);
$paymentIntentID = CRM_Utils_Request::retrieveValue('payment_intent_id', 'String');
$amount = CRM_Utils_Request::retrieveValue('amount', 'Money', NULL, TRUE);
$currency = CRM_Utils_Request::retrieveValue('currency', 'String', CRM_Core_Config::singleton()->defaultCurrency);
$processorID = CRM_Utils_Request::retrieveValue('id', 'Integer', NULL, TRUE);
$processor = new CRM_Core_Payment_Stripe('', civicrm_api3('PaymentProcessor', 'getsingle', ['id' => $processorID]));
$processor->setAPIParams();
if ($paymentIntentID) {
$intent = \Stripe\PaymentIntent::retrieve($paymentIntentID);
$intent->confirm([
'payment_method' => $paymentMethodID,
]);
}
else {
try {
$intent = \Stripe\PaymentIntent::create([
'payment_method' => $paymentMethodID,
'amount' => $processor->getAmount(['amount' => $amount]),
'currency' => $currency,
'confirmation_method' => 'manual',
'capture_method' => 'manual',
// authorize the amount but don't take from card yet
'setup_future_usage' => 'off_session',
// Setup the card to be saved and used later
'confirm' => TRUE,
]);
}
catch (Exception $e) {
CRM_Utils_JSON::output(['error' => ['message' => $e->getMessage()]]);
}
}
self::generatePaymentResponse($intent);
}
/**
* Generate the json response for civicrm_stripe.js
*
* @param \Stripe\PaymentIntent $intent
*/
private static function generatePaymentResponse($intent) {
if ($intent->status == 'requires_action' &&
$intent->next_action->type == 'use_stripe_sdk') {
// Tell the client to handle the action
CRM_Utils_JSON::output([
'requires_action' => true,
'payment_intent_client_secret' => $intent->client_secret,
]);
} else if (($intent->status == 'requires_capture') || ($intent->status == 'requires_confirmation')) {
// The payment intent has been confirmed, we just need to capture the payment
// Handle post-payment fulfillment
CRM_Utils_JSON::output([
'success' => true,
'paymentIntent' => ['id' => $intent->id],
]);
} else {
// Invalid status
CRM_Utils_JSON::output(['error' => ['message' => 'Invalid PaymentIntent status']]);
}
}
}
......@@ -20,11 +20,17 @@ class CRM_Stripe_Api {
return (bool) $stripeObject->refunded;
case 'amount_refunded':
return (int) $stripeObject->amount_refunded / 100;
return (float) $stripeObject->amount_refunded / 100;
case 'customer_id':
return (string) $stripeObject->customer;
case 'balance_transaction':
return (string) $stripeObject->balance_transaction;
case 'receive_date':
return self::formatDate($stripeObject->created);
}
break;
......@@ -37,7 +43,7 @@ class CRM_Stripe_Api {
return (string) $stripeObject->id;
case 'receive_date':
return $stripeObject->date ? date("Y-m-d H:i:s", $stripeObject->date) : NULL;
return self::formatDate($stripeObject->created);
case 'subscription_id':
return (string) $stripeObject->subscription;
......@@ -64,10 +70,13 @@ class CRM_Stripe_Api {
case 'description':
return (string) $stripeObject->description;
case 'customer_id':
return (string) $stripeObject->customer;
case 'failure_message':
$stripeCharge = \Stripe\Charge::retrieve($stripeObject->charge);
return (string) $stripeCharge->failure_message;
}
break;
......@@ -93,7 +102,10 @@ class CRM_Stripe_Api {
return (string) $stripeObject->plan->name;
case 'plan_start':
return $stripeObject->start ? date("Y-m-d H:i:s", $stripeObject->start) : NULL;
return self::formatDate($stripeObject->start_date);
case 'cancel_date':
return self::formatDate($stripeObject->canceled_at);
case 'cycle_day':
return date("d", $stripeObject->billing_cycle_anchor);
......@@ -119,6 +131,16 @@ class CRM_Stripe_Api {
return NULL;
}
/**
* Return a formatted date from a stripe timestamp or NULL if not set
* @param int $stripeTimestamp
*
* @return string|null
*/
private static function formatDate($stripeTimestamp) {
return $stripeTimestamp ? date('YmdHis', $stripeTimestamp) : NULL;
}
public static function getParam($name, $stripeObject) {
// Common parameters
switch ($name) {
......@@ -128,6 +150,9 @@ class CRM_Stripe_Api {
case 'event_type':
return (string) $stripeObject->type;
case 'id':
return (string) $stripeObject->id;
case 'previous_plan_id':
if (preg_match('/\.updated$/', $stripeObject->type)) {
return (string) $stripeObject->data->previous_attributes->plan->id;
......
<?php
/**
* https://civicrm.org/licensing
*/
use CRM_Stripe_ExtensionUtil as E;
/**
* Class CRM_Stripe_Check
*/
class CRM_Stripe_Check {
const MIN_VERSION_MJWSHARED = 0.3;
public static function checkRequirements(&$messages) {
$extensions = civicrm_api3('Extension', 'get', [
'full_name' => "mjwshared",
]);
if (empty($extensions['id']) || ($extensions['values'][$extensions['id']]['status'] !== 'installed')) {
$messages[] = new CRM_Utils_Check_Message(
'stripe_requirements',
E::ts('The Stripe extension requires the mjwshared extension which is not installed (https://lab.civicrm.org/extensions/mjwshared).'),
E::ts('Stripe: Missing Requirements'),
\Psr\Log\LogLevel::ERROR,
'fa-money'
);
}
if (version_compare($extensions['values'][$extensions['id']]['version'], self::MIN_VERSION_MJWSHARED) === -1) {
$messages[] = new CRM_Utils_Check_Message(
'stripe_requirements',
E::ts('The Stripe extension requires the mjwshared extension version %1 or greater but your system has version %2.',
[
1 => self::MIN_VERSION_MJWSHARED,
2 => $extensions['values'][$extensions['id']]['version']
]),
E::ts('Stripe: Missing Requirements'),
\Psr\Log\LogLevel::ERROR,
'fa-money'
);
}
}
}
......@@ -24,7 +24,7 @@ class CRM_Stripe_Customer {
1 => [$params['contact_id'], 'String'],
2 => [$params['processor_id'], 'Positive'],
];
return CRM_Core_DAO::singleValueQuery("SELECT id
FROM civicrm_stripe_customers
......@@ -108,39 +108,29 @@ class CRM_Stripe_Customer {
}
/**
* @param $params
* @param array $params
* @param \CRM_Core_Payment_Stripe $stripe
*
* @return \Stripe\ApiResource
* @throws \CiviCRM_API3_Exception
* @throws \Civi\Payment\Exception\PaymentProcessorException
*/
public static function create($params) {
$requiredParams = ['contact_id', 'card_token', 'processor_id'];
// $optionalParams = ['email'];
public static function create($params, $stripe) {
$requiredParams = ['contact_id', 'processor_id'];
foreach ($requiredParams as $required) {
if (empty($params[$required])) {
throw new \Civi\Payment\Exception\PaymentProcessorException('Stripe Customer (create): Missing required parameter: ' . $required);
}
}
$contactDisplayName = civicrm_api3('Contact', 'getvalue', [
'return' => 'display_name',
'id' => $params['contact_id'],
]);
$stripeCustomerParams = [
'description' => $contactDisplayName . ' (CiviCRM)',
'card' => $params['card_token'],
'email' => CRM_Utils_Array::value('email', $params),
'metadata' => ['civicrm_contact_id' => $params['contact_id']],
];
$stripeCustomerParams = self::getStripeCustomerMetadata($params);
try {
$stripeCustomer = \Stripe\Customer::create($stripeCustomerParams);
}
catch (Exception $e) {
$err = CRM_Core_Payment_Stripe::parseStripeException('create_customer', $e, FALSE);
$errorMessage = CRM_Core_Payment_Stripe::handleErrorNotification($err, $params['stripe_error_url']);
$errorMessage = $stripe->handleErrorNotification($err, $params['stripe_error_url']);
throw new \Civi\Payment\Exception\PaymentProcessorException('Failed to create Stripe Customer: ' . $errorMessage);
}
......@@ -155,6 +145,61 @@ class CRM_Stripe_Customer {
return $stripeCustomer;
}
/**
* @param array $params
* @param \CRM_Core_Payment_Stripe $stripe
* @param string $stripeCustomerID
*
* @return \Stripe\Customer
* @throws \CiviCRM_API3_Exception
* @throws \Civi\Payment\Exception\PaymentProcessorException
*/
public static function updateMetadata($params, $stripe, $stripeCustomerID) {
$requiredParams = ['contact_id', 'processor_id'];
foreach ($requiredParams as $required) {
if (empty($params[$required])) {
throw new \Civi\Payment\Exception\PaymentProcessorException('Stripe Customer (updateMetadata): Missing required parameter: ' . $required);
}
}
$stripeCustomerParams = self::getStripeCustomerMetadata($params);
try {
$stripeCustomer = \Stripe\Customer::update($stripeCustomerID, $stripeCustomerParams);
}
catch (Exception $e) {
$err = CRM_Core_Payment_Stripe::parseStripeException('create_customer', $e, FALSE);
$errorMessage = $stripe->handleErrorNotification($err, $params['stripe_error_url']);
throw new \Civi\Payment\Exception\PaymentProcessorException('Failed to update Stripe Customer: ' . $errorMessage);
}
return $stripeCustomer;
}
/**
* @param array $params
* Required: contact_id; Optional: email
*
* @return array
* @throws \CiviCRM_API3_Exception
*/
private static function getStripeCustomerMetadata($params) {
$contactDisplayName = civicrm_api3('Contact', 'getvalue', [
'return' => 'display_name',
'id' => $params['contact_id'],
]);
$stripeCustomerParams = [
'name' => $contactDisplayName,
'description' => 'CiviCRM: ' . civicrm_api3('Domain', 'getvalue', ['current_domain' => 1, 'return' => 'name']),
'email' => CRM_Utils_Array::value('email', $params),
'metadata' => [
'CiviCRM Contact ID' => $params['contact_id'],
'CiviCRM URL' => CRM_Utils_System::url('civicrm/contact/view', "reset=1&cid={$params['contact_id']}", TRUE),
],
];
return $stripeCustomerParams;
}
/**
* Delete a Stripe customer from the CiviCRM database
*
......
<?php
/**
* Collection of upgrade steps.
* DO NOT USE a naming scheme other than upgrade_N, where N is an integer.
* Naming scheme upgrade_X_Y_Z is offically wrong!
* DO NOT USE a naming scheme other than upgrade_N, where N is an integer.
* Naming scheme upgrade_X_Y_Z is offically wrong!
* https://chat.civicrm.org/civicrm/pl/usx3pfjzjbrhzpewuggu1e6ftw
*/
class CRM_Stripe_Upgrader extends CRM_Stripe_Upgrader_Base {
......@@ -56,7 +56,7 @@ class CRM_Stripe_Upgrader extends CRM_Stripe_Upgrader_Base {
CRM_Core_DAO::executeQuery('ALTER TABLE civicrm_stripe_plans ADD COLUMN `processor_id` int(10) DEFAULT NULL COMMENT "ID from civicrm_payment_processor"');
CRM_Core_DAO::executeQuery('ALTER TABLE civicrm_stripe_subscriptions ADD COLUMN `processor_id` int(10) DEFAULT NULL COMMENT "ID from civicrm_payment_processor"');
}
return TRUE;
return TRUE;
}
......@@ -70,14 +70,14 @@ class CRM_Stripe_Upgrader extends CRM_Stripe_Upgrader_Base {
$config = CRM_Core_Config::singleton();
$dbName = DB::connect($config->dsn)->_db;
$null_count = CRM_Core_DAO::executeQuery('SELECT COUNT(*) FROM civicrm_stripe_customers where processor_id IS NULL') +
$null_count = CRM_Core_DAO::executeQuery('SELECT COUNT(*) FROM civicrm_stripe_customers where processor_id IS NULL') +
CRM_Core_DAO::executeQuery('SELECT COUNT(*) FROM civicrm_stripe_plans where processor_id IS NULL') +
CRM_Core_DAO::executeQuery('SELECT COUNT(*) FROM civicrm_stripe_subscriptions where processor_id IS NULL');
if ( $null_count == 0 ) {
$this->ctx->log->info('Skipped civicrm_stripe update 5002. No nulls found in column processor_id in our tables.');
return TRUE;
}
else {
}
else {
try {
// Set processor ID if there's only one.
$processorCount = civicrm_api3('PaymentProcessorType', 'get', array(
......@@ -109,8 +109,8 @@ class CRM_Stripe_Upgrader extends CRM_Stripe_Upgrader_Base {
return TRUE;
}
/**
/**
* Add subscription_id column to civicrm_stripe_subscriptions table.
*
* @return TRUE on success
......@@ -131,11 +131,11 @@ class CRM_Stripe_Upgrader extends CRM_Stripe_Upgrader_Base {
CRM_Core_DAO::executeQuery('ALTER TABLE civicrm_stripe_subscriptions ADD COLUMN `subscription_id` varchar(255) DEFAULT NULL COMMENT "Subscription ID from Stripe" FIRST');
CRM_Core_DAO::executeQuery('ALTER TABLE `civicrm_stripe_subscriptions` ADD UNIQUE KEY(`subscription_id`)');
}
return TRUE;
}
/**
return TRUE;
}
/**
* Populates the subscription_id column in table civicrm_stripe_subscriptions.
*
* @return TRUE on success
......@@ -145,51 +145,43 @@ class CRM_Stripe_Upgrader extends CRM_Stripe_Upgrader_Base {
$config = CRM_Core_Config::singleton();
$dbName = DB::connect($config->dsn)->_db;
$null_count = CRM_Core_DAO::executeQuery('SELECT COUNT(*) FROM civicrm_stripe_subscriptions where subscription_id IS NULL');
$null_count = CRM_Core_DAO::executeQuery('SELECT COUNT(*) FROM civicrm_stripe_subscriptions where subscription_id IS NULL');
if ( $null_count == 0 ) {
$this->ctx->log->info('Skipped civicrm_stripe update 5004. No nulls found in column subscription_id in our civicrm_stripe_subscriptions table.');
}
else {
$customer_infos = CRM_Core_DAO::executeQuery("SELECT customer_id,processor_id
}
else {
$customer_infos = CRM_Core_DAO::executeQuery("SELECT customer_id,processor_id
FROM `civicrm_stripe_subscriptions`;");
while ( $customer_infos->fetch() ) {
$processor_id = $customer_infos->processor_id;
$customer_id = $customer_infos->customer_id;
try {
$stripe_key = civicrm_api3('PaymentProcessor', 'getvalue', array(
'return' => 'user_name',
'id' => $processor_id,
));
}
catch (Exception $e) {
Civi::log()->debug('Update 5004 failed. Has Stripe been removed as a payment processor?', $out = false);
return;
}
try {
\Stripe\Stripe::setApiKey($stripe_key);
$subscription = \Stripe\Subscription::all(array(
'customer'=> $customer_id,
'limit'=>1,
));
}
catch (Exception $e) {
// Don't quit here. A missing customer in Stipe is OK. They don't exist, so they can't have a subscription.
Civi::log()->debug('Cannot find Stripe API key: ' . $e->getMessage());
}
if (!empty($subscription['data'][0]['id'])) {
$query_params = array(
1 => array($subscription['data'][0]['id'], 'String'),
2 => array($customer_id, 'String'),
);
CRM_Core_DAO::executeQuery('UPDATE civicrm_stripe_subscriptions SET subscription_id = %1 where customer_id = %2;', $query_params);
unset($subscription);
}
try {
$processor = new CRM_Core_Payment_Stripe('', civicrm_api3('PaymentProcessor', 'getsingle', ['id' => $processor_id]));
$processor->setAPIParams();
$subscription = \Stripe\Subscription::all(array(
'customer'=> $customer_id,
'limit'=>1,
));
}
catch (Exception $e) {
// Don't quit here. A missing customer in Stipe is OK. They don't exist, so they can't have a subscription.
Civi::log()->debug('Cannot find Stripe API key: ' . $e->getMessage());
}
if (!empty($subscription['data'][0]['id'])) {
$query_params = array(
1 => array($subscription['data'][0]['id'], 'String'),
2 => array($customer_id, 'String'),
);
CRM_Core_DAO::executeQuery('UPDATE civicrm_stripe_subscriptions SET subscription_id = %1 where customer_id = %2;', $query_params);
unset($subscription);
}
}
}
return TRUE;
return TRUE;
}
/**
/**
* Add contribution_recur_id column to civicrm_stripe_subscriptions table.
*
* @return TRUE on success
......@@ -213,10 +205,10 @@ class CRM_Stripe_Upgrader extends CRM_Stripe_Upgrader_Base {
CRM_Core_DAO::executeQuery('ALTER TABLE `civicrm_stripe_subscriptions` ADD INDEX(`contribution_recur_id`);');
CRM_Core_DAO::executeQuery('ALTER TABLE `civicrm_stripe_subscriptions` ADD CONSTRAINT `FK_civicrm_stripe_contribution_recur_id` FOREIGN KEY (`contribution_recur_id`) REFERENCES `civicrm_contribution_recur`(`id`) ON DELETE SET NULL ON UPDATE RESTRICT;');
}
return TRUE;
return TRUE;
}
/**
/**
* Method 1 for populating the contribution_recur_id column in the civicrm_stripe_subscriptions table.
* ( A simple approach if that works if there have never been any susbcription edits in the Stripe UI. )
......@@ -230,93 +222,93 @@ class CRM_Stripe_Upgrader extends CRM_Stripe_Upgrader_Base {
$subscriptions = CRM_Core_DAO::executeQuery("SELECT invoice_id,is_live
FROM `civicrm_stripe_subscriptions`;");
while ( $subscriptions->fetch() ) {
$test_mode = (int)!$subscriptions->is_live;
try {
// Fetch the recurring contribution Id.
$recur_id = civicrm_api3('Contribution', 'getvalue', array(
'sequential' => 1,
'return' => "contribution_recur_id",
'invoice_id' => $subscriptions->invoice_id,
'contribution_test' => $test_mode,
));
}
catch (CiviCRM_API3_Exception $e) {
// Don't quit here. If we can't find the recurring ID for a single customer, make a note in the error log and carry on.
Civi::log()->debug('Recurring contribution search: ' . $e->getMessage());
}
if (!empty($recur_id)) {
$p = array(
1 => array($recur_id, 'Integer'),
2 => array($subscriptions->invoice_id, 'String'),
);
CRM_Core_DAO::executeQuery('UPDATE civicrm_stripe_subscriptions SET contribution_recur_id = %1 WHERE invoice_id = %2;', $p);
}
while ( $subscriptions->fetch() ) {
$test_mode = (int)!$subscriptions->is_live;
try {
// Fetch the recurring contribution Id.
$recur_id = civicrm_api3('Contribution', 'getvalue', array(
'sequential' => 1,
'return' => "contribution_recur_id",
'invoice_id' => $subscriptions->invoice_id,
'contribution_test' => $test_mode,
));
}
catch (CiviCRM_API3_Exception $e) {
// Don't quit here. If we can't find the recurring ID for a single customer, make a note in the error log and carry on.
Civi::log()->debug('Recurring contribution search: ' . $e->getMessage());
}
return TRUE;
if (!empty($recur_id)) {
$p = array(
1 => array($recur_id, 'Integer'),
2 => array($subscriptions->invoice_id, 'String'),
);
CRM_Core_DAO::executeQuery('UPDATE civicrm_stripe_subscriptions SET contribution_recur_id = %1 WHERE invoice_id = %2;', $p);
}
}
return TRUE;
}
/**
/**
* Method 2 for populating the contribution_recur_id column in the civicrm_stripe_subscriptions table. Uncomment this and comment 5006.
* ( A more convoluted approach that works if there HAVE been susbcription edits in the Stripe UI. )
* @return TRUE on success. Please let users uncomment this as needed and increment past 5007 for the next upgrade.
* @throws Exception
*/
/*
public function upgrade_5007() {
$config = CRM_Core_Config::singleton();
$dbName = DB::connect($config->dsn)->_db;
$subscriptions = CRM_Core_DAO::executeQuery("SELECT customer_id,is_live,processor_id
FROM `civicrm_stripe_subscriptions`;");
while ( $subscriptions->fetch() ) {
$test_mode = (int)!$subscriptions->is_live;
$p = array(
1 => array($subscriptions->customer_id, 'String'),
2 => array($subscriptions->is_live, 'Integer'),
);
$customer = CRM_Core_DAO::executeQuery("SELECT email
FROM `civicrm_stripe_customers` WHERE id = %1 AND is_live = %2;", $p);
$customer->fetch();
// Try the billing email first, since that's what we send to Stripe.
try {
$contact = civicrm_api3('Email', 'get', array(
'sequential' => 1,
'return' => "contact_id",
'is_billing' => 1,
'email' => $customer->email,
'api.ContributionRecur.get' => array('return' => "id", 'contact_id' => "\$value.contact_id", 'contribution_status_id' => "In Progress"),
));
}
catch (CiviCRM_API3_Exception $e) {
// Uh oh, that didn't work. Try to retrieve the recurring id using the primary email.
$contact = civicrm_api3('Contact', 'get', array(
'sequential' => 1,
'return' => "id",
'email' => $customer->email,
'api.ContributionRecur.get' => array('sequential' => 1, 'return' => "id", 'contact_id' => "\$values.id", 'contribution_status_id' => "In Progress"),
));
}
/*
public function upgrade_5007() {
$config = CRM_Core_Config::singleton();
$dbName = DB::connect($config->dsn)->_db;
$subscriptions = CRM_Core_DAO::executeQuery("SELECT customer_id,is_live,processor_id
FROM `civicrm_stripe_subscriptions`;");
while ( $subscriptions->fetch() ) {
$test_mode = (int)!$subscriptions->is_live;
$p = array(
1 => array($subscriptions->customer_id, 'String'),
2 => array($subscriptions->is_live, 'Integer'),
);
$customer = CRM_Core_DAO::executeQuery("SELECT email
FROM `civicrm_stripe_customers` WHERE id = %1 AND is_live = %2;", $p);
$customer->fetch();
// Try the billing email first, since that's what we send to Stripe.
try {
$contact = civicrm_api3('Email', 'get', array(
'sequential' => 1,
'return' => "contact_id",
'is_billing' => 1,
'email' => $customer->email,
'api.ContributionRecur.get' => array('return' => "id", 'contact_id' => "\$value.contact_id", 'contribution_status_id' => "In Progress"),
));
}
catch (CiviCRM_API3_Exception $e) {
// Uh oh, that didn't work. Try to retrieve the recurring id using the primary email.
$contact = civicrm_api3('Contact', 'get', array(
'sequential' => 1,
'return' => "id",
'email' => $customer->email,
'api.ContributionRecur.get' => array('sequential' => 1, 'return' => "id", 'contact_id' => "\$values.id", 'contribution_status_id' => "In Progress"),
));
}
if (!empty($contact['values'][0]['api.ContributionRecur.get']['values'][0]['id'])) {
$recur_id = $contact['values'][0]['api.ContributionRecur.get']['values'][0]['id'];
$p = array(
1 => array($recur_id, 'Integer'),
2 => array($subscriptions->customer_id, 'String'),
);
CRM_Core_DAO::executeQuery('UPDATE civicrm_stripe_subscriptions SET contribution_recur_id = %1 WHERE customer_id = %2;', $p);
} else {
// Crap.
$this->ctx->log->info('Update 5007 failed. Consider adding recurring IDs manuallly to civicrm_stripe_subscriptions. ');
return;
if (!empty($contact['values'][0]['api.ContributionRecur.get']['values'][0]['id'])) {
$recur_id = $contact['values'][0]['api.ContributionRecur.get']['values'][0]['id'];
$p = array(
1 => array($recur_id, 'Integer'),
2 => array($subscriptions->customer_id, 'String'),
);
CRM_Core_DAO::executeQuery('UPDATE civicrm_stripe_subscriptions SET contribution_recur_id = %1 WHERE customer_id = %2;', $p);
} else {
// Crap.
$this->ctx->log->info('Update 5007 failed. Consider adding recurring IDs manuallly to civicrm_stripe_subscriptions. ');
return;
}
}
}
return TRUE;
}
*/
return TRUE;
}
*/
/**
/**
* Add change default NOT NULL to NULL in vestigial invoice_id column in civicrm_stripe_subscriptions table if needed. (issue #192)
*
* @return TRUE on success
......@@ -338,7 +330,7 @@ class CRM_Stripe_Upgrader extends CRM_Stripe_Upgrader_Base {
MODIFY COLUMN `invoice_id` varchar(255) NULL default ""
COMMENT "Safe to remove this column if the update retrieving subscription IDs completed satisfactorily."');
}
return TRUE;
return TRUE;
}
/**
......@@ -407,4 +399,25 @@ class CRM_Stripe_Upgrader extends CRM_Stripe_Upgrader_Base {
return TRUE;
}
public function upgrade_5023() {
$this->ctx->log->info('Applying Stripe update 5023. Swap over public/secret key settings');
$stripeProcessors = civicrm_api3('PaymentProcessor', 'get', [
'payment_processor_type_id' => "Stripe",
]);
foreach ($stripeProcessors['values'] as $processor) {
if ((substr($processor['user_name'], 0, 3) === 'sk_')
&& (substr($processor['password'], 0, 3) === 'pk_')) {
// Need to switch over parameters
$createParams = [
'id' => $processor['id'],
'user_name' => $processor['password'],
'password' => $processor['user_name'],
];
civicrm_api3('PaymentProcessor', 'create', $createParams);
}
}
CRM_Utils_System::flushCache();
return TRUE;
}
}
......@@ -20,13 +20,16 @@ class CRM_Stripe_Webhook {
$result = civicrm_api3('PaymentProcessor', 'get', [
'class_name' => 'Payment_Stripe',
'is_active' => 1,
'domain_id' => CRM_Core_Config::domainID(),
]);
foreach ($result['values'] as $paymentProcessor) {
$messageTexts = [];
$webhook_path = self::getWebhookPath($paymentProcessor['id']);
\Stripe\Stripe::setApiKey(CRM_Core_Payment_Stripe::getSecretKey($paymentProcessor));
$processor = new CRM_Core_Payment_Stripe('', civicrm_api3('PaymentProcessor', 'getsingle', ['id' => $paymentProcessor['id']]));
$processor->setAPIParams();
try {
$webhooks = \Stripe\WebhookEndpoint::all(["limit" => 100]);
}
......@@ -35,10 +38,7 @@ class CRM_Stripe_Webhook {
$messages[] = new CRM_Utils_Check_Message(
'stripe_webhook',
$error,
E::ts('Stripe Payment Processor: %1 (%2)', [
1 => $paymentProcessor['name'],
2 => $paymentProcessor['id'],
]),
self::getTitle($paymentProcessor),
\Psr\Log\LogLevel::ERROR,
'fa-money'
);
......@@ -91,9 +91,12 @@ class CRM_Stripe_Webhook {
}
}
else {
$messageTexts[] = E::ts('Stripe Webhook missing! Please visit <a href="%1">Fix Stripe Webhook</a> to fix.', [
1 => CRM_Utils_System::url('civicrm/stripe/fix-webhook'),
]);
$messageTexts[] = E::ts('Stripe Webhook missing! Please visit <a href="%1">Fix Stripe Webhook</a> to fix.<br />Expected webhook path is: <a href="%2" target="_blank">%2</a>',
[
1 => CRM_Utils_System::url('civicrm/stripe/fix-webhook'),
2 => $webhook_path,
]
);
}
}
......@@ -101,10 +104,7 @@ class CRM_Stripe_Webhook {
$messages[] = new CRM_Utils_Check_Message(
'stripe_webhook',
$messageText,
E::ts('Stripe Payment Processor Webhook: %1 (%2)', [
1 => $paymentProcessor['name'],
2 => $paymentProcessor['id'],
]),
self::getTitle($paymentProcessor),
\Psr\Log\LogLevel::WARNING,
'fa-money'
);
......@@ -112,13 +112,30 @@ class CRM_Stripe_Webhook {
}
}
/**
* Get the error message title for the system check
* @param array $paymentProcessor
*
* @return string
*/
private static function getTitle($paymentProcessor) {
if (!empty($paymentProcessor['is_test'])) {
$paymentProcessor['name'] .= ' (test)';
}
return E::ts('Stripe Payment Processor: %1 (%2)', [
1 => $paymentProcessor['name'],
2 => $paymentProcessor['id'],
]);
}
/**
* Create a new webhook for payment processor
*
* @param int $paymentProcessorId
*/
public static function createWebhook($paymentProcessorId) {
\Stripe\Stripe::setApiKey(CRM_Core_Payment_Stripe::getSecretKeyById($paymentProcessorId));
$processor = new CRM_Core_Payment_Stripe('', civicrm_api3('PaymentProcessor', 'getsingle', ['id' => $paymentProcessorId]));
$processor->setAPIParams();
$params = [
'enabled_events' => self::getDefaultEnabledEvents(),
......
<?php
/**
* This api allows you to replay Stripe events.
* This api allows you to replay Stripe events.
*
* You can either pass the id of an entry in the System Log (which can
* be populated with the Stripe.PopulateLog call) or you can pass a
* event id from Stripe directly.
*
* When processing an event, the event will always be re-fetched from the
* Stripe server first, so this will not work while offline or with
* Stripe server first, so this will not work while offline or with
* events that were not generated by the Stripe server.
*/
*/
/**
* Stripe.Ipn API specification
......@@ -57,12 +57,9 @@ function civicrm_api3_stripe_Ipn($params) {
throw new API_Exception('Please pass the payment processor id (ppid) if using evtid.', 3236);
}
$ppid = $params['ppid'];
$results = civicrm_api3('PaymentProcessor', 'getsingle', array('id' => $ppid));
// YES! I know, password and user are backwards. wtf??
$sk = $results['user_name'];
$processor = new CRM_Core_Payment_Stripe('', civicrm_api3('PaymentProcessor', 'getsingle', ['id' => $ppid]));
$processor->setAPIParams();
require_once ("vendor/stripe/stripe-php/init.php");
\Stripe\Stripe::setApiKey($sk);
$object = \Stripe\Event::retrieve($params['evtid']);
}
// Avoid a SQL error if this one has been processed already.
......@@ -74,7 +71,7 @@ function civicrm_api3_stripe_Ipn($params) {
return civicrm_api3_create_error("Ipn already processed.");
}
if (class_exists('CRM_Core_Payment_StripeIPN')) {
// The $_GET['processor_id'] value is normally set by
// The $_GET['processor_id'] value is normally set by
// CRM_Core_Payment::handlePaymentMethod
$_GET['processor_id'] = $ppid;
$ipnClass = new CRM_Core_Payment_StripeIPN($object);
......
......@@ -9,7 +9,7 @@
/**
* Stripe.ListEvents API specification
*
*
*
* @param array $spec description of fields supported by this API call
* @return void
......@@ -17,12 +17,13 @@
*/
function _civicrm_api3_stripe_ListEvents_spec(&$spec) {
$spec['ppid']['title'] = ts("Use the given Payment Processor ID");
$spec['ppid']['type'] = CRM_Utils_Type::T_INT;
$spec['ppid']['type'] = CRM_Utils_Type::T_INT;
$spec['ppid']['api.required'] = TRUE;
$spec['type']['title'] = ts("Limit to the given Stripe events type, defaults to invoice.payment_succeeded.");
$spec['type']['api.default'] = 'invoice.payment_succeeded';
$spec['limit']['title'] = ts("Limit number of results returned (100 is max)");
$spec['starting_after']['title'] = ts("Only return results after this event id.");
$spec['output']['api.default'] = 'brief';
$spec['output']['api.default'] = 'brief';
$spec['output']['title'] = ts("How to format the output, brief or raw. Defaults to brief.");
}
......@@ -122,18 +123,17 @@ function civicrm_api3_stripe_VerifyEventType($eventType) {
* Process parameters to determine ppid and sk.
*
* @param array $params
*
* @return array
* @throws \API_Exception
*/
function civicrm_api3_stripe_ProcessParams($params) {
$ppid = NULL;
$type = NULL;
$created = NULL;
$limit = NULL;
$starting_after = NULL;
$sk = NULL;
if (array_key_exists('ppid', $params) ) {
$ppid = $params['ppid'];
}
if (array_key_exists('created', $params) ) {
$created = $params['created'];
}
......@@ -144,29 +144,6 @@ function civicrm_api3_stripe_ProcessParams($params) {
$starting_after = $params['starting_after'];
}
// Select the right payment processor to use.
if ($ppid) {
$query_params = array('id' => $ppid);
}
else {
// By default, select the live stripe processor (we expect there to be
// only one).
$query_params = array('class_name' => 'Payment_Stripe', 'is_test' => 0);
}
try {
$results = civicrm_api3('PaymentProcessor', 'getsingle', $query_params);
// YES! I know, password and user are backwards. wtf??
$sk = $results['user_name'];
}
catch (CiviCRM_API3_Exception $e) {
if(preg_match('/Expected one PaymentProcessor but/', $e->getMessage())) {
throw new API_Exception("Expected one live Stripe payment processor, but found none or more than one. Please specify ppid=.", 1234);
}
else {
throw new API_Exception("Error getting the Stripe Payment Processor to use", 1235);
}
}
// Check to see if we should filter by type.
if (array_key_exists('type', $params) ) {
// Validate - since we will be appending this to an URL.
......@@ -185,7 +162,7 @@ function civicrm_api3_stripe_ProcessParams($params) {
throw new API_Exception("Created can only be passed in programatically as an array", 1237);
}
}
return array('sk' => $sk, 'type' => $type, 'created' => $created, 'limit' => $limit, 'starting_after' => $starting_after);
return ['type' => $type, 'created' => $created, 'limit' => $limit, 'starting_after' => $starting_after];
}
/**
......@@ -199,7 +176,6 @@ function civicrm_api3_stripe_ProcessParams($params) {
*/
function civicrm_api3_stripe_Listevents($params) {
$parsed = civicrm_api3_stripe_ProcessParams($params);
$sk = $parsed['sk'];
$type = $parsed['type'];
$created = $parsed['created'];
$limit = $parsed['limit'];
......@@ -218,9 +194,10 @@ function civicrm_api3_stripe_Listevents($params) {
if ($starting_after) {
$args['starting_after'] = $starting_after;
}
require_once ("vendor/stripe/stripe-php/init.php");
\Stripe\Stripe::setApiKey($sk);
$processor = new CRM_Core_Payment_Stripe('', civicrm_api3('PaymentProcessor', 'getsingle', ['id' => $params['ppid']]));
$processor->setAPIParams();
$data_list = \Stripe\Event::all($args);
if (array_key_exists('error', $data_list)) {
$err = $data_list['error'];
......
......@@ -258,8 +258,7 @@ function civicrm_api3_stripe_customer_updatestripemetadata($params) {
throw new CiviCRM_API3_Exception('Could not find contact ID for stripe customer: ' . $customerId);
}
$paymentProcessor = \Civi\Payment\System::singleton()
->getById($customerParams['processor_id']);
$paymentProcessor = \Civi\Payment\System::singleton()->getById($customerParams['processor_id']);
$paymentProcessor->setAPIParams();
// Get the stripe customer from stripe
......@@ -267,7 +266,7 @@ function civicrm_api3_stripe_customer_updatestripemetadata($params) {
$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);
$errorMessage = $paymentProcessor->handleErrorNotification($err, NULL);
throw new \Civi\Payment\Exception\PaymentProcessorException('Failed to retrieve Stripe Customer: ' . $errorMessage);
}
......
......@@ -149,9 +149,10 @@ function civicrm_api3_stripe_subscription_import($params) {
$paymentProcessor = \Civi\Payment\System::singleton()->getById($params['payment_processor_id'])->getPaymentProcessor();
// Now re-retrieve the data from Stripe to ensure it's legit.
\Stripe\Stripe::setApiKey($paymentProcessor['user_name']);
$processor = new CRM_Core_Payment_Stripe('', $paymentProcessor);
$processor->setAPIParams();
// Now re-retrieve the data from Stripe to ensure it's legit.
$stripeSubscription = \Stripe\Subscription::retrieve($params['subscription_id']);
// Create the stripe customer in CiviCRM
......
......@@ -8,16 +8,16 @@
"packages": [
{
"name": "stripe/stripe-php",
"version": "v6.40.0",
"version": "v6.43.1",
"source": {
"type": "git",
"url": "https://github.com/stripe/stripe-php.git",
"reference": "9c22ffab790ef4dae0f371929de50e8b53c9ec8d"
"reference": "42fcdaf99c44bb26937223f8eae1f263491d5ab8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/stripe/stripe-php/zipball/9c22ffab790ef4dae0f371929de50e8b53c9ec8d",
"reference": "9c22ffab790ef4dae0f371929de50e8b53c9ec8d",
"url": "https://api.github.com/repos/stripe/stripe-php/zipball/42fcdaf99c44bb26937223f8eae1f263491d5ab8",
"reference": "42fcdaf99c44bb26937223f8eae1f263491d5ab8",
"shasum": ""
},
"require": {
......@@ -60,7 +60,7 @@
"payment processing",
"stripe"
],
"time": "2019-06-27T23:24:51+00:00"
"time": "2019-08-29T16:56:12+00:00"
}
],
"packages-dev": [],
......
#card-element {
padding: 2%;
margin: 2% auto;
max-width: 800px;
background-color: ghostwhite;
-webkit-box-shadow: 10px 10px 7px -6px rgba(0,0,0,0.81);
-moz-box-shadow: 10px 10px 7px -6px rgba(0, 0, 0, 0.81);
box-shadow: 10px 10px 7px -6px rgba(0, 0, 0, 0.81);
}
#card-errors {
margin: 2%;
display: none;
}
......@@ -8,13 +8,21 @@ Latest releases can be found here: https://civicrm.org/extensions/stripe-payment
View this extension in the [Extension Directory](https://civicrm.org/extensions/stripe-payment-processor).
## Compatibility / Requirements
* CiviCRM 5.10+
* PHP 7.0+
* CiviCRM 5.13+
* PHP 7.1+
* Jquery 1.10 (Use jquery_update module on Drupal).
* Drupal 7 / Joomla / Wordpress (latest supported release). *Not currently tested with other CMS but it may work.*
* Stripe API version: 2019-02-19
* Stripe API version: 2019-09-09
* Drupal webform_civicrm 7.x-4.22+ (if using webform integration)
If using test mode with drupal webform_civicrm you need this patch: https://github.com/colemanw/webform_civicrm/pull/266
## Troubleshooting
Under *Administer->CiviContribute->Stripe Settings* you can find a setting:
* Enable Stripe Javascript debugging?
This can be switched on to output debug info to the browser console and can be used to debug problems with submitting your payments.
## Credits
Current Maintainer: Matthew Wire - https://www.mjwconsult.co.uk
......
## Release 6.0.alpha3
* Support recurring payments with paymentIntents/Elements. Cancel subscription with Stripe when we reach recurring end date
* **Update required Stripe API version to 2019-09-09**
* Handle confirmation pages properly for contribution pages (make sure we pass through paymentIntentID).
* Handle card declined on client side.
* Support creating recurring payment (subscription).
* Handle IPN events for charges / invoices (support cancel/refund etc).
* Add basic support for PaymentProcessor.refund API.
* Remove membership_type_tag from plan name.
## Release 6.0.alpha2
* Support Drupal Webform CiviCRM.
* Support Event Registration.
* Support Confirm/Thankyou pages on contribution pages / events.
* Support cards using 3dsecure and cards not using 3dsecure.
### Not Supported (should be in final 6.0 release):
* Recurring payments.
* Backend payments.
## Release 6.0.alpha1
* ONLY contribution pages with no confirm pages are supported.
## Release 6.0 (not yet released)
**This is a major new release. You cannot rollback once you've upgraded.**
**This extension REQUIRES the mjwshared extension available here: https://lab.civicrm.org/extensions/mjwshared**
* Use Stripe Elements: https://stripe.com/payments/elements.
* Use PaymentIntents and comply with the European SCA directive (https://stripe.com/docs/strong-customer-authentication).
* Require Stripe API Version: 2019-09-09 and ensure that all codepaths specify the API version.
* Switch publishable key/secret key in settings (upgrader does this automatically) so they are now "correct" per CiviCRM settings pages.
* Support cards using 3dsecure and cards not using 3dsecure (workflows with Stripe are slightly different but both are now handled).
## Release 5.4.1
* Don't overwrite system messages when performing webhook checks.
* Add form to handle creating/updating webhooks instead of automatically during system check (Thanks @artfulrobot)
......
<?xml version="1.0"?>
<extension key="com.drastikbydesign.stripe" type="module">
<file>stripe</file>
<name>Stripe</name>
<name>Stripe (SCA payments development version)</name>
<description>Stripe Payment Processor</description>
<urls>
<url desc="Main Extension Page">https://lab.civicrm.org/extensions/stripe</url>
......@@ -12,15 +12,18 @@
<author>Matthew Wire (MJW Consulting)</author>
<email>mjw@mjwconsult.co.uk</email>
</maintainer>
<releaseDate>2019-07-21</releaseDate>
<version>5.4.1</version>
<develStage>stable</develStage>
<releaseDate>2019-09-10</releaseDate>
<version>6.0.alpha3</version>
<develStage>alpha</develStage>
<compatibility>
<ver>5.13</ver>
</compatibility>
<comments>Original Author: Joshua Walker (drastik) - Drastik by Design.
Jamie Mcclelland (ProgressiveTech) did a lot of the 5.x compatibility work.
</comments>
<requires>
<ext>mjwshared</ext>
</requires>
<civix>
<namespace>CRM/Stripe</namespace>
</civix>
......
This diff is collapsed.