mxneyio/laravel-revolut-business

Laravel 为 Revolut 商业 Open API 的非官方封装

v1.0 2023-03-09 13:59 UTC

This package is not auto-updated.

Last update: 2024-09-20 19:40:40 UTC


README

Latest Stable Version License Build Status Scrutinizer Code Quality

Laravel-Revolut (Business)

Revolut 商业 Open API 提供非官方 Laravel 封装。
有关 Revolut 商户 API 的同族包可以在 此处 找到。

入门指南

阅读 Revolut 的官方文档,熟悉 API 和授权流程。

⚠️ 请在设置此包时使用 沙盒账户,并且只有在您确认一切正常后,才切换到您的真实账户。

要求

  • Laravel >=5.8
  • PHP >=7.2

安装

通过 composer 拉取此包。

composer require tbclla/laravel-revolut-business

服务提供者 & 门面

如果您已禁用 Laravel 的自动发现功能,请将服务提供者和门面添加到您的 config/app.php 文件中。

'providers' => [
    // ...
    tbclla\Revolut\Providers\RevolutServiceProvider::class,
],

'aliases' => [
    // ...
    'Revolut' => tbclla\Revolut\Facades\Revolut::class,
]

配置

安装此包后,发布配置文件。

php artisan vendor:publish --provider "tbclla\Revolut\Providers\RevolutServiceProvider"

现在您可以在 config/revolut.php 下配置此包。

访问过期

根据 官方文档,自由职业者计划上的企业必须每 90 天明确授权通过 API 访问其账户。如果您的企业适用,请将 'expire_api_access' 设置为 true

凭证

将以下密钥添加到您的 .env 文件中,因为大多数配置值都是从那里读取的。
您将在设置 API 访问时填写缺失的值。

REVOLUT_SANDBOX=true
REVOLUT_PRIVATE_KEY=
REVOLUT_REDIRECT_URI=
REVOLUT_CLIENT_ID=

令牌存储

令牌可以存储在您的数据库或缓存中。

缓存

当使用 'cache' 存储时,您可以可选地定义一个 driver。如果设置为 null,则使用您的应用程序的默认缓存驱动程序(config/cache.php > default)。

数据库

当使用 'database' 存储时,在运行迁移时将自动包含所需的表的迁移。您可以在配置中自定义此表的名称。

运行 php artisan migrate 以创建表。

设置 API 访问

请遵循 Revolut 关于如何设置 API 访问的文档 中的步骤 1 和 2。

步骤 1 - 生成一对公钥/私钥

完成 Revolut 指令的步骤 1 以生成密钥对。
❗将您的私钥路径添加到 .env 中,作为 REVOLUT_PRIVATE_KEY

步骤 2 - 上传您的公钥

遵循 Revolut 的步骤 2 上传您新创建的公钥并提供重定向 URI。
⚠️ 您不需要为重定向 URI 创建路由或控制器,此包将处理它。
❗将此重定向 URI 添加到 .env 中,作为 REVOLUT_REDIRECT_URI

Revolut 现在将为您创建一个客户端 ID。
❗将此客户端 ID 添加到 .env 中,作为 REVOLUT_CLIENT_ID

步骤 3 - 签名 JWT

跳过此步骤,此包将在需要时为您生成 JWT。
您可以通过以下 artisan 命令生成 JWT 来验证您已正确配置一切。可选地,您可以使用 --public 标志传递匹配的公钥路径,以验证 JWT。

php artisan revolut:jwt

php artisan revolut:jwt --public /Path/to/publickey.cer

步骤 4-7

您无需完成剩余的任何步骤!
相反,使用以下 artisan 命令启动授权,并按照生成的链接进行操作。

php artisan revolut:authorize

为了减轻 CSRF 攻击,通过 Revolut 的网络界面请求授权代码在 Laravel-Revolut 中不起作用!有关授权过程的更多信息,请参阅此处

完成

现在,访问和刷新令牌将存储在您定义的令牌存储中。此包现在将使用此访问令牌直到其过期,并通过刷新令牌在需要时从 Revolut 请求新的令牌。

要验证您的应用已被授权,您可以使用以下 artisan 命令检索访问令牌。

php artisan revolut:access-token

使用 API

要使用客户端,您可以通过外观或从 Laravel 的服务容器中解析它来访问其方法。

use tbclla\Revolut\Client;
use tbclla\Revolut\Facades\Revolut;

$revolut = resolve(Client::class);
$revolut->account()->details('11d79893-2703-489f-96e9-7946d9aba8b7');
// or simply
Revolut::account()->details('11d79893-2703-489f-96e9-7946d9aba8b7');

账户

请参阅Revolut 的文档

获取所有账户

$accounts = Revolut::account()->all();

