前台JS执行
文件: vendor/magento/module-checkout/view/frontend/web/template/shipping.html
<!--
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
-->
<li id="shipping" class="checkout-shipping-address" data-bind="fadeVisible: visible()">
<div class="step-title" translate="'Shipping Address'" data-role="title"></div>
<div id="checkout-step-shipping"
class="step-content"
data-role="content">
<each if="!quoteIsVirtual" args="getRegion('customer-email')" render="" ></each>
<each args="getRegion('address-list')" render="" ></each>
<each args="getRegion('address-list-additional-addresses')" render="" ></each>
<!-- Address form pop up -->
<if args="!isFormInline">
<div class="new-address-popup">
<button type="button"
class="action action-show-popup"
click="showFormPopUp"
visible="!isNewAddressAdded()">
<span translate="'New Address'"></span>
</button>
</div>
<div id="opc-new-shipping-address"
visible="isFormPopUpVisible()"
render="shippingFormTemplate"></div>
</if>
<each args="getRegion('before-form')" render="" ></each>
<!-- Inline address form -->
<render if="isFormInline" args="shippingFormTemplate"></render>
</div>
</li>
<!--Shipping method template-->
<li id="opc-shipping_method"
class="checkout-shipping-method"
data-bind="fadeVisible: visible(), blockLoader: isLoading"
role="presentation">
<div class="checkout-shipping-method">
<div class="step-title"
translate="'Shipping Methods'"
data-role="title"></div>
<each args="getRegion('before-shipping-method-form')" render="" ></each>
<div id="checkout-step-shipping_method"
class="step-content"
data-role="content"
role="tabpanel"
aria-hidden="false">
<form id="co-shipping-method-form"
class="form methods-shipping"
if="rates().length"
submit="setShippingInformation"
novalidate="novalidate">
<render args="shippingMethodListTemplate"></render>
<div id="onepage-checkout-shipping-method-additional-load">
<each args="getRegion('shippingAdditional')" render="" ></each>
</div>
<div role="alert"
if="errorValidationMessage().length"
class="message notice">
<span text="errorValidationMessage()"></span>
</div>
<div class="actions-toolbar" id="shipping-method-buttons-container">
<div class="primary">
<button data-role="opc-continue" type="submit" class="button action continue primary">
<span translate="'Next'"></span>
</button>
</div>
</div>
</form>
<div class="no-quotes-block"
ifnot="rates().length > 0"
translate="'Sorry, no quotes are available for this order at this time'"></div>
</div>
</div>
</li>
JS提交数据 :view/frontend/web/js/model/shipping-save-processor/default.js
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
define([
'ko',
'Magento_Checkout/js/model/quote',
'Magento_Checkout/js/model/resource-url-manager',
'mage/storage',
'Magento_Checkout/js/model/payment-service',
'Magento_Checkout/js/model/payment/method-converter',
'Magento_Checkout/js/model/error-processor',
'Magento_Checkout/js/model/full-screen-loader',
'Magento_Checkout/js/action/select-billing-address',
'Magento_Checkout/js/model/shipping-save-processor/payload-extender'
], function (
ko,
quote,
resourceUrlManager,
storage,
paymentService,
methodConverter,
errorProcessor,
fullScreenLoader,
selectBillingAddressAction,
payloadExtender
) {
'use strict';
return {
/**
* @return {jQuery.Deferred}
*/
saveShippingInformation: function () {
var payload;
if (!quote.billingAddress() && quote.shippingAddress().canUseForBilling()) {
selectBillingAddressAction(quote.shippingAddress());
}
payload = {
addressInformation: {
'shipping_address': quote.shippingAddress(),
'billing_address': quote.billingAddress(),
'shipping_method_code': quote.shippingMethod()['method_code'],
'shipping_carrier_code': quote.shippingMethod()['carrier_code']
}
};
payloadExtender(payload);
fullScreenLoader.startLoader();
return storage.post(
resourceUrlManager.getUrlForSetShippingInformation(quote),
JSON.stringify(payload)
).done(
function (response) {
quote.setTotals(response.totals);
paymentService.setPaymentMethods(methodConverter(response['payment_methods']));
fullScreenLoader.stopLoader();
}
).fail(
function (response) {
errorProcessor.process(response);
fullScreenLoader.stopLoader();
}
);
}
};
});
后台php执行
这里分注册登录用户和未登录的访客用户
1. 注册登录用户执行下面逻辑
http://m248.demo.com/rest/default/V1/carts/mine/shipping-information
请求方法 POST
文件: vendor/magento/module-checkout/etc/webapi.xml
<!-- Managing My shipping information -->
<route url="/V1/carts/mine/shipping-information" method="POST">
<service class="Magento\Checkout\Api\ShippingInformationManagementInterface" method="saveAddressInformation"/>
<resources>
<resource ref="self" />
</resources>
<data>
<parameter name="cartId" force="true">%cart_id%</parameter>
</data>
</route>

