fix: checkout issues - hidden fields, coupons, shipping in order totals

1. Hidden fields now properly hidden in SPA
   - Added billing_postcode and shipping_postcode to isFieldHidden checks
   - Fields with type='hidden' from PHP now conditionally rendered

2. Coupons now applied to order total
   - Added coupons array to order submission payload
   - CartController now calls calculate_totals() before reading discounts
   - Returns per-coupon discount amounts {code, discount, type}

3. Shipping now applied to order total
   - Already handled in submit() via find_shipping_rate_for_order
   - Frontend now sends shipping_method in payload

4. Order details now include shipping/tracking info
   - checkout/order/{id} API includes shipping_lines, tracking_number, tracking_url
   - account/orders/{id} API includes same shipping/tracking fields
   - Tracking info read from multiple plugin meta keys

5. Thank you/OrderDetails page shows shipping method and AWB
   - Shipping Method section with courier name and cost
   - AWB tracking for processing/completed orders with Track Shipment button
This commit is contained in:
Dwindi Ramadhana
2026-01-08 23:04:31 +07:00
parent e8c60b3a09
commit 0e561d9e8c
4 changed files with 98 additions and 20 deletions

View File

@@ -122,6 +122,10 @@ add_filter('woocommerce_checkout_fields', function($fields) {
$fields['billing']['billing_city']['type'] = 'hidden'; $fields['billing']['billing_city']['type'] = 'hidden';
$fields['billing']['billing_city']['required'] = false; $fields['billing']['billing_city']['required'] = false;
} }
if (isset($fields['billing']['billing_postcode'])) {
$fields['billing']['billing_postcode']['type'] = 'hidden';
$fields['billing']['billing_postcode']['required'] = false;
}
// Hide shipping fields // Hide shipping fields
if (isset($fields['shipping']['shipping_country'])) { if (isset($fields['shipping']['shipping_country'])) {
@@ -137,6 +141,10 @@ add_filter('woocommerce_checkout_fields', function($fields) {
$fields['shipping']['shipping_city']['type'] = 'hidden'; $fields['shipping']['shipping_city']['type'] = 'hidden';
$fields['shipping']['shipping_city']['required'] = false; $fields['shipping']['shipping_city']['required'] = false;
} }
if (isset($fields['shipping']['shipping_postcode'])) {
$fields['shipping']['shipping_postcode']['type'] = 'hidden';
$fields['shipping']['shipping_postcode']['required'] = false;
}
} }
// Destination field definition (reused for billing and shipping) // Destination field definition (reused for billing and shipping)

View File

