Skip to content

Commit

Permalink
Feature: add stateless donation confirmation receipt routing, templat…
Browse files Browse the repository at this point in the history
…e, and implementation (impress-org#113)

* feature: add donation receipt route, dto, view, controller, template, and rendering

* feature: make receipt settings dynamic, add query params to route url, update generators and tests

* refactor: update form logic to listen for redirects from gateways, update receipt styles to match classic form, get next gen test gateways to use new receipt route as faux redirect

* chore: scaffold same page receipt redirect for stripe

* chore: add cors to postData for offsite redirects

* feature: add concept of originId for showing receipt with multiple forms on a page

* refactor: add redirectReturnUrl to gatewayData via filter and replace individual gateway logic

* refactor: use givewp-event and givewp-listenr for redirect query params

* feature: add isEmbed, embedId and RouteListener

* feature: add backwards compatability to new receipts for legacy forms

* feature: filter sub gateway data

* chore: add receipt detail classnames

* refactor: use detail classname for icons

* refactor: use new donation status label getter

* reactor: use isReceiptIdValid

* refactor: udpate filter method names
  • Loading branch information
jonwaldstein committed Jan 18, 2023
1 parent 8406fe7 commit c8b6d30
Show file tree
Hide file tree
Showing 60 changed files with 1,900 additions and 245 deletions.
15 changes: 15 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"packages/form-builder"
],
"dependencies": {
"@fortawesome/fontawesome-free": "^6.2.1",
"@hookform/error-message": "^2.0.0",
"@hookform/resolvers": "^2.9.6",
"@stripe/react-stripe-js": "^1.2.2",
Expand Down
7 changes: 1 addition & 6 deletions src/NextGen/DonationForm/Actions/GenerateDonateRouteUrl.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,6 @@ public function __invoke(): string
'givewp-route-signature-expiration' => $signature->expiration,
];

return esc_url_raw(
add_query_arg(
$queryArgs,
Route::url('donate')
)
);
return esc_url_raw(Route::url('donate', $queryArgs));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

namespace Give\NextGen\DonationForm\Actions;

use Give\Donations\Models\Donation;
use Give\NextGen\Framework\Routes\RouteListener;