获取一个账户

Revolut::account()->get('ac57ffc9-a5cb-4322-89d2-088e8a007a97');

获取账户详情

Revolut::account()->details('11d79893-2703-489f-96e9-7946d9aba8b7');

对手方

获取所有对手方

Revolut::counterparty()->all();

获取一个对手方

Revolut::counterparty()->get('5435ff9e-bacd-430b-95c2-094da8662829');

删除一个对手方

Revolut::counterparty()->delete('5435ff9e-bacd-430b-95c2-094da8662829');

创建一个对手方

有关更多详细信息,请参阅Revolut 的文档

创建 Revolut 对手方
Revolut::counterparty()->create([
    "profile_type" => "personal",
    "name" => "John Smith",
    "phone" => "+4412345678900"
]);
创建非 Revolut 对手方
Revolut::counterparty()->create([
    "company_name" => "John Smith Co.",
    "bank_country" => "GB",
    "currency" => "GBP",
    "account_no" => "12345678",
    "sort_code" => "223344",
    "email" => "test@sandboxcorp.com",
    "address" => [
        "street_line1" => "1 Canada Square",
        "street_line2" => "Canary Wharf",
        "region" => "East End",
        "postcode" => "E115AB",
        "city" => "London",
        "country" => "GB"
    ]
]);

构建对手方

有关构建器和如何使用它们的更多信息,请参阅此处

构建 Revolut 对手方
$builder = Revolut::counterparty()->build();
$builder->profileType('personal')
$builder->name('John Doe')
$builder->phone('+4412345678900');
$builder->create();

构建器还提供了以下快捷方式以实现相同的功能

Revolut::counterparty()->build()->personal('John Doe', '+4412345678900')->create();

Revolut::counterparty()->build()->business('test@sandboxcorp.com')->create();
构建非 Revolut 对手方
$counterparty = Revolut::counterparty()->build()
    ->bankCountry('GB')
    ->currency('GBP')
    ->accountNumber('12345678')
    ->sortCode('223344');

$counterparty->companyName('John Smith Co');

// or for an individual
$counterparty->individualName('John', 'Smith');

// The counterparty builder accepts the address as an array
$counterparty->address([
    "street_line1" => "1 Canada Square",
    "street_line2" => "Canary Wharf",
    "region" => "East End",
    "postcode" => "E115AB",
    "city" => "London",
    "country" => "GB"
]);

// Alternatively, the builder lets you build the address fluently
$counterparty->streetLine1('1 Canada Square')
    ->streetLine2('Canary Wharf')
    ->region('East End')
    ->postcode('E115AB')
    ->city('London')
    ->country('GB');

转账

请参阅Revolut 关于如何创建转账的文档

创建转账

Revolut::transfer()->create([
    "request_id" => "e0cbf84637264ee082a848b",
    "source_account_id" => "bdab1c20-8d8c-430d-b967-87ac01af060c",
    "target_account_id" => "5138z40d1-05bb-49c0-b130-75e8cf2f7693",
    "amount" => 123.11,
    "currency" => "EUR",
]);

构建转账

有关构建器和如何使用它们的更多信息,请参阅此处

$transfer = Revolut::transfer()->build()
    ->sourceAccount($sourceAccountId)
    ->targetAccout($targetAccountId)
    ->amount(231.20)
    ->reference('payroll'); // optional

// If you want to keep the request ID for your records, retrieve it from the builder
$requestId = $transfer->request_id;

$transfer->create();

付款

请参阅Revolut 关于如何创建付款的文档

创建付款

Revolut::payment()->create([
    "request_id" => "e0cbf84637264ee082a848b",
    "account_id" => "bdab1c20-8d8c-430d-b967-87ac01af060c",
    "receiver" =>[
        "counterparty_id" => "5138z40d1-05bb-49c0-b130-75e8cf2f7693",
        "account_id" => "db7c73d3-b0df-4e0e-8a9a-f42aa99f52ab"
    ],
    "amount" => 123.11,
    "currency" => "EUR",
]);

构建付款

有关构建器和如何使用它们的更多信息,请参阅此处

$payment = Revolut::payment()->build()
    ->account('bdab1c20-8d8c-430d-b967-87ac01af060c')
    ->receiver('5138z40d1-05bb-49c0-b130-75e8cf2f7693')
    ->amount(93.12)
    ->currency('USD')
    ->create();

安排付款

schedule() 方法接受与 create() 方法相同的数据,以及 ISO 日期作为其第二个参数。

Revolut::payment()->schedule($data, '2020-05-19');

取消安排的付款

Revolut::payment()->cancel('b63f30f0-62dc-4b6b-98cf-2a9a2e5ac981');

交易

获取所有交易

