Skip to content

Commit

Permalink
feat: paypal providers (#274)
Browse files Browse the repository at this point in the history
* feat: paypal providers

* 使用驼峰命名法

* docs: paypal
  • Loading branch information
izhiqiang authored Jun 7, 2024
1 parent 457b48f commit 4929bbb
Show file tree
Hide file tree
Showing 4 changed files with 228 additions and 18 deletions.
55 changes: 37 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ $socialite = new SocialiteManager($config);

$url = $socialite->create('github')->redirect();

return redirect($url);
return redirect($url);
```

`callback.php`:
Expand Down Expand Up @@ -122,15 +122,15 @@ $config = [
'client_secret' => 'your-app-secret',
'redirect' => 'http://localhost/socialite/callback.php',
],

// 另外一个名字叫做 bar 的 github 应用
'bar' => [
'provider' => 'github', // <-- provider name
'client_id' => 'your-app-id',
'client_secret' => 'your-app-secret',
'redirect' => 'http://localhost/socialite/callback.php',
],

//...
];

Expand Down Expand Up @@ -159,7 +159,7 @@ $config = [
];

$socialite = new SocialiteManager($config);

$socialite->extend('myprovider', function(array $config) {
return new MyCustomProvider($config);
});
Expand All @@ -172,7 +172,7 @@ $app = $socialite->create('foo');
>👋🏻 你的自定义服务提供类必须实现`Overtrue\Socialite\Contracts\ProviderInterface` 接口
```php
class MyCustomProvider implements \Overtrue\Socialite\Contracts\ProviderInterface
class MyCustomProvider implements \Overtrue\Socialite\Contracts\ProviderInterface
{
//...
}
Expand Down Expand Up @@ -208,8 +208,8 @@ $app = $socialite->create('foo');
$config = [
'alipay' => [
// 这个键名还能像官方文档那样叫做 'app_id'
'client_id' => 'your-app-id',
'client_id' => 'your-app-id',

// 请根据官方文档,在官方管理后台配置 RSA2
// 注意: 这是你自己的私钥
// 注意: 不允许私钥内容有其他字符
Expand All @@ -219,7 +219,7 @@ $config = [
// 确保这里的值与你在服务后台绑定的地址值一致
// 这个键名还能像官方文档那样叫做 'redirect_url'
'redirect' => 'http://localhost/socialite/callback.php',

// 沙箱模式接入地址见 https://opendocs.alipay.com/open/220/105337#%E5%85%B3%E4%BA%8E%E6%B2%99%E7%AE%B1
'sandbox' => false,
]
Expand Down Expand Up @@ -252,7 +252,7 @@ $config = [
// or 'app_id'
'client_id' => 'your app id',

// or 'app_secret'
// or 'app_secret'
'client_secret' => 'your app secret',

// or 'redirect_url'
Expand Down Expand Up @@ -361,7 +361,7 @@ $config = [
// or 'app_id'
'client_id' => 'your app id',

// or 'app_secret'
// or 'app_secret'
'client_secret' => 'your app secret',

// or 'redirect_url'
Expand Down Expand Up @@ -391,7 +391,7 @@ $config = [
// or 'app_id'
'client_id' => 'your app id',

// or 'app_secret'
// or 'app_secret'
'client_secret' => 'your app secret',

// or 'redirect_url'
Expand All @@ -413,7 +413,7 @@ $larkDriver->withDefaultMode()->withAppTicket('app_ticket')->userFromCode('here

### [淘宝](https://open.taobao.com/doc.htm?docId=102635&docType=1&source=search)

其他配置与其他平台的一样,你能选择你想要展示的重定向页面类型通过使用 `withView()`
其他配置与其他平台的一样,你能选择你想要展示的重定向页面类型通过使用 `withView()`

```php
$authUrl = $socialite->create('taobao')->withView('wap')->redirect();
Expand Down Expand Up @@ -456,14 +456,32 @@ $authUrl = $socialite->create('taobao')->withView('wap')->redirect();
```php
$config = [
'coding' => [
'team_url' => 'https://{your-team}.coding.net',
'team_url' => 'https://{your-team}.coding.net',
'client_id' => 'your app id',
'client_secret' => 'your app secret',
'redirect' => 'redirect URL',
]
];
```

### [PayPal](https://developer.paypal.com/docs/log-in-with-paypal/)

您可能需要设置responseType,可以使用`withResponseType`函数进行设置,默认是`code` 还可以设置为`id_token``code` & `id_token`

> https://developer.paypal.com/docs/log-in-with-paypal/integrate/generate-button/

```php
$config = [
'paypal' => [
'client_id' => 'AT******************',
'client_secret' => 'EK**************',
'sandbox' => false,
'redirect_url'=>"nativexo://paypalpay",
],
];
```

## 其他一些技巧

### Scopes
Expand Down Expand Up @@ -498,7 +516,7 @@ $socialite->withRedirectUrl($url)->redirect();
```php
<?php
session_start();

$config = [
//...
];
Expand All @@ -510,7 +528,7 @@ $socialite = new SocialiteManager($config);

$url = $socialite->create('github')->withState($state)->redirect();

return redirect($url);
return redirect($url);
```

### 检验回调的 `state`
Expand All @@ -520,10 +538,10 @@ return redirect($url);
```php
<?php
session_start();

$state = request()->query('state');
$code = request()->query('code');

// Check the state received with current session id
if ($state != hash('sha256', session_id())) {
exit('State does not match!');
Expand Down Expand Up @@ -596,7 +614,7 @@ mixed $user->getId();
?string $user->getEmail();
?string $user->getAvatar();
?string $user->getRaw();
?string $user->getAccessToken();
?string $user->getAccessToken();
?string $user->getRefreshToken();
?int $user->getExpiresIn();
?array $user->getTokenResponse();
Expand Down Expand Up @@ -648,6 +666,7 @@ $user = $socialite->userFromToken($accessToken);
- [Tapd - 用户授权说明](https://www.tapd.cn/help/show#1120003271001000093)
- [Line - OAuth 2.0](https://developers.line.biz/en/docs/line-login/integrate-line-login/)
- [Gitee - OAuth文档](https://gitee.com/api/v5/oauth_doc#/)
- [PayPal - OAuth文档](https://developer.paypal.com/docs/log-in-with-paypal/)



Expand Down
21 changes: 21 additions & 0 deletions README_EN.md
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,26 @@ $config = [
];
```

### [PayPal](https://developer.paypal.com/docs/log-in-with-paypal/)

You may need to set the responseType, which can be set using the `withResponseType` function. The default is `code` and can also be set to `id_token` or `code` & `id_token`

> https://developer.paypal.com/docs/log-in-with-paypal/integrate/generate-button/

```php
$config = [
'paypal' => [
'client_id' => 'AT******************',
'client_secret' => 'EK**************',
'sandbox' => false,
'redirect_url'=>"nativexo://paypalpay",
],
];
```



## Some Skill

### Scopes
Expand Down Expand Up @@ -627,6 +647,7 @@ $user = $socialite->userFromToken($accessToken);
- [Tapd - 用户授权说明](https://www.tapd.cn/help/show#1120003271001000093)
- [Line - OAuth 2.0](https://developers.line.biz/en/docs/line-login/integrate-line-login/)
- [Gitee - OAuth文档](https://gitee.com/api/v5/oauth_doc#/)
- [PayPal - OAuth文档](https://developer.paypal.com/docs/log-in-with-paypal/)

[![Sponsor me](https://github.com/overtrue/overtrue/blob/master/sponsor-me.svg?raw=true)](https://github.com/sponsors/overtrue)

Expand Down
169 changes: 169 additions & 0 deletions src/Providers/PayPal.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
<?php

namespace Overtrue\Socialite\Providers;

use JetBrains\PhpStorm\Pure;
use Overtrue\Socialite\Contracts;
use Overtrue\Socialite\User;

/**
* Class PayPal
* @author zhiqiang
* @package Overtrue\Socialite\Providers
* @see https://developer.paypal.com/docs/log-in-with-paypal/
*/
class PayPal extends Base
{
public const NAME = 'paypal';

protected string $scopeSeparator = ' ';
/**
* @var string|null
* code or id_token
*/
protected ?string $responseType = Contracts\RFC6749_ABNF_CODE;
protected string $flowEntry = 'static';

protected string $authUrl = 'https://www.paypal.com/signin/authorize';
protected string $tokenURL = "https://api.sandbox.paypal.com/v1/oauth2/token";
protected string $userinfoURL = "https://api.paypal.com/v1/identity/openidconnect/userinfo";

protected array $scopes = [
'openid', 'profile', 'email', 'address'
];

protected bool $sandbox = true;

public function __construct(array $config)
{
parent::__construct($config);
$this->sandbox = (bool)$this->config->get('sandbox', false);
if ($this->sandbox) {
$this->authUrl = 'https://www.sandbox.paypal.com/signin/authorize';
$this->tokenURL = 'https://api-m.sandbox.paypal.com/v1/oauth2/token';
$this->userinfoURL = 'https://api-m.sandbox.paypal.com/v1/identity/openidconnect/userinfo';
}
}

/**
* @param string|null $responseType
* @return $this
* @see https://developer.paypal.com/docs/log-in-with-paypal/integrate/generate-button/
*/
public function withResponseType(?string $responseType)
{
$this->responseType = $responseType;
return $this;
}

protected function getAuthUrl(): string
{
return $this->buildAuthUrlFromBase($this->authUrl);
}

protected function getCodeFields(): array
{
$fields = \array_merge(
[
'flowEntry' => $this->flowEntry,
Contracts\RFC6749_ABNF_CLIENT_ID => $this->getClientId(),
Contracts\RFC6749_ABNF_RESPONSE_TYPE => $this->responseType,
Contracts\RFC6749_ABNF_SCOPE => $this->formatScopes($this->scopes, $this->scopeSeparator),
Contracts\RFC6749_ABNF_REDIRECT_URI => $this->redirectUrl,
],
$this->parameters
);

if ($this->state) {
$fields[Contracts\RFC6749_ABNF_STATE] = $this->state;
}
return $fields;
}


protected function getTokenUrl(): string
{
return $this->tokenURL;
}

/**
* @param string $code
* @return array
* @throws \GuzzleHttp\Exception\GuzzleException
* @throws \Overtrue\Socialite\Exceptions\AuthorizeFailedException
* @see https://developer.paypal.com/docs/log-in-with-paypal/integrate/#link-getaccesstoken
*/
public function tokenFromCode(string $code): array
{
$response = $this->getHttpClient()->post(
$this->getTokenUrl(),
[
'form_params' => [
Contracts\RFC6749_ABNF_GRANT_TYPE => Contracts\RFC6749_ABNF_AUTHORATION_CODE,
Contracts\RFC6749_ABNF_CODE => $code,
],
'headers' => [
'Accept' => 'application/json',
'Authorization' => 'Basic ' . \base64_encode(\sprintf('%s:%s', $this->getClientId(), $this->getClientSecret())),
],
]
);
return $this->normalizeAccessTokenResponse((string)$response->getBody());
}

/**
* @param string $refreshToken
* @return mixed
* @throws \GuzzleHttp\Exception\GuzzleException
* @throws \Overtrue\Socialite\Exceptions\AuthorizeFailedException
* @see https://developer.paypal.com/docs/log-in-with-paypal/integrate/#link-exchangerefreshtokenforaccesstoken
*/
public function refreshToken(string $refreshToken): mixed
{
$response = $this->getHttpClient()->post(
$this->getTokenUrl(),
[
'form_params' => [
Contracts\RFC6749_ABNF_GRANT_TYPE => Contracts\RFC6749_ABNF_REFRESH_TOKEN,
Contracts\RFC6749_ABNF_REFRESH_TOKEN => $refreshToken,
],
'headers' => [
'Accept' => 'application/json',
'Authorization' => 'Basic ' . \base64_encode(\sprintf('%s:%s', $this->getClientId(), $this->getClientSecret())),
],
]
);
return $this->normalizeAccessTokenResponse((string)$response->getBody());
}

/**
* @param string $token
* @return array
* @throws \GuzzleHttp\Exception\GuzzleException
* @see https://developer.paypal.com/docs/api/identity/v1/#userinfo_get
*/
protected function getUserByToken(string $token): array
{
$response = $this->getHttpClient()->get(
$this->userinfoURL,
[
'headers' => [
'Content-Type' => 'application/x-www-form-urlencoded',
'Authorization' => 'Bearer ' . $token,
],
]
);
return $this->fromJsonBody($response);
}

#[Pure]
protected function mapUserToObject(array $user): Contracts\UserInterface
{
$user[Contracts\ABNF_ID] = $user['user_id'] ?? null;
$user[Contracts\ABNF_NICKNAME] = $user['given_name'] ?? $user['family_name'] ?? $user['middle_name'] ?? null;
$user[Contracts\ABNF_NAME] = $user['name'] ?? '';
$user[Contracts\ABNF_EMAIL] = $user[Contracts\ABNF_EMAIL] ?? null;
$user[Contracts\ABNF_AVATAR] = $user['picture'] ?? null;
return new User($user);
}
}
1 change: 1 addition & 0 deletions src/SocialiteManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class SocialiteManager implements Contracts\FactoryInterface
Providers\WeWork::NAME => Providers\WeWork::class,
Providers\Weibo::NAME => Providers\Weibo::class,
Providers\XiGua::NAME => Providers\XiGua::class,
Providers\PayPal::NAME => Providers\PayPal::class,
];

#[Pure]
Expand Down

0 comments on commit 4929bbb

Please sign in to comment.