wc_create_refund() │ WC 2.2
Create a new order refund programmatically.
Returns a new refund object on success which can then be used to add additional data.
Hooks from the function
Returns
WC_Order_Refund|WP_Error.
Usage
wc_create_refund( $args );
- $args(array)
- New refund arguments.
Default:array()
Changelog
| Since 2.2 | Introduced. |
wc_create_refund() wc create refund code WC 10.7.0
function wc_create_refund( $args = array() ) {
$default_args = array(
'amount' => 0,
'reason' => null,
'order_id' => 0,
'refund_id' => 0,
'line_items' => array(),
'refund_payment' => false,
'restock_items' => false,
);
try {
$args = wp_parse_args( $args, $default_args );
$order = wc_get_order( $args['order_id'] );
if ( ! $order ) {
throw new Exception( __( 'Invalid order ID.', 'woocommerce' ) );
}
$remaining_refund_amount = $order->get_remaining_refund_amount();
$remaining_refund_items = $order->get_remaining_refund_items();
$refund_item_count = 0;
$refund = new WC_Order_Refund( $args['refund_id'] );
$refunded_order_and_products = array();
if ( 0 > $args['amount'] || $args['amount'] > $remaining_refund_amount ) {
throw new Exception( __( 'Invalid refund amount.', 'woocommerce' ) );
}
$refund->set_currency( $order->get_currency() );
$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 );
$refund->set_prices_include_tax( $order->get_prices_include_tax() );
if ( ! is_null( $args['reason'] ) ) {
$refund->set_reason( $args['reason'] );
}
// Negative line items.
if ( is_array( $args['line_items'] ) && count( $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;
}
// array of order id and product id which were refunded.
// later to be used for revoking download permission.
// checking if the item is a product, as we only need to revoke download permission for products.
if ( $item->is_type( 'line_item' ) ) {
$refunded_order_and_products[ $item_id ] = array(
'order_id' => $order->get_id(),
'product_id' => $item->get_product_id(),
);
}
$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_item_count += $qty;
}
}
$refund->update_taxes();
$refund->calculate_totals( false );
$refund->set_total( $args['amount'] * -1 );
// this should remain after update_taxes(), as this will save the order, and write the current date to the db
// so we must wait until the order is persisted to set the date.
if ( isset( $args['date_created'] ) ) {
$refund->set_date_created( $args['date_created'] );
}
/**
* Action hook to adjust refund before save.
*
* @since 3.0.0
*/
do_action( 'woocommerce_create_refund', $refund, $args );
if ( $refund->save() ) {
if ( $args['refund_payment'] ) {
$result = wc_refund_payment( $order, $refund->get_amount(), $refund->get_reason() );
if ( is_wp_error( $result ) ) {
$refund->delete();
return $result;
}
$refund->set_refunded_payment( true );
$refund->save();
}
if ( $args['restock_items'] ) {
wc_restock_refunded_items( $order, $args['line_items'] );
}
// delete downloads that were refunded using order and product id, if present.
if ( ! empty( $refunded_order_and_products ) ) {
$download_data_store = WC_Data_Store::load( 'customer-download' );
foreach ( $refunded_order_and_products as $refunded_order_and_product ) {
$downloads = $download_data_store->get_downloads( $refunded_order_and_product );
if ( ! empty( $downloads ) ) {
foreach ( $downloads as $download ) {
$download_data_store->delete_by_id( $download->get_id() );
}
}
}
}
/**
* Trigger notification emails.
*
* Filter hook to modify the partially-refunded status conditions.
*
* @since 6.7.0
*
* @param bool $is_partially_refunded Whether the order is partially refunded.
* @param int $order_id The order id.
* @param int $refund_id The refund id.
*/
if ( (bool) apply_filters(
'woocommerce_order_is_partially_refunded',
( $remaining_refund_amount - $args['amount'] ) > 0 ||
( ! empty( $args['line_items'] ) && $order->has_free_item() && ( $remaining_refund_items - $refund_item_count ) > 0 ),
$order->get_id(),
$refund->get_id()
) ) {
do_action( 'woocommerce_order_partially_refunded', $order->get_id(), $refund->get_id() );
} else {
do_action( 'woocommerce_order_fully_refunded', $order->get_id(), $refund->get_id() );
/**
* Filter the status to set the order to when fully refunded.
*
* @since 2.7.0
*
* @param string $parent_status The status to set the order to when fully refunded.
* @param int $order_id The order ID.
* @param int $refund_id The refund ID.
*/
$parent_status = apply_filters( 'woocommerce_order_fully_refunded_status', OrderStatus::REFUNDED, $order->get_id(), $refund->get_id() );
if ( $parent_status ) {
$order->update_status( $parent_status );
}
}
}
$order->set_date_modified( time() );
if ( wc_get_container()->get( CostOfGoodsSoldController::class )->feature_is_enabled() && $order->has_cogs() ) {
$order->calculate_cogs_total_value();
}
$order->save();
do_action( 'woocommerce_refund_created', $refund->get_id(), $args );
do_action( 'woocommerce_order_refunded', $order->get_id(), $refund->get_id() );
} catch ( Exception $e ) {
if ( isset( $refund ) && is_a( $refund, 'WC_Order_Refund' ) ) {
$refund->delete( true );
}
return new WP_Error( 'error', $e->getMessage() );
}
return $refund;
}