all() 方法接受一个可选的数组作为其第一个参数。请参阅Revolut 的文档以获取可用过滤器的列表。

$transactions = Revolut::transaction()->all();

$filtered = Revolut::transaction()->all([
    'count' => 200,
    'type' => 'card_payment',
]);

根据官方文档,90 天前的交易只能在您的访问令牌在过去 5 分钟内生成的情况下才能访问。为了处理这种情况,您可以通过传递一个可选的布尔值作为第二个参数,指示是否在发出请求之前强制客户端刷新访问令牌。

Revolut::transaction()->all([], true);

获取一个交易

get() 方法允许您通过其 ID 获取交易。如果您想通过其请求 ID 获取交易,可以使用 getByRequestId() 方法。

Revolut::transaction()->get($id);

Revolut::transaction()->getByRequestId($requestId);

付款草稿

获取所有付款草稿

Revolut::paymentDraft()->all();

获取一个付款草稿

Revolut::paymentDraft()->get($id);

删除一个付款草稿

Revolut::paymentDraft()->delete($id);

创建一个付款草稿

Revolut::paymentDraft()->create([
    "title": "Sample title",
    "schedule_for": '2020-05-29',
    "payments" => [
        [
            "currency" => "EUR",
            "amount" => 123,
            "account_id" => "db7c73d3-b0df-4e0e-8a9a-f42aa99f52ab",
            "receiver" => [
                "counterparty_id" => "5138z40d1-05bb-49c0-b130-75e8cf2f7693",
                "account_id" => "bdab1c20-8d8c-430d-b967-87ac01af060c"
            ],
        ]
    ]
]);

构建一个付款草稿

在构建支付草案时,可以通过传递支付数组给payment()方法来设置支付,或者通过addPayment()方法添加单个支付。

$date = now()->addDays(7)->format('Y-m-d');

$draft = Revolut::paymentDraft()->build()
    ->title('Sample title')
    ->schedule($date)
    ->payments($payments);

foreach ($employees as $employee) {
    $draft->addPayment($payment);
}

$draft->create()

汇率

获取汇率

get()方法接受源货币和目标货币作为前两个参数。
您可以选择传递一个交换金额作为第三个参数,否则默认为1。

Revolut::rate()->get('EUR', 'CHF');

Revolut::rate()->get('USD', 'GBP', 143.23);

交换

创建交换

Revolut::exchange()->create([
    "from" => [
        "account_id" => "d56dd396-523b-4613-8cc7-54974c17bcac",
        "currency" => "USD",
        "amount" => 135.25
    ],
    "to": [
        "account_id" => "a44dd365-523b-4613-8457-54974c8cc7ac",
        "currency" => "EUR"
    ],
    "reference" => "Time to sell",
    "request_id" => Revolut::generateRequestId(),
]);

构建交换

$exchange = Revolut::exchange()->build()
    ->reference('Time to sell')
    ->from('d56dd396-523b-4613-8cc7-54974c17bcac', 'USD', 135.25)
    ->to('a44dd365-523b-4613-8457-54974c8cc7ac', 'EUR');

$response = $exchange->create()

Web钩子

有关Web钩子和可用事件的信息,请参阅Revolut的文档

创建web钩子

Revolut::webhook()->create('https://mydomain.com/endpoint');

删除web钩子

Revolut::webhook()->delete();

构建器

所有具有create()方法(Web钩子除外)的API资源也都有一个返回特定资源实例的tbclla\Revolut\Builders\Builderbuild()方法。构建器可以用来以更流畅的方式创建有时复杂的数据数组。

Revolut::counterparty()->build() // tbclla\Revolut\Builders\CounterpartyBuilder
Revolut::payment()->build() // tbclla\Revolut\Builders\PaymentBuilder
Revolut::paymentDraft()->build() // tbclla\Revolut\Builders\PaymentDraftBuilder
Revolut::transfer()->build() // tbclla\Revolut\Builders\TransferBuilder
Revolut::exchange()->build() // tbclla\Revolut\Builders\ExchangeBuilder

输出

所有构建器都使用toArray()方法以Revolut要求的格式返回数据。
例如

Revolut::exchange()->build()
    ->from('d56dd396-523b-4613-8cc7-54974c17bcac', 'USD')
    ->to('a44dd365-523b-4613-8457-54974c8cc7ac', 'EUR', 735.23)
    ->reference('Off to France!')
    ->toArray();

将返回

[
    'from' => [
        'account_id' => 'd56dd396-523b-4613-8cc7-54974c17bcac',
        'currency' => 'USD'
    ],
    'to' => [
        'account_id' => 'a44dd365-523b-4613-8457-54974c8cc7ac',
        'currency' => 'EUR',
        'amount' => 735.23,
    ],
    'reference' => 'Off to France!',
    'request_id' => 'c60ec5b3-c5b9-4cea-936c-fa0306374df5'
]

