diff --git a/README.md b/README.md index 667e3cb..ed72139 100644 --- a/README.md +++ b/README.md @@ -15,11 +15,11 @@ - [Configuration](#configuration) - [Billable Model](#Billable-Model) - [YouCanPay Keys](#YouCanPay-Keys) -- [Customers](#Customers) - - [Retrieving Customers](#Retrieving-Customers) - - [Generate Token](#Generate-Token) - - [Generate Payment URL](#Generate-Payment-URL) - [Usage](#Usage) + - [Customers](#Customers) + - [Retrieving Customers](#Retrieving-Customers) + - [Generate Token](#Generate-Token) + - [Generate Payment URL](#Generate-Payment-URL) - [Tokenization](#Create-a-payment) - [Get Token id](#Get-token-id) - [Get Payment url](#Get-Payment-url) @@ -75,7 +75,7 @@ If you want the package to manage the transactions based on the user model, add This trait provides various methods to perform transaction tasks, such as creating a transaction, getting `paid`, `failed` and `pending` transactions. ```php -use Devinweb\LaravelYoucanPay\Traits\Billable; +use Devinweb\LaravelYouCanPay\Traits\Billable; class User extends Authenticatable { @@ -83,12 +83,12 @@ class User extends Authenticatable } ``` -LaravelYoucanPay assumes your user model will be `App\Models\User`, if you use a different user model namespace you should specify it using the method `useCustomerModel` method. +LaravelYouCanPay assumes your user model will be `App\Models\User`, if you use a different user model namespace you should specify it using the method `useCustomerModel` method. This method should typically be called in the boot method of your `AppServiceProvider` class ```php use App\Models\Core\User; -use Devinweb\LaravelYoucanPay\LaravelYoucanPay;; +use Devinweb\LaravelYouCanPay\LaravelYouCanPay;; /** * Bootstrap any application services. @@ -97,7 +97,7 @@ use Devinweb\LaravelYoucanPay\LaravelYoucanPay;; */ public function boot() { - LaravelYoucanPay::useCustomerModel(User::class); + LaravelYouCanPay::useCustomerModel(User::class); } ``` @@ -147,9 +147,9 @@ You can retrieve a customer by their YouCanPay ID using the `findBillable` metho ```php -use Devinweb\LaravelYoucanPay\Facades\LaravelYoucanPay; +use Devinweb\LaravelYouCanPay\Facades\LaravelYouCanPay; -$user = LaravelYoucanPay::findBillable($order_id); +$user = LaravelYouCanPay::findBillable($order_id); ``` @@ -206,7 +206,7 @@ The first step we need is to create a token based on the credentails get it from ```php -use Devinweb\LaravelYoucanPay\Facades\LaravelYoucanPay; +use Devinweb\LaravelYouCanPay\Facades\LaravelYouCanPay; use Illuminate\Support\Str; @@ -217,7 +217,7 @@ public function tokenization(Request $request) 'amount' => 200 ]; - $token= LaravelYoucanPay::createTokenization($order_data, $request)->getId(); + $token= LaravelYouCanPay::createTokenization($order_data, $request)->getId(); $public_key = config('youcanpay.public_key'); $isSandbox = config('youcanpay.sandboxMode'); $language = config('app.locale'); @@ -236,7 +236,7 @@ public function tokenization(Request $request) Standalone Integration, you can generate the payment url using the method `getPaymentUrl()` ```php -$paymentUrl= LaravelYoucanPay::createTokenization($data, $request)->getPaymentURL(); +$paymentUrl= LaravelYouCanPay::createTokenization($data, $request)->getPaymentURL(); ``` Then you can put that url in your html page @@ -250,7 +250,7 @@ Then you can put that url in your html page If you need to add the customer data during the tokenization, Please keep these array keys(`name`, `address`, `zip_code`, `city`, `state`, `country_code`, `phone` and `email`). you can use ```php -use Devinweb\LaravelYoucanPay\Facades\LaravelYoucanPay; +use Devinweb\LaravelYouCanPay\Facades\LaravelYouCanPay; $customerInfo = [ 'name' => '', @@ -263,7 +263,7 @@ $customerInfo = [ 'email' => '', ]; -$token= LaravelYoucanPay::setCustomerInfo($customerInfo)->createTokenization($data, $request)->getId(); +$token= LaravelYouCanPay::setCustomerInfo($customerInfo)->createTokenization($data, $request)->getId(); ``` #### Metadata @@ -271,7 +271,7 @@ $token= LaravelYoucanPay::setCustomerInfo($customerInfo)->createTokenization($da You can use the metadata to send data that can be retrieved after the response or in the webhook. ```php -use Devinweb\LaravelYoucanPay\Facades\LaravelYoucanPay; +use Devinweb\LaravelYouCanPay\Facades\LaravelYouCanPay; $customerInfo = [ 'name' => '', @@ -289,7 +289,7 @@ $metadata = [ 'key' => 'value' ]; -$token= LaravelYoucanPay::setMetadata($metadata) +$token= LaravelYouCanPay::setMetadata($metadata) ->setCustomerInfo($customerInfo) ->createTokenization($data, $request)->getId(); ``` @@ -344,6 +344,23 @@ Then to display the form your logic it's will be looks like the code below ``` +To start the payment you need an action, you can use a button + +```javascript +document.getElementById("pay").addEventListener("click", function () { + // execute the payment + ycPay.pay(tokenId).then(successCallback).catch(errorCallback); +}); + +function successCallback(response) { + //your code here +} + +function errorCallback(response) { + //your code here +} +``` + For more information please check this [link](https://github.com/NextmediaMa/youcan-payment-php-sdk). ### Handling YouCanPay Webhooks @@ -395,7 +412,7 @@ class WebHookController extends Controller LaravelYouCanPay handles the common YouCanPay webhook events, if you need to handle the webhook events that you need you can listen to the event that is dispatched by the package. -- Devinweb\LaravelYoucanPay\Events\WebhookReceived +- Devinweb\LaravelYouCanPay\Events\WebhookReceived You need to register a listener that can handle the event: @@ -404,14 +421,14 @@ You need to register a listener that can handle the event: namespace App\Listeners; -use Devinweb\LaravelYoucanPay\Events\WebhookReceived; +use Devinweb\LaravelYouCanPay\Events\WebhookReceived; class YouCanPayEventListener { /** * Handle received Stripe webhooks. * - * @param \Devinweb\LaravelYoucanPay\Events\WebhookReceived $event + * @param \Devinweb\LaravelYouCanPay\Events\WebhookReceived $event * @return void */ public function handle(WebhookReceived $event) @@ -433,7 +450,7 @@ namespace App\Providers; use App\Listeners\YouCanPayEventListener; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; -use Devinweb\LaravelYoucanPay\Events\WebhookReceived; +use Devinweb\LaravelYouCanPay\Events\WebhookReceived; class EventServiceProvider extends ServiceProvider { @@ -509,7 +526,7 @@ To verify the webhook signature before processing any logic or action. namespace App\Http\Controllers; -use Devinweb\LaravelYoucanPay\Facades\LaravelYoucanPay; +use Devinweb\LaravelYouCanPay\Facades\LaravelYouCanPay; use Illuminate\Http\Request; class YouCanPayWebhooksController extends Controller @@ -518,7 +535,7 @@ class YouCanPayWebhooksController extends Controller { $signature = $request->header('x-youcanpay-signature'); $payload = json_decode($request->getContent(), true); - if (LaravelYoucanPay::verifyWebhookSignature($signature, $payload)) { + if (LaravelYouCanPay::verifyWebhookSignature($signature, $payload)) { // you code here } } @@ -534,14 +551,14 @@ The validation has the same impact as the verification, but the validation throw namespace App\Http\Controllers; -use Devinweb\LaravelYoucanPay\Facades\LaravelYoucanPay; +use Devinweb\LaravelYouCanPay\Facades\LaravelYouCanPay; use Illuminate\Http\Request; class YouCanPayWebhooksController extends Controller { public function handle(Request $request) { - LaravelYoucanPay::validateWebhookSignature($signature, $payload) + LaravelYouCanPay::validateWebhookSignature($signature, $payload) // you code here } diff --git a/composer.json b/composer.json index 8198e37..8464d19 100644 --- a/composer.json +++ b/composer.json @@ -29,13 +29,13 @@ }, "autoload": { "psr-4": { - "Devinweb\\LaravelYoucanPay\\": "src", - "Devinweb\\LaravelYoucanPay\\Database\\Factories\\": "database/factories/" + "Devinweb\\LaravelYouCanPay\\": "src", + "Devinweb\\LaravelYouCanPay\\Database\\Factories\\": "database/factories/" } }, "autoload-dev": { "psr-4": { - "Devinweb\\LaravelYoucanPay\\Tests\\": "tests" + "Devinweb\\LaravelYouCanPay\\Tests\\": "tests" } }, "scripts": { @@ -48,10 +48,10 @@ "extra": { "laravel": { "providers": [ - "Devinweb\\LaravelYoucanPay\\Providers\\LaravelYoucanPayServiceProvider" + "Devinweb\\LaravelYouCanPay\\Providers\\LaravelYouCanPayServiceProvider" ], "aliases": { - "LaravelYoucanPay": "Devinweb\\LaravelYoucanPay\\Facades\\LaravelYoucanPay" + "LaravelYouCanPay": "Devinweb\\LaravelYouCanPay\\Facades\\LaravelYouCanPay" } } } diff --git a/database/factories/TransactionFactory.php b/database/factories/TransactionFactory.php index 683ea9c..983b2bf 100644 --- a/database/factories/TransactionFactory.php +++ b/database/factories/TransactionFactory.php @@ -1,10 +1,10 @@ Str::uuid()->toString(), (new $model)->getForeignKey() => ($model)::factory(), 'name' => 'default', 'order_id' => Str::random(40), diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php index 08da549..89353ce 100644 --- a/database/factories/UserFactory.php +++ b/database/factories/UserFactory.php @@ -1,8 +1,8 @@ bigIncrements('id'); + $table->uuid('id')->primary(); $table->unsignedBigInteger('user_id')->nullable(); $table->string('name'); $table->string('order_id')->unique(); - $table->string('youcanpay_id')->unique(); + $table->string('youcanpay_id')->nullable(); $table->string('status'); $table->string('price')->nullable(); $table->string('refund')->nullable(); diff --git a/routes/web.php b/routes/web.php index 63a5007..799a98e 100644 --- a/routes/web.php +++ b/routes/web.php @@ -1,6 +1,6 @@ name('webhook'); diff --git a/src/Actions/CreateToken.php b/src/Actions/CreateToken.php index 4895201..5d0ab71 100644 --- a/src/Actions/CreateToken.php +++ b/src/Actions/CreateToken.php @@ -1,5 +1,5 @@ createOrUpdate($payload, YouCanPayStatus::paid()); + } + + /** + * Handle transaction failed webhook event. + * + * @param array $payload + * @return void + */ + protected function handleTransactionFailed(array $payload) + { + $this->createOrUpdate($payload, YouCanPayStatus::failed()); + } + + /** + * Undocumented function + * + * @param array $payload + * @param mixed $status + * @return bool + */ + private function createOrUpdate(array $payload, $status) + { + $transaction_data = Arr::get($payload, 'payload.transaction'); + $order_id = $transaction_data['order_id']; $youcanpay_id = Arr::get($payload, 'id'); + $customer = Arr::get($payload, 'payload.customer'); $user_model = LaravelYoucanPay::$customerModel; $user = (new $user_model)->whereEmail($customer['email'])->first(); + $transaction = Transaction::whereOrderId($order_id)->first(); - Transaction::create([ + if ($transaction) { + return $transaction->update([ + 'status' => $status, + 'youcanpay_id' => $youcanpay_id, + 'payload' => $payload + ]); + } + + + return Transaction::create([ 'user_id' => $user? $user->id : null, 'name' => 'default', - 'order_id' => $transaction['order_id'], - 'status' => YouCanPayStatus::PAID(), + 'order_id' => $transaction_data['order_id'], + 'status' => $status, 'youcanpay_id' => $youcanpay_id, - 'price' => $transaction['amount'], + 'price' => $transaction_data['amount'], 'payload' => $payload ]); } - // /** - // * Handle transaction failed webhook event. - // * - // * @param array $payload - // * @return void - // */ - // protected function handleTransactionFailed(array $payload) - // { - // Log::info([ - // 'failed' => $payload - // ]); - // } - // /** // * Handle transaction refunded webhook event. // * diff --git a/src/Http/Middleware/VerifyWebhookSignature.php b/src/Http/Middleware/VerifyWebhookSignature.php index c4b60d8..80849e5 100644 --- a/src/Http/Middleware/VerifyWebhookSignature.php +++ b/src/Http/Middleware/VerifyWebhookSignature.php @@ -1,9 +1,9 @@ getContent(), true); $signature = $request->header('x-youcanpay-signature'); - LaravelYoucanPay::validateWebhookSignature($signature, $payload); + LaravelYouCanPay::validateWebhookSignature($signature, $payload); return $next($request); } diff --git a/src/LaravelYouCanPay.php b/src/LaravelYouCanPay.php new file mode 100644 index 0000000..abacb56 --- /dev/null +++ b/src/LaravelYouCanPay.php @@ -0,0 +1,297 @@ + 'required', + 'amount' => 'required', + ]; + + /** + * Price + * + * @var int + */ + private $price; + + /** + * Order id + * + * @var string + */ + private $order_id; + + /** + * Youcan Pay instance + * + * @var \YouCan\Pay\YouCanPay + */ + private $youcanpay_instance; + + /** + * The config data + * + * @var array + */ + private $config; + + /** + * Tokenization fileds + * + * @var array + */ + private $required_tokenization_fields; + + /** + * Ip address + * + * @var string + */ + private $ip; + + /** + * @var \YouCan\Pay\API\Endpoints\TokenEndpoint + */ + private $token; + + /** + * The metadata is the data retrieved after the response or in the webhook + * + * @var array + */ + private $metadata; + + /** + * The Customer info + * + * @var array + */ + private $customer_info; + + /** + * The default customer model class name. + * + * @var string + */ + public static $customerModel = 'App\\Models\\User'; + + /** + * Create a new LaravelYouCanPay instance. + * + * @return void + */ + public function __construct() + { + $this->config = config('youcanpay'); + + if ($this->config['sandboxMode']) { + YouCanPay::setIsSandboxMode(true); + } + + $this->required_tokenization_fields = self::REQUIRED_FIELDS; + + $this->youcanpay_instance = YouCanPay::instance()->useKeys($this->config['private_key'], $this->config['public_key']); + } + + /** + * Create a Tokenization + * + * @param array $paramters + * @param \Illuminate\Http\Request $request + * @return $this + */ + public function createTokenization(array $attributes, Request $request) + { + $this->validateTokenizationParameters($attributes); + + $this->price = Arr::get($attributes, 'amount'); + + $this->order_id = Arr::get($attributes, 'order_id'); + + $this->ip = $request->ip(); + + $this->metadata = $this->metadata??[]; + + $this->customer_info = $this->customer_info??[]; + + $this->token =app(CreateToken::class)( + $this->youcanpay_instance, + [ + 'attributes' => $attributes, + 'config' => $this->config, + 'customer_info' => $this->customer_info, + 'metadata' => $this->metadata, + 'ip'=> $this->ip + ] + ); + + return $this; + } + + /** + * Set the customer model class name. + * + * @param string $customerModel + * @return void + */ + public static function useCustomerModel($customerModel) + { + static::$customerModel = $customerModel; + } + + /** + * Get the customer instance by its YouCanPay ID. + * + * @param string|null $orderId + * @return \Illuminate\Database\Eloquent\Model|null + */ + public function findBillable($orderId) + { + return $orderId ? Transaction::where('order_id', $orderId)->first()->user : null; + } + + /** + * Set the customer data + * + * @param array $customer_info + * @return $this + */ + public function setCustomerInfo(array $customer_info) + { + $this->customer_info = $customer_info; + + return $this; + } + + /** + * Set the metadata data to use them when the app receive a callback + * + * @param array $metadata + * @return $this + */ + public function setMetadata(array $metadata) + { + $this->metadata = $metadata; + + return $this; + } + + /** + * Get the token id + * + * @return string + */ + public function getId() + { + $this->createPendingTransaction(); + + return $this->token->getId(); + } + + /** + * Get the payment URL + * + * @param string|null $lang + * @return string + */ + public function getPaymentURL(?string $lang=null) + { + $this->createPendingTransaction(); + $lang = $lang ?? config('app.locale'); + + return $this->token->getPaymentURL($lang); + } + + + /** + * Check the keys (private_key, public_key) + * + * @param string|null $privateKey + * @param string|null $publicKey + * @return boolean + */ + public function checkKeys(?string $privateKey = null, ?string $publicKey = null): bool + { + return YouCanPay::instance()->checkKeys($privateKey, $publicKey); + } + + + /** + * Verify the webhook signature + * + * @param string $signature + * @param array $payload + * @return boolean + */ + public function verifyWebhookSignature(string $signature, array $payload): bool + { + return $this->youcanpay_instance->verifyWebhookSignature($signature, $payload); + } + + /** + * Validate the webhook signature + * + * @param string $signature + * @param array $payload + * @throws \YouCan\Pay\API\Exceptions\InvalidWebhookSignatureException + * @return void + */ + public function validateWebhookSignature(string $signature, array $payload): void + { + $this->youcanpay_instance->validateWebhookSignature($signature, $payload); + } + + /** + * Validate the toknization required fields + * + * @param array $attributes + * @throws \InvalidArgumentException + * @return void + */ + private function validateTokenizationParameters(array $attributes) + { + foreach ($this->required_tokenization_fields as $key => $value) { + if ($value == 'required' && !Arr::has($attributes, $key)) { + throw new InvalidArgumentException("The ${key} must be availabe in the array"); + } + } + } + + /** + * Create pending transaction + * + * @return void + */ + private function createPendingTransaction() + { + $email = Arr::get($this->customer_info, 'email'); + $user = (new static::$customerModel)->where('email', $email)->first(); + + Transaction::create([ + 'user_id' => $user? $user->id : null, + 'name' => 'default', + 'order_id' => $this->order_id, + 'status' => YouCanPayStatus::PENDING(), + 'price' => $this->price, + 'payload' => [ + 'payload' => [ + 'metadata' => $this->metadata, + 'customer' => $this->customer_info + ] + ] + ]); + } +} diff --git a/src/LaravelYoucanPay.php b/src/LaravelYoucanPay.php index f9ebde5..abacb56 100644 --- a/src/LaravelYoucanPay.php +++ b/src/LaravelYoucanPay.php @@ -1,15 +1,16 @@ 'required', ]; + /** + * Price + * + * @var int + */ + private $price; + + /** + * Order id + * + * @var string + */ + private $order_id; + /** * Youcan Pay instance * @@ -102,19 +117,23 @@ public function createTokenization(array $attributes, Request $request) { $this->validateTokenizationParameters($attributes); + $this->price = Arr::get($attributes, 'amount'); + + $this->order_id = Arr::get($attributes, 'order_id'); + $this->ip = $request->ip(); - $metadata = $this->metadata??[]; + $this->metadata = $this->metadata??[]; - $customer_info = $this->customer_info??[]; + $this->customer_info = $this->customer_info??[]; $this->token =app(CreateToken::class)( $this->youcanpay_instance, [ 'attributes' => $attributes, 'config' => $this->config, - 'customer_info' => $customer_info, - 'metadata' => $metadata, + 'customer_info' => $this->customer_info, + 'metadata' => $this->metadata, 'ip'=> $this->ip ] ); @@ -177,9 +196,11 @@ public function setMetadata(array $metadata) */ public function getId() { + $this->createPendingTransaction(); + return $this->token->getId(); } - + /** * Get the payment URL * @@ -188,7 +209,9 @@ public function getId() */ public function getPaymentURL(?string $lang=null) { + $this->createPendingTransaction(); $lang = $lang ?? config('app.locale'); + return $this->token->getPaymentURL($lang); } @@ -246,4 +269,29 @@ private function validateTokenizationParameters(array $attributes) } } } + + /** + * Create pending transaction + * + * @return void + */ + private function createPendingTransaction() + { + $email = Arr::get($this->customer_info, 'email'); + $user = (new static::$customerModel)->where('email', $email)->first(); + + Transaction::create([ + 'user_id' => $user? $user->id : null, + 'name' => 'default', + 'order_id' => $this->order_id, + 'status' => YouCanPayStatus::PENDING(), + 'price' => $this->price, + 'payload' => [ + 'payload' => [ + 'metadata' => $this->metadata, + 'customer' => $this->customer_info + ] + ] + ]); + } } diff --git a/src/Models/Transaction.php b/src/Models/Transaction.php index 49b7abc..7cc31ad 100644 --- a/src/Models/Transaction.php +++ b/src/Models/Transaction.php @@ -1,19 +1,20 @@ belongsTo($model, (new $model)->getForeignKey()); } diff --git a/src/Providers/LaravelYouCanPayServiceProvider.php b/src/Providers/LaravelYouCanPayServiceProvider.php new file mode 100644 index 0000000..c1f836b --- /dev/null +++ b/src/Providers/LaravelYouCanPayServiceProvider.php @@ -0,0 +1,99 @@ +registerMiddleware(); + $this->registerRoutes(); + $this->registerMigrations(); + $this->registerPublishing(); + } + + /** + * Register the package migrations. + * + * @return void + */ + protected function registerMigrations() + { + if ($this->app->runningInConsole()) { + $this->loadMigrationsFrom(__DIR__.'/../../database/migrations'); + } + } + + + /** + * Register the package's publishable resources. + * + * @return void + */ + protected function registerPublishing() + { + if ($this->app->runningInConsole()) { + $this->publishes([ + __DIR__.'/../../config/youcanpay.php' => config_path('youcanpay.php'), + ], 'youcanpay-config'); + + + $this->publishes([ + __DIR__.'/../../database/migrations' => $this->app->databasePath('migrations'), + ], 'youcanpay-migrations'); + } + } + + + /** + * Register the application services. + */ + public function register() + { + // Automatically apply the package configuration + $this->mergeConfigFrom(__DIR__.'/../../config/youcanpay.php', 'youcanpay'); + + // Register the main class to use with the facade + $this->app->singleton('laravel-youcan-pay', function () { + return new LaravelYouCanPay; + }); + } + + + /** + * Register the package routes. + * + * @return void + */ + protected function registerRoutes() + { + Route::group([ + 'prefix' => 'youcanpay', + 'namespace' => 'Devinweb\LaravelYouCanPay\Http\Controllers', + 'as' => 'youcanpay.', + ], function () { + $this->loadRoutesFrom(__DIR__.'/../../routes/web.php'); + }); + } + + /** + * Undocumented function + * + * @return void + */ + protected function registerMiddleware() + { + $router = $this->app->make(Router::class); + $router->aliasMiddleware('verify-youcanpay-webhook-signature', VerifyWebhookSignature::class); + } +} diff --git a/src/Providers/LaravelYoucanPayServiceProvider.php b/src/Providers/LaravelYoucanPayServiceProvider.php index 71f6679..c1f836b 100644 --- a/src/Providers/LaravelYoucanPayServiceProvider.php +++ b/src/Providers/LaravelYoucanPayServiceProvider.php @@ -1,14 +1,14 @@ app->singleton('laravel-youcan-pay', function () { - return new LaravelYoucanPay; + return new LaravelYouCanPay; }); } @@ -79,7 +79,7 @@ protected function registerRoutes() { Route::group([ 'prefix' => 'youcanpay', - 'namespace' => 'Devinweb\LaravelYoucanPay\Http\Controllers', + 'namespace' => 'Devinweb\LaravelYouCanPay\Http\Controllers', 'as' => 'youcanpay.', ], function () { $this->loadRoutesFrom(__DIR__.'/../../routes/web.php'); diff --git a/src/Traits/Billable.php b/src/Traits/Billable.php index 874c156..66121cc 100644 --- a/src/Traits/Billable.php +++ b/src/Traits/Billable.php @@ -1,9 +1,9 @@ getCustomerInfo())->setMetadata($metadata)->createTokenization($data, $request); + return LaravelYouCanPay::setCustomerInfo($this->getCustomerInfo())->setMetadata($metadata)->createTokenization($data, $request); } } diff --git a/src/Traits/HasUniqID.php b/src/Traits/HasUniqID.php new file mode 100644 index 0000000..1ef43ca --- /dev/null +++ b/src/Traits/HasUniqID.php @@ -0,0 +1,18 @@ +getKey()) { + $model->{$model->getKeyName()} = Str::uuid()->toString(); + } + }); + } +} diff --git a/tests/ApiKeysTest.php b/tests/ApiKeysTest.php index b696000..dcb36c6 100644 --- a/tests/ApiKeysTest.php +++ b/tests/ApiKeysTest.php @@ -1,8 +1,8 @@ config['private_key'], $this->config['public_key']); + $result = LaravelYouCanPay::checkKeys($this->config['private_key'], $this->config['public_key']); $this->assertFalse($result); } } diff --git a/tests/FakeToken.php b/tests/FakeToken.php index 872054b..226309d 100644 --- a/tests/FakeToken.php +++ b/tests/FakeToken.php @@ -1,6 +1,6 @@ up(); - - LaravelYoucanPay::useCustomerModel(User::class); + LaravelYouCanPay::useCustomerModel(User::class); } protected function createCustomer($email = 'imad', array $options = []): User diff --git a/tests/TokenizationTest.php b/tests/TokenizationTest.php index a4b2084..e277407 100644 --- a/tests/TokenizationTest.php +++ b/tests/TokenizationTest.php @@ -1,16 +1,20 @@ request); + LaravelYouCanPay::createTokenization($required_data, $this->request); } catch (InvalidArgumentException $e) { $this->assertStringContainsString('The order_id must be availabe in the array', $e->getMessage()); } @@ -53,7 +57,7 @@ public function test_fail_when_we_generate_a_tokenization_without_amount() ]; try { - LaravelYoucanPay::createTokenization($required_data, $this->request); + LaravelYouCanPay::createTokenization($required_data, $this->request); } catch (InvalidArgumentException $e) { $this->assertStringContainsString('The amount must be availabe in the array', $e->getMessage()); } @@ -66,7 +70,10 @@ public function test_fail_when_we_generate_a_tokenization_without_amount() public function test_a_user_can_generate_token_from_required_attributes() { [$request, $required_data, $token_id] = $this->initData(); - $token_id_generated = LaravelYoucanPay::createTokenization($required_data, $request)->getId(); + $token_id_generated = LaravelYouCanPay::createTokenization($required_data, $request)->getId(); + $this->assertDatabaseHas('transactions', [ + 'status' => YouCanPayStatus::pending() + ]); $this->assertEquals($token_id_generated, $token_id); } @@ -77,7 +84,10 @@ public function test_a_user_can_generate_token_from_required_attributes() public function test_a_user_can_generate_token_from_required_attributes_and_customer_info() { [$request, $required_data, $token_id, $customer_info] = $this->initDataWithCustomerInfo(); - $token_id_generated = LaravelYoucanPay::setCustomerInfo($customer_info)->createTokenization($required_data, $request)->getId(); + $token_id_generated = LaravelYouCanPay::setCustomerInfo($customer_info)->createTokenization($required_data, $request)->getId(); + $this->assertDatabaseHas('transactions', [ + 'status' => YouCanPayStatus::pending(), + ]); $this->assertEquals($token_id_generated, $token_id); } @@ -88,7 +98,10 @@ public function test_a_user_can_generate_token_from_required_attributes_and_cust public function test_a_user_can_generate_token_from_required_attributes_and_metadata() { [$request, $required_data, $token_id, $metadata] = $this->initDataWithMetadata(); - $token_id_generated = LaravelYoucanPay::setMetadata($metadata)->createTokenization($required_data, $request)->getId(); + $token_id_generated = LaravelYouCanPay::setMetadata($metadata)->createTokenization($required_data, $request)->getId(); + $this->assertDatabaseHas('transactions', [ + 'status' => YouCanPayStatus::pending(), + ]); $this->assertEquals($token_id_generated, $token_id); } @@ -100,7 +113,10 @@ public function test_a_user_can_get_payment_url() { [$request, $required_data] = $this->initData(); $payment_url = "https://youcanpay.com/sandbox/payment-form/token_id?lang=en"; - $url = LaravelYoucanPay::createTokenization($required_data, $request)->getPaymentURL(); + $url = LaravelYouCanPay::createTokenization($required_data, $request)->getPaymentURL(); + $this->assertDatabaseHas('transactions', [ + 'status' => YouCanPayStatus::pending(), + ]); $this->assertEquals($url, $payment_url); } diff --git a/tests/UserTest.php b/tests/UserTest.php index ca388dd..c0bfb46 100644 --- a/tests/UserTest.php +++ b/tests/UserTest.php @@ -1,13 +1,13 @@ user->getPaymentToken($required_data, $request); - + $this->assertDatabaseHas('transactions', [ + 'status' => YouCanPayStatus::pending(), + 'user_id' => $this->user->id + ]); $this->assertEquals($token_id, $token); } @@ -66,7 +69,7 @@ public function test_user_can_generate_a_token() */ public function test_user_can_generate_a_payment_url() { - LaravelYoucanPay::useCustomerModel(FixturesUser::class); + LaravelYouCanPay::useCustomerModel(FixturesUser::class); $token_id = 'token_id'; @@ -91,6 +94,10 @@ public function test_user_can_generate_a_payment_url() $payment_url = "https://youcanpay.com/sandbox/payment-form/token_id?lang=en"; $url = $this->user->getPaymentURL($required_data, $request); + $this->assertDatabaseHas('transactions', [ + 'status' => YouCanPayStatus::pending(), + 'user_id' => $this->user->id + ]); $this->assertEquals($url, $payment_url); } @@ -170,7 +177,7 @@ public function test_an_exception_should_fired_if_getCustomerInfo_exist_and_a_ke */ public function test_transactions() { - \Devinweb\LaravelYoucanPay\Tests\Fixtures\User::factory() + \Devinweb\LaravelYouCanPay\Tests\Fixtures\User::factory() ->has(Transaction::factory()->count(3)) ->create(); @@ -183,7 +190,7 @@ public function test_transactions() */ public function test_transaction_owner() { - $user = \Devinweb\LaravelYoucanPay\Tests\Fixtures\User::factory() + $user = \Devinweb\LaravelYouCanPay\Tests\Fixtures\User::factory() ->has(Transaction::factory()->count(1)) ->create(); @@ -201,7 +208,7 @@ public function test_transaction_owner() */ public function test_user_transaction_status() { - $user = \Devinweb\LaravelYoucanPay\Tests\Fixtures\User::factory() + $user = \Devinweb\LaravelYouCanPay\Tests\Fixtures\User::factory() ->has(Transaction::factory()->count(1)) ->create(); @@ -239,13 +246,13 @@ public function test_user_transaction_status() */ public function test_find_billable_from_order_id() { - $user = \Devinweb\LaravelYoucanPay\Tests\Fixtures\User::factory() + $user = \Devinweb\LaravelYouCanPay\Tests\Fixtures\User::factory() ->has(Transaction::factory()->count(1)) ->create(); $transaction = Transaction::first(); - $billable = LaravelYoucanPay::findBillable($transaction->order_id); + $billable = LaravelYouCanPay::findBillable($transaction->order_id); $this->assertEquals($user->email, $billable->email); } diff --git a/tests/WebHookEventsTest.php b/tests/WebHookEventsTest.php index 3a1dd8c..869d630 100644 --- a/tests/WebHookEventsTest.php +++ b/tests/WebHookEventsTest.php @@ -1,21 +1,13 @@ createCustomer(); + + $transaction = Transaction::create([ + 'user_id' =>$user->id, + 'name' => 'default', + 'status' => YouCanPayStatus::pending(), + 'price' => $amount = 2000, + 'order_id' => $order_id='123', + 'payload' => [] + ]); + + $this->assertDatabaseHas('transactions', [ + 'user_id' => $user->id, + 'status' => YouCanPayStatus::pending(), + 'price' => $amount = 2000, + 'order_id' => $order_id='123' + ]); $payload = [ 'id' => $youcanpay_id = 'a433f4de-b1f8-4e6a-a462-11ab2a92dba7', 'event_name'=> 'transaction.paid', + 'payload' => [ + 'customer' => [ + 'email' => 'imad@devinweb.com' + ], + 'transaction' => [ + 'order_id'=> $order_id, + 'amount' => $amount + ] + ] + ]; + + $signature = hash_hmac( + 'sha256', + json_encode($payload), + config('youcanpay.private_key'), + false + ); + + $response = $this->withHeaders([ + 'x-youcanpay-signature' => $signature, + ])->postJson(route('youcanpay.webhook'), $payload); + + Event::assertDispatched(WebhookReceived::class); + + $this->assertDatabaseHas('transactions', [ + 'order_id' => $transaction->order_id, + 'user_id' => $transaction->user_id, + 'status' => YouCanPayStatus::paid(), + 'youcanpay_id' => $youcanpay_id, + ]); + + $response->assertOk(); + } + + + /** + * @test + * @return void + */ + public function test_webhook_uri_with_event_name_transaction_failed() + { + Event::fake([ + WebhookReceived::class, + ]); + + $payload = [ + 'id' => $youcanpay_id = 'a433f4de-b1f8-4e6a-a462-11ab2a92dba7', + 'event_name'=> 'transaction.failed', 'payload' => [ 'customer' => [ 'email' => 'imad@devinweb.com' @@ -53,11 +113,12 @@ public function test_webhook_uri_with_an_existing_event_name() $response = $this->withHeaders([ 'x-youcanpay-signature' => $signature, ])->postJson(route('youcanpay.webhook'), $payload); - + Event::assertDispatched(WebhookReceived::class); $this->assertDatabaseHas('transactions', [ 'order_id' => $order_id, + 'status' => YouCanPayStatus::failed(), 'youcanpay_id' => $youcanpay_id, ]); diff --git a/tests/WebHooksTest.php b/tests/WebHooksTest.php index eaf809b..d1b7e2c 100644 --- a/tests/WebHooksTest.php +++ b/tests/WebHooksTest.php @@ -1,9 +1,8 @@ 'bar']; $expectedSignature = "ceee75263456946bf35b87708bea371708ce0e31f4daf9e8b301c1e53e3e3b06"; - $result = LaravelYoucanPay::verifyWebhookSignature($expectedSignature, $payload); + $result = LaravelYouCanPay::verifyWebhookSignature($expectedSignature, $payload); $this->assertTrue($result); } @@ -39,7 +38,7 @@ public function test_fail_verify_webhook_signature_should_return_false() { $payload = ['foo'=> 'bar']; $expectedSignature = "1234"; - $result = LaravelYoucanPay::verifyWebhookSignature($expectedSignature, $payload); + $result = LaravelYouCanPay::verifyWebhookSignature($expectedSignature, $payload); $this->assertFalse($result); } @@ -51,7 +50,7 @@ public function test_success_validate_webhook_signature() { $payload = ['foo'=> 'bar']; $expectedSignature = "ceee75263456946bf35b87708bea371708ce0e31f4daf9e8b301c1e53e3e3b06"; - $result = LaravelYoucanPay::validateWebhookSignature($expectedSignature, $payload); + $result = LaravelYouCanPay::validateWebhookSignature($expectedSignature, $payload); $this->assertNull($result); } @@ -65,7 +64,7 @@ public function test_throw_an_exception_when_the_validate_webhook_signature_fail $expectedSignature = "1234"; try { - LaravelYoucanPay::validateWebhookSignature($expectedSignature, $payload); + LaravelYouCanPay::validateWebhookSignature($expectedSignature, $payload); } catch (InvalidWebhookSignatureException $e) { $this->assertStringContainsString('invalid webhook signature', $e->getMessage()); }