问题
Updated to show new code / new error @ 10/12/2020 11:30
I need to copy invoices from my website to xero using their API. I managed to get this working using the OAuth 1 but now need to update to OAuth2.0.
I've installed xero-php-oauth2-starter which connects and works fine with the examples. e.g. if I click the example "Create one Contact" link, the demo contact is created in xero.
As such, I know the link is working.
I've been looking around trying to find an example of how to create an invoice using the API but am finding it incredibly hard to work out.
Below is what I currently have in my xero api folder:
<?php
ini_set('display_errors', 'On');
require __DIR__ . '/vendor/autoload.php';
require_once('storage.php');
// Use this class to deserialize error caught
use XeroAPI\XeroPHP\AccountingObjectSerializer;
// Storage Classe uses sessions for storing token > extend to your DB of choice
$storage = new StorageClass();
$xeroTenantId = (string)$storage->getSession()['tenant_id'];
if ($storage->getHasExpired()) {
$provider = new \League\OAuth2\Client\Provider\GenericProvider([
'clientId' => 'REMOVED',
'clientSecret' => 'REMOVED',
'redirectUri' => 'https://REMOVED/callback.php',
'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' => $storage->getRefreshToken()
]);
// Save my token, expiration and refresh token
$storage->setToken(
$newAccessToken->getToken(),
$newAccessToken->getExpires(),
$xeroTenantId,
$newAccessToken->getRefreshToken(),
$newAccessToken->getValues()["id_token"] );
}
$config = XeroAPI\XeroPHP\Configuration::getDefaultConfiguration()->setAccessToken( (string)$storage->getSession()['token'] );
$apiInstance = new XeroAPI\XeroPHP\Api\AccountingApi(
new GuzzleHttp\Client(),
$config
);
$invoices = '{"invoices":[{
"type":"Invoice.TypeEnum.ACCREC",
"contact":{"contactID":"97af3783-0f32-42be-a06d-8c586c8aa8ec"},
"lineItems":[{
"description":"Acme Tires",
"quantity":2,
"unitAmount":20,
"accountCode":"000",
"taxType":"NONE",
"lineAmount":40
}],
"date":"2019-03-11",
"dueDate":"2018-12-10",
"reference":"Website Design",
"status":"Invoice.StatusEnum.DRAFT"
}]}';
foreach ($import as $invoice) {
//create contact
$xcontact = new XeroAPI\XeroPHP\Models\Accounting\Contact();
$xcontact->setName($contactname);
//create line item
$lineItems = array();
foreach ($invoice['invoiceLineItems'] as $line) {
$newLine = new XeroAPI\XeroPHP\Models\Accounting\LineItem();
$newLine->setDescription($invoice['message']."\n".$line['description']);
$newLine->setQuantity($line['quantity']);
$newLine->setUnitAmount($line['amount']);
$newLine->setAccountCode('200');
$lineItems[] = $newLine;
}
$xinvoice = new XeroAPI\XeroPHP\Models\Accounting\Invoice();
$xinvoice->setType("ACCREC");
$xinvoice->setStatus("AUTHORISED");
$xinvoice->setDate($invoice['invoiceDate']);
$xinvoice->setDueDate($invoice['dueDate']);
$xinvoice->setLineAmountTypes("NoTax");
$xinvoice->setContact($xcontact);
$xinvoice->setLineItems($lineItems);
$xinvoice->setInvoiceNumber("INV-0".$invoice['invoiceNumber']);
$xinvoices['Invoices'][] = $xinvoice;
}
$apiResponse = $apiInstance->createInvoices($xeroTenantId,$xinvoices);
$summarize_errors = false; // bool | If false return 200 OK and mix of successfully created objects and any with validation errors
$unitdp = 4; // int | e.g. unitdp=4 – (Unit Decimal Places) You can opt in to use four decimal places for unit amounts
?>
gives
Fatal error: Uncaught InvalidArgumentException: Missing the required parameter $invoices when calling createInvoices
which I can't seem to find any details on.
I've created the product "Acme Tires" in xero just incase that was causing the issue (I remember in auth 1 that it wouldn't work if the product wasn't listed first).
Any help would be greatly appreciated.
回答1:
With the new PHP SDK you need to pass an array of Invoice objects.
The following creates Invoices based on the contents of a pre-existing array ($import).
You'll see that you need to place the line items into an array, and then insert that into the invoice object. The invoice itself is then inserted into an array which is passed to the API.
<?php
foreach ($import as $invoice) {
//create contact
$xcontact = new XeroAPI\XeroPHP\Models\Accounting\Contact();
$xcontact->setName($contactname);
//create line item
$lineItems = array();
foreach ($invoice['invoiceLineItems'] as $line) {
$newLine = new XeroAPI\XeroPHP\Models\Accounting\LineItem();
$newLine->setDescription($invoice['message']."\n".$line['description']);
$newLine->setQuantity($line['quantity']);
$newLine->setUnitAmount($line['amount']);
$newLine->setAccountCode('200');
$lineItems[] = $newLine;
}
$xinvoice = new XeroAPI\XeroPHP\Models\Accounting\Invoice();
$xinvoice->setType("ACCREC");
$xinvoice->setStatus("AUTHORISED");
$xinvoice->setDate($invoice['invoiceDate']);
$xinvoice->setDueDate($invoice['dueDate']);
$xinvoice->setLineAmountTypes("NoTax");
$xinvoice->setContact($xcontact);
$xinvoice->setLineItems($lineItems);
$xinvoice->setInvoiceNumber("INV-0".$invoice['invoiceNumber']);
$xinvoices['Invoices'][] = $xinvoice;
}
$apiResponse = $apiInstance->createInvoices($xeroTenantId,$xinvoices);
?>
Update:
$apiInstance will have been created earlier eg.
$apiInstance = new XeroAPI\XeroPHP\Api\AccountingApi(
new GuzzleHttp\Client(),
$config
);
$import was an array in the original script that contained the raw data I wanted to import. You would need to replace this with your own data.
Update 2:
To use your original data, you'll need to remove the
foreach ($import as $invoice) {
loop. and replace the references to $invoice with your own data.
For example:
//create contact
$xcontact = new XeroAPI\XeroPHP\Models\Accounting\Contact();
$xcontact->setName($contactname);
Would become:
//create contact
$xcontact = new XeroAPI\XeroPHP\Models\Accounting\Contact();
$xcontact->setContactId("97af3783-0f32-42be-a06d-8c586c8aa8ec");
来源:https://stackoverflow.com/questions/65232512/xero-api-struggling-to-add-a-basic-invoice