Skip to content
Snippets Groups Projects
Commit 4a1bd83a authored by Jamie McClelland's avatar Jamie McClelland Committed by mattwire
Browse files

make payment intent process an api call, include tests

This merge requests also fixes the previously broken tests.
parent 1097edc3
Branches
Tags
1 merge request!1246.5
......@@ -817,9 +817,9 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
'paymentintent_id' => $intent->id,
'payment_processor_id' => $this->_paymentProcessor['id'],
'status' => $intent->status,
'contribution_id' => $params['contributionID'],
'contribution_id' => $params['contributionID'] ?? NULL,
'description' => $this->getDescription($params, 'description'),
'identifier' => $params['qfKey'],
'identifier' => $params['qfKey'] ?? NULL,
'contact_id' => $params['contactID'],
];
if (empty($intentParams['contribution_id'])) {
......
......@@ -9,6 +9,8 @@
+--------------------------------------------------------------------+
*/
use CRM_Stripe_ExtensionUtil as E;
/**
* StripePaymentintent.create API
*
......@@ -44,3 +46,144 @@ function civicrm_api3_stripe_paymentintent_delete($params) {
function civicrm_api3_stripe_paymentintent_get($params) {
return _civicrm_api3_basic_get('CRM_Stripe_BAO_StripePaymentintent', $params, TRUE, 'StripePaymentintent');
}
/**
* StripePaymentintent.process API specification
*
* @param array $spec description of fields supported by this API call
*
* @return void
*/
function _civicrm_api3_stripe_paymentintent_process_spec(&$spec) {
$spec['payment_method_id']['title'] = E::ts("Stripe generated code used to create a payment intent.");
$spec['payment_method_id']['type'] = CRM_Utils_Type::T_STRING;
$spec['payment_method_id']['api.default'] = NULL;
$spec['payment_intent_id']['title'] = ts("The payment intent id itself, if available.");
$spec['payment_intent_id']['type'] = CRM_Utils_Type::T_STRING;
$spec['payment_intent_id']['api.default'] = NULL;
$spec['amount']['title'] = ts("The payment amount.");
$spec['amount']['type'] = CRM_Utils_Type::T_STRING;
$spec['amount']['api.default'] = NULL;
$spec['capture']['title'] = ts("Whether we should try to capture the amount, not just confirm it.");
$spec['capture']['type'] = CRM_Utils_Type::T_BOOLEAN;
$spec['capture']['api.default'] = FALSE;
$spec['description']['title'] = ts("Describe the payment.");
$spec['description']['type'] = CRM_Utils_Type::T_STRING;
$spec['description']['api.default'] = NULL;
$spec['currency']['title'] = ts("Whether we should try to capture the amount, not just confirm it.");
$spec['currency']['type'] = CRM_Utils_Type::T_STRING;
$spec['currency']['api.default'] = CRM_Core_Config::singleton()->defaultCurrency;
$spec['payment_processor_id']['title'] = ts("The stripe payment processor id.");
$spec['payment_processor_id']['type'] = CRM_Utils_Type::T_INT;
$spec['payment_processor_id']['api.required'] = TRUE;
}
/**
* StripePaymentintent.process API
*
* In the normal flow of a CiviContribute form, this will be called with a
* payment_method_id (which is generated by Stripe via its javascript code),
* in which case it will create a PaymentIntent using that and *attempt* to
* 'confirm' it.
*
* This can also be called with a payment_intent_id instead, in which case it
* will retrieve the PaymentIntent and attempt (again) to 'confirm' it. This
* is useful to confirm funds after a user has completed SCA in their
* browser.
*
* 'confirming' a PaymentIntent refers to the process by which the funds are
* reserved in the cardholder's account, but not actually taken yet.
*
* Taking the funds ('capturing') should go through without problems once the
* transaction has been confirmed - this is done later on in the process.
*
* Nb. confirmed funds are released and will become available to the
* cardholder again if the PaymentIntent is cancelled or is not captured
* within 1 week.
*
* @param array $params
*
* @return array API result descriptor
* @throws \API_Exception
* @throws \CiviCRM_API3_Exception
* @throws \Stripe\Error\Api
*/
function civicrm_api3_stripe_paymentintent_process($params) {
$paymentMethodID = $params['payment_method_id'];
$paymentIntentID = $params['payment_intent_id'];
$amount = $params['amount'];
$capture = $params['capture'];
$title = $params['description'];
$confirm = TRUE;
if (empty($amount)) {
$amount = 1;
$confirm = FALSE;
}
$currency = $params['currency'];
$processorID = $params['payment_processor_id'];
$processor = new CRM_Core_Payment_Stripe('', civicrm_api3('PaymentProcessor', 'getsingle', ['id' => $processorID]));
$processor->setAPIParams();
if ($paymentIntentID) {
// We already have a PaymentIntent, retrieve and attempt confirm.
$intent = \Stripe\PaymentIntent::retrieve($paymentIntentID);
if ($intent->status === 'requires_confirmation') {
$intent->confirm();
}
if ($capture && $intent->status === 'requires_capture') {
$intent->capture();
}
}
else {
// We don't yet have a PaymentIntent, create one using the
// Payment Method ID and attempt to confirm it too.
$intent = \Stripe\PaymentIntent::create([
'payment_method' => $paymentMethodID,
'amount' => $processor->getAmount(['amount' => $amount, 'currency' => $currency]),
'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' => $confirm,
]);
}
// Save the generated paymentIntent in the CiviCRM database for later tracking
$intentParams = [
'paymentintent_id' => $intent->id,
'payment_processor_id' => $processorID,
'status' => $intent->status,
'description' => $title,
];
CRM_Stripe_BAO_StripePaymentintent::create($intentParams);
if ($intent->status === 'requires_action' &&
$intent->next_action->type === 'use_stripe_sdk') {
// Tell the client to handle the action
return civicrm_api3_create_success([
'requires_action' => true,
'payment_intent_client_secret' => $intent->client_secret,
]);
}
elseif (($intent->status === 'requires_capture') || ($intent->status === 'requires_confirmation')) {
// paymentIntent = requires_capture / requires_confirmation
// The payment intent has been confirmed, we just need to capture the payment
// Handle post-payment fulfillment
return civicrm_api3_create_success([
'success' => true,
'paymentIntent' => ['id' => $intent->id],
]);
}
elseif ($intent->status === 'succeeded') {
return civicrm_api3_create_success([
'success' => true,
'paymentIntent' => ['id' => $intent->id],
]);
}
else {
// Invalid status
throw new API_Exception('Invalid PaymentIntent status');
}
}
......@@ -16,43 +16,25 @@ use Civi\Test\TransactionalInterface;
define('STRIPE_PHPUNIT_TEST', 1);
/**
* FIXME - Add test description.
*
* Tips:
* - With HookInterface, you may implement CiviCRM hooks directly in the test class.
* Simply create corresponding functions (e.g. "hook_civicrm_post(...)" or similar).
* - With TransactionalInterface, any data changes made by setUp() or test****() functions will
* rollback automatically -- as long as you don't manipulate schema or truncate tables.
* If this test needs to manipulate schema or truncate tables, then either:
* a. Do all that using setupHeadless() and Civi\Test.
* b. Disable TransactionalInterface, and handle all setup/teardown yourself.
* This class provides helper functions for other Stripe Tests. There are no
* tests in this class.
*
* @group headless
*/
class CRM_Stripe_BaseTest extends \PHPUnit\Framework\TestCase implements HeadlessInterface, HookInterface, TransactionalInterface {
protected $_contributionID;
protected $_invoiceID = 'in_19WvbKAwDouDdbFCkOnSwAN7';
protected $_financialTypeID = 1;
protected $org;
protected $_orgID;
protected $contributionID;
protected $financialTypeID = 1;
protected $contact;
protected $_contactID;
protected $_contributionPageID;
protected $_paymentProcessorID;
protected $_paymentProcessor;
protected $_trxn_id;
protected $_created_ts;
protected $_subscriptionID;
protected $_membershipTypeID;
// Secret/public keys are PTP test keys.
protected $_sk = 'sk_test_TlGdeoi8e1EOPC3nvcJ4q5UZ';
protected $_pk = 'pk_test_k2hELLGpBLsOJr6jZ2z9RaYh';
protected $_cc = NULL;
protected $contactID;
protected $paymentProcessorID;
protected $paymentProcessor;
protected $trxn_id;
protected $processorID;
protected $cc = '4111111111111111';
protected $total = '400.00';
public function setUpHeadless() {
// Civi\Test has many helpers, like install(), uninstall(), sql(), and sqlFile().
// See: https://github.com/civicrm/org.civicrm.testapalooza/blob/master/civi-test.md
return \Civi\Test::headless()
->install('mjwshared')
->installMe(__DIR__)
......@@ -64,23 +46,7 @@ class CRM_Stripe_BaseTest extends \PHPUnit\Framework\TestCase implements Headles
require_once('vendor/stripe/stripe-php/init.php');
$this->createPaymentProcessor();
$this->createContact();
$this->createContributionPage();
$this->_created_ts = time();
$this->set_cc();
}
/**
* Switch between test cc number that works and that fails
*
*/
public function set_cc($type = 'works') {
// See https://stripe.com/docs/testing
if ($type == 'works') {
$this->_cc = '4111111111111111';
}
elseif ($type == 'fails') {
$this->_cc = '4000000000000002';
}
$this->created_ts = time();
}
public function tearDown() {
......@@ -91,7 +57,7 @@ class CRM_Stripe_BaseTest extends \PHPUnit\Framework\TestCase implements Headles
* Create contact.
*/
function createContact() {
if (!empty($this->_contactID)) {
if (!empty($this->contactID)) {
return;
}
$results = civicrm_api3('Contact', 'create', [
......@@ -99,13 +65,13 @@ class CRM_Stripe_BaseTest extends \PHPUnit\Framework\TestCase implements Headles
'first_name' => 'Jose',
'last_name' => 'Lopez'
]);;
$this->_contactID = $results['id'];
$this->contactID = $results['id'];
$this->contact = (Object) array_pop($results['values']);
// Now we have to add an email address.
$email = 'susie@example.org';
civicrm_api3('email', 'create', [
'contact_id' => $this->_contactID,
'contact_id' => $this->contactID,
'email' => $email,
'location_type_id' => 1
]);
......@@ -119,30 +85,8 @@ class CRM_Stripe_BaseTest extends \PHPUnit\Framework\TestCase implements Headles
function createPaymentProcessor($params = []) {
$result = civicrm_api3('Stripe', 'setuptest', $params);
$processor = array_pop($result['values']);
$this->_sk = $processor['user_name'];
$this->_pk = $processor['password'];
$this->_paymentProcessor = $processor;
$this->_paymentProcessorID = $result['id'];
}
/**
* Create a stripe contribution page.
*
*/
function createContributionPage($params = []) {
$params = array_merge([
'title' => "Test Contribution Page",
'financial_type_id' => $this->_financialTypeID,
'currency' => 'USD',
'payment_processor' => $this->_paymentProcessorID,
'max_amount' => 1000,
'receipt_from_email' => 'gaia@the.cosmos',
'receipt_from_name' => 'Pachamama',
'is_email_receipt' => 0,
], $params);
$result = civicrm_api3('ContributionPage', 'create', $params);
$this->assertEquals(0, $result['is_error']);
$this->_contributionPageID = $result['id'];
$this->paymentProcessor = $processor;
$this->paymentProcessorID = $result['id'];
}
/**
......@@ -150,15 +94,14 @@ class CRM_Stripe_BaseTest extends \PHPUnit\Framework\TestCase implements Headles
*/
public function doPayment($params = []) {
$mode = 'test';
$pp = $this->_paymentProcessor;
\Stripe\Stripe::setApiKey(CRM_Core_Payment_Stripe::getSecretKey($pp));
\Stripe\Stripe::setApiKey(CRM_Core_Payment_Stripe::getSecretKey($this->paymentProcessor));
// Send in credit card to get payment method.
$paymentMethod = \Stripe\PaymentMethod::create([
'type' => 'card',
'card' => [
'number' => $this->_cc,
'number' => $this->cc,
'exp_month' => 12,
'exp_year' => date('Y') + 1,
'cvc' => '123',
......@@ -170,12 +113,14 @@ class CRM_Stripe_BaseTest extends \PHPUnit\Framework\TestCase implements Headles
if (!array_key_exists('is_recur', $params)) {
// Send in payment method to get payment intent.
$params = [
$paymentIntentParams = [
'payment_method_id' => $paymentMethod->id,
'amount' => $this->_total,
'payment_processor_id' => $pp['id'],
'amount' => $this->total,
'payment_processor_id' => $this->paymentProcessorID,
'payment_intent_id' => NULL,
'description' => NULL,
];
$result = civicrm_api3('StripePaymentintent', 'process', $params);
$result = civicrm_api3('StripePaymentintent', 'process', $paymentIntentParams);
$paymentIntentID = $result['values']['paymentIntent']['id'];
}
......@@ -183,30 +128,37 @@ class CRM_Stripe_BaseTest extends \PHPUnit\Framework\TestCase implements Headles
$paymentMethodID = $paymentMethod->id;
}
$stripe = new CRM_Core_Payment_Stripe($mode, $pp);
$stripe = new CRM_Core_Payment_Stripe($mode, $this->paymentProcessor);
$params = array_merge([
'payment_processor_id' => $this->_paymentProcessorID,
'amount' => $this->_total,
'payment_processor_id' => $this->paymentProcessorID,
'amount' => $this->total,
'paymentIntentID' => $paymentIntentID,
'paymentMethodID' => $paymentMethodID,
'email' => $this->contact->email,
'contactID' => $this->contact->id,
'description' => 'Test from Stripe Test Code',
'currencyID' => 'USD',
'invoiceID' => $this->_invoiceID,
// Avoid missing key php errors by adding these un-needed parameters.
'qfKey' => NULL,
'entryURL' => 'http://civicrm.localhost/civicrm/test?foo',
'query' => NULL,
'additional_participants' => [],
], $params);
$ret = $stripe->doPayment($params);
if (array_key_exists('trxn_id', $ret)) {
$this->_trxn_id = $ret['trxn_id'];
$this->trxn_id = $ret['trxn_id'];
}
if (array_key_exists('subscription_id', $ret)) {
$this->_subscriptionID = $ret['subscription_id'];
if (array_key_exists('contributionRecurID', $ret)) {
// Get processor id.
$sql = "SELECT processor_id FROM civicrm_contribution_recur WHERE id = %0";
$params = [ 0 => [ $ret['contributionRecurID'], 'Integer' ] ];
$dao = CRM_Core_DAO::executeQuery($sql, $params);
if ($dao->N > 0) {
$dao->fetch();
$this->processorID = $dao->processor_id;
}
}
}
......@@ -215,13 +167,13 @@ class CRM_Stripe_BaseTest extends \PHPUnit\Framework\TestCase implements Headles
*
*/
public function assertValidTrxn() {
$this->assertNotEmpty($this->_trxn_id, "A trxn id was assigned");
$this->assertNotEmpty($this->trxn_id, "A trxn id was assigned");
$processor = new CRM_Core_Payment_Stripe('', civicrm_api3('PaymentProcessor', 'getsingle', ['id' => $this->_paymentProcessorID]));
$processor = new CRM_Core_Payment_Stripe('', civicrm_api3('PaymentProcessor', 'getsingle', ['id' => $this->paymentProcessorID]));
$processor->setAPIParams();
try {
$results = \Stripe\Charge::retrieve(["id" => $this->_trxn_id]);
$results = \Stripe\Charge::retrieve(["id" => $this->trxn_id]);
$found = TRUE;
}
catch (Stripe_Error $e) {
......@@ -236,58 +188,20 @@ class CRM_Stripe_BaseTest extends \PHPUnit\Framework\TestCase implements Headles
*/
public function setupTransaction($params = []) {
$contribution = civicrm_api3('contribution', 'create', array_merge([
'contact_id' => $this->_contactID,
'contact_id' => $this->contactID,
'contribution_status_id' => 2,
'payment_processor_id' => $this->_paymentProcessorID,
'payment_processor_id' => $this->paymentProcessorID,
// processor provided ID - use contact ID as proxy.
'processor_id' => $this->_contactID,
'total_amount' => $this->_total,
'invoice_id' => $this->_invoiceID,
'financial_type_id' => $this->_financialTypeID,
'processor_id' => $this->contactID,
'total_amount' => $this->total,
'financial_type_id' => $this->financialTypeID,
'contribution_status_id' => 'Pending',
'contact_id' => $this->_contactID,
'contribution_page_id' => $this->_contributionPageID,
'payment_processor_id' => $this->_paymentProcessorID,
'contact_id' => $this->contactID,
'payment_processor_id' => $this->paymentProcessorID,
'is_test' => 1,
], $params));
$this->assertEquals(0, $contribution['is_error']);
$this->_contributionID = $contribution['id'];
}
public function createOrganization() {
if (!empty($this->_orgID)) {
return;
}
$results = civicrm_api3('Contact', 'create', [
'contact_type' => 'Organization',
'organization_name' => 'My Great Group'
]);;
$this->_orgID = $results['id'];
}
public function createMembershipType() {
CRM_Member_PseudoConstant::flush('membershipType');
CRM_Core_Config::clearDBCache();
$this->createOrganization();
$params = [
'name' => 'General',
'duration_unit' => 'year',
'duration_interval' => 1,
'period_type' => 'rolling',
'member_of_contact_id' => $this->_orgID,
'domain_id' => 1,
'financial_type_id' => 2,
'is_active' => 1,
'sequential' => 1,
'visibility' => 'Public',
];
$result = civicrm_api3('MembershipType', 'Create', $params);
$this->_membershipTypeID = $result['id'];
CRM_Member_PseudoConstant::flush('membershipType');
CRM_Utils_Cache::singleton()->flush();
$this->contributionID = $contribution['id'];
}
}
......@@ -14,25 +14,13 @@ use Civi\Test\HookInterface;
use Civi\Test\TransactionalInterface;
/**
* FIXME - Add test description.
*
* Tips:
* - With HookInterface, you may implement CiviCRM hooks directly in the test class.
* Simply create corresponding functions (e.g. "hook_civicrm_post(...)" or similar).
* - With TransactionalInterface, any data changes made by setUp() or test****() functions will
* rollback automatically -- as long as you don't manipulate schema or truncate tables.
* If this test needs to manipulate schema or truncate tables, then either:
* a. Do all that using setupHeadless() and Civi\Test.
* b. Disable TransactionalInterface, and handle all setup/teardown yourself.
* Test a simple, direct payment via Stripe.
*
* @group headless
*/
require ('BaseTest.php');
class CRM_Stripe_DirectTest extends CRM_Stripe_BaseTest {
protected $_contributionRecurID;
protected $_total = '200';
public function setUpHeadless() {
// Civi\Test has many helpers, like install(), uninstall(), sql(), and sqlFile().
// See: https://github.com/civicrm/org.civicrm.testapalooza/blob/master/civi-test.md
......
......@@ -14,27 +14,17 @@ use Civi\Test\HookInterface;
use Civi\Test\TransactionalInterface;
/**
* FIXME - Add test description.
*
* Tips:
* - With HookInterface, you may implement CiviCRM hooks directly in the test class.
* Simply create corresponding functions (e.g. "hook_civicrm_post(...)" or similar).
* - With TransactionalInterface, any data changes made by setUp() or test****() functions will
* rollback automatically -- as long as you don't manipulate schema or truncate tables.
* If this test needs to manipulate schema or truncate tables, then either:
* a. Do all that using setupHeadless() and Civi\Test.
* b. Disable TransactionalInterface, and handle all setup/teardown yourself.
* Tests simple recurring contribution with IPN.
*
* @group headless
*/
require ('BaseTest.php');
class CRM_Stripe_IpnTest extends CRM_Stripe_BaseTest {
protected $_total = '200';
protected $_contributionRecurID;
protected $_installments = 5;
protected $_frequency_unit = 'month';
protected $_frequency_interval = 1;
protected $_membershipID;
protected $contributionRecurID;
protected $installments = 5;
protected $frequency_unit = 'month';
protected $frequency_interval = 1;
protected $created_ts;
// This test is particularly dirty for some reason so we have to
// force a reset.
......@@ -47,196 +37,36 @@ class CRM_Stripe_IpnTest extends CRM_Stripe_BaseTest {
}
/**
* Test creating a membership related recurring contribution and
* Test creating a recurring contribution and
* update it after creation. The membership should also be updated.
*/
public function testIPNRecurMembershipUpdate() {
public function testIPNContribution() {
// Setup a recurring contribution for $200 per month.
$this->setupRecurringTransaction();
// Create a membership type (this will create the member org too).
$this->createMembershipType();
// Create the membership and link to the recurring contribution.
$params = [
'contact_id' => $this->_contactID,
'membership_type_id' => $this->_membershipTypeID,
'contribution_recur_id' => $this->_contributionRecurID
];
$result = civicrm_api3('membership', 'create', $params);
$this->_membershipID = $result['id'];
$status = $result['values'][$this->_membershipID]['status_id'];
$this->assertEquals(1, $status, 'Membership is in new status');
// Submit the payment.
$payment_extra_params = [
'is_recur' => 1,
'contributionRecurID' => $this->_contributionRecurID,
'frequency_unit' => $this->_frequency_unit,
'frequency_interval' => $this->_frequency_interval,
'installments' => $this->_installments,
'selectMembership' => [
0 => $this->_membershipTypeID
]
];
$this->doPayment($payment_extra_params);
// Now check to see if an event was triggered and if so, process it.
$payment_object = $this->getEvent('invoice.payment_succeeded');
if ($payment_object) {
$this->ipn($payment_object);
}
// Now that we have a recurring contribution, let's update it.
$processor = new CRM_Core_Payment_Stripe('', civicrm_api3('PaymentProcessor', 'getsingle', ['id' => $this->_paymentProcessorID]));
$processor->setAPIParams();
$sub = \Stripe\Subscription::retrieve($this->_subscriptionID);
// Create a new plan if it doesn't yet exist.
$plan_id = 'membertype_1-every-2-month-40000-usd-test';
// It's possible that this test plan is still in Stripe, so try to
// retrieve it and catch the error triggered if it doesn't exist.
try {
$plan = \Stripe\Plan::retrieve($plan_id);
}
catch (Stripe\Error\InvalidRequest $e) {
// The plan has not been created yet, so create it.
$product = \Stripe\Product::create([
"name" => "CiviCRM testing product",
"type" => "service"
]);
$plan_details = [
'id' => $plan_id,
'amount' => '40000',
'interval' => 'month',
'product' => $product,
'currency' => 'usd',
'interval_count' => 2
];
$plan = \Stripe\Plan::create($plan_details);
}
$sub->plan = $plan_id;
$sub->save();
// Now check to see if an event was triggered and if so, process it.
$payment_object = $this->getEvent('customer.subscription.updated');
if ($payment_object) {
$this->ipn($payment_object);
}
// Check for a new recurring contribution.
$params = [
'contact_id' => $this->_contactID,
'amount' => '400',
'contribution_status_id' => "In Progress",
'return' => ['id'],
];
$result = civicrm_api3('ContributionRecur', 'getsingle', $params);
$newContributionRecurID = $result['id'];
// Now ensure that the membership record is updated to have this
// new recurring contribution id.
$membership_contribution_recur_id = civicrm_api3('Membership', 'getvalue', [
'id' => $this->_membershipID,
'return' => 'contribution_recur_id'
]);
$this->assertEquals($newContributionRecurID, $membership_contribution_recur_id, 'Membership is updated to new contribution recur id');
// Delete the new plan so we can cleanly run the next time.
$plan->delete();
}
/**
* Test making a failed recurring contribution.
*/
public function testIPNRecurFail() {
// @todo Update and make this test work
return;
$this->setupRecurringTransaction();
$payment_extra_params = [
'is_recur' => 1,
'contributionRecurID' => $this->_contributionRecurID,
'frequency_unit' => $this->_frequency_unit,
'frequency_interval' => $this->_frequency_interval,
'installments' => $this->_installments
'contributionRecurID' => $this->contributionRecurID,
'frequency_unit' => $this->frequency_unit,
'frequency_interval' => $this->frequency_interval,
'installments' => $this->installments,
];
// Note - this will succeed. It is very hard to test a failed transaction.
// We will manipulate the event to make it a failed transaction below.
$this->doPayment($payment_extra_params);
// Now check to see if an event was triggered and if so, process it.
$payment_object = $this->getEvent('invoice.payment_succeeded');
if ($payment_object) {
// Now manipulate the transaction so it appears to be a failed one.
$payment_object->type = 'invoice.payment_failed';
// Tell Ipn not to verify it - because we manipulated it.
$verify = FALSE;
$this->ipn($payment_object, $verify);
}
$contribution = civicrm_api3('contribution', 'getsingle', ['id' => $this->_contributionID]);
$contribution_status_id = $contribution['contribution_status_id'];
$status = CRM_Contribute_PseudoConstant::contributionStatus($contribution_status_id, 'name');
$this->assertEquals('Failed', $status, "Failed contribution was properly marked as failed via a stripe event.");
$failure_count = civicrm_api3('ContributionRecur', 'getvalue', [
'sequential' => 1,
'id' => $this->_contributionRecurID,
'return' => 'failure_count',
]);
$this->assertEquals(1, $failure_count, "Failed contribution count is correct..");
}
/**
* Test making a recurring contribution.
*/
public function testIPNRecurSuccess() {
// @todo Update and make this test work
return;
$this->setupRecurringTransaction();
$payment_extra_params = [
'is_recur' => 1,
'contributionRecurID' => $this->_contributionRecurID,
'frequency_unit' => $this->_frequency_unit,
'frequency_interval' => $this->_frequency_interval,
'installments' => $this->_installments
];
$this->doPayment($payment_extra_params);
// Ensure contribution status is set to pending.
$status_id = civicrm_api3('Contribution', 'getvalue', [ 'id' => $this->contributionID, 'return' => 'contribution_status_id' ]);
$this->assertEquals(2, $status_id);
// Now check to see if an event was triggered and if so, process it.
$payment_object = $this->getEvent('invoice.payment_succeeded');
if ($payment_object) {
$this->ipn($payment_object);
}
$contribution = civicrm_api3('contribution', 'getsingle', ['id' => $this->_contributionID]);
$contribution_status_id = $contribution['contribution_status_id'];
$this->assertEquals(1, $contribution_status_id, "Recurring payment was properly processed via a stripe event.");
// Now, cancel the subscription and ensure it is properly cancelled.
$processor = new CRM_Core_Payment_Stripe('', civicrm_api3('PaymentProcessor', 'getsingle', ['id' => $this->_paymentProcessorID]));
$processor->setAPIParams();
$sub = \Stripe\Subscription::retrieve($this->_subscriptionID);
$sub->cancel();
$sub_object = $this->getEvent('customer.subscription.deleted');
if ($sub_object) {
$this->ipn($sub_object);
}
$this->assertContributionRecurIsCancelled();
}
// Ensure Contribution status is updated to complete.
$status_id = civicrm_api3('Contribution', 'getvalue', [ 'id' => $this->contributionID, 'return' => 'contribution_status_id' ]);
$this->assertEquals(1, $status_id);
public function assertContributionRecurIsCancelled() {
$contribution_recur = civicrm_api3('contributionrecur', 'getsingle', ['id' => $this->_contributionRecurID]);
$contribution_recur_status_id = $contribution_recur['contribution_status_id'];
$status = CRM_Contribute_PseudoConstant::contributionStatus($contribution_recur_status_id, 'name');
$this->assertEquals('Cancelled', $status, "Recurring payment was properly cancelled via a stripe event.");
}
/**
......@@ -252,16 +82,17 @@ class CRM_Stripe_IpnTest extends CRM_Stripe_BaseTest {
$property = 'subscription';
}
// Gather all events since this class was instantiated.
$params['sk'] = $this->_sk;
$params['created'] = ['gte' => $this->_created_ts];
$params['type'] = $type;
$params['ppid'] = $this->_paymentProcessorID;
$params['created'] = ['gte' => $this->created_ts];
//$params['type'] = $type;
$params['ppid'] = $this->paymentProcessorID;
$params['output'] = 'raw';
// Now try to retrieve this transaction.
// Give it a few seconds to be processed...
sleep(5);
$transactions = civicrm_api3('Stripe', 'listevents', $params );
foreach($transactions['values']['data'] as $transaction) {
if ($transaction->data->object->$property == $this->_subscriptionID) {
if ($transaction->data->object->$property == $this->processorID) {
return $transaction;
}
}
......@@ -276,7 +107,7 @@ class CRM_Stripe_IpnTest extends CRM_Stripe_BaseTest {
public function ipn($data, $verify = TRUE) {
// The $_GET['processor_id'] value is normally set by
// CRM_Core_Payment::handlePaymentMethod
$_GET['processor_id'] = $this->_paymentProcessorID;
$_GET['processor_id'] = $this->paymentProcessorID;
$ipnClass = new CRM_Core_Payment_StripeIPN($data, $verify);
$ipnClass->main();
}
......@@ -286,33 +117,28 @@ class CRM_Stripe_IpnTest extends CRM_Stripe_BaseTest {
*/
public function setupRecurringTransaction($params = []) {
$contributionRecur = civicrm_api3('contribution_recur', 'create', array_merge([
'financial_type_id' => $this->_financialTypeID,
'financial_type_id' => $this->financialTypeID,
'payment_instrument_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_ContributionRecur', 'payment_instrument_id', 'Credit Card'),
'contact_id' => $this->_contactID,
'amount' => $this->_total,
'contact_id' => $this->contactID,
'amount' => $this->total,
'sequential' => 1,
'installments' => $this->_installments,
'frequency_unit' => $this->_frequency_unit,
'frequency_interval' => $this->_frequency_interval,
'invoice_id' => $this->_invoiceID,
'installments' => $this->installments,
'frequency_unit' => $this->frequency_unit,
'frequency_interval' => $this->frequency_interval,
'contribution_status_id' => 2,
'payment_processor_id' => $this->_paymentProcessorID,
// processor provided ID - use contact ID as proxy.
'processor_id' => $this->_contactID,
'payment_processor_id' => $this->paymentProcessorID,
'api.contribution.create' => [
'total_amount' => $this->_total,
'invoice_id' => $this->_invoiceID,
'financial_type_id' => $this->_financialTypeID,
'total_amount' => $this->total,
'financial_type_id' => $this->financialTypeID,
'contribution_status_id' => 'Pending',
'contact_id' => $this->_contactID,
'contribution_page_id' => $this->_contributionPageID,
'payment_processor_id' => $this->_paymentProcessorID,
'contact_id' => $this->contactID,
'payment_processor_id' => $this->paymentProcessorID,
'is_test' => 1,
],
], $params));
$this->assertEquals(0, $contributionRecur['is_error']);
$this->_contributionRecurID = $contributionRecur['id'];
$this->_contributionID = $contributionRecur['values']['0']['api.contribution.create']['id'];
$this->contributionRecurID = $contributionRecur['id'];
$this->contributionID = $contributionRecur['values']['0']['api.contribution.create']['id'];
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment