app/Plugin/PayPalCheckout42/Service/PayPalRequestService.php line 725

Open in your IDE?
  1. <?php
  2. namespace Plugin\PayPalCheckout42\Service;
  3. use Plugin\PayPalCheckout42\Contracts\ShowOrderDetailsResponse;
  4. use Plugin\PayPalCheckout42\Lib\PayPalHttp\HttpException as PayPalHttpException;
  5. use Plugin\PayPalCheckout42\Lib\PayPalHttp\HttpRequest as PayPalHttpRequest;
  6. use Plugin\PayPalCheckout42\Lib\PayPalHttp\HttpResponse as PayPalHttpResponse;
  7. use Plugin\PayPalCheckout42\Lib\PayPalCheckoutSdk\Core\PayPalHttpClient;
  8. use Plugin\PayPalCheckout42\Lib\PayPalCheckoutSdk\Core\ProductionEnvironment;
  9. use Plugin\PayPalCheckout42\Lib\PayPalCheckoutSdk\Core\SandboxEnvironment;
  10. use Plugin\PayPalCheckout42\Lib\PayPalCheckoutSdk\Orders\OrdersCaptureRequest;
  11. use Plugin\PayPalCheckout42\Lib\PayPalCheckoutSdk\Orders\OrdersCreateRequest;
  12. use Plugin\PayPalCheckout42\Lib\PayPalCheckoutSdk\Orders\OrdersGetRequest;
  13. use Plugin\PayPalCheckout42\Lib\PayPalCheckoutSdk\Orders\OrdersPatchRequest;
  14. use Plugin\PayPalCheckout42\Lib\PayPalCheckoutSdk\Payments\CapturesRefundRequest;
  15. use Plugin\PayPalCheckout42\Contracts\CaptureTransactionResponse;
  16. use Plugin\PayPalCheckout42\Contracts\EccubeAddressAccessible;
  17. use Plugin\PayPalCheckout42\Contracts\OrderResultResponse;
  18. use Plugin\PayPalCheckout42\Contracts\ReferenceTransactionResponse;
  19. use Plugin\PayPalCheckout42\Exception\PayPalRequestException;
  20. use Plugin\PayPalCheckout42\Service\Method\InlineGuest;
  21. use Plugin\PayPalCheckout42\Service\Method\Acdc;
  22. use Plugin\PayPalCheckout42\Util\StringUtil;
  23. use stdClass;
  24. /**
  25.  * Class PayPalRequestService
  26.  * @package Plugin\PayPalCheckout42\Service
  27.  */
  28. class PayPalRequestService
  29. {
  30.     /**
  31.      * @var PayPalHttpClient|null
  32.      */
  33.     private $client;
  34.     /**
  35.      * PayPalRequestService constructor.
  36.      */
  37.     public function __construct()
  38.     {
  39.         $this->client null;
  40.     }
  41.     /**
  42.      * @param string $clientId
  43.      * @param string $clientSecret
  44.      * @param bool $sandbox
  45.      */
  46.     public function setEnv(string $clientIdstring $clientSecret$sandbox false): void
  47.     {
  48.         if ($sandbox) {
  49.             /** @var SandboxEnvironment $env */
  50.             $env = new SandboxEnvironment($clientId$clientSecret);
  51.         } else {
  52.             /** @var ProductionEnvironment $env */
  53.             $env = new ProductionEnvironment($clientId$clientSecret);
  54.         }
  55.         /** @var PayPalHttpClient $client */
  56.         $this->client = new PayPalHttpClient($env);
  57.     }
  58.     /**
  59.      * @param string $total
  60.      * @return string
  61.      */
  62.     public function roundedCurrencyFormat(string $total): string
  63.     {
  64.         return (string) ceil(floatval($total));
  65.     }
  66.     /**
  67.      * @param stdClass $options
  68.      * @param string $paymentMethodClass
  69.      * @return OrdersCreateRequest
  70.      */
  71.     public function prepareOrderTransaction(stdClass $options$paymentMethodClass): OrdersCreateRequest
  72.     {
  73.         /** @var OrdersCreateRequest $request */
  74.         $request = new OrdersCreateRequest();
  75.         $request->prefer('return=representation');
  76.         $request->payPalPartnerAttributionId('ECCUBE4_Cart_EC_JP');
  77.         // クレジットカード決済の場合は別のBNコード
  78.         if ($paymentMethodClass === Acdc::class) {
  79.             $request->payPalPartnerAttributionId('ECCUBE4_Cart_UCC_JP');
  80.         }
  81.         $request->body = [
  82.             'intent' => 'CAPTURE',
  83.             'application_context' => $options->application_context,
  84.             'purchase_units' => [
  85.                 => [
  86.                     'reference_id' => '1',
  87.                     'amount' => $options->amount,
  88.                     'items' => $options->items
  89.                 ]
  90.             ]
  91.         ];
  92.         if (property_exists($options'shipping')) {
  93.             $request->body["purchase_units"][0]["shipping"] = $options->shipping;
  94.         }
  95.         // クレジットカード決済の場合はpayerの情報を付加
  96.         if ($paymentMethodClass === InlineGuest::class || $paymentMethodClass === Acdc::class) {
  97.             $request->body['payer'] = [
  98.                 'name' => [
  99.                     'given_name' => $options->customer['name02'],
  100.                     'surname' => $options->customer['name01'],
  101.                 ],
  102.                 'phone' => [
  103.                     'phone_number' => [
  104.                         'national_number' => $options->customer['phone_number']
  105.                     ]
  106.                 ],
  107.                 'email_address' => $options->customer['email'],
  108.                 'address' => $options->customer['address'],
  109.             ];
  110.         }
  111.         return $request;
  112.     }
  113.     /**
  114.      * @param OrdersCreateRequest $transaction
  115.      * @return PayPalHttpResponse
  116.      * @throws PayPalRequestException
  117.      */
  118.     public function orderTransaction(OrdersCreateRequest $transaction): PayPalHttpResponse
  119.     {
  120.         /** @var PayPalHttpResponse $response */
  121.         $response $this->send($transaction);
  122.         return new class($response) extends PayPalHttpResponse implements OrderResultResponse {
  123.             /**
  124.              *  constructor.
  125.              * @param PayPalHttpResponse $response
  126.              */
  127.             function __construct(PayPalHttpResponse $response)
  128.             {
  129.                 parent::__construct($response->statusCode$response->result$response->headers);
  130.             }
  131.             /**
  132.              * @return string
  133.              */
  134.             public function getOrderingId(): string
  135.             {
  136.                 return $this
  137.                     ->result
  138.                     ->id;
  139.             }
  140.         };
  141.     }
  142.     /**
  143.      * @param string $orderId
  144.      * @return OrdersGetRequest
  145.      */
  146.     public function prepareOrderDetailTransaction(string $orderId): OrdersGetRequest
  147.     {
  148.         /** @var OrdersGetRequest $request */
  149.         $request = new OrdersGetRequest($orderId);
  150.         return $request;
  151.     }
  152.     /**
  153.      * @param OrdersGetRequest $transaction
  154.      * @return PayPalHttpResponse
  155.      * @throws PayPalRequestException
  156.      */
  157.     public function orderDetailTransaction(OrdersGetRequest $transaction): PayPalHttpResponse
  158.     {
  159.         /** @var PayPalHttpResponse $response */
  160.         $response $this->send($transaction);
  161.         return new class($response) extends PayPalHttpResponse implements OrderResultResponseEccubeAddressAccessible {
  162.             /**
  163.              *  constructor.
  164.              * @param PayPalHttpResponse $response
  165.              */
  166.             function __construct(PayPalHttpResponse $response)
  167.             {
  168.                 parent::__construct($response->statusCode$response->result$response->headers);
  169.             }
  170.             /**
  171.              * @return string
  172.              */
  173.             public function getOrderingId(): string
  174.             {
  175.                 return $this
  176.                     ->result
  177.                     ->id;
  178.             }
  179.             /**
  180.              * @return array
  181.              */
  182.             public function getEccubeAddressFormat(): array
  183.             {
  184.                 /** @var array $fullName */
  185.                 $fullName explode(' '$this->result->purchase_units[0]->shipping->name->full_name);
  186.                 // 「建物名」が address_line_2 に入っている場合があるので、 address_line_1 とくっつけて格納する
  187.                 // PayPal と EC-CUBE では住所の持ち方が違うため、 address_line_2 があったりなかったりが発生してしまう。
  188.                 $address_2 $this->result->purchase_units[0]->shipping->address->address_line_1;
  189.                 if (isset($this->result->purchase_units[0]->shipping->address->address_line_2)) {
  190.                     $address_2 .= $this->result->purchase_units[0]->shipping->address->address_line_2;
  191.                 }
  192.                 return [
  193.                     'name01' => $fullName[1],
  194.                     'name02' => $fullName[0],
  195.                     'kana01' => null,
  196.                     'kana02' => null,
  197.                     'company_name' => null,
  198.                         'postal_code' => $this->result->purchase_units[0]->shipping->address->postal_code,
  199.                         'pref' => $this->result->purchase_units[0]->shipping->address->admin_area_1,
  200.                         'addr01' => $this->result->purchase_units[0]->shipping->address->admin_area_2,
  201.                         'addr02' => $address_2,
  202.                     'phone_number' => null,
  203.                     'email' => $this->result->payer->email_address
  204.                 ];
  205.             }
  206.         };
  207.     }
  208.     /**
  209.      * @param string $orderId
  210.      * @param stdClass $options
  211.      * @return OrdersPatchRequest
  212.      */
  213.     public function prepareOrderPatchTransaction(string $orderIdstdClass $options): OrdersPatchRequest
  214.     {
  215.         /** @var OrdersPatchRequest $request */
  216.         $request = new OrdersPatchRequest($orderId);
  217.         $request->body = [
  218.             => [
  219.                 'op' => 'replace',
  220.                 "path" => "/purchase_units/@reference_id=='1'/amount",
  221.                 'value' => $options->amount
  222.             ]
  223.         ];
  224.         return $request;
  225.     }
  226.     /**
  227.      * @param OrdersPatchRequest $transaction
  228.      * @return PayPalHttpResponse
  229.      * @throws PayPalRequestException
  230.      */
  231.     public function orderPatchTransaction(OrdersPatchRequest $transaction): PayPalHttpResponse
  232.     {
  233.         /** @var PayPalHttpResponse $response */
  234.         $response $this->send($transaction);
  235.         return new class($response$transaction) extends PayPalHttpResponse implements OrderResultResponse {
  236.             /**
  237.              * @var string
  238.              */
  239.             private $orderingId;
  240.             /**
  241.              *  constructor.
  242.              * @param PayPalHttpResponse $response
  243.              * @param OrdersPatchRequest $transaction
  244.              */
  245.             function __construct(PayPalHttpResponse $responseOrdersPatchRequest $transaction)
  246.             {
  247.                 parent::__construct($response->statusCode$response->result$response->headers);
  248.                 /** @var array $parsedUrl */
  249.                 $parsedUrl parse_url($transaction->path);
  250.                 $this->orderingId basename($parsedUrl['path']);
  251.             }
  252.             /**
  253.              * @return string
  254.              */
  255.             public function getOrderingId(): string
  256.             {
  257.                 return $this->orderingId;
  258.             }
  259.         };
  260.     }
  261.     /**
  262.      * @param string $orderId
  263.      * @return PayPalHttpRequest
  264.      */
  265.     public function prepareShowOrderDetails(string $orderId): PayPalHttpRequest
  266.     {
  267.         $request = new class($orderId) extends PayPalHttpRequest {
  268.             function __construct($orderId)
  269.             {
  270.                 parent::__construct("/v2/checkout/orders/{order_id}""GET");
  271.                 $this->path str_replace("{order_id}"urlencode($orderId), $this->path);
  272.                 $this->headers["Content-Type"] = "application/json";
  273.             }
  274.         };
  275.         return $request;
  276.     }
  277.     /**
  278.      * @param PayPalHttpRequest $request
  279.      * @return PayPalHttpResponse
  280.      * @throws PayPalRequestException
  281.      */
  282.     public function showOrderDetails(PayPalHttpRequest $request): PayPalHttpResponse
  283.     {
  284.         /** @var PayPalHttpResponse $response */
  285.         $response $this->send($request);
  286.         return new class($response) extends PayPalHttpResponse implements ShowOrderDetailsResponse {
  287.             /**
  288.              *  constructor.
  289.              * @param PayPalHttpResponse $response
  290.              */
  291.             public function __construct(PayPalHttpResponse $response)
  292.             {
  293.                 parent::__construct($response->statusCode$response->result$response->headers);
  294.             }
  295.             /**
  296.              * @return string
  297.              */
  298.             public function getLiabilityShift(): string
  299.             {
  300.                 return $this->result->payment_source->card->authentication_result->liability_shift ?? '';
  301.             }
  302.             /**
  303.              * @return string
  304.              */
  305.             public function getEnrollmentStatus(): string
  306.             {
  307.                 return $this->result->payment_source->card->authentication_result->three_d_secure->enrollment_status ?? '';
  308.             }
  309.             /**
  310.              * @return string
  311.              */
  312.             public function getAuthenticationStatus(): string
  313.             {
  314.                 return $this->result->payment_source->card->authentication_result->three_d_secure->authentication_status ?? '';
  315.             }
  316.             /**
  317.              * @return bool
  318.              */
  319.             public function isOk(): bool
  320.             {
  321.                 return $this->getLiabilityShift() === 'POSSIBLE';
  322.                 // #230 3DS2.0対応のため判定条件を緩和
  323.                 //$this->getEnrollmentStatus() === 'Y' &&
  324.                 //$this->getAuthenticationStatus() === 'Y';
  325.                 //!empty($this->getAuthenticationStatus());
  326.             }
  327.         };
  328.     }
  329.     /**
  330.      * @param string $orderId
  331.      * @param string $vaultId
  332.      * @param string $fraudNetSessionId
  333.      * @return PayPalHttpRequest
  334.      */
  335.     public function prepareCaptureTransaction(string $orderId$vaultId ''$fraudNetSessionId ''): PayPalHttpRequest
  336.     {
  337.         $request = new class($orderId$vaultId$fraudNetSessionId) extends PayPalHttpRequest {
  338.             function __construct($orderId$vaultId$fraudNetSessionId)
  339.             {
  340.                 parent::__construct("/v2/checkout/orders/{order_id}/capture?""POST");
  341.                 $this->path str_replace("{order_id}"urlencode($orderId), $this->path);
  342.                 $this->headers["Content-Type"] = "application/json";
  343.                 // 6時間保存されるキーのため、適当な長さのランダム文字列を指定しておく
  344.                 $this->headers["PayPal-Request-Id"] = StringUtil::createRandomString(30);
  345.                 // vault に保存されたクレジットカードを利用する場合
  346.                 if (!empty($vaultId)) {
  347.                     // 顧客が「前回のクレジットカードを使用する」場合に、FraudNetのスクリプトを注入する
  348.                     $this->headers["PayPal-Client-Metadata-Id"] = $fraudNetSessionId;
  349.                     $this->body = [
  350.                         'payment_source' => [
  351.                             'token' => [
  352.                                 'id' => $vaultId,
  353.                                 'type' => 'PAYMENT_METHOD_TOKEN',
  354.                             ],
  355.                         ]
  356.                     ];
  357.                 }
  358.             }
  359.         };
  360.         return $request;
  361.     }
  362.     /**
  363.      * @param OrdersCaptureRequest $transaction
  364.      * @return PayPalHttpResponse
  365.      * @throws PayPalRequestException
  366.      */
  367.     public function captureTransaction(PayPalHttpRequest $transaction): PayPalHttpResponse
  368.     {
  369.         /** @var PayPalHttpResponse $response */
  370.         $response $this->send($transaction);
  371.         return new class($response) extends PayPalHttpResponse implements CaptureTransactionResponse {
  372.             /**
  373.              *  constructor.
  374.              * @param PayPalHttpResponse $response
  375.              */
  376.             function __construct(PayPalHttpResponse $response)
  377.             {
  378.                 parent::__construct($response->statusCode$response->result$response->headers);
  379.             }
  380.             /**
  381.              * @return string
  382.              */
  383.             public function getCaptureTransactionId(): string
  384.             {
  385.                 return $this
  386.                     ->result
  387.                     ->purchase_units[0]
  388.                     ->payments
  389.                     ->captures[0]
  390.                     ->id;
  391.             }
  392.             /**
  393.              * @return string
  394.              */
  395.             public function getCaptureTransactionStatus(): string
  396.             {
  397.                 return $this->result->purchase_units[0]->payments->captures[0]->status ?? '';
  398.             }
  399.             /**
  400.              * @return bool
  401.              */
  402.             public function isOk(): bool
  403.             {
  404.                 return $this->getCaptureTransactionStatus() === 'COMPLETED';
  405.             }
  406.             /**
  407.              * @return bool
  408.              */
  409.             public function isNg(): bool
  410.             {
  411.                 return !$this->isOk();
  412.             }
  413.             /**
  414.              * @return string
  415.              */
  416.             public function getDebugId(): string
  417.             {
  418.                 return $this->headers["Paypal-Debug-Id"] ?? 'no debug_id';
  419.             }
  420.         };
  421.     }
  422.     /**
  423.      * @param string $captureId
  424.      * @return CapturesRefundRequest
  425.      */
  426.     public function prepareRefoundTransaction(string $captureId): CapturesRefundRequest
  427.     {
  428.         /** @var CapturesRefundRequest $request */
  429.         $request = new CapturesRefundRequest($captureId);
  430.         return $request;
  431.     }
  432.     /**
  433.      * @param CapturesRefundRequest $transaction
  434.      * @return PayPalHttpResponse
  435.      * @throws PayPalRequestException
  436.      */
  437.     public function refoundTransaction(CapturesRefundRequest $transaction): PayPalHttpResponse
  438.     {
  439.         return $this->send($transaction);
  440.     }
  441.     /**
  442.      * @param stdClass $options
  443.      * @return PayPalHttpRequest
  444.      */
  445.     public function prepareBillingAgreementToken(stdClass $options): PayPalHttpRequest
  446.     {
  447.         $request = new class() extends PayPalHttpRequest {
  448.             /**
  449.              *  constructor.
  450.              */
  451.             function __construct()
  452.             {
  453.                 parent::__construct("/v1/billing-agreements/agreement-tokens""POST");
  454.                 $this->headers["Content-Type"] = "application/json";
  455.             }
  456.             /**
  457.              * @param $payPalClientMetadataId
  458.              */
  459.             public function payPalClientMetadataId($payPalClientMetadataId)
  460.             {
  461.                 $this->headers["PayPal-Client-Metadata-Id"] = $payPalClientMetadataId;
  462.             }
  463.             /**
  464.              * @param $payPalRequestId
  465.              */
  466.             public function payPalRequestId($payPalRequestId)
  467.             {
  468.                 $this->headers["PayPal-Request-Id"] = $payPalRequestId;
  469.             }
  470.             /**
  471.              * @param $prefer
  472.              */
  473.             public function prefer($prefer)
  474.             {
  475.                 $this->headers["Prefer"] = $prefer;
  476.             }
  477.         };
  478.         $request->prefer('return=representation');
  479.         $request->body = [
  480.             "description" => "Billing Agreement",
  481.             "shipping_address" => $options->shipping_address,
  482.             "payer" => [
  483.                 "payment_method" => "PAYPAL"
  484.             ],
  485.             "plan" => [
  486.                 "type" => "MERCHANT_INITIATED_BILLING",
  487.                 "merchant_preferences" => [
  488.                     "return_url" => "https://www.paypal.com/checkoutnow/error",
  489.                     "cancel_url" => "https://www.paypal.com/checkoutnow/error",
  490.                     "notify_url" => "http://localhost/notify",
  491.                     "accepted_pymt_type" => "INSTANT",
  492.                     "skip_shipping_address" => false,
  493.                     "immutable_shipping_address" => true
  494.                 ]
  495.             ]
  496.         ];
  497.         return $request;
  498.     }
  499.     /**
  500.      * @param PayPalHttpRequest $transaction
  501.      * @return PayPalHttpResponse
  502.      * @throws PayPalRequestException
  503.      */
  504.     public function billingAgreementToken(PayPalHttpRequest $transaction): PayPalHttpResponse
  505.     {
  506.         return $this->send($transaction);
  507.     }
  508.     /**
  509.      * @param string $billingToken
  510.      * @return PayPalHttpRequest
  511.      */
  512.     public function prepareBillingAgreement(string $billingToken): PayPalHttpRequest
  513.     {
  514.         $request = new class() extends PayPalHttpRequest {
  515.             /**
  516.              *  constructor.
  517.              */
  518.             function __construct()
  519.             {
  520.                 parent::__construct("/v1/billing-agreements/agreements""POST");
  521.                 $this->headers["Content-Type"] = "application/json";
  522.             }
  523.             /**
  524.              * @param $payPalClientMetadataId
  525.              */
  526.             public function payPalClientMetadataId($payPalClientMetadataId)
  527.             {
  528.                 $this->headers["PayPal-Client-Metadata-Id"] = $payPalClientMetadataId;
  529.             }
  530.             /**
  531.              * @param $payPalRequestId
  532.              */
  533.             public function payPalRequestId($payPalRequestId)
  534.             {
  535.                 $this->headers["PayPal-Request-Id"] = $payPalRequestId;
  536.             }
  537.             /**
  538.              * @param $prefer
  539.              */
  540.             public function prefer($prefer)
  541.             {
  542.                 $this->headers["Prefer"] = $prefer;
  543.             }
  544.         };
  545.         $request->prefer('return=representation');
  546.         $request->body = [
  547.             "token_id" => $billingToken
  548.         ];
  549.         return $request;
  550.     }
  551.     /**
  552.      * @param PayPalHttpRequest $transaction
  553.      * @return PayPalHttpResponse
  554.      * @throws PayPalRequestException
  555.      */
  556.     public function billingAgreement(PayPalHttpRequest $transaction): PayPalHttpResponse
  557.     {
  558.         return $this->send($transaction);
  559.     }
  560.     /**
  561.      * @param string $billingAgreementId
  562.      * @param stdClass $options
  563.      * @return PayPalHttpRequest
  564.      */
  565.     public function prepareReferenceTransaction(string $billingAgreementIdstdClass $options): PayPalHttpRequest
  566.     {
  567.         $request = new class() extends PayPalHttpRequest {
  568.             /**
  569.              *  constructor.
  570.              */
  571.             function __construct()
  572.             {
  573.                 parent::__construct("/v1/payments/payment""POST");
  574.                 $this->headers["Content-Type"] = "application/json";
  575.             }
  576.             /**
  577.              * @param $payPalClientMetadataId
  578.              */
  579.             public function payPalClientMetadataId($payPalClientMetadataId)
  580.             {
  581.                 $this->headers["PayPal-Client-Metadata-Id"] = $payPalClientMetadataId;
  582.             }
  583.             /**
  584.              * @param $payPalRequestId
  585.              */
  586.             public function payPalRequestId($payPalRequestId)
  587.             {
  588.                 $this->headers["PayPal-Request-Id"] = $payPalRequestId;
  589.             }
  590.             /**
  591.              * @param $prefer
  592.              */
  593.             public function prefer($prefer)
  594.             {
  595.                 $this->headers["Prefer"] = $prefer;
  596.             }
  597.         };
  598.         $request->prefer('return=representation');
  599.         $request->body = [
  600.             "intent" => "sale",
  601.             "payer" => [
  602.                 "payment_method" => "PAYPAL",
  603.                 "funding_instruments" => [
  604.                     [
  605.                         "billing"=> [
  606.                             "billing_agreement_id" => $billingAgreementId
  607.                         ]
  608.                     ]
  609.                 ]
  610.             ],
  611.             "transactions" => [
  612.                 [
  613.                     "amount"=> $options->amount,
  614.                     "description" => "Payment transaction.",
  615.                     "custom" => "Payment custom field.",
  616.                     "note_to_payee" => "Note to payee field.",
  617.                     "invoice_number" => $options->invoice_number,
  618.                     "item_list"=> [
  619.                         "items" => $options->items,
  620.                         "shipping_address" => $options->shipping_address
  621.                     ]
  622.                 ]
  623.             ],
  624.             "redirect_urls" => [
  625.                 "return_url" => "https=>//example.com/return",
  626.                 "cancel_url" => "https=>//example.com/cancel"
  627.             ]
  628.         ];
  629.         return $request;
  630.     }
  631.     /**
  632.      * @param PayPalHttpRequest $transaction
  633.      * @return PayPalHttpResponse
  634.      * @throws PayPalRequestException
  635.      */
  636.     public function referenceTransactionPayment(PayPalHttpRequest $transaction): PayPalHttpResponse
  637.     {
  638.         /** @var PayPalHttpResponse $response */
  639.         $response $this->send($transaction);
  640.         return new class($response) extends PayPalHttpResponse implements ReferenceTransactionResponse {
  641.             /**
  642.              *  constructor.
  643.              * @param PayPalHttpResponse $response
  644.              */
  645.             function __construct(PayPalHttpResponse $response)
  646.             {
  647.                 parent::__construct($response->statusCode$response->result$response->headers);
  648.             }
  649.             /**
  650.              * @return string
  651.              */
  652.             public function getBillingAgreementId(): string
  653.             {
  654.                 return $this
  655.                     ->result
  656.                     ->payer
  657.                     ->funding_instruments[0]
  658.                     ->billing
  659.                     ->billing_agreement_id;
  660.             }
  661.         };
  662.     }
  663.     /**
  664.      * @param PayPalHttpRequest $transaction
  665.      * @return PayPalHttpResponse
  666.      * @throws PayPalRequestException
  667.      */
  668.     private function send(PayPalHttpRequest $transaction): PayPalHttpResponse
  669.     {
  670.         try {
  671.             /** @var PayPalHttpResponse $response */
  672.             $response $this->client->execute($transaction);
  673.         } catch (PayPalHttpException $e) {
  674.             throw new PayPalRequestException($e->getMessage(), $e->statusCode$e);
  675.         }
  676.         return $response;
  677.     }
  678. }