Skip to content

Commit

Permalink
Applepay express (#232)
Browse files Browse the repository at this point in the history
* ShippingMethodService Introduced

* ExpressShippingMethod -> ShippingMethod rename

* Express Mapper
  • Loading branch information
aashwin-rvvup authored Jan 7, 2025
1 parent eca2948 commit c4066a1
Show file tree
Hide file tree
Showing 4 changed files with 351 additions and 106 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

declare(strict_types=1);

namespace Rvvup\Payments\Model\Express;
namespace Rvvup\Payments\Model\Shipping;

use Magento\Quote\Api\Data\ShippingMethodInterface;

class ExpressShippingMethod
class ShippingMethod
{

/** @var string */
Expand Down Expand Up @@ -42,6 +42,9 @@ public function getId(): string
return $this->id;
}

/**
* @return string
*/
public function getLabel(): string
{
return $this->label;
Expand All @@ -55,11 +58,18 @@ public function getAmount(): string
return $this->amount;
}

/**
* @return string
*/
public function getCurrency(): string
{
return $this->currency;
}

/**
* @param ShippingMethodInterface $shippingMethod
* @return string
*/
private function generateLabel(ShippingMethodInterface $shippingMethod): string
{
if ($shippingMethod->getCarrierTitle() === null && $shippingMethod->getMethodTitle() === null) {
Expand Down
122 changes: 18 additions & 104 deletions Service/Express/ExpressPaymentManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,46 +6,38 @@

use Magento\Checkout\Model\Type\Onepage;
use Magento\Customer\Api\Data\GroupInterface;
use Magento\Customer\Model\Session;
use Magento\Framework\Exception\InputException;
use Magento\Quote\Api\CartRepositoryInterface;
use Magento\Quote\Api\ShipmentEstimationInterface;
use Magento\Checkout\Api\Data\ShippingInformationInterfaceFactory;
use Magento\Checkout\Api\ShippingInformationManagementInterface;
use Magento\Quote\Model\Quote;
use Magento\Customer\Model\Session;
use Magento\Quote\Model\Quote\Address;
use Rvvup\Payments\Model\Express\ExpressShippingMethod;
use Rvvup\Payments\Service\Shipping\ShippingMethodService;

class ExpressPaymentManager
{

/** @var ShipmentEstimationInterface */
private $shipmentEstimation;

/** @var CartRepositoryInterface */
private $quoteRepository;

/** @var ShippingInformationInterfaceFactory */
private $shippingInformationFactory;

/** @var ShippingInformationManagementInterface */
private $shippingInformationManagement;

/** @var Session */
private $customerSession;

/** @var ShippingMethodService */
private $shippingMethodService;

/**
* @param CartRepositoryInterface $quoteRepository
* @param Session $customerSession
* @param ShippingMethodService $shippingMethodService
*/
public function __construct(
ShipmentEstimationInterface $shipmentEstimation,
CartRepositoryInterface $quoteRepository,
ShippingInformationInterfaceFactory $shippingInformationFactory,
ShippingInformationManagementInterface $shippingInformationManagement,
Session $customerSession
CartRepositoryInterface $quoteRepository,
Session $customerSession,
ShippingMethodService $shippingMethodService
) {
$this->shipmentEstimation = $shipmentEstimation;
$this->quoteRepository = $quoteRepository;
$this->shippingInformationFactory = $shippingInformationFactory;
$this->shippingInformationManagement = $shippingInformationManagement;
$this->customerSession = $customerSession;
$this->shippingMethodService = $shippingMethodService;
}

/**
Expand All @@ -65,9 +57,8 @@ public function updateShippingAddress(Quote $quote, array $address): array
->setPostcode($address['postcode'] ?? null)
->setCollectShippingRates(true);

$shippingMethods = $this->getAvailableShippingMethods($quote);
$methodId = empty($shippingMethods) ? null : $shippingMethods[0]->getId();
$this->setShippingMethodInQuote($quote, $methodId, $shippingAddress);
$shippingMethods = $this->shippingMethodService->setFirstShippingMethodInQuote($quote, $shippingAddress)
["availableShippingMethods"];

$quote->setTotalsCollectedFlag(false);
$quote->collectTotals();
Expand All @@ -78,59 +69,9 @@ public function updateShippingAddress(Quote $quote, array $address): array

/**
* @param Quote $quote
* @param string|null $methodId
* @param array $data
* @return Quote
*/
public function updateShippingMethod(
Quote $quote,
?string $methodId
): Quote {
$shippingAddress = $quote->getShippingAddress();
$quote = $this->setShippingMethodInQuote($quote, $methodId, $shippingAddress);

$quote->setTotalsCollectedFlag(false);
$quote->collectTotals();

$this->quoteRepository->save($quote);

return $quote;
}

/**
* @param Quote $quote
* @param string|null $methodId
* @param Address $shippingAddress
* @return Quote
* @throws InputException
*/
public function setShippingMethodInQuote(
Quote $quote,
?string $methodId,
Quote\Address $shippingAddress
): Quote {
$availableMethods = $this->getAvailableShippingMethods($quote);
$isMethodAvailable = count(array_filter($availableMethods, function ($method) use ($methodId) {
return $method->getId() === $methodId;
})) > 0;

$carrierCodeToMethodCode = empty($methodId) ? [] : explode('_', $methodId);

if (!$isMethodAvailable || count($carrierCodeToMethodCode) !== 2) {
$shippingAddress->setShippingMethod('');
} else {
$shippingAddress->setShippingMethod($methodId)->setCollectShippingRates(true)->collectShippingRates();

$this->shippingInformationManagement->saveAddressInformation(
$quote->getId(),
$this->shippingInformationFactory->create()
->setShippingAddress($shippingAddress)
->setShippingCarrierCode($carrierCodeToMethodCode[0])
->setShippingMethodCode($carrierCodeToMethodCode[1])
);
}
return $quote;
}

public function updateQuoteBeforePaymentAuth(Quote $quote, array $data): Quote
{
if (!$quote->isVirtual() &&
Expand Down Expand Up @@ -172,9 +113,7 @@ public function updateQuoteBeforePaymentAuth(Quote $quote, array $data): Quote
$selectedMethod = $shippingAddress->getShippingMethod();
// If the shipping method is not set then the first method was displayed in the sheet and was not changed
if (empty($selectedMethod)) {
$shippingMethods = $this->getAvailableShippingMethods($quote);
$methodId = empty($shippingMethods) ? null : $shippingMethods[0]->getId();
$this->setShippingMethodInQuote($quote, $methodId, $shippingAddress);
$this->shippingMethodService->setFirstShippingMethodInQuote($quote, $shippingAddress);
}

$quote->setTotalsCollectedFlag(false);
Expand All @@ -184,31 +123,6 @@ public function updateQuoteBeforePaymentAuth(Quote $quote, array $data): Quote
return $quote;
}

/**
* @param Quote $quote
* @return ExpressShippingMethod[] $shippingMethods
* @throws InputException
*/
public function getAvailableShippingMethods(Quote $quote): array
{
$shippingMethods = $this->shipmentEstimation->estimateByExtendedAddress(
$quote->getId(),
$quote->getShippingAddress()
);
if (empty($shippingMethods)) {
return [];
}
$returnedShippingMethods = [];
foreach ($shippingMethods as $shippingMethod) {
if ($shippingMethod->getErrorMessage()) {
continue;
}

$returnedShippingMethods[] = new ExpressShippingMethod($shippingMethod, $quote->getQuoteCurrencyCode());
}
return $returnedShippingMethods;
}

/**
* @param Address $quoteAddress
* @param array $contact
Expand Down
173 changes: 173 additions & 0 deletions Service/Express/ExpressPaymentRequestMapper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
<?php

declare(strict_types=1);

namespace Rvvup\Payments\Service\Express;

use Magento\Quote\Model\Quote;
use Magento\Quote\Model\Quote\Address;
use Rvvup\Payments\Model\Shipping\ShippingMethod;
use Rvvup\Payments\Service\Shipping\ShippingMethodService;

class ExpressPaymentRequestMapper
{

/** @var ShippingMethodService */
private $shippingMethodService;

/**
* @param ShippingMethodService $shippingMethodService
*/
public function __construct(ShippingMethodService $shippingMethodService)
{
$this->shippingMethodService = $shippingMethodService;
}

/**
* @param Quote $quote
* @return array returns object that can be passed into the Rvvup Sdk for Express Payment
*/
public function map(Quote $quote): array
{
$total = $quote->getGrandTotal();
$result = [
'methodOptions' => [
'APPLE_PAY' => $this->getApplePayOptions($quote)
],
'total' => [
'amount' => is_numeric($total) ? number_format((float)$total, 2, '.', '') : $total,
'currency' => $quote->getQuoteCurrencyCode()
],
'billing' => $this->mapAddress($quote->getBillingAddress()),
'shipping' => $this->mapShippingAddress($quote),
];
$result['shippingMethods'] = $this->getShippingMethods($quote, $result['shipping'] !== null);

// If methods are empty, need to choose a new address in the express sheet
if (empty($result['shippingMethods'])) {
$result['shipping'] = null;
}
return $result;
}

/**
* @param Quote $quote
* @param bool $hasShippingAddress
* @return array|null
*/
private function getShippingMethods(Quote $quote, bool $hasShippingAddress): ?array
{
if ($quote->isVirtual()) {
return null;
}
// If address is not present then shipping methods will appear after the address update
if (!$hasShippingAddress) {
return null;
}
$availableMethods = $this->shippingMethodService->getAvailableShippingMethods($quote);
$shippingMethods = $this->mapShippingMethods($availableMethods);
if (empty($shippingMethods)) {
return null;
}

$selectedMethod = $quote->getShippingAddress()->getShippingMethod();
if (empty($selectedMethod)) {
$shippingMethods[0]['selected'] = true;
} else {
$numShippingMethods = count($shippingMethods);
for ($i = 0; $i < $numShippingMethods; $i++) {
if ($shippingMethods[$i]['id'] === $selectedMethod) {
$shippingMethods[$i]['selected'] = true;
break;
}
}
}
return $shippingMethods;
}

/**
* @param ShippingMethod[] $shippingMethods
* @return array
*/
public function mapShippingMethods(array $shippingMethods): array
{
return array_reduce($shippingMethods, function ($carry, $method) {
$carry[] = [
'id' => $method->getId(),
'label' => $method->getLabel(),
'amount' => ['amount' => $method->getAmount(), 'currency' => $method->getCurrency()],
];
return $carry;
}, []);
}

/**
* @param Quote $quote
* @return array|array[]
*/
private function getApplePayOptions(Quote $quote): array
{
$options = [
'paymentRequest' => [
'requiredBillingContactFields' => ['postalAddress', 'name', 'email', 'phone'],
// Apple quirk - We need these "shipping" fields to fill the billing email and phone
'requiredShippingContactFields' => ['email', 'phone']
],
];
if (!$quote->isVirtual()) {
$options['paymentRequest']['requiredShippingContactFields'] = ['postalAddress', 'name', 'email', 'phone'];
$options['paymentRequest']['shippingType'] = 'shipping';
$options['paymentRequest']['shippingContactEditingMode'] = 'available';
}

return $options;
}

/**
* @param Address $quoteAddress
* @return array[]
*/
private function mapAddress(Quote\Address $quoteAddress): ?array
{
// We ignore country code because it's always pre-selected by magento.
// We also ignore region, city, postcode because apple partially sets this, if you cancel the sheet after a
// address change. We only pre-fill the apple sheet when the user has actively entered the other fields.
if ((!empty($quoteAddress->getStreet()) && !empty($quoteAddress->getStreet()[0])) ||
!empty($quoteAddress->getFirstname()) ||
!empty($quoteAddress->getLastname()) ||
!empty($quoteAddress->getEmail()) ||
!empty($quoteAddress->getTelephone())
) {
return [
'address' => [
'addressLines' => $quoteAddress->getStreet(),
'city' => $quoteAddress->getCity(),
'countryCode' => $quoteAddress->getCountryId(),
'postcode' => $quoteAddress->getPostcode(),
'state' => $quoteAddress->getRegion()
],
'contact' => [
'givenName' => $quoteAddress->getFirstname(),
'surname' => $quoteAddress->getLastname(),
'email' => $quoteAddress->getEmail(),
'phoneNumber' => $quoteAddress->getTelephone()
]
];
}

return null;
}

/**
* @param Quote $quote
* @return void
*/
private function mapShippingAddress(Quote $quote): ?array
{
if ($quote->isVirtual()) {
return null;
}
$quoteShippingAddress = $quote->getShippingAddress();
return $this->mapAddress($quoteShippingAddress);
}
}
Loading

0 comments on commit c4066a1

Please sign in to comment.