move module.local to module

This commit is contained in:
David Remer 2020-09-01 14:21:58 +03:00
parent c443e4053c
commit f8e82bdfaf
217 changed files with 8 additions and 2 deletions

View file

@ -0,0 +1,25 @@
<?php
/**
* The bearer interface.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Authentication
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Authentication;
use Inpsyde\PayPalCommerce\ApiClient\Entity\Token;
/**
* Interface Bearer
*/
interface Bearer {
/**
* Returns the bearer.
*
* @return Token
*/
public function bearer(): Token;
}

View file

@ -0,0 +1,32 @@
<?php
/**
* The connect dummy bearer.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Authentication
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Authentication;
use Inpsyde\PayPalCommerce\ApiClient\Entity\Token;
/**
* Class ConnectBearer
*/
class ConnectBearer implements Bearer {
/**
* Returns the bearer.
*
* @return Token
*/
public function bearer(): Token {
$data = (object) array(
'created' => time(),
'expires_in' => 3600,
'token' => 'token',
);
return new Token( $data );
}
}

View file

@ -0,0 +1,142 @@
<?php
/**
* The PayPal bearer.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Authentication
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Authentication;
use Inpsyde\PayPalCommerce\ApiClient\Endpoint\RequestTrait;
use Inpsyde\PayPalCommerce\ApiClient\Entity\Token;
use Inpsyde\PayPalCommerce\ApiClient\Exception\RuntimeException;
use Psr\Log\LoggerInterface;
use Psr\SimpleCache\CacheInterface;
/**
* Class PayPalBearer
*/
class PayPalBearer implements Bearer {
use RequestTrait;
public const CACHE_KEY = 'ppcp-bearer';
/**
* The cache.
*
* @var CacheInterface
*/
private $cache;
/**
* The host.
*
* @var string
*/
private $host;
/**
* The client key.
*
* @var string
*/
private $key;
/**
* The client secret.
*
* @var string
*/
private $secret;
/**
* The logger.
*
* @var LoggerInterface
*/
private $logger;
/**
* PayPalBearer constructor.
*
* @param CacheInterface $cache The cache.
* @param string $host The host.
* @param string $key The key.
* @param string $secret The secret.
* @param LoggerInterface $logger The logger.
*/
public function __construct(
CacheInterface $cache,
string $host,
string $key,
string $secret,
LoggerInterface $logger
) {
$this->cache = $cache;
$this->host = $host;
$this->key = $key;
$this->secret = $secret;
$this->logger = $logger;
}
/**
* Returns a bearer token.
*
* @return Token
* @throws \Psr\SimpleCache\InvalidArgumentException When cache is invalid.
* @throws RuntimeException When request fails.
*/
public function bearer(): Token {
try {
$bearer = Token::from_json( (string) $this->cache->get( self::CACHE_KEY ) );
return ( $bearer->is_valid() ) ? $bearer : $this->newBearer();
} catch ( RuntimeException $error ) {
return $this->newBearer();
}
}
/**
* Creates a new bearer token.
*
* @return Token
* @throws \Psr\SimpleCache\InvalidArgumentException When cache is invalid.
* @throws RuntimeException When request fails.
*/
private function newBearer(): Token {
$url = trailingslashit( $this->host ) . 'v1/oauth2/token?grant_type=client_credentials';
$args = array(
'method' => 'POST',
'headers' => array(
// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
'Authorization' => 'Basic ' . base64_encode( $this->key . ':' . $this->secret ),
),
);
$response = $this->request(
$url,
$args
);
if ( is_wp_error( $response ) || wp_remote_retrieve_response_code( $response ) !== 200 ) {
$error = new RuntimeException(
__( 'Could not create token.', 'paypal-for-woocommerce' )
);
$this->logger->log(
'warning',
$error->getMessage(),
array(
'args' => $args,
'response' => $response,
)
);
throw $error;
}
$token = Token::from_json( $response['body'] );
$this->cache->set( self::CACHE_KEY, $token->as_json() );
return $token;
}
}

View file

@ -0,0 +1,133 @@
<?php
/**
* Fetches identity tokens.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Endpoint
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Endpoint;
use Inpsyde\PayPalCommerce\ApiClient\Authentication\Bearer;
use Inpsyde\PayPalCommerce\ApiClient\Entity\Token;
use Inpsyde\PayPalCommerce\ApiClient\Exception\PayPalApiException;
use Inpsyde\PayPalCommerce\ApiClient\Exception\RuntimeException;
use Psr\Log\LoggerInterface;
/**
* Class IdentityToken
*/
class IdentityToken {
use RequestTrait;
/**
* The Bearer.
*
* @var Bearer
*/
private $bearer;
/**
* The host.
*
* @var string
*/
private $host;
/**
* The logger.
*
* @var LoggerInterface
*/
private $logger;
/**
* The prefix.
*
* @var string
*/
private $prefix;
/**
* IdentityToken constructor.
*
* @param string $host The host.
* @param Bearer $bearer The bearer.
* @param LoggerInterface $logger The logger.
* @param string $prefix The prefix.
*/
public function __construct( string $host, Bearer $bearer, LoggerInterface $logger, string $prefix ) {
$this->host = $host;
$this->bearer = $bearer;
$this->logger = $logger;
$this->prefix = $prefix;
}
/**
* Generates a token for a specific customer.
*
* @param int $customer_id The id of the customer.
*
* @return Token
* @throws RuntimeException If the request fails.
*/
public function generate_for_customer( int $customer_id ): Token {
$bearer = $this->bearer->bearer();
$url = trailingslashit( $this->host ) . 'v1/identity/generate-token';
$args = array(
'method' => 'POST',
'headers' => array(
'Authorization' => 'Bearer ' . $bearer->token(),
'Content-Type' => 'application/json',
),
);
if ( $customer_id && defined( 'PPCP_FLAG_SUBSCRIPTION' ) && PPCP_FLAG_SUBSCRIPTION ) {
$args['body'] = wp_json_encode( array( 'customer_id' => $this->prefix . $customer_id ) );
}
$response = $this->request( $url, $args );
if ( is_wp_error( $response ) ) {
$error = new RuntimeException(
__(
'Could not create identity token.',
'paypal-for-woocommerce'
)
);
$this->logger->log(
'warning',
$error->getMessage(),
array(
'args' => $args,
'response' => $response,
)
);
throw $error;
}
$json = json_decode( $response['body'] );
$status_code = (int) wp_remote_retrieve_response_code( $response );
if ( 200 !== $status_code ) {
$error = new PayPalApiException(
$json,
$status_code
);
$this->logger->log(
'warning',
$error->getMessage(),
array(
'args' => $args,
'response' => $response,
)
);
throw $error;
}
$token = Token::from_json( $response['body'] );
return $token;
}
}

View file

@ -0,0 +1,196 @@
<?php
/**
* Fetches credentials for an instance.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Endpoint
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Endpoint;
use Inpsyde\PayPalCommerce\ApiClient\Exception\PayPalApiException;
use Inpsyde\PayPalCommerce\ApiClient\Exception\RuntimeException;
use Psr\Log\LoggerInterface;
/**
* Class LoginSeller
*/
class LoginSeller {
use RequestTrait;
/**
* The host.
*
* @var string
*/
private $host;
/**
* The partner merchant id.
*
* @var string
*/
private $partner_merchant_id;
/**
* The logger.
*
* @var LoggerInterface
*/
private $logger;
/**
* LoginSeller constructor.
*
* @param string $host The host.
* @param string $partner_marchant_id The partner merchant id.
* @param LoggerInterface $logger The logger.
*/
public function __construct(
string $host,
string $partner_marchant_id,
LoggerInterface $logger
) {
$this->host = $host;
$this->partner_merchant_id = $partner_marchant_id;
$this->logger = $logger;
}
/**
* Fetches credentials for a shared id, auth code and seller nonce.
*
* @param string $shared_id The shared id.
* @param string $auth_code The auth code.
* @param string $seller_nonce The seller nonce.
*
* @return \stdClass
* @throws RuntimeException If the request fails.
*/
public function credentials_for(
string $shared_id,
string $auth_code,
string $seller_nonce
): \stdClass {
$token = $this->generate_token_for( $shared_id, $auth_code, $seller_nonce );
$url = trailingslashit( $this->host ) .
'v1/customer/partners/' . $this->partner_merchant_id .
'/merchant-integrations/credentials/';
$args = array(
'method' => 'GET',
'headers' => array(
'Authorization' => 'Bearer ' . $token,
'Content-Type' => 'application/json',
),
);
$response = $this->request( $url, $args );
if ( is_wp_error( $response ) ) {
$error = new RuntimeException(
__( 'Could not fetch credentials.', 'paypal-for-woocommerce' )
);
$this->logger->log(
'warning',
$error->getMessage(),
array(
'args' => $args,
'response' => $response,
)
);
throw $error;
}
$json = json_decode( $response['body'] );
$status_code = (int) wp_remote_retrieve_response_code( $response );
if ( ! isset( $json->client_id ) || ! isset( $json->client_secret ) ) {
$error = isset( $json->details ) ?
new PayPalApiException(
$json,
$status_code
) : new RuntimeException(
__( 'Credentials not found.', 'paypal-for-woocommerce' )
);
$this->logger->log(
'warning',
$error->getMessage(),
array(
'args' => $args,
'response' => $response,
)
);
throw $error;
}
return $json;
}
/**
* Generates a token for a shared id and auth token and seller nonce.
*
* @param string $shared_id The shared id.
* @param string $auth_code The auth code.
* @param string $seller_nonce The seller nonce.
*
* @return string
* @throws RuntimeException If the request fails.
*/
private function generate_token_for(
string $shared_id,
string $auth_code,
string $seller_nonce
): string {
$url = trailingslashit( $this->host ) . 'v1/oauth2/token/';
$args = array(
'method' => 'POST',
'headers' => array(
// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
'Authorization' => 'Basic ' . base64_encode( $shared_id . ':' ),
),
'body' => array(
'grant_type' => 'authorization_code',
'code' => $auth_code,
'code_verifier' => $seller_nonce,
),
);
$response = $this->request( $url, $args );
if ( is_wp_error( $response ) ) {
$error = new RuntimeException(
__( 'Could not create token.', 'paypal-for-woocommerce' )
);
$this->logger->log(
'warning',
$error->getMessage(),
array(
'args' => $args,
'response' => $response,
)
);
throw $error;
}
$json = json_decode( $response['body'] );
$status_code = (int) wp_remote_retrieve_response_code( $response );
if ( ! isset( $json->access_token ) ) {
$error = isset( $json->details ) ?
new PayPalApiException(
$json,
$status_code
) : new RuntimeException(
__( 'No token found.', 'paypal-for-woocommerce' )
);
$this->logger->log(
'warning',
$error->getMessage(),
array(
'args' => $args,
'response' => $response,
)
);
throw $error;
}
return (string) $json->access_token;
}
}

View file

@ -0,0 +1,538 @@
<?php
/**
* The order endpoint.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Endpoint
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Endpoint;
use Inpsyde\PayPalCommerce\ApiClient\Authentication\Bearer;
use Inpsyde\PayPalCommerce\ApiClient\Entity\ApplicationContext;
use Inpsyde\PayPalCommerce\ApiClient\Entity\Order;
use Inpsyde\PayPalCommerce\ApiClient\Entity\OrderStatus;
use Inpsyde\PayPalCommerce\ApiClient\Entity\Payer;
use Inpsyde\PayPalCommerce\ApiClient\Entity\PaymentMethod;
use Inpsyde\PayPalCommerce\ApiClient\Entity\PaymentToken;
use Inpsyde\PayPalCommerce\ApiClient\Entity\PurchaseUnit;
use Inpsyde\PayPalCommerce\ApiClient\Exception\PayPalApiException;
use Inpsyde\PayPalCommerce\ApiClient\Exception\RuntimeException;
use Inpsyde\PayPalCommerce\ApiClient\Factory\OrderFactory;
use Inpsyde\PayPalCommerce\ApiClient\Factory\PatchCollectionFactory;
use Inpsyde\PayPalCommerce\ApiClient\Helper\ErrorResponse;
use Inpsyde\PayPalCommerce\ApiClient\Repository\ApplicationContextRepository;
use Inpsyde\PayPalCommerce\ApiClient\Repository\PayPalRequestIdRepository;
use Psr\Log\LoggerInterface;
/**
* Class OrderEndpoint
*/
class OrderEndpoint {
use RequestTrait;
/**
* The host.
*
* @var string
*/
private $host;
/**
* The bearer.
*
* @var Bearer
*/
private $bearer;
/**
* The order factory.
*
* @var OrderFactory
*/
private $order_factory;
/**
* The patch collection factory.
*
* @var PatchCollectionFactory
*/
private $patch_collection_factory;
/**
* The intent.
*
* @var string
*/
private $intent;
/**
* The logger.
*
* @var LoggerInterface
*/
private $logger;
/**
* The application context repository.
*
* @var ApplicationContextRepository
*/
private $application_context_repository;
/**
* The BN Code.
*
* @var string
*/
private $bn_code;
/**
* The paypal request id repository.
*
* @var PayPalRequestIdRepository
*/
private $paypal_request_id_repository;
/**
* OrderEndpoint constructor.
*
* @param string $host The host.
* @param Bearer $bearer The bearer.
* @param OrderFactory $order_factory The order factory.
* @param PatchCollectionFactory $patch_collection_factory The patch collection factory.
* @param string $intent The intent.
* @param LoggerInterface $logger The logger.
* @param ApplicationContextRepository $application_context_repository The application context repository.
* @param PayPalRequestIdRepository $paypal_request_id_repository The paypal request id repository.
* @param string $bn_code The BN Code.
*/
public function __construct(
string $host,
Bearer $bearer,
OrderFactory $order_factory,
PatchCollectionFactory $patch_collection_factory,
string $intent,
LoggerInterface $logger,
ApplicationContextRepository $application_context_repository,
PayPalRequestIdRepository $paypal_request_id_repository,
string $bn_code = ''
) {
$this->host = $host;
$this->bearer = $bearer;
$this->order_factory = $order_factory;
$this->patch_collection_factory = $patch_collection_factory;
$this->intent = $intent;
$this->logger = $logger;
$this->application_context_repository = $application_context_repository;
$this->bn_code = $bn_code;
$this->paypal_request_id_repository = $paypal_request_id_repository;
}
/**
* Changes the used BN Code.
*
* @param string $bn_code The new BN Code to use.
*
* @return OrderEndpoint
* @throws RuntimeException If the request fails.
*/
public function with_bn_code( string $bn_code ): OrderEndpoint {
$this->bn_code = $bn_code;
return $this;
}
/**
* Creates an order.
*
* @param PurchaseUnit[] $items The purchase unit items for the order.
* @param Payer|null $payer The payer off the order.
* @param PaymentToken|null $payment_token The payment token.
* @param PaymentMethod|null $payment_method The payment method.
* @param string $paypal_request_id The paypal request id.
*
* @return Order
* @throws RuntimeException If the request fails.
*/
public function create(
array $items,
Payer $payer = null,
PaymentToken $payment_token = null,
PaymentMethod $payment_method = null,
string $paypal_request_id = ''
): Order {
$contains_physical_goods = false;
$items = array_filter(
$items,
static function ( $item ) use ( &$contains_physical_goods ): bool {
$is_purchase_unit = is_a( $item, PurchaseUnit::class );
/**
* A purchase unit.
*
* @var PurchaseUnit $item
*/
if ( $is_purchase_unit && $item->contains_physical_goods() ) {
$contains_physical_goods = true;
}
return $is_purchase_unit;
}
);
$shipping_preferences = $contains_physical_goods
? ApplicationContext::SHIPPING_PREFERENCE_GET_FROM_FILE
: ApplicationContext::SHIPPING_PREFERENCE_NO_SHIPPING;
$bearer = $this->bearer->bearer();
$data = array(
'intent' => $this->intent,
'purchase_units' => array_map(
static function ( PurchaseUnit $item ): array {
return $item->to_array();
},
$items
),
'application_context' => $this->application_context_repository
->current_context( $shipping_preferences )->to_array(),
);
if ( $payer ) {
$data['payer'] = $payer->to_array();
}
if ( $payment_token ) {
$data['payment_source']['token'] = $payment_token->to_array();
}
if ( $payment_method ) {
$data['payment_method'] = $payment_method->to_array();
}
$url = trailingslashit( $this->host ) . 'v2/checkout/orders';
$args = array(
'method' => 'POST',
'headers' => array(
'Authorization' => 'Bearer ' . $bearer->token(),
'Content-Type' => 'application/json',
'Prefer' => 'return=representation',
),
'body' => wp_json_encode( $data ),
);
$paypal_request_id = $paypal_request_id ? $paypal_request_id : uniqid( 'ppcp-', true );
$args['headers']['PayPal-Request-Id'] = $paypal_request_id;
if ( $this->bn_code ) {
$args['headers']['PayPal-Partner-Attribution-Id'] = $this->bn_code;
}
$response = $this->request( $url, $args );
if ( is_wp_error( $response ) ) {
$error = new RuntimeException(
__( 'Could not create order.', 'paypal-for-woocommerce' )
);
$this->logger->log(
'warning',
$error->getMessage(),
array(
'args' => $args,
'response' => $response,
)
);
throw $error;
}
$json = json_decode( $response['body'] );
$status_code = (int) wp_remote_retrieve_response_code( $response );
if ( 201 !== $status_code ) {
$error = new PayPalApiException(
$json,
$status_code
);
$this->logger->log(
'warning',
$error->getMessage(),
array(
'args' => $args,
'response' => $response,
)
);
throw $error;
}
$order = $this->order_factory->from_paypal_response( $json );
$this->paypal_request_id_repository->set_for_order( $order, $paypal_request_id );
return $order;
}
/**
* Captures an order.
*
* @param Order $order The order.
*
* @return Order
* @throws RuntimeException If the request fails.
*/
public function capture( Order $order ): Order {
if ( $order->status()->is( OrderStatus::COMPLETED ) ) {
return $order;
}
$bearer = $this->bearer->bearer();
$url = trailingslashit( $this->host ) . 'v2/checkout/orders/' . $order->id() . '/capture';
$args = array(
'method' => 'POST',
'headers' => array(
'Authorization' => 'Bearer ' . $bearer->token(),
'Content-Type' => 'application/json',
'Prefer' => 'return=representation',
'PayPal-Request-Id' => $this->paypal_request_id_repository->get_for_order( $order ),
),
);
if ( $this->bn_code ) {
$args['headers']['PayPal-Partner-Attribution-Id'] = $this->bn_code;
}
$response = $this->request( $url, $args );
if ( is_wp_error( $response ) ) {
$error = new RuntimeException(
__( 'Could not capture order.', 'paypal-for-woocommerce' )
);
$this->logger->log(
'warning',
$error->getMessage(),
array(
'args' => $args,
'response' => $response,
)
);
throw $error;
}
$json = json_decode( $response['body'] );
$status_code = (int) wp_remote_retrieve_response_code( $response );
if ( 201 !== $status_code ) {
$error = new PayPalApiException(
$json,
$status_code
);
// If the order has already been captured, we return the updated order.
if ( strpos( $response['body'], ErrorResponse::ORDER_ALREADY_CAPTURED ) !== false ) {
return $this->order( $order->id() );
}
$this->logger->log(
'warning',
$error->getMessage(),
array(
'args' => $args,
'response' => $response,
)
);
throw $error;
}
$order = $this->order_factory->from_paypal_response( $json );
return $order;
}
/**
* Authorize an order.
*
* @param Order $order The order.
*
* @return Order
* @throws RuntimeException If the request fails.
*/
public function authorize( Order $order ): Order {
$bearer = $this->bearer->bearer();
$url = trailingslashit( $this->host ) . 'v2/checkout/orders/' . $order->id() . '/authorize';
$args = array(
'method' => 'POST',
'headers' => array(
'Authorization' => 'Bearer ' . $bearer->token(),
'Content-Type' => 'application/json',
'Prefer' => 'return=representation',
'PayPal-Request-Id' => $this->paypal_request_id_repository->get_for_order( $order ),
),
);
if ( $this->bn_code ) {
$args['headers']['PayPal-Partner-Attribution-Id'] = $this->bn_code;
}
$response = $this->request( $url, $args );
if ( is_wp_error( $response ) ) {
$error = new RuntimeException(
__(
'Could not authorize order.',
'paypal-for-woocommerce'
)
);
$this->logger->log(
'warning',
$error->getMessage(),
array(
'args' => $args,
'response' => $response,
)
);
throw $error;
}
$json = json_decode( $response['body'] );
$status_code = (int) wp_remote_retrieve_response_code( $response );
if ( 201 !== $status_code ) {
if ( false !== strpos( $response['body'], ErrorResponse::ORDER_ALREADY_AUTHORIZED ) ) {
return $this->order( $order->id() );
}
$error = new PayPalApiException(
$json,
$status_code
);
$this->logger->log(
'warning',
$error->getMessage(),
array(
'args' => $args,
'response' => $response,
)
);
throw $error;
}
$order = $this->order_factory->from_paypal_response( $json );
return $order;
}
/**
* Fetches an order for a given ID.
*
* @param string $id The ID.
*
* @return Order
* @throws RuntimeException If the request fails.
*/
public function order( string $id ): Order {
$bearer = $this->bearer->bearer();
$url = trailingslashit( $this->host ) . 'v2/checkout/orders/' . $id;
$args = array(
'headers' => array(
'Authorization' => 'Bearer ' . $bearer->token(),
'Content-Type' => 'application/json',
'PayPal-Request-Id' => $this->paypal_request_id_repository->get_for_order_id( $id ),
),
);
if ( $this->bn_code ) {
$args['headers']['PayPal-Partner-Attribution-Id'] = $this->bn_code;
}
$response = $this->request( $url, $args );
if ( is_wp_error( $response ) ) {
$error = new RuntimeException(
__( 'Could not retrieve order.', 'paypal-for-woocommerce' )
);
$this->logger->log(
'warning',
$error->getMessage(),
array(
'args' => $args,
'response' => $response,
)
);
throw $error;
}
$json = json_decode( $response['body'] );
$status_code = (int) wp_remote_retrieve_response_code( $response );
if ( 404 === $status_code || empty( $response['body'] ) ) {
$error = new RuntimeException(
__( 'Could not retrieve order.', 'paypal-for-woocommerce' ),
404
);
$this->logger->log(
'warning',
$error->getMessage(),
array(
'args' => $args,
'response' => $response,
)
);
throw $error;
}
if ( 200 !== $status_code ) {
$error = new PayPalApiException(
$json,
$status_code
);
$this->logger->log(
'warning',
$error->getMessage(),
array(
'args' => $args,
'response' => $response,
)
);
throw $error;
}
$order = $this->order_factory->from_paypal_response( $json );
return $order;
}
/**
* Patches an order.
*
* @param Order $order_to_update The order to patch.
* @param Order $order_to_compare The target order.
*
* @return Order
* @throws RuntimeException If the request fails.
*/
public function patch_order_with( Order $order_to_update, Order $order_to_compare ): Order {
$patches = $this->patch_collection_factory->from_orders( $order_to_update, $order_to_compare );
if ( ! count( $patches->patches() ) ) {
return $order_to_update;
}
$bearer = $this->bearer->bearer();
$url = trailingslashit( $this->host ) . 'v2/checkout/orders/' . $order_to_update->id();
$args = array(
'method' => 'PATCH',
'headers' => array(
'Authorization' => 'Bearer ' . $bearer->token(),
'Content-Type' => 'application/json',
'Prefer' => 'return=representation',
'PayPal-Request-Id' => $this->paypal_request_id_repository->get_for_order(
$order_to_update
),
),
'body' => wp_json_encode( $patches->to_array() ),
);
if ( $this->bn_code ) {
$args['headers']['PayPal-Partner-Attribution-Id'] = $this->bn_code;
}
$response = $this->request( $url, $args );
if ( is_wp_error( $response ) ) {
$error = new RuntimeException(
__( 'Could not retrieve order.', 'paypal-for-woocommerce' )
);
$this->logger->log(
'warning',
$error->getMessage(),
array(
'args' => $args,
'response' => $response,
)
);
throw $error;
}
$json = json_decode( $response['body'] );
$status_code = (int) wp_remote_retrieve_response_code( $response );
if ( 204 !== $status_code ) {
$error = new PayPalApiException(
$json,
$status_code
);
$this->logger->log(
'warning',
$error->getMessage(),
array(
'args' => $args,
'response' => $response,
)
);
throw $error;
}
$new_order = $this->order( $order_to_update->id() );
return $new_order;
}
}

