Commit c359c9c5 authored by mattwire's avatar mattwire Committed by mattwire

Add StripeSubscription import api

parent c04aefe2
......@@ -151,102 +151,7 @@ class CRM_Core_Payment_StripeIPN extends CRM_Core_Payment_BaseIPN {
* @throws \CRM_Core_Exception
*/
public function retrieve($name, $type, $abort = TRUE) {
$className = get_class($this->_inputParameters->data->object);
$value = NULL;
switch ($className) {
case 'Stripe\Charge':
switch ($name) {
case 'charge_id':
$value = $this->_inputParameters->data->object->id;
break;
case 'failure_code':
$value = $this->_inputParameters->data->object->failure_code;
break;
case 'failure_message':
$value = $this->_inputParameters->data->object->failure_message;
break;
case 'refunded':
$value = $this->_inputParameters->data->object->refunded;
break;
case 'amount_refunded':
$value = $this->_inputParameters->data->object->amount_refunded;
break;
}
break;
case 'Stripe\Invoice':
switch ($name) {
case 'charge_id':
$value = $this->_inputParameters->data->object->charge;
break;
case 'invoice_id':
$value = $this->_inputParameters->data->object->id;
break;
case 'receive_date':
$value = date("Y-m-d H:i:s", $this->_inputParameters->data->object->date);
break;
case 'subscription_id':
$value = $this->_inputParameters->data->object->subscription;
break;
}
break;
case 'Stripe\Subscription':
switch ($name) {
case 'frequency_interval':
$value = $this->_inputParameters->data->object->plan->interval_count;
break;
case 'frequency_unit':
$value = $this->_inputParameters->data->object->plan->interval;
break;
case 'plan_amount':
$value = $this->_inputParameters->data->object->plan->amount / 100;
break;
case 'plan_id':
$value = $this->_inputParameters->data->object->plan->id;
break;
case 'plan_name':
$value = $this->_inputParameters->data->object->plan->name;
break;
case 'plan_start':
$value = date("Y-m-d H:i:s", $this->_inputParameters->data->object->start);
break;
case 'subscription_id':
$value = $this->_inputParameters->data->object->id;
break;
}
break;
}
// Common parameters
switch ($name) {
case 'customer_id':
$value = $this->_inputParameters->data->object->customer;
break;
case 'event_type':
$value = $this->_inputParameters->type;
break;
case 'previous_plan_id':
if (preg_match('/\.updated$/', $this->_inputParameters->type)) {
$value = $this->_inputParameters->data->previous_attributes->plan->id;
}
break;
}
$value = CRM_Stripe_Api::getObjectParam($name, $type, $this->_inputParameters->data->object);
$value = CRM_Utils_Type::validate($value, $type, FALSE);
if ($abort && $value === NULL) {
......@@ -262,7 +167,7 @@ class CRM_Core_Payment_StripeIPN extends CRM_Core_Payment_BaseIPN {
*/
public function main() {
// Collect and determine all data about this event.
$this->event_type = $this->retrieve('event_type', 'String');
$this->event_type = CRM_Stripe_Api::getParam('event_type', $this->_inputParameters);
$pendingStatusId = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Pending');
......@@ -454,13 +359,17 @@ class CRM_Core_Payment_StripeIPN extends CRM_Core_Payment_BaseIPN {
*/
public function setInfo() {
$abort = FALSE;
$this->customer_id = $this->retrieve('customer_id', 'String');
$this->customer_id = CRM_Stripe_Api::getParam('customer_id', $this->_inputParameters);
if (empty($this->customer_id)) {
$this->exception('Missing customer_id!');
}
$this->previous_plan_id = CRM_Stripe_Api::getParam('previous_plan_id', $this->_inputParameters);
$this->subscription_id = $this->retrieve('subscription_id', 'String', $abort);
$this->invoice_id = $this->retrieve('invoice_id', 'String', $abort);
$this->receive_date = $this->retrieve('receive_date', 'String', $abort);
$this->charge_id = $this->retrieve('charge_id', 'String', $abort);
$this->plan_id = $this->retrieve('plan_id', 'String', $abort);
$this->previous_plan_id = $this->retrieve('previous_plan_id', 'String', $abort);
$this->plan_amount = $this->retrieve('plan_amount', 'String', $abort);
$this->frequency_interval = $this->retrieve('frequency_interval', 'String', $abort);
$this->frequency_unit = $this->retrieve('frequency_unit', 'String', $abort);
......
<?php
class CRM_Stripe_Api {
public static function getObjectParam($name, $stripeObject) {
$className = get_class($stripeObject);
switch ($className) {
case 'Stripe\Charge':
switch ($name) {
case 'charge_id':
return (string) $stripeObject->id;
case 'failure_code':
return (string) $stripeObject->failure_code;
case 'failure_message':
return (string) $stripeObject->failure_message;
case 'refunded':
return (bool) $stripeObject->refunded;
case 'amount_refunded':
return (int) $stripeObject->amount_refunded / 100;
}
break;
case 'Stripe\Invoice':
switch ($name) {
case 'charge_id':
return (string) $stripeObject->charge;
case 'invoice_id':
return (string) $stripeObject->id;
case 'receive_date':
return date("Y-m-d H:i:s", $stripeObject->date);
case 'subscription_id':
return (string) $stripeObject->subscription;
case 'amount':
return (string) $stripeObject->amount_due / 100;
case 'amount_paid':
return (string) $stripeObject->amount_paid / 100;
case 'amount_remaining':
return (string) $stripeObject->amount_remaining / 100;
case 'currency':
return (string) mb_strtoupper($stripeObject->currency);
case 'status_id':
if ((bool)$stripeObject->paid) {
return 'Completed';
}
else {
return 'Pending';
}
case 'description':
return (string) $stripeObject->description;
}
break;
case 'Stripe\Subscription':
switch ($name) {
case 'frequency_interval':
return (string) $stripeObject->plan->interval_count;
case 'frequency_unit':
return (string) $stripeObject->plan->interval;
case 'plan_amount':
return (string) $stripeObject->plan->amount / 100;
case 'currency':
return (string) mb_strtoupper($stripeObject->plan->currency);
case 'plan_id':
return (string) $stripeObject->plan->id;
case 'plan_name':
return (string) $stripeObject->plan->name;
case 'plan_start':
return date("Y-m-d H:i:s", $stripeObject->start);
case 'cycle_day':
return date("d", $stripeObject->billing_cycle_anchor);
case 'subscription_id':
return (string) $stripeObject->id;
case 'status_id':
switch ($stripeObject->status) {
case \Stripe\Subscription::STATUS_ACTIVE:
return CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'In Progress');
case \Stripe\Subscription::STATUS_CANCELED:
return CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Cancelled');
}
case 'customer_id':
return (string) $stripeObject->customer;
}
break;
}
return NULL;
}
public static function getParam($name, $stripeObject) {
// Common parameters
switch ($name) {
case 'customer_id':
return (string) $stripeObject->customer;
case 'event_type':
return (string) $stripeObject->type;
case 'previous_plan_id':
if (preg_match('/\.updated$/', $stripeObject->type)) {
return (string) $stripeObject->data->previous_attributes->plan->id;
}
break;
}
return NULL;
}
}
......@@ -109,13 +109,12 @@ class CRM_Stripe_Customer {
/**
* @param $params
* @param $paymentProcessor
*
* @return \Stripe\ApiResource
* @throws \CiviCRM_API3_Exception
* @throws \Civi\Payment\Exception\PaymentProcessorException
*/
public static function create($params, $paymentProcessor) {
public static function create($params) {
$requiredParams = ['contact_id', 'card_token', 'processor_id'];
// $optionalParams = ['email'];
foreach ($requiredParams as $required) {
......
......@@ -74,7 +74,7 @@ function civicrm_api3_stripe_customer_get($params) {
}
/**
* StripeCustomer.Get API specification
* StripeCustomer.delete API specification
*
* @param array $spec description of fields supported by this API call
* @return void
......@@ -105,6 +105,41 @@ function civicrm_api3_stripe_customer_delete($params) {
return civicrm_api3_create_success([]);
}
/**
* StripeCustomer.create API specification
*
* @param array $spec description of fields supported by this API call
* @return void
* @see http://wiki.civicrm.org/confluence/display/CRMDOC/API+Architecture+Standards
*/
function _civicrm_api3_stripe_customer_create_spec(&$spec) {
$spec['id']['title'] = ts("Stripe Customer ID");
$spec['id']['type'] = CRM_Utils_Type::T_STRING;
$spec['id']['api.required'] = TRUE;
$spec['contact_id']['title'] = ts("CiviCRM Contact ID");
$spec['contact_id']['type'] = CRM_Utils_Type::T_INT;
$spec['contact_id']['api.required'] = TRUE;
$spec['processor_id']['title'] = ts("Payment Processor ID");
$spec['processor_id']['type'] = CRM_Utils_Type::T_INT;
$spec['processor_id']['api.required'] = TRUE;
}
/**
* StripeCustomer.create API
* This api will add a stripe customer to CiviCRM
*
* @param array $params
* @see civicrm_api3_create_success
*
* @throws \Civi\Payment\Exception\PaymentProcessorException
* @return array
*/
function civicrm_api3_stripe_customer_create($params) {
CRM_Stripe_Customer::add($params);
return civicrm_api3_create_success([]);
}
/**
* Stripe.Customer.Updatecontactids API
* This api will update the civicrm_stripe_customers table and add contact IDs for all known email addresses
......
......@@ -132,3 +132,153 @@ WHERE trxn_id=%1;";
}
return civicrm_api3_create_success($counts);
}
/**
* API to import a stripe subscription, create a customer, recur, contribution and optionally link to membership
* You run it once for each subscription and it creates/updates a recurring contribution in civicrm (and optionally links it to a membership).
*
* @param array $params
*
* @return array
* @throws \API_Exception
* @throws \CiviCRM_API3_Exception
* @throws \Stripe\Error\Api
*/
function civicrm_api3_stripe_subscription_import($params) {
civicrm_api3_verify_mandatory($params, NULL, ['subscription_id', 'contact_id', 'payment_processor_id']);
$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']);
$stripeSubscription = \Stripe\Subscription::retrieve($params['subscription_id']);
// Create the stripe customer in CiviCRM
$customerParams = [
'id' => CRM_Stripe_Api::getObjectParam('customer_id', $stripeSubscription),
'contact_id' => $params['contact_id'],
'processor_id' => (int) $params['payment_processor_id'],
];
$customer = civicrm_api3('StripeCustomer', 'get', $customerParams);
if (empty($customer['count'])) {
civicrm_api3('StripeCustomer', 'create', $customerParams);
}
// Create the recur record in CiviCRM
$contributionRecurParams = [
'contact_id' => $params['contact_id'],
'amount' => CRM_Stripe_Api::getObjectParam('plan_amount', $stripeSubscription),
'currency' => CRM_Stripe_Api::getObjectParam('currency', $stripeSubscription),
'frequency_unit' => CRM_Stripe_Api::getObjectParam('frequency_unit', $stripeSubscription),
'frequency_interval' => CRM_Stripe_Api::getObjectParam('frequency_interval', $stripeSubscription),
'start_date' => CRM_Stripe_Api::getObjectParam('plan_start', $stripeSubscription),
'processor_id' => $params['subscription_id'],
'trxn_id' => $params['subscription_id'],
'contribution_status_id' => CRM_Stripe_Api::getObjectParam('status_id', $stripeSubscription),
'cycle_day' => CRM_Stripe_Api::getObjectParam('cycle_day', $stripeSubscription),
'auto_renew' => 1,
'payment_processor_id' => $params['payment_processor_id'],
'payment_instrument_id' => !empty($params['payment_instrument_id']) ? $params['payment_instrument_id'] : 'Credit Card',
'financial_type_id' => !empty($params['financial_type_id']) ? $params['financial_type_id'] : 'Donation',
'is_email_receipt' => !empty($params['is_email_receipt']) ? 1 : 0,
'is_test' => isset($paymentProcessor['is_test']) && $paymentProcessor['is_test'] ? 1 : 0,
];
if ($params['recur_id']) {
$contributionRecurParams['id'] = $params['recur_id'];
}
$contributionRecur = civicrm_api3('ContributionRecur', 'create', $contributionRecurParams);
// Get the invoices for the subscription
$invoiceParams = [
'customer' => CRM_Stripe_Api::getObjectParam('customer_id', $stripeSubscription),
'limit' => 10,
//'due_date[lte]' => time(),
];
$stripeInvoices = \Stripe\Invoice::all($invoiceParams);
foreach ($stripeInvoices->data as $stripeInvoice) {
if (CRM_Stripe_Api::getObjectParam('subscription_id', $stripeInvoice) === $params['subscription_id']) {
$contributionParams = [
'contact_id' => $params['contact_id'],
'total_amount' => CRM_Stripe_Api::getObjectParam('amount', $stripeInvoice),
'currency' => CRM_Stripe_Api::getObjectParam('currency', $stripeInvoice),
'receive_date' => CRM_Stripe_Api::getObjectParam('receive_date', $stripeInvoice),
'trxn_id' => CRM_Stripe_Api::getObjectParam('charge_id', $stripeInvoice),
'contribution_status_id' => CRM_Stripe_Api::getObjectParam('status_id', $stripeInvoice),
'payment_instrument_id' => !empty($params['payment_instrument_id']) ? $params['payment_instrument_id'] : 'Credit Card',
'financial_type_id' => !empty($params['financial_type_id']) ? $params['financial_type_id'] : 'Donation',
'is_test' => isset($paymentProcessor['is_test']) && $paymentProcessor['is_test'] ? 1 : 0,
'contribution_source' => CRM_Stripe_Api::getObjectParam('description', $stripeInvoice),
'contribution_recur_id' => $contributionRecur['id'],
];
$existingContribution = civicrm_api3('Contribution', 'get',
[
'contribution_test' => '',
'trxn_id' => $contributionParams['trxn_id']
]);
if (!empty($existingContribution['id'])) {
$contributionParams['id'] = $existingContribution['id'];
}
elseif ($params['contribution_id']) {
$contributionParams['id'] = $params['contribution_id'];
}
$contribution = civicrm_api3('Contribution', 'create', $contributionParams);
break;
}
}
// Link to membership record
if (!empty($params['membership_id'])) {
$membershipParams = [
'id' => $params['membership_id'],
'contribution_recur_id' => $contributionRecur['id'],
];
$membership = civicrm_api3('Membership', 'create', $membershipParams);
}
$results = [
'subscription_id' => $params['subscription_id'],
'customer_id' => CRM_Stripe_Api::getObjectParam('customer_id', $stripeSubscription),
'recur_id' => $contributionRecur['id'],
'contribution_id' => !empty($contribution['id'])? $contribution['id'] : NULL,
'membership_id' => !empty($membership['id']) ? $membership['id'] : NULL,
];
return civicrm_api3_create_success($results, $params, 'StripeSubscription', 'import');
}
function _civicrm_api3_stripe_subscription_import_spec(&$spec) {
$spec['subscription_id']['title'] = ts("Stripe Subscription ID");
$spec['subscription_id']['type'] = CRM_Utils_Type::T_STRING;
$spec['subscription_id']['api.required'] = TRUE;
$spec['contact_id']['title'] = ts("Contact ID");
$spec['contact_id']['type'] = CRM_Utils_Type::T_INT;
$spec['contact_id']['api.required'] = TRUE;
$spec['payment_processor_id']['title'] = ts("Payment Processor ID");
$spec['payment_processor_id']['type'] = CRM_Utils_Type::T_INT;
$spec['payment_processor_id']['api.required'] = TRUE;
$spec['recur_id']['title'] = ts("Contribution Recur ID");
$spec['recur_id']['type'] = CRM_Utils_Type::T_INT;
$spec['contribution_id']['title'] = ts("Contribution ID");
$spec['contribution_id']['type'] = CRM_Utils_Type::T_INT;
$spec['membership_id']['title'] = ts("Membership ID");
$spec['membership_id']['type'] = CRM_Utils_Type::T_INT;
$spec['financial_type_id'] = [
'title' => 'Financial ID (ignored if more than one line item)',
'name' => 'financial_type_id',
'type' => CRM_Utils_Type::T_INT,
'pseudoconstant' => [
'table' => 'civicrm_financial_type',
'keyColumn' => 'id',
'labelColumn' => 'name',
],
];
$spec['payment_instrument_id']['api.aliases'] = ['payment_instrument'];
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment