Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
depsimon committed Feb 22, 2018
0 parents commit b12998e
Show file tree
Hide file tree
Showing 16 changed files with 509 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/.gitattributes export-ignore
/.gitignore export-ignore
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
build
composer.lock
docs
vendor
coverage
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Changelog

All notable changes to `laravel-wallet` will be documented in this file

## 1.0.0 - 2018-02-20

- initial release
21 changes: 21 additions & 0 deletions LICENSE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
The MIT License (MIT)

Copyright (c) WEBARTISAN <[email protected]>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
82 changes: 82 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Laravel Wallet

In a few projects I had to implement a virtual currency. The user would buy packs of credits with Stripe and then use them in the app in exchange of services or goods.
This package is a small and simple implementation of this concept with place for customization.

## Installation

Install the package with composer:

```bash
composer require depsimon/laravel-wallet
```

## Run Migrations

Publish the migrations with this artisan command:

```bash
php artisan vendor:publish --provider="Depsimon\Wallet\WalletServiceProvider" --tag=migrations
```

## Configuration

You can publish the config file with this artisan command:

```bash
php artisan vendor:publish --provider="Depsimon\Wallet\WalletServiceProvider" --tag=config
```

This will merge the `wallet.php` config file where you can specify the Users, Wallets & Transactions classes if you have custom ones.

## Usage

Add the `HasWallet` trait to your User model.

``` php

use Depsimon\Wallet\HasWallet;

class User extends Model
{
use HasWallet;

...
}
```

Then you can easily make transactions from your user model.

``` php
$user = User::find(1);
$user->balance; // 0

$user->deposit(100);
$user->balance; // 100

$user->withdraw(50);
$user->balance; // 50

$user->forceWithdraw(200);
$user->balance; // -150
```

You can easily add meta information to the transactions to suit your needs.

``` php
$user = User::find(1);
$user->deposit(100, 'deposit', ['stripe_source' => 'ch_BEV2Iih1yzbf4G3HNsfOQ07h', 'description' => 'Deposit of 100 credits from Stripe Payment']);
$user->withdraw(10, 'withdraw', ['description' => 'Purchase of Item #1234']);
```

### Security

If you discover any security related issues, please email [email protected] instead of using the issue tracker.

## Credits

- [Simon Depelchin](https://github.com/depsimon)

## License

The MIT License (MIT). Please see [License File](LICENSE.md) for more information.
49 changes: 49 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"name": "depsimon/laravel-wallet",
"description": "Easy to use virtual wallet for your app",
"keywords": [
"depsimon",
"laravel-wallet",
"virtual",
"currency",
"credits",
"wallet"
],
"homepage": "https://github.com/depsimon/laravel-wallet",
"license": "MIT",
"authors": [
{
"name": "Simon Depelchin",
"email": "[email protected]",
"homepage": "https://webartisan.be",
"role": "Developer"
}
],
"require": {
"php": "^7.0",
"illuminate/database": "~5.6"
},
"autoload": {
"psr-4": {
"Depsimon\\Wallet\\": "src"
}
},
"autoload-dev": {
"psr-4": {
"Depsimon\\Wallet\\Tests\\": "tests"
}
},
"config": {
"sort-packages": true
},
"extra": {
"laravel": {
"providers": [
"Depsimon\\Wallet\\WalletServiceProvider"
],
"aliases": {
"Wallet": "Depsimon\\Wallet\\WalletFacade"
}
}
}
}
Empty file added config/.gitkeep
Empty file.
20 changes: 20 additions & 0 deletions config/config.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

return [

/**
* Which model is your User's
*/
'user_model' => 'App\User',

/**
* Change this if you extend the default Wallet Model
*/
'wallet_model' => 'Depsimon\Wallet\Wallet',

/**
* Change this if you extend the default Transaction Model
*/
'transaction_model' => 'Depsimon\Wallet\Transaction',

];
42 changes: 42 additions & 0 deletions resources/migrations/2018_02_20_113000_create_wallets_table.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateWalletsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
$userClass = app(config('wallet.user_model', 'App\User'));

Schema::create('wallets', function (Blueprint $table) use ($userClass) {
$table->increments('id');
$table->unsignedInteger($userClass->getForeignKey())->nullable();

$table->bigInteger('balance');

$table->timestamps();

$table->foreign($userClass->getForeignKey())
->references($userClass->getKeyName())
->on($userClass->getTable())
->onDelete('set null');
});
}

/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('wallets');
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateWalletTransactionsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('wallet_transactions', function (Blueprint $table) {
$table->increments('id');
$table->unsignedInteger('wallet_id');

$table->integer('amount'); // amount is an integer, it could be "dollars" or "cents"
$table->string('hash', 60); // hash is a uniqid for each transaction
$table->string('type', 30); // type can be anything in your app, by default we use "deposit" and "withdraw"
$table->boolean('accepted'); // All transactions will be added in the book, some can be refused
$table->json('meta')->nullable(); // Add all kind of meta information you need

$table->timestamps();

$table->foreign('wallet_id')->references('id')->on('wallets')->onDelete('cascade');
});
}

/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('wallet_transactions');
}
}
117 changes: 117 additions & 0 deletions src/HasWallet.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
<?php

namespace Depsimon\Wallet;

trait HasWallet
{
/**
* Retrieve the balance of this user's wallet
*/
public function getBalanceAttribute()
{
return $this->wallet->balance;
}

/**
* Retrieve the wallet of this user
*/
public function wallet()
{
return $this->hasOne(config('wallet.wallet_model', Wallet::class))->withDefault();
}

/**
* Retrieve all transactions of this user
*/
public function transactions()
{
return $this->hasManyThrough(config('wallet.transaction_model', Transaction::class), config('wallet.wallet_model', Wallet::class))->latest();
}

/**
* Determine if the user can withdraw the given amount
* @param integer $amount
* @return boolean
*/
public function canWithdraw($amount)
{
return $this->balance >= $amount;
}

/**
* Move credits to this account
* @param integer $amount
* @param string $type
* @param array $meta
*/
public function deposit($amount, $type = 'deposit', $meta = [])
{
$this->balance += $amount;
$this->save();

$this->transactions()
->create([
'amount' => $amount,
'hash' => uniqid('lwch_'),
'type' => $type,
'accepted' => true,
'meta' => $meta
]);
}

/**
* Attempt to move credits from this account
* @param integer $amount
* @param string $type
* @param array $meta
* @param boolean $shouldAccept
*/
public function withdraw($amount, $type = 'withdraw', $meta = [], $shouldAccept = true)
{
$accepted = $shouldAccept ? $this->canWithdraw($amount) : true;

if ($accepted) {
$this->balance += $amount;
$this->save();
}

$this->transactions()
->create([
'amount' => $amount,
'hash' => uniqid('lwch_'),
'type' => $type,
'accepted' => $accepted,
'meta' => $meta
]);
}

/**
* Move credits from this account
* @param integer $amount
* @param string $type
* @param array $meta
* @param boolean $shouldAccept
*/
public function forceWithdraw($amount, $type = 'withdraw', $meta = [])
{
return $this->withdraw($amount, $type, $meta, false);
}

/**
* Returns the actual balance for this wallet.
* Might be different from the balance property if the database is manipulated
* @return float balance
*/
public function actualBalance()
{
$credits = $this->transactions()
->whereIn('type', ['deposit', 'refund'])
->sum('amount');

$debits = $this->transactions()
->whereIn('type', ['withdraw', 'payout'])
->sum('amount');

return $credits - $debits;
}
}
Loading

0 comments on commit b12998e

Please sign in to comment.