<?php
	namespace Main\Libs\Payment\PaymentGateways;
	
	use Exception;
	use Main\Libs\Payment\PaymentGateway;
	use Main\Libs\Payment\SuccessfulPayment;
	use PayPal\Api\VerifyWebhookSignature;
	use PayPal\Auth\OAuthTokenCredential;
	use PayPal\Rest\ApiContext;
	use PayPalCheckoutSdk\Core\PayPalHttpClient;
	use PayPalCheckoutSdk\Core\ProductionEnvironment;
	use PayPalCheckoutSdk\Core\SandboxEnvironment;
	use PayPalCheckoutSdk\Orders\OrdersCaptureRequest;
	use PayPalCheckoutSdk\Orders\OrdersCreateRequest;
	use PayPalHttp\HttpException;
	use PayPalHttp\IOException;
	
	class PaypalCheckout extends PaymentGateway
	{
		public function payment()
		{
			$environment = $this->config["sandbox"] ? new SandboxEnvironment($this->config["client_id"], $this->config["client_secret"]) : new ProductionEnvironment($this->config["client_id"], $this->config["client_secret"]);
			$client = new PayPalHttpClient($environment);
			
			$paypalProducts = [
				[
					"reference_id" => $this->order->id,
					"amount" => [
						"value" => $this->order->paymentAmount,
						"currency_code" => $this->currency,
						"breakdown" => [
							"item_total" => [
								"value" => $this->order->paymentAmount,
								"currency_code" => $this->currency
							]
						]
					],
					"items" => [
						[
							"name" => $this->order->singleProductName,
							"quantity" => 1,
							"unit_amount" => [
								"value" => $this->order->paymentAmount,
								"currency_code" => $this->currency
							]
						]
					]
				]
			];
			
			$request = new OrdersCreateRequest();
			$request->prefer('return=representation');
			$request->body = [
				"intent" => "CAPTURE",
				"purchase_units" => $paypalProducts,
				"application_context" => [
					"cancel_url" => $this->getCancelUrl(),
					"return_url" => $this->getSuccessUrl(),
					"brand_name" => settings('serverName'),
					"shipping_preference" => "NO_SHIPPING"
				]
			];
			
			// Call API with your client and get a response for your call
			$response = $client->execute($request);
			
			// If call returns body in response, you can get the deserialized version from the result attribute of the response
			if ($response->statusCode == 201) {
				$checkoutURL = null;
				foreach ($response->result->links as $link) {
					if ($link->rel == "approve") {
						$checkoutURL = $link->href;
						break;
					}
				}
				header("HTTP/1.1 303 See Other");
				header("Location: " . $checkoutURL);
			}
		}
		
		/**
		 * @throws HttpException
		 * @throws IOException
		 * @throws Exception
		 */
		public function callback(): SuccessfulPayment
		{
			$webhookBody = @file_get_contents('php://input');
			$requestHeaders = getallheaders();
			$requestHeaders = array_change_key_case($requestHeaders, CASE_UPPER);
			
			$signatureVerification = new VerifyWebhookSignature();
			$signatureVerification->setAuthAlgo($requestHeaders['PAYPAL-AUTH-ALGO']);
			$signatureVerification->setTransmissionId($requestHeaders['PAYPAL-TRANSMISSION-ID']);
			$signatureVerification->setCertUrl($requestHeaders['PAYPAL-CERT-URL']);
			$signatureVerification->setWebhookId($this->config["webhook_id"]); // Note that the Webhook ID must be a currently valid Webhook that you created with your client ID/secret.
			$signatureVerification->setTransmissionSig($requestHeaders['PAYPAL-TRANSMISSION-SIG']);
			$signatureVerification->setTransmissionTime($requestHeaders['PAYPAL-TRANSMISSION-TIME']);
			
			$signatureVerification->setRequestBody($webhookBody);
			$request = clone $signatureVerification;
			
			$paypalApiContext = new ApiContext(
				new OAuthTokenCredential(
					$this->config["client_id"],
					$this->config["client_secret"]
				)
			);
			$paypalApiContext->setConfig([
				'mode' => $this->config["sandbox"] == "1" ? 'sandbox' : 'live',
			]);
			$signatureVerificationResponse = $signatureVerification->post($paypalApiContext);
			
			$signatureVerificationStatus = $signatureVerificationResponse->getVerificationStatus();
			if ($signatureVerificationStatus == "SUCCESS") {
				$webhookBody = json_decode($webhookBody);
				$webhookID = $webhookBody->id;
				$webhookEventType = $webhookBody->event_type;
				
				$environment = $this->config["sandbox"] ? new SandboxEnvironment($this->config["client_id"], $this->config["client_secret"]) : new ProductionEnvironment($this->config["client_id"], $this->config["client_secret"]);
				$client = new PayPalHttpClient($environment);
				
				$orderCaptureRequest = new OrdersCaptureRequest($webhookBody->resource->id);
				$orderCaptureRequest->prefer('return=representation');
				
				// Call API with your client and get a response for your call
				$orderCaptureResponse = $client->execute($orderCaptureRequest);
				
				// If call returns body in response, you can get the deserialized version from the result attribute of the response
				if ($orderCaptureResponse->statusCode == 201) {
					$orderID = $orderCaptureResponse->result->purchase_units[0]->reference_id;
					$paymentID = $orderCaptureResponse->result->id;
					
					return new SuccessfulPayment($orderID, $paymentID);
				} else {
					throw new Exception("PayPal responded with http code " . $orderCaptureResponse->statusCode);
				}
			} else {
				throw new Exception("Webhook signature verification failed. Check the Webhook signature and verify that the Webhook event occurred.");
			}
		}
	}