1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Give - Stripe Core | Process Webhooks |
4
|
|
|
* |
5
|
|
|
* @since 2.5.0 |
6
|
|
|
* |
7
|
|
|
* @package Give |
8
|
|
|
* @subpackage Stripe Core |
9
|
|
|
* @copyright Copyright (c) 2019, GiveWP |
10
|
|
|
* @license https://opensource.org/licenses/gpl-license GNU Public License |
11
|
|
|
*/ |
12
|
|
|
|
13
|
|
|
// Exit if accessed directly. |
14
|
|
|
if ( ! defined( 'ABSPATH' ) ) { |
15
|
|
|
exit; |
16
|
|
|
} |
17
|
|
|
|
18
|
|
|
if ( ! class_exists( 'Give_Stripe_Webhooks' ) ) { |
19
|
|
|
|
20
|
|
|
/** |
21
|
|
|
* Class Give_Stripe_Webhooks |
22
|
|
|
* |
23
|
|
|
* @since 2.5.0 |
24
|
|
|
*/ |
25
|
|
|
class Give_Stripe_Webhooks { |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* Stripe Gateway |
29
|
|
|
* |
30
|
|
|
* @since 2.5.0 |
31
|
|
|
* @access public |
32
|
|
|
* |
33
|
|
|
* @var $stripe_gateway |
34
|
|
|
*/ |
35
|
|
|
public $stripe_gateway; |
36
|
|
|
|
37
|
|
|
/** |
38
|
|
|
* Give_Stripe_Webhooks constructor. |
39
|
|
|
* |
40
|
|
|
* @since 2.5.0 |
41
|
|
|
*/ |
42
|
|
|
public function __construct() { |
43
|
|
|
|
44
|
|
|
$this->stripe_gateway = new Give_Stripe_Gateway(); |
45
|
|
|
|
46
|
|
|
add_action( 'init', array( $this, 'listen' ) ); |
47
|
|
|
} |
48
|
|
|
|
49
|
|
|
/** |
50
|
|
|
* Listen for Stripe events. |
51
|
|
|
* |
52
|
|
|
* @access public |
53
|
|
|
* @since 2.5.0 |
54
|
|
|
* |
55
|
|
|
* @return void |
56
|
|
|
*/ |
57
|
|
|
public function listen() { |
58
|
|
|
|
59
|
|
|
$give_listener = give_clean( filter_input( INPUT_GET, 'give-listener' ) ); |
60
|
|
|
|
61
|
|
|
// Must be a stripe listener to proceed. |
62
|
|
|
if ( ! isset( $give_listener ) || 'stripe' !== $give_listener ) { |
63
|
|
|
return; |
64
|
|
|
} |
65
|
|
|
|
66
|
|
|
// Get the Stripe SDK autoloader. |
67
|
|
|
require_once GIVE_PLUGIN_DIR . 'vendor/autoload.php'; |
68
|
|
|
|
69
|
|
|
// Set App Info, API Key, and API Version. |
70
|
|
|
give_stripe_set_app_info(); |
71
|
|
|
$this->stripe_gateway->set_api_version(); |
72
|
|
|
|
73
|
|
|
// Retrieve the request's body and parse it as JSON. |
74
|
|
|
$body = @file_get_contents( 'php://input' ); |
75
|
|
|
$event = json_decode( $body ); |
76
|
|
|
|
77
|
|
|
$processed_event = $this->process( $event ); |
78
|
|
|
|
79
|
|
|
if ( false === $processed_event ) { |
80
|
|
|
$message = __( 'Something went wrong with processing the payment gateway event.', 'give' ); |
81
|
|
|
} else { |
82
|
|
|
$message = sprintf( |
83
|
|
|
/* translators: 1. Processing result. */ |
84
|
|
|
__( 'Processed event: %s', 'give' ), |
85
|
|
|
$processed_event |
86
|
|
|
); |
87
|
|
|
} |
88
|
|
|
|
89
|
|
|
give_stripe_record_log( |
90
|
|
|
__( 'Stripe - Webhook Received', 'give' ), |
91
|
|
|
sprintf( |
92
|
|
|
/* translators: 1. Event ID 2. Event Type 3. Message */ |
93
|
|
|
__( 'Webhook received with ID %1$s and TYPE %2$s which processed and returned a message %3$s.', 'give' ), |
94
|
|
|
$event_json->id, |
95
|
|
|
$event_json->type, |
96
|
|
|
$message |
97
|
|
|
) |
98
|
|
|
); |
99
|
|
|
|
100
|
|
|
status_header( 200 ); |
101
|
|
|
exit( $message ); |
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
/** |
105
|
|
|
* Process Stripe Webhooks. |
106
|
|
|
* |
107
|
|
|
* @since 2.5.0 |
108
|
|
|
* @access public |
109
|
|
|
* |
110
|
|
|
* @param \Stripe\Event $event_json Stripe Event. |
111
|
|
|
*/ |
112
|
|
|
public function process( $event_json ) { |
113
|
|
|
|
114
|
|
|
// Next, proceed with additional webhooks. |
115
|
|
|
if ( isset( $event_json->id ) ) { |
116
|
|
|
|
117
|
|
|
status_header( 200 ); |
118
|
|
|
|
119
|
|
|
try { |
120
|
|
|
|
121
|
|
|
$event = \Stripe\Event::retrieve( $event_json->id ); |
122
|
|
|
|
123
|
|
|
// Update time of webhook received whenever the event is retrieved. |
124
|
|
|
give_update_option( 'give_stripe_last_webhook_received_timestamp', current_time( 'timestamp', 1 ) ); |
125
|
|
|
|
126
|
|
|
} catch ( \Stripe\Error\Authentication $e ) { |
127
|
|
|
|
128
|
|
|
if ( strpos( $e->getMessage(), 'Platform access may have been revoked' ) !== false ) { |
129
|
|
|
give_stripe_connect_delete_options(); |
130
|
|
|
} |
131
|
|
|
} catch ( Exception $e ) { |
132
|
|
|
die( 'Invalid event ID' ); |
133
|
|
|
} |
134
|
|
|
|
135
|
|
|
// Bailout, if event type doesn't exists. |
136
|
|
|
if ( empty( $event->type ) ) { |
137
|
|
|
return false; |
138
|
|
|
} |
139
|
|
|
|
140
|
|
|
switch ( $event->type ) { |
141
|
|
|
|
142
|
|
View Code Duplication |
case 'payment_intent.succeeded': |
|
|
|
|
143
|
|
|
$intent = $event->data->object; |
144
|
|
|
|
145
|
|
|
if ( 'succeeded' === $intent->status ) { |
146
|
|
|
$donation_id = give_get_purchase_id_by_transaction_id( $intent->id ); |
147
|
|
|
|
148
|
|
|
// Update payment status to donation. |
149
|
|
|
give_update_payment_status( $donation_id, 'publish' ); |
150
|
|
|
|
151
|
|
|
// Insert donation note to inform admin that charge succeeded. |
152
|
|
|
give_insert_payment_note( $donation_id, __( 'Charge succeeded in Stripe.', 'give' ) ); |
153
|
|
|
} |
154
|
|
|
|
155
|
|
|
break; |
156
|
|
|
|
157
|
|
View Code Duplication |
case 'payment_intent.payment_failed': |
|
|
|
|
158
|
|
|
$intent = $event->data->object; |
159
|
|
|
$donation_id = give_get_purchase_id_by_transaction_id( $intent->id ); |
160
|
|
|
|
161
|
|
|
// Update payment status to donation. |
162
|
|
|
give_update_payment_status( $donation_id, 'failed' ); |
163
|
|
|
|
164
|
|
|
// Insert donation note to inform admin that charge succeeded. |
165
|
|
|
give_insert_payment_note( $donation_id, __( 'Charge failed in Stripe.', 'give' ) ); |
166
|
|
|
|
167
|
|
|
break; |
168
|
|
|
|
169
|
|
|
case 'charge.refunded': |
170
|
|
|
global $wpdb; |
171
|
|
|
|
172
|
|
|
$charge = $event->data->object; |
173
|
|
|
|
174
|
|
|
if ( $charge->refunded ) { |
175
|
|
|
|
176
|
|
|
$payment_id = $wpdb->get_var( $wpdb->prepare( "SELECT donation_id FROM {$wpdb->donationmeta} WHERE meta_key = '_give_payment_transaction_id' AND meta_value = %s LIMIT 1", $charge->id ) ); |
177
|
|
|
|
178
|
|
|
if ( $payment_id ) { |
179
|
|
|
|
180
|
|
|
give_update_payment_status( $payment_id, 'refunded' ); |
181
|
|
|
give_insert_payment_note( $payment_id, __( 'Charge refunded in Stripe.', 'give' ) ); |
182
|
|
|
|
183
|
|
|
} |
184
|
|
|
} |
185
|
|
|
|
186
|
|
|
break; |
187
|
|
|
} |
188
|
|
|
|
189
|
|
|
do_action( 'give_stripe_event_' . $event->type, $event ); |
190
|
|
|
|
191
|
|
|
return $event->type; |
192
|
|
|
|
193
|
|
|
} else { |
194
|
|
|
status_header( 500 ); |
195
|
|
|
// Something went wrong outside of Stripe. |
196
|
|
|
give_record_gateway_error( __( 'Stripe Error', 'give' ), sprintf( __( 'An error occurred while processing a webhook.', 'give' ) ) ); |
197
|
|
|
die( '-1' ); // Failed. |
198
|
|
|
} // End if(). |
199
|
|
|
} |
200
|
|
|
} |
201
|
|
|
} |
202
|
|
|
|
203
|
|
|
new Give_Stripe_Webhooks(); |
204
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.