这个接口文件由: vendor/magento/module-checkout/etc/di.xml 的
Magento\Checkout\Model\ShippingInformationManagement 来实现

文件:vendor/magento/module-checkout/Model/ShippingInformationManagement.php的saveAddressInformation方法
保存收货地址然后返回付款方式
public function saveAddressInformation(
$cartId,
ShippingInformationInterface $addressInformation
): PaymentDetailsInterface {
/** @var Quote $quote */
$quote = $this->quoteRepository->getActive($cartId);
$this->validateQuote($quote);
// 直接打印到页面(仅适用于非 API 环境)
// echo '<pre>';
// print_r($addressInformation->getShippingAddress()->getData());
// die();
$address = $addressInformation->getShippingAddress();
$this->validateAddress($address);
//print_r($quote);
// print_r($address);
$this->updateCustomerShippingAddressId($quote, $address);
//die();
if (!$address->getCustomerAddressId()) {
$address->setCustomerAddressId(null);
}
try {
$billingAddress = $addressInformation->getBillingAddress();
if ($billingAddress) {
$this->updateCustomerBillingAddressId($quote, $billingAddress);
if (!$billingAddress->getCustomerAddressId()) {
$billingAddress->setCustomerAddressId(null);
}
$this->addressValidator->validateForCart($quote, $billingAddress);
$quote->setBillingAddress($billingAddress);
}
$this->addressValidator->validateForCart($quote, $address);
$carrierCode = $addressInformation->getShippingCarrierCode();
$address->setLimitCarrier($carrierCode);
$methodCode = $addressInformation->getShippingMethodCode();
$quote = $this->prepareShippingAssignment($quote, $address, $carrierCode . '_' . $methodCode);
$quote->setIsMultiShipping(false);
$this->quoteRepository->save($quote);
} catch (LocalizedException $e) {
$this->logger->critical($e);
throw new InputException(
__(
'The shipping information was unable to be saved. Error: "%message"',
['message' => $e->getMessage()]
)
);
} catch (\Exception $e) {
$this->logger->critical($e);
throw new InputException(
__('The shipping information was unable to be saved. Verify the input data and try again.')
);
}
$shippingAddress = $quote->getShippingAddress();
if (!$quote->getIsVirtual()
&& !$shippingAddress->getShippingRateByCode($shippingAddress->getShippingMethod())
) {
$errorMessage = $methodCode ?
__('Carrier with such method not found: %1, %2', $carrierCode, $methodCode)
: __('The shipping method is missing. Select the shipping method and try again.');
throw new NoSuchEntityException(
$errorMessage
);
}
/** @var PaymentDetailsInterface $paymentDetails */
$paymentDetails = $this->paymentDetailsFactory->create();
$paymentDetails->setPaymentMethods($this->paymentMethodManagement->getList($cartId));
$paymentDetails->setTotals($this->cartTotalsRepository->get($cartId));
return $paymentDetails;
}
2.未登录的访客用户执行下面逻辑
http://m248.demo.com/rest/default/V1/guest-carts/aosn0t36KW23VvMrJFNN6IjWhYYiekVy/shipping-information
请求方法
POST
