You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP调用Xero API创建发票:客户地址与联系电话未同步问题

Xero API创建发票:客户地址与联系电话同步失败排查

使用Xero API创建发票时,客户已成功创建、发票邮件正常发送,但客户地址和联系电话无法同步到Xero平台,其余功能正常,代码执行无报错。以下是相关代码:

$xeroTenantId = $this->Storage_model->getSession()['tenant_id'];

if ($this->Storage_model->getHasExpired()) {
    $provider = new \League\OAuth2\Client\Provider\GenericProvider([
        'clientId'                => 'xxxxxxxxxx',
        'clientSecret'            => 'xxxxxxxxxx',
        'redirectUri'             => 'xxxxxxxxxx.co.uk/callback',
        'urlAuthorize'            => 'https://login.xero.com/identity/connect/authorize',
        'urlAccessToken'          => 'https://identity.xero.com/connect/token',
        'urlResourceOwnerDetails' => 'https://api.xero.com/api.xro/2.0/Organisation'
    ]);

    $newAccessToken = $provider->getAccessToken('refresh_token', [
        'refresh_token' => $this->Storage_model->getRefreshToken()
    ]);

    // Save my token, expiration and refresh token
    $this->Storage_model->setToken(
        $newAccessToken->getToken(),
        $newAccessToken->getExpires(),
        $xeroTenantId,
        $newAccessToken->getRefreshToken(),
        $newAccessToken->getValues()["id_token"] );
}

$config = XeroAPI\XeroPHP\Configuration::getDefaultConfiguration()->setAccessToken( (string)$this->Storage_model->getSession()['token'] );

$apiInstance = new XeroAPI\XeroPHP\Api\AccountingApi(
    new GuzzleHttp\Client(),
    $config
);

$idempotencyKey = rand()."IDDEMP";
$summarizeErrors = true;
$unitdp = 4;
$dateValue = new DateTime('now');
$dueDateValue = new DateTime('now');
$dueDateValue->modify("+1 month");

//test data
$contact['firstName'] = "Fred";
$contact['surname'] = "Blogs";
$contact['email'] = "Fred@test.co.uk";
$contact['contactNumber'] = "079671111111";
$contact['billingAddress1'] = "Address 1";
$contact['billingAddress2'] = "Address 2";
$contact['billingAddress3'] = "Address 3";
$contact['billingAddress4'] = "Address 4";
$contact['billingPostcode'] = "DT94XX";

//[add a contact]
$setFirstName = $contact['firstName'];
$setLastName = $contact['surname'];
$setEmailAddress = $contact['email'];
$setContactNumber = $contact['contactNumber'];

$address = array(array(
    'AddressType' => 'STREET',
    'AddressLine1' => $contact['billingAddress1'],
    'AddressLine2' => $contact['billingAddress2'],
    'City' => $contact['billingAddress3'],
    'State' => $contact['billingAddress4'],
    'PostalCode' => $contact['billingPostcode']
));

$contact = new XeroAPI\XeroPHP\Models\Accounting\Contact;
$contact->setName($setFirstName." ".$setLastName)
    ->setFirstName($setFirstName)
    ->setLastName($setLastName)
    ->setEmailAddress($setEmailAddress)
    ->setContactNumber($setContactNumber)
    ->setAddresses($address);

$arr_contacts = [];
array_push($arr_contacts, $contact);

$contacts = new XeroAPI\XeroPHP\Models\Accounting\Contacts;
$contacts->setContacts($arr_contacts);

$apiResponse = $apiInstance->createContacts($xeroTenantId,$contacts);
//[/add a contact]
$lineItems = array();

$lineItemTracking = new XeroAPI\XeroPHP\Models\Accounting\LineItemTracking;
$lineItemTracking->setTrackingCategoryID('00000000-0000-0000-0000-000000000000');
$lineItemTracking->setTrackingOptionID('00000000-0000-0000-0000-000000000000');
$lineItemTrackings = [];
array_push($lineItemTrackings, $lineItemTracking);