View file

@ -0,0 +1,146 @@
<?php
/**
* The partner referrals endpoint.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Endpoint
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Endpoint;
use Inpsyde\PayPalCommerce\ApiClient\Authentication\Bearer;
use Inpsyde\PayPalCommerce\ApiClient\Exception\PayPalApiException;
use Inpsyde\PayPalCommerce\ApiClient\Exception\RuntimeException;
use Inpsyde\PayPalCommerce\ApiClient\Repository\PartnerReferralsData;
use Psr\Log\LoggerInterface;
/**
* Class PartnerReferrals
*/
class PartnerReferrals {
use RequestTrait;
/**
* The host.
*
* @var string
*/
private $host;
/**
* The bearer.
*
* @var Bearer
*/
private $bearer;
/**
* The PartnerReferralsData.
*
* @var PartnerReferralsData
*/
private $data;
/**
* The logger.
*
* @var LoggerInterface
*/
private $logger;
/**
* PartnerReferrals constructor.
*
* @param string $host The host.
* @param Bearer $bearer The bearer.
* @param PartnerReferralsData $data The partner referrals data.
* @param LoggerInterface $logger The logger.
*/
public function __construct(
string $host,
Bearer $bearer,
PartnerReferralsData $data,
LoggerInterface $logger
) {
$this->host = $host;
$this->bearer = $bearer;
$this->data = $data;
$this->logger = $logger;
}
/**
* Fetch the signup link.
*
* @return string
* @throws RuntimeException If the request fails.
*/
public function signup_link(): string {
$data = $this->data->data();
$bearer = $this->bearer->bearer();
$args = array(
'method' => 'POST',
'headers' => array(
'Authorization' => 'Bearer ' . $bearer->token(),
'Content-Type' => 'application/json',
'Prefer' => 'return=representation',
),
'body' => wp_json_encode( $data ),
);
$url = trailingslashit( $this->host ) . 'v2/customer/partner-referrals';
$response = $this->request( $url, $args );
if ( is_wp_error( $response ) ) {
$error = new RuntimeException(
__( 'Could not create referral.', 'paypal-for-woocommerce' )
);
$this->logger->log(
'warning',
$error->getMessage(),
array(
'args' => $args,
'response' => $response,
)
);
throw $error;
}
$json = json_decode( $response['body'] );
$status_code = (int) wp_remote_retrieve_response_code( $response );
if ( 201 !== $status_code ) {
$error = new PayPalApiException(
$json,
$status_code
);
$this->logger->log(
'warning',
$error->getMessage(),
array(
'args' => $args,
'response' => $response,
)
);
throw $error;
}
foreach ( $json->links as $link ) {
if ( 'action_url' === $link->rel ) {
return (string) $link->href;
}
}
$error = new RuntimeException(
__( 'Action URL not found.', 'paypal-for-woocommerce' )
);
$this->logger->log(
'warning',
$error->getMessage(),
array(
'args' => $args,
'response' => $response,
)
);
throw $error;
}
}

View file

@ -0,0 +1,191 @@
<?php
/**
* The payments endpoint.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Endpoint
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Endpoint;
use Inpsyde\PayPalCommerce\ApiClient\Authentication\Bearer;
use Inpsyde\PayPalCommerce\ApiClient\Entity\Authorization;
use Inpsyde\PayPalCommerce\ApiClient\Exception\PayPalApiException;
use Inpsyde\PayPalCommerce\ApiClient\Exception\RuntimeException;
use Inpsyde\PayPalCommerce\ApiClient\Factory\AuthorizationFactory;
use Psr\Log\LoggerInterface;
/**
* Class PaymentsEndpoint
*/
class PaymentsEndpoint {
use RequestTrait;
/**
* The host.
*
* @var string
*/
private $host;
/**
* The bearer.
*
* @var Bearer
*/
private $bearer;
/**
* The authorization factory.
*
* @var AuthorizationFactory
*/
private $authorizations_factory;
/**
* The logger.
*
* @var LoggerInterface
*/
private $logger;
/**
* PaymentsEndpoint constructor.
*
* @param string $host The host.
* @param Bearer $bearer The bearer.
* @param AuthorizationFactory $authorization_factory The authorization factory.
* @param LoggerInterface $logger The logger.
*/
public function __construct(
string $host,
Bearer $bearer,
AuthorizationFactory $authorization_factory,
LoggerInterface $logger
) {
$this->host = $host;
$this->bearer = $bearer;
$this->authorizations_factory = $authorization_factory;
$this->logger = $logger;
}
/**
* Fetch an authorization by a given id.
*
* @param string $authorization_id The id.
*
* @return Authorization
* @throws RuntimeException If the request fails.
*/
public function authorization( string $authorization_id ): Authorization {
$bearer = $this->bearer->bearer();
$url = trailingslashit( $this->host ) . 'v2/payments/authorizations/' . $authorization_id;
$args = array(
'headers' => array(
'Authorization' => 'Bearer ' . $bearer->token(),
'Content-Type' => 'application/json',
'Prefer' => 'return=representation',
),
);
$response = $this->request( $url, $args );
$json = json_decode( $response['body'] );
if ( is_wp_error( $response ) ) {
$error = new RuntimeException(
__( 'Could not get authorized payment info.', 'paypal-for-woocommerce' )
);
$this->logger->log(
'warning',
$error->getMessage(),
array(
'args' => $args,
'response' => $response,
)
);
throw $error;
}
$status_code = (int) wp_remote_retrieve_response_code( $response );
if ( 200 !== $status_code ) {
$error = new PayPalApiException(
$json,
$status_code
);
$this->logger->log(
'warning',
$error->getMessage(),
array(
'args' => $args,
'response' => $response,
)
);
throw $error;
}
$authorization = $this->authorizations_factory->from_paypal_response( $json );
return $authorization;
}
/**
* Capture an authorization by a given ID.
*
* @param string $authorization_id The id.
*
* @return Authorization
* @throws RuntimeException If the request fails.
*/
public function capture( string $authorization_id ): Authorization {
$bearer = $this->bearer->bearer();
$url = trailingslashit( $this->host ) . 'v2/payments/authorizations/' . $authorization_id . '/capture';
$args = array(
'method' => 'POST',
'headers' => array(
'Authorization' => 'Bearer ' . $bearer->token(),
'Content-Type' => 'application/json',
'Prefer' => 'return=representation',
),
);
$response = $this->request( $url, $args );
$json = json_decode( $response['body'] );
if ( is_wp_error( $response ) ) {
$error = new RuntimeException(
__( 'Could not capture authorized payment.', 'paypal-for-woocommerce' )
);
$this->logger->log(
'warning',
$error->getMessage(),
array(
'args' => $args,
'response' => $response,
)
);
throw $error;
}
$status_code = (int) wp_remote_retrieve_response_code( $response );
if ( 201 !== $status_code ) {
$error = new PayPalApiException(
$json,
$status_code
);
$this->logger->log(
'warning',
$error->getMessage(),
array(
'args' => $args,
'response' => $response,
)
);
throw $error;
}
$authorization = $this->authorizations_factory->from_paypal_response( $json );
return $authorization;
}
}

View file

@ -0,0 +1,203 @@
<?php
/**
* The payment token endpoint.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Endpoint
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Endpoint;
use Inpsyde\PayPalCommerce\ApiClient\Authentication\Bearer;
use Inpsyde\PayPalCommerce\ApiClient\Entity\PaymentToken;
use Inpsyde\PayPalCommerce\ApiClient\Exception\PayPalApiException;
use Inpsyde\PayPalCommerce\ApiClient\Exception\RuntimeException;
use Inpsyde\PayPalCommerce\ApiClient\Factory\PaymentTokenFactory;
use Psr\Log\LoggerInterface;
/**
* Class PaymentTokenEndpoint
*/
class PaymentTokenEndpoint {
use RequestTrait;
/**
* The bearer.
*
* @var Bearer
*/
private $bearer;
/**
* The host.
*
* @var string
*/
private $host;
/**
* The payment token factory.
*
* @var PaymentTokenFactory
*/
private $factory;
/**
* The logger.
*
* @var LoggerInterface
*/
private $logger;
/**
* The prefix.
*
* @var string
*/
private $prefix;
/**
* PaymentTokenEndpoint constructor.
*
* @param string $host The host.
* @param Bearer $bearer The bearer.
* @param PaymentTokenFactory $factory The payment token factory.
* @param LoggerInterface $logger The logger.
* @param string $prefix The prefix.
*/
public function __construct(
string $host,
Bearer $bearer,
PaymentTokenFactory $factory,
LoggerInterface $logger,
string $prefix
) {
$this->host = $host;
$this->bearer = $bearer;
$this->factory = $factory;
$this->logger = $logger;
$this->prefix = $prefix;
}
/**
* Returns the payment tokens for a user.
*
* @param int $id The user id.
*
* @return PaymentToken[]
* @throws RuntimeException If the request fails.
*/
public function for_user( int $id ): array {
$bearer = $this->bearer->bearer();
$customer_id = $this->prefix . $id;
$url = trailingslashit( $this->host ) . 'v2/vault/payment-tokens/?customer_id=' . $customer_id;
$args = array(
'method' => 'GET',
'headers' => array(
'Authorization' => 'Bearer ' . $bearer->token(),
'Content-Type' => 'application/json',
),
);
$response = $this->request( $url, $args );
if ( is_wp_error( $response ) ) {
$error = new RuntimeException(
__( 'Could not fetch payment token.', 'paypal-for-woocommerce' )
);
$this->logger->log(
'warning',
$error->getMessage(),
array(
'args' => $args,
'response' => $response,
)
);
throw $error;
}
$json = json_decode( $response['body'] );
$status_code = (int) wp_remote_retrieve_response_code( $response );
if ( 200 !== $status_code ) {
$error = new PayPalApiException(
$json,
$status_code
);
$this->logger->log(
'warning',
$error->getMessage(),
array(
'args' => $args,
'response' => $response,
)
);
throw $error;
}
$tokens = array();
foreach ( $json->payment_tokens as $token_value ) {
$tokens[] = $this->factory->from_paypal_response( $token_value );
}
if ( empty( $tokens ) ) {
$error = new RuntimeException(
sprintf(
// translators: %d is the customer id.
__( 'No token stored for customer %d.', 'paypal-for-woocommerce' ),
$id
)
);
$this->logger->log(
'warning',
$error->getMessage(),
array(
'args' => $args,
'response' => $response,
)
);
throw $error;
}
return $tokens;
}
/**
* Deletes a payment token.
*
* @param PaymentToken $token The token to delete.
*
* @return bool
* @throws RuntimeException If the request fails.
*/
public function delete_token( PaymentToken $token ): bool {
$bearer = $this->bearer->bearer();
$url = trailingslashit( $this->host ) . 'v2/vault/payment-tokens/' . $token->id();
$args = array(
'method' => 'DELETE',
'headers' => array(
'Authorization' => 'Bearer ' . $bearer->token(),
'Content-Type' => 'application/json',
),
);
$response = $this->request( $url, $args );
if ( is_wp_error( $response ) ) {
$error = new RuntimeException(
__( 'Could not delete payment token.', 'paypal-for-woocommerce' )
);
$this->logger->log(
'warning',
$error->getMessage(),
array(
'args' => $args,
'response' => $response,
)
);
throw $error;
}
return wp_remote_retrieve_response_code( $response ) === 204;
}
}

View file

@ -0,0 +1,39 @@
<?php
/**
* The RequestTrait wraps the wp_remote_get functionality for the API client.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Endpoint
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Endpoint;
/**
* Trait RequestTrait
*/
trait RequestTrait {
/**
* Performs a request
*
* @param string $url The URL to request.
* @param array $args The arguments by which to request.
*
* @return array|\WP_Error
*/
private function request( string $url, array $args ) {
/**
* This filter can be used to alter the request args.
* For example, during testing, the PayPal-Mock-Response header could be
* added here.
*/
$args = apply_filters( 'ppcp_request_args', $args, $url );
if ( ! isset( $args['headers']['PayPal-Partner-Attribution-Id'] ) ) {
$args['headers']['PayPal-Partner-Attribution-Id'] = 'Woo_PPCP';
}
return wp_remote_get( $url, $args );
}
}

View file

@ -0,0 +1,305 @@
<?php
/**
* The webhook endpoint.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Endpoint
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Endpoint;
use Inpsyde\PayPalCommerce\ApiClient\Authentication\Bearer;
use Inpsyde\PayPalCommerce\ApiClient\Entity\Webhook;
use Inpsyde\PayPalCommerce\ApiClient\Exception\PayPalApiException;
use Inpsyde\PayPalCommerce\ApiClient\Exception\RuntimeException;
use Inpsyde\PayPalCommerce\ApiClient\Factory\WebhookFactory;
use Psr\Log\LoggerInterface;
/**
* Class WebhookEndpoint
*/
class WebhookEndpoint {
use RequestTrait;
/**
* The host.
*
* @var string
*/
private $host;
/**
* The bearer.
*
* @var Bearer
*/
private $bearer;
/**
* The webhook factory.
*
* @var WebhookFactory
*/
private $webhook_factory;
/**
* The logger.
*
* @var LoggerInterface
*/
private $logger;
/**
* WebhookEndpoint constructor.
*
* @param string $host The host.
* @param Bearer $bearer The bearer.
* @param WebhookFactory $webhook_factory The webhook factory.
* @param LoggerInterface $logger The logger.
*/
public function __construct(
string $host,
Bearer $bearer,
WebhookFactory $webhook_factory,
LoggerInterface $logger
) {
$this->host = $host;
$this->bearer = $bearer;
$this->webhook_factory = $webhook_factory;
$this->logger = $logger;
}
/**
* Creates a webhook with PayPal.
*
* @param Webhook $hook The webhook to create.
*
* @return Webhook
* @throws RuntimeException If the request fails.
*/
public function create( Webhook $hook ): Webhook {
/**
* An hook, which has an ID has already been created.
*/
if ( $hook->id() ) {
return $hook;
}
$bearer = $this->bearer->bearer();
$url = trailingslashit( $this->host ) . 'v1/notifications/webhooks';
$args = array(
'method' => 'POST',
'headers' => array(
'Authorization' => 'Bearer ' . $bearer->token(),
'Content-Type' => 'application/json',
),
'body' => wp_json_encode( $hook->to_array() ),
);
$response = $this->request( $url, $args );
if ( is_wp_error( $response ) ) {
$error = new RuntimeException(
__( 'Not able to create a webhook.', 'paypal-for-woocommerce' )
);
$this->logger->log(
'warning',
$error->getMessage(),
array(
'args' => $args,
'response' => $response,
)
);
throw $error;
}
$json = json_decode( $response['body'] );
$status_code = (int) wp_remote_retrieve_response_code( $response );
if ( 201 !== $status_code ) {
$error = new PayPalApiException(
$json,
$status_code
);
$this->logger->log(
'warning',
$error->getMessage(),
array(
'args' => $args,
'response' => $response,
)
);
throw $error;
}
$hook = $this->webhook_factory->from_paypal_response( $json );
return $hook;
}
/**
* Deletes a webhook.
*
* @param Webhook $hook The webhook to delete.
*
* @return bool
* @throws RuntimeException If the request fails.
*/
public function delete( Webhook $hook ): bool {
if ( ! $hook->id() ) {
return false;
}
$bearer = $this->bearer->bearer();
$url = trailingslashit( $this->host ) . 'v1/notifications/webhooks/' . $hook->id();
$args = array(
'method' => 'DELETE',
'headers' => array(
'Authorization' => 'Bearer ' . $bearer->token(),
),
);
$response = $this->request( $url, $args );
if ( is_wp_error( $response ) ) {
$error = new RuntimeException(
__( 'Not able to delete the webhook.', 'paypal-for-woocommerce' )
);
$this->logger->log(
'warning',
$error->getMessage(),
array(
'args' => $args,
'response' => $response,
)
);
throw $error;
}
return wp_remote_retrieve_response_code( $response ) === 204;
}
/**
* Verifies if a webhook event is legitimate.
*
* @param string $auth_algo The auth algo.
* @param string $cert_url The cert URL.
* @param string $transmission_id The transmission id.
* @param string $transmission_sig The transmission signature.
* @param string $transmission_time The transmission time.
* @param string $webhook_id The webhook id.
* @param \stdClass $webhook_event The webhook event.
*
* @return bool
* @throws RuntimeException If the request fails.
*/
public function verify_event(
string $auth_algo,
string $cert_url,
string $transmission_id,
string $transmission_sig,
string $transmission_time,
string $webhook_id,
\stdClass $webhook_event
): bool {
$bearer = $this->bearer->bearer();
$url = trailingslashit( $this->host ) . 'v1/notifications/verify-webhook-signature';
$args = array(
'method' => 'POST',
'headers' => array(
'Authorization' => 'Bearer ' . $bearer->token(),
'Content-Type' => 'application/json',
),
'body' => wp_json_encode(
array(
'transmission_id' => $transmission_id,
'transmission_time' => $transmission_time,
'cert_url' => $cert_url,
'auth_algo' => $auth_algo,
'transmission_sig' => $transmission_sig,
'webhook_id' => $webhook_id,
'webhook_event' => $webhook_event,
)
),
);
$response = $this->request( $url, $args );
if ( is_wp_error( $response ) ) {
$error = new RuntimeException(
__( 'Not able to verify webhook event.', 'paypal-for-woocommerce' )
);
$this->logger->log(
'warning',
$error->getMessage(),
array(
'args' => $args,
'response' => $response,
)
);
throw $error;
}
$json = json_decode( $response['body'] );
return isset( $json->verification_status ) && 'SUCCESS' === $json->verification_status;
}
/**
* Verifies if the current request is a legit webhook event.
*
* @param Webhook $webhook The webhook.
*
* @return bool
* @throws RuntimeException If the request fails.
*/
public function verify_current_request_for_webhook( Webhook $webhook ): bool {
if ( ! $webhook->id() ) {
$error = new RuntimeException(
__( 'Not a valid webhook to verify.', 'paypal-for-woocommerce' )
);
$this->logger->log( 'warning', $error->getMessage(), array( 'webhook' => $webhook ) );
throw $error;
}
$expected_headers = array(
'PAYPAL-AUTH-ALGO' => '',
'PAYPAL-CERT-URL' => '',
'PAYPAL-TRANSMISSION-ID' => '',
'PAYPAL-TRANSMISSION-SIG' => '',
'PAYPAL-TRANSMISSION-TIME' => '',
);
$headers = getallheaders();
foreach ( $headers as $key => $header ) {
$key = strtoupper( $key );
if ( isset( $expected_headers[ $key ] ) ) {
$expected_headers[ $key ] = $header;
}
};
foreach ( $expected_headers as $key => $value ) {
if ( ! empty( $value ) ) {
continue;
}
$error = new RuntimeException(
sprintf(
// translators: %s is the headers key.
__(
'Not a valid webhook event. Header %s is missing',
'paypal-for-woocommerce'
),
$key
)
);
$this->logger->log( 'warning', $error->getMessage(), array( 'webhook' => $webhook ) );
throw $error;
}
$request_body = json_decode( file_get_contents( 'php://input' ) );
return $this->verify_event(
$expected_headers['PAYPAL-AUTH-ALGO'],
$expected_headers['PAYPAL-CERT-URL'],
$expected_headers['PAYPAL-TRANSMISSION-ID'],
$expected_headers['PAYPAL-TRANSMISSION-SIG'],
$expected_headers['PAYPAL-TRANSMISSION-TIME'],
$webhook->id(),
$request_body ? $request_body : new \stdClass()
);
}
}

View file

@ -0,0 +1,155 @@
<?php
/**
* The address object.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Entity
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Entity;
/**
* Class Address
*/
class Address {
/**
* The country code.
*
* @var string
*/
private $country_code;
/**
* The 1st address line.
*
* @var string
*/
private $address_line_1;
/**
* The 2nd address line.
*
* @var string
*/
private $address_line_2;
/**
* The admin area 1.
*
* @var string
*/
private $admin_area_1;
/**
* The admin area 2.
*
* @var string
*/
private $admin_area_2;
/**
* The postal code.
*
* @var string
*/
private $postal_code;
/**
* Address constructor.
*
* @param string $country_code The country code.
* @param string $address_line_1 The 1st address line.
* @param string $address_line_2 The 2nd address line.
* @param string $admin_area_1 The admin area 1.
* @param string $admin_area_2 The admin area 2.
* @param string $postal_code The postal code.
*/
public function __construct(
string $country_code,
string $address_line_1 = '',
string $address_line_2 = '',
string $admin_area_1 = '',
string $admin_area_2 = '',
string $postal_code = ''
) {
$this->country_code = $country_code;
$this->address_line_1 = $address_line_1;
$this->address_line_2 = $address_line_2;
$this->admin_area_1 = $admin_area_1;
$this->admin_area_2 = $admin_area_2;
$this->postal_code = $postal_code;
}
/**
* Returns the country code.
*
* @return string
*/
public function country_code(): string {
return $this->country_code;
}
/**
* Returns the 1st address line.
*
* @return string
*/
public function address_line_1(): string {
return $this->address_line_1;
}
/**
* Returns the 2nd address line.
*
* @return string
*/
public function address_line_2(): string {
return $this->address_line_2;
}
/**
* Returns the admin area 1.
*
* @return string
*/
public function admin_area_1(): string {
return $this->admin_area_1;
}
/**
* Returns the admin area 2.
*
* @return string
*/
public function admin_area_2(): string {
return $this->admin_area_2;
}
/**
* Returns the postal code.
*
* @return string
*/
public function postal_code(): string {
return $this->postal_code;
}
/**
* Returns the object as array.
*
* @return array
*/
public function to_array(): array {
return array(
'country_code' => $this->country_code(),
'address_line_1' => $this->address_line_1(),
'address_line_2' => $this->address_line_2(),
'admin_area_1' => $this->admin_area_1(),
'admin_area_2' => $this->admin_area_2(),
'postal_code' => $this->postal_code(),
);
}
}

View file

@ -0,0 +1,84 @@
<?php
/**
* The amount object
*
* @package Inpsyde\PayPalCommerce\ApiClient\Entity
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Entity;
/**
* Class Amount
*/
class Amount {
/**
* The money.
*
* @var Money
*/
private $money;
/**
* The breakdown.
*
* @var AmountBreakdown
*/
private $breakdown;
/**
* Amount constructor.
*
* @param Money $money The money.
* @param AmountBreakdown|null $breakdown The breakdown.
*/
public function __construct( Money $money, AmountBreakdown $breakdown = null ) {
$this->money = $money;
$this->breakdown = $breakdown;
}
/**
* Returns the currency code.
*
* @return string
*/
public function currency_code(): string {
return $this->money->currency_code();
}
/**
* Returns the value.
*
* @return float
*/
public function value(): float {
return $this->money->value();
}
/**
* Returns the breakdown.
*
* @return AmountBreakdown|null
*/
public function breakdown(): ?AmountBreakdown {
return $this->breakdown;
}
/**
* Returns the object as array.
*
* @return array
*/
public function to_array(): array {
$amount = array(
'currency_code' => $this->currency_code(),
'value' => $this->value(),
);
if ( $this->breakdown() && count( $this->breakdown()->to_array() ) ) {
$amount['breakdown'] = $this->breakdown()->to_array();
}
return $amount;
}
}

View file

@ -0,0 +1,190 @@
<?php
/**
* The Amount Breakdown object.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Entity
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Entity;
/**
* Class AmountBreakdown
*/
class AmountBreakdown {
/**
* The item total.
*
* @var Money
*/
private $item_total;
/**
* The shipping.
*
* @var Money
*/
private $shipping;
/**
* The tax total.
*
* @var Money
*/
private $tax_total;
/**
* The handling.
*
* @var Money
*/
private $handling;
/**
* The insurance.
*
* @var Money
*/
private $insurance;
/**
* The shipping discount.
*
* @var Money
*/
private $shipping_discount;
/**
* The discount.
*
* @var Money
*/
private $discount;
/**
* AmountBreakdown constructor.
*
* @param Money|null $item_total The item total.
* @param Money|null $shipping The shipping.
* @param Money|null $tax_total The tax total.
* @param Money|null $handling The handling.
* @param Money|null $insurance The insurance.
* @param Money|null $shipping_discount The shipping discount.
* @param Money|null $discount The discount.
*/
public function __construct(
Money $item_total = null,
Money $shipping = null,
Money $tax_total = null,
Money $handling = null,
Money $insurance = null,
Money $shipping_discount = null,
Money $discount = null
) {
$this->item_total = $item_total;
$this->shipping = $shipping;
$this->tax_total = $tax_total;
$this->handling = $handling;
$this->insurance = $insurance;
$this->shipping_discount = $shipping_discount;
$this->discount = $discount;
}
/**
* Returns the item total.
*
* @return Money|null
*/
public function item_total(): ?Money {
return $this->item_total;
}
/**
* Returns the shipping.
*
* @return Money|null
*/
public function shipping(): ?Money {
return $this->shipping;
}
/**
* Returns the tax total.
*
* @return Money|null
*/
public function tax_total(): ?Money {
return $this->tax_total;
}
/**
* Returns the handling.
*
* @return Money|null
*/
public function handling(): ?Money {
return $this->handling;
}
/**
* Returns the insurance.
*
* @return Money|null
*/
public function insurance(): ?Money {
return $this->insurance;
}
/**
* Returns the shipping discount.
*
* @return Money|null
*/
public function shipping_discount(): ?Money {
return $this->shipping_discount;
}
/**
* Returns the discount.
*
* @return Money|null
*/
public function discount(): ?Money {
return $this->discount;
}
/**
* Returns the object as array.
*
* @return array
*/
public function to_array(): array {
$breakdown = array();
if ( $this->item_total ) {
$breakdown['item_total'] = $this->item_total->to_array();
}
if ( $this->shipping ) {
$breakdown['shipping'] = $this->shipping->to_array();
}
if ( $this->tax_total ) {
$breakdown['tax_total'] = $this->tax_total->to_array();
}
if ( $this->handling ) {
$breakdown['handling'] = $this->handling->to_array();
}
if ( $this->insurance ) {
$breakdown['insurance'] = $this->insurance->to_array();
}
if ( $this->shipping_discount ) {
$breakdown['shipping_discount'] = $this->shipping_discount->to_array();
}
if ( $this->discount ) {
$breakdown['discount'] = $this->discount->to_array();
}
return $breakdown;
}
}

View file

@ -0,0 +1,249 @@
<?php
/**
* The application context object.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Entity
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Entity;
use Inpsyde\PayPalCommerce\ApiClient\Exception\RuntimeException;
/**
* Class ApplicationContext
*/
class ApplicationContext {
public const LANDING_PAGE_LOGIN = 'LOGIN';
public const LANDING_PAGE_BILLING = 'BILLING';
public const LANDING_PAGE_NO_PREFERENCE = 'NO_PREFERENCE';
private const VALID_LANDING_PAGE_VALUES = array(
self::LANDING_PAGE_LOGIN,
self::LANDING_PAGE_BILLING,
self::LANDING_PAGE_NO_PREFERENCE,
);
public const SHIPPING_PREFERENCE_GET_FROM_FILE = 'GET_FROM_FILE';
public const SHIPPING_PREFERENCE_NO_SHIPPING = 'NO_SHIPPING';
public const SHIPPING_PREFERENCE_SET_PROVIDED_ADDRESS = 'SET_PROVIDED_ADDRESS';
private const VALID_SHIPPING_PREFERENCE_VALUES = array(
self::SHIPPING_PREFERENCE_GET_FROM_FILE,
self::SHIPPING_PREFERENCE_NO_SHIPPING,
self::SHIPPING_PREFERENCE_SET_PROVIDED_ADDRESS,
);
public const USER_ACTION_CONTINUE = 'CONTINUE';
public const USER_ACTION_PAY_NOW = 'PAY_NOW';
private const VALID_USER_ACTION_VALUES = array(
self::USER_ACTION_CONTINUE,
self::USER_ACTION_PAY_NOW,
);
/**
* The brand name.
*
* @var string
*/
private $brand_name;
/**
* The locale.
*
* @var string
*/
private $locale;
/**
* The landing page.
*
* @var string
*/
private $landing_page;
/**
* The shipping preference.
*
* @var string
*/
private $shipping_preference;
/**
* The user action.
*
* @var string
*/
private $user_action;
/**
* The return url.
*
* @var string
*/
private $return_url;
/**
* The cancel url.
*
* @var string
*/
private $cancel_url;
/**
* The payment method.
*
* @var null
*/
private $payment_method;
/**
* ApplicationContext constructor.
*
* @param string $return_url The return URL.
* @param string $cancel_url The cancel URL.
* @param string $brand_name The brand name.
* @param string $locale The locale.
* @param string $landing_page The landing page.
* @param string $shipping_preference The shipping preference.
* @param string $user_action The user action.
*
* @throws RuntimeException When values are not valid.
*/
public function __construct(
string $return_url = '',
string $cancel_url = '',
string $brand_name = '',
string $locale = '',
string $landing_page = self::LANDING_PAGE_NO_PREFERENCE,
string $shipping_preference = self::SHIPPING_PREFERENCE_NO_SHIPPING,
string $user_action = self::USER_ACTION_CONTINUE
) {
if ( ! in_array( $landing_page, self::VALID_LANDING_PAGE_VALUES, true ) ) {
throw new RuntimeException( 'Landingpage not correct' );
}
if ( ! in_array( $shipping_preference, self::VALID_SHIPPING_PREFERENCE_VALUES, true ) ) {
throw new RuntimeException( 'Shipping preference not correct' );
}
if ( ! in_array( $user_action, self::VALID_USER_ACTION_VALUES, true ) ) {
throw new RuntimeException( 'User action preference not correct' );
}
$this->return_url = $return_url;
$this->cancel_url = $cancel_url;
$this->brand_name = $brand_name;
$this->locale = $locale;
$this->landing_page = $landing_page;
$this->shipping_preference = $shipping_preference;
$this->user_action = $user_action;
// Currently we have not implemented the payment method.
$this->payment_method = null;
}
/**
* Returns the brand name.
*
* @return string
*/
public function brand_name(): string {
return $this->brand_name;
}
/**
* Returns the locale.
*
* @return string
*/
public function locale(): string {
return $this->locale;
}
/**
* Returns the landing page.
*
* @return string
*/
public function landing_page(): string {
return $this->landing_page;
}
/**
* Returns the shipping preference.
*
* @return string
*/
public function shipping_preference(): string {
return $this->shipping_preference;
}
/**
* Returns the user action.
*
* @return string
*/
public function user_action(): string {
return $this->user_action;
}
/**
* Returns the return URL.
*
* @return string
*/
public function return_url(): string {
return $this->return_url;
}
/**
* Returns the cancel URL.
*
* @return string
*/
public function cancel_url(): string {
return $this->cancel_url;
}
/**
* Returns the payment method.
*
* @return PaymentMethod|null
*/
public function payment_method(): ?PaymentMethod {
return $this->payment_method;
}
/**
* Returns the object as array.
*
* @return array
*/
public function to_array(): array {
$data = array();
if ( $this->user_action() ) {
$data['user_action'] = $this->user_action();
}
if ( $this->payment_method() ) {
$data['payment_method'] = $this->payment_method();
}
if ( $this->shipping_preference() ) {
$data['shipping_preference'] = $this->shipping_preference();
}
if ( $this->landing_page() ) {
$data['landing_page'] = $this->landing_page();
}
if ( $this->locale() ) {
$data['locale'] = $this->locale();
}
if ( $this->brand_name() ) {
$data['brand_name'] = $this->brand_name();
}
if ( $this->return_url() ) {
$data['return_url'] = $this->return_url();
}
if ( $this->cancel_url() ) {
$data['cancel_url'] = $this->cancel_url();
}
return $data;
}
}

View file

@ -0,0 +1,75 @@
<?php
/**
* The Authorization object
*
* @package Inpsyde\PayPalCommerce\ApiClient\Entity
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Entity;
/**
* Class Authorization
*/
class Authorization {
/**
* The Id.
*
* @var string
*/
private $id;
/**
* The status.
*
* @var AuthorizationStatus
*/
private $authorization_status;
/**
* Authorization constructor.
*
* @param string $id The id.
* @param AuthorizationStatus $authorization_status The status.
*/
public function __construct(
string $id,
AuthorizationStatus $authorization_status
) {
$this->id = $id;
$this->authorization_status = $authorization_status;
}
/**
* Returns the Id.
*
* @return string
*/
public function id(): string {
return $this->id;
}
/**
* Returns the status.
*
* @return AuthorizationStatus
*/
public function status(): AuthorizationStatus {
return $this->authorization_status;
}
/**
* Returns the object as array.
*
* @return array
*/
public function to_array(): array {
return array(
'id' => $this->id,
'status' => $this->authorization_status->name(),
);
}
}

View file

@ -0,0 +1,94 @@
<?php
/**
* The AuthorizationStatus object.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Entity
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Entity;
use Inpsyde\PayPalCommerce\ApiClient\Exception\RuntimeException;
/**
* Class AuthorizationStatus
*/
class AuthorizationStatus {
public const INTERNAL = 'INTERNAL';
public const CREATED = 'CREATED';
public const CAPTURED = 'CAPTURED';
public const COMPLETED = 'COMPLETED';
public const DENIED = 'DENIED';
public const EXPIRED = 'EXPIRED';
public const PARTIALLY_CAPTURED = 'PARTIALLY_CAPTURED';
public const VOIDED = 'VOIDED';
public const PENDING = 'PENDING';
public const VALID_STATUS = array(
self::INTERNAL,
self::CREATED,
self::CAPTURED,
self::COMPLETED,
self::DENIED,
self::EXPIRED,
self::PARTIALLY_CAPTURED,
self::VOIDED,
self::PENDING,
);
/**
* The status.
*
* @var string
*/
private $status;
/**
* AuthorizationStatus constructor.
*
* @param string $status The status.
* @throws RuntimeException When the status is not valid.
*/
public function __construct( string $status ) {
if ( ! in_array( $status, self::VALID_STATUS, true ) ) {
throw new RuntimeException(
sprintf(
// translators: %s is the current status.
__( '%s is not a valid status', 'paypal-for-woocommerce' ),
$status
)
);
}
$this->status = $status;
}
/**
* Returns an AuthorizationStatus as Internal.
*
* @return AuthorizationStatus
*/
public static function as_internal(): AuthorizationStatus {
return new self( self::INTERNAL );
}
/**
* Compares the current status with a given one.
*
* @param string $status The status to compare with.
*
* @return bool
*/
public function is( string $status ): bool {
return $this->status === $status;
}
/**
* Returns the status.
*
* @return string
*/
public function name(): string {
return $this->status;
}
}

View file

@ -0,0 +1,119 @@
<?php
/**
* The CardauthenticationResult object
*
* @package Inpsyde\PayPalCommerce\ApiClient\Entity
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Entity;
/**
* Class CardAuthenticationResult
*/
class CardAuthenticationResult {
public const LIABILITY_SHIFT_POSSIBLE = 'POSSIBLE';
public const LIABILITY_SHIFT_NO = 'NO';
public const LIABILITY_SHIFT_UNKNOWN = 'UNKNOWN';
public const ENROLLMENT_STATUS_YES = 'Y';
public const ENROLLMENT_STATUS_NO = 'N';
public const ENROLLMENT_STATUS_UNAVAILABLE = 'U';
public const ENROLLMENT_STATUS_BYPASS = 'B';
public const AUTHENTICATION_RESULT_YES = 'Y';
public const AUTHENTICATION_RESULT_NO = 'N';
public const AUTHENTICATION_RESULT_REJECTED = 'R';
public const AUTHENTICATION_RESULT_ATTEMPTED = 'A';
public const AUTHENTICATION_RESULT_UNABLE = 'U';
public const AUTHENTICATION_RESULT_CHALLENGE_REQUIRED = 'C';
public const AUTHENTICATION_RESULT_INFO = 'I';
public const AUTHENTICATION_RESULT_DECOUPLED = 'D';
/**
* The liability shift.
*
* @var string
*/
private $liability_shift;
/**
* The enrollment status.
*
* @var string
*/
private $enrollment_status;
/**
* The authentication result.
*
* @var string
*/
private $authentication_result;
/**
* CardAuthenticationResult constructor.
*
* @param string $liability_shift The liability shift.
* @param string $enrollment_status The enrollment status.
* @param string $authentication_result The authentication result.
*/
public function __construct(
string $liability_shift,
string $enrollment_status,
string $authentication_result
) {
$this->liability_shift = strtoupper( $liability_shift );
$this->enrollment_status = strtoupper( $enrollment_status );
$this->authentication_result = strtoupper( $authentication_result );
}
/**
* Returns the liability shift.
*
* @return string
*/
public function liability_shift(): string {
return $this->liability_shift;
}
/**
* Returns the enrollment status.
*
* @return string
*/
public function enrollment_status(): string {
return $this->enrollment_status;
}
/**
* Returns the authentication result.
*
* @return string
*/
public function authentication_result(): string {
return $this->authentication_result;
}
/**
* Returns the object as array.
*
* @return array
*/
public function to_array(): array {
$data = array();
$data['liability_shift'] = $this->liability_shift();
$data['three_d_secure'] = array(
'enrollment_status' => $this->enrollment_status(),
'authentication_result' => $this->authentication_result(),
);
return $data;
}
}

View file

@ -0,0 +1,185 @@
<?php
/**
* The item object.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Entity
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Entity;
/**
* Class Item
*/
class Item {
public const PHYSICAL_GOODS = 'PHYSICAL_GOODS';
public const DIGITAL_GOODS = 'DIGITAL_GOODS';
/**
* The name.
*
* @var string
*/
private $name;
/**
* The unit amount.
*
* @var Money
*/
private $unit_amount;
/**
* The quantity.
*
* @var int
*/
private $quantity;
/**
* The description.
*
* @var string
*/
private $description;
/**
* The tax.
*
* @var Money|null
*/
private $tax;
/**
* The SKU.
*
* @var string
*/
private $sku;
/**
* The category.
*
* @var string
*/
private $category;
/**
* Item constructor.
*
* @param string $name The name.
* @param Money $unit_amount The unit amount.
* @param int $quantity The quantity.
* @param string $description The description.
* @param Money|null $tax The tax.
* @param string $sku The SKU.
* @param string $category The category.
*/
public function __construct(
string $name,
Money $unit_amount,
int $quantity,
string $description = '',
Money $tax = null,
string $sku = '',
string $category = 'PHYSICAL_GOODS'
) {
$this->name = $name;
$this->unit_amount = $unit_amount;
$this->quantity = $quantity;
$this->description = $description;
$this->tax = $tax;
$this->sku = $sku;
$this->category = ( self::DIGITAL_GOODS === $category ) ?
self::DIGITAL_GOODS : self::PHYSICAL_GOODS;
}
/**
* Returns the name of the item.
*
* @return string
*/
public function name(): string {
return $this->name;
}
/**
* Returns the unit amount.
*
* @return Money
*/
public function unit_amount(): Money {
return $this->unit_amount;
}
/**
* Returns the quantity.
*
* @return int
*/
public function quantity(): int {
return $this->quantity;
}
/**
* Returns the description.
*
* @return string
*/
public function description(): string {
return $this->description;
}
/**
* Returns the tax.
*
* @return Money|null
*/
public function tax(): ?Money {
return $this->tax;
}
/**
* Returns the SKU.
*
* @return string
*/
public function sku(): string {
return $this->sku;
}
/**
* Returns the category.
*
* @return string
*/
public function category(): string {
return $this->category;
}
/**
* Returns the object as array.
*
* @return array
*/
public function to_array(): array {
$item = array(
'name' => $this->name(),
'unit_amount' => $this->unit_amount()->to_array(),
'quantity' => $this->quantity(),
'description' => $this->description(),
'sku' => $this->sku(),
'category' => $this->category(),
);
if ( $this->tax() ) {
$item['tax'] = $this->tax()->to_array();
}
return $item;
}
}

View file

@ -0,0 +1,71 @@
<?php
/**
* The money object.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Entity
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Entity;
/**
* Class Money
*/
class Money {
/**
* The currency code.
*
* @var string
*/
private $currency_code;
/**
* The value.
*
* @var float
*/
private $value;
/**
* Money constructor.
*
* @param float $value The value.
* @param string $currency_code The currency code.
*/
public function __construct( float $value, string $currency_code ) {
$this->value = $value;
$this->currency_code = $currency_code;
}
/**
* The value.
*
* @return float
*/
public function value(): float {
return $this->value;
}
/**
* The currency code.
*
* @return string
*/
public function currency_code(): string {
return $this->currency_code;
}
/**
* Returns the object as array.
*
* @return array
*/
public function to_array(): array {
return array(
'currency_code' => $this->currency_code(),
'value' => number_format( $this->value(), 2 ),
);
}
}

View file

@ -0,0 +1,245 @@
<?php
/**
* The order object.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Entity
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Entity;
/**
* Class Order
*/
class Order {
/**
* The ID.
*
* @var string
*/
private $id;
/**
* The create time.
*
* @var \DateTime|null
*/
private $create_time;
/**
* The purchase units.
*
* @var PurchaseUnit[]
*/
private $purchase_units;
/**
* The payer.
*
* @var Payer|null
*/
private $payer;
/**
* The order status.
*
* @var OrderStatus
*/
private $order_status;
/**
* The intent.
*
* @var string
*/
private $intent;
/**
* The update time.
*
* @var \DateTime|null
*/
private $update_time;
/**
* The application context.
*
* @var ApplicationContext|null
*/
private $application_context;
/**
* The payment source.
*
* @var PaymentSource|null
*/
private $payment_source;
/**
* Order constructor.
*
* @see https://developer.paypal.com/docs/api/orders/v2/#orders-create-response
*
* @param string $id The ID.
* @param PurchaseUnit[] $purchase_units The purchase units.
* @param OrderStatus $order_status The order status.
* @param ApplicationContext|null $application_context The application context.
* @param PaymentSource|null $payment_source The payment source.
* @param Payer|null $payer The payer.
* @param string $intent The intent.
* @param \DateTime|null $create_time The create time.
* @param \DateTime|null $update_time The update time.
*/
public function __construct(
string $id,
array $purchase_units,
OrderStatus $order_status,
ApplicationContext $application_context = null,
PaymentSource $payment_source = null,
Payer $payer = null,
string $intent = 'CAPTURE',
\DateTime $create_time = null,
\DateTime $update_time = null
) {
$this->id = $id;
$this->application_context = $application_context;
$this->purchase_units = array_values(
array_filter(
$purchase_units,
static function ( $unit ): bool {
return is_a( $unit, PurchaseUnit::class );
}
)
);
$this->payer = $payer;
$this->order_status = $order_status;
$this->intent = ( 'CAPTURE' === $intent ) ? 'CAPTURE' : 'AUTHORIZE';
$this->purchase_units = $purchase_units;
$this->create_time = $create_time;
$this->update_time = $update_time;
$this->payment_source = $payment_source;
}
/**
* Returns the ID.
*
* @return string
*/
public function id(): string {
return $this->id;
}
/**
* Returns the create time.
*
* @return \DateTime|null
*/
public function create_time(): ?\DateTime {
return $this->create_time;
}
/**
* Returns the update time.
*
* @return \DateTime|null
*/
public function update_time(): ?\DateTime {
return $this->update_time;
}
/**
* Returns the intent.
*
* @return string
*/
public function intent(): string {
return $this->intent;
}
/**
* Returns the payer.
*
* @return Payer|null
*/
public function payer(): ?Payer {
return $this->payer;
}
/**
* Returns the purchase units.
*
* @return PurchaseUnit[]
*/
public function purchase_units(): array {
return $this->purchase_units;
}
/**
* Returns the order status.
*
* @return OrderStatus
*/
public function status(): OrderStatus {
return $this->order_status;
}
/**
* Returns the application context.
*
* @return ApplicationContext|null
*/
public function application_context(): ?ApplicationContext {
return $this->application_context;
}
/**
* Returns the payment source.
*
* @return PaymentSource|null
*/
public function payment_source(): ?PaymentSource {
return $this->payment_source;
}
/**
* Returns the object as array.
*
* @return array
*/
public function to_array(): array {
$order = array(
'id' => $this->id(),
'intent' => $this->intent(),
'status' => $this->status()->name(),
'purchase_units' => array_map(
static function ( PurchaseUnit $unit ): array {
return $unit->to_array();
},
$this->purchase_units()
),
);
if ( $this->create_time() ) {
$order['create_time'] = $this->create_time()->format( \DateTimeInterface::ISO8601 );
}
if ( $this->payer() ) {
$order['payer'] = $this->payer()->to_array();
}
if ( $this->update_time() ) {
$order['update_time'] = $this->update_time()->format( \DateTimeInterface::ISO8601 );
}
if ( $this->application_context() ) {
$order['application_context'] = $this->application_context()->to_array();
}
if ( $this->payment_source() ) {
$order['payment_source'] = $this->payment_source()->to_array();
}
return $order;
}
}

View file

@ -0,0 +1,89 @@
<?php
/**
* The OrderStatus object.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Entity
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Entity;
use Inpsyde\PayPalCommerce\ApiClient\Exception\RuntimeException;
/**
* Class OrderStatus
*/
class OrderStatus {
public const INTERNAL = 'INTERNAL';
public const CREATED = 'CREATED';
public const SAVED = 'SAVED';
public const APPROVED = 'APPROVED';
public const VOIDED = 'VOIDED';
public const COMPLETED = 'COMPLETED';
public const VALID_STATI = array(
self::INTERNAL,
self::CREATED,
self::SAVED,
self::APPROVED,
self::VOIDED,
self::COMPLETED,
);
/**
* The status.
*
* @var string
*/
private $status;
/**
* OrderStatus constructor.
*
* @param string $status The status.
* @throws RuntimeException When the status is not valid.
*/
public function __construct( string $status ) {
if ( ! in_array( $status, self::VALID_STATI, true ) ) {
throw new RuntimeException(
sprintf(
// translators: %s is the current status.
__( '%s is not a valid status', 'paypal-for-woocommerce' ),
$status
)
);
}
$this->status = $status;
}
/**
* Creates an OrderStatus "Internal"
*
* @return OrderStatus
*/
public static function as_internal(): OrderStatus {
return new self( self::INTERNAL );
}
/**
* Compares the current status with a given one.
*
* @param string $status The status to compare with.
*
* @return bool
*/
public function is( string $status ): bool {
return $this->status === $status;
}
/**
* Returns the status.
*
* @return string
*/
public function name(): string {
return $this->status;
}
}

View file

@ -0,0 +1,100 @@
<?php
/**
* The Patch object.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Entity
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Entity;
/**
* Class Patch
*/
class Patch {
/**
* The operation.
*
* @var string
*/
private $op;
/**
* The path to the change.
*
* @var string
*/
private $path;
/**
* The new value.
*
* @var array
*/
private $value;
/**
* Patch constructor.
*
* @param string $op The operation.
* @param string $path The path.
* @param array $value The new value.
*/
public function __construct( string $op, string $path, array $value ) {
$this->op = $op;
$this->path = $path;
$this->value = $value;
}
/**
* Returns the operation.
*
* @return string
*/
public function op(): string {
return $this->op;
}
/**
* Returns the path.
*
* @return string
*/
public function path(): string {
return $this->path;
}
/**
* Returns the value.
*
* @return array
*/
public function value() {
return $this->value;
}
/**
* Returns the object as array.
*
* @return array
*/
public function to_array(): array {
return array(
'op' => $this->op(),
'value' => $this->value(),
'path' => $this->path(),
);
}
/**
* Needed for the move operation. We currently do not
* support the move operation.
*
* @return string
*/
public function from(): string {
return '';
}
}

View file

@ -0,0 +1,55 @@
<?php
/**
* The Patch collection object.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Entity
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Entity;
/**
* Class PatchCollection
*/
class PatchCollection {
/**
* The patches.
*
* @var Patch[]
*/
private $patches;
/**
* PatchCollection constructor.
*
* @param Patch ...$patches The patches.
*/
public function __construct( Patch ...$patches ) {
$this->patches = $patches;
}
/**
* Returns the patches.
*
* @return Patch[]
*/
public function patches(): array {
return $this->patches;
}
/**
* Returns the object as array.
*
* @return array
*/
public function to_array(): array {
return array_map(
static function ( Patch $patch ): array {
return $patch->to_array();
},
$this->patches()
);
}
}

View file

@ -0,0 +1,79 @@
<?php
/**
* The payee object.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Entity
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Entity;
/**
* Class Payee
* The entity, which receives the money.
*/
class Payee {
/**
* The email address.
*
* @var string
*/
private $email;
/**
* The merchant id.
*
* @var string
*/
private $merchant_id;
/**
* Payee constructor.
*
* @param string $email The email.
* @param string $merchant_id The merchant id.
*/
public function __construct(
string $email,
string $merchant_id
) {
$this->email = $email;
$this->merchant_id = $merchant_id;
}
/**
* Returns the email.
*
* @return string
*/
public function email(): string {
return $this->email;
}
/**
* Returns the merchant id.
*
* @return string
*/
public function merchant_id(): string {
return $this->merchant_id;
}
/**
* Returns the object as array.
*
* @return array
*/
public function to_array(): array {
$data = array(
'email_address' => $this->email(),
);
if ( $this->merchant_id ) {
$data['merchant_id'] = $this->merchant_id();
}
return $data;
}
}

View file

@ -0,0 +1,186 @@
<?php
/**
* The payer object.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Entity
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Entity;
/**
* Class Payer
* The customer who sends the money.
*/
class Payer {
/**
* The name.
*
* @var PayerName
*/
private $name;
/**
* The email address.
*
* @var string
*/
private $email_address;
/**
* The payer id.
*
* @var string
*/
private $payer_id;
/**
* The birth date.
*
* @var \DateTime|null
*/
private $birthdate;
/**
* The address.
*
* @var Address
*/
private $address;
/**
* The phone.
*
* @var PhoneWithType|null
*/
private $phone;
/**
* The tax info.
*
* @var PayerTaxInfo|null
*/
private $tax_info;
/**
* Payer constructor.
*
* @param PayerName $name The name.
* @param string $email_address The email.
* @param string $payer_id The payer id.
* @param Address $address The address.
* @param \DateTime|null $birthdate The birth date.
* @param PhoneWithType|null $phone The phone.
* @param PayerTaxInfo|null $tax_info The tax info.
*/
public function __construct(
PayerName $name,
string $email_address,
string $payer_id,
Address $address,
\DateTime $birthdate = null,
PhoneWithType $phone = null,
PayerTaxInfo $tax_info = null
) {
$this->name = $name;
$this->email_address = $email_address;
$this->payer_id = $payer_id;
$this->birthdate = $birthdate;
$this->address = $address;
$this->phone = $phone;
$this->tax_info = $tax_info;
}
/**
* Returns the name.
*
* @return PayerName
*/
public function name(): PayerName {
return $this->name;
}
/**
* Returns the email address.
*
* @return string
*/
public function email_address(): string {
return $this->email_address;
}
/**
* Returns the payer id.
*
* @return string
*/
public function payer_id(): string {
return $this->payer_id;
}
/**
* Returns the birth date.
*
* @return \DateTime|null
*/
public function birthdate(): ?\DateTime {
return $this->birthdate;
}
/**
* Returns the address.
*
* @return Address
*/
public function address(): Address {
return $this->address;
}
/**
* Returns the phone.
*
* @return PhoneWithType|null
*/
public function phone(): ?PhoneWithType {
return $this->phone;
}
/**
* Returns the tax info.
*
* @return PayerTaxInfo|null
*/
public function tax_info(): ?PayerTaxInfo {
return $this->tax_info;
}
/**
* Returns the object as array.
*
* @return array
*/
public function to_array(): array {
$payer = array(
'name' => $this->name()->to_array(),
'email_address' => $this->email_address(),
'address' => $this->address()->to_array(),
);
if ( $this->payer_id() ) {
$payer['payer_id'] = $this->payer_id();
}
if ( $this->phone() ) {
$payer['phone'] = $this->phone()->to_array();
}
if ( $this->tax_info() ) {
$payer['tax_info'] = $this->tax_info()->to_array();
}
if ( $this->birthdate() ) {
$payer['birth_date'] = $this->birthdate()->format( 'Y-m-d' );
}
return $payer;
}
}

View file

@ -0,0 +1,75 @@
<?php
/**
* The PayerName object.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Entity
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Entity;
/**
* Class PayerName
*/
class PayerName {
/**
* The given name.
*
* @var string
*/
private $given_name;
/**
* The surname.
*
* @var string
*/
private $surname;
/**
* PayerName constructor.
*
* @param string $given_name The given name.
* @param string $surname The surname.
*/
public function __construct(
string $given_name,
string $surname
) {
$this->given_name = $given_name;
$this->surname = $surname;
}
/**
* Returns the given name.
*
* @return string
*/
public function given_name(): string {
return $this->given_name;
}
/**
* Returns the surname.
*
* @return string
*/
public function surname(): string {
return $this->surname;
}
/**
* Returns the object as array.
*
* @return array
*/
public function to_array(): array {
return array(
'given_name' => $this->given_name(),
'surname' => $this->surname(),
);
}
}

View file

@ -0,0 +1,93 @@
<?php
/**
* The PayerTaxInfo object.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Entity
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Entity;
use Inpsyde\PayPalCommerce\ApiClient\Exception\RuntimeException;
/**
* Class PayerTaxInfo
*/
class PayerTaxInfo {
public const VALID_TYPES = array(
'BR_CPF',
'BR_CNPJ',
);
/**
* The tax id.
*
* @var string
*/
private $tax_id;
/**
* The type.
*
* @var string
*/
private $type;
/**
* PayerTaxInfo constructor.
*
* @param string $tax_id The tax id.
* @param string $type The type.
* @throws RuntimeException When the type is not valid.
*/
public function __construct(
string $tax_id,
string $type
) {
if ( ! in_array( $type, self::VALID_TYPES, true ) ) {
throw new RuntimeException(
sprintf(
// translators: %s is the current type.
__( '%s is not a valid tax type.', 'paypal-for-woocommerce' ),
$type
)
);
}
$this->tax_id = $tax_id;
$this->type = $type;
}
/**
* Returns the type.
*
* @return string
*/
public function type(): string {
return $this->type;
}
/**
* Returns the tax id
*
* @return string
*/
public function tax_id(): string {
return $this->tax_id;
}
/**
* Returns the object as array.
*
* @return array
*/
public function to_array(): array {
return array(
'tax_id' => $this->tax_id(),
'tax_id_type' => $this->type(),
);
}
}

View file

@ -0,0 +1,81 @@
<?php
/**
* The PaymentMethod object
*
* @package Inpsyde\PayPalCommerce\ApiClient\Entity
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Entity;
/**
* Class PaymentMethod
*/
class PaymentMethod {
public const PAYER_SELECTED_DEFAULT = 'PAYPAL';
public const PAYEE_PREFERRED_UNRESTRICTED = 'UNRESTRICTED';
public const PAYEE_PREFERRED_IMMEDIATE_PAYMENT_REQUIRED = 'IMMEDIATE_PAYMENT_REQUIRED';
/**
* The preferred value.
*
* @var string
*/
private $preferred;
/**
* The selected value.
*
* @var string
*/
private $selected;
/**
* PaymentMethod constructor.
*
* @param string $preferred The preferred value.
* @param string $selected The selected value.
*/
public function __construct(
string $preferred = self::PAYEE_PREFERRED_UNRESTRICTED,
string $selected = self::PAYER_SELECTED_DEFAULT
) {
$this->preferred = $preferred;
$this->selected = $selected;
}
/**
* Returns the payer preferred value.
*
* @return string
*/
public function payee_preferred(): string {
return $this->preferred;
}
/**
* Returns the payer selected value.
*
* @return string
*/
public function payer_selected(): string {
return $this->selected;
}
/**
* Returns the object as array.
*
* @return array
*/
public function to_array(): array {
return array(
'payee_preferred' => $this->payee_preferred(),
'payer_selected' => $this->payer_selected(),
);
}
}

View file

@ -0,0 +1,57 @@
<?php
/**
* The Payments object.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Entity
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Entity;
/**
* Class Payments
*/
class Payments {
/**
* The Authorizations.
*
* @var Authorization[]
*/
private $authorizations;
/**
* Payments constructor.
*
* @param Authorization ...$authorizations The Authorizations.
*/
public function __construct( Authorization ...$authorizations ) {
$this->authorizations = $authorizations;
}
/**
* Returns the object as array.
*
* @return array
*/
public function to_array(): array {
return array(
'authorizations' => array_map(
static function ( Authorization $authorization ): array {
return $authorization->to_array();
},
$this->authorizations()
),
);
}
/**
* Returns the Authoriatzions.
*
* @return Authorization[]
**/
public function authorizations(): array {
return $this->authorizations;
}
}

View file

@ -0,0 +1,82 @@
<?php
/**
* The PaymentSource object.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Entity
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Entity;
/**
* Class PaymentSource
*/
class PaymentSource {
/**
* The card.
*
* @var PaymentSourceCard|null
*/
private $card;
/**
* The wallet.
*
* @var PaymentSourceWallet|null
*/
private $wallet;
/**
* PaymentSource constructor.
*
* @param PaymentSourceCard|null $card The card.
* @param PaymentSourceWallet|null $wallet The wallet.
*/
public function __construct(
PaymentSourceCard $card = null,
PaymentSourceWallet $wallet = null
) {
$this->card = $card;
$this->wallet = $wallet;
}
/**
* Returns the card.
*
* @return PaymentSourceCard|null
*/
public function card(): ?PaymentSourceCard {
return $this->card;
}
/**
* Returns the wallet.
*
* @return PaymentSourceWallet|null
*/
public function wallet(): ?PaymentSourceWallet {
return $this->wallet;
}
/**
* Returns the array of the object.
*
* @return array
*/
public function to_array(): array {
$data = array();
if ( $this->card() ) {
$data['card'] = $this->card()->to_array();
}
if ( $this->wallet() ) {
$data['wallet'] = $this->wallet()->to_array();
}
return $data;
}
}

View file

@ -0,0 +1,123 @@
<?php
/**
* The PaymentSourceCard object.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Entity
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Entity;
/**
* Class PaymentSourceCard
*/
class PaymentSourceCard {
/**
* The last digits of the card.
*
* @var string
*/
private $last_digits;
/**
* The brand.
*
* @var string
*/
private $brand;
/**
* The type.
*
* @var string
*/
private $type;
/**
* The authentication result.
*
* @var CardAuthenticationResult|null
*/
private $authentication_result;
/**
* PaymentSourceCard constructor.
*
* @param string $last_digits The last digits of the card.
* @param string $brand The brand of the card.
* @param string $type The type of the card.
* @param CardAuthenticationResult|null $authentication_result The authentication result.
*/
public function __construct(
string $last_digits,
string $brand,
string $type,
CardAuthenticationResult $authentication_result = null
) {
$this->last_digits = $last_digits;
$this->brand = $brand;
$this->type = $type;
$this->authentication_result = $authentication_result;
}
/**
* Returns the last digits.
*
* @return string
*/
public function last_digits(): string {
return $this->last_digits;
}
/**
* Returns the brand.
*
* @return string
*/
public function brand(): string {
return $this->brand;
}
/**
* Returns the type.
*
* @return string
*/
public function type(): string {
return $this->type;
}
/**
* Returns the authentication result.
*
* @return CardAuthenticationResult|null
*/
public function authentication_result(): ?CardAuthenticationResult {
return $this->authentication_result;
}
/**
* Returns the object as array.
*
* @return array
*/
public function to_array(): array {
$data = array(
'last_digits' => $this->last_digits(),
'brand' => $this->brand(),
'type' => $this->type(),
);
if ( $this->authentication_result() ) {
$data['authentication_result'] = $this->authentication_result()->to_array();
}
return $data;
}
}

View file

@ -0,0 +1,25 @@
<?php
/**
* The PaymentSourcewallet.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Entity
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Entity;
/**
* Class PaymentSourceWallet
*/
class PaymentSourceWallet {
/**
* Returns the object as array.
*
* @return array
*/
public function to_array(): array {
return array();
}
}

View file

@ -0,0 +1,85 @@
<?php
/**
* The PaymentToken object.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Entity
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Entity;
use Inpsyde\PayPalCommerce\ApiClient\Exception\RuntimeException;
/**
* Class PaymentToken
*/
class PaymentToken {
public const TYPE_PAYMENT_METHOD_TOKEN = 'PAYMENT_METHOD_TOKEN';
public const VALID_TYPES = array(
self::TYPE_PAYMENT_METHOD_TOKEN,
);
/**
* The Id.
*
* @var string
*/
private $id;
/**
* The type.
*
* @var string
*/
private $type;
/**
* PaymentToken constructor.
*
* @param string $id The Id.
* @param string $type The type.
* @throws RuntimeException When the type is not valid.
*/
public function __construct( string $id, string $type = self::TYPE_PAYMENT_METHOD_TOKEN ) {
if ( ! in_array( $type, self::VALID_TYPES, true ) ) {
throw new RuntimeException(
__( 'Not a valid payment source type.', 'paypal-for-woocommerce' )
);
}
$this->id = $id;
$this->type = $type;
}
/**
* Returns the ID.
*
* @return string
*/
public function id(): string {
return $this->id;
}
/**
* Returns the type.
*
* @return string
*/
public function type(): string {
return $this->type;
}
/**
* Returns the object as array.
*
* @return array
*/
public function to_array(): array {
return array(
'id' => $this->id(),
'type' => $this->type(),
);
}
}

View file

@ -0,0 +1,52 @@
<?php
/**
* The Phone object.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Entity
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Entity;
/**
* Class Phone
*/
class Phone {
/**
* The number.
*
* @var string
*/
private $national_number;
/**
* Phone constructor.
*
* @param string $national_number The number.
*/
public function __construct( string $national_number ) {
$this->national_number = $national_number;
}
/**
* Returns the number.
*
* @return string
*/
public function national_number(): string {
return $this->national_number;
}
/**
* Returns the object as array.
*
* @return array
*/
public function to_array(): array {
return array(
'national_number' => $this->national_number(),
);
}
}

View file

@ -0,0 +1,79 @@
<?php
/**
* The PhoneWithType object
*
* @package Inpsyde\PayPalCommerce\ApiClient\Entity
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Entity;
/**
* Class PhoneWithType
*/
class PhoneWithType {
public const VALLID_TYPES = array(
'FAX',
'HOME',
'MOBILE',
'OTHER',
'PAGER',
);
/**
* The type.
*
* @var string
*/
private $type;
/**
* The phone.
*
* @var Phone
*/
private $phone;
/**
* PhoneWithType constructor.
*
* @param string $type The type.
* @param Phone $phone The phone.
*/
public function __construct( string $type, Phone $phone ) {
$this->type = in_array( $type, self::VALLID_TYPES, true ) ? $type : 'OTHER';
$this->phone = $phone;
}
/**
* Returns the type.
*
* @return string
*/
public function type(): string {
return $this->type;
}
/**
* Returns the phone.
*
* @return Phone
*/
public function phone(): Phone {
return $this->phone;
}
/**
* Returns the object as array.
*
* @return array
*/
public function to_array(): array {
return array(
'phone_type' => $this->type(),
'phone_number' => $this->phone()->to_array(),
);
}
}

View file

@ -0,0 +1,358 @@
<?php
/**
* The purchase unit object.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Entity
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Entity;
/**
* Class PurchaseUnit
*/
class PurchaseUnit {
/**
* The amount.
*
* @var Amount
*/
private $amount;
/**
* The Items.
*
* @var Item[]
*/
private $items;
/**
* The shipping.
*
* @var Shipping|null
*/
private $shipping;
/**
* The reference id.
*
* @var string
*/
private $reference_id;
/**
* The description.
*
* @var string
*/
private $description;
/**
* The Payee.
*
* @var Payee|null
*/
private $payee;
/**
* The custom id.
*
* @var string
*/
private $custom_id;
/**
* The invoice id.
*
* @var string
*/
private $invoice_id;
/**
* The soft descriptor.
*
* @var string
*/
private $soft_descriptor;
/**
* The Payments.
*
* @var Payments|null
*/
private $payments;
/**
* Whether the unit contains physical goods.
*
* @var bool
*/
private $contains_physical_goods = false;
/**
* PurchaseUnit constructor.
*
* @param Amount $amount The Amount.
* @param Item[] $items The Items.
* @param Shipping|null $shipping The Shipping.
* @param string $reference_id The reference ID.
* @param string $description The description.
* @param Payee|null $payee The Payee.
* @param string $custom_id The custom ID.
* @param string $invoice_id The invoice ID.
* @param string $soft_descriptor The soft descriptor.
* @param Payments|null $payments The Payments.
*/
public function __construct(
Amount $amount,
array $items = array(),
Shipping $shipping = null,
string $reference_id = 'default',
string $description = '',
Payee $payee = null,
string $custom_id = '',
string $invoice_id = '',
string $soft_descriptor = '',
Payments $payments = null
) {
$this->amount = $amount;
$this->shipping = $shipping;
$this->reference_id = $reference_id;
$this->description = $description;
//phpcs:disable Inpsyde.CodeQuality.ArgumentTypeDeclaration.NoArgumentType
$this->items = array_values(
array_filter(
$items,
function ( $item ): bool {
$is_item = is_a( $item, Item::class );
/**
* The item.
*
* @var Item $item
*/
if ( $is_item && Item::PHYSICAL_GOODS === $item->category() ) {
$this->contains_physical_goods = true;
}
return $is_item;
}
)
);
$this->payee = $payee;
$this->custom_id = $custom_id;
$this->invoice_id = $invoice_id;
$this->soft_descriptor = $soft_descriptor;
$this->payments = $payments;
}
/**
* Returns the amount.
*
* @return Amount
*/
public function amount(): Amount {
return $this->amount;
}
/**
* Returns the shipping.
*
* @return Shipping|null
*/
public function shipping(): ?Shipping {
return $this->shipping;
}
/**
* Returns the reference id.
*
* @return string
*/
public function reference_id(): string {
return $this->reference_id;
}
/**
* Returns the description.
*
* @return string
*/
public function description(): string {
return $this->description;
}
/**
* Returns the custom id.
*
* @return string
*/
public function custom_id(): string {
return $this->custom_id;
}
/**
* Returns the invoice id.
*
* @return string
*/
public function invoice_id(): string {
return $this->invoice_id;
}
/**
* Returns the soft descriptor.
*
* @return string
*/
public function soft_descriptor(): string {
return $this->soft_descriptor;
}
/**
* Returns the Payee.
*
* @return Payee|null
*/
public function payee(): ?Payee {
return $this->payee;
}
/**
* Returns the Payments.
*
* @return Payments|null
*/
public function payments(): ?Payments {
return $this->payments;
}
/**
* Returns the Items.
*
* @return Item[]
*/
public function items(): array {
return $this->items;
}
/**
* Whether the unit contains physical goods.
*
* @return bool
*/
public function contains_physical_goods(): bool {
return $this->contains_physical_goods;
}
/**
* Returns the object as array.
*
* @return array
*/
public function to_array(): array {
$purchase_unit = array(
'reference_id' => $this->reference_id(),
'amount' => $this->amount()->to_array(),
'description' => $this->description(),
'items' => array_map(
static function ( Item $item ): array {
return $item->to_array();
},
$this->items()
),
);
if ( $this->ditch_items_when_mismatch( $this->amount(), ...$this->items() ) ) {
unset( $purchase_unit['items'] );
unset( $purchase_unit['amount']['breakdown'] );
}
if ( $this->payee() ) {
$purchase_unit['payee'] = $this->payee()->to_array();
}
if ( $this->payments() ) {
$purchase_unit['payments'] = $this->payments()->to_array();
}
if ( $this->shipping() ) {
$purchase_unit['shipping'] = $this->shipping()->to_array();
}
if ( $this->custom_id() ) {
$purchase_unit['custom_id'] = $this->custom_id();
}
if ( $this->invoice_id() ) {
$purchase_unit['invoice_id'] = $this->invoice_id();
}
if ( $this->soft_descriptor() ) {
$purchase_unit['soft_descriptor'] = $this->soft_descriptor();
}
return $purchase_unit;
}
/**
* All money values send to PayPal can only have 2 decimal points. Woocommerce internally does
* not have this restriction. Therefore the totals of the cart in Woocommerce and the totals
* of the rounded money values of the items, we send to PayPal, can differ. In those cases,
* we can not send the line items.
*
* @param Amount $amount The amount.
* @param Item ...$items The items.
* @return bool
*/
private function ditch_items_when_mismatch( Amount $amount, Item ...$items ): bool {
$fee_items_total = ( $amount->breakdown() && $amount->breakdown()->item_total() ) ?
$amount->breakdown()->item_total()->value() : null;
$fee_tax_total = ( $amount->breakdown() && $amount->breakdown()->tax_total() ) ?
$amount->breakdown()->tax_total()->value() : null;
foreach ( $items as $item ) {
if ( null !== $fee_items_total ) {
$fee_items_total -= $item->unit_amount()->value() * $item->quantity();
}
if ( null !== $fee_tax_total ) {
$fee_tax_total -= $item->tax()->value() * $item->quantity();
}
}
$fee_items_total = round( $fee_items_total, 2 );
$fee_tax_total = round( $fee_tax_total, 2 );
if ( 0.0 !== $fee_items_total || 0.0 !== $fee_tax_total ) {
return true;
}
$breakdown = $this->amount()->breakdown();
if ( ! $breakdown ) {
return false;
}
$amount_total = 0;
if ( $breakdown->shipping() ) {
$amount_total += $breakdown->shipping()->value();
}
if ( $breakdown->item_total() ) {
$amount_total += $breakdown->item_total()->value();
}
if ( $breakdown->discount() ) {
$amount_total -= $breakdown->discount()->value();
}
if ( $breakdown->tax_total() ) {
$amount_total += $breakdown->tax_total()->value();
}
if ( $breakdown->shipping_discount() ) {
$amount_total -= $breakdown->shipping_discount()->value();
}
if ( $breakdown->handling() ) {
$amount_total += $breakdown->handling()->value();
}
if ( $breakdown->insurance() ) {
$amount_total += $breakdown->insurance()->value();
}
$amount_value = $this->amount()->value();
$needs_to_ditch = (string) $amount_total !== (string) $amount_value;
return $needs_to_ditch;
}
}

View file

@ -0,0 +1,73 @@
<?php
/**
* The Shipping object.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Entity
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Entity;
/**
* Class Shipping
*/
class Shipping {
/**
* The name.
*
* @var string
*/
private $name;
/**
* The address.
*
* @var Address
*/
private $address;
/**
* Shipping constructor.
*
* @param string $name The name.
* @param Address $address The address.
*/
public function __construct( string $name, Address $address ) {
$this->name = $name;
$this->address = $address;
}
/**
* Returns the name.
*
* @return string
*/
public function name(): string {
return $this->name;
}
/**
* Returns the shipping address.
*
* @return Address
*/
public function address(): Address {
return $this->address;
}
/**
* Returns the object as array.
*
* @return array
*/
public function to_array(): array {
return array(
'name' => array(
'full_name' => $this->name(),
),
'address' => $this->address()->to_array(),
);
}
}

View file

@ -0,0 +1,123 @@
<?php
/**
* The Token object.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Entity
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Entity;
use Inpsyde\PayPalCommerce\ApiClient\Exception\RuntimeException;
/**
* Class Token
*/
class Token {
/**
* The Token data.
*
* @var \stdClass
*/
private $json;
/**
* The timestamp when the Token was created.
*
* @var int
*/
private $created;
/**
* Token constructor.
*
* @param \stdClass $json The JSON object.
* @throws RuntimeException When The JSON object is not valid.
*/
public function __construct( \stdClass $json ) {
if ( ! isset( $json->created ) ) {
$json->created = time();
}
if ( ! $this->validate( $json ) ) {
throw new RuntimeException( 'Token not valid' );
}
$this->json = $json;
}
/**
* Returns the timestamp when the Token is expired.
*
* @return int
*/
public function expiration_timestamp(): int {
return $this->json->created + $this->json->expires_in;
}
/**
* Returns the token.
*
* @return string
*/
public function token(): string {
return (string) $this->json->token;
}
/**
* Returns whether the Token is still valid.
*
* @return bool
*/
public function is_valid(): bool {
return time() < $this->json->created + $this->json->expires_in;
}
/**
* Returns the Token as JSON string.
*
* @return string
*/
public function as_json(): string {
return wp_json_encode( $this->json );
}
/**
* Returns a Token based off a JSON string.
*
* @param string $json The JSON string.
*
* @return static
*/
public static function from_json( string $json ): self {
$json = (object) json_decode( $json );
if ( isset( $json->access_token ) || isset( $json->client_token ) ) {
$json->token = isset( $json->access_token ) ? $json->access_token : $json->client_token;
}
return new Token( $json );
}
/**
* Validates whether a JSON object can be transformed to a Token object.
*
* @param \stdClass $json The JSON object.
*
* @return bool
*/
private function validate( \stdClass $json ): bool {
$property_map = array(
'created' => 'is_int',
'expires_in' => 'is_int',
'token' => 'is_string',
);
foreach ( $property_map as $property => $validator ) {
if ( ! isset( $json->{$property} ) || ! $validator( $json->{$property} ) ) {
return false;
}
}
return true;
}
}

View file

@ -0,0 +1,97 @@
<?php
/**
* The Webhook object.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Entity
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Entity;
/**
* Class Webhook
*/
class Webhook {
/**
* The ID of the webhook.
*
* @var string
*/
private $id;
/**
* The URL of the webhook.
*
* @var string
*/
private $url;
/**
* The event types.
*
* @var string[]
*/
private $event_types;
/**
* Webhook constructor.
*
* @param string $url The URL of the webhook.
* @param string[] $event_types The associated event types.
* @param string $id The id of the webhook.
*/
public function __construct( string $url, array $event_types, string $id = '' ) {
$this->url = $url;
$this->event_types = $event_types;
$this->id = $id;
}
/**
* Returns the id of the webhook.
*
* @return string
*/
public function id(): string {
return $this->id;
}
/**
* Returns the URL listening to the hook.
*
* @return string
*/
public function url(): string {
return $this->url;
}
/**
* Returns the event types.
*
* @return array
*/
public function event_types(): array {
return $this->event_types;
}
/**
* Returns the object as array.
*
* @return array
*/
public function to_array(): array {
$data = array(
'url' => $this->url(),
'event_types' => $this->event_types(),
);
if ( $this->id() ) {
$data['id'] = $this->id();
}
return $data;
}
}

View file

@ -0,0 +1,21 @@
<?php
/**
* The modules Not Found exception.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Exception
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Exception;
use Psr\Container\NotFoundExceptionInterface;
use Exception;
/**
* Class NotFoundException
*/
class NotFoundException extends Exception implements NotFoundExceptionInterface {
}

View file

@ -0,0 +1,109 @@
<?php
/**
* The PayPal API Exception.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Exception
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Exception;
/**
* Class PayPalApiException
*/
class PayPalApiException extends RuntimeException {
/**
* The JSON response object of PayPal.
*
* @var \stdClass
*/
private $response;
/**
* The HTTP status code of the PayPal response.
*
* @var int
*/
private $status_code;
/**
* PayPalApiException constructor.
*
* @param \stdClass|null $response The JSON object.
* @param int $status_code The HTTP status code.
*/
public function __construct( \stdClass $response = null, int $status_code = 0 ) {
if ( is_null( $response ) ) {
$response = new \stdClass();
}
if ( ! isset( $response->message ) ) {
$response->message = __(
'Unknown error while connecting to PayPal.',
'paypal-for-woocommerce'
);
}
if ( ! isset( $response->name ) ) {
$response->name = __( 'Error', 'paypal-for-woocommerce' );
}
if ( ! isset( $response->details ) ) {
$response->details = array();
}
if ( ! isset( $response->links ) || ! is_array( $response->links ) ) {
$response->links = array();
}
/**
* The JSON response object.
*
* @var \stdClass $response
*/
$this->response = $response;
$this->status_code = $status_code;
$message = $response->message;
if ( $response->name ) {
$message = '[' . $response->name . '] ' . $message;
}
foreach ( $response->links as $link ) {
if ( isset( $link->rel ) && 'information_link' === $link->rel ) {
$message .= ' ' . $link->href;
}
}
parent::__construct( $message, $status_code );
}
/**
* The name of the exception.
*
* @return string
*/
public function name(): string {
return $this->response->name;
}
/**
* The details of the Exception.
*
* @return array
*/
public function details(): array {
return $this->response->details;
}
/**
* Whether a certain detail is part of the exception reason.
*
* @param string $issue The issue.
*
* @return bool
*/
public function has_detail( string $issue ): bool {
foreach ( $this->details() as $detail ) {
if ( isset( $detail->issue ) && $detail->issue === $issue ) {
return true;
}
}
return false;
}
}

View file

@ -0,0 +1,18 @@
<?php
/**
* The modules runtime exception.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Exception
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Exception;
/**
* Class RuntimeException
*/
class RuntimeException extends \RuntimeException {
}

View file

@ -0,0 +1,86 @@
<?php
/**
* The Address factory.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Factory
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Factory;
use Inpsyde\PayPalCommerce\ApiClient\Entity\Address;
use Inpsyde\PayPalCommerce\ApiClient\Exception\RuntimeException;
/**
* Class AddressFactory
*/
class AddressFactory {
/**
* Returns either the shipping or billing Address object of a customer.
*
* @param \WC_Customer $customer The Woocommerce customer.
* @param string $type Either 'shipping' or 'billing'.
*
* @return Address
*/
public function from_wc_customer( \WC_Customer $customer, string $type = 'shipping' ): Address {
return new Address(
( 'shipping' === $type ) ?
$customer->get_shipping_country() : $customer->get_billing_country(),
( 'shipping' === $type ) ?
$customer->get_shipping_address_1() : $customer->get_billing_address_1(),
( 'shipping' === $type ) ?
$customer->get_shipping_address_2() : $customer->get_billing_address_2(),
( 'shipping' === $type ) ?
$customer->get_shipping_state() : $customer->get_billing_state(),
( 'shipping' === $type ) ?
$customer->get_shipping_city() : $customer->get_billing_city(),
( 'shipping' === $type ) ?
$customer->get_shipping_postcode() : $customer->get_billing_postcode()
);
}
/**
* Returns an Address object based of a Woocommerce order.
*
* @param \WC_Order $order The order.
*
* @return Address
*/
public function from_wc_order( \WC_Order $order ): Address {
return new Address(
$order->get_shipping_country(),
$order->get_shipping_address_1(),
$order->get_shipping_address_2(),
$order->get_shipping_state(),
$order->get_shipping_city(),
$order->get_shipping_postcode()
);
}
/**
* Creates an Address object based off a PayPal Response.
*
* @param \stdClass $data The JSON object.
*
* @return Address
* @throws RuntimeException When JSON object is malformed.
*/
public function from_paypal_response( \stdClass $data ): Address {
if ( ! isset( $data->country_code ) ) {
throw new RuntimeException(
__( 'No country given for address.', 'paypal-for-woocommerce' )
);
}
return new Address(
$data->country_code,
( isset( $data->address_line_1 ) ) ? $data->address_line_1 : '',
( isset( $data->address_line_2 ) ) ? $data->address_line_2 : '',
( isset( $data->admin_area_1 ) ) ? $data->admin_area_1 : '',
( isset( $data->admin_area_2 ) ) ? $data->admin_area_2 : '',
( isset( $data->postal_code ) ) ? $data->postal_code : ''
);
}
}

View file

@ -0,0 +1,222 @@
<?php
/**
* The Amount factory.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Factory
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Factory;
use Inpsyde\PayPalCommerce\ApiClient\Entity\Amount;
use Inpsyde\PayPalCommerce\ApiClient\Entity\AmountBreakdown;
use Inpsyde\PayPalCommerce\ApiClient\Entity\Item;
use Inpsyde\PayPalCommerce\ApiClient\Entity\Money;
use Inpsyde\PayPalCommerce\ApiClient\Exception\RuntimeException;
/**
* Class AmountFactory
*/
class AmountFactory {
/**
* The item factory.
*
* @var ItemFactory
*/
private $item_factory;
/**
* AmountFactory constructor.
*
* @param ItemFactory $item_factory The Item factory.
*/
public function __construct( ItemFactory $item_factory ) {
$this->item_factory = $item_factory;
}
/**
* Returns an Amount object based off a Woocommerce cart.
*
* @param \WC_Cart $cart The cart.
*
* @return Amount
*/
public function from_wc_cart( \WC_Cart $cart ): Amount {
$currency = get_woocommerce_currency();
$total = new Money( (float) $cart->get_total( 'numeric' ), $currency );
$item_total = $cart->get_cart_contents_total() + $cart->get_discount_total();
$item_total = new Money( (float) $item_total, $currency );
$shipping = new Money(
(float) $cart->get_shipping_total() + $cart->get_shipping_tax(),
$currency
);
$taxes = new Money(
(float) $cart->get_cart_contents_tax() + (float) $cart->get_discount_tax(),
$currency
);
$discount = null;
if ( $cart->get_discount_total() ) {
$discount = new Money(
(float) $cart->get_discount_total() + $cart->get_discount_tax(),
$currency
);
}
$breakdown = new AmountBreakdown(
$item_total,
$shipping,
$taxes,
null, // insurance?
null, // handling?
null, // shipping discounts?
$discount
);
$amount = new Amount(
$total,
$breakdown
);
return $amount;
}
/**
* Returns an Amount object based off a Woocommerce order.
*
* @param \WC_Order $order The order.
*
* @return Amount
*/
public function from_wc_order( \WC_Order $order ): Amount {
$currency = $order->get_currency();
$items = $this->item_factory->from_wc_order( $order );
$total = new Money( (float) $order->get_total(), $currency );
$item_total = new Money(
(float) array_reduce(
$items,
static function ( float $total, Item $item ): float {
return $total + $item->quantity() * $item->unit_amount()->value();
},
0
),
$currency
);
$shipping = new Money(
(float) $order->get_shipping_total() + (float) $order->get_shipping_tax(),
$currency
);
$taxes = new Money(
(float) array_reduce(
$items,
static function ( float $total, Item $item ): float {
return $total + $item->quantity() * $item->tax()->value();
},
0
),
$currency
);
$discount = null;
if ( (float) $order->get_total_discount( false ) ) {
$discount = new Money(
(float) $order->get_total_discount( false ),
$currency
);
}
$breakdown = new AmountBreakdown(
$item_total,
$shipping,
$taxes,
null, // insurance?
null, // handling?
null, // shipping discounts?
$discount
);
$amount = new Amount(
$total,
$breakdown
);
return $amount;
}
/**
* Returns an Amount object based off a PayPal Response.
*
* @param \stdClass $data The JSON object.
*
* @return Amount
* @throws RuntimeException When JSON object is malformed.
*/
public function from_paypal_response( \stdClass $data ): Amount {
if ( ! isset( $data->value ) || ! is_numeric( $data->value ) ) {
throw new RuntimeException( __( 'No value given', 'paypal-for-woocommerce' ) );
}
if ( ! isset( $data->currency_code ) ) {
throw new RuntimeException(
__( 'No currency given', 'paypal-for-woocommerce' )
);
}
$money = new Money( (float) $data->value, $data->currency_code );
$breakdown = ( isset( $data->breakdown ) ) ? $this->break_down( $data->breakdown ) : null;
return new Amount( $money, $breakdown );
}
/**
* Returns a AmountBreakdown object based off a PayPal response.
*
* @param \stdClass $data The JSON object.
*
* @return AmountBreakdown
* @throws RuntimeException When JSON object is malformed.
*/
private function break_down( \stdClass $data ): AmountBreakdown {
/**
* The order of the keys equals the necessary order of the constructor arguments.
*/
$ordered_constructor_keys = array(
'item_total',
'shipping',
'tax_total',
'handling',
'insurance',
'shipping_discount',
'discount',
);
$money = array();
foreach ( $ordered_constructor_keys as $key ) {
if ( ! isset( $data->{$key} ) ) {
$money[] = null;
continue;
}
$item = $data->{$key};
if ( ! isset( $item->value ) || ! is_numeric( $item->value ) ) {
throw new RuntimeException(
sprintf(
// translators: %s is the current breakdown key.
__( 'No value given for breakdown %s', 'paypal-for-woocommerce' ),
$key
)
);
}
if ( ! isset( $item->currency_code ) ) {
throw new RuntimeException(
sprintf(
// translators: %s is the current breakdown key.
__( 'No currency given for breakdown %s', 'paypal-for-woocommerce' ),
$key
)
);
}
$money[] = new Money( (float) $item->value, $item->currency_code );
}
return new AmountBreakdown( ...$money );
}
}

View file

@ -0,0 +1,44 @@
<?php
/**
* The ApplicationContext factory.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Factory
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Factory;
use Inpsyde\PayPalCommerce\ApiClient\Entity\ApplicationContext;
/**
* Class ApplicationContextFactory
*/
class ApplicationContextFactory {
/**
* Returns an Application Context based off a PayPal Response.
*
* @param \stdClass $data The JSON object.
*
* @return ApplicationContext
*/
public function from_paypal_response( \stdClass $data ): ApplicationContext {
return new ApplicationContext(
isset( $data->return_url ) ?
$data->return_url : '',
isset( $data->cancel_url ) ?
$data->cancel_url : '',
isset( $data->brand_name ) ?
$data->brand_name : '',
isset( $data->locale ) ?
$data->locale : '',
isset( $data->landing_page ) ?
$data->landing_page : ApplicationContext::LANDING_PAGE_NO_PREFERENCE,
isset( $data->shipping_preference ) ?
$data->shipping_preference : ApplicationContext::SHIPPING_PREFERENCE_GET_FROM_FILE,
isset( $data->user_action ) ?
$data->user_action : ApplicationContext::USER_ACTION_CONTINUE
);
}
}

View file

@ -0,0 +1,47 @@
<?php
/**
* The Authorization factory.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Factory
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Factory;
use Inpsyde\PayPalCommerce\ApiClient\Entity\Authorization;
use Inpsyde\PayPalCommerce\ApiClient\Entity\AuthorizationStatus;
use Inpsyde\PayPalCommerce\ApiClient\Exception\RuntimeException;
/**
* Class AuthorizationFactory
*/
class AuthorizationFactory {
/**
* Returns an Authorization based off a PayPal response.
*
* @param \stdClass $data The JSON object.
*
* @return Authorization
* @throws RuntimeException When JSON object is malformed.
*/
public function from_paypal_response( \stdClass $data ): Authorization {
if ( ! isset( $data->id ) ) {
throw new RuntimeException(
__( 'Does not contain an id.', 'paypal-for-woocommerce' )
);
}
if ( ! isset( $data->status ) ) {
throw new RuntimeException(
__( 'Does not contain status.', 'paypal-for-woocommerce' )
);
}
return new Authorization(
$data->id,
new AuthorizationStatus( $data->status )
);
}
}

View file

@ -0,0 +1,155 @@
<?php
/**
* The Item factory.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Factory
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Factory;
use Inpsyde\PayPalCommerce\ApiClient\Entity\Item;
use Inpsyde\PayPalCommerce\ApiClient\Entity\Money;
use Inpsyde\PayPalCommerce\ApiClient\Exception\RuntimeException;
/**
* Class ItemFactory
*/
class ItemFactory {
/**
* Creates items based off a Woocommerce cart.
*
* @param \WC_Cart $cart The cart.
*
* @return Item[]
*/
public function from_wc_cart( \WC_Cart $cart ): array {
$currency = get_woocommerce_currency();
$items = array_map(
static function ( array $item ) use ( $currency ): Item {
$product = $item['data'];
/**
* The Woocommerce product.
*
* @var \WC_Product $product
*/
$quantity = (int) $item['quantity'];
$price = (float) wc_get_price_including_tax( $product );
$price_without_tax = (float) wc_get_price_excluding_tax( $product );
$price_without_tax_rounded = round( $price_without_tax, 2 );
$tax = round( $price - $price_without_tax_rounded, 2 );
$tax = new Money( $tax, $currency );
return new Item(
mb_substr( $product->get_name(), 0, 127 ),
new Money( $price_without_tax_rounded, $currency ),
$quantity,
mb_substr( $product->get_description(), 0, 127 ),
$tax,
$product->get_sku(),
( $product->is_virtual() ) ? Item::DIGITAL_GOODS : Item::PHYSICAL_GOODS
);
},
$cart->get_cart_contents()
);
return $items;
}
/**
* Creates Items based off a Woocommerce order.
*
* @param \WC_Order $order The order.
* @return Item[]
*/
public function from_wc_order( \WC_Order $order ): array {
return array_map(
function ( \WC_Order_Item_Product $item ) use ( $order ): Item {
return $this->from_wc_order_line_item( $item, $order );
},
$order->get_items( 'line_item' )
);
}
/**
* Creates an Item based off a Woocommerce Order Item.
*
* @param \WC_Order_Item_Product $item The Woocommerce order item.
* @param \WC_Order $order The Woocommerce order.
*
* @return Item
*/
private function from_wc_order_line_item( \WC_Order_Item_Product $item, \WC_Order $order ): Item {
$currency = $order->get_currency();
$product = $item->get_product();
/**
* The Woocommerce product.
*
* @var \WC_Product $product
*/
$quantity = $item->get_quantity();
$price = (float) $order->get_item_subtotal( $item, true );
$price_without_tax = (float) $order->get_item_subtotal( $item, false );
$price_without_tax_rounded = round( $price_without_tax, 2 );
$tax = round( $price - $price_without_tax_rounded, 2 );
$tax = new Money( $tax, $currency );
return new Item(
mb_substr( $product->get_name(), 0, 127 ),
new Money( $price_without_tax_rounded, $currency ),
$quantity,
mb_substr( $product->get_description(), 0, 127 ),
$tax,
$product->get_sku(),
( $product->is_virtual() ) ? Item::DIGITAL_GOODS : Item::PHYSICAL_GOODS
);
}
/**
* Creates an Item based off a PayPal response.
*
* @param \stdClass $data The JSON object.
*
* @return Item
* @throws RuntimeException When JSON object is malformed.
*/
public function from_paypal_response( \stdClass $data ): Item {
if ( ! isset( $data->name ) ) {
throw new RuntimeException(
__( 'No name for item given', 'paypal-for-woocommerce' )
);
}
if ( ! isset( $data->quantity ) || ! is_numeric( $data->quantity ) ) {
throw new RuntimeException(
__( 'No quantity for item given', 'paypal-for-woocommerce' )
);
}
if ( ! isset( $data->unit_amount->value ) || ! isset( $data->unit_amount->currency_code ) ) {
throw new RuntimeException(
__( 'No money values for item given', 'paypal-for-woocommerce' )
);
}
$unit_amount = new Money( (float) $data->unit_amount->value, $data->unit_amount->currency_code );
$description = ( isset( $data->description ) ) ? $data->description : '';
$tax = ( isset( $data->tax ) ) ?
new Money( (float) $data->tax->value, $data->tax->currency_code )
: null;
$sku = ( isset( $data->sku ) ) ? $data->sku : '';
$category = ( isset( $data->category ) ) ? $data->category : 'PHYSICAL_GOODS';
return new Item(
$data->name,
$unit_amount,
(int) $data->quantity,
$description,
$tax,
$sku,
$category
);
}
}

View file

@ -0,0 +1,171 @@
<?php
/**
* The Order factory.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Factory
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Factory;
use Inpsyde\PayPalCommerce\ApiClient\Entity\Order;
use Inpsyde\PayPalCommerce\ApiClient\Entity\OrderStatus;
use Inpsyde\PayPalCommerce\ApiClient\Entity\PurchaseUnit;
use Inpsyde\PayPalCommerce\ApiClient\Exception\RuntimeException;
use Inpsyde\PayPalCommerce\ApiClient\Repository\ApplicationContextRepository;
/**
* Class OrderFactory
*/
class OrderFactory {
/**
* The PurchaseUnit factory.
*
* @var PurchaseUnitFactory
*/
private $purchase_unit_factory;
/**
* The Payer factory.
*
* @var PayerFactory
*/
private $payer_factory;
/**
* The ApplicationContext repository.
*
* @var ApplicationContextRepository
*/
private $application_context_repository;
/**
* The ApplicationContext factory.
*
* @var ApplicationContextFactory
*/
private $application_context_factory;
/**
* The PaymentSource factory.
*
* @var PaymentSourceFactory
*/
private $payment_source_factory;
/**
* OrderFactory constructor.
*
* @param PurchaseUnitFactory $purchase_unit_factory The PurchaseUnit factory.
* @param PayerFactory $payer_factory The Payer factory.
* @param ApplicationContextRepository $application_context_repository The Application Context repository.
* @param ApplicationContextFactory $application_context_factory The Application Context factory.
* @param PaymentSourceFactory $payment_source_factory The Payment Source factory.
*/
public function __construct(
PurchaseUnitFactory $purchase_unit_factory,
PayerFactory $payer_factory,
ApplicationContextRepository $application_context_repository,
ApplicationContextFactory $application_context_factory,
PaymentSourceFactory $payment_source_factory
) {
$this->purchase_unit_factory = $purchase_unit_factory;
$this->payer_factory = $payer_factory;
$this->application_context_repository = $application_context_repository;
$this->application_context_factory = $application_context_factory;
$this->payment_source_factory = $payment_source_factory;
}
/**
* Creates an Order object based off a Woocommerce order and another Order object.
*
* @param \WC_Order $wc_order The Woocommerce order.
* @param Order $order The order object.
*
* @return Order
*/
public function from_wc_order( \WC_Order $wc_order, Order $order ): Order {
$purchase_units = array( $this->purchase_unit_factory->from_wc_order( $wc_order ) );
return new Order(
$order->id(),
$purchase_units,
$order->status(),
$order->application_context(),
$order->payment_source(),
$order->payer(),
$order->intent(),
$order->create_time(),
$order->update_time()
);
}
/**
* Returns an Order object based off a PayPal Response.
*
* @param \stdClass $order_data The JSON object.
*
* @return Order
* @throws RuntimeException When JSON object is malformed.
*/
public function from_paypal_response( \stdClass $order_data ): Order {
if ( ! isset( $order_data->id ) ) {
throw new RuntimeException(
__( 'Order does not contain an id.', 'paypal-for-woocommerce' )
);
}
if ( ! isset( $order_data->purchase_units ) || ! is_array( $order_data->purchase_units ) ) {
throw new RuntimeException(
__( 'Order does not contain items.', 'paypal-for-woocommerce' )
);
}
if ( ! isset( $order_data->status ) ) {
throw new RuntimeException(
__( 'Order does not contain status.', 'paypal-for-woocommerce' )
);
}
if ( ! isset( $order_data->intent ) ) {
throw new RuntimeException(
__( 'Order does not contain intent.', 'paypal-for-woocommerce' )
);
}
$purchase_units = array_map(
function ( \stdClass $data ): PurchaseUnit {
return $this->purchase_unit_factory->from_paypal_response( $data );
},
$order_data->purchase_units
);
$create_time = ( isset( $order_data->create_time ) ) ?
\DateTime::createFromFormat( \DateTime::ISO8601, $order_data->create_time )
: null;
$update_time = ( isset( $order_data->update_time ) ) ?
\DateTime::createFromFormat( \DateTime::ISO8601, $order_data->update_time )
: null;
$payer = ( isset( $order_data->payer ) ) ?
$this->payer_factory->from_paypal_response( $order_data->payer )
: null;
$application_context = ( isset( $order_data->application_context ) ) ?
$this->application_context_factory->from_paypal_response( $order_data->application_context )
: null;
$payment_source = ( isset( $order_data->payment_source ) ) ?
$this->payment_source_factory->from_paypal_response( $order_data->payment_source ) :
null;
return new Order(
$order_data->id,
$purchase_units,
new OrderStatus( $order_data->status ),
$application_context,
$payment_source,
$payer,
$order_data->intent,
$create_time,
$update_time
);
}
}

View file

@ -0,0 +1,85 @@
<?php
/**
* The PatchCollection factory.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Factory
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Factory;
use Inpsyde\PayPalCommerce\ApiClient\Entity\Order;
use Inpsyde\PayPalCommerce\ApiClient\Entity\Patch;
use Inpsyde\PayPalCommerce\ApiClient\Entity\PatchCollection;
use Inpsyde\PayPalCommerce\ApiClient\Entity\PurchaseUnit;
/**
* Class PatchCollectionFactory
*/
class PatchCollectionFactory {
/**
* Creates a Patch Collection by comparing two orders.
*
* @param Order $from The inital order.
* @param Order $to The target order.
*
* @return PatchCollection
*/
public function from_orders( Order $from, Order $to ): PatchCollection {
$all_patches = array();
$all_patches += $this->purchase_units( $from->purchase_units(), $to->purchase_units() );
return new PatchCollection( ...$all_patches );
}
/**
* Returns patches from purchase units diffs.
*
* @param PurchaseUnit[] $from The Purchase Units to start with.
* @param PurchaseUnit[] $to The Purchase Units to end with after patches where applied.
*
* @return Patch[]
*/
private function purchase_units( array $from, array $to ): array {
$patches = array();
$path = '/purchase_units';
foreach ( $to as $purchase_unit_to ) {
$needs_update = ! count(
array_filter(
$from,
static function ( PurchaseUnit $unit ) use ( $purchase_unit_to ): bool {
//phpcs:disable WordPress.PHP.StrictComparisons.LooseComparison
// Loose comparison needed to compare two objects.
return $unit == $purchase_unit_to;
//phpcs:enable WordPress.PHP.StrictComparisons.LooseComparison
}
)
);
$needs_update = true;
if ( ! $needs_update ) {
continue;
}
$purchase_unit_from = current(
array_filter(
$from,
static function ( PurchaseUnit $unit ) use ( $purchase_unit_to ): bool {
return $purchase_unit_to->reference_id() === $unit->reference_id();
}
)
);
$operation = $purchase_unit_from ? 'replace' : 'add';
$value = $purchase_unit_to->to_array();
$patches[] = new Patch(
$operation,
$path . "/@reference_id=='" . $purchase_unit_to->reference_id() . "'",
$value
);
}
return $patches;
}
}

View file

@ -0,0 +1,38 @@
<?php
/**
* The Payee Factory.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Factory
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Factory;
use Inpsyde\PayPalCommerce\ApiClient\Entity\Payee;
use Inpsyde\PayPalCommerce\ApiClient\Exception\RuntimeException;
/**
* Class PayeeFactory
*/
class PayeeFactory {
/**
* Returns a Payee object based off a PayPal Response.
*
* @param \stdClass $data The JSON object.
*
* @return Payee|null
* @throws RuntimeException When JSON object is malformed.
*/
public function from_paypal_response( \stdClass $data ): ?Payee {
if ( ! isset( $data->email_address ) ) {
throw new RuntimeException(
__( 'No email for payee given.', 'paypal-for-woocommerce' )
);
}
$merchant_id = ( isset( $data->merchant_id ) ) ? $data->merchant_id : '';
return new Payee( $data->email_address, $merchant_id );
}
}

View file

