Order line items are non-subscription based items created by an order, representing transactional charges such as one-time fees, physical goods, or professional service charges that are not sold as subscription services. With the Order Line Items feature enabled, customers now have the ability to launch non-subscription and unified monetization business models in Zuora, in addition to subscription business models. And you can integrate Fulfillment with Zuora, e.g. add fulfillments to existing order line items that have been activated.
By following this tutorial, you will learn how to build a classic basic fulfillment integration leveraging the Zuora REST API.
-
You must have the following features enabled on your tenant:
- Orders. If this feature is enabled in your tenant, you can see Orders under Customers in the left navigation panel in the Zuora UI. To obtain access to this feature, submit a request at Zuora Global Support.
- Invoice Settlement. If this feature is enabled in your tenant, you can see Credit and Debit Memos under Billing in the left navigation panel in the Zuora UI. To obtain access to this feature, submit a request at Zuora Global Support.
-
You must have an OAuth client created. See Create an OAuth Client for a User for more information.
You must create an OAuth token that will be used for API requests with a client ID and client secret created on UI.
API Request:
curl --location --request POST 'https://rest.zuora.com/oauth/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id={{your client ID}}' \
--data-urlencode 'client_secret={{your client secret}}’ \
--data-urlencode 'grant_type=client_credentials'
Get the access_token and replace {{bearer_token}}
in this doc with the returned value.
API Request:
curl --location --request POST 'https://rest.zuora.com/v1/accounts' \
--header 'Authorization: Bearer {{bearer_token}}' \
--header 'Content-Type: application/json' \
--data-raw '{
"additionalEmailAddresses": [
"contact1@example.com",
"contact2@example.com"
],
"autoPay": false,
"billCycleDay": 0,
"billToContact": {
"address1": "1051 E Hillsdale Blvd",
"city": "Foster City",
"country": "United States",
"firstName": "John",
"lastName": "Smith",
"state": "CA",
"workEmail": "smith@example.com",
"zipCode": "94404"
},
"currency": "USD",
"name": "Zuora Fulfillment Account",
"notes": "This account is for demo purposes."
}'
The response body looks like
{
"success": true,
"accountId": "8a90f50888092efb0188097c364511d2",
"accountNumber": "A00082003",
"billToContactId": "8a90f50888092efb0188097c367311d3",
"soldToContactId": "8a90f50888092efb0188097c368311d5"
}
In this step, the end user bought 10 physical goods with each unit priced at $30. An invoice is generated automatically with the total amount of $300.
Need to replace {{accountNumber}}
with the returned value from step2. In this tutorial, it is A00082003
.
API Request:
curl --location --request POST 'https://rest.zuora.com/v1/orders?returnIds=true' \
--header 'Authorization: Bearer {{bearer_token}}' \
--header 'Content-Type: application/json' \
--data-raw '{
"orderDate": "2021-01-01",
"existingAccountNumber": "{{accountNumber}}",
"orderLineItems": [
{
"itemName": "physical good _fbff79b9",
"itemType": "Product",
"description": "physical good to sell",
"purchaseOrderNumber": "PO-12345678",
"productCode": "P-0000001",
"quantity": 10,
"amountPerUnit": 30,
"listPricePerUnit": 30,
"UOM": "Each",
"transactionDate": "2021-01-01",
"billTargetDate": "2021-01-01",
"itemState": "SentToBilling"
}
],
"processingOptions": {
"runBilling": true,
"billingOptions": {
"targetDate": "2021-01-01",
"documentDate": "2021-01-01"
}
}
}'
The response body looks like
{
"success": true,
"orderNumber": "O-00170371",
"accountNumber": "A00082003",
"status": "Completed",
"orderId": "8a90f50888092efb0188097c379511d8",
"accountId": "8a90f50888092efb0188097c364511d2",
"orderLineItems": [
{
"itemNumber": "1",
"id": "8a90f50888092efb0188097c37db11da"
}
],
"invoiceNumbers": [
"INV10208-00088595"
],
"invoiceIds": [
"8a90f50888092efb0188097c382a11dd"
]
}
In this step, the end user returned 2 physical goods. Set the billing rule to "TriggerAsFulfillmentOccurs" so no credit memo will be generated immediately until the fulfillment has been completed.
More details can be found here
Need to replace
{{accountNumber}}
with the returned value from step2, in this tutorial it isA00082003
.{{orderNumber}}
with the returned value from step3, in this tutorial it isO-00170371
.{{itemNumber}}
with the returned value from step3, in this tutorial it is1
in orderLineItems.itemNumber.
API Request:
curl --location --request POST 'https://rest.zuora.com/v1/orders?returnIds=true' \
--header 'Authorization: Bearer {{bearer_token}}' \
--header 'Content-Type: application/json' \
--data-raw '{
"orderDate": "2021-02-01",
"category": "Return",
"reasonCode": "No Longer Needed",
"existingAccountNumber": "{{accountNumber}}",
"description": "return device",
"processingOptions": {
"runBilling": false
},
"orderLineItems": [
{
"itemName": "return device detail",
"itemState": "Booked",
"itemNumber": "1",
"itemCategory": "Return",
"originalOrderNumber": "{{orderNumber}}",
"originalOrderLineItemNumber": "{{itemNumber}}",
"description": "With Dual Stereo Microphones.",
"quantity": 2,
"billTargetDate": "2021-02-01",
"transactionDate": "2021-02-01",
"transactionStartDate": "2021-02-01",
"transactionEndDate": "2021-02-01",
"billingRule": "TriggerAsFulfillmentOccurs"
}
]
}'
The response body looks like
{
"success": true,
"orderNumber": "RMA-00170372",
"accountNumber": "A00082003",
"status": "Completed",
"orderId": "8a90f50888092efb0188097c3a1511e6",
"accountId": "8a90f50888092efb0188097c364511d2",
"orderLineItems": [
{
"itemNumber": "1",
"id": "8a90f50888092efb0188097c3a7c11e8"
}
]
}
In this step, the two fulfillments with state "SentToBilling" are added to the returned order line item.
You can set different state for the fulfillment. The state of the fulfillment are Executing, Booked, Sent To Billing, Complete, and Canceled. The default value is Executing.
- If the fulfillment is in the Executing state, you can update the fulfillment.
- If the fulfillment is specified to other states, you can add fulfillment items to the fulfillment. To generate billing documents, you need to specify the fulfillment to the Sent To Billing state and set the Billing Target Date field.
More details can be found here
Need to replace {{returnOrderLineItemId}}
with the returned value from step3, in this tutorial it is 8a90f50888092efb0188097c3a7c11e8
in orderLineItems[0].id.
API Request:
curl --location --request POST 'https://rest.zuora.com/v1/fulfillments' \
--header 'Authorization: Bearer {{bearer_token}}' \
--header 'Content-Type: application/json' \
--data-raw '{
"fulfillments": [
{
"fulfillmentDate": "2021-02-01",
"quantity": 2,
"state": "SentToBilling",
"billTargetDate": "2021-02-01",
"orderLineItemId": "{{returnOrderLineItemId}}",
"fulfillmentType": "Return"
}
],
"processingOptions": {
"runBilling": true,
"billingOptions": {
"documentDate": "2021-02-01",
"targetDate": "2021-02-01"
}
}
}'
The response body looks like
{
"fulfillments": [
{
"id": "8a90876c880945ab0188097c3b790afa",
"fulfillmentNumber": "F-00007053",
"fulfillmentItems": []
}
],
"invoiceNumbers": null,
"creditMemoNumbers": [
"CM10208-00007761"
],
"paymentNumber": null,
"paidAmount": null,
"success": true
}
In this step, we verify the generated credit memo for the account with total amount $60, $30 for each of the 2 returned devices.
Need to replace {{accountNumber}}
with the returned value from step2, in this tutorial it is A00082003
.
API Request:
curl --location --request GET 'https://rest.zuora.com/v1/creditmemos?accountNumber={{accountNumber}}' \
--header 'Authorization: Bearer {{bearer_token}}' \
--header 'Content-Type: application/json'
The response body looks like
{
"creditmemos": [
{
"id": "8a90876c880945ab0188097c3bc90afd",
"number": "CM10208-00007761",
"accountId": "8a90f50888092efb0188097c364511d2",
"accountNumber": "A00082003",
"currency": "USD",
"creditMemoDate": "2021-02-01",
"targetDate": "2021-02-01",
"postedById": "2c92c8fe78a9a0740178a9b0b06c007a",
"postedOn": "2023-05-11 14:25:16",
"status": "Posted",
"amount": 60,
"taxAmount": 0,
"totalTaxExemptAmount": 0,
"unappliedAmount": 60,
"refundAmount": 0,
"appliedAmount": 0,
"comment": null,
"source": "API",
"sourceId": null,
"referredInvoiceId": null,
"reasonCode": "Return Order",
"createdDate": "2023-05-11 14:25:16",
"createdById": "2c92c8fe78a9a0740178a9b0b06c007a",
"updatedDate": "2023-05-11 14:25:16",
"updatedById": "2c92c8fe78a9a0740178a9b0b06c007a",
"cancelledOn": null,
"cancelledById": null,
"latestPDFFileId": null,
"Origin__NS": null,
"IntegrationId__NS": null,
"IntegrationStatus__NS": null,
"Transaction__NS": null,
"Test__c": null,
"SyncDate__NS": null,
"transferredToAccounting": "No",
"excludeFromAutoApplyRules": false,
"autoApplyUponPosting": false,
"reversed": false,
"taxStatus": "Complete",
"sourceType": "Order",
"taxMessage": null,
"billToContactId": null,
"billToContactSnapshotId": null,
"sequenceSetId": null
}
],
"success": true
}