Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 151
0.00% covered (danger)
0.00%
0 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
JPCRM_GiveWP
0.00% covered (danger)
0.00%
0 / 150
0.00% covered (danger)
0.00%
0 / 7
756
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 check_dependencies
0.00% covered (danger)
0.00%
0 / 21
0.00% covered (danger)
0.00%
0 / 1
6
 init_hooks
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 add_donation
0.00% covered (danger)
0.00%
0 / 33
0.00% covered (danger)
0.00%
0 / 1
30
 add_update_donor
0.00% covered (danger)
0.00%
0 / 76
0.00% covered (danger)
0.00%
0 / 1
182
 update_donation_status
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
12
 get_transaction_id_from_give_id
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/*
3* Jetpack CRM
4* https://jetpackcrm.com
5*
6* GiveWP Module
7*
8*/
9namespace Automattic\JetpackCRM;
10
11// block direct access
12defined( 'ZEROBSCRM_PATH' ) || exit( 0 );
13
14/**
15 *
16 * GiveWP Connector for Jetpack CRM
17 */
18class JPCRM_GiveWP {
19
20    public function __construct() {
21        if ( $this->check_dependencies() ) {
22            $this->init_hooks();
23        }
24    }
25    /**
26     *
27     * Checks dependencies for GiveWP integration
28     *
29     * @return bool
30     */
31    public function check_dependencies() {
32
33        global $zbs;
34
35        $feature_name = 'GiveWP Connector for Jetpack CRM';
36
37        $give_core_reqs = array(
38            'req_core_ver' => $zbs::VERSION, // will match current core version
39            'req_DAL_ver'  => '3.0',
40        );
41        $give_plug_reqs = array(
42            'name'    => 'GiveWP',
43            'slug'    => 'give/give.php',
44            'link'    => 'https://wordpress.org/plugins/give/',
45            'kb_link' => $zbs->urls['kb_givewp'],
46            'req_ver' => '2.13.0',
47        );
48        $meets_all_reqs = $zbs->dependency_checker->check_all_reqs(
49            $feature_name,
50            $give_core_reqs,
51            $give_plug_reqs
52        );
53
54        if ( $meets_all_reqs ) {
55            return true;
56        }
57        return false;
58    }
59
60    /**
61     *
62     * Adds GiveWP hooks
63     */
64    public function init_hooks() {
65        // fires at end of GiveWP's give_insert_payment() function
66        add_action( 'give_insert_payment', array( $this, 'add_donation' ), 100, 2 );
67        // fires at end of GiveWP's Give_Payment::update_status() function
68        add_action( 'give_update_payment_status', array( $this, 'update_donation_status' ), 200, 3 );
69    }
70
71    /**
72     *
73     * Adds a donation transaction and its assigned donor contact
74     *
75     * This is hooked into the 'give_insert_payment' action, which is run at
76     * the end of GiveWP's give_insert_payment() function
77     *
78     * @param int   $givewp_donation_id donation ID
79     * @param array $payment_data       donor/donation info
80     */
81    public function add_donation( $givewp_donation_id, $payment_data ) {
82
83        global $zbs;
84
85        // add/update donor first
86        $contact_id = $this->add_update_donor( $givewp_donation_id, $payment_data );
87
88        // check if transaction exists
89        $transaction_id = $this->get_transaction_id_from_give_id( $givewp_donation_id );
90
91        // if donor now exists and the transaction ID does not exist, create!
92        if ( $contact_id && ! $transaction_id ) {
93
94            // build transaction
95
96            // status isn't always available, but we we don't really need it anyway as it'll be set by the 'update_donation_status' hook
97            $transaction_status = isset( $payment_data['status'] ) ? ucfirst( $payment_data['status'] ) : 'pending';
98
99            // date isn't consistently available, so use now if it doesn't exist:
100            // https://github.com/impress-org/givewp/blob/fd807ed0844996af33810dad11658e5c0b4aee5d/includes/payments/functions.php#L191-L193
101            // also, there's no indication as to timezone, so we'll just use server timezone
102            $transaction_date = isset( $payment_data['post_date'] ) ? strtotime( $payment_data['post_date'] ) : date( 'U' );
103
104            $transaction_title = sprintf( __( 'GiveWP donation via the %s form', 'zero-bs-crm' ), $payment_data['give_form_title'] );
105
106            $new_transaction_data = array(
107                'status'          => $transaction_status,
108                'type'            => __( 'Sale', 'zero-bs-crm' ), // someday maybe we'll add a donation type
109                'ref'             => $payment_data['purchase_key'],
110                'title'           => $transaction_title,
111                'date'            => $transaction_date,
112                'currency'        => $payment_data['currency'],
113                'total'           => $payment_data['price'],
114                'date_paid'       => $transaction_date,
115                'date_completed'  => $transaction_date,
116                'contacts'        => array( $contact_id ),
117                'tags'            => array( 'GiveWP' ),
118                'tag_mode'        => 'append',
119                'externalSources' => array(
120                    array(
121                        'source' => 'givewp',
122                        'uid'    => $givewp_donation_id,
123                    ),
124                ),
125            );
126
127            // add transaction
128            $transaction_id = $zbs->DAL->transactions->addUpdateTransaction(
129                array(
130                    'data'      => $new_transaction_data,
131                    'extraMeta' => array( 'givewp_transaction_id' => $givewp_donation_id ),
132                )
133            );
134        }
135    }
136
137    /**
138     * Adds or updates a donor contact
139     *
140     * @param int   $givewp_donation_id donation ID.
141     * @param array $payment_data       donor/donation info.
142     *
143     * @return int|false  $contact_id if successful, false otherwise
144     */
145    private function add_update_donor( $givewp_donation_id, $payment_data ) {
146
147        global $zbs;
148
149        // inspired by Jetpack Forms implementation
150        $restricted_keys    = array(
151            'externalSources',
152            'companies',
153            'lastcontacted',
154            'created',
155            'aliases',
156        );
157        $jpcrm_field_prefix = 'jpcrm-';
158
159        // build contact
160        $new_contact_data = array(
161            'status'   => __( 'Donor', 'zero-bs-crm' ),
162            'tags'     => array( 'GiveWP' ),
163            'tag_mode' => 'append',
164        );
165
166        // note that GiveWP explicitly requires a name (first_name) and email by design
167        // https://givewp.com/documentation/core/frequent-troubleshooting-issues/
168        foreach ( $payment_data['user_info'] as $k => $v ) {
169            switch ( $k ) {
170                case 'title':
171                    $new_contact_data['prefix'] = $v;
172                    break;
173                case 'first_name':
174                    $new_contact_data['fname'] = $v;
175                    break;
176                case 'last_name':
177                    $new_contact_data['lname'] = $v;
178                    break;
179                case 'email':
180                    $new_contact_data['email'] = $v;
181                    break;
182                case 'address':
183                    if ( ! empty( $v ) ) {
184                        $new_contact_data['addr1']    = $v['line1'];
185                        $new_contact_data['addr2']    = $v['line2'];
186                        $new_contact_data['city']     = $v['city'];
187                        $new_contact_data['county']   = $v['state'];
188                        $new_contact_data['postcode'] = $v['zip'];
189                        $new_contact_data['country']  = $v['country'];
190                    }
191                    break;
192                default:
193                    // handle any fields prefixed with $jpcrm_field_prefix as needed,
194                    // though by default GiveWP doesn't support custom fields
195                    if ( str_starts_with( $k, $jpcrm_field_prefix ) ) {
196                        $data_key = substr( $k, strlen( $jpcrm_field_prefix ) );
197                        if ( ! in_array( $data_key, $restricted_keys, true ) ) {
198                            if ( $data_key === 'tags' ) {
199                                $new_contact_data['tags']   = explode( ',', $v );
200                                $new_contact_data['tags'][] = 'GiveWP';
201                            } else {
202                                $new_contact_data[ $data_key ] = $v;
203                            }
204                        }
205                    }
206            }
207        }
208
209        // If this is an existing WordPress user, make sure not to lose that association.
210        $wp_user = get_user_by( 'email', $new_contact_data['email'] );
211        if ( is_object( $wp_user ) ) {
212            $new_contact_data['wpid'] = $wp_user->ID;
213        }
214
215        // specify contact source
216        $new_contact_data['externalSources'] = array(
217            array(
218                'source' => 'givewp',
219                'uid'    => $new_contact_data['email'],
220            ),
221        );
222
223        // log if contact is created by GiveWP
224        $longdesc     = sprintf( __( 'User was created from GiveWP when submitting donation %1$s through the %2$s form.', 'zero-bs-crm' ), $givewp_donation_id, '<b>' . $payment_data['give_form_title'] . '</b>' );
225        $created_meta = array(
226            'note_override' =>
227                array(
228                    'type'      => __( 'Form filled', 'zero-bs-crm' ),
229                    'shortdesc' => __( 'Created from GiveWP', 'zero-bs-crm' ),
230                    'longdesc'  => $longdesc,
231                ),
232        );
233
234        // log if GiveWP transaction was added to this contact
235        $longdesc    = sprintf( __( 'A GiveWP donation was submitted by this user via the %s form.', 'zero-bs-crm' ), '<b>' . $payment_data['give_form_title'] . '</b>' );
236        $exists_meta = array(
237            'type'      => __( 'Form filled', 'zero-bs-crm' ),
238            'shortdesc' => __( 'Donation via GiveWP', 'zero-bs-crm' ),
239            'longdesc'  => $longdesc,
240        );
241
242        // add or update contact
243        $contact_id = $zbs->DAL->contacts->addUpdateContact(
244            array(
245                'data'                 => $new_contact_data,
246                'automatorPassthrough' => $created_meta,
247                'fallBackLog'          => $exists_meta,
248                'extraMeta'            => array(),
249            )
250        );
251        return $contact_id;
252    }
253
254    /**
255     *
256     * Update a transaction status
257     *
258     * This is hooked into the 'give_update_payment_status' action, which
259     * is run at the end of GiveWP's Give_Payment::update_status() function
260     *
261     * Note that this also runs when a new donation is created
262     *
263     * @param int    $givewp_donation_id donation ID
264     * @param string $new_status         new donation status
265     * @param string $old_status         old donation status
266     *
267     * @return bool
268     */
269    public function update_donation_status( $givewp_donation_id, $new_status, $old_status ) {
270
271        global $zbs;
272
273        // completed status in GiveWP is actually publish
274        if ( $new_status === 'publish' ) {
275            $new_status = 'Completed';
276        } else {
277            // GiveWP passes lowercase statuses
278            $new_status = ucfirst( $new_status );
279        }
280
281        // check if transaction exists
282        $transaction_id = (int) $this->get_transaction_id_from_give_id( $givewp_donation_id );
283
284        // update status if transaction exists
285        if ( $transaction_id > 0 ) {
286            return $zbs->DAL->transactions->setTransactionStatus( $transaction_id, $new_status );
287        }
288        return false;
289    }
290
291    /**
292     * Gets a transaction by its GiveWP donation ID
293     *
294     * @param int $givewp_donation_id the GiveWP donation ID.
295     *
296     * @return int|false    $contact_id if successful, false otherwise
297     */
298    public function get_transaction_id_from_give_id( $givewp_donation_id ) {
299        global $zbs;
300        return $zbs->DAL->getIDWithMeta(
301            array(
302                'objtype' => ZBS_TYPE_TRANSACTION,
303                'key'     => 'extra_givewp_transaction_id',
304                'val'     => $givewp_donation_id,
305            )
306        );
307    }
308}