infusionsoft / infusionsoft-php

PHP client library for the Infusionsoft API.
https://developer.infusionsoft.com/
Other
129 stars 126 forks source link

Call to undefined method Infusionsoft\Api\Rest\ContactService::add() #136

Closed Prattski closed 7 years ago

Prattski commented 7 years ago

I just installed the SDK via composer a few days ago. I ensured that it's 1.3. I've followed all of the instructions to a T. I've got my app connected to the API - I've successfully received an access token and have stored it in the database, and I store it in the session upon loading my script.

I have verified that $_SESSION['token'] has the Infusionsoft Token object data. Everything looks good. I know for sure that the access token is not out of date, because I just generated it an hour ago.

I am trying to run this code:

if (isset($_SESSION['token'])) {
    $infusionsoft->setToken(unserialize($_SESSION['token']));
    $infusionsoft->contacts->add(array('FirstName' => 'John', 'LastName' => 'Doe'));
}

However, I'm getting the following error:

Fatal error: Call to undefined method Infusionsoft\Api\Rest\ContactService::add()

Can't figure out what the problem is - hoping someone might be able to help. Thanks!

mfairch commented 7 years ago

1.3 uses the REST for contacts by default. Try using "create" instead of add.

$infusionsoft->contacts->create(array('FirstName' => 'John', 'LastName' => 'Doe'));
Prattski commented 7 years ago

Thanks for the response micfai.

