View source
<?php
namespace Drupal\commerce_paypal;
use Drupal\address\AddressInterface;
use Drupal\commerce_order\AdjustmentTransformerInterface;
use Drupal\commerce_order\Entity\OrderInterface;
use Drupal\commerce_paypal\Event\CheckoutOrderRequestEvent;
use Drupal\commerce_paypal\Event\PayPalEvents;
use Drupal\commerce_price\Calculator;
use Drupal\commerce_product\Entity\ProductVariationInterface;
use Drupal\Component\Datetime\TimeInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use GuzzleHttp\ClientInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
class CheckoutSdk implements CheckoutSdkInterface {
protected $client;
protected $adjustmentTransformer;
protected $eventDispatcher;
protected $moduleHandler;
protected $time;
protected $config;
public function __construct(ClientInterface $client, AdjustmentTransformerInterface $adjustment_transformer, EventDispatcherInterface $event_dispatcher, ModuleHandlerInterface $module_handler, TimeInterface $time, array $config) {
$this->client = $client;
$this->adjustmentTransformer = $adjustment_transformer;
$this->eventDispatcher = $event_dispatcher;
$this->moduleHandler = $module_handler;
$this->time = $time;
$this->config = $config;
}
public function getAccessToken() {
return $this->client
->post('/v1/oauth2/token', [
'auth' => [
$this->config['client_id'],
$this->config['secret'],
],
'form_params' => [
'grant_type' => 'client_credentials',
],
]);
}
public function getClientToken() {
return $this->client
->post('/v1/identity/generate-token', [
'headers' => [
'Content-Type' => 'application/json',
],
]);
}
public function createOrder(OrderInterface $order, AddressInterface $billing_address = NULL) {
$params = $this
->prepareOrderRequest($order, $billing_address);
$event = new CheckoutOrderRequestEvent($order, $params);
$this->eventDispatcher
->dispatch(PayPalEvents::CHECKOUT_CREATE_ORDER_REQUEST, $event);
return $this->client
->post('/v2/checkout/orders', [
'json' => $event
->getRequestBody(),
]);
}
public function getOrder($remote_id) {
return $this->client
->get(sprintf('/v2/checkout/orders/%s', $remote_id));
}
public function updateOrder($remote_id, OrderInterface $order) {
$params = $this
->prepareOrderRequest($order);
$update_params = [
[
'op' => 'replace',
'path' => "/purchase_units/@reference_id=='default'",
'value' => $params['purchase_units'][0],
],
];
$event = new CheckoutOrderRequestEvent($order, $update_params);
$this->eventDispatcher
->dispatch(PayPalEvents::CHECKOUT_UPDATE_ORDER_REQUEST, $event);
return $this->client
->patch(sprintf('/v2/checkout/orders/%s', $remote_id), [
'json' => $event
->getRequestBody(),
]);
}
public function authorizeOrder($remote_id) {
$headers = [
'Content-Type' => 'application/json',
];
return $this->client
->post(sprintf('/v2/checkout/orders/%s/authorize', $remote_id), [
'headers' => $headers,
]);
}
public function captureOrder($remote_id) {
$headers = [
'Content-Type' => 'application/json',
];
return $this->client
->post(sprintf('/v2/checkout/orders/%s/capture', $remote_id), [
'headers' => $headers,
]);
}
public function capturePayment($authorization_id, array $parameters = []) {
$options = [
'headers' => [
'Content-Type' => 'application/json',
],
];
if ($parameters) {
$options['json'] = $parameters;
}
return $this->client
->post(sprintf('/v2/payments/authorizations/%s/capture', $authorization_id), $options);
}
public function reAuthorizePayment($authorization_id, array $parameters = []) {
$options = [
'headers' => [
'Content-Type' => 'application/json',
],
];
if ($parameters) {
$options['json'] = $parameters;
}
return $this->client
->post(sprintf('/v2/payments/authorizations/%s/reauthorize', $authorization_id), $options);
}
public function refundPayment($capture_id, array $parameters = []) {
$options = [
'headers' => [
'Content-Type' => 'application/json',
],
];
if ($parameters) {
$options['json'] = $parameters;
}
return $this->client
->post(sprintf('/v2/payments/captures/%s/refund', $capture_id), $options);
}
public function voidPayment($authorization_id, array $parameters = []) {
$options = [
'headers' => [
'Content-Type' => 'application/json',
],
];
return $this->client
->post(sprintf('/v2/payments/authorizations/%s/void', $authorization_id), $options);
}
protected function prepareOrderRequest(OrderInterface $order, AddressInterface $billing_address = NULL) {
$items = [];
$item_total = NULL;
$tax_total = NULL;
foreach ($order
->getItems() as $order_item) {
$item_total = $item_total ? $item_total
->add($order_item
->getTotalPrice()) : $order_item
->getTotalPrice();
$item = [
'name' => mb_substr($order_item
->getTitle(), 0, 127),
'unit_amount' => [
'currency_code' => $order_item
->getUnitPrice()
->getCurrencyCode(),
'value' => Calculator::trim($order_item
->getUnitPrice()
->getNumber()),
],
'quantity' => intval($order_item
->getQuantity()),
];
$purchased_entity = $order_item
->getPurchasedEntity();
if ($purchased_entity instanceof ProductVariationInterface) {
$item['sku'] = mb_substr($purchased_entity
->getSku(), 0, 127);
}
$items[] = $item;
}
$adjustments = $order
->collectAdjustments();
$adjustments = $this->adjustmentTransformer
->processAdjustments($adjustments);
foreach ($adjustments as $adjustment) {
if ($adjustment
->isIncluded() || in_array($adjustment
->getType(), [
'tax',
'shipping',
'promotion',
])) {
continue;
}
$item_total = $item_total ? $item_total
->add($adjustment
->getAmount()) : $adjustment
->getAmount();
$items[] = [
'name' => mb_substr($adjustment
->getLabel(), 0, 127),
'unit_amount' => [
'currency_code' => $adjustment
->getAmount()
->getCurrencyCode(),
'value' => Calculator::trim($adjustment
->getAmount()
->getNumber()),
],
'quantity' => 1,
];
}
$breakdown = [
'item_total' => [
'currency_code' => $item_total
->getCurrencyCode(),
'value' => Calculator::trim($item_total
->getNumber()),
],
];
$tax_total = $this
->getAdjustmentsTotal($adjustments, [
'tax',
]);
if (!empty($tax_total)) {
$breakdown['tax_total'] = [
'currency_code' => $tax_total
->getCurrencyCode(),
'value' => Calculator::trim($tax_total
->getNumber()),
];
}
$shipping_total = $this
->getAdjustmentsTotal($adjustments, [
'shipping',
]);
if (!empty($shipping_total)) {
$breakdown['shipping'] = [
'currency_code' => $shipping_total
->getCurrencyCode(),
'value' => Calculator::trim($shipping_total
->getNumber()),
];
}
$promotion_total = $this
->getAdjustmentsTotal($adjustments, [
'promotion',
]);
if (!empty($promotion_total)) {
$breakdown['discount'] = [
'currency_code' => $promotion_total
->getCurrencyCode(),
'value' => Calculator::trim($promotion_total
->multiply(-1)
->getNumber()),
];
}
$payer = [];
if (!empty($order
->getEmail())) {
$payer['email_address'] = $order
->getEmail();
}
$profiles = $order
->collectProfiles();
if (!empty($billing_address)) {
$payer += static::formatAddress($billing_address);
}
elseif (isset($profiles['billing'])) {
$address = $profiles['billing']->address
->first();
if (!empty($address)) {
$payer += static::formatAddress($address);
}
}
$params = [
'intent' => strtoupper($this->config['intent']),
'purchase_units' => [
[
'reference_id' => 'default',
'custom_id' => $order
->id(),
'invoice_id' => $order
->id() . '-' . $this->time
->getRequestTime(),
'amount' => [
'currency_code' => $order
->getTotalPrice()
->getCurrencyCode(),
'value' => Calculator::trim($order
->getTotalPrice()
->getNumber()),
'breakdown' => $breakdown,
],
'items' => $items,
],
],
'application_context' => [
'brand_name' => mb_substr($order
->getStore()
->label(), 0, 127),
],
];
$shipping_address = [];
if (isset($profiles['shipping'])) {
$address = $profiles['shipping']->address
->first();
if (!empty($address)) {
$shipping_address = static::formatAddress($address, 'shipping');
}
}
$shipping_preference = $this->config['shipping_preference'];
if (!$this->moduleHandler
->moduleExists('commerce_shipping')) {
$shipping_preference = 'no_shipping';
}
else {
if ($shipping_preference == 'set_provided_address' && !$shipping_address) {
$shipping_preference = 'get_from_file';
}
}
if ($shipping_address && $shipping_preference !== 'no_shipping') {
$params['purchase_units'][0]['shipping'] = $shipping_address;
}
$params['application_context']['shipping_preference'] = strtoupper($shipping_preference);
if ($payer) {
$params['payer'] = $payer;
}
return $params;
}
protected function getAdjustmentsTotal(array $adjustments, array $adjustment_types = []) {
$adjustments_total = NULL;
$matching_adjustments = [];
foreach ($adjustments as $adjustment) {
if ($adjustment_types && !in_array($adjustment
->getType(), $adjustment_types)) {
continue;
}
if ($adjustment
->isIncluded()) {
continue;
}
$matching_adjustments[] = $adjustment;
}
if ($matching_adjustments) {
$matching_adjustments = $this->adjustmentTransformer
->processAdjustments($matching_adjustments);
foreach ($matching_adjustments as $adjustment) {
$adjustments_total = $adjustments_total ? $adjustments_total
->add($adjustment
->getAmount()) : $adjustment
->getAmount();
}
}
return $adjustments_total;
}
public static function formatAddress(AddressInterface $address, $type = 'billing') {
$return = [
'address' => [
'address_line_1' => $address
->getAddressLine1(),
'address_line_2' => $address
->getAddressLine2(),
'admin_area_2' => mb_substr($address
->getLocality(), 0, 120),
'admin_area_1' => $address
->getAdministrativeArea(),
'postal_code' => mb_substr($address
->getPostalCode(), 0, 60),
'country_code' => $address
->getCountryCode(),
],
];
if ($type == 'billing') {
$return['name'] = [
'given_name' => $address
->getGivenName(),
'surname' => $address
->getFamilyName(),
];
}
elseif ($type == 'shipping') {
$return['name'] = [
'full_name' => mb_substr($address
->getGivenName() . ' ' . $address
->getFamilyName(), 0, 300),
];
}
return $return;
}
}