sebdesign / laravel-viva-payments
Laravel 集成 Viva Payments 通道的包
Requires
- php: ^8.1
- guzzlehttp/guzzle: ^7.2
- illuminate/config: ^9.0 | ^10.0 | ^11.0
- illuminate/routing: ^9.0 | ^10.0 | ^11.0
- illuminate/support: ^9.0 | ^10.0 | ^11.0
- spatie/laravel-data: ^2.0 | ^3.0 | ^4.0
Requires (Dev)
- larastan/larastan: ^2.7
- laravel/pint: ^1.4
- orchestra/testbench: ^7.0 | ^8.0 | ^9.0
- phpstan/phpstan-phpunit: ^1.3
- phpstan/phpstan-strict-rules: ^1.4
- phpunit/phpunit: ^9.5.16 | ^10.5
- dev-main
- v6.1.3
- v6.1.2
- v6.1.1
- v6.1.0
- v6.0.12
- v6.0.11
- v6.0.10
- v6.0.9
- v6.0.8
- v6.0.7
- v6.0.6
- v6.0.5
- v6.0.4
- v6.0.3
- v6.0.2
- v6.0.1
- v6.0.0
- v6.0.0-beta.3
- v6.0.0-beta.2
- v6.0.0-beta.1
- v6.0.0-alpha.6
- v6.0.0-alpha.5
- 6.0.0-alpha.4
- v6.0.0-alpha.3
- v6.0.0-alpha.2
- v6.0.0-alpha.1
- 5.x-dev
- v5.3.1
- v5.3.0
- v5.2.0
- v5.1.6
- v5.1.5
- v5.1.4
- v5.1.3
- v5.1.2
- v5.1.1
- v5.1.0
- v5.0.0
- v4.4.0
- v4.3.1
- v4.3.0
- v4.2.0
- v4.1.1
- v4.1.0
- v4.0.0
- v3.2.0
- v3.1.0
- v3.0.1
- v3.0.0
- v2.0.1
- v2.0.0
- v1.0.1
- v1.0.0
This package is auto-updated.
Last update: 2024-09-02 10:27:19 UTC
README
此包提供了 Viva Wallet 支付 API 的接口。它处理 智能结账 集成、ISV 支付 API 和 Webhooks。
请访问 Viva Wallet 开发者门户以获取有关 API 和更多信息的相关说明: https://developer.vivawallet.com
注意:此项目不是官方包,并且我与 Viva Payments 没有任何关联。
设置
安装
通过 Composer 安装此包。
此包需要 PHP 8.1 和 Laravel 9.0 或更高版本,并使用 Guzzle 7.0 进行 API 调用。根据您的依赖项使用适当的版本。
composer require sebdesign/laravel-viva-payments
服务提供者
该包将自动注册其服务提供者。
配置
将以下数组添加到您的 config/services.php
。
'viva' => [ 'api_key' => env('VIVA_API_KEY'), 'merchant_id' => env('VIVA_MERCHANT_ID'), 'environment' => env('VIVA_ENVIRONMENT', 'production'), 'client_id' => env('VIVA_CLIENT_ID'), 'client_secret' => env('VIVA_CLIENT_SECRET'), 'isv_partner_id' => env('VIVA_ISV_PARTNER_ID'), 'isv_partner_api_key' => env('VIVA_ISV_PARTNER_API_KEY'), ],
api_key
和 merchant_id
可在您的个人资料 设置 > API 访问 部分中找到。
client_id
和 client_secret
用于 智能结账。您可以在个人资料 设置 > API 访问 部分中生成 智能结账凭据。
isv_partner_id
和 isv_partner_api_key
是使用基本身份验证使用 ISV 支付 API 所必需的。
有关 API 身份验证的更多信息,请参阅开发者门户: https://developer.vivawallet.com/getting-started/find-your-account-credentials/client-smart-checkout-credentials/
环境
可以是 生产
或 演示
。
智能结账
有关智能结账过程的更多信息,请参阅开发者门户: https://developer.vivawallet.com/smart-checkout/
\Sebdesign\VivaPayments\Facades\Viva
门面提供了与智能结账集成交互所需的所有方法。
以下指南将引导您完成必要的步骤
创建支付订单
所需的请求金额以分为单位。所有其他参数都是可选的。请参阅 请求体架构。
use Sebdesign\VivaPayments\Facades\Viva; $orderCode = Viva::orders()->create( order: new CreatePaymentOrder(amount: 1000), );
重定向到智能结账
use Sebdesign\VivaPayments\Facades\Viva; $redirectUrl = Viva::orders()->redirectUrl( ref: $orderCode, color: '0000ff', paymentMethod: 23, ); return redirect()->away(path: $redirectUrl);
验证支付
use Sebdesign\VivaPayments\Facades\Viva; $response = Viva::transactions()->retrieve(transactionId: request('t'));
完整示例
<?php namespace App\Http\Controllers; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Sebdesign\VivaPayments\Enums\TransactionStatus; use Sebdesign\VivaPayments\Facades\Viva; use Sebdesign\VivaPayments\Requests\CreatePaymentOrder; use Sebdesign\VivaPayments\Requests\Customer; use Sebdesign\VivaPayments\VivaException; class CheckoutController extends Controller { /** * Create a payment order and redirect to the checkout page. */ public function checkout(): RedirectResponse { try { $orderCode = Viva::orders()->create(new CreatePaymentOrder( amount: 1000, customerTrns: 'Short description of purchased items/services to display to your customer', customer: new Customer( email: 'johdoe@vivawallet.com', fullName: 'John Doe', countryCode: 'GB', requestLang: 'en-GB', ), )); } catch (VivaException $e) { report($e); return back()->withErrors($e->getMessage()); } $redirectUrl = Viva::orders()->redirectUrl( ref: $orderCode, color: '0000ff', paymentMethod: 23, ); return redirect()->away($redirectUrl); } /** * Redirect from the checkout page and get the order details from the API. */ public function confirm(Request $request): RedirectResponse { try { $transaction = Viva::transactions()->retrieve($request->input('t')); } catch (VivaException $e) { report($e); return back()->withErrors($e->getMessage()); } $status = match ($transaction->statusId) { case TransactionStatus::PaymentPending: 'The order is pending.', case TransactionStatus::PaymentSuccessful: 'The order is paid.', case TransactionStatus::Error: 'The order was not paid.', } return view('order/success', compact('status')); } }
处理 Webhooks
Viva Payments 支持 Webhooks,此包提供了一种控制器,用于验证和处理传入的通知事件。
有关 Webhooks 的更多信息,请参阅开发者门户: https://developer.vivawallet.com/webhooks-for-payments/
定义路由
在您的 routes/web.php
中,为您的个人资料中每个 Webhooks 定义以下路由,相应地替换 URI 和控制器。
Route::get('viva/webhooks', [WebhookController::class, 'verify']); Route::post('viva/webhooks', [WebhookController::class, 'handle']);
排除 CSRF 保护
不要忘记将您的 Webhooks URI 添加到 VerifyCsrfToken
中间件的 $except
数组中。
<?php namespace App\Http\Middleware; use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware; class VerifyCsrfToken extends Middleware { /** * The URIs that should be excluded from CSRF verification. * * @var array<int, string> */ protected $except = ['viva/webhooks']; }
处理 Webhooks 事件
要处理来自 Viva Wallet 的任何请求,您可能需要监听 WebhookEvent
。根据 EventTypeId
,您可以处理任何事件。
use Sebdesign\VivaPayments\Enums\WebhookEventType; use Sebdesign\VivaPayments\Events\WebhookEvent; class EventServiceProvider { protected $listen = [ WebhookEvent::class => [WebhookEventListener::class], ]; } class WebhookEventListener { public function handle(WebhookEvent $event): void { match ($event->EventTypeId) { WebhookEventType::TransactionPaymentCreated => ..., WebhookEventType::TransactionFailed => ..., WebhookEventType::TransactionReversalCreated => ..., default => ..., }; } }
EventData
属性包含一个包含实际通知的对象。对象的类取决于通知类型
此外,还派发了 TransactionPaymentCreated
和 TransactionFailed
事件。您可以选择监听这些特定事件而不是监听 WebhookEvent
。
use Sebdesign\VivaPayments\Enums\WebhookEventType; use Sebdesign\VivaPayments\Events\TransactionFailed; use Sebdesign\VivaPayments\Events\TransactionPaymentCreated; class EventServiceProvider { protected $listen = [ TransactionPaymentCreated::class => [ ConfirmOrder::class, ], TransactionFailed::class => [ CancelOrder::class, ], ]; } class ConfirmOrder { public function handle(TransactionPaymentCreated $event): void { // } } class CancelOrder { public function handle(TransactionFailed $event): void { // } }
支付 API 参考
所有方法都接受一个$guzzleOptions
数组作为最后一个参数。此参数完全可选,允许您向Guzzle
客户端指定额外的请求选项。
订单
创建付款订单
use Sebdesign\VivaPayments\Facades\Viva; use Sebdesign\VivaPayments\Requests\CreatePaymentOrder; use Sebdesign\VivaPayments\Requests\Customer; $orderCode = Viva::orders()->create( order: new CreatePaymentOrder( amount: 1000, customerTrns: 'Short description of purchased items/services to display to your customer', customer: new Customer( email: 'johdoe@vivawallet.com', fullName: 'John Doe', phone: '+30999999999', countryCode: 'GB', requestLang: 'en-GB', ), paymentTimeOut: 300, preauth: false, allowRecurring: false, maxInstallments: 12, paymentNotification: true, tipAmount: 100, disableExactAmount: false, disableCash: true, disableWallet: true, sourceCode: '1234', merchantTrns: 'Short description of items/services purchased by customer', tags: [ 'tags for grouping and filtering the transactions', 'this tag can be searched on VivaWallet sales dashboard', 'Sample tag 1', 'Sample tag 2', 'Another string', ], cardTokens: ['ct_5d0a4e3a7e04469f82da228ca98fd661'], ), guzzleOptions: [], );
获取重定向URL
use Sebdesign\VivaPayments\Facades\Viva; $url = Viva::orders()->redirectUrl( ref: $orderCode, color: '0000ff', paymentMethod: 23, );
交易
检索交易
use Sebdesign\VivaPayments\Facades\Viva; $transaction = Viva::transactions()->retrieve( transactionId: 'c90d4902-6245-449f-b2b0-51d99cd09cfe', guzzleOptions: [], );
创建周期性交易
use Sebdesign\VivaPayments\Facades\Viva; use Sebdesign\VivaPayments\Requests\CreateRecurringTransaction; $response = Viva::transactions()->createRecurring( transactionId: '252b950e-27f2-4300-ada1-4dedd7c17904', transaction: new CreateRecurringTransaction( amount: 100, installments: 1, customerTrns: 'A description of products / services that is displayed to the customer', merchantTrns: 'Your merchant reference', sourceCode: '6054', tipAmount: 0, ), guzzleOptions: [], );
OAuth
请求访问令牌
参见:https://developer.vivawallet.com/authentication/#step-2-request-access-token
您不需要调用此方法,因为客户端在需要时会自动请求访问令牌。但是,如果您想的话,可以在运行时指定客户端凭据。
use Sebdesign\VivaPayments\Facades\Viva; Viva::withOAuthCredentials( clientId: 'client_id', clientSecret: 'client_secret', );
如果您需要手动请求访问令牌,可以使用requestToken
方法。此方法将令牌作为AccessToken
对象返回。
use Sebdesign\VivaPayments\Facades\Viva; // Using `client_id` and `client_secret` from `config/services.php`: $token = Viva::oauth()->requestToken(); // Using custom client credentials $token = Viva::oauth()->requestToken( clientId: 'client_id', clientSecret: 'client_secret', guzzleOptions: [], );
使用现有的访问令牌
如果您将令牌存储在某个地方,例如数据库或缓存中,您可以设置客户端的访问令牌字符串,将其用作Bearer令牌。
use Sebdesign\VivaPayments\Facades\Viva; Viva::withToken(token: 'eyJhbGciOiJSUzI1...');
卡
创建卡令牌
use Sebdesign\VivaPayments\Facades\Viva; $cardToken = Viva::cards()->createToken( transactionId: '6cffe5bf-909c-4d69-b6dc-2bef1a6202f7', guzzleOptions: [], );
Webhooks
获取授权代码
参见:https://developer.vivawallet.com/webhooks-for-payments/#generate-a-webhook-verification-key
use Sebdesign\VivaPayments\Facades\Viva; $key = Viva::webhooks()->getVerificationKey( guzzleOptions: [], );
ISV付款API参考
ISV付款API方法可通过Viva::isv()
服务获取。
订单
创建付款订单
use Sebdesign\VivaPayments\Facades\Viva; use Sebdesign\VivaPayments\Requests\CreatePaymentOrder; use Sebdesign\VivaPayments\Requests\Customer; $orderCode = Viva::isv()->orders()->create( order: new CreatePaymentOrder( amount: 1000, customerTrns: 'Short description of purchased items/services to display to your customer', customer: new Customer( email: 'johdoe@vivawallet.com', fullName: 'John Doe', phone: '+30999999999', countryCode: 'GB', requestLang: 'en-GB', ), paymentTimeOut: 300, preauth: false, allowRecurring: false, maxInstallments: 12, paymentNotification: true, tipAmount: 100, disableExactAmount: false, disableCash: true, disableWallet: true, sourceCode: '1234', merchantTrns: 'Short description of items/services purchased by customer', tags: [ 'tags for grouping and filtering the transactions', 'this tag can be searched on VivaWallet sales dashboard', 'Sample tag 1', 'Sample tag 2', 'Another string', ], isvAmount: 10, resellerSourceCode: '2345', ), guzzleOptions: [], );
交易
检索交易
use Sebdesign\VivaPayments\Facades\Viva; $transaction = Viva::isv()->transactions()->retrieve( transactionId: 'c90d4902-6245-449f-b2b0-51d99cd09cfe', guzzleOptions: [], );
创建周期性交易
use Sebdesign\VivaPayments\Facades\Viva; use Sebdesign\VivaPayments\Requests\CreateRecurringTransaction; Viva::withBasicAuthCredentials( config('services.viva.isv_partner_id').':'.config('services.viva.merchant_id'), config('services.viva.isv_partner_api_key'), ); $transaction = Viva::isv()->transactions()->createRecurring( transactionId: 'c90d4902-6245-449f-b2b0-51d99cd09cfe', transaction: new CreateRecurringTransaction( amount: 100, isvAmount: 1, customerTrns: 'A description of products / services that is displayed to the customer', merchantTrns: 'Your merchant reference', sourceCode: '4929333', resellerSourceCode: '1565', ), guzzleOptions: [], );
异常
当VivaPayments API返回错误时,会抛出Sebdesign\VivaPayments\VivaException
。
对于任何其他HTTP错误,会抛出GuzzleHttp\Exception\ClientException
。
测试
通过运行phpunit --group unit
触发单元测试。
要运行功能测试,您必须在根目录中包含一个.env
文件,其中包含凭据(VIVA_API_KEY
、VIVA_MERCHANT_ID
、VIVA_CLIENT_ID
、VIVA_CLIENT_SECRET
),以便连接到VivaPayments演示API。然后运行phpunit --group functional
来触发测试。