@ -0,0 +1,111 @@
<?php
/**
* The Payer factory.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Factory
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Factory;
use Inpsyde\PayPalCommerce\ApiClient\Entity\Payer;
use Inpsyde\PayPalCommerce\ApiClient\Entity\PayerName;
use Inpsyde\PayPalCommerce\ApiClient\Entity\PayerTaxInfo;
use Inpsyde\PayPalCommerce\ApiClient\Entity\Phone;
use Inpsyde\PayPalCommerce\ApiClient\Entity\PhoneWithType;
/**
* Class PayerFactory
*/
class PayerFactory {
/**
* The address factory.
*
* @var AddressFactory
*/
private $address_factory;
/**
* PayerFactory constructor.
*
* @param AddressFactory $address_factory The Address factory.
*/
public function __construct( AddressFactory $address_factory ) {
$this->address_factory = $address_factory;
}
/**
* Returns a Payer object based off a Woocommerce customer.
*
* @param \WC_Customer $customer The Woocommerce customer.
*
* @return Payer
*/
public function from_customer( \WC_Customer $customer ): Payer {
$payer_id = '';
$birthdate = null;
$phone = null;
if ( $customer->get_billing_phone() ) {
// make sure the phone number contains only numbers and is max 14. chars long.
$national_number = $customer->get_billing_phone();
$national_number = preg_replace( '/[^0-9]/', '', $national_number );
$national_number = substr( $national_number, 0, 14 );
$phone = new PhoneWithType(
'HOME',
new Phone( $national_number )
);
}
return new Payer(
new PayerName(
$customer->get_billing_first_name(),
$customer->get_billing_last_name()
),
$customer->get_billing_email(),
$payer_id,
$this->address_factory->from_wc_customer( $customer, 'billing' ),
$birthdate,
$phone
);
}
/**
* Returns a Payer object based off a PayPal Response.
*
* @param \stdClass $data The JSON object.
*
* @return Payer
*/
public function from_paypal_response( \stdClass $data ): Payer {
$address = $this->address_factory->from_paypal_response( $data->address );
$payer_name = new PayerName(
isset( $data->name->given_name ) ? (string) $data->name->given_name : '',
isset( $data->name->surname ) ? (string) $data->name->surname : ''
);
// TODO deal with phones without type instead of passing a invalid type.
$phone = ( isset( $data->phone ) ) ? new PhoneWithType(
( isset( $data->phone->phone_type ) ) ? $data->phone->phone_type : 'undefined',
new Phone(
$data->phone->phone_number->national_number
)
) : null;
$tax_info = ( isset( $data->tax_info ) ) ?
new PayerTaxInfo( $data->tax_info->tax_id, $data->tax_info->tax_id_type )
: null;
$birth_date = ( isset( $data->birth_date ) ) ?
\DateTime::createFromFormat( 'Y-m-d', $data->birth_date )
: null;
return new Payer(
$payer_name,
isset( $data->email_address ) ? $data->email_address : '',
( isset( $data->payer_id ) ) ? $data->payer_id : '',
$address,
$birth_date,
$phone,
$tax_info
);
}
}

View file

@ -0,0 +1,56 @@
<?php
/**
* The Payments factory.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Factory
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Factory;
use Inpsyde\PayPalCommerce\ApiClient\Entity\Authorization;
use Inpsyde\PayPalCommerce\ApiClient\Entity\Payments;
/**
* Class PaymentsFactory
*/
class PaymentsFactory {
/**
* The Authorization factory.
*
* @var AuthorizationFactory
*/
private $authorization_factory;
/**
* PaymentsFactory constructor.
*
* @param AuthorizationFactory $authorization_factory The AuthorizationFactory.
*/
public function __construct(
AuthorizationFactory $authorization_factory
) {
$this->authorization_factory = $authorization_factory;
}
/**
* Returns a Payments object based off a PayPal response.
*
* @param \stdClass $data The JSON object.
*
* @return Payments
*/
public function from_paypal_response( \stdClass $data ): Payments {
$authorizations = array_map(
function ( \stdClass $authorization ): Authorization {
return $this->authorization_factory->from_paypal_response( $authorization );
},
isset( $data->authorizations ) ? $data->authorizations : array()
);
$payments = new Payments( ...$authorizations );
return $payments;
}
}

View file

@ -0,0 +1,53 @@
<?php
/**
* The PaymentSource factory.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Factory
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Factory;
use Inpsyde\PayPalCommerce\ApiClient\Entity\CardAuthenticationResult;
use Inpsyde\PayPalCommerce\ApiClient\Entity\PaymentSource;
use Inpsyde\PayPalCommerce\ApiClient\Entity\PaymentSourceCard;
/**
* Class PaymentSourceFactory
*/
class PaymentSourceFactory {
/**
* Returns a PaymentSource for a PayPal Response.
*
* @param \stdClass $data The JSON object.
*
* @return PaymentSource
*/
public function from_paypal_response( \stdClass $data ): PaymentSource {
$card = null;
$wallet = null;
if ( isset( $data->card ) ) {
$authentication_result = null;
if ( isset( $data->card->authentication_result ) ) {
$authentication_result = new CardAuthenticationResult(
isset( $data->card->authentication_result->liability_shift ) ?
(string) $data->card->authentication_result->liability_shift : '',
isset( $data->card->authentication_result->three_d_secure->enrollment_status ) ?
(string) $data->card->authentication_result->three_d_secure->enrollment_status : '',
isset( $data->card->authentication_result->three_d_secure->authentication_result ) ?
(string) $data->card->authentication_result->three_d_secure->authentication_result : ''
);
}
$card = new PaymentSourceCard(
isset( $data->card->last_digits ) ? (string) $data->card->last_digits : '',
isset( $data->card->brand ) ? (string) $data->card->brand : '',
isset( $data->card->type ) ? (string) $data->card->type : '',
$authentication_result
);
}
return new PaymentSource( $card, $wallet );
}
}

View file

@ -0,0 +1,50 @@
<?php
/**
* The PaymentToken Factory.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Factory
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Factory;
use Inpsyde\PayPalCommerce\ApiClient\Entity\PaymentToken;
use Inpsyde\PayPalCommerce\ApiClient\Exception\RuntimeException;
/**
* Class PaymentTokenFactory
*/
class PaymentTokenFactory {
/**
* Returns a PaymentToken based off a PayPal Response object.
*
* @param \stdClass $data The JSON object.
*
* @return PaymentToken
* @throws RuntimeException When JSON object is malformed.
*/
public function from_paypal_response( \stdClass $data ): PaymentToken {
if ( ! isset( $data->id ) ) {
throw new RuntimeException(
__( 'No id for payment token given', 'paypal-for-woocommerce' )
);
}
return new PaymentToken(
$data->id,
( isset( $data->type ) ) ? $data->type : PaymentToken::TYPE_PAYMENT_METHOD_TOKEN
);
}
/**
* Creates a payment token based off a data array.
*
* @param array $data The data array.
*
* @return PaymentToken
*/
public function from_array( array $data ): PaymentToken {
return $this->from_paypal_response( (object) $data );
}
}

View file

@ -0,0 +1,246 @@
<?php
/**
* The PurchaseUnit factory.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Factory
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Factory;
use Inpsyde\PayPalCommerce\ApiClient\Entity\Item;
use Inpsyde\PayPalCommerce\ApiClient\Entity\PurchaseUnit;
use Inpsyde\PayPalCommerce\ApiClient\Exception\RuntimeException;
use Inpsyde\PayPalCommerce\ApiClient\Repository\PayeeRepository;
/**
* Class PurchaseUnitFactory
*/
class PurchaseUnitFactory {
/**
* The amount factory.
*
* @var AmountFactory
*/
private $amount_factory;
/**
* The payee repository.
*
* @var PayeeRepository
*/
private $payee_repository;
/**
* The payee factory.
*
* @var PayeeFactory
*/
private $payee_factory;
/**
* The item factory.
*
* @var ItemFactory
*/
private $item_factory;
/**
* The shipping factory.
*
* @var ShippingFactory
*/
private $shipping_factory;
/**
* The payments factory.
*
* @var PaymentsFactory
*/
private $payments_factory;
/**
* The Prefix.
*
* @var string
*/
private $prefix;
/**
* PurchaseUnitFactory constructor.
*
* @param AmountFactory $amount_factory The amount factory.
* @param PayeeRepository $payee_repository The Payee repository.
* @param PayeeFactory $payee_factory The Payee factory.
* @param ItemFactory $item_factory The item factory.
* @param ShippingFactory $shipping_factory The shipping factory.
* @param PaymentsFactory $payments_factory The payments factory.
* @param string $prefix The prefix.
*/
public function __construct(
AmountFactory $amount_factory,
PayeeRepository $payee_repository,
PayeeFactory $payee_factory,
ItemFactory $item_factory,
ShippingFactory $shipping_factory,
PaymentsFactory $payments_factory,
string $prefix = 'WC-'
) {
$this->amount_factory = $amount_factory;
$this->payee_repository = $payee_repository;
$this->payee_factory = $payee_factory;
$this->item_factory = $item_factory;
$this->shipping_factory = $shipping_factory;
$this->payments_factory = $payments_factory;
$this->prefix = $prefix;
}
/**
* Creates a PurchaseUnit based off a Woocommerce order.
*
* @param \WC_Order $order The order.
*
* @return PurchaseUnit
*/
public function from_wc_order( \WC_Order $order ): PurchaseUnit {
$amount = $this->amount_factory->from_wc_order( $order );
$items = $this->item_factory->from_wc_order( $order );
$shipping = $this->shipping_factory->from_wc_order( $order );
if (
empty( $shipping->address()->country_code() ) ||
( $shipping->address()->country_code() && ! $shipping->address()->postal_code() )
) {
$shipping = null;
}
$reference_id = 'default';
$description = '';
$payee = $this->payee_repository->payee();
$wc_order_id = $order->get_id();
$custom_id = $this->prefix . $wc_order_id;
$invoice_id = $this->prefix . $wc_order_id;
$soft_descriptor = '';
$purchase_unit = new PurchaseUnit(
$amount,
$items,
$shipping,
$reference_id,
$description,
$payee,
$custom_id,
$invoice_id,
$soft_descriptor
);
return $purchase_unit;
}
/**
* Creates a PurchaseUnit based off a Woocommerce cart.
*
* @param \WC_Cart $cart The cart.
*
* @return PurchaseUnit
*/
public function from_wc_cart( \WC_Cart $cart ): PurchaseUnit {
$amount = $this->amount_factory->from_wc_cart( $cart );
$items = $this->item_factory->from_wc_cart( $cart );
$shipping = null;
$customer = \WC()->customer;
if ( is_a( $customer, \WC_Customer::class ) ) {
$shipping = $this->shipping_factory->from_wc_customer( \WC()->customer );
if (
! $shipping->address()->country_code()
|| ( $shipping->address()->country_code() && ! $shipping->address()->postal_code() )
) {
$shipping = null;
}
}
$reference_id = 'default';
$description = '';
$payee = $this->payee_repository->payee();
$custom_id = '';
$invoice_id = '';
$soft_descriptor = '';
$purchase_unit = new PurchaseUnit(
$amount,
$items,
$shipping,
$reference_id,
$description,
$payee,
$custom_id,
$invoice_id,
$soft_descriptor
);
return $purchase_unit;
}
/**
* Builds a Purchase unit based off a PayPal JSON response.
*
* @param \stdClass $data The JSON object.
*
* @return PurchaseUnit
* @throws RuntimeException When JSON object is malformed.
*/
public function from_paypal_response( \stdClass $data ): PurchaseUnit {
if ( ! isset( $data->reference_id ) || ! is_string( $data->reference_id ) ) {
throw new RuntimeException(
__( 'No reference ID given.', 'paypal-for-woocommerce' )
);
}
$amount = $this->amount_factory->from_paypal_response( $data->amount );
$description = ( isset( $data->description ) ) ? $data->description : '';
$custom_id = ( isset( $data->custom_id ) ) ? $data->custom_id : '';
$invoice_id = ( isset( $data->invoice_id ) ) ? $data->invoice_id : '';
$soft_descriptor = ( isset( $data->soft_descriptor ) ) ? $data->soft_descriptor : '';
$items = array();
if ( isset( $data->items ) && is_array( $data->items ) ) {
$items = array_map(
function ( \stdClass $item ): Item {
return $this->item_factory->from_paypal_response( $item );
},
$data->items
);
}
$payee = isset( $data->payee ) ? $this->payee_factory->from_paypal_response( $data->payee ) : null;
$shipping = null;
try {
if ( isset( $data->shipping ) ) {
$shipping = $this->shipping_factory->from_paypal_response( $data->shipping );
}
} catch ( RuntimeException $error ) {
;
}
$payments = null;
try {
if ( isset( $data->payments ) ) {
$payments = $this->payments_factory->from_paypal_response( $data->payments );
}
} catch ( RuntimeException $error ) {
;
}
$purchase_unit = new PurchaseUnit(
$amount,
$items,
$shipping,
$data->reference_id,
$description,
$payee,
$custom_id,
$invoice_id,
$soft_descriptor,
$payments
);
return $purchase_unit;
}
}

View file

@ -0,0 +1,99 @@
<?php
/**
* The shipping factory.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Factory
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Factory;
use Inpsyde\PayPalCommerce\ApiClient\Entity\Shipping;
use Inpsyde\PayPalCommerce\ApiClient\Exception\RuntimeException;
/**
* Class ShippingFactory
*/
class ShippingFactory {
/**
* The address factory.
*
* @var AddressFactory
*/
private $address_factory;
/**
* ShippingFactory constructor.
*
* @param AddressFactory $address_factory The address factory.
*/
public function __construct( AddressFactory $address_factory ) {
$this->address_factory = $address_factory;
}
/**
* Creates a shipping object based off a Woocommerce customer.
*
* @param \WC_Customer $customer The Woocommerce customer.
*
* @return Shipping
*/
public function from_wc_customer( \WC_Customer $customer ): Shipping {
// Replicates the Behavior of \WC_Order::get_formatted_shipping_full_name().
$full_name = sprintf(
// translators: %1$s is the first name and %2$s is the second name. wc translation.
_x( '%1$s %2$s', 'full name', 'paypal-for-woocommerce' ),
$customer->get_shipping_first_name(),
$customer->get_shipping_last_name()
);
$address = $this->address_factory->from_wc_customer( $customer );
return new Shipping(
$full_name,
$address
);
}
/**
* Creates a Shipping object based off a Woocommerce order.
*
* @param \WC_Order $order The Woocommerce order.
*
* @return Shipping
*/
public function from_wc_order( \WC_Order $order ): Shipping {
$full_name = $order->get_formatted_shipping_full_name();
$address = $this->address_factory->from_wc_order( $order );
return new Shipping(
$full_name,
$address
);
}
/**
* Creates a Shipping object based of from the PayPal JSON response.
*
* @param \stdClass $data The JSON object.
*
* @return Shipping
* @throws RuntimeException When JSON object is malformed.
*/
public function from_paypal_response( \stdClass $data ): Shipping {
if ( ! isset( $data->name->full_name ) ) {
throw new RuntimeException(
__( 'No name was given for shipping.', 'paypal-for-woocommerce' )
);
}
if ( ! isset( $data->address ) ) {
throw new RuntimeException(
__( 'No address was given for shipping.', 'paypal-for-woocommerce' )
);
}
$address = $this->address_factory->from_paypal_response( $data->address );
return new Shipping(
$data->name->full_name,
$address
);
}
}

View file

@ -0,0 +1,83 @@
<?php
/**
* Creates Webhooks.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Factory
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Factory;
use Inpsyde\PayPalCommerce\ApiClient\Entity\Webhook;
use Inpsyde\PayPalCommerce\ApiClient\Exception\RuntimeException;
/**
* Class WebhookFactory
*/
class WebhookFactory {
/**
* Returns a webhook for a URL with an array of event types associated to this URL.
*
* @param string $url The URL.
* @param array $event_types The event types to which this URL listens to.
*
* @return Webhook
*/
public function for_url_and_events( string $url, array $event_types ): Webhook {
$event_types = array_map(
static function ( string $type ): array {
return array( 'name' => $type );
},
$event_types
);
return new Webhook(
$url,
$event_types
);
}
/**
* Returns a webhook from a given data array.
*
* @param array $data The data array.
*
* @return Webhook
*/
public function from_array( array $data ): Webhook {
return $this->from_paypal_response( (object) $data );
}
/**
* Returns a Webhook based of a PayPal JSON response.
*
* @param \stdClass $data The JSON object.
*
* @return Webhook
* @throws RuntimeException When JSON object is malformed.
*/
public function from_paypal_response( \stdClass $data ): Webhook {
if ( ! isset( $data->id ) ) {
throw new RuntimeException(
__( 'No id for webhook given.', 'paypal-for-woocommerce' )
);
}
if ( ! isset( $data->url ) ) {
throw new RuntimeException(
__( 'No URL for webhook given.', 'paypal-for-woocommerce' )
);
}
if ( ! isset( $data->event_types ) ) {
throw new RuntimeException(
__( 'No event types for webhook given.', 'paypal-for-woocommerce' )
);
}
return new Webhook(
(string) $data->url,
(array) $data->event_types,
(string) $data->id
);
}
}

View file

@ -0,0 +1,535 @@
<?php
/**
* The DCC Applies helper checks if the current installation can use DCC or not.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Helper
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Helper;
/**
* Class DccApplies
*/
class DccApplies {
/**
* The matrix which countries and currency combinations can be used for DCC.
*
* @var array
*/
private $allowed_country_currency_matrix = array(
'AU' => array(
'AUD',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'JPY',
'NOK',
'NZD',
'PLN',
'SEK',
'SGD',
'USD',
),
'BE' => array(
'AUD',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'JPY',
'NOK',
'NZD',
'PLN',
'SEK',
'SGD',
'USD',
),
'BG' => array(
'AUD',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'JPY',
'NOK',
'NZD',
'PLN',
'SEK',
'SGD',
'USD',
),
'CY' => array(
'AUD',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'JPY',
'NOK',
'NZD',
'PLN',
'SEK',
'SGD',
'USD',
),
'CZ' => array(
'AUD',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'JPY',
'NOK',
'NZD',
'PLN',
'SEK',
'SGD',
'USD',
),
'DK' => array(
'AUD',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'JPY',
'NOK',
'NZD',
'PLN',
'SEK',
'SGD',
'USD',
),
'EE' => array(
'AUD',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'JPY',
'NOK',
'NZD',
'PLN',
'SEK',
'SGD',
'USD',
),
'FI' => array(
'AUD',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'JPY',
'NOK',
'NZD',
'PLN',
'SEK',
'SGD',
'USD',
),
'FR' => array(
'AUD',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'JPY',
'NOK',
'NZD',
'PLN',
'SEK',
'SGD',
'USD',
),
'GR' => array(
'AUD',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'JPY',
'NOK',
'NZD',
'PLN',
'SEK',
'SGD',
'USD',
),
'HU' => array(
'AUD',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'JPY',
'NOK',
'NZD',
'PLN',
'SEK',
'SGD',
'USD',
),
'IT' => array(
'AUD',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'JPY',
'NOK',
'NZD',
'PLN',
'SEK',
'SGD',
'USD',
),
'LV' => array(
'AUD',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'JPY',
'NOK',
'NZD',
'PLN',
'SEK',
'SGD',
'USD',
),
'LI' => array(
'AUD',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'JPY',
'NOK',
'NZD',
'PLN',
'SEK',
'SGD',
'USD',
),
'LT' => array(
'AUD',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'JPY',
'NOK',
'NZD',
'PLN',
'SEK',
'SGD',
'USD',
),
'LU' => array(
'AUD',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'JPY',
'NOK',
'NZD',
'PLN',
'SEK',
'SGD',
'USD',
),
'MT' => array(
'AUD',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'JPY',
'NOK',
'NZD',
'PLN',
'SEK',
'SGD',
'USD',
),
'NL' => array(
'AUD',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'JPY',
'NOK',
'NZD',
'PLN',
'SEK',
'SGD',
'USD',
),
'NO' => array(
'AUD',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'JPY',
'NOK',
'NZD',
'PLN',
'SEK',
'SGD',
'USD',
),
'PL' => array(
'AUD',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'JPY',
'NOK',
'NZD',
'PLN',
'SEK',
'SGD',
'USD',
),
'PT' => array(
'AUD',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'JPY',
'NOK',
'NZD',
'PLN',
'SEK',
'SGD',
'USD',
),
'RO' => array(
'AUD',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'JPY',
'NOK',
'NZD',
'PLN',
'SEK',
'SGD',
'USD',
),
'SK' => array(
'AUD',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'JPY',
'NOK',
'NZD',
'PLN',
'SEK',
'SGD',
'USD',
),
'SI' => array(
'AUD',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'JPY',
'NOK',
'NZD',
'PLN',
'SEK',
'SGD',
'USD',
),
'ES' => array(
'AUD',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'JPY',
'NOK',
'NZD',
'PLN',
'SEK',
'SGD',
'USD',
),
'SE' => array(
'AUD',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'JPY',
'NOK',
'NZD',
'PLN',
'SEK',
'SGD',
'USD',
),
'US' => array(
'AUD',
'CAD',
'EUR',
'GBP',
'JPY',
'USD',
),
'GB' => array(
'AUD',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'JPY',
'NOK',
'NZD',
'PLN',
'SEK',
'SGD',
'USD',
),
);
/**
* Returns whether DCC can be used in the current country and the current currency used.
*
* @return bool
*/
public function for_country_currency(): bool {
$region = wc_get_base_location();
$country = $region['country'];
$currency = get_woocommerce_currency();
if ( ! in_array( $country, array_keys( $this->allowed_country_currency_matrix ), true ) ) {
return false;
}
$applies = in_array( $currency, $this->allowed_country_currency_matrix[ $country ], true );
return $applies;
}
}

