Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
0.00% |
0 / 151 |
|
0.00% |
0 / 7 |
CRAP | |
0.00% |
0 / 1 |
| JPCRM_GiveWP | |
0.00% |
0 / 150 |
|
0.00% |
0 / 7 |
756 | |
0.00% |
0 / 1 |
| __construct | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
6 | |||
| check_dependencies | |
0.00% |
0 / 21 |
|
0.00% |
0 / 1 |
6 | |||
| init_hooks | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
| add_donation | |
0.00% |
0 / 33 |
|
0.00% |
0 / 1 |
30 | |||
| add_update_donor | |
0.00% |
0 / 76 |
|
0.00% |
0 / 1 |
182 | |||
| update_donation_status | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
12 | |||
| get_transaction_id_from_give_id | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
2 | |||
| 1 | <?php |
| 2 | /* |
| 3 | * Jetpack CRM |
| 4 | * https://jetpackcrm.com |
| 5 | * |
| 6 | * GiveWP Module |
| 7 | * |
| 8 | */ |
| 9 | namespace Automattic\JetpackCRM; |
| 10 | |
| 11 | // block direct access |
| 12 | defined( 'ZEROBSCRM_PATH' ) || exit( 0 ); |
| 13 | |
| 14 | /** |
| 15 | * |
| 16 | * GiveWP Connector for Jetpack CRM |
| 17 | */ |
| 18 | class 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 | } |