Making WordPress.org

Changeset 2532


Ignore:
Timestamp:
02/19/2016 12:10:43 AM (10 years ago)
Author:
iandunn
Message:

WordCamp Budgets Dashboard: Send Sponsor Invoices to QuickBooks.

This includes several interconnected pieces:

  • Creating new Customers
  • Fetching existing Customers
  • Creating Invoices
  • Sending Invoices
Location:
sites/trunk/wordcamp.org/public_html/wp-content/plugins
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-payments-network/includes/sponsor-invoices-dashboard.php

    r2528 r2532  
    217217    switch_to_blog( $site_id );
    218218
    219     $quickbooks_result = send_invoice_to_quickbooks( $site_id, $invoice_id );
     219    $quickbooks_result = \WordCamp_QBO_Client::send_invoice_to_quickbooks( $invoice_id );
    220220
    221221    if ( is_int( $quickbooks_result ) ) {
     
    236236        wp_send_json_error( $result );
    237237    }
    238 }
    239 
    240 /**
    241  * Send an invoice to the sponsor through QuickBooks Online's API
    242  *
    243  * @param int $site_id
    244  * @param int $invoice_id
    245  *
    246  * @return int|string
    247  */
    248 function send_invoice_to_quickbooks( $site_id, $invoice_id ) {
    249     switch_to_blog( $site_id );
    250 
    251     $invoice_meta = get_post_custom( $invoice_id );
    252     $sponsor_meta = get_post_custom( $invoice_meta['_wcbsi_sponsor_id'][0] );
    253 
    254     /* these are the values needed for the API call. they're guaranteed to exist.
    255     wp_send_json_error( array(
    256         $sponsor_meta['_wcpt_sponsor_company_name'][0],
    257         $sponsor_meta['_wcpt_sponsor_first_name'][0],
    258         $sponsor_meta['_wcpt_sponsor_last_name'][0],
    259         $sponsor_meta['_wcpt_sponsor_email_address'][0],
    260         $sponsor_meta['_wcpt_sponsor_phone_number'][0],
    261 
    262         $sponsor_meta['_wcpt_sponsor_street_address1'][0],
    263         $sponsor_meta['_wcpt_sponsor_street_address2'][0],
    264         $sponsor_meta['_wcpt_sponsor_city'][0],
    265         $sponsor_meta['_wcpt_sponsor_state'][0],
    266         $sponsor_meta['_wcpt_sponsor_zip_code'][0],
    267         $sponsor_meta['_wcpt_sponsor_country'][0],
    268 
    269         $invoice_meta['_wcbsi_due_date'][0],
    270         $invoice_meta['_wcbsi_description'][0],
    271         $invoice_meta['_wcbsi_currency'][0],
    272         $invoice_meta['_wcbsi_amount'][0],
    273     ) );
    274     */
    275 
    276     $sent = 'QuickBooks integration is not complete yet.';
    277     // todo return QBO invoice ID on success, or an error message string on failure
    278 
    279     restore_current_blog();
    280 
    281     return $sent;
    282238}
    283239
  • sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-qbo-client/wordcamp-qbo-client.php

    r2524 r2532  
    270270
    271271    /**
     272     * Send an invoice to the sponsor through QuickBooks Online's API
     273     *
     274     * @param int $invoice_id
     275     *
     276     * @return string
     277     */
     278    public static function send_invoice_to_quickbooks( $invoice_id ) {
     279        $request  = self::build_send_invoice_request( $invoice_id );
     280        $response = wp_remote_post( $request['url'], $request['args'] );
     281
     282        if ( is_wp_error( $response ) ) {
     283            $sent = $response->get_error_message();
     284        } else {
     285            $body = json_decode( wp_remote_retrieve_body( $response ) );
     286
     287            if ( is_numeric( $body ) ) {
     288                $sent = absint( $body );
     289            } elseif ( isset( $body->message ) ) {
     290                $sent = $body->message;
     291            } else {
     292                $sent = 'Unknown error.';
     293            }
     294        }
     295
     296        return $sent;
     297    }
     298
     299    /**
     300     * Build a request for sending an invoice to QuickBooks
     301     *
     302     * @param int $invoice_id
     303     *
     304     * @return array
     305     */
     306    protected static function build_send_invoice_request( $invoice_id ) {
     307        $invoice      = get_post( $invoice_id );
     308        $invoice_meta = get_post_custom( $invoice_id );
     309        $sponsor_meta = get_post_custom( $invoice_meta['_wcbsi_sponsor_id'][0] );
     310
     311        $due_date = new \DateTime(
     312            date( 'Y-m-d', $invoice_meta['_wcbsi_due_date'][0] ),
     313            new \DateTimeZone( get_option('timezone_string') )
     314        );
     315
     316        $payload = array(
     317            'invoice_title'   => sanitize_text_field( $invoice->post_title                       ),
     318            'currency_code'   => sanitize_text_field( $invoice_meta['_wcbsi_currency'       ][0] ),
     319            'qbo_class_id'    => sanitize_text_field( $invoice_meta['_wcbsi_qbo_class_id'   ][0] ),
     320            'amount'          => floatval(            $invoice_meta['_wcbsi_amount'         ][0] ),
     321            'description'     => sanitize_text_field( $invoice_meta['_wcbsi_description'    ][0] ),
     322            'due_date'        => $due_date->format( 'Y-m-dP' ),
     323
     324            'statement_memo' => sprintf(
     325                'WordCamp.org Invoice: %s',
     326                esc_url_raw( admin_url( sprintf( 'post.php?post=%s&action=edit', $invoice_id ) ) )
     327            ),
     328
     329            'sponsor' => array(
     330                'company-name'  => sanitize_text_field( $sponsor_meta['_wcpt_sponsor_company_name' ][0] ),
     331                'first-name'    => sanitize_text_field( $sponsor_meta['_wcpt_sponsor_first_name'   ][0] ),
     332                'last-name'     => sanitize_text_field( $sponsor_meta['_wcpt_sponsor_last_name'    ][0] ),
     333                'email-address' => is_email(            $sponsor_meta['_wcpt_sponsor_email_address'][0] ),
     334                'phone-number'  => sanitize_text_field( $sponsor_meta['_wcpt_sponsor_phone_number' ][0] ),
     335
     336                'address1' => sanitize_text_field( $sponsor_meta['_wcpt_sponsor_street_address1'][0] ),
     337                'city'     => sanitize_text_field( $sponsor_meta['_wcpt_sponsor_city'           ][0] ),
     338                'state'    => sanitize_text_field( $sponsor_meta['_wcpt_sponsor_state'          ][0] ),
     339                'zip-code' => sanitize_text_field( $sponsor_meta['_wcpt_sponsor_zip_code'       ][0] ),
     340                'country'  => sanitize_text_field( $sponsor_meta['_wcpt_sponsor_country'        ][0] ),
     341            )
     342        );
     343
     344        if ( isset( $sponsor_meta['_wcpt_sponsor_street_address2'][0] ) ) {
     345            $payload['sponsor']['address2'] = sanitize_text_field( $sponsor_meta['_wcpt_sponsor_street_address2'][0] );
     346        }
     347
     348        $request_url  = self::$api_base . '/invoice';
     349        $body         = wp_json_encode( $payload );
     350        $oauth_header = self::_get_auth_header( 'post', $request_url, $body );
     351
     352        $args = array(
     353            'headers' => array(
     354                'Authorization' => $oauth_header,
     355                'Content-Type'  => 'application/json',
     356            ),
     357            'body' => $body,
     358        );
     359
     360        return array(
     361            'url'  => $request_url,
     362            'args' => $args,
     363        );
     364    }
     365
     366    /**
    272367     * Create an HMAC signature header for a request.
    273368     *
  • sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-qbo/wordcamp-qbo.php

    r2521 r2532  
    8383            'methods' => 'GET',
    8484            'callback' => array( __CLASS__, 'rest_callback_classes' ),
     85        ) );
     86
     87        register_rest_route( 'wordcamp-qbo/v1', '/invoice', array(
     88            'methods' => 'GET, POST',
     89            'callback' => array( __CLASS__, 'rest_callback_invoice' ),
    8590        ) );
    8691    }
     
    264269
    265270    /**
     271     * REST: /invoice
     272     *
     273     * Creates a new Invoice in QuickBooks and sends it to the Customer
     274     *
     275     * @param WP_REST_Request $request
     276     *
     277     * @return int|WP_Error The invoice ID on success, or a WP_Error on failure
     278     */
     279    public static function rest_callback_invoice( $request ) {
     280        if ( ! self::_is_valid_request( $request ) ) {
     281            return new WP_Error( 'unauthorized', 'Unauthorized', array( 'status' => 401 ) );
     282        }
     283
     284        $invoice_id = self::create_invoice(
     285            $request->get_param( 'sponsor'         ),
     286            $request->get_param( 'currency_code'   ),
     287            $request->get_param( 'qbo_class_id'    ),
     288            $request->get_param( 'invoice_title'   ),
     289            $request->get_param( 'amount'          ),
     290            $request->get_param( 'description'     ),
     291            $request->get_param( 'due_date'        ),
     292            $request->get_param( 'statement_memo'  )
     293        );
     294
     295        if ( is_wp_error( $invoice_id ) ) {
     296            return $invoice_id;
     297        }
     298
     299        /*
     300         * @todo Sending invoices automatically is initially disabled so we can manually review them for accuracy
     301        $invoice_sent = self::send_invoice( $invoice_id );
     302
     303        if ( is_wp_error( $invoice_sent ) ) {
     304            self::notify_invoice_failed_to_send( $invoice_id, $invoice_sent );
     305        }
     306        */
     307
     308        return $invoice_id;
     309    }
     310
     311    /**
     312     * Creates an Invoice in QuickBooks
     313     *
     314     * @param array  $sponsor
     315     * @param string $currency_code
     316     * @param int    $class_id
     317     * @param string $invoice_title
     318     * @param float  $amount
     319     * @param string $description
     320     * @param string $due_date
     321     * @param string $statement_memo
     322     *
     323     * @return int|WP_Error Invoice ID on success; error on failure
     324     */
     325    protected static function create_invoice( $sponsor, $currency_code, $class_id, $invoice_title, $amount, $description, $due_date, $statement_memo ) {
     326        $qbo_request = self::build_qbo_create_invoice_request(
     327            $sponsor,
     328            $currency_code,
     329            $class_id,
     330            $invoice_title,
     331            $amount,
     332            $description,
     333            $due_date,
     334            $sponsor['email-address'],
     335            $statement_memo
     336        );
     337
     338        if ( is_wp_error( $qbo_request ) ) {
     339            return $qbo_request;
     340        }
     341
     342        $response = wp_remote_post( $qbo_request['url'], $qbo_request['args'] );
     343
     344        if ( is_wp_error( $response ) ) {
     345            $result = $response;
     346        } elseif ( 200 != wp_remote_retrieve_response_code( $response ) ) {
     347            $result = new WP_Error( 'invalid_http_code', 'Invalid HTTP response code', $response );
     348        } else {
     349            $body = json_decode( wp_remote_retrieve_body( $response ), true );
     350
     351            if ( isset( $body['Invoice']['Id'] ) ) {
     352                $result = absint( $body['Invoice']['Id'] );
     353            } else {
     354                $result = new WP_Error( 'empty_body', 'Could not decode invoice result.', $response );
     355            }
     356        }
     357
     358        return $result;
     359    }
     360
     361    /**
     362     * Build the requset to create an invoice in QuickBooks
     363     *
     364     * @param array  $sponsor
     365     * @param string $currency_code
     366     * @param int    $class_id
     367     * @param string $invoice_title
     368     * @param float  $amount
     369     * @param string $description
     370     * @param string $due_date
     371     * @param string $customer_email
     372     * @param string $statement_memo
     373     *
     374     * @return array|WP_Error
     375     */
     376    protected static function build_qbo_create_invoice_request( $sponsor, $currency_code, $class_id, $invoice_title, $amount, $description, $due_date, $customer_email, $statement_memo ) {
     377        $customer_id = self::probably_get_customer_id( $sponsor, $currency_code );
     378
     379        if ( is_wp_error( $customer_id ) ) {
     380            return $customer_id;
     381        }
     382
     383        $class_id        = sanitize_text_field( $class_id        );
     384        $invoice_title   = sanitize_text_field( $invoice_title   );
     385        $amount          = floatval(            $amount          );
     386        $description     = sanitize_text_field( $description     );
     387        $due_date        = sanitize_text_field( $due_date        );
     388        $statement_memo  = sanitize_text_field( $statement_memo  );
     389
     390        /*
     391         * The currency code only needs to be sanitized, not validated, because QBO will reject the invoice if
     392         * an invalid code is passed. We don't have to worry about an invoice being assigned the the home currency
     393         * by accident.
     394         */
     395        $currency_code = sanitize_text_field( $currency_code );
     396
     397        /*
     398         * QBO sandboxes will send invoices to whatever e-mail address you assign them, rather than sending them
     399         * to the sandbox owner. So to avoid sending sandbox e-mails to real sponsor addresses, we use a fake
     400         * address instead.
     401         */
     402        if ( self::$sandbox_mode ) {
     403            $customer_email = 'jane.doe@example.org';
     404        } else {
     405            $customer_email = is_email( $customer_email );
     406        }
     407
     408        foreach ( array( 'amount', 'due_date', 'customer_id', 'customer_email' ) as $field ) {
     409            if ( empty( $$field ) ) {
     410                return new WP_Error( 'required_field_empty', "$field cannot be empty." );
     411            }
     412        }
     413
     414        self::load_options();
     415        $oauth = self::_get_oauth();
     416        $oauth->set_token( self::$options['auth']['oauth_token'], self::$options['auth']['oauth_token_secret'] );
     417
     418        $payment_instructions = str_replace( "\t", '', "
     419            Please remit checks to: WordPress Community Support, PBC, 3426 SE Kathryn Ct, Milwaukie, OR 97222
     420
     421            For payments via ACH or international wire transfers:
     422
     423            Bank Name: JPMorgan Chase Bank, N.A.
     424            Bank Address: 4 New York Plaza, Floor 15, New York, NY 10004, USA
     425            SWIFT/BIC: CHASUS33
     426            Bank Routing & Transit Number: 021000021
     427            Account Number: 791828879
     428
     429            To pay via credit card: Please send the payment via PayPal to sponsor@wordcamp.org. An additional 3% on the payment to cover PayPal fees is highly appreciated."
     430        );
     431
     432        $payload = array(
     433            'PrivateNote' => $statement_memo,
     434
     435            'Line' => array(
     436                array(
     437                    'Amount'      => $amount,
     438                    'Description' => $invoice_title,
     439                    'DetailType'  => 'SalesItemLineDetail',
     440
     441                    'SalesItemLineDetail' => array(
     442                        'ItemRef' => array(
     443                            'value' => '20', // Sponsorship
     444                        ),
     445
     446                        'ClassRef' => array(
     447                            'value' => $class_id,
     448                        ),
     449
     450                        'UnitPrice' => $amount,
     451                        'Qty'       => 1,
     452                    )
     453                )
     454            ),
     455
     456            'CustomerRef' => array(
     457                'value' => $customer_id,
     458            ),
     459
     460            // Note: the limit for this is 1,000 characters
     461            'CustomerMemo' => array(
     462                'value' => sprintf( "%s\n%s", $description, $payment_instructions ),
     463            ),
     464
     465            'SalesTermRef' => array(
     466                'value' => 1, // Due on receipt
     467            ),
     468
     469            'DueDate' => $due_date,
     470
     471            'BillEmail' => array(
     472                'Address' => $customer_email,
     473            ),
     474        );
     475
     476        /*
     477         * QuickBooks doesn't have a CustomerCurrency row for the home currency, so a CurrencyRef is only used
     478         * for foreign currencies.
     479         *
     480         * QBO will automatically activate a valid currency for our Company when we create an invoice using it
     481         * for the first time, so we don't need any code to automatically activate them.
     482         */
     483        if ( 'USD' != $currency_code ) {
     484            $payload['CurrencyRef'] = array(
     485                'value' => $currency_code,
     486            );
     487        }
     488
     489        $request_url = sprintf(
     490            '%s/v3/company/%d/invoice',
     491            self::$api_base_url,
     492            rawurlencode( self::$options['auth']['realmId'] )
     493        );
     494
     495        $payload = wp_json_encode( $payload );
     496
     497        $args = array(
     498            'headers' => array(
     499                'Authorization' => $oauth->get_oauth_header( 'POST', $request_url, $payload ),
     500                'Accept'        => 'application/json',
     501                'Content-Type'  => 'application/json',
     502            ),
     503            'body' => $payload,
     504        );
     505
     506        return array(
     507            'url'  => $request_url,
     508            'args' => $args
     509        );
     510    }
     511
     512    /**
     513     * Email a QuickBooks invoice to the Customer
     514     *
     515     * @param int $invoice_id
     516     *
     517     * @return bool|WP_Error true on success; WP_Error on failure
     518     */
     519    protected static function send_invoice( $invoice_id ) {
     520        $qbo_request = self::build_qbo_send_invoice_request( $invoice_id );
     521        $response    = wp_remote_post( $qbo_request['url'], $qbo_request['args'] );
     522
     523        if ( is_wp_error( $response ) ) {
     524            $result = $response;
     525        } elseif ( 200 != wp_remote_retrieve_response_code( $response ) ) {
     526            $result = new WP_Error( 'invalid_http_code', 'Invalid HTTP response code', $response );
     527        } else {
     528            $body = json_decode( wp_remote_retrieve_body( $response ), true );
     529
     530            if ( isset( $body['Invoice']['EmailStatus'] ) && 'EmailSent' === $body['Invoice']['EmailStatus'] ) {
     531                $result = true;
     532            } else {
     533                $result = new WP_Error( 'empty_body', 'Could not decode invoice result.', $response );
     534            }
     535        }
     536
     537        return $result;
     538    }
     539
     540    /**
     541     * Build a request to send an Invoice via QuickBook's API
     542     *
     543     * @param int $invoice_id
     544     *
     545     * @return array
     546     */
     547    protected static function build_qbo_send_invoice_request( $invoice_id ) {
     548        self::load_options();
     549        $oauth = self::_get_oauth();
     550        $oauth->set_token( self::$options['auth']['oauth_token'], self::$options['auth']['oauth_token_secret'] );
     551
     552        $request_url = sprintf(
     553            '%s/v3/company/%d/invoice/%s/send',
     554            self::$api_base_url,
     555            rawurlencode( self::$options['auth']['realmId'] ),
     556            rawurlencode( absint( $invoice_id ) )
     557        );
     558
     559        $args = array(
     560            'headers' => array(
     561                'Authorization' => $oauth->get_oauth_header( 'POST', $request_url ),
     562                'Accept'        => 'application/json',
     563                'Content-Type'  => 'application/octet-stream',
     564            ),
     565            'body' => '',
     566        );
     567
     568        return array(
     569            'url'  => $request_url,
     570            'args' => $args,
     571        );
     572    }
     573
     574    /**
     575     * Notify Central that an invoice was created but couldn't be sent to the sponsor
     576     *
     577     * @param int      $invoice_id
     578     * @param WP_Error $error
     579     *
     580     * @return bool
     581     */
     582    protected static function notify_invoice_failed_to_send( $invoice_id, $error ) {
     583        $message = sprintf( "
     584            QuickBooks invoice $invoice_id was created, but an error occurred while trying to send it to the sponsor.
     585
     586            This may be an indication of a bug on WordCamp.org, so please ask your friendly neighborhood developers to investigate.
     587
     588            The invoice will probably need to be sent manually in QuickBooks, but let the developers investigate first, and then go from there.
     589
     590            Debugging information for the developers:
     591
     592            %s",
     593            print_r( $error, true )
     594        );
     595        $message = str_replace( "\t", '', $message );
     596
     597        return wp_mail( 'support@wordcamp.org', "QuickBooks invoice $invoice_id failed to send", $message );
     598    }
     599
     600    /**
     601     * Get a Customer ID, either by finding an existing one, or creating a new one
     602     *
     603     * @param string $sponsor
     604     * @param string $currency_code
     605     *
     606     * @return int|WP_Error The customer ID if success; a WP_Error if failure
     607     */
     608    protected static function probably_get_customer_id( $sponsor, $currency_code ) {
     609        $customer_id = self::get_customer( $sponsor['company-name'], $currency_code );
     610
     611        if ( is_wp_error( $customer_id ) || ! $customer_id ) {
     612            $customer_id = self::create_customer( $sponsor, $currency_code );
     613        }
     614
     615        return $customer_id;
     616    }
     617
     618    /**
     619     * Fetch a Customer record from QBO
     620     *
     621     * @param string $customer_name
     622     * @param string $currency_code
     623     *
     624     * @return int|false|WP_Error A customer ID as integer, if one was found; false if no match was found; a WP_Error if an error occurred.
     625     */
     626    protected static function get_customer( $customer_name, $currency_code ) {
     627        $qbo_request = self::build_qbo_get_customer_request( $customer_name );
     628
     629        if ( is_wp_error( $qbo_request ) ) {
     630            return $qbo_request;
     631        }
     632
     633        $response = wp_remote_get( $qbo_request['url'], $qbo_request['args'] );
     634
     635        if ( is_wp_error( $response ) ) {
     636            $result = $response;
     637        } elseif ( 200 != wp_remote_retrieve_response_code( $response ) ) {
     638            $result = new WP_Error( 'invalid_http_code', 'Invalid HTTP response code', $response );
     639        } else {
     640            $body = json_decode( wp_remote_retrieve_body( $response ), true );
     641
     642            if ( isset( $body['QueryResponse']['Customer'][0]['Id'] ) ) {
     643                $result = self::pluck_customer_id_by_currency( $body['QueryResponse']['Customer'], $currency_code );
     644            } elseif ( isset( $body['QueryResponse'] ) && 0 === count( $body['QueryResponse'] ) ) {
     645                $result = false;
     646            } else {
     647                $result = new WP_Error( 'invalid_response_body', 'Could not extract information from response.', $response );
     648            }
     649        }
     650
     651        return $result;
     652    }
     653
     654    /**
     655     * Build a request to fetch a Customer from QuickBook's API
     656     *
     657     * @param string $customer_name
     658     *
     659     * @return array|WP_Error
     660     */
     661    protected static function build_qbo_get_customer_request( $customer_name ) {
     662        global $wpdb;
     663
     664        $customer_name = sanitize_text_field( $customer_name );
     665
     666        self::load_options();
     667        $oauth = self::_get_oauth();
     668        $oauth->set_token( self::$options['auth']['oauth_token'], self::$options['auth']['oauth_token_secret'] );
     669
     670        $request_url = sprintf(
     671            '%s/v3/company/%d/query',
     672            self::$api_base_url,
     673            rawurlencode( self::$options['auth']['realmId'] )
     674        );
     675
     676        $request_url_query = array(
     677            'query' => $wpdb->prepare(
     678                "SELECT * FROM Customer WHERE CompanyName = '%s'",
     679                $customer_name
     680            ),
     681        );
     682
     683        $args = array(
     684            'headers' => array(
     685                'Authorization' => $oauth->get_oauth_header( 'GET', $request_url, $request_url_query ),
     686                'Accept'        => 'application/json',
     687            ),
     688        );
     689
     690        $request_url_query = array_map( 'rawurlencode', $request_url_query ); // has to be done after get_oauth_header(), or oauth_signature won't be generated correctly
     691        $request_url       = add_query_arg( $request_url_query, $request_url );
     692
     693        return array(
     694            'url'  => $request_url,
     695            'args' => $args,
     696        );
     697    }
     698
     699    /**
     700     * Pluck a Customer out of an array based on their currency
     701     *
     702     * QuickBook's API doesn't allow you to filter query results based on a CurrencyRef, so we have to do it
     703     * manually.
     704     *
     705     * @param array  $customers
     706     * @param string $currency_code
     707     *
     708     * @return int|false A customer ID on success, or false on failure
     709     */
     710    protected static function pluck_customer_id_by_currency( $customers, $currency_code ) {
     711        $customer_id = false;
     712
     713        foreach ( $customers as $customer ) {
     714            if ( $customer['CurrencyRef']['value'] === $currency_code ) {
     715                $customer_id = absint( $customer['Id'] );
     716                break;
     717            }
     718        }
     719
     720        return $customer_id;
     721    }
     722
     723    /**
     724     * Create a customer in QuickBooks for a corresponding Sponsor in WordCamp.org
     725     *
     726     * @param array  $sponsor
     727     * @param string $currency_code
     728     *
     729     * @return int|WP_Error The customer ID if success; a WP_Error if failure
     730     */
     731    protected static function create_customer( $sponsor, $currency_code ) {
     732        $qbo_request = self::build_qbo_create_customer_request( $sponsor, $currency_code );
     733
     734        if ( is_wp_error( $qbo_request ) ) {
     735            return $qbo_request;
     736        }
     737
     738        $response = wp_remote_post( $qbo_request['url'], $qbo_request['args'] );
     739
     740        if ( is_wp_error( $response ) ) {
     741            $result = $response;
     742        } elseif ( 200 != wp_remote_retrieve_response_code( $response ) ) {
     743            $result = new WP_Error( 'invalid_http_code', 'Invalid HTTP response code', $response );
     744        } else {
     745            $body = json_decode( wp_remote_retrieve_body( $response ), true );
     746
     747            if ( isset( $body['Customer']['Id'] ) ) {
     748                $result = absint( $body['Customer']['Id'] );
     749            } else {
     750                $result = new WP_Error( 'invalid_response_body', 'Could not extract customer ID from response.', $response );
     751            }
     752        }
     753
     754        return $result;
     755    }
     756
     757    /**
     758     * Build a request to create a Customer via QuickBook's API
     759     *
     760     * @param array  $sponsor
     761     * @param string $currency_code
     762     *
     763     * @return array|WP_Error
     764     */
     765    protected static function build_qbo_create_customer_request( $sponsor, $currency_code ) {
     766        self::load_options();
     767        $oauth = self::_get_oauth();
     768        $oauth->set_token( self::$options['auth']['oauth_token'], self::$options['auth']['oauth_token_secret'] );
     769
     770        $sponsor                  = array_map( 'sanitize_text_field', $sponsor );
     771        $sponsor['email-address'] = is_email( $sponsor['email-address'] );
     772        $currency_code            = sanitize_text_field( $currency_code );
     773
     774        if ( empty( $sponsor['company-name'] ) || empty( $sponsor['email-address'] ) ) {
     775            return new WP_Error( 'required_fields_missing', 'Required fields are missing.', $sponsor );
     776        }
     777
     778        $payload = array(
     779            'BillAddr' => array(
     780                'Line1'                  => $sponsor['address1'],
     781                'City'                   => $sponsor['city'],
     782                'Country'                => $sponsor['country'],
     783                'CountrySubDivisionCode' => $sponsor['state'],
     784                'PostalCode'             => $sponsor['zip-code'],
     785            ),
     786
     787            'CurrencyRef' => array(
     788                'value' => $currency_code
     789            ),
     790
     791            'PreferredDeliveryMethod' =>'Email',
     792
     793            'GivenName'        => $sponsor['first-name'],
     794            'FamilyName'       => $sponsor['last-name'],
     795            'CompanyName'      => $sponsor['company-name'],
     796            'DisplayName'      => sprintf( '%s - %s', $sponsor['company-name'], $currency_code ),
     797            'PrintOnCheckName' => $sponsor['company-name'],
     798
     799            'PrimaryPhone' => array(
     800                'FreeFormNumber' => $sponsor['phone-number'],
     801            ),
     802
     803            'PrimaryEmailAddr' => array(
     804                'Address' => $sponsor['email-address'],
     805            ),
     806        );
     807
     808        if ( isset( $sponsor['address2'] ) ) {
     809            $payload['BillAddr']['Line2'] = $sponsor['address2'];
     810        }
     811
     812        $request_url = sprintf(
     813            '%s/v3/company/%d/customer',
     814            self::$api_base_url,
     815            rawurlencode( self::$options['auth']['realmId'] )
     816        );
     817
     818        $payload = wp_json_encode( $payload );
     819
     820        $args = array(
     821            'headers' => array(
     822                'Authorization' => $oauth->get_oauth_header( 'POST', $request_url, $payload ),
     823                'Accept'        => 'application/json',
     824                'Content-Type'  => 'application/json',
     825            ),
     826            'body' => $payload,
     827        );
     828
     829        return array(
     830            'url'  => $request_url,
     831            'args' => $args,
     832        );
     833    }
     834
     835    /**
    266836     * Verify an HMAC signature for an API request.
    267837     *
Note: See TracChangeset for help on using the changeset viewer.