View file

@ -0,0 +1,121 @@
<?php
/**
* A Collection of all error responses for the order endpoint.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Helper
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Helper;
/**
* Class ErrorResponse
*/
class ErrorResponse {
public const UNKNOWN = 'UNKNOWN';
/* Order error codes */
public const ACTION_DOES_NOT_MATCH_INTENT = 'ACTION_DOES_NOT_MATCH_INTENT';
public const AGREEMENT_ALREADY_CANCELLED = 'AGREEMENT_ALREADY_CANCELLED';
public const AMOUNT_CANNOT_BE_SPECIFIED = 'AMOUNT_CANNOT_BE_SPECIFIED';
public const AMOUNT_MISMATCH = 'AMOUNT_MISMATCH';
public const AMOUNT_NOT_PATCHABLE = 'AMOUNT_NOT_PATCHABLE';
public const AUTH_CAPTURE_NOT_ENABLED = 'AUTH_CAPTURE_NOT_ENABLED';
public const AUTHENTICATION_FAILURE = 'AUTHENTICATION_FAILURE';
public const AUTHORIZATION_AMOUNT_EXCEEDED = 'AUTHORIZATION_AMOUNT_EXCEEDED';
public const AUTHORIZATION_CURRENCY_MISMATCH = 'AUTHORIZATION_CURRENCY_MISMATCH';
public const BILLING_AGREEMENT_NOT_FOUND = 'BILLING_AGREEMENT_NOT_FOUND';
public const CANNOT_BE_NEGATIVE = 'CANNOT_BE_NEGATIVE';
public const CANNOT_BE_ZERO_OR_NEGATIVE = 'CANNOT_BE_ZERO_OR_NEGATIVE';
public const CARD_TYPE_NOT_SUPPORTED = 'CARD_TYPE_NOT_SUPPORTED';
public const INVALID_SECURITY_CODE_LENGTH = 'INVALID_SECURITY_CODE_LENGTH';
public const CITY_REQUIRED = 'CITY_REQUIRED';
public const COMPLIANCE_VIOLATION = 'COMPLIANCE_VIOLATION';
public const CONSENT_NEEDED = 'CONSENT_NEEDED';
public const CURRENCY_NOT_SUPPORTED_FOR_CARD_TYPE = 'CURRENCY_NOT_SUPPORTED_FOR_CARD_TYPE';
public const CURRENCY_NOT_SUPPORTED_FOR_COUNTRY = 'CURRENCY_NOT_SUPPORTED_FOR_COUNTRY';
public const DECIMAL_PRECISION = 'DECIMAL_PRECISION';
public const DOMESTIC_TRANSACTION_REQUIRED = 'DOMESTIC_TRANSACTION_REQUIRED';
public const DUPLICATE_INVOICE_ID = 'DUPLICATE_INVOICE_ID';
public const DUPLICATE_REQUEST_ID = 'DUPLICATE_REQUEST_ID';
public const FIELD_NOT_PATCHABLE = 'FIELD_NOT_PATCHABLE';
public const INSTRUMENT_DECLINED = 'INSTRUMENT_DECLINED';
public const INTERNAL_SERVER_ERROR = 'INTERNAL_SERVER_ERROR';
public const INTERNAL_SERVICE_ERROR = 'INTERNAL_SERVICE_ERROR';
public const INVALID_ACCOUNT_STATUS = 'INVALID_ACCOUNT_STATUS';
public const INVALID_ARRAY_MAX_ITEMS = 'INVALID_ARRAY_MAX_ITEMS';
public const INVALID_ARRAY_MIN_ITEMS = 'INVALID_ARRAY_MIN_ITEMS';
public const INVALID_COUNTRY_CODE = 'INVALID_COUNTRY_CODE';
public const INVALID_CURRENCY_CODE = 'INVALID_CURRENCY_CODE';
public const INVALID_JSON_POINTER_FORMAT = 'INVALID_JSON_POINTER_FORMAT';
public const INVALID_PARAMETER_SYNTAX = 'INVALID_PARAMETER_SYNTAX';
public const INVALID_PARAMETER_VALUE = 'INVALID_PARAMETER_VALUE';
public const INVALID_PARAMETER = 'INVALID_PARAMETER';
public const INVALID_PATCH_OPERATION = 'INVALID_PATCH_OPERATION';
public const INVALID_PAYER_ID = 'INVALID_PAYER_ID';
public const INVALID_RESOURCE_ID = 'INVALID_RESOURCE_ID';
public const INVALID_STRING_LENGTH = 'INVALID_STRING_LENGTH';
public const ITEM_TOTAL_MISMATCH = 'ITEM_TOTAL_MISMATCH';
public const ITEM_TOTAL_REQUIRED = 'ITEM_TOTAL_REQUIRED';
public const MAX_AUTHORIZATION_COUNT_EXCEEDED = 'MAX_AUTHORIZATION_COUNT_EXCEEDED';
public const MAX_NUMBER_OF_PAYMENT_ATTEMPTS_EXCEEDED = 'MAX_NUMBER_OF_PAYMENT_ATTEMPTS_EXCEEDED';
public const MAX_VALUE_EXCEEDED = 'MAX_VALUE_EXCEEDED';
public const MISSING_REQUIRED_PARAMETER = 'MISSING_REQUIRED_PARAMETER';
public const MISSING_SHIPPING_ADDRESS = 'MISSING_SHIPPING_ADDRESS';
public const MULTI_CURRENCY_ORDER = 'MULTI_CURRENCY_ORDER';
public const MULTIPLE_SHIPPING_ADDRESS_NOT_SUPPORTED = 'MULTIPLE_SHIPPING_ADDRESS_NOT_SUPPORTED';
public const MULTIPLE_SHIPPING_OPTION_SELECTED = 'MULTIPLE_SHIPPING_OPTION_SELECTED';
public const INVALID_PICKUP_ADDRESS = 'INVALID_PICKUP_ADDRESS';
public const NOT_AUTHORIZED = 'NOT_AUTHORIZED';
public const NOT_ENABLED_FOR_CARD_PROCESSING = 'NOT_ENABLED_FOR_CARD_PROCESSING';
public const NOT_PATCHABLE = 'NOT_PATCHABLE';
public const NOT_SUPPORTED = 'NOT_SUPPORTED';
public const ORDER_ALREADY_AUTHORIZED = 'ORDER_ALREADY_AUTHORIZED';
public const ORDER_ALREADY_CAPTURED = 'ORDER_ALREADY_CAPTURED';
public const ORDER_ALREADY_COMPLETED = 'ORDER_ALREADY_COMPLETED';
public const ORDER_CANNOT_BE_SAVED = 'ORDER_CANNOT_BE_SAVED';
public const ORDER_COMPLETED_OR_VOIDED = 'ORDER_COMPLETED_OR_VOIDED';
public const ORDER_EXPIRED = 'ORDER_EXPIRED';
public const ORDER_NOT_APPROVED = 'ORDER_NOT_APPROVED';
public const ORDER_NOT_SAVED = 'ORDER_NOT_SAVED';
public const ORDER_PREVIOUSLY_VOIDED = 'ORDER_PREVIOUSLY_VOIDED';
public const PARAMETER_VALUE_NOT_SUPPORTED = 'PARAMETER_VALUE_NOT_SUPPORTED';
public const PATCH_PATH_REQUIRED = 'PATCH_PATH_REQUIRED';
public const PATCH_VALUE_REQUIRED = 'PATCH_VALUE_REQUIRED';
public const PAYEE_ACCOUNT_INVALID = 'PAYEE_ACCOUNT_INVALID';
public const PAYEE_ACCOUNT_LOCKED_OR_CLOSED = 'PAYEE_ACCOUNT_LOCKED_OR_CLOSED';
public const PAYEE_ACCOUNT_RESTRICTED = 'PAYEE_ACCOUNT_RESTRICTED';
public const PAYEE_BLOCKED_TRANSACTION = 'PAYEE_BLOCKED_TRANSACTION';
public const PAYER_ACCOUNT_LOCKED_OR_CLOSED = 'PAYER_ACCOUNT_LOCKED_OR_CLOSED';
public const PAYER_ACCOUNT_RESTRICTED = 'PAYER_ACCOUNT_RESTRICTED';
public const PAYER_CANNOT_PAY = 'PAYER_CANNOT_PAY';
public const PAYER_CONSENT_REQUIRED = 'PAYER_CONSENT_REQUIRED';
public const PAYER_COUNTRY_NOT_SUPPORTED = 'PAYER_COUNTRY_NOT_SUPPORTED';
public const PAYEE_NOT_ENABLED_FOR_CARD_PROCESSING = 'PAYEE_NOT_ENABLED_FOR_CARD_PROCESSING';
public const PAYMENT_INSTRUCTION_REQUIRED = 'PAYMENT_INSTRUCTION_REQUIRED';
public const PERMISSION_DENIED = 'PERMISSION_DENIED';
public const POSTAL_CODE_REQUIRED = 'POSTAL_CODE_REQUIRED';
public const PREFERRED_SHIPPING_OPTION_AMOUNT_MISMATCH = 'PREFERRED_SHIPPING_OPTION_AMOUNT_MISMATCH';
public const REDIRECT_PAYER_FOR_ALTERNATE_FUNDING = 'REDIRECT_PAYER_FOR_ALTERNATE_FUNDING';
public const REFERENCE_ID_NOT_FOUND = 'REFERENCE_ID_NOT_FOUND';
public const REFERENCE_ID_REQUIRED = 'REFERENCE_ID_REQUIRED';
public const DUPLICATE_REFERENCE_ID = 'DUPLICATE_REFERENCE_ID';
public const SHIPPING_ADDRESS_INVALID = 'SHIPPING_ADDRESS_INVALID';
public const SHIPPING_OPTION_NOT_SELECTED = 'SHIPPING_OPTION_NOT_SELECTED';
public const SHIPPING_OPTIONS_NOT_SUPPORTED = 'SHIPPING_OPTIONS_NOT_SUPPORTED';
public const TAX_TOTAL_MISMATCH = 'TAX_TOTAL_MISMATCH';
public const TAX_TOTAL_REQUIRED = 'TAX_TOTAL_REQUIRED';
public const TRANSACTION_AMOUNT_EXCEEDS_MONTHLY_MAX_LIMIT = 'TRANSACTION_AMOUNT_EXCEEDS_MONTHLY_MAX_LIMIT';
public const TRANSACTION_BLOCKED_BY_PAYEE = 'TRANSACTION_BLOCKED_BY_PAYEE';
public const TRANSACTION_LIMIT_EXCEEDED = 'TRANSACTION_LIMIT_EXCEEDED';
public const TRANSACTION_RECEIVING_LIMIT_EXCEEDED = 'TRANSACTION_RECEIVING_LIMIT_EXCEEDED';
public const TRANSACTION_REFUSED = 'TRANSACTION_REFUSED';
public const UNSUPPORTED_INTENT = 'UNSUPPORTED_INTENT';
public const UNSUPPORTED_PATCH_PARAMETER_VALUE = 'UNSUPPORTED_PATCH_PARAMETER_VALUE';
public const UNSUPPORTED_PAYMENT_INSTRUCTION = 'UNSUPPORTED_PAYMENT_INSTRUCTION';
public const PAYEE_ACCOUNT_NOT_SUPPORTED = 'PAYEE_ACCOUNT_NOT_SUPPORTED';
public const PAYEE_ACCOUNT_NOT_VERIFIED = 'PAYEE_ACCOUNT_NOT_VERIFIED';
public const PAYEE_NOT_CONSENTED = 'PAYEE_NOT_CONSENTED';
}

View file

@ -0,0 +1,62 @@
<?php
/**
* Returns the current application context.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Repository
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Repository;
use Inpsyde\PayPalCommerce\ApiClient\Entity\ApplicationContext;
use Inpsyde\PayPalCommerce\WcGateway\Endpoint\ReturnUrlEndpoint;
use Psr\Container\ContainerInterface;
/**
* Class ApplicationContextRepository
*/
class ApplicationContextRepository {
/**
* The Settings.
*
* @var ContainerInterface
*/
private $settings;
/**
* ApplicationContextRepository constructor.
*
* @param ContainerInterface $settings The settings.
*/
public function __construct( ContainerInterface $settings ) {
$this->settings = $settings;
}
/**
* Returns the current application context.
*
* @param string $shipping_preferences The shipping preferences.
*
* @return ApplicationContext
*/
public function current_context(
string $shipping_preferences = ApplicationContext::SHIPPING_PREFERENCE_NO_SHIPPING
): ApplicationContext {
$brand_name = $this->settings->has( 'brand_name' ) ? $this->settings->get( 'brand_name' ) : '';
$locale = str_replace( '_', '-', get_user_locale() );
$landingpage = $this->settings->has( 'landing_page' ) ?
$this->settings->get( 'landing_page' ) : ApplicationContext::LANDING_PAGE_NO_PREFERENCE;
$context = new ApplicationContext(
(string) home_url( \WC_AJAX::get_endpoint( ReturnUrlEndpoint::ENDPOINT ) ),
(string) wc_get_checkout_url(),
(string) $brand_name,
$locale,
(string) $landingpage,
$shipping_preferences
);
return $context;
}
}

View file

@ -0,0 +1,46 @@
<?php
/**
* The cart repository returns the purchase units from the current \WC_Cart.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Repository
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Repository;
use Inpsyde\PayPalCommerce\ApiClient\Entity\Item;
use Inpsyde\PayPalCommerce\ApiClient\Entity\PurchaseUnit;
use Inpsyde\PayPalCommerce\ApiClient\Factory\PurchaseUnitFactory;
/**
* Class CartRepository
*/
class CartRepository implements PurchaseUnitRepositoryInterface {
/**
* The purchase unit factory.
*
* @var PurchaseUnitFactory
*/
private $factory;
/**
* CartRepository constructor.
*
* @param PurchaseUnitFactory $factory The purchase unit factory.
*/
public function __construct( PurchaseUnitFactory $factory ) {
$this->factory = $factory;
}
/**
* Returns all Pur of the Woocommerce cart.
*
* @return PurchaseUnit[]
*/
public function all(): array {
$cart = WC()->cart ?? new \WC_Cart();
return array( $this->factory->from_wc_cart( $cart ) );
}
}

View file

@ -0,0 +1,118 @@
<?php
/**
* The partner referrals data object.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Repository
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Repository;
use Inpsyde\PayPalCommerce\ApiClient\Helper\DccApplies;
/**
* Class PartnerReferralsData
*/
class PartnerReferralsData {
/**
* The merchant email.
*
* @var string
*/
private $merchant_email;
/**
* The DCC Applies Helper object.
*
* @var DccApplies
*/
private $dcc_applies;
/**
* PartnerReferralsData constructor.
*
* @param string $merchant_email The email of the merchant.
* @param DccApplies $dcc_applies The DCC Applies helper.
*/
public function __construct(
string $merchant_email,
DccApplies $dcc_applies
) {
$this->merchant_email = $merchant_email;
$this->dcc_applies = $dcc_applies;
}
/**
* Returns a nonce.
*
* @return string
*/
public function nonce(): string {
return 'a1233wtergfsdt4365tzrshgfbaewa36AGa1233wtergfsdt4365tzrshgfbaewa36AG';
}
/**
* Returns the data.
*
* @return array
*/
public function data(): array {
$data = $this->default_data();
return $data;
}
/**
* Returns the default data.
*
* @return array
*/
private function default_data(): array {
return array(
'email' => $this->merchant_email,
'partner_config_override' => array(
'partner_logo_url' => 'https://connect.woocommerce.com/images/woocommerce_logo.png',
'return_url' => admin_url(
'admin.php?page=wc-settings&tab=checkout&section=ppcp-gateway'
),
'return_url_description' => __(
'Return to your shop.',
'paypal-for-woocommerce'
),
'show_add_credit_card' => true,
),
'products' => array(
$this->dcc_applies->for_country_currency() ? 'PPCP' : 'EXPRESS_CHECKOUT',
),
'legal_consents' => array(
array(
'type' => 'SHARE_DATA_CONSENT',
'granted' => true,
),
),
'operations' => array(
array(
'operation' => 'API_INTEGRATION',
'api_integration_preference' => array(
'rest_api_integration' => array(
'integration_method' => 'PAYPAL',
'integration_type' => 'FIRST_PARTY',
'first_party_details' => array(
'features' => array(
'PAYMENT',
'FUTURE_PAYMENT',
'REFUND',
'ADVANCED_TRANSACTIONS_SEARCH',
),
'seller_nonce' => $this->nonce(),
),
),
),
),
),
);
}
}

View file

@ -0,0 +1,55 @@
<?php
/**
* The Payee Repository.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Repository
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Repository;
use Inpsyde\PayPalCommerce\ApiClient\Entity\Payee;
/**
* Class PayeeRepository
*/
class PayeeRepository {
/**
* The merchant email.
*
* @var string
*/
private $merchant_email;
/**
* The merchant ID.
*
* @var string
*/
private $merchant_id;
/**
* PayeeRepository constructor.
*
* @param string $merchant_email The email of the merchant.
* @param string $merchant_id The ID of the merchant.
*/
public function __construct( string $merchant_email, string $merchant_id ) {
$this->merchant_email = $merchant_email;
$this->merchant_id = $merchant_id;
}
/**
* Returns the current Payee.
*
* @return Payee
*/
public function payee(): Payee {
return new Payee(
$this->merchant_email,
$this->merchant_id
);
}
}

View file

@ -0,0 +1,90 @@
<?php
/**
* The repository for the request IDs.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Repository
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Repository;
use Inpsyde\PayPalCommerce\ApiClient\Entity\Order;
/**
* Class PayPalRequestIdRepository
*/
class PayPalRequestIdRepository {
public const KEY = 'ppcp-request-ids';
/**
* Returns a request ID based on the order ID.
*
* @param string $order_id The order ID.
*
* @return string
*/
public function get_for_order_id( string $order_id ): string {
$all = $this->all();
return isset( $all[ $order_id ] ) ? (string) $all[ $order_id ]['id'] : '';
}
/**
* Returns the request ID for an order.
*
* @param Order $order The order.
*
* @return string
*/
public function get_for_order( Order $order ): string {
return $this->get_for_order_id( $order->id() );
}
/**
* Sets a request ID for a specific order.
*
* @param Order $order The order.
* @param string $request_id The ID.
*
* @return bool
*/
public function set_for_order( Order $order, string $request_id ): bool {
$all = $this->all();
$all[ $order->id() ] = array(
'id' => $request_id,
'expiration' => time() + 10 * DAY_IN_SECONDS,
);
$all = $this->cleanup( $all );
update_option( self::KEY, $all );
return true;
}
/**
* Return all IDs.
*
* @return array
*/
private function all(): array {
return (array) get_option( 'ppcp-request-ids', array() );
}
/**
* Clean up outdated request IDs.
*
* @param array $all All request IDs.
*
* @return array
*/
private function cleanup( array $all ): array {
foreach ( $all as $order_id => $value ) {
if ( time() < $value['expiration'] ) {
continue;
}
unset( $all[ $order_id ] );
}
return $all;
}
}

View file

@ -0,0 +1,26 @@
<?php
/**
* The Purchase Unit Repository interface.
*
* @package Inpsyde\PayPalCommerce\ApiClient\Repository
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient\Repository;
use Inpsyde\PayPalCommerce\ApiClient\Entity\PurchaseUnit;
/**
* Interface PurchaseUnitRepositoryInterface
*/
interface PurchaseUnitRepositoryInterface {
/**
* Returns all purchase units.
*
* @return PurchaseUnit[]
*/
public function all(): array;
}

View file

@ -0,0 +1,42 @@
<?php
/**
* The API module.
*
* @package Inpsyde\PayPalCommerce\ApiClient
*/
declare(strict_types=1);
namespace Inpsyde\PayPalCommerce\ApiClient;
use Dhii\Container\ServiceProvider;
use Dhii\Modular\Module\Exception\ModuleExceptionInterface;
use Dhii\Modular\Module\ModuleInterface;
use Interop\Container\ServiceProviderInterface;
use Psr\Container\ContainerInterface;
/**
* Class ApiModule
*/
class ApiModule implements ModuleInterface {
/**
* Sets up the module.
*
* @return ServiceProviderInterface
*/
public function setup(): ServiceProviderInterface {
return new ServiceProvider(
require __DIR__ . '/../services.php',
require __DIR__ . '/../extensions.php'
);
}
/**
* Runs the module.
*
* @param ContainerInterface $container The container.
*/
public function run( ContainerInterface $container ) {
}
}