Commit 65909563 authored by mattwire's avatar mattwire
Browse files

Further tweaks and add docs

parent 5e7df9e7
......@@ -588,54 +588,50 @@ trait CRM_Core_Payment_MJWTrait {
/**
* Call this at the end of a call to doPayment to ensure everything is updated/returned correctly.
*
* @param array $params
* @param array $contributionParams;
* Parameters that should be saved to the contribution now instead of relying on CiviCRM to do so later
* @param \Civi\Payment\PropertyBag|array $params
*
* @return array
* @throws \CiviCRM_API3_Exception
*/
protected function endDoPayment($params, $contributionParams = []) {
if ($this->getPaymentProcessorOrderID()) {
$contributionParams['trxn_id'] = $this->getPaymentProcessorOrderID();
}
if (isset($params['contributionID']) && !empty($contributionParams)) {
$contributionParams['id'] = $params['contributionID'];
$contributionParams['skipCleanMoney'] = TRUE;
$contributionParams['skipRecentView'] = TRUE;
$contributionParams['skipLineItem'] = TRUE;
$contributionParams['is_post_payment_create'] = TRUE;
civicrm_api3('Contribution', 'create', $contributionParams);
unset($contributionParams['id']);
}
$params = array_merge($params, $contributionParams);
protected function endDoPayment($params) {
$propertyBag = PropertyBag::cast($params);
// We need to set this to ensure that contributions are set to the correct status
// It should have already been set to "Completed" if we made a successful payment
if (empty($params['payment_status_id'])) {
$params = $this->setStatusPaymentPending($params);
if (!$propertyBag->has('payment_status_id')) {
$propertyBag = $this->setStatusPaymentPending($propertyBag);
}
// payment_status is the newer property. It *should* be set but we'll make sure.
if (!$propertyBag->has('payment_status')) {
CRM_Core_Error::deprecatedWarning('endDoPayment payment_status is not set! Make sure you are using setStatusPaymentPending/Completed');
if ($propertyBag->getCustomProperty('payment_status_id') == CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Completed')) {
$propertyBag->setCustomProperty('payment_status', 'Completed');
}
else {
$propertyBag->setCustomProperty('payment_status', 'Pending');
}
}
// See https://lab.civicrm.org/dev/financial/-/issues/141
$returnParams = [
'payment_status_id' => $params['payment_status_id'],
'payment_status' => $params['payment_status'],
'payment_status_id' => $propertyBag->getCustomProperty('payment_status_id'),
'payment_status' => $propertyBag->getCustomProperty('payment_status'),
'trxn_id' => $this->getPaymentProcessorOrderID(),
];
if (!empty($params['fee_amount'])) {
$returnParams['fee_amount'] = $params['fee_amount'];
if ($propertyBag->has('feeAmount')) {
$returnParams['fee_amount'] = $propertyBag->getFeeAmount();
}
return $returnParams;
}
/**
* Set the payment status to Pending
* @param array $params
* @param \Civi\Payment\PropertyBag|array $params
*
* @return array
*/
protected function setStatusPaymentPending(array $params) {
protected function setStatusPaymentPending($params) {
$params['payment_status_id'] = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Pending');
$params['payment_status'] = 'Pending';
return $params;
......@@ -643,11 +639,11 @@ trait CRM_Core_Payment_MJWTrait {
/**
* Set the payment status to Completed
* @param $params
* @param \Civi\Payment\PropertyBag|array $params
*
* @return array
*/
protected function setStatusPaymentCompleted(array $params) {
protected function setStatusPaymentCompleted($params) {
$params['payment_status_id'] = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Completed');
$params['payment_status'] = 'Completed';
return $params;
......
# Hooks
## webhookEventNotMatched
This allows you to implement custom handling for unrecognised/unknown webhook events.
For example if you use the same Stripe account to take payments through multiple systems
(eg. online shop, CiviCRM) you will receive webhooks for payments to both systems.
But only the payments that were created using CiviCRM will be matched.
By implementing this hook you can choose to do something with those payments from external
systems - eg. add them into CiviCRM. Once they are in CiviCRM they will be handled like any
other payment in future and subscriptions will continue to be updated automatically in CiviCRM.
```php
/**
* @param string $type The type of webhook - eg. 'stripeipn'
* @param Object $object The object (eg. CRM_Core_Payment_StripeIPN)
* @param string $code "Code" to identify what was not matched (eg. customer_not_found)
*
* @return mixed
*/
function myextension_civicrm_webhookEventNotMatched(string $type, $object, string $code = '') {
if ($type !== 'stripe') {
return;
}
if (!($object instanceof CRM_Core_Payment_StripeIPN)) {
return;
}
switch ($code) {
case 'customer_not_found':
createStripeCustomerInCiviCRM($object);
break;
}
}
```
# Payment Processor Implementation
This library provides two helper "traits":
- `CRM_Core_Payment_MJWTrait` - helpers to implement your `CRM_Core_Payment_Xx` class.
- `CRM_Core_Payment_MJWIPNTrait` - helpers to implement IPN/webhook processing for your payment processor.
## The Payment Processor
### doPayment()
```php
public function doPayment(&$params, $component = 'contribute') {
/* @var \Civi\Payment\PropertyBag $propertyBag */
$propertyBag = $this->beginDoPayment($params);
// Set payment pending
$this->setStatusPaymentPending($propertyBag)
// ... Handle the actual payment / communicate with external servers etc.
// Payment succeeded?
$this->setStatusPaymentCompleted($propertyBag)
return $this->endDoPayment($propertyBag);
}
```
## changeSubscriptionAmount() (Edit recurring contribution)
```php
public function changeSubscriptionAmount(&$message = '', $params = []) {
$propertyBag = $this->beginChangeSubscriptionAmount($params);
// ... Handle update subscription / communicate with external servers.
// On error throw exception
return TRUE;
}
```
## updateSubscriptionBillingInfo() (Update payment info + address)
```php
public function updateSubscriptionBillingInfo(&$message = '', $params = []) {
$propertyBag = $this->beginUpdateSubscriptionBillingInfo($params);
// ... Handle update billing info / communicate with external servers.
// On error throw exception
return TRUE;
}
```
## doCancelRecurring() (Cancel recurring contribution)
```php
public function doCancelRecurring(PropertyBag $propertyBag) {
// By default we always notify the processor and we don't give the user the option
// because supportsCancelRecurringNotifyOptional() = FALSE
if (!$propertyBag->has('isNotifyProcessorOnCancelRecur')) {
// If isNotifyProcessorOnCancelRecur is NOT set then we set our default
$propertyBag->setIsNotifyProcessorOnCancelRecur(TRUE);
}
$notifyProcessor = $propertyBag->getIsNotifyProcessorOnCancelRecur();
if (!$notifyProcessor) {
return ['message' => E::ts('Successfully cancelled the subscription in CiviCRM ONLY.')];
}
if (!$propertyBag->has('recurProcessorID')) {
$errorMessage = E::ts('The recurring contribution cannot be cancelled (No reference (contribution_recur.processor_id) found).');
\Civi::log()->error($errorMessage);
throw new \Civi\Payment\Exception\PaymentProcessorException($errorMessage);
}
// ... Handle cancel recurring / communicate with external servers.
// If we failed to cancel
if ($failed) {
throw new \Civi\Payment\Exception\PaymentProcessorException($this->handleError(NULL, 'Failed to cancel'));
}
return ['message' => E::ts('Successfully cancelled the subscription at XYZ.net.')];
}
```
......@@ -10,6 +10,8 @@ Releases use the following numbering system:
* **[BC]**: Items marked with [BC] indicate a breaking change that will require updates to your code if you are using that code in your extension.
## Release 1.1 (not yet released)
**This release *should* be compatible with payment processors that require 1.0 or higher.
But make sure you test before upgrading.**
* Add multiple functions to CRM.payment (that were previously in civicrmStripe.js):
* resetBillingFieldsRequiredForJQueryValidate
......@@ -28,6 +30,12 @@ Releases use the following numbering system:
* Check for "Separate Membership Payment" is enabled.
* Support X.X-dev versioning for system checks - display a warning if dev version, version check no longer fails if eg. using 1.1-dev and minimum requirement is 1.1.
* Return a fixed set of params from `doPayment()` - see [dev/financial/issues#141](https://lab.civicrm.org/dev/financial/-/issues/141).
* Move cast to PropertyBag to beginDoPayment (reduce lines of code required in doPayment).
* Automatically handle deprecated `trxn_id` on `civicrm_contribution_recur` (copy from `processor_id` and add deprecated warnings).
* Add `beginUpdateSubscriptionBillingInfo()` and `beginChangeSubscriptionAmount()` methods - see [Payment Processor](paymentprocessor.md).
* Convert internal method `getContactID()` to require propertyBag.
* Define contributionRecur property on IPN class.
* Add new [hook `webhookEventNotMatched`](hooks.md).
## Release 1.0.1
......
......@@ -14,7 +14,7 @@
<url desc="Release Notes">https://lab.civicrm.org/extensions/mjwshared/-/blob/master/docs/releasenotes.md</url>
<url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
</urls>
<releaseDate>2021-09-02</releaseDate>
<releaseDate>2021-09-19</releaseDate>
<version>1.1-dev</version>
<develStage>beta</develStage>
<compatibility>
......
......@@ -19,5 +19,7 @@ nav:
- Overview: index.md
- Release Notes: releasenotes.md
- API: api.md
- Hooks: hooks.md
- How to implement a payment processor: paymentprocessor.md
- Refund UI: refunds.md
- Webhook Queue: webhookqueue.md
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