class GenerateDonationConfirmationReceiptUrl
{
/**
* @unreleased
*/
public function __invoke(Donation $donation, string $originUrl, string $embedId = ''): string
{
$routeListener = new RouteListener(
'donation-completed',
'show-donation-confirmation-receipt'
);

return $routeListener->toUrl($originUrl, [
'givewp-receipt-id' => $donation->purchaseKey,
'givewp-embed-id' => $embedId,
]);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

namespace Give\NextGen\DonationForm\Actions;


use Give\NextGen\Framework\Routes\Route;

/**
* @unreleased
*/
class GenerateDonationConfirmationReceiptViewRouteUrl
{
/**
* @unreleased
*/
public function __invoke(string $receiptId): string
{
$args = [
'receipt-id' => $receiptId
];

return esc_url_raw(Route::url('donation-confirmation-receipt-view', $args));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,6 @@ public function __invoke(int $formId): string
'form-id' => $formId
];

return esc_url(
add_query_arg(
$args,
Route::url('donation-form-view-preview')
)
);
return esc_url(Route::url('donation-form-view-preview', $args));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,6 @@ public function __invoke(int $formId): string
'form-id' => $formId
];

return esc_url_raw(
add_query_arg(
$args,
Route::url('donation-form-view')
)
);
return esc_url_raw(Route::url('donation-form-view', $args));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
namespace Give\NextGen\DonationForm\Blocks\DonationFormBlock\Controllers;

use Give\Framework\EnqueueScript;
use Give\NextGen\DonationForm\Actions\GenerateDonationConfirmationReceiptViewRouteUrl;
use Give\NextGen\DonationForm\Actions\GenerateDonationFormViewRouteUrl;
use Give\NextGen\DonationForm\Blocks\DonationFormBlock\DataTransferObjects\BlockAttributes;
use Give\NextGen\DonationForm\DataTransferObjects\DonationConfirmationReceiptViewRouteData;
use Give\NextGen\DonationForm\Models\DonationForm;
use Give\NextGen\Framework\Routes\RouteListener;

class BlockRenderController
{
Expand All @@ -27,29 +30,75 @@ public function render(array $attributes)
return null;
}

/**
* Load embed givewp script to resize iframe
*
* @see https://github.com/davidjbradshaw/iframe-resizer
*/
(new EnqueueScript(
'givewp-donation-form-embed',
'build/donationFormEmbed.js',
GIVE_NEXT_GEN_DIR,
GIVE_NEXT_GEN_URL,
'give'
))->loadInFooter()->enqueue();
$this->loadEmbedScript();

/** @var DonationForm $donationForm */
$donationForm = DonationForm::find($blockAttributes->formId);

$viewUrl = (new GenerateDonationFormViewRouteUrl())($donationForm->id);
$embedId = $blockAttributes->blockId ?? '';

$viewUrl = $this->getViewUrl($donationForm, $embedId);

/**
* Note: iframe-resizer uses querySelectorAll so using a data attribute makes the most sense to target.
* It will also generate a dynamic ID - so when we have multiple embeds on a page there will be no conflict.
*/
return "<iframe data-givewp-embed src='$viewUrl'
style='width: 1px;min-width: 100%;border: 0;'></iframe>";
return "<iframe data-givewp-embed src='$viewUrl' data-givewp-embed-id='$embedId' style='width: 1px;min-width: 100%;border: 0;'></iframe>";
}

/**
* If the page loads with our receipt route listener args then we need to render the receipt.
*
* @unreleased
*/
protected function shouldDisplayDonationConfirmationReceipt(string $embedId): bool
{
$routeListener = new RouteListener(
'donation-completed',
'show-donation-confirmation-receipt'
);

return $routeListener->isValid($_GET, function ($request) use ($embedId) {
$isset = isset($request['givewp-embed-id'], $request['givewp-receipt-id']);

return $isset && $request['givewp-embed-id'] === $embedId && DonationConfirmationReceiptViewRouteData::isReceiptIdValid(
$request['givewp-receipt-id']
);
});
}

/**
* Get the iframe URL.
* This could either be the donation form view or the donation confirmation receipt view.
*
* @unreleased
*/
private function getViewUrl(DonationForm $donationForm, string $embedId): string
{
if ($this->shouldDisplayDonationConfirmationReceipt($embedId)) {
$receiptId = give_clean($_GET['givewp-receipt-id']);

return (new GenerateDonationConfirmationReceiptViewRouteUrl())($receiptId);
}

return (new GenerateDonationFormViewRouteUrl())($donationForm->id);
}

/**
*
* Load embed givewp script to resize iframe
* @see https://github.com/davidjbradshaw/iframe-resizer
*
* @unreleased
*/
private function loadEmbedScript()
{
(new EnqueueScript(
'givewp-donation-form-embed',
'build/donationFormEmbed.js',
GIVE_NEXT_GEN_DIR,
GIVE_NEXT_GEN_URL,
'give'
))->loadInFooter()->enqueue();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ class BlockAttributes implements Arrayable
* @var int|null
*/
public $formId;
/**
* @var string
*/
public $blockId;

/**
* @unreleased
Expand All @@ -19,6 +23,7 @@ public static function fromArray(array $array): BlockAttributes
$self = new self();

$self->formId = !empty($array['formId']) ? (int)$array['formId'] : null;
$self->blockId = !empty($array['blockId']) ? (string)$array['blockId'] : null;

return $self;
}
Expand Down
3 changes: 3 additions & 0 deletions src/NextGen/DonationForm/Blocks/DonationFormBlock/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
"attributes": {
"formId": {
"type": "string"
},
"blockId": {
"type": "string"
}
},
"textdomain": "give",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {__} from '@wordpress/i18n';
import {InspectorControls, useBlockProps} from '@wordpress/block-editor';
import {ExternalLink, PanelBody, PanelRow, SelectControl} from '@wordpress/components';
import {Fragment} from '@wordpress/element';
import {Fragment, useEffect} from '@wordpress/element';
import useFormOptions from './hooks/useFormOptions';
import ConfirmButton from './components/ConfirmButton';
import Logo from './components/Logo';
Expand All @@ -13,10 +13,16 @@ import {useCallback} from 'react';
/**
* @since 1.0.0
*/
export default function Edit({attributes, setAttributes}: BlockEditProps<any>) {
const {formId} = attributes;
export default function Edit({clientId, attributes, setAttributes}: BlockEditProps<any>) {
const {formId, blockId} = attributes;
const {formOptions, isResolving} = useFormOptions();

useEffect(() => {
if (!blockId) {
setAttributes({blockId: clientId});
}
}, []);

const getDefaultFormId = useCallback(() => {
if (!isResolving && formOptions.length > 0) {
return formId && formOptions?.find(({value}) => value === formId);
Expand Down
67 changes: 47 additions & 20 deletions src/NextGen/DonationForm/Controllers/DonateController.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
use Give\Framework\PaymentGateways\PaymentGateway;
use Give\NextGen\DonationForm\Actions\StoreCustomFields;
use Give\NextGen\DonationForm\DataTransferObjects\DonateControllerData;
use Give\NextGen\DonationForm\DataTransferObjects\LegacyPurchaseFormData;
use Give\NextGen\DonationForm\Models\DonationForm;

/**
Expand Down Expand Up @@ -40,8 +39,9 @@ public function donate(DonateControllerData $formData, PaymentGateway $registere

$this->saveCustomFields($form, $donation, $formData->getCustomFields());

// setting sessions is required for legacy receipts
$this->setSession($donation, $donor);
$this->temporarilyReplaceLegacySuccessPageUri($formData, $donation);

$this->addToGatewayData($formData, $donation);

$registeredGateway->handleCreatePayment($donation);
}
Expand Down Expand Up @@ -92,37 +92,64 @@ private function getOrCreateDonor(
}

/**
* This logic is intended to work with the legacy receipt functionality
* by setting and updating the give_purchase session.
*
* @unreleased
*
* @return void
*/
private function setSession(Donation $donation, Donor $donor)
private function saveCustomFields(DonationForm $form, Donation $donation, array $customFields)
{
give()->session->maybe_start_session();

$purchaseSession = (array)give()->session->get('give_purchase');

if ($purchaseSession && array_key_exists('donation_id', $purchaseSession)) {
$purchaseSession['donation_id'] = $donation->id;
(new StoreCustomFields())($form, $donation, $customFields);
}

give()->session->set('give_purchase', $purchaseSession);
} else {
$legacyPurchaseFormData = LegacyPurchaseFormData::fromArray(['donation' => $donation, 'donor' => $donor]);
/**
* Use our new receipt url for the success page uri.
*
* The give_get_success_page_uri() function is used by the legacy gateway processing and is specific to how that form works.
*
* In Next Gen, our confirmation receipt page is stateless, and need to use the form request data to generate the url.
*
* This is a temporary solution until we can update the gateway api to support the new receipt urls.
*
* @unreleased
*
* @return void
*/
protected function temporarilyReplaceLegacySuccessPageUri(DonateControllerData $formData, Donation $donation)
{
$filteredUrl = $formData->getDonationConfirmationReceiptViewRouteUrl($donation);

give_set_purchase_session($legacyPurchaseFormData->toPurchaseData());
}
add_filter('give_get_success_page_uri', static function ($url) use ($filteredUrl) {
return $filteredUrl;
});
}

/**
* This adds the `redirectReturnUrl` key to the gateway data.
*
* This is necessary so gateways can use this value in both legacy and next gen donation forms.
*
* @unreleased
*
* @return void
*/
private function saveCustomFields(DonationForm $form, Donation $donation, array $customFields)
protected function addToGatewayData(DonateControllerData $formData, $donation)
{
(new StoreCustomFields())($form, $donation, $customFields);
add_filter(
"givewp_create_payment_gateway_data_{$donation->gatewayId}",
static function ($data) use ($formData, $donation) {
return array_merge($data, [
'redirectReturnUrl' => $formData->getRedirectReturnUrl($donation),
]);
}
);

add_filter(
"givewp_create_subscription_gateway_data_{$donation->gatewayId}",
static function ($data) use ($formData, $donation) {
return array_merge($data, [
'redirectReturnUrl' => $formData->getRedirectReturnUrl($donation),
]);
}
);
}
}
Loading

0 comments on commit c8b6d30

Please sign in to comment.