> This page is for Developer.

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.bigcommerce.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.bigcommerce.com/_mcp/server.

# Lab - Postman Order and Payment Workflow

Plan: B2B Developer

Lesson 16 of 18 · 45 min

## Introduction

In the previous lab, you built an API workflow to submit a quote from a storefront user and then initiate a checkout with that quote, ultimately completing an order. Now you'll complete a common purchasing workflow with tasks to view a company's orders, initiate a checkout for invoice payment, and inspect existing payments.

### Prerequisites

* A BigCommerce [sandbox store](/developer/docs/overview/sandboxes) or [trial store](https://www.bigcommerce.com/essentials/), or a full production store
* B2B Edition enabled in your store
* [Postman](https://www.postman.com/) or a similar API client
* An existing Postman environment, collection, and headers preset as configured in previous labs
* Minimal invoice settings as described below

### In this lab, you will:

* Create API requests to handle viewing orders, viewing and initiating payment on invoices, and viewing payments
* Implement Postman scripts to automate values used in API requests
* Practice sending quote-related requests from the perspective of users with different roles/permissions

### Required Invoice Settings

The lab will involve logging into your storefront as a company user and placing an order to pay an invoice. The following minimal settings are required in the B2B Edition section of your control panel:

* In Settings > General, "Invoices" must be checked under "Feature management."
* In Settings > Invoice, at least one valid option must be enabled under "Payment methods for paying invoices."

If you don't have a valid payment method option available in the invoice settings, make sure you have enabled a method other than Check/Purchase Order in the main Settings > Payments section of the BigCommerce control panel.

## Postman Recap

As in the previous lab, you will need to authenticate using the "Admin Login" request, powered by valid credentials in the `admin_email` and `admin_password` variables on your collection.

## Step 1: Get Orders

If you haven't already placed an order from your previously created quote, make sure you have at least one placed order using the Purchase Order payment method under the company matching your Admin user.

1. If necessary, **re-run** the "Junior Buyer Login" request to regenerate your GraphQL authentication token and other details.
2. **Create** a new HTTP request and **save** it to your collection with the name "Get Orders."
3. **Set** the same configuration that was used for the previous requests, including the HTTP method, URL, Auth Type, Headers (using your previously created "GraphQL" preset), and Body type.
4. **Enter** the following query.

```graphql showLineNumbers={false}
query GetOrders(
    $limit: Int,
    $after: String,
    $orderBy: String
) {
    allOrders(
        first: $limit,
        after: $after,
        orderBy: $orderBy
    ) {
        pageInfo {
            hasNextPage
            hasPreviousPage
            startCursor
            endCursor
        }
        totalCount
        edges {
            node {
                id
                bcOrderId
                createdAt
                userId
                bcCustomerId
                companyId {
                    id
                    companyName
                }
                totalIncTax
                currencyCode
                items
                poNumber
                status
                shippingAddress
                shipments
                products
                customer
                companyName
                firstName
                lastName
            }
        }
    }
}
```

5. **Enter** the following in the GraphQL Variables panel.

```json showLineNumbers={false}
{
    "limit": 5,
    "after": null,
    "orderBy": "-createdAt"
}
```

Like previous requests, the arguments and GraphQL variables set up in this request can easily facilitate pagination with a sufficient number of orders.

6. In the Scripts tab, **enter** the following "Post-response" code.

```js showLineNumbers={false}
pm.test('Response is not an error', () => {
    pm.response.to.not.be.error;
    pm.response.to.not.have.jsonBody("errors");
});
pm.test('Response is JSON with data', () => {
    pm.response.to.have.jsonBody("data");
});

const result = pm.response.json().data?.allOrders;
pm.test('Total count includes at least one order', () => {
    pm.expect(
        result?.totalCount
    ).to.be.greaterThan(0);
});

let firstOrder = null;
if (Array.isArray(result?.edges)) {
    firstOrder = result.edges[0]?.node;
}
pm.test('Response includes at least one order with an ID', () => {
    pm.expect(
        firstOrder?.id
    ).to.be.a('string');
});
```

7. **Send** the request and **verify** that all tests succeed.

Observe the order details included in the response, which will automatically be filtered by the company matching the current authentication. Since the results set is sorted descending by date, your recent order from quote should be included in the first page of results.

## Step 2: Get Ordered Products

Now let's craft a request that will give your storefront user aggregate information on the products that have been ordered for the company.

1. **Create** a new HTTP request and **save** it to your collection with the name "Get Ordered Products."
2. **Set** the same configuration that was used for the previous requests, including the HTTP method, URL, Auth Type, Headers (using your previously created "GraphQL" preset), and Body type.
3. **Enter** the following query.

```graphql showLineNumbers={false}
query GetOrderedProducts(
    $limit: Int,
    $after: String,
    $orderBy: String
) {
    orderedProducts(
        first: $limit,
        after: $after,
        orderBy: $orderBy
    ) {
        pageInfo {
            hasNextPage
            hasPreviousPage
            startCursor
            endCursor
        }
        totalCount
        edges {
            node {
                id
                productId
                productName
                sku
                variantSku
                variantId
                optionList
                optionSelections
                orderedTimes
                firstOrderedAt
                lastOrderedAt
                imageUrl
                productUrl
            }
        }
    }
}
```

4. **Enter** the following in the GraphQL Variables panel.

```json showLineNumbers={false}
{
    "limit": 5,
    "after": null,
    "orderBy": "-lastOrderedAt"
}
```

You're configuring your ordered products list to be sorted by most recently ordered.

5. In the Scripts tab, **enter** the following "Post-response" code.

```js showLineNumbers={false}
pm.test('Response is not an error', () => {
    pm.response.to.not.be.error;
    pm.response.to.not.have.jsonBody("errors");
});
pm.test('Response is JSON with data', () => {
    pm.response.to.have.jsonBody("data");
});

const result = pm.response.json().data?.orderedProducts;
pm.test('Total count includes at least one product', () => {
    pm.expect(
        result?.totalCount
    ).to.be.greaterThan(0);
});

let firstProduct = null;
if (Array.isArray(result?.edges)) {
    firstProduct = result.edges[0]?.node;
}
pm.test('Response includes at least one product with an ID', () => {
    pm.expect(
        firstProduct?.id
    ).to.be.a('string');
});
```

6. **Send** the request and **verify** that all tests succeed.

## Step 3: Get Open Invoices

Let's craft a request to retrieve all of the current company's invoices that are not fully paid.

The creation of invoice is done on the back-end, so before you can fetch any relevant results, you'll need to take on the role of a sales rep first, to issue the invoice.

1. **Log in** to your BigCommerce control panel and **navigate** to the B2B Edition admin.
2. In Invoice Management > Orders, **find** the order you placed from your quote (or any other un-invoiced order paid with Purchase Order) and **Create Invoice** from the order's action menu.
3. **Create** a new HTTP request and **save** it to your collection with the name "Get Open Invoices."
4. **Set** the same configuration that was used for the previous requests, including the HTTP method, URL, Auth Type, Headers (using your previously created "GraphQL" preset), and Body type.
5. **Enter** the following query.

```graphql showLineNumbers={false}
query GetInvoices(
    $limit: Int,
    $after: String,
    $orderBy: String,
    $status: [String]
) {
    invoices(
        first: $limit,
        after: $after,
        orderBy: $orderBy,
        status: $status
    ) {
        pageInfo {
            hasNextPage
            hasPreviousPage
            startCursor
            endCursor
        }
        totalCount
        edges {
            node {
                id
                invoiceNumber
                createdAt
                customerId
                dueDate
                status
                orderNumber
                purchaseOrderNumber
                details
                pendingPaymentCount
                originalBalance {
                    code
                    value
                }
                openBalance {
                    code
                    value
                }
            }
        }
    }
}
```

6. **Enter** the following in the GraphQL Variables panel.

```json showLineNumbers={false}
{
    "limit": 5,
    "after": null,
    "orderBy": "-createdAt",
    "status": ["0","1"]
}
```

You're not only sorting the invoice results by most recently created, but also filtering on the open (0) and partially paid (1) statuses.

7. In the Scripts tab, **enter** the following "Post-response" code.

```js showLineNumbers={false}
pm.test('Response is not an error', () => {
    pm.response.to.not.be.error;
    pm.response.to.not.have.jsonBody("errors");
});
pm.test('Response is JSON with data', () => {
    pm.response.to.have.jsonBody("data");
});

const result = pm.response.json().data?.invoices;
pm.test('Total count includes at least one invoice', () => {
    pm.expect(
        result?.totalCount
    ).to.be.greaterThan(0);
});

let firstInvoice = null;
if (Array.isArray(result?.edges)) {
    firstInvoice = result.edges[0]?.node;
}
pm.test('Response includes at least one invoice with an ID and open balance', () => {
    pm.expect(
        firstInvoice?.id
    ).to.be.a('string');
    pm.expect(
        firstInvoice?.openBalance?.value
    ).to.be.a('string');
});

pm.collectionVariables.unset('invoice_id');
pm.collectionVariables.unset('invoice_amount');

pm.collectionVariables.set('invoice_id', firstInvoice?.id);
pm.collectionVariables.set('invoice_amount', firstInvoice?.openBalance?.value);
```

In addition to tests, this script is capturing both the ID and the open balance of the first returned invoice in collection variables for use in later requests; these values will be needed once you're ready to initiate payment on an invoice.

You're ready to run the request, but remember that your authentication credentials should currently match your Junior Buyer user, and Junior Buyers do not have permission to view invoices.

8. **Send** the request and **verify** that you receive a "Permission denied" error.
9. **Re-run** the "Admin Login" request to generate new authentication credentials for your Admin user, who should be able to fetch invoice details.
10. **Re-run** the "Get Open Invoices" request, **verify** that all tests succeed, and **verify** that `invoice_id` and `invoice_amount` variables have been set on your collection.

If the first returned invoice in your results is not the one you wish to use in the steps for downloading an invoice PDF and initiating an invoice payment, feel free to manually change the current values of `invoice_id` and `invoice_amount` on your collection.

## Step 4: Export Invoice CSV and PDF

In addition to viewing their invoices within your storefront application, company users will commonly with to export them in other formats. In this step, you'll practice generating a CSV of all open invoices and the PDF for your most recent open invoice.

1. **Create** a new HTTP request and **save** it to your collection with the name "Export Open Invoices."
2. **Set** the same configuration that was used for the previous requests, including the HTTP method, URL, Auth Type, Headers (using your previously created "GraphQL" preset), and Body type.
3. **Enter** the following query.

```graphql showLineNumbers={false}
mutation InvoicesExport(
    $status: [Int]
) {
    invoicesExport(
        invoiceFilterData: {
            status: $status
        }
    ) {
        url
    }
}
```

4. **Enter** the following in the GraphQL Variables panel.

```json showLineNumbers={false}
{
    "status": [0,1]
}
```

Note the expression of the status filters as integers rather than strings in this case.

5. In the Scripts tab, **enter** the following "Post-response" code.

```js showLineNumbers={false}
pm.test('Response is not an error', () => {
    pm.response.to.not.be.error;
    pm.response.to.not.have.jsonBody("errors");
});
pm.test('Response is JSON with data', () => {
    pm.response.to.have.jsonBody("data");
});

const url = pm.response.json().data?.invoicesExport?.url;
pm.test(`Response includes order with ID`, () => {
    pm.expect(
        url
    ).to.be.a('string');
});
```

6. **Send** the request and **verify** that all tests succeed.
7. **Copy** the URL from the response and **enter** it in a browser to verify that a downloaded CSV is received and contains the appropriate details.

Next you'll generate an invoice PDF using the "most recent invoice" information you previously captured.

8. **Create** a new HTTP request and **save** it to your collection with the name "Get Invoice PDF."
9. **Set** the same configuration that was used for the previous requests, including the HTTP method, URL, Auth Type, Headers (using your previously created "GraphQL" preset), and Body type.
10. **Enter** the following query.

```graphql showLineNumbers={false}
mutation GetInvoicePdf(
    $invoiceId: Int!
) {
    invoicePdf(
        invoiceId: $invoiceId,
        isPayNow: true
    ) {
        url
    }
}
```

11. **Enter** the following in the GraphQL Variables panel.

```json showLineNumbers={false}
{
    "invoiceId": {{invoice_id}}
}
```

12. In the Scripts tab, **enter** the following "Post-response" code.

```js showLineNumbers={false}
pm.test('Response is not an error', () => {
    pm.response.to.not.be.error;
    pm.response.to.not.have.jsonBody("errors");
});
pm.test('Response is JSON with data', () => {
    pm.response.to.have.jsonBody("data");
});

const url = pm.response.json().data?.invoicePdf?.url;
pm.test(`Response includes PDF URL`, () => {
    pm.expect(
        url
    ).to.be.a('string');
});
```

13. **Send** the request and **verify** that all tests succeed.
14. **Copy** the URL from the response and **enter** it in a browser to verify that a downloaded PDF is received and contains the appropriate details.

## Step 5: Initiate Invoice Checkout

Similar to the process of initiating a checkout from an order, your final invoice request will generate a URL to allow a storefront user to initiate payment of a specific invoice via the BigCommerce checkout.

1. **Create** a new HTTP request and **save** it to your collection with the name "Get Invoice Checkout."
2. **Set** the same configuration that was used for the previous requests, including the HTTP method, URL, Auth Type, Headers (using your previously created "GraphQL" preset), and Body type.
3. **Enter** the following query.

```graphql showLineNumbers={false}
mutation GetInvoiceCheckout(
    $invoiceId: Int!,
    $amount: String!,
    $currency: String!
) {
    invoiceCreateBcCart(
        bcCartData: {
            lineItems: [
                {
                    invoiceId: $invoiceId,
                    amount: $amount
                }
            ],
            currency: $currency,
            details: {}
        }
    ) {
        result {
            checkoutUrl
            cartId
        }
    }
}
```

4. **Enter** the following in the GraphQL Variables panel.

```json showLineNumbers={false}
{
    "invoiceId": {{invoice_id}},
    "amount": "{{invoice_amount}}",
    "currency": "{{currency_code}}"
}
```

Again, you can change the value of the `invoice_id` variable on your collection to initiate a payment for a different invoice than the most recent.

You can also try changing the `invoice_amount` collection variable to a value less than the full open balance of the invoice, in order to test out a partial payment. (Don't forget to save the collection.)

5. In the Scripts tab, **enter** the following "Post-response" code.

```js showLineNumbers={false}
pm.test('Response is not an error', () => {
    pm.response.to.not.be.error;
    pm.response.to.not.have.jsonBody("errors");
});
pm.test('Response is JSON with data', () => {
    pm.response.to.have.jsonBody("data");
});

const url = pm.response.json().data?.invoiceCreateBcCart?.result?.checkoutUrl;
pm.test(`Response includes checkout URL`, () => {
    pm.expect(
        url
    ).to.be.a('string');
});
```

6. If necessary, **re-send** your previous "Get Currencies" request to make sure the default currency code is captured in your collection variables.
7. **Send** the request and **verify** that all tests succeed.
8. **Copy** the `checkoutUrl` from the response and **enter** it in a browser to verify that the checkout is initiated.
9. **Complete** the checkout and **place** an order for the invoice payment.

> **Info**
>
> With the invoice now paid, you can try re-running your "Get Open Invoices" request to observe the effects. Try removing the "status" filter and examining the updated status and balance of the paid invoice.

## Step 6: Get Payment Details

1. **Create** a new HTTP request and **save** it to your collection with the name "Get Payments."
2. **Set** the same configuration that was used for the previous requests, including the HTTP method, URL, Auth Type, Headers (using your previously created "GraphQL" preset), and Body type.
3. **Enter** the following query.

```graphql showLineNumbers={false}
query GetPayments(
    $limit: Int,
    $after: String
) {
    invoicePayments(
        first: $limit,
        after: $after
    ) {
        pageInfo {
            hasNextPage
            hasPreviousPage
            startCursor
            endCursor
        }
        totalCount
        edges {
            node {
                id
                createdAt
                totalCode
                totalAmount
                payerName
                processingStatus
                companyId
                companyName
                paymentReceiptSet {
                    edges {
                        node {
                            id
                            createdAt
                            customerId
                            totalCode
                            totalAmount
                            payerName
                            paymentId
                            paymentType
                            receiptLineSet {
                                edges {
                                    node {
                                        id
                                        createdAt
                                        customerId
                                        receiptId
                                        paymentId
                                        invoiceId
                                        invoiceNumber
                                        amount
                                        paymentType
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```

4. **Enter** the following in the GraphQL Variables panel.

```json showLineNumbers={false}
{
    "limit": 5,
    "after": null
}
```

5. In the Scripts tab, **enter** the following "Post-response" code.

```js showLineNumbers={false}
pm.test('Response is not an error', () => {
    pm.response.to.not.be.error;
    pm.response.to.not.have.jsonBody("errors");
});
pm.test('Response is JSON with data', () => {
    pm.response.to.have.jsonBody("data");
});

const result = pm.response.json().data?.invoicePayments;
pm.test('Total count includes at least one payment', () => {
    pm.expect(
        result?.totalCount
    ).to.be.greaterThan(0);
});

let firstPayment = null;
if (Array.isArray(result?.edges)) {
    firstPayment = result.edges[0]?.node;
}
pm.test('Response includes at least one payment with an ID', () => {
    pm.expect(
        firstPayment?.id
    ).to.be.a('string');
});
```

6. **Send** the request and **verify** that all tests succeed.

If needed, you can manipulate the `after` value in the GraphQL Variables panel using the `endCursor` value from the response to page through results.

7. **Create** a new HTTP request and **save** it to your collection with the name "Get Invoice Receipt Lines."
8. **Set** the same configuration that was used for the previous requests, including the HTTP method, URL, Auth Type, Headers (using your previously created "GraphQL" preset), and Body type.
9. **Enter** the following query.

```graphql showLineNumbers={false}
query GetReceiptLines(
    $invoiceId: String,
    $limit: Int,
    $after: String
) {
    allReceiptLines(
        invoiceId: $invoiceId,
        first: $limit,
        after: $after
    ) {
        pageInfo {
            hasNextPage
            hasPreviousPage
            startCursor
            endCursor
        }
        totalCount
        edges {
            node {
                id
                createdAt
                customerId
                receiptId
                paymentId
                invoiceId
                invoiceNumber
                amount
                paymentStatus
                paymentType
            }
        }
    }
}
```

10. **Enter** the following in the GraphQL Variables panel.

```json showLineNumbers={false}
{
    "invoiceId": "{{invoice_id}}",
    "limit": 5,
    "after": null
}
```

11. In the Scripts tab, **enter** the following "Post-response" code.

```js showLineNumbers={false}
pm.test('Response is not an error', () => {
    pm.response.to.not.be.error;
    pm.response.to.not.have.jsonBody("errors");
});
pm.test('Response is JSON with data', () => {
    pm.response.to.have.jsonBody("data");
});

const result = pm.response.json().data?.allReceiptLines;
pm.test('Total count includes at least one receipt line', () => {
    pm.expect(
        result?.totalCount
    ).to.be.greaterThan(0);
});

let firstLine = null;
if (Array.isArray(result?.edges)) {
    firstLine = result.edges[0]?.node;
}
pm.test('Response includes at least one receipt line with an ID', () => {
    pm.expect(
        firstLine?.id
    ).to.be.a('string');
});
```

12. **Send** the request and **verify** that all tests succeed.

If you previously paid only a partial amount on the invoice, you can try using the "Get Invoice Checkout" request again to initiate a further payment, then re-run your "Get Invoice Receipt Lines" request to observe the expanded details.

With the workflow you followed (making a payment on a single invoice), there aren't many differences between the details returned in your "Get Payments" and "Get Invoice Receipt Lines" requests. Remember, however, that a checkout can be initiated for multiple open invoices at once. When this is supported, this introduces different use cases for "Get Payments" (full details by payment) and "Get Invoice Receipt Lines" (payment info for a specific invoice).

## Taking It Further

Try out the following steps to further explore the purchasing workflow:

* Create multiple open invoices on a company and configure a "Get Invoice Checkout" request to initiate a checkout for multiple invoices. Observe the effects in the responses for "Get Payments" and "Get Invoice Receipt Lines."
* Create a request for a single order using the `order` query. Remember to pass the BigCommerce order ID in for the `id` argument.
* Create a request for a single invoice using the `invoice` query, and configure it to automatically use the most recent invoice from "Get Open Invoices" using the `invoice_id` collection variable.