Automattic\WooCommerce\Internal\RestApi\Routes\V4\Refunds

DataUtils::fill_missing_refund_totalspublicWC 10.9.0

Fill in refund_total for any line item that omits it, computing the value from the order item's unit price × quantity via compute_quantity_refund_total(), which caps the result to the line's remaining refundable amount.

Items that already have refund_total (including an explicit 0) are left untouched so validation can decide whether the explicit amount is valid. Items where refund_total is omitted OR is explicitly null are treated as "compute it for me". Items that can't be resolved (missing line_item_id, item not on order, invalid quantity, unsupported item type, product with zero source quantity) are also left untouched — validate_line_items surfaces the right error for those cases.

Auto-computed values are tax-inclusive, matching the convention enforced by the existing converter (convert_line_items_to_internal_format extracts tax from a tax-inclusive refund_total).

Method of the class: DataUtils{}

No Hooks.

Returns

array. The line items with refund_total populated where possible (same shape as input).

Usage

$DataUtils = new DataUtils();
$DataUtils->fill_missing_refund_totals( $line_items, $order ): array;
$line_items(array) (required)
Line items from the request (schema format). Each item: array{line_item_id?: int, quantity?: int, refund_total?: float|int|null, refund_tax?: array<int, mixed>}.
$order(WC_Order) (required)
The order being refunded.

Changelog

Since 10.9.0 Introduced.

DataUtils::fill_missing_refund_totals() code WC 11.1.1

public function fill_missing_refund_totals( array $line_items, WC_Order $order ): array {
	// Round caller-supplied amounts up front so explicit values are stored at the
	// same precision the preview validated and showed. Computed values below are
	// already rounded by compute_quantity_refund_total().
	$line_items = $this->normalize_refund_totals( $line_items );

	// Loaded lazily: only requests with at least one auto-computed line pay for
	// the refund-history scan the remaining-amount cap needs.
	$refund_data = null;

	foreach ( $line_items as $key => $line_item ) {
		// Treat a missing key and an explicit `null` value the same — both mean
		// "compute it for me". An explicit `0` is caller-supplied input, so leave
		// it untouched and let validation decide whether the gross line refund is valid.
		if ( array_key_exists( 'refund_total', $line_item ) && null !== $line_item['refund_total'] ) {
			continue;
		}

		// Skip auto-compute when the client also supplied an explicit
		// refund_tax. Auto-compute writes a tax-inclusive refund_total, but
		// the converter then skips tax extraction whenever refund_tax is
		// already present — and calculate_refund_amount would add both,
		// inflating the total by the tax amount. Leave refund_total unset;
		// validate_line_items rejects this ambiguous combination with a
		// clear error.
		if ( isset( $line_item['refund_tax'] ) ) {
			continue;
		}

		$line_item_id = $line_item['line_item_id'] ?? null;
		$quantity     = $line_item['quantity'] ?? null;
		if ( ! $line_item_id || ! is_int( $quantity ) || $quantity < 1 ) {
			continue;
		}

		$item = $order->get_item( $line_item_id );
		if ( ! $item || ! ( $item instanceof WC_Order_Item_Product || $item instanceof WC_Order_Item_Shipping || $item instanceof WC_Order_Item_Fee ) ) {
			continue;
		}

		// A product whose source line has zero quantity has no unit price to
		// derive a refund from. Skip so validate_line_items surfaces a clear
		// 'invalid_line_item' error to the API consumer instead of letting a
		// silent 0.0 propagate into the misleading "must be greater than zero"
		// branch downstream.
		if ( $item instanceof WC_Order_Item_Product && 0 === $item->get_quantity() ) {
			continue;
		}

		if ( null === $refund_data ) {
			$refund_data = $this->compute_refunded_quantities_and_totals( $order );
		}

		$line_items[ $key ]['refund_total'] = $this->compute_quantity_refund_total( $item, $quantity, $refund_data );
	}

	return $line_items;
}