/**
 * Create a new order refund programmatically.
 *
 * Returns a new refund object on success which can then be used to add additional data.
 *
 * @since 2.2
 * @param array $args
 * @return WC_Order_Refund|WP_Error
 */
function wc_create_refund($args = array())
{
    $default_args = array('amount' => 0, 'reason' => null, 'order_id' => 0, 'refund_id' => 0, 'line_items' => array());
    try {
        $args = wp_parse_args($args, $default_args);
        $order = wc_get_order($args['order_id']);
        $refund = new WC_Order_Refund($args['refund_id']);
        if (!$order) {
            throw new Exception(__('Invalid order ID.', 'woocommerce'));
        }
        // prevent negative refunds
        if (0 > $args['amount']) {
            $args['amount'] = 0;
        }
        $refund->set_amount($args['amount']);
        $refund->set_parent_id(absint($args['order_id']));
        $refund->set_refunded_by(get_current_user_id() ? get_current_user_id() : 1);
        if (!is_null($args['reason'])) {
            $refund->set_reason($args['reason']);
        }
        // Negative line items
        if (sizeof($args['line_items']) > 0) {
            $items = $order->get_items(array('line_item', 'fee', 'shipping'));
            foreach ($items as $item_id => $item) {
                if (!isset($args['line_items'][$item_id])) {
                    continue;
                }
                $qty = isset($args['line_items'][$item_id]['qty']) ? $args['line_items'][$item_id]['qty'] : 0;
                $refund_total = $args['line_items'][$item_id]['refund_total'];
                $refund_tax = isset($args['line_items'][$item_id]['refund_tax']) ? array_filter((array) $args['line_items'][$item_id]['refund_tax']) : array();
                if (empty($qty) && empty($refund_total) && empty($args['line_items'][$item_id]['refund_tax'])) {
                    continue;
                }
                $class = get_class($item);
                $refunded_item = new $class($item);
                $refunded_item->set_id(0);
                $refunded_item->add_meta_data('_refunded_item_id', $item_id, true);
                $refunded_item->set_total(wc_format_refund_total($refund_total));
                $refunded_item->set_taxes(array('total' => array_map('wc_format_refund_total', $refund_tax), 'subtotal' => array_map('wc_format_refund_total', $refund_tax)));
                if (is_callable(array($refunded_item, 'set_subtotal'))) {
                    $refunded_item->set_subtotal(wc_format_refund_total($refund_total));
                }
                if (is_callable(array($refunded_item, 'set_quantity'))) {
                    $refunded_item->set_quantity($qty * -1);
                }
                $refund->add_item($refunded_item);
            }
        }
        $refund->update_taxes();
        $refund->calculate_totals(false);
        $refund->set_total($args['amount'] * -1);
        $refund->save();
    } catch (Exception $e) {
        return new WP_Error('error', $e->getMessage());
    }
    return $refund;
}