foreach($order as $value) {
    $lineItem = new XeroAPI\XeroPHP\Models\Accounting\LineItem;
    $lineItem->setDescription($value['rowTitle']);
    $lineItem->setQuantity($value['rowQty']);
    $lineItem->setUnitAmount($value['rowPrice']);
    $lineItem->setAccountCode('200');
    $lineItem->setTracking($lineItemTrackings);
    array_push($lineItems, $lineItem);
}

$invoice = new XeroAPI\XeroPHP\Models\Accounting\Invoice;
$invoice->setType(XeroAPI\XeroPHP\Models\Accounting\Invoice::TYPE_ACCREC);
$invoice->setContact($contact);
$invoice->setDate($dateValue);
$invoice->setDueDate($dueDateValue);
$invoice->setLineItems($lineItems);
$invoice->setReference($contact['reference']);
$invoice->setStatus('AUTHORISED');  //$invoice->setStatus(XeroAPI\XeroPHP\Models\Accounting\Invoice::STATUS_DRAFT);

$invoices = new XeroAPI\XeroPHP\Models\Accounting\Invoices;
$arr_invoices = [];
array_push($arr_invoices, $invoice);
$invoices->setInvoices($arr_invoices);

try {
    $result = $apiInstance->createInvoices($xeroTenantId, $invoices, $idempotencyKey, $summarizeErrors, $unitdp);
    $data = XeroAPI\XeroPHP\AccountingObjectSerializer::sanitizeForSerialization($result);

    //email invoice
    $requestEmpty = new XeroAPI\XeroPHP\Models\Accounting\RequestEmpty;

    try {
        $idempotencyKey = rand()."IDDEMP";  
        $apiInstance->emailInvoice($xeroTenantId, $invocieID = $data->Invoices[0]->InvoiceID, $requestEmpty, $idempotencyKey);
    } catch (Exception $e) {
        echo 'Exception when calling AccountingApi->emailInvoice: ', $e->getMessage(), PHP_EOL;
    }

} catch (Exception $e) {
    echo 'Exception when calling AccountingApi->createInvoices: ', $e->getMessage(), PHP_EOL;
}

echo "complete";

排查关键点

1. 联系电话字段使用错误

Xero API的Contact模型中,联系电话不能直接用setContactNumber(),需要创建Phone对象并指定电话类型:

// 替换原setContactNumber的代码
$phone = new XeroAPI\XeroPHP\Models\Accounting\Phone();
$phone->setPhoneType(\XeroAPI\XeroPHP\Models\Accounting\Phone::PHONE_TYPE_DEFAULT)
      ->setPhoneNumber($setContactNumber);
$contact->setPhones([$phone]);

2. 地址字段映射与类型问题

  • 你当前将billingAddress3赋值给City、billingAddress4赋值给State,请确认地址数据是否匹配这些字段(比如billingAddress3确实是城市名称),否则会导致Xero无法解析地址。
  • 如果需要标记该地址为账单地址,需确保地址的AddressType设置正确,同时可在Contact对象中指定setBillToAddress($address)(根据SDK版本调整,部分版本需通过Addresses数组的属性标记)。

3. 未使用Xero返回的Contact对象关联发票

调用createContacts后,Xero会返回包含完整信息的Contact对象,你直接使用本地创建的对象关联发票,可能导致部分字段未被同步。应替换为返回的对象:

$apiResponse = $apiInstance->createContacts($xeroTenantId,$contacts);
// 获取Xero返回的完整Contact对象
$createdContact = $apiResponse->getContacts()[0];
// 发票中使用该对象
$invoice->setContact($createdContact);

4. 发票Reference字段错误

代码中$invoice->setReference($contact['reference']);的$contact已经被重新赋值为Contact对象,原数组的reference字段不存在,会传入空值,建议删除该行或设置有效的参考值。

内容的提问来源于stack exchange,提问作者user3507560

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 05:05:21