PluginProbe ʕ •ᴥ•ʔ
WooCommerce / 10.9.4
WooCommerce v10.9.4
10.9.4 10.9.3 10.9.2 10.9.1 10.9.0 10.9.0-rc.1 10.9.0-beta.2 10.9.0-beta.1 10.8.1 10.8.0 10.8.0-rc.1 10.8.0-beta.2 10.8.0-beta.1 7.8.0-beta.1 7.8.0-beta.2 7.8.0-rc.1 7.8.0-rc.2 7.8.1 7.8.2 7.8.3 7.8.4 7.9.0 7.9.0-beta.1 7.9.0-beta.2 7.9.0-rc.2 7.9.0-rc.3 7.9.1 7.9.2 8.0.0 8.0.0-beta.1 8.0.0-beta.2 8.0.0-rc.1 8.0.0-rc.2 8.0.1 8.0.2 8.0.3 8.0.4 8.0.5 8.1.0 8.1.0-beta.1 8.1.0-rc.1 8.1.0-rc.2 8.1.1 8.1.2 8.1.3 8.1.4 8.2.0 8.2.0-beta.1 8.2.0-rc.1 8.2.0-rc.2 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.3.0 8.3.0-beta.1 8.3.0-rc.1 8.3.0-rc.2 8.3.1 8.3.2 8.3.3 8.3.4 8.4.0 8.4.0-beta.1 8.4.0-rc.1 8.4.1 8.4.2 8.4.3 8.5.0 8.5.0-beta.1 8.5.0-rc.1 8.5.1 8.5.2 8.5.3 8.5.4 8.5.5 8.6.0 8.6.0-beta.1 8.6.0-rc.1 8.6.1 8.6.2 8.6.3 8.6.4 8.7.0 8.7.0-beta.1 8.7.0-beta.2 8.7.0-rc.1 8.7.1 8.7.2 8.7.3 8.8.0 8.8.0-beta.1 8.8.0-rc.1 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.8.6 8.8.7 8.9.0 8.9.0-beta.1 8.9.0-rc.1 8.9.1 8.9.2 8.9.3 8.9.4 8.9.5 9.0.0 9.0.0-beta.1 9.0.0-beta.2 9.0.0-rc.1 9.0.1 9.0.2 9.0.3 9.0.4 9.1.0 9.1.0-beta.1 9.1.0-rc.1 9.1.1 9.1.2 9.1.3 9.1.4 9.1.5 9.1.6 9.2.0 9.2.0-beta.1 9.2.0-rc.1 9.2.1 9.2.2 9.2.3 9.2.4 9.2.5 9.3.0 9.3.0-beta.1 9.3.0-rc.1 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.3.6 9.4.0 9.4.0-beta.1 9.4.0-beta.2 9.4.0-rc.1 9.4.0-rc.2 9.4.0-rc.3 9.4.0-rc.4 9.4.1 9.4.2 9.4.3 9.4.4 9.4.5 9.5.0 9.5.0-beta.1 9.5.0-beta.2 9.5.0-rc.1 9.5.1 9.5.2 9.5.3 9.5.4 9.6.0 9.6.0-beta.1 9.6.0-beta.2 9.6.0-rc.1 9.6.1 9.6.2 9.6.3 9.6.4 9.7.0 9.7.0-beta.1 9.7.0-rc.1 9.7.1 9.7.2 9.7.3 9.8.0 9.8.0-beta.1 9.8.0-rc.1 9.8.1 9.8.2 9.8.3 9.8.4 9.8.5 9.8.6 9.8.7 9.9.0 9.9.0-beta.1 9.9.0-rc.1 9.9.1 9.9.2 9.9.3 9.9.4 9.9.5 9.9.6 9.9.7 3.7.3 7.1.2 3.8.0 7.2.0 3.8.0-beta.1 7.2.0-beta.1 3.8.0-rc.1 7.2.0-beta.2 3.8.0-rc.2 7.2.0-rc.1 3.8.1 7.2.0-rc.2 3.8.2 7.2.1 3.8.3 7.2.2 3.9.0 7.2.3 3.9.0-beta.1 7.2.4 3.9.0-beta.2 7.3.0 3.9.0-rc.1 7.3.0-beta.1 3.9.0-rc.2 7.3.0-beta.2 3.9.0-rc.3 7.3.0-rc.1 3.9.0-rc.4 7.3.0-rc.2 3.9.1 7.3.1 3.9.2 7.4.0 3.9.3 7.4.0-beta.1 3.9.4 7.4.0-beta.2 3.9.5 7.4.0-rc.1 4.0.0 7.4.0-rc.2 4.0.0-beta.1 7.4.1 4.0.0-rc.1 7.4.2 4.0.0-rc.2 7.5.0 4.0.1 7.5.0-beta.1 4.0.2 7.5.0-beta.2 4.0.3 7.5.0-rc.1 4.0.4 7.5.1 4.1.0 7.5.2 4.1.0-beta.1 7.6.0 4.1.0-beta.2 7.6.0-beta.1 4.1.0-rc.1 7.6.0-beta.2 4.1.0-rc.2 7.6.0-rc.1 4.1.1 7.6.0-rc.2 4.1.2 7.6.0-rc.3 4.1.3 7.6.1 4.1.4 7.6.2 4.2.0 7.7.0 4.2.0-RC.1 7.7.0-beta.1 4.2.0-RC.2 7.7.0-beta.2 4.2.0-beta.1 7.7.0-rc.1 4.2.1 7.7.1 4.2.2 7.7.2 4.2.3 7.7.3 4.2.4 7.8.0 4.2.5 4.3.0 4.3.0-beta.1 4.3.0-rc.1 4.3.0-rc.2 4.3.0-rc.3 4.3.1 4.3.2 4.3.3 4.3.4 4.3.5 4.3.6 4.4.0 4.4.0-beta.1 4.4.0-rc.1 4.4.1 4.4.2 4.4.3 4.4.4 4.5.0 4.5.0-beta.1 4.5.0-rc.1 4.5.0-rc.3 4.5.1 4.5.2 4.5.3 4.5.4 4.5.5 4.6.0 4.6.0-beta.1 4.6.0-rc.1 4.6.1 4.6.2 4.6.3 4.6.4 4.6.5 4.7.0 4.7.0-beta.1 4.7.0-beta.2 4.7.0-rc.1 4.7.1 4.7.1-beta.1 4.7.2 4.7.3 4.7.4 4.8.0 4.8.0-beta.1 4.8.0-rc.1 4.8.0-rc.2 4.8.1 4.8.2 4.8.3 4.9.0 4.9.0-beta.1 4.9.0-rc.1 4.9.0-rc.2 4.9.1 4.9.2 4.9.3 4.9.4 4.9.5 5.0.0 5.0.0-beta.1 5.0.0-beta.2 5.0.0-rc.1 5.0.0-rc.2 5.0.0-rc.3 5.0.1 5.0.2 5.0.3 5.1.0 5.1.0-beta.1 5.1.0-rc.1 trunk 5.1.1 10.0.0 5.1.2 10.0.0-rc.1 5.1.3 10.0.0-rc.2 5.2.0 10.0.1 5.2.0-beta.1 10.0.2 5.2.0-rc.1 10.0.3 5.2.0-rc.2 10.0.4 5.2.1 10.0.5 5.2.2 10.0.6 5.2.3 10.1.0 5.2.4 10.1.0-rc.1 5.2.5 10.1.0-rc.2 5.3.0 10.1.0-rc.3 5.3.0-beta.1 10.1.0-rc.4 5.3.0-rc.1 10.1.1 5.3.0-rc.2 10.1.2 5.3.1 10.1.3 5.3.2 10.1.4 5.3.3 10.2.0 5.4.0 10.2.0-beta.1 5.4.0-beta.1 10.2.0-beta.2 5.4.0-rc.1 10.2.0-rc.1 5.4.1 10.2.1 5.4.2 10.2.2 5.4.3 10.2.3 5.4.4 10.2.4 5.4.5 10.3.0 5.5.0 10.3.0-beta.1 5.5.0-beta.1 10.3.0-beta.2 5.5.0-rc.1 10.3.0-rc.1 5.5.0-rc.2 10.3.0-rc.2 5.5.1 10.3.1 5.5.2 10.3.2 5.5.3 10.3.3 5.5.4 10.3.4 5.5.5 10.3.5 5.6.0 10.3.6 5.6.0-beta.1 10.3.7 5.6.0-rc.1 10.3.8 5.6.0-rc.2 10.4.0 5.6.1 10.4.0-beta.1 5.6.2 10.4.0-beta.2 5.6.3 10.4.0-rc.1 5.7.0 10.4.1 5.7.0-beta.1 10.4.2 5.7.0-rc.1 10.4.3 5.7.1 10.4.4 5.7.2 10.5.0 5.7.3 10.5.0-beta.1 5.8.0 10.5.0-beta.2 5.8.0-beta.1 10.5.0-rc.1 5.8.0-beta.2 10.5.0-rc.2 5.8.0-rc.1 10.5.0-rc.3 5.8.1 10.5.1 5.8.2 10.5.2 5.9.0 10.5.3 5.9.0-beta.1 10.6.0 5.9.0-rc.1 10.6.0-beta.1 5.9.0-rc.2 10.6.0-beta.2 5.9.1 10.6.0-rc.1 5.9.2 10.6.1 6.0.0 10.6.2 6.0.0-beta.1 10.7.0 6.0.0-rc.1 10.7.0-beta.1 6.0.1 10.7.0-beta.2 6.0.2 10.7.0-rc.1 6.1.0 3.0.0 6.1.0-beta.1 3.0.1 6.1.0-rc.1 3.0.2 6.1.0-rc.2 3.0.3 6.1.1 3.0.4 6.1.2 3.0.5 6.1.3 3.0.6 6.2.0 3.0.7 6.2.0-beta.1 3.0.8 6.2.0-rc.1 3.0.9 6.2.0-rc.2 3.1.0 6.2.1 3.1.1 6.2.2 3.1.2 6.2.3 3.2.0 6.3.0 3.2.1 6.3.0-beta.1 3.2.2 6.3.0-rc.1 3.2.3 6.3.0-rc.2 3.2.4 6.3.1 3.2.5 6.3.2 3.2.6 6.4.0 3.3.0 6.4.0-beta.1 3.3.1 6.4.0-rc.1 3.3.2 6.4.1 3.3.2-rc.1 6.4.2 3.3.3 6.5.0 3.3.4 6.5.0-beta.1 3.3.5 6.5.0-rc.1 3.3.6 6.5.0-rc.2 3.4.0 6.5.1 3.4.0-beta.1 6.5.2 3.4.0-rc.2 6.6.0 3.4.1 6.6.0-beta.1 3.4.2 6.6.0-rc.1 3.4.3 6.6.0-rc.2 3.4.4 6.6.1 3.4.5 6.6.2 3.4.6 6.7.0 3.4.7 6.7.0-beta.1 3.4.8 6.7.0-beta.2 3.5.0 6.7.0-rc.1 3.5.0-beta.1 6.7.1 3.5.0-rc.1 6.8.0 3.5.0-rc.2 6.8.0-beta.1 3.5.1 6.8.0-beta.2 3.5.10 6.8.0-rc.1 3.5.2 6.8.1 3.5.3 6.8.2 3.5.4 6.8.3 3.5.5 6.9.0 3.5.6 6.9.0-beta.1 3.5.7 6.9.0-beta.2 3.5.8 6.9.0-rc.1 3.5.9 6.9.1 3.6.0 6.9.2 3.6.0-beta.1 6.9.3 3.6.0-rc.1 6.9.4 3.6.0-rc.2 6.9.5 3.6.0-rc.3 7.0.0 3.6.1 7.0.0-beta.1 3.6.2 7.0.0-beta.2 3.6.3 7.0.0-beta.3 3.6.4 7.0.0-rc.1 3.6.5 7.0.0-rc.2 3.6.6 7.0.1 3.6.7 7.0.2 3.7.0 7.1.0 3.7.0-beta.1 7.1.0-beta.1 3.7.0-rc.1 7.1.0-beta.2 3.7.0-rc.2 7.1.0-rc.1 3.7.1 7.1.0-rc.2 3.7.2 7.1.1
woocommerce / src / Internal / ReceiptRendering / ReceiptRenderingEngine.php
woocommerce / src / Internal / ReceiptRendering Last commit date
Templates 3 months ago ReceiptRenderingEngine.php 2 months ago ReceiptRenderingRestController.php 1 year ago
ReceiptRenderingEngine.php
438 lines
1 <?php
2
3 namespace Automattic\WooCommerce\Internal\ReceiptRendering;
4
5 use Automattic\WooCommerce\Enums\OrderItemType;
6 use Automattic\WooCommerce\Internal\Orders\PaymentInfo;
7 use Automattic\WooCommerce\Internal\TransientFiles\TransientFilesEngine;
8 use Automattic\WooCommerce\Proxies\LegacyProxy;
9 use Automattic\WooCommerce\Utilities\ArrayUtil;
10 use Exception;
11 use WC_Abstract_Order;
12
13 /**
14 * This class generates printable order receipts as transient files (see src/Internal/TransientFiles).
15 * The template for the receipt is Templates/order-receipt.php, it uses the variables returned as array keys
16 * 'get_order_data'.
17 *
18 * When a receipt is generated for an order with 'generate_receipt' the receipt file name is stored as order meta
19 * (see RECEIPT_FILE_NAME_META_KEY) for later retrieval with 'get_existing_receipt'. Beware! The files pointed
20 * by such meta keys could have expired and thus no longer exist. 'get_existing_receipt' will appropriately return null
21 * if the meta entry exists but the file doesn't.
22 */
23 class ReceiptRenderingEngine {
24
25 private const FONT_SIZE = 12;
26
27 private const LINE_HEIGHT = self::FONT_SIZE * 1.5;
28
29 private const ICON_HEIGHT = self::LINE_HEIGHT;
30
31 private const ICON_WIDTH = self::ICON_HEIGHT * ( 4 / 3 );
32
33 private const MARGIN = 16;
34
35 private const TITLE_FONT_SIZE = 24;
36
37 private const FOOTER_FONT_SIZE = 10;
38
39 /**
40 * This array must contain all the names of the files in the CardIcons directory (without extension),
41 * except 'unknown'.
42 */
43 private const KNOWN_CARD_TYPES = array( 'amex', 'diners', 'discover', 'interac', 'jcb', 'mastercard', 'visa' );
44
45 /**
46 * Order meta key that stores the file name of the last generated receipt.
47 */
48 public const RECEIPT_FILE_NAME_META_KEY = '_receipt_file_name';
49
50 /**
51 * The instance of TransientFilesEngine to use.
52 *
53 * @var TransientFilesEngine
54 */
55 private $transient_files_engine;
56
57 /**
58 * The instance of LegacyProxy to use.
59 *
60 * @var LegacyProxy
61 */
62 private $legacy_proxy;
63
64 /**
65 * Initializes the class.
66 *
67 * @param TransientFilesEngine $transient_files_engine The instance of TransientFilesEngine to use.
68 * @param LegacyProxy $legacy_proxy The instance of LegacyProxy to use.
69 * @internal
70 */
71 final public function init( TransientFilesEngine $transient_files_engine, LegacyProxy $legacy_proxy ) {
72 $this->transient_files_engine = $transient_files_engine;
73 $this->legacy_proxy = $legacy_proxy;
74 }
75
76 /**
77 * Get the (transient) file name of the receipt for an order, creating a new file if necessary.
78 *
79 * If $force_new is false, and a receipt file for the order already exists (as pointed by order meta key
80 * RECEIPT_FILE_NAME_META_KEY), then the name of the already existing receipt file is returned.
81 *
82 * If $force_new is true, OR if it's false but no receipt file for the order exists (no order meta with key
83 * RECEIPT_FILE_NAME_META_KEY exists, OR it exists but the file it points to doesn't), then a new receipt
84 * transient file is created with the supplied expiration date (defaulting to "tomorrow"), and the new file name
85 * is stored as order meta with the key RECEIPT_FILE_NAME_META_KEY.
86 *
87 * @param int|WC_Abstract_Order $order The order object or order id to get the receipt for.
88 * @param string|int|null $expiration_date GMT expiration date formatted as yyyy-mm-dd, or as a timestamp, or null for "tomorrow".
89 * @param bool $force_new If true, creates a new receipt file even if one already exists for the order.
90 * @return string|null The file name of the new or already existing receipt file, null if an order id is passed and the order doesn't exist.
91 * @throws InvalidArgumentException Invalid expiration date (wrongly formatted, or it's a date in the past).
92 * @throws Exception The directory to store the file doesn't exist and can't be created.
93 */
94 public function generate_receipt( $order, $expiration_date = null, bool $force_new = false ): ?string {
95 if ( ! $order instanceof WC_Abstract_Order ) {
96 $order = wc_get_order( $order );
97 if ( false === $order ) {
98 return null;
99 }
100 }
101
102 if ( ! $force_new ) {
103 $existing_receipt_filename = $this->get_existing_receipt( $order );
104 if ( ! is_null( $existing_receipt_filename ) ) {
105 return $existing_receipt_filename;
106 }
107 }
108
109 $expiration_date ??=
110 $this->legacy_proxy->call_function(
111 'gmdate',
112 'Y-m-d',
113 $this->legacy_proxy->call_function(
114 'strtotime',
115 '+1 days'
116 )
117 );
118
119 /**
120 * Filter to customize the set of data that is used to render the receipt.
121 * The formatted line items aren't included, use the woocommerce_printable_order_receipt_formatted_line_item
122 * filter to customize those.
123 *
124 * See the value returned by the 'get_order_data' and 'get_woo_pay_data' methods for a reference of
125 * the structure of the data.
126 *
127 * See the template file, Templates/order-receipt.php, for reference on how the data is used.
128 *
129 * @param array $data The original set of data.
130 * @param WC_Abstract_Order $order The order for which the receipt is being generated.
131 * @returns array The updated set of data.
132 *
133 * @since 9.0.0
134 */
135 $data = apply_filters( 'woocommerce_printable_order_receipt_data', $this->get_order_data( $order ), $order );
136
137 $formatted_line_items = array();
138 $row_index = 0;
139 foreach ( $data['line_items'] as $line_item_data ) {
140 $quantity_data = isset( $line_item_data['quantity'] ) ? " × {$line_item_data['quantity']}" : '';
141 $line_item_display_data = array(
142 'inner_html' => "<td>{$line_item_data['title']}$quantity_data</td><td>{$line_item_data['amount']}</td>",
143 'tr_attributes' => array(),
144 'row_index' => $row_index++,
145 );
146
147 /**
148 * Filter to customize the HTML that gets rendered for each order line item in the receipt.
149 *
150 * $line_item_display_data will be passed (and must be returned) with the following keys:
151 *
152 * - inner_html: the HTML text that will go inside a <tr> element, note that
153 * wp_kses_post will be applied to this text before actual rendering.
154 * - tr_attributes: attributes (e.g. 'class', 'data', 'style') that will be applied to the <tr> element,
155 * as an associative array of attribute name => value.
156 * - row_index: a number that starts at 0 and increases by one for each processed line item.
157 *
158 * $line_item_data will contain the following keys:
159 *
160 * - type: One of 'product', 'subtotal', 'discount', 'fee', 'shipping_total', 'taxes_total', 'amount_paid'
161 * - title
162 * - amount (formatted with wc_price)
163 * - item (only when type is 'product'), and instance of WC_Order_Item
164 * - quantity (only when type is 'product')
165 *
166 * @param string $line_item_display_data Data to use to generate the HTML table row to be rendered for the line item.
167 * @param array $line_item_data The relevant data for the line item for which the HTML table row is being generated.
168 * @param WC_Abstract_Order $order The order for which the receipt is being generated.
169 * @return string The actual data to use to generate the HTML for the line item.
170 *
171 * @since 9.0.0
172 */
173 $line_item_display_data = apply_filters( 'woocommerce_printable_order_receipt_line_item_display_data', $line_item_display_data, $line_item_data, $order );
174 $attributes = '';
175 foreach ( $line_item_display_data['tr_attributes'] as $attribute_name => $attribute_value ) {
176 $attribute_value = esc_attr( $attribute_value );
177 $attributes .= " $attribute_name=\"$attribute_value\"";
178 }
179 $formatted_line_items[] = wp_kses_post( "<tr$attributes>{$line_item_display_data['inner_html']}</tr>" );
180 }
181 $data['formatted_line_items'] = $formatted_line_items;
182
183 ob_start();
184 $css = include __DIR__ . '/Templates/order-receipt-css.php';
185 $css = ob_get_contents();
186 ob_end_clean();
187
188 /**
189 * Filter to customize the CSS styles used to render the receipt.
190 *
191 * See Templates/order-receipt.php for guidance on the existing HTMl elements and their ids.
192 * See Templates/order-receipt-css.php for the original CSS styles.
193 *
194 * @param string $css The original CSS styles to use.
195 * @param WC_Abstract_Order $order The order for which the receipt is being generated.
196 * @return string The actual CSS styles that will be used.
197 *
198 * @since 9.0.0
199 */
200 $data['css'] = apply_filters( 'woocommerce_printable_order_receipt_css', $css, $order );
201
202 $default_template_path = __DIR__ . '/Templates/order-receipt.php';
203
204 /**
205 * Filter the order receipt template path.
206 *
207 * @since 9.2.0
208 * @hook wc_get_template
209 * @param string $template The template path.
210 * @param string $template_name The template name.
211 * @param array $args The available data for the template.
212 * @param string $template_path The template path.
213 * @param string $default_path The default template path.
214 */
215 $template_path = apply_filters(
216 'wc_get_template',
217 $default_template_path,
218 'ReceiptRendering/order-receipt.php',
219 $data,
220 $default_template_path,
221 $default_template_path
222 );
223
224 if ( ! file_exists( $template_path ) ) {
225 $template_path = $default_template_path;
226 }
227
228 ob_start();
229 include $template_path;
230 $rendered_template = ob_get_contents();
231 ob_end_clean();
232
233 $file_name = $this->transient_files_engine->create_transient_file( $rendered_template, $expiration_date );
234
235 $order->update_meta_data( self::RECEIPT_FILE_NAME_META_KEY, $file_name );
236 $order->save_meta_data();
237
238 return $file_name;
239 }
240
241 /**
242 * Get the file name of an existing receipt file for an order.
243 *
244 * A receipt is considered to be available for the order if there's an order meta entry with key
245 * RECEIPT_FILE_NAME_META_KEY AND the transient file it points to exists AND it has not expired.
246 *
247 * @param WC_Abstract_Order $order The order object or order id to get the receipt for.
248 * @return string|null The receipt file name, or null if no receipt is currently available for the order.
249 * @throws Exception Thrown if a wrong file path is passed.
250 */
251 public function get_existing_receipt( $order ): ?string {
252 if ( ! $order instanceof WC_Abstract_Order ) {
253 $order = wc_get_order( $order );
254 if ( false === $order ) {
255 return null;
256 }
257 }
258
259 $existing_receipt_filename = $order->get_meta( self::RECEIPT_FILE_NAME_META_KEY, true );
260
261 if ( '' === $existing_receipt_filename ) {
262 return null;
263 }
264
265 $file_path = $this->transient_files_engine->get_transient_file_path( $existing_receipt_filename );
266 if ( is_null( $file_path ) ) {
267 return null;
268 }
269
270 return $this->transient_files_engine->file_has_expired( $file_path ) ? null : $existing_receipt_filename;
271 }
272
273 /**
274 * Get the order data that the receipt template will use.
275 *
276 * @param WC_Abstract_Order $order The order to get the data from.
277 * @return array The order data as an associative array.
278 */
279 private function get_order_data( WC_Abstract_Order $order ): array {
280 $store_name = get_bloginfo( 'name' );
281 if ( $store_name ) {
282 /* translators: %s = store name */
283 $receipt_title = sprintf( __( 'Receipt from %s', 'woocommerce' ), $store_name );
284 } else {
285 $receipt_title = __( 'Receipt', 'woocommerce' );
286 }
287
288 $order_id = $order->get_id();
289 if ( $order_id ) {
290 /* translators: %d = order id */
291 $summary_title = sprintf( __( 'Summary: Order #%d', 'woocommerce' ), $order->get_id() );
292 } else {
293 $summary_title = __( 'Summary', 'woocommerce' );
294 }
295
296 $get_price_args = array( 'currency' => $order->get_currency() );
297
298 $line_items_info = array();
299 $line_items = $order->get_items( OrderItemType::LINE_ITEM );
300 foreach ( $line_items as $line_item ) {
301 $line_item_product = $line_item->get_product();
302 if ( false === $line_item_product ) {
303 $line_item_title = $line_item->get_name();
304 } else {
305 $line_item_title =
306 ( $line_item_product instanceof \WC_Product_Variation ) ?
307 ( wc_get_product( $line_item_product->get_parent_id() )->get_name() ) . '. ' . $line_item_product->get_attribute_summary() :
308 $line_item_product->get_name();
309 }
310 $line_items_info[] = array(
311 'type' => 'product',
312 'item' => $line_item,
313 'title' => wp_kses( $line_item_title, array() ),
314 'quantity' => $line_item->get_quantity(),
315 'amount' => wc_price( $line_item->get_subtotal(), $get_price_args ),
316 );
317 }
318
319 $line_items_info[] = array(
320 'type' => 'subtotal',
321 'title' => __( 'Subtotal', 'woocommerce' ),
322 'amount' => wc_price( $order->get_subtotal(), $get_price_args ),
323 );
324
325 $coupon_names = ArrayUtil::select( $order->get_coupons(), 'get_name', ArrayUtil::SELECT_BY_OBJECT_METHOD );
326 if ( ! empty( $coupon_names ) ) {
327 $line_items_info[] = array(
328 'type' => 'discount',
329 /* translators: %s = comma-separated list of coupon codes */
330 'title' => sprintf( __( 'Discount (%s)', 'woocommerce' ), join( ', ', $coupon_names ) ),
331 'amount' => wc_price( -$order->get_total_discount(), $get_price_args ),
332 );
333 }
334
335 foreach ( $order->get_fees() as $fee ) {
336 $name = $fee->get_name();
337 $line_items_info[] = array(
338 'type' => OrderItemType::FEE,
339 'title' => '' === $name ? __( 'Fee', 'woocommerce' ) : $name,
340 'amount' => wc_price( $fee->get_total(), $get_price_args ),
341 );
342 }
343
344 $shipping_total = (float) $order->get_shipping_total();
345 if ( $shipping_total ) {
346 $line_items_info[] = array(
347 'type' => 'shipping_total',
348 'title' => __( 'Shipping', 'woocommerce' ),
349 'amount' => wc_price( $order->get_shipping_total(), $get_price_args ),
350 );
351 }
352
353 $total_taxes = 0;
354 foreach ( $order->get_taxes() as $tax ) {
355 $total_taxes += (float) $tax->get_tax_total() + (float) $tax->get_shipping_tax_total();
356 }
357
358 if ( $total_taxes ) {
359 $line_items_info[] = array(
360 'type' => 'taxes_total',
361 'title' => __( 'Taxes', 'woocommerce' ),
362 'amount' => wc_price( $total_taxes, $get_price_args ),
363 );
364 }
365
366 $is_order_failed = $order->has_status( 'failed' );
367
368 $line_items_info[] = array(
369 'type' => 'amount_paid',
370 'title' => $is_order_failed ? __( 'Amount', 'woocommerce' ) : __( 'Amount Paid', 'woocommerce' ),
371 'amount' => wc_price( $order->get_total(), $get_price_args ),
372 );
373
374 $payment_info = $this->get_woo_pay_data( $order );
375
376 return array(
377 'order' => $order,
378 'constants' => array(
379 'font_size' => self::FONT_SIZE,
380 'margin' => self::MARGIN,
381 'title_font_size' => self::TITLE_FONT_SIZE,
382 'footer_font_size' => self::FOOTER_FONT_SIZE,
383 'line_height' => self::LINE_HEIGHT,
384 'icon_height' => self::ICON_HEIGHT,
385 'icon_width' => self::ICON_WIDTH,
386 ),
387 'texts' => array(
388 'receipt_title' => $receipt_title,
389 'amount_paid_section_title' => $is_order_failed ? __( 'Order Total', 'woocommerce' ) : __( 'Amount Paid', 'woocommerce' ),
390 'date_paid_section_title' => $is_order_failed ? __( 'Order Date', 'woocommerce' ) : __( 'Date Paid', 'woocommerce' ),
391 'payment_method_section_title' => __( 'Payment method', 'woocommerce' ),
392 'payment_status_section_title' => __( 'Payment status', 'woocommerce' ),
393 'payment_status' => $is_order_failed ? __( 'Failed', 'woocommerce' ) : __( 'Success', 'woocommerce' ),
394 'summary_section_title' => $summary_title,
395 'order_notes_section_title' => __( 'Notes', 'woocommerce' ),
396 'app_name' => __( 'Application Name', 'woocommerce' ),
397 'aid' => __( 'AID', 'woocommerce' ),
398 'account_type' => __( 'Account Type', 'woocommerce' ),
399 ),
400 'formatted_amount' => wc_price( $order->get_total(), $get_price_args ),
401 'formatted_date' => wc_format_datetime( $order->get_date_paid() ?? $order->get_date_created() ),
402 'line_items' => $line_items_info,
403 'payment_method' => $order->get_payment_method_title(),
404 'show_payment_method_title' => empty( $payment_info['card_last4'] ) && empty( $payment_info['brand'] ),
405 'notes' => array_map( 'get_comment_text', $order->get_customer_order_notes() ),
406 'payment_info' => $payment_info,
407 );
408 }
409
410 /**
411 * Get the order data related to WooCommerce Payments.
412 *
413 * It will return null if any of these is true:
414 *
415 * - Payment method is not "woocommerce_payments".
416 * - WooCommerce Payments is not installed.
417 * - No intent id is stored for the order.
418 * - Retrieving the payment information from Stripe API (providing the intent id) fails.
419 * - The received data set doesn't contain the expected information.
420 *
421 * @param WC_Abstract_Order $order The order to get the data from.
422 * @return array|null An array of payment information for the order, or null if not available.
423 */
424 private function get_woo_pay_data( WC_Abstract_Order $order ): ?array {
425 $card_info = PaymentInfo::get_card_info( $order );
426
427 if ( empty( $card_info ) ) {
428 return null;
429 }
430
431 // Backcompat for custom templates.
432 $card_info['card_icon'] = $card_info['icon'];
433 $card_info['card_last4'] = $card_info['last4'];
434
435 return $card_info;
436 }
437 }
438