Non-product invoice items
Typically you would not restrict your invoice_line table to specifically products, instead create an exclusive subtype depending on what type each line is (product, fee, discount, tax, memo, etc.).
CREATE TABLE line_type
(
  line_type_cd  CHAR(1)      NOT NULL
 ,description   VARCHAR(50)  NOT NULL
 ,CONSTRAINT PK_line_type PRIMARY KEY (invoice_line_type_cd)
 ,CONSTRAINT AK_line_type UNIQUE (invoice_line_type_cd)
)
;

CREATE TABLE invoice_line
(
  invoice_id    INT           NOT NULL
 ,line_nbr      SMALLINT      NOT NULL
 ,line_type_cd  CHAR(1)       NOT NULL
 ,quantity      DECIMAL(9,2)  NOT NULL
 ,amount        DECIMAL(9,2)  NOT NULL
 ,CONSTRAINT FK_invoice_line_for_invoice FOREIGN KEY (invoice_id) REFERENCES invoice (invoice_id)
 ,CONSTRAINT FK_invoice_line_discriminated_by_line_type FOREIGN KEY (line_type_cd) REFERENCES line_type (line_type_cd)
 ,CONSTRAINT PK_invoice_line PRIMARY KEY (invoice_id, line_nbr)
)
;

CREATE TABLE invoice_product
(
  invoice_id        INT       NOT NULL
 ,product_line_nbr  SMALLINT  NOT NULL
 ,product_id        INT       NOT NULL
 ,order_id          UUID      NOT NULL
 ,CONSTRAINT FK_invoice_line_fulfills_order_line FOREIGN KEY (order_id, product_id) REFERENCES order_line (order_id, product_id)
 ,CONSTRAINT FK_invoice_product_is_invoice_line FOREIGN KEY (invoice_id, product_line_nbr) REFERENCES invoice_line (invoice_id, line_nbr)
 ,CONSTRAINT PK_invoice_fee PRIMARY KEY (invoice_id, product_line_nbr)
)
;

CREATE TABLE invoice_fee
(
  invoice_id    INT       NOT NULL
 ,fee_line_nbr  SMALLINT  NOT NULL
 ,fee_cd        CHAR(6)   NOT NULL
 ,CONSTRAINT FK_invoice_line_charges_fee FOREIGN KEY (fee_cd) REFERENCES fee (fee_cd)
 ,CONSTRAINT FK_invoice_fee_is_invoice_line FOREIGN KEY (invoice_id, fee_line_nbr) REFERENCES invoice_line (invoice_id, line_nbr)
 ,CONSTRAINT PK_invoice_fee PRIMARY KEY (invoice_id, fee_line_nbr)
)
;

CREATE TABLE invoice_tax
(
  invoice_id    INT       NOT NULL
 ,tax_line_nbr  SMALLINT  NOT NULL
 ,tax_auth_cd   CHAR(6)   NOT NULL
 ,CONSTRAINT FK_invoice_line_remitted_to_tax_authority FOREIGN KEY (tax_authority_cd) REFERENCES tax_authority (tax_authority_cd)
 ,CONSTRAINT FK_invoice_tax_is_invoice_line FOREIGN KEY (invoice_id, tax_line_nbr) REFERENCES invoice_line (invoice_id, line_nbr)
 ,CONSTRAINT PK_invoice_fee PRIMARY KEY (invoice_id, tax_line_nbr)
)
;

The last step is to add a function/trigger to ensure the insert into each of the subtype tables corresponds to the appropriate line_type.
An invoice_product table isn't something to be avoided - you need a way to separate concerns/unique attributes while still associating the quantities/amounts to each invoice_line.
A few other observations:
There is a normalization error that will permit invalid order/customer combinations for an invoice.  So you'll need to either:

Restrict an invoice to one order - thus allowing one path to customer (inflexible).
Make customer_id part of the primary key of invoice and order and use the power of the composite key to enforce the relationship (most flexible).

The second option would look like this:
CREATE TABLE invoice
(
  customer_id  INT   NOT NULL
 ,invoice_id   INT   NOT NULL
 /* everything else */
 ,CONSTRAINT FK_invoice_for_customer FOREIGN KEY (customer_id) REFERENCES customer (customer_id)
 ,CONSTRAINT PK_invoice PRIMARY KEY (invoice_id, customer_id)
 ,CONSTRAINT AK_invoice UNIQUE (invoice_id)
)
;

CREATE TABLE order
(
  cust_id         INT          NOT NULL
 ,order_nbr       INT          NOT NULL
 ,cust_order_ref  VARCHAR(20)  NOT NULL
 /* everything else */
 ,CONSTRAINT FK_order_placed_by_customer FOREIGN KEY (cust_id) REFERENCES customer (cust_id)
 ,CONSTRAINT PK_order PRIMARY KEY (cust_id, order_nbr)
 ,CONSTRAINT AK_order UNIQUE (cust_id, cust_order_ref)
)
;

At this point cust_id would migrate to your invoice_line table (and subtypes) and enforce only items will be billed to the customer that were placed by the customer  This also allows you to avoid the ugly UUID data type - much easier to reference a smaller composite key than break everything with a 16-byte row pointer:
CREATE TABLE invoice_line
(
  invoice_id    INT           NOT NULL
 ,cust_id       INT           NOT NULL
 ,line_nbr      SMALLINT      NOT NULL
 ,line_type_cd  CHAR(1)       NOT NULL
 ,quantity      DECIMAL(9,2)  NOT NULL
 ,amount        DECIMAL(9,2)  NOT NULL
 ,CONSTRAINT FK_invoice_line_for_invoice FOREIGN KEY (invoice_id, cust_id) REFERENCES invoice (invoice_id, cust_id)
 ,CONSTRAINT FK_invoice_line_discriminated_by_line_type FOREIGN KEY (line_type_cd) REFERENCES line_type (line_type_cd)
 ,CONSTRAINT PK_invoice_line PRIMARY KEY (invoice_id, cust_id, line_nbr)
)
;

CREATE TABLE invoice_product
(
  invoice_id        INT       NOT NULL
 ,cust_id           INT       NOT NULL
 ,product_line_nbr  SMALLINT  NOT NULL
 ,product_id        INT       NOT NULL
 ,order_nbr         INT       NOT NULL
 ,CONSTRAINT FK_invoice_line_fulfills_order_line FOREIGN KEY (cust_id, order_nbr, product_id) REFERENCES order_line (cust_id, order_nbr, product_id)
 ,CONSTRAINT FK_invoice_product_is_invoice_line FOREIGN KEY (invoice_id, cust_id, product_line_nbr) REFERENCES invoice_line (invoice_id, line_nbr)
 ,CONSTRAINT PK_invoice_fee PRIMARY KEY (invoice_id, cust_id, product_line_nbr)
)
;

The appropriate choice will be dependent on your other requirements - if customers do not want multiple orders combined on a single invoice, the first option is perfectly acceptable and there are other ways to handle if multiple orders were fulfilled at the same time (you'd generally have a manifest or packing_list entity that could handle this).