When I try that, then I wind up with a different error: (I added ###### instead of showing the actual access token in this post)

Fatal error: Uncaught exception 'Infusionsoft\Http\HttpException' with message 'exception 'GuzzleHttp\Exception\ClientException' with message 'Client error: POST https://api.infusionsoft.com/crm/rest/v1/contacts/?access_token=####### resulted in a 400 Bad Request response: {"message":"Input could not be converted to a valid request"}

mfairch commented 7 years ago

@Prattski that means your object structure is incorrect. Can you share an example of how you are building the object?

Prattski commented 7 years ago

@micfai - Ok. I've been trying to test it out with the API documentation, but it appears that capability doesn't work for me for some reason.. I'm trying to follow the structure outlined in the documentation (https://developer.infusionsoft.com/docs/rest/#!/Contact/createContactUsingPOST). Here is the array I'm trying to feed it to create a test contact:

    $contact = array(
        'email_addresses' => array(
            'email' => 'test@test.com',
            'field' => 'EMAIL1'
        ),
        'given_name' => 'John',
        'family_name' => 'Doe',
        'phone_numbers' => array(
            'field' => 'PHONE1', 
            'number' => '123-123-1234'
        )
    );

    $infusionsoft->contacts->create($contact);

I get the same error when trying your example above too:

$infusionsoft->contacts->create(array('FirstName' => 'John', 'LastName' => 'Doe'));

Also, not sure if it makes a difference or not, but in the README.md, the examples you give for the REST calls show that there's a () after the main call: the $infusionsoft->tasks(), so I also tried the following, but it didn't seem to make a difference:

$infusionsoft->contacts()->create(array('FirstName' => 'John', 'LastName' => 'Doe'));

mfairch commented 7 years ago

email_address and phone_numbers is an array of objects.

Here is a working example

    $email1         = new \stdClass;
    $email1->field  = 'EMAIL1';
    $email1->email  = 'john.doe@example.com';
    $phone1         = new \stdClass;
    $phone1->field  = 'PHONE1';
    $phone1->number = '123-123-1234';
    $contact        = ['given_name'      => 'John',
                       'family_name'     => 'Doe',
                       'email_addresses' => [$email1],
                       'phone_numbers'   => [$phone1]
    ];
Prattski commented 7 years ago

@micfai - Well first of all, thank you - that worked, and it successfully created a contact.

Now is this just how the SDK was built then? Because according to the API documentation, the email_address and phone_numbers are specifically specified as an array. How is anyone supposed to know that these fields needed to be an array of objects?

mfairch commented 7 years ago

We probably need to adjust the documentation for contacts. The example body is showing it as an array (list) of objects.

"email_addresses": [
    {
      "email": "string",
      "field": "EMAIL1"
    }
  ],
Prattski commented 7 years ago

@micfai - Ok, so when the API documentation has the [ ... ], that will almost always mean that it's an object. Is that safe to assume?

mfairch commented 7 years ago

No, not always. Take applying tags to a contact, that is an array of Ids whereas the email_addresses is an array of objects. The "Body Sample" should always be correct.

{
  "addresses": [
    {
      "country_code": "string",
      "field": "BILLING",
      "line1": "string",
      "line2": "string",
      "locality": "string",
      "postal_code": "string",
      "region": "string"
    }
  ],
  "birthday": "2017-08-16T15:05:26.349Z",
  "company": {
    "company_name": "string",
    "id": 0
  },
  "contact_type": "string",
  "custom_fields": [
    {
      "content": {},
      "id": 0
    }
  ],
  "date_created": "2017-08-16T15:05:26.349Z",
  "email_addresses": [
    {
      "email": "string",
      "field": "EMAIL1"
    }
  ],
  "family_name": "string",
  "fax_numbers": [
    {
      "field": "FAX1",
      "number": "string",
      "type": "string"
    }
  ],
  "given_name": "string",
  "job_title": "string",
  "last_updated": "2017-08-16T15:05:26.349Z",
  "lead_source_id": 0,
  "middle_name": "string",
  "notes": "string",
  "opt_in_reason": "string",
  "owner_id": 0,
  "phone_numbers": [
    {
      "extension": "string",
      "field": "PHONE1",
      "number": "string",
      "type": "string"
    }
  ],
  "preferred_locale": "en_US",
  "preferred_name": "string",
  "prefix": "string",
  "relationships": [
    {
      "id": 0,
      "linked_contact_id": 0,
      "relationship_type_id": 0
    }
  ],
  "source_type": "WEBFORM",
  "suffix": "string",
  "tag_ids": [
    0
  ],
  "time_zone": "string",
  "website": "string"
}
Prattski commented 7 years ago

@micfai - Wow.. I remember when I first talked with the sales people at Infusionsoft, they boasted at how incredible the API documentation was, and that it would be super easy. This is definitely not the case, at least speaking for myself. I really wish the documentation on this SDK was much more thorough as well. That would definitely be a request of mine. It's very much lacking. Even just providing the sample you gave me, and explaining how to use the API with arrays VS arrays of objects would be incredibly helpful. The sample you have in the README.md doesn't even work. There isn't a FirstName field...

Prattski commented 7 years ago

@micfai - FYI: This is from someone in the Infusionsoft forums that responded to my post. You can avoid having to create and populate objects within the array by adding an extra array. I have tested this with the SDK, and it works:

$contact = array(
  'email_addresses' => array(
    array(
      'email' => 'test@test.com',
      'field' => 'EMAIL1'
    )
  ),
  'given_name' => 'John',
  'family_name' => 'Doe',
  'phone_numbers' => array(
    array(
      'field' => 'PHONE1', 
      'number' => '123-123-1234'
    )
  )
);
MikeChristianson commented 7 years ago

@Prattski Beyond what I've written below, I invite you to join us at the Infusionsoft Community API forum for any help or feedback you have on the API documentation. We'd love to hear from you.

In JSON [] indicates an array and {} indicates an object. So, [{}] would be an array of objects. Using Create a Contact as an example:

"email_addresses": [
    {
      "email": "string",
      "field": "EMAIL1"
    }
]

The above example is an array of email address objects, each of which have the properties email and field. A realistic example with more than one email address might look like:

"email_addresses": [
    {
      "email": "email@example.com",
      "field": "EMAIL1"
    },
    {
      "email": "email2@example.com",
      "field": "EMAIL2"
    },
    {
      "email": "email3@example.com",
      "field": "EMAIL3"
    }
]

Unfortunately this can get confusing in PHP since it represents JSON objects as arrays. The above example in PHP is:

'email_addresses' => array( //this is the array
    array(                  //this is an object
      'email' => 'email@example.com',
      'field' => 'EMAIL1'
    ),
    array(                  //this is an object
      'email' => 'email2@example.com',
      'field' => 'EMAIL2'
    ),
    array(                  //this is an object  
      'email' => 'email3@example.com',
      'field' => 'EMAIL3'
    ),
  )
MikeChristianson commented 7 years ago

@Prattski

The sample you have in the README.md doesn't even work. There isn't a FirstName field...

The only reference I can find to FirstName is in the Making XML-RPC Requests section. Is that what you are referring to?

If so, please note that section is for XML-RPC which does have a FirstName field. As you found out, that is not the case for REST.

kevindecapite commented 7 years ago

I'm getting the same error when building my data either way (array of objects or array of arrays).

Array of Arrays

array(
    'addresses' => array(
        (int) 0 => array(
            'country_code' => 'US',
            'field' => 'BILLING',
            'line1' => '1234 Main St',
            'line2' => '',
            'locality' => 'Portland',
            'postal_code' => '97267',
            'region' => 'OR'
        ),
        (int) 1 => array(
            'country_code' => 'US',
            'field' => 'SHIPPING',
            'line1' => '1234 Main St',
            'line2' => '',
            'locality' => 'Portland',
            'postal_code' => '97267',
            'region' => 'OR'
        )
    ),
    'company' => array(
        'company_name' => 'Arc Point Group'
    ),
    'contact_type' => 'Customer',
    'date_created' => '2017-10-10T20:47:22.000Z',
    'duplicate_option' => 'Email',
    'email_addresses' => array(
        (int) 0 => array(
            'email' => 'somename@domain.com',
            'field' => 'EMAIL1'
        )
    ),
    'family_name' => 'McFrustrated',
    'given_name' => 'Confucius',
    'last_updated' => '2017-10-10T20:47:22.000Z',
    'phone_numbers' => array(
        (int) 0 => array(
            'field' => 'PHONE1',
            'number' => '8005551234'
        )
    ),
    'preferred_locale' => 'en_US',
    'preferred_name' => 'Confucius McFrustrated',
    'source_type' => 'WEBFORM',
    'opt_in_reason' => 'Opted in during checkout process.'
)

Calling $infusionsoft->contacts()->create($contact) gives me:

GuzzleHttp\Exception\ClientException: Client error:
`POST https://api.infusionsoft.com/crm/rest/v1/contacts/?access_token=12345`
resulted in a `400 Bad Request`
response:
{"message":"Input could not be converted to a valid request"}

Array of Objects

array(
    'addresses' => array(
        (int) 0 => object(stdClass) {
            field => 'BILLING'
            line1 => '1234 Main St'
            line2 => ''
            locality => 'Portland'
            postal_code => '97267'
            region => 'OR'
            country_code => 'US'
        },
        (int) 1 => object(stdClass) {
            field => 'SHIPPING'
            line1 => '1234 Main St'
            line2 => ''
            locality => 'Portland'
            postal_code => '97267'
            region => 'OR'
            country_code => 'US'
        }
    ),
    'company' => array(
        'company_name' => 'Arc Point Group'
    ),
    'contact_type' => 'Customer',
    'date_created' => '2017-10-10T20:53:33.000Z',
    'duplicate_option' => 'Email',
    'email_addresses' => array(
        (int) 0 => object(stdClass) {
            field => 'EMAIL1'
            email => 'somename@domain.com'
        }
    ),
    'family_name' => 'McFrustrated',
    'given_name' => 'Confucius',
    'last_updated' => '2017-10-10T20:53:33.000Z',
    'phone_numbers' => array(
        (int) 0 => object(stdClass) {
            field => 'PHONE1'
            number => '8005551234'
        }
    ),
    'preferred_locale' => 'en_US',
    'preferred_name' => 'Confucius McFrustrated',
    'source_type' => 'WEBFORM',
    'opt_in_reason' => 'Opted in during checkout process.'
)

Calling $infusionsoft->contacts()->create($contact) gives me:

GuzzleHttp\Exception\ClientException: Client error:
`POST https://api.infusionsoft.com/crm/rest/v1/contacts/?access_token=12345`
resulted in a `400 Bad Request`
response:
{"message":"Input could not be converted to a valid request"}

What am I doing incorrectly?

dkaitou commented 7 years ago

For anyone who is trying to apply tags to "tag_ids" when creating new contact, you will get error in that POST request. Documentation from Infusionsoft REST API page will mislead you. Message - "Tags can't be added or removed through this object"

This problem has been addressed in developer forum here

kevindecapite commented 7 years ago

Ok I got my implementation working after running more limited tests and adding in the values one at a time. In case anyone runs into the same issue as I did, here's a brief:

1. The duplicate_option => 'Email' is invalid, despite the documentation located here: https://developer.infusionsoft.com/docs/rest/#!/Contact/createOrUpdateContactUsingPUT

It needs to be removed.

2. Address region and country_codes need to be provided in a standard format, which appears non-published by InfusionSoft: https://community.infusionsoft.com/t/invalid-country-code-and-region-what-is-valid/13354

In short, you need to use USA instead of US for the country_code, BUT when providing the region (i.e. state) you need to use US-OR (I think US-Oregon may also work, but I didn't test that).

So my sample above, when changed to the following, works (snipped for brevity):

object(stdClass) {
    field => 'BILLING'
    line1 => '1234 Main St'
    line2 => ''
    locality => 'Portland'
    postal_code => '97267'
    region => 'US-OR'
    country_code => 'USA'
}

This also means that, in my case at least, I have to ignore the region and country_code properties for all my non-US addresses as CA-Alberta did not work in my testing. So I have no idea what the proper way to pass in non-US locale data is, at least not until InfusionSoft publishes this information.

MikeChristianson commented 7 years ago

@kevindecapite duplicate_option is valid for the PUT operation only. It cannot be used with the POST operation. You may be using POST without realizing it:

GuzzleHttp\Exception\ClientException: Client error:
`POST https://api.infusionsoft.com/crm/rest/v1/contacts/?access_token=12345`

To use PUT it looks like you'd need to specify the dupCheck parameter:

$infusionsoft->contacts()->create($contact, true) //dupCheck = true
kevindecapite commented 7 years ago

@MikeChristianson Thanks for the assist but that still didn't work for me. You're right in that the IS docs I linked to indicate that I need to PUT and not POST, but I couldn't figure out how to do that with the SDK (using true still ran a POST).

mfairch commented 7 years ago

@kevindecapite run a composer update. I just published the release that adds the duplicate check option.

kevindecapite commented 7 years ago

@micfai Very nice! I updated to 1.3.5 and was able to call $is->contacts()->create($contact, true) successfully. (And $contact included the duplicate_option => 'Email' key as well.) Thank you for the responsiveness to this.

Also, should I be concerned that composer alerts with the following when installing & updating this package?

Package egeloen/http-adapter is abandoned, you should avoid using it. Use php-http/httplug instead.
jacobrossdev commented 6 years ago

And here we are going into the second quarter of 2018, and the docs still haven't been updated.

Darios1 commented 6 years ago

Good Day, I would like to know how to add information to custom fields from my php application? Thank you

MikeChristianson commented 6 years ago

@Darios1 Thanks for your interest, we invite you to post on the official Infusionsoft Community forum.

roymckenzie commented 6 years ago

Y'all need better documentation on what objects are supposed to look like, good grief.

chayan89 commented 3 years ago

Hi, First of all great environment for Rest APIs. Right now i'm facing an issue is with findContactByEmail. I want to update contact custom_fields. Class '\Infusionsoft\Api\Rest\ContactService' got this error when I try to get contact using sdk. Please help me on this. Thanks in advance.