Skip to content

Commit bbd1de1

Browse files
authored
Feature: add sample integration for off-site gateways (#66)
* feature: add initial classes for an off-site gateway sample * wip * feature: add off-site gateway simulation page * refactor: change file name and add comments * refactor: add description parameter * doc: add comments * feature: add webhook notification logic * refactor: change OffSiteGatewayWebhookNotification properties * fix: property name * feature: add webhook sample logic * refactor: change async job search parameter * refactor: change hook name * refactor: add rel attribute * doc: add comments about DTO * doc: replace unreleased tags * refactor: rename gateway * doc: add comment about event handler classes * refactor: change messages * refactor: rename logo * feature: add refund support * doc: update comments
1 parent 1f44c14 commit bbd1de1

13 files changed

+863
-0
lines changed

give-addon-boilerplate.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use GiveAddon\Addon\Environment;
66
use GiveAddon\Domain\AddonServiceProvider;
77
use GiveAddon\FormExtension\FormExtensionServiceProvider;
8+
use GiveAddon\OffSiteGateway\OffSiteGatewayServiceProvider;
89

910
/**
1011
* Plugin Name: ADDON_NAME
@@ -52,6 +53,7 @@ function () {
5253
if (Environment::giveMinRequiredVersionCheck()) {
5354
give()->registerServiceProvider(AddonServiceProvider::class);
5455
give()->registerServiceProvider(FormExtensionServiceProvider::class);
56+
give()->registerServiceProvider(OffSiteGatewayServiceProvider::class);
5557
}
5658
}
5759
);
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
namespace GiveAddon\OffSiteGateway\DataTransferObjects;
4+
5+
/**
6+
* This Data Transfer Object class converts the gateway API response (when creating a new payment) to a local
7+
* object where we know what properties can be accessed. The fromArray() method is where the conversion from
8+
* the gateway API response to an object of this class is made. You should edit this method according to the
9+
* gateway you are integrating since the API response probably should differ.
10+
*
11+
* @since 1.0.0
12+
*/
13+
class OffSiteGatewayPayment
14+
{
15+
/**
16+
* @var string
17+
*/
18+
public $merchantPaymentId;
19+
20+
/**
21+
* @var string
22+
*/
23+
public $gatewayPaymentId;
24+
25+
public static function fromArray(array $data): OffSiteGatewayPayment
26+
{
27+
$self = new self();
28+
29+
$self->gatewayPaymentId = $data['gatewayPaymentId'] ?? '';
30+
$self->merchantPaymentId = $data['merchantPaymentId'] ?? '';
31+
32+
return $self;
33+
}
34+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
namespace GiveAddon\OffSiteGateway\DataTransferObjects;
4+
5+
/**
6+
* This Data Transfer Object class converts the gateway webhook notification to a local object where we know
7+
* what properties can be accessed. The fromRequest() method is where the conversion from the gateway webhook
8+
* notification to an object of this class is made. You should edit this method according to the gateway you
9+
* are integrating since the webhook notification attributes probably should differ.
10+
*
11+
* @since 1.0.0
12+
*/
13+
class OffSiteGatewayWebhookNotification
14+
{
15+
/**
16+
* @var string
17+
*/
18+
public $gatewayNotificationType;
19+
20+
/**
21+
* @var string
22+
*/
23+
public $gatewayPaymentStatus;
24+
25+
/**
26+
* @var string
27+
*/
28+
public $gatewayPaymentId;
29+
30+
/**
31+
* @var string
32+
*/
33+
public $merchantPaymentId;
34+
35+
/**
36+
* @since 1.0.0
37+
*/
38+
public static function fromRequest(array $request): OffSiteGatewayWebhookNotification
39+
{
40+
$self = new self();
41+
42+
$self->gatewayNotificationType = $request['notification_type'] ?? '';
43+
$self->gatewayPaymentStatus = $request['payment_status'] ?? '';
44+
$self->gatewayPaymentId = $request['payment_id'] ?? '';
45+
$self->merchantPaymentId = $request['merchant_payment_id'] ?? '';
46+
47+
return $self;
48+
}
49+
}
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
<?php
2+
3+
namespace GiveAddon\OffSiteGateway\Gateway;
4+
5+
/**
6+
* IMPORTANT: you don't need to keep this file in your integration; this is just a sample to demonstrate how off-site gateway integrations should work.
7+
*
8+
* @since 1.0.0
9+
*/
10+
class OffSiteCheckoutPageSimulation
11+
{
12+
/**
13+
* @since 1.0.0
14+
*/
15+
public function __invoke()
16+
{
17+
if ( ! isset($_GET['off-site-gateway-simulation'])) {
18+
return;
19+
}
20+
21+
/**
22+
* We need to do this workaround because the legacy forms submit logic tries to redirect URLs from
23+
* the same site inside the donation form iframe instead of using the parent page as the reference.
24+
*/
25+
if ($this->isLegacyFormReferrer()) {
26+
echo '<script>window.top.location.href ="' . home_url($_SERVER['REQUEST_URI']) . '";</script>';
27+
exit();
28+
}
29+
30+
ob_start();
31+
32+
$this->loadOffSiteGatewaySimulationMarkup();
33+
34+
echo ob_get_clean();
35+
36+
exit();
37+
}
38+
39+
/**
40+
* @since 1.0.0
41+
*/
42+
private function isLegacyFormReferrer(): bool
43+
{
44+
//V2 referrer: https://example.com/give/v2-tests?giveDonationFormInIframe=1
45+
//V3 referrer: https://example.com/?givewp-route=donation-form-view&form-id=1350
46+
return strpos($_SERVER['HTTP_REFERER'], 'giveDonationFormInIframe') !== false;
47+
}
48+
49+
/**
50+
* @since 1.0.0
51+
*/
52+
private function loadOffSiteGatewaySimulationMarkup()
53+
{
54+
?>
55+
<style>
56+
.container {
57+
font-family: "Open Sans", Helvetica, Arial, sans-serif;
58+
max-width: 800px;
59+
margin: 60px auto;
60+
}
61+
a {
62+
font-size: 1.5rem;
63+
}
64+
</style>
65+
<div class="container">
66+
<h1>Off-site Checkout Page Simulation</h1>
67+
<p>
68+
Gateway Payment ID: <strong><?php
69+
echo $_GET['gatewayPaymentId'] ?? ' -'; ?></strong>
70+
</p>
71+
<p>
72+
Donation amount: <strong><?php
73+
echo isset($_GET['amount']) ? $_GET['amount']['currency'] . ' ' . $_GET['amount']['value'] : ' -'; ?></strong>
74+
</p>
75+
<p>
76+
Description: <strong><?php
77+
echo $_GET['description'] ?? ' -'; ?></strong>
78+
</p>
79+
<hr />
80+
<p>
81+
<strong>Click on the links below to simulate off-site gateway actions:</strong>
82+
</p>
83+
<a style="color:#696969;font-weight:bold;font-size: 1.2rem" target="_blank" rel="noopener noreferrer"
84+
href="<?php
85+
echo add_query_arg([
86+
'notification_type' => 'one-time',
87+
'payment_status' => 'complete',
88+
'payment_id' => $_GET['gatewayPaymentId'],
89+
'merchant_payment_id' => $_GET['merchantPaymentId'],
90+
],
91+
$_GET['webhookUrl']) ?>">
92+
Send Webhook Notification To Change Donation Status To Complete ⭷
93+
</a>
94+
<p>
95+
🛈 Some gateways send webhook notifications to change the transaction status a few hours after the
96+
payment process is finished, and others can send them even before the user is redirected back to the
97+
referrer's website — the action above simulates this last scenario.
98+
</p>
99+
<a style="color:green;font-weight: bold;" href="<?php
100+
echo $_GET['returnUrl'] ?>">Success Payment</a> | <a style="color:red;font-weight: bold;" href="<?php
101+
echo $_GET['cancelUrl'] ?>">Canceled Payment</a>
102+
<br />
103+
<br />
104+
<p>
105+
⚠️ This page is being loaded directly from your website to demonstrate how off-site gateways work. In
106+
real-world integrations, this page should be the checkout page provided by the gateway you are
107+
integrating, so users can complete or cancel the payment and be redirected back to your site.
108+
</p>
109+
</div>
110+
<?php
111+
}
112+
}

0 commit comments

Comments
 (0)