创建构建资源

完成构建后,您只需在构建器上调用create()方法。

Revolut::transfer()->build()
    ->sourceAccount('bdab1c20-8d8c-430d-b967-87ac01af060c')
    ->targetAccout('5138z40d1-05bb-49c0-b130-75e8cf2f7693')
    ->amount(231.20)
    ->create();

请求ID

Revolut要求某些请求包含唯一的request_id参数。

如果您使用构建器,请求ID将为您自动创建。您可以设置自己的请求ID,或从构建器获取现有请求ID。

$builder = Revolut::exchange()->build()->requestId('my_own_request_id');

$requestId = $builder->request_id;

如果您不使用构建器,您可以使用Revolut客户端上的静态generateRequestId()方法创建请求ID——这是构建器在底层使用的。此方法使用\Illuminate\Support\Str::Uuid()返回一个UUIDv4字符串。

use tbclla\Revolut\Client;

Client::generateRequestId();

令牌和授权

授权

根据RFC6749 10.12,此包实现了对redirect_uri的CSRF保护,并包含一个控制器来处理授权请求和后续响应以强制执行此操作。

授权请求

由于控制器将需要一个有效的状态参数,因此您**不能**使用Revolut的Web界面与该包一起授权您的应用程序。要授权您的应用程序,您必须从auth_route进入Revolut的OAuth流程。
您可以通过以下artisan命令获取URL。

php artisan revolut:authorize

如果您需要将用户重定向到Revolut的授权流程,您可以通过路由助手获取URL。auth_route是一个命名路由,其名称在您的config/revolut.php文件中的auth_route.name下可配置。您可以将可选的'after_success'参数传递给它,该参数将在授权完成后将用户重定向到指定位置。

$url = route('revolut-authorize');

$url = route('revolut-authorize', [
    'after_success' => route('home')
]);

return redirect($url);

要始终将用户重定向到授权流程,当您的应用程序未授权时,您可以捕获一个AppUnauthorizedException,该异常在没有有效的刷新令牌且您的应用程序需要重新授权时抛出。

use tbclla\Revolut\Exceptions\AppUnauthorizedException;

Route::get('/accounts', function () {
    try {
        return Revolut::account()->all();
    } catch(AppUnauthorizedException $e) {
        return redirect(route('revolut-authorization', [
            'after_success' => '/accounts'
        ]));
    }
});

授权响应

您的应用程序已授权后,Revolut将重定向您到您在创建API证书时设置的重定向URI。此重定向URI必须与config/revolut.php中设置的redirect_uri匹配。

您**不需要**为该重定向URI创建路由或控制器。

包含在此包中的授权控制器将验证响应的状态参数的存在和有效性,如果接受,将交换响应的授权代码以获取访问令牌和刷新令牌。一旦收到并存储这些令牌,控制器将重定向用户到指定位置。如果没有提供位置,控制器将返回一个200响应。

令牌存储

此包会将访问令牌和刷新令牌存储在您的数据库或缓存中。授权码永远不会被存储,而是立即交换为令牌。您可以在 config/revolut.php 文件中配置令牌驱动程序。

令牌加密

默认情况下,所有访问令牌和刷新令牌在存储到您的数据库或缓存之前都会被加密。此包使用 Laravel 内置加密工具 加密您的令牌,因此请确保在您的 config/app.php 中设置了一个强大的 key

清理过期令牌

缓存

存储在您的缓存中的令牌仅在其有效期内被记住,因此不需要删除它们。如果您想从缓存中删除令牌,可以使用它们的相应键调用 forget()

// remove access token
Cache::forget('revolut_access_token');
// remove refresh token
Cache::forget('revolut_refresh_token');

数据库

如果您正在使用数据库令牌存储,可以使用以下 artisan 命令从数据库中删除过期的访问令牌和刷新令牌。

php artisan revolut:cleanup

您还可以在 App\Console\Kernel 类中安排此命令,以自动执行此过程。

$schedule->command('revolut:cleanup')->daily();

从沙盒环境切换到真实账户

  • 更新您的 .env 文件并设置 REVOLUT_SANDBOX=false
  • 从您的数据库或缓存中清除任何沙盒令牌。
  • 更新您的 config/revolut.php 并将 encrypt_tokens 设置为 true(如果尚未设置)。
  • 通过访问账户设置 > API,为将访问 API 的 IP 地址添加到白名单。
  • 重新授权您的应用程序。

许可证

本项目采用 MIT 许可证授权 - 有关详细信息,请参阅 LICENSE.md 文件。