@@ -532,6 +532,7 @@ export default function Checkout() {
}, },
payment_method: paymentMethod, payment_method: paymentMethod,
shipping_method: selectedShippingRate || undefined, // Selected shipping rate ID shipping_method: selectedShippingRate || undefined, // Selected shipping rate ID
coupons: appliedCoupons.map(c => c.code), // Send applied coupon codes
customer_note: orderNotes, customer_note: orderNotes,
// Include all custom field data for backend processing // Include all custom field data for backend processing
custom_fields: customFieldData, custom_fields: customFieldData,
@@ -771,16 +772,19 @@ export default function Checkout() {
)} )}
</div> </div>
)} )}
<div> {/* Postcode field - hidden if PHP sets type to 'hidden' */}
<label className="block text-sm font-medium mb-2">Postcode / ZIP *</label> {!isFieldHidden('billing_postcode') && (
<input <div>
type="text" <label className="block text-sm font-medium mb-2">Postcode / ZIP *</label>
required <input
value={billingData.postcode} type="text"
onChange={(e) => setBillingData({ ...billingData, postcode: e.target.value })} required
className="w-full border rounded-lg px-4 py-2" value={billingData.postcode}
/> onChange={(e) => setBillingData({ ...billingData, postcode: e.target.value })}
</div> className="w-full border rounded-lg px-4 py-2"
/>
</div>
)}
{/* Custom billing fields from plugins */} {/* Custom billing fields from plugins */}
{billingCustomFields.map(field => ( {billingCustomFields.map(field => (
@@ -962,16 +966,19 @@ export default function Checkout() {
)} )}
</div> </div>
)} )}
<div> {/* Postcode field - hidden if PHP sets type to 'hidden' */}
<label className="block text-sm font-medium mb-2">Postcode / ZIP *</label> {!isFieldHidden('shipping_postcode') && (
<input <div>
type="text" <label className="block text-sm font-medium mb-2">Postcode / ZIP *</label>
required <input
value={shippingData.postcode} type="text"
onChange={(e) => setShippingData({ ...shippingData, postcode: e.target.value })} required
className="w-full border rounded-lg px-4 py-2" value={shippingData.postcode}
/> onChange={(e) => setShippingData({ ...shippingData, postcode: e.target.value })}
</div> className="w-full border rounded-lg px-4 py-2"
/>
</div>
)}
{/* Custom shipping fields from plugins */} {/* Custom shipping fields from plugins */}
{shippingCustomFields.map(field => ( {shippingCustomFields.map(field => (

View File

@@ -197,6 +197,33 @@ class CheckoutController {
'image' => $product ? wp_get_attachment_image_url($product->get_image_id(), 'thumbnail') : null, 'image' => $product ? wp_get_attachment_image_url($product->get_image_id(), 'thumbnail') : null,
]; ];
} }
// Build shipping lines
$shipping_lines = [];
foreach ($order->get_shipping_methods() as $shipping_item) {
$shipping_lines[] = [
'id' => $shipping_item->get_id(),
'method_title' => $shipping_item->get_method_title(),
'method_id' => $shipping_item->get_method_id(),
'total' => wc_price($shipping_item->get_total()),
];
}
// Get tracking info from order meta (various plugins use different keys)
$tracking_number = $order->get_meta('_tracking_number')
?: $order->get_meta('_wc_shipment_tracking_items')
?: $order->get_meta('_rajaongkir_awb_number')
?: '';
$tracking_url = $order->get_meta('_tracking_url')
?: $order->get_meta('_rajaongkir_tracking_url')
?: '';
// Check for shipment tracking plugin format (array of tracking items)
if (is_array($tracking_number) && !empty($tracking_number)) {
$first_tracking = reset($tracking_number);
$tracking_number = $first_tracking['tracking_number'] ?? '';
$tracking_url = $first_tracking['tracking_url'] ?? $tracking_url;
}
return [ return [
'ok' => true, 'ok' => true,
@@ -204,12 +231,17 @@ class CheckoutController {
'number' => $order->get_order_number(), 'number' => $order->get_order_number(),
'status' => $order->get_status(), 'status' => $order->get_status(),
'subtotal' => (float) $order->get_subtotal(), 'subtotal' => (float) $order->get_subtotal(),
'discount_total' => (float) $order->get_discount_total(),
'shipping_total' => (float) $order->get_shipping_total(), 'shipping_total' => (float) $order->get_shipping_total(),
'tax_total' => (float) $order->get_total_tax(), 'tax_total' => (float) $order->get_total_tax(),
'total' => (float) $order->get_total(), 'total' => (float) $order->get_total(),
'currency' => $order->get_currency(), 'currency' => $order->get_currency(),
'currency_symbol' => get_woocommerce_currency_symbol($order->get_currency()), 'currency_symbol' => get_woocommerce_currency_symbol($order->get_currency()),
'payment_method' => $order->get_payment_method_title(), 'payment_method' => $order->get_payment_method_title(),
'needs_shipping' => count($shipping_lines) > 0 || $order->needs_shipping_address(),
'shipping_lines' => $shipping_lines,
'tracking_number' => $tracking_number,
'tracking_url' => $tracking_url,
'billing' => [ 'billing' => [
'first_name' => $order->get_billing_first_name(), 'first_name' => $order->get_billing_first_name(),
'last_name' => $order->get_billing_last_name(), 'last_name' => $order->get_billing_last_name(),

View File

@@ -521,6 +521,37 @@ class AccountController {
$data['shipping_total'] = html_entity_decode(strip_tags(wc_price($order->get_shipping_total()))); $data['shipping_total'] = html_entity_decode(strip_tags(wc_price($order->get_shipping_total())));
$data['tax_total'] = html_entity_decode(strip_tags(wc_price($order->get_total_tax()))); $data['tax_total'] = html_entity_decode(strip_tags(wc_price($order->get_total_tax())));
$data['discount_total'] = html_entity_decode(strip_tags(wc_price($order->get_discount_total()))); $data['discount_total'] = html_entity_decode(strip_tags(wc_price($order->get_discount_total())));
// Shipping lines with method details
$shipping_lines = [];
foreach ($order->get_shipping_methods() as $shipping_item) {
$shipping_lines[] = [
'id' => $shipping_item->get_id(),
'method_title' => $shipping_item->get_method_title(),
'method_id' => $shipping_item->get_method_id(),
'total' => html_entity_decode(strip_tags(wc_price($shipping_item->get_total()))),
];
}
$data['shipping_lines'] = $shipping_lines;
// Tracking info (from various shipping tracking plugins)
$tracking_number = $order->get_meta('_tracking_number')
?: $order->get_meta('_wc_shipment_tracking_items')
?: $order->get_meta('_rajaongkir_awb_number')
?: '';
$tracking_url = $order->get_meta('_tracking_url')
?: $order->get_meta('_rajaongkir_tracking_url')
?: '';
// Handle WooCommerce Shipment Tracking plugin format (array)
if (is_array($tracking_number) && !empty($tracking_number)) {
$first_tracking = reset($tracking_number);
$tracking_number = $first_tracking['tracking_number'] ?? '';
$tracking_url = $first_tracking['tracking_url'] ?? $tracking_url;
}
$data['tracking_number'] = $tracking_number;
$data['tracking_url'] = $tracking_url;
} }
return $data; return $data;