payPalClient = $payPalClient; $this->connectClient = $connectClient; } /** * Retrieves a token for the Client ID and Secret * * @since 2.32.0 Use PayPal client for rest api calls. * @since 2.25.0 Validate paypal response. * @since 2.9.0 * * @param string $client_id * @param string $client_secret * * @throws RuntimeException|\Exception */ public function getTokenFromClientCredentials($client_id, $client_secret): array { $auth = base64_encode("$client_id:$client_secret"); $response = $this->payPalClient->getHttpClient() ->execute(new GetAccessToken( ['grant_type' => 'client_credentials'], ['Authorization' => "Basic $auth"] )); if (200 !== $response->statusCode) { give(Log::class)->http( 'PayPal Commerce: Error retrieving access token with client credentials', [ 'category' => 'Payment Gateway', 'source' => 'Paypal Commerce', 'response' => $response, ] ); throw new RuntimeException('PayPal Commerce: Error retrieving access token with client credentials'); } $result = (array)$response->result; $this->validateAccessToken($result); return ArrayDataSet::camelCaseKeys($result); } /** * Retrieves a token from the authorization code * * @since 2.32.0 Use PayPal client for rest api calls. * @since 2.9.0 * * @param string $authCode * @param string $sharedId * @param string $nonce * * @return array|null * @throws RuntimeException|\Exception */ public function getTokenFromAuthorizationCode($authCode, $sharedId, $nonce) { $auth = base64_encode($sharedId); $response = $this->payPalClient->getHttpClient() ->execute(new GetAccessToken( [ 'grant_type' => 'authorization_code', 'code' => $authCode, 'code_verifier' => $nonce, // Seller nonce. ], ['Authorization' => "Basic $auth"] )); if (200 !== $response->statusCode) { give(Log::class)->http( 'PayPal Commerce: Error retrieving access token with authorization code', [ 'category' => 'Payment Gateway', 'source' => 'Paypal Commerce', 'response' => $response, ] ); throw new RuntimeException('PayPal Commerce: Error retrieving access token with authorization code'); } $result = (array)$response->result; $this->validateSellerAccessToken($result); return ArrayDataSet::camelCaseKeys($result); } /** * Retrieves a Partner Link for on-boarding * * @since 4.1.0 Add PayPal-Partner-Attribution-Id header * @since 3.0.0 Implement $accountType. This parameter is required by GiveWP gateway server. * @since 2.9.0 * * @return array|null */ public function getSellerPartnerLink($returnUrl, $accountType) { $response = wp_remote_retrieve_body( wp_remote_post( sprintf( $this->connectClient->getApiUrl('paypal?mode=%1$s&request=partner-link'), $this->payPalClient->getMode() ), [ 'headers' => [ 'PayPal-Partner-Attribution-Id' => give('PAYPAL_COMMERCE_ATTRIBUTION_ID'), ], 'body' => [ 'return_url' => $returnUrl, 'account_type' => $accountType, ], ] ) ); return empty($response) ? null : json_decode($response, true); } /** * Get seller on-boarding details from seller. * * @since 4.1.0 Add PayPal-Partner-Attribution-Id header * @since 2.9.0 * * @param string $accessToken * * @param string $merchantId * * @return array */ public function getSellerOnBoardingDetailsFromPayPal($merchantId, $accessToken) { $request = wp_remote_post( $this->connectClient->getApiUrl( sprintf( 'paypal?mode=%1$s&request=seller-status', $this->payPalClient->getMode() ) ), [ 'headers' => [ 'PayPal-Partner-Attribution-Id' => give('PAYPAL_COMMERCE_ATTRIBUTION_ID'), ], 'body' => [ 'merchant_id' => $merchantId, 'token' => $accessToken, ], ] ); return json_decode(wp_remote_retrieve_body($request), true); } /** * Get seller rest API credentials * * @since 4.1.0 Add PayPal-Partner-Attribution-Id header * @since 2.9.0 * * @param string $accessToken * * @return array */ public function getSellerRestAPICredentials($accessToken) { $request = wp_remote_post( $this->connectClient->getApiUrl( sprintf( 'paypal?mode=%1$s&request=seller-credentials', $this->payPalClient->getMode() ) ), [ 'headers' => [ 'PayPal-Partner-Attribution-Id' => give('PAYPAL_COMMERCE_ATTRIBUTION_ID'), ], 'body' => [ 'token' => $accessToken, ], ] ); return json_decode(wp_remote_retrieve_body($request), true); } /** * Validate PayPal access token. * * Sample PayPal access token: https://developer.paypal.com/api/rest/authentication/#link-sampleresponse * * @since 3.5.1 removed app_id from required keys as PayPal no longer supplies it * @since 2.25.0 * * @param array $accessToken Access token response from PayPal. * * @return void * @throws UnexpectedValueException */ private function validateAccessToken(array $accessToken) { $requiredKeys = [ 'scope', 'access_token', 'token_type', 'expires_in', 'nonce' ]; if (array_diff($requiredKeys, array_keys($accessToken))) { give(Log::class)->error( 'PayPal Commerce: Invalid access token', [ 'category' => 'Payment Gateway', 'source' => 'Paypal Commerce', 'response' => $accessToken, ] ); throw new UnexpectedValueException('PayPal Commerce: Error retrieving access token'); } } /** * Validate PayPal seller access token. * * Sample PayPal access token: https://developer.paypal.com/docs/multiparty/seller-onboarding/build-onboarding/#link-sampleresponse * * @since 2.32.0 * * @param array $sellerAccessToken Seller access token response from PayPal. * * @return void * @throws UnexpectedValueException */ private function validateSellerAccessToken(array $sellerAccessToken) { $requiredKeys = [ 'scope', 'access_token', 'token_type', 'refresh_token', 'expires_in', 'nonce' ]; if (array_diff($requiredKeys, array_keys($sellerAccessToken))) { give(Log::class)->error( 'PayPal Commerce: Invalid seller access token', [ 'category' => 'Payment Gateway', 'source' => 'Paypal Commerce', 'response' => $sellerAccessToken, ] ); throw new UnexpectedValueException('PayPal Commerce: Error retrieving seller access token'); } } }