Commit 617c38bb authored by bgm's avatar bgm Committed by Aegir user

Update Stripe extension to 5.0

parent cca46ba3
<?php
/*
* Form Class for Stripe
*/
class CRM_Core_Form_Stripe extends CRM_Core_Form {
/**
* Function to access protected payProcessors array in event registraion forms
*/
public static function get_ppids(&$form) {
$payprocessorIds = $form->_paymentProcessors;
return $payprocessorIds;
}
}
...@@ -111,15 +111,17 @@ class CRM_Stripe_Page_Webhook extends CRM_Core_Page { ...@@ -111,15 +111,17 @@ class CRM_Stripe_Page_Webhook extends CRM_Core_Page {
CRM_Utils_System::civiExit(); CRM_Utils_System::civiExit();
} }
require_once ("packages/stripe-php/init.php");
\Stripe\Stripe::setAppInfo('CiviCRM', CRM_Utils_System::version(), CRM_Utils_System::baseURL()); \Stripe\Stripe::setAppInfo('CiviCRM', CRM_Utils_System::version(), CRM_Utils_System::baseURL());
\Stripe\Stripe::setApiKey($stripe_key); \Stripe\Stripe::setApiKey($stripe_key);
// Retrieve Event from Stripe using ID even though we already have the values now. // Retrieve Event from Stripe using ID even though we already have the values now.
// This is for extra security precautions mentioned here: https://stripe.com/docs/webhooks // This is for extra security precautions mentioned here: https://stripe.com/docs/webhooks
$stripe_event_data = \Stripe\Event::retrieve($data->id); $stripe_event_data = \Stripe\Event::retrieve($data->id);
$customer_id = $stripe_event_data->data->object->customer; // Not all event objects have a customer property. Check first.
if (isset($stripe_event_data->data->object->customer)) {
$customer_id = $stripe_event_data->data->object->customer;
}
switch($stripe_event_data->type) { switch($stripe_event_data->type) {
// Successful recurring payment. // Successful recurring payment.
case 'invoice.payment_succeeded': case 'invoice.payment_succeeded':
......
<?php <?php
require_once('packages/stripe-php/init.php');
/** /**
* Collection of upgrade steps. * Collection of upgrade steps.
* DO NOT USE a naming scheme other than upgrade_N, where N is an integer. * DO NOT USE a naming scheme other than upgrade_N, where N is an integer.
...@@ -115,7 +114,7 @@ class CRM_Stripe_Upgrader extends CRM_Stripe_Upgrader_Base { ...@@ -115,7 +114,7 @@ class CRM_Stripe_Upgrader extends CRM_Stripe_Upgrader_Base {
} }
} }
catch (CiviCRM_API3_Exception $e) { catch (CiviCRM_API3_Exception $e) {
CRM_Core_Error::debug_log_message("Cannot find a PaymentProcessorType named Stripe.", $out = false); Civi::log()->debug("Cannot find a PaymentProcessorType named Stripe.");
return; return;
} }
} }
...@@ -175,7 +174,7 @@ class CRM_Stripe_Upgrader extends CRM_Stripe_Upgrader_Base { ...@@ -175,7 +174,7 @@ class CRM_Stripe_Upgrader extends CRM_Stripe_Upgrader_Base {
)); ));
} }
catch (Exception $e) { catch (Exception $e) {
CRM_Core_Error::debug_log_message('Update 5004 failed. Has Stripe been removed as a payment processor?', $out = false); Civi::log()->debug('Update 5004 failed. Has Stripe been removed as a payment processor?', $out = false);
return; return;
} }
try { try {
...@@ -187,8 +186,7 @@ class CRM_Stripe_Upgrader extends CRM_Stripe_Upgrader_Base { ...@@ -187,8 +186,7 @@ class CRM_Stripe_Upgrader extends CRM_Stripe_Upgrader_Base {
} }
catch (Exception $e) { 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. // Don't quit here. A missing customer in Stipe is OK. They don't exist, so they can't have a subscription.
$debug_code = 'Cannot find Stripe API key: ' . $e->getMessage(); Civi::log()->debug('Cannot find Stripe API key: ' . $e->getMessage());
CRM_Core_Error::debug_log_message($debug_code, $out = false);
} }
if (!empty($subscription['data'][0]['id'])) { if (!empty($subscription['data'][0]['id'])) {
$query_params = array( $query_params = array(
...@@ -257,8 +255,7 @@ class CRM_Stripe_Upgrader extends CRM_Stripe_Upgrader_Base { ...@@ -257,8 +255,7 @@ class CRM_Stripe_Upgrader extends CRM_Stripe_Upgrader_Base {
} }
catch (CiviCRM_API3_Exception $e) { 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. // 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.
$debug_code = 'Recurring contribution search: ' . $e->getMessage(); Civi::log()->debug('Recurring contribution search: ' . $e->getMessage());
CRM_Core_Error::debug_log_message($debug_code, $out = false);
} }
if (!empty($recur_id)) { if (!empty($recur_id)) {
$p = array( $p = array(
......
<?php
/**
* 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
* events that were not generated by the Stripe server.
*/
/**
* Stripe.Ipn 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_Ipn_spec(&$spec) {
$spec['id']['title'] = ts("CiviCRM System Log id to replay from system log.");
$spec['evtid']['title'] = ts("An event id as generated by Stripe.");
$spec['ppid']['title'] = ts("The payment processor to use (required if using evtid)");
$spec['noreceipt']['title'] = ts("Set to 1 to override contribution page settings and do not send a receipt (default is off or 0). )");
$spec['noreceipt']['api.default'] = 0;
}
/**
* Stripe.Ipn API
*
* @param array $params
* @return array API result descriptor
* @see civicrm_api3_create_success
* @see civicrm_api3_create_error
* @throws API_Exception
*/
function civicrm_api3_stripe_Ipn($params) {
$object = NULL;
$ppid = NULL;
if (array_key_exists('id', $params)) {
$data = civicrm_api3('SystemLog', 'getsingle', array('id' => $params['id'], 'return' => array('message', 'context')));
if (empty($data)) {
throw new API_Exception('Failed to find that entry in the system log', 3234);
}
$object = json_decode($data['context']);
if (preg_match('/processor_id=([0-9]+)$/', $object['message'], $matches)) {
$ppid = $matches[1];
}
else {
throw new API_Exception('Failed to find payment processor id in system log', 3235);
}
}
elseif (array_key_exists('evtid', $params)) {
if (!array_key_exists('ppid', $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'];
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.
$sql = "SELECT COUNT(*) AS count FROM civicrm_contribution WHERE trxn_id = %0";
$sql_params = array(0 => array($object->data->object->charge, 'String'));
$dao = CRM_Core_DAO::executeQuery($sql, $sql_params);
$dao->fetch();
if ($dao->count > 0) {
return civicrm_api3_create_error("Ipn already processed.");
}
if (class_exists('CRM_Core_Payment_StripeIPN')) {
// The $_GET['processor_id'] value is normally set by
// CRM_Core_Payment::handlePaymentMethod
$_GET['processor_id'] = $ppid;
$ipnClass = new CRM_Core_Payment_StripeIPN($object);
if ($params['noreceipt'] == 1) {
$ipnClass->is_email_receipt = 0;
}
$ipnClass->main();
}
else {
trigger_error("The api depends on CRM_Core_Payment_StripeIPN");
}
return civicrm_api3_create_success(array());
}
<?php
/**
* This api provides a list of events generated by Stripe
*
* See the Stripe event reference for a full explanation of the options.
* https://stripe.com/docs/api#events
*/
/**
* Stripe.ListEvents 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_ListEvents_spec(&$spec) {
$spec['ppid']['title'] = ts("Use the given Payment Processor ID");
$spec['ppid']['type'] = CRM_Utils_Type::T_INT;
$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']['title'] = ts("How to format the output, brief or raw. Defaults to brief.");
}
/**
* Stripe.VerifyEventType
*
* @param string $eventType
* @return bolean True if valid type, false otherwise.
*/
function civicrm_api3_stripe_VerifyEventType($eventType) {
return in_array($eventType, array(
'account.external_account.created',
'account.external_account.deleted',
'account.external_account.updated',
'application_fee.created',
'application_fee.refunded',
'application_fee.refund.updated',
'balance.available',
'bitcoin.receiver.created',
'bitcoin.receiver.filled',
'bitcoin.receiver.updated',
'bitcoin.receiver.transaction.created',
'charge.captured',
'charge.failed',
'charge.pending',
'charge.refunded',
'charge.succeeded',
'charge.updated',
'charge.dispute.closed',
'charge.dispute.created',
'charge.dispute.funds_reinstated',
'charge.dispute.funds_withdrawn',
'charge.dispute.updated',
'charge.refund.updated',
'coupon.created',
'coupon.deleted',
'coupon.updated',
'customer.created',
'customer.deleted',
'customer.updated',
'customer.discount.created',
'customer.discount.deleted',
'customer.discount.updated',
'customer.source.created',
'customer.source.deleted',
'customer.source.updated',
'customer.subscription.created',
'customer.subscription.deleted',
'customer.subscription.trial_will_end',
'customer.subscription.updated',
'invoice.created',
'invoice.payment_failed',
'invoice.payment_succeeded',
'invoice.upcoming',
'invoice.updated',
'invoiceitem.created',
'invoiceitem.deleted',
'invoiceitem.updated',
'order.created',
'order.payment_failed',
'order.payment_succeeded',
'order.updated',
'order_return.created',
'payout.canceled',
'payout.created',
'payout.failed',
'payout.paid',
'payout.updated',
'plan.created',
'plan.deleted',
'plan.updated',
'product.created',
'product.deleted',
'product.updated',
'recipient.created',
'recipient.deleted',
'recipient.updated',
'review.closed',
'review.opened',
'sku.created',
'sku.deleted',
'sku.updated',
'source.canceled',
'source.chargeable',
'source.failed',
'source.transaction.created',
'transfer.created',
'transfer.reversed',
'transfer.updated',
'ping',
)
);
}
/**
* Process parameters to determine ppid and sk.
*
* @param array $params
*/
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'];
}
if (array_key_exists('limit', $params) ) {
$limit = $params['limit'];
}
if (array_key_exists('starting_after', $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.
if (!civicrm_api3_stripe_VerifyEventType($params['type'])) {
throw new API_Exception("Unrecognized Event Type.", 1236);
}
else {
$type = $params['type'];
}
}
// Created can only be passed in as an array
if (array_key_exists('created', $params)) {
$created = $params['created'];
if (!is_array($created)) {
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);
}
/**
* Stripe.ListEvents API
*
* @param array $params
* @return array API result descriptor
* @see civicrm_api3_create_success
* @see civicrm_api3_create_error
* @throws API_Exception
*/
function civicrm_api3_stripe_Listevents($params) {
$parsed = civicrm_api3_stripe_ProcessParams($params);
$sk = $parsed['sk'];
$type = $parsed['type'];
$created = $parsed['created'];
$limit = $parsed['limit'];
$starting_after = $parsed['starting_after'];
$args = array();
if ($type) {
$args['type'] = $type;
}
if ($created) {
$args['created'] = $created;
}
if ($limit) {
$args['limit'] = $limit;
}
if ($starting_after) {
$args['starting_after'] = $starting_after;
}
require_once ("vendor/stripe/stripe-php/init.php");
\Stripe\Stripe::setApiKey($sk);
$data_list = \Stripe\Event::all($args);
if (array_key_exists('error', $data_list)) {
$err = $data_list['error'];
throw new API_Exception(/*errorMessage*/ "Stripe returned an error: " . $err->message, /*errorCode*/ $err->type);
}
$out = $data_list;
if ($params['output'] == 'brief') {
$out = array();
foreach($data_list['data'] as $data) {
$item = array(
'id' => $data['id'],
'created' => date('Y-m-d H:i:s', $data['created']),
'livemode' => $data['livemode'],
'pending_webhooks' => $data['pending_webhooks'],
'type' => $data['type'],
);
if (preg_match('/invoice\.payment_/', $data['type'])) {
$item['invoice'] = $data['data']['object']->id;
$item['charge'] = $data['data']['object']->charge;
$item['customer'] = $data['data']['object']->customer;
$item['subscription'] = $data['data']['object']->subscription;
$item['total'] = $data['data']['object']->total;
// Check if this is in the contributions table.
$item['processed'] = 'no';
$results = civicrm_api3('Contribution', 'get', array('trxn_id' => $item['charge']));
if ($results['count'] > 0) {
$item['processed'] = 'yes';
}
}
$out[] = $item;
}
}
return civicrm_api3_create_success($out);
}
<?php
/**
* Populate the CiviCRM civicrm_system_log with Stripe events.
*
* This api will take all stripe events known to Stripe that are of the type
* invoice.payment_succeeded and add them * to the civicrm_system_log table.
* It will not add an event that has already been added, so it can be run
* multiple times. Once added, they can be replayed using the Stripe.Ipn
* api call.
*/
/**
* Stripe.Populatelog 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_Populatelog_spec(&$spec) {
$spec['ppid']['title'] = ts("The id of the payment processor.");
}
/**
* Stripe.Populatelog API
*
* @param array $params
* @return array API result descriptor
* @see civicrm_api3_create_success
* @see civicrm_api3_create_error
* @throws API_Exception
*/
function civicrm_api3_stripe_Populatelog($params) {
$ppid = NULL;
if (array_key_exists('ppid', $params)) {
$ppid = $params['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, 'return' => 'id');
try {
$ppid = civicrm_api3('PaymentProcessor', 'getvalue', $params);
}
catch (CiviCRM_API3_Exception $e) {
throw new API_Exception("Expected one live Stripe payment processor, but found none or more than one. Please specify ppid=.", 2234);
}
}
$params = array('limit' => 100, 'type' => 'invoice.payment_succeeded');
if ($ppid) {
$params['ppid'] = $ppid;
}
$items = array();
$last_item = NULL;
$more = TRUE;
while(1) {
if ($last_item) {
$params['starting_after'] = $last_item->id;
}
$objects = civicrm_api3('Stripe', 'Listevents', $params);
if (count($objects['values']['data']) == 0) {
// No more!
break;
}
$items = array_merge($items, $objects['values']['data']);
$last_item = end($objects['values']['data']);
}
$results = array();
foreach($items as $item) {
$id = $item->id;
// Insert into System Log if it doesn't exist.
$like_event_id = '%event_id=' . addslashes($id);
$sql = "SELECT id FROM civicrm_system_log WHERE message LIKE '$like_event_id'";
$dao= CRM_Core_DAO::executeQuery($sql);
if ($dao->N == 0) {
$message = "payment_notification processor_id=${ppid} event_id=${id}";
$contact_id = civicrm_api3_stripe_cid_for_trxn($item->data->object->charge);
if ($contact_id) {
$item['contact_id'] = $contact_id;
}
$log = new CRM_Utils_SystemLogger();
$log->alert($message, $item);
$results[] = $id;
}
}
return civicrm_api3_create_success($results);
}
function civcrm_api3_stripe_cid_for_trxn($trxn) {
$params = array('trxn_id' => $trxn, 'return' => 'contact_id');
$result = civicrm_api3('Contribution', 'getvalue', $params);
return $result;
}
<?php
/**
* This api sets up a Stripe Payment Processor with test credentials.
*
* This api should only be used for testing purposes.
*/
/**
* Stripe.Setuptest 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_Setuptest_spec(&$spec) {
// Note: these test credentials belong to PTP and are contributed to
// tests can be automated. If you are setting up your own testing
// infrastructure, please use your own keys.
$spec['sk']['api.default'] = 'sk_test_TlGdeoi8e1EOPC3nvcJ4q5UZ';
$spec['pk']['api.default'] = 'pk_test_k2hELLGpBLsOJr6jZ2z9RaYh';
}
/**
* Stripe.Setuptest API
*
* @param array $params
* @return array API result descriptor
* @see civicrm_api3_create_success
* @see civicrm_api3_create_error
* @throws API_Exception
*/
function civicrm_api3_stripe_Setuptest($params) {
$params = array(
'name' => 'Stripe',
'domain_id' => CRM_Core_Config::domainID(),
'payment_processor_type_id' => 'Stripe',
'title' => 'Stripe',
'is_active' => 1,
'is_default' => 0,
'is_test' => 1,
'is_recur' => 1,
'user_name' => $params['sk'],
'password' => $params['pk'],
'url_site' => 'https://api.stripe.com/v1',
'url_recur' => 'https://api.stripe.com/v1',
'class_name' => 'Payment_Stripe',
'billing_mode' => 1
);
// First see if it already exists.
$result = civicrm_api3('PaymentProcessor', 'get', $params);
if ($result['count'] != 1) {
// Nope, create it.
$result = civicrm_api3('PaymentProcessor', 'create', $params);