first
This commit is contained in:
4
modules/Payment/.gitignore
vendored
Normal file
4
modules/Payment/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
.idea
|
||||
vendor
|
||||
.DS_Store
|
||||
composer.lock
|
||||
18
modules/Payment/Config/config.php
Normal file
18
modules/Payment/Config/config.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'version' => 3,
|
||||
'logger' => true,//是否开启日志,yansongda/pay v3版本有效
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| 退款单编号前缀
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
'refund_no_counter_prefix' => 'RF',
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| 退款单编号计数器位数,取值范围 6 - 16 - 退款编号前缀位数
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
'refund_no_counter_length' => 6,
|
||||
];
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreatePaymentAlipaysTable extends Migration
|
||||
{
|
||||
|
||||
/**
|
||||
* Run the migrations.
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('payment_alipays', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->string('app_id');
|
||||
$table->string('notify_url')->nullable();
|
||||
$table->string('return_url')->nullable();
|
||||
$table->text('ali_public_key')->nullable();
|
||||
$table->text('private_key')->nullable();
|
||||
$table->string('app_cert_path')->nullable()->comment('应用公钥证书');
|
||||
$table->string('alipay_cert_path')->nullable()->comment('支付宝公钥证书');
|
||||
$table->string('alipay_root_cert_path')->nullable()->comment('支付宝根证书文件');
|
||||
$table->json('log');
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('payment_alipays');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreatePaymentBillsTable extends Migration
|
||||
{
|
||||
|
||||
/**
|
||||
* Run the migrations.
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('payment_bills', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->date('date')->comment('账单日期');
|
||||
$table->decimal('total', 20)->comment('累计收款');
|
||||
$table->decimal('refund', 20)->comment('累计退款');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('payment_bills');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreatePaymentNotifiesTable extends Migration
|
||||
{
|
||||
|
||||
/**
|
||||
* Run the migrations.
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('payment_notifies', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('payment_id');
|
||||
$table->json('payload');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('payment_notifies');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreatePaymentRedpacksTable extends Migration
|
||||
{
|
||||
|
||||
/**
|
||||
* Run the migrations.
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('payment_redpacks', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->boolean('type')->default(0)->index()->comment('0:普通红包,1:裂变红包');
|
||||
$table->uuid('redpack_no')->comment('商户订单号')->index();
|
||||
$table->decimal('amount')->comment('发放金额');
|
||||
$table->unsignedTinyInteger('number')->default(1)->comment('总发放个数');
|
||||
$table->enum('driver', ['alipay', 'wechat'])->index()->comment('付款渠道');
|
||||
$table->string('to')->index()->comment('用户OPENID');
|
||||
$table->string('status', 16)->nullable()->comment('发放状态');
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('payment_redpacks');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreatePaymentRefundsTable extends Migration
|
||||
{
|
||||
|
||||
/**
|
||||
* Run the migrations.
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('payment_refunds', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('payment_id')->index();
|
||||
$table->uuid('refund_no')->comment('退款单号');
|
||||
$table->decimal('total', 20)->comment('退款金额');
|
||||
$table->timestamp('refund_at')->nullable()->comment('退款时间');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('payment_refunds');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreatePaymentSettingsTable extends Migration
|
||||
{
|
||||
|
||||
/**
|
||||
* Run the migrations.
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('payment_settings', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name')->comment('配置名称');
|
||||
$table->tinyInteger('trade_id_counter_length')->default(6);
|
||||
$table->boolean('in_use')->default(0)->comment('是否是使用中的配置');
|
||||
$table->unsignedBigInteger('alipay_id')->nullable();
|
||||
$table->unsignedBigInteger('wechat_id')->nullable();
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('payment_settings');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreatePaymentTransfersTable extends Migration
|
||||
{
|
||||
|
||||
/**
|
||||
* Run the migrations.
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('payment_transfers', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->uuid('transfer_no')->index()->comment('商户订单号');
|
||||
$table->decimal('amount')->comment('转账金额');
|
||||
$table->enum('driver', ['alipay', 'wechat'])->index()->comment('付款渠道');
|
||||
$table->string('to')->index()->comment('微信openid,支付宝identity');
|
||||
$table->boolean('check')->default(0)->comment('是否校验真实姓名');
|
||||
$table->string('user_name')->nullable()->comment('收款人姓名');
|
||||
$table->string('desc')->nullable()->comment('付款说明');
|
||||
$table->string('status', 16)->nullable()->comment('转账状态');
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('payment_transfers');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreatePaymentWechatsTable extends Migration
|
||||
{
|
||||
|
||||
/**
|
||||
* Run the migrations.
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('payment_wechats', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->string('appid')->nullable();
|
||||
$table->string('app_id')->nullable();
|
||||
$table->string('miniapp_id')->nullable();
|
||||
$table->string('mch_id');
|
||||
$table->string('key');
|
||||
$table->string('notify_url')->nullable();
|
||||
$table->string('cert_client')->nullable();
|
||||
$table->string('cert_key')->nullable();
|
||||
$table->json('log');
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('payment_wechats');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreatePaymentsTable extends Migration
|
||||
{
|
||||
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('payments', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->morphs('order');
|
||||
$table->unsignedBigInteger('user_id')->index();
|
||||
$table->uuid('trade_id')->unique();
|
||||
$table->decimal('total', 20, 2)->unsigned()->comment('支付金额');
|
||||
$table->enum('driver', ['alipay', 'wechat', 'score'])->index()->comment('支付方式');
|
||||
$table->string('gateway', 16)->comment('支付渠道');
|
||||
$table->string('state', 16)->index()->comment('订单状态');
|
||||
$table->uuid('transaction_id')->nullable()->index()->comment('三方交易号');
|
||||
$table->timestamp('paid_at')->nullable()->comment('订单支付时间');
|
||||
$table->timestamps();
|
||||
|
||||
$table->index('created_at');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('payments');
|
||||
}
|
||||
|
||||
}
|
||||
39
modules/Payment/Events/Paid.php
Normal file
39
modules/Payment/Events/Paid.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Payment\Events;
|
||||
|
||||
use Illuminate\Foundation\Events\Dispatchable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Modules\Payment\Models\Payment;
|
||||
|
||||
/**
|
||||
* Class Paid
|
||||
* 支付完成事件
|
||||
*
|
||||
* @package Module\Payment\Events
|
||||
*/
|
||||
class Paid
|
||||
{
|
||||
|
||||
use Dispatchable,
|
||||
SerializesModels;
|
||||
|
||||
/**
|
||||
* 支付订单模型
|
||||
*
|
||||
* @var \Modules\Payment\Models\Payment
|
||||
*/
|
||||
public Payment $payment;
|
||||
|
||||
/**
|
||||
* 创建一个事件的实例
|
||||
*
|
||||
* @param \Modules\Payment\Models\Payment $payment
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Payment $payment)
|
||||
{
|
||||
$this->payment = $payment;
|
||||
}
|
||||
|
||||
}
|
||||
31
modules/Payment/Facades/Pay.php
Normal file
31
modules/Payment/Facades/Pay.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Payment\Facades;
|
||||
|
||||
use Illuminate\Support\Facades\Facade;
|
||||
use Modules\Payment\BlockChain\Pay as ChainPay;
|
||||
|
||||
class Pay extends Facade
|
||||
{
|
||||
|
||||
public static function getFacadeAccessor(): string
|
||||
{
|
||||
return 'pay.wechat';
|
||||
}
|
||||
|
||||
public static function alipay()
|
||||
{
|
||||
return app('pay.alipay');
|
||||
}
|
||||
|
||||
public static function wechat()
|
||||
{
|
||||
return app('pay.wechat');
|
||||
}
|
||||
|
||||
public static function chain()
|
||||
{
|
||||
return new ChainPay();
|
||||
}
|
||||
|
||||
}
|
||||
73
modules/Payment/Http/Controllers/Admin/AlipayController.php
Normal file
73
modules/Payment/Http/Controllers/Admin/AlipayController.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Payment\Http\Controllers\Admin;
|
||||
|
||||
use Encore\Admin\Controllers\AdminController;
|
||||
use Encore\Admin\Form;
|
||||
use Encore\Admin\Grid;
|
||||
use Illuminate\Http\Request;
|
||||
use Modules\Payment\Models\Alipay;
|
||||
|
||||
class AlipayController extends AdminController
|
||||
{
|
||||
|
||||
protected $title = '支付宝';
|
||||
|
||||
public function grid(): Grid
|
||||
{
|
||||
$grid = new Grid(new Alipay());
|
||||
$grid->disableFilter();
|
||||
|
||||
$grid->column('id', '#ID#');
|
||||
$grid->column('name', '支付名称');
|
||||
$grid->column('app_id', '应用APPID');
|
||||
$grid->column('created_at', '创建时间');
|
||||
|
||||
return $grid;
|
||||
}
|
||||
|
||||
public function form(): Form
|
||||
{
|
||||
$form = new Form(new Alipay());
|
||||
|
||||
$form->text('name', '支付名称')->required();
|
||||
|
||||
$form->text('app_id', '应用APPID')->required();
|
||||
$form->url('notify_url', '通知地址');
|
||||
$form->url('return_url', '返回地址');
|
||||
|
||||
$form->textarea('ali_public_key', '支付公钥');
|
||||
$form->textarea('private_key', '私钥');
|
||||
|
||||
$form->text('app_cert_path', '应用公钥证书')->help('相对/storage/app下的路径如:certs/XXX.crt');
|
||||
$form->text('alipay_cert_path', '支付宝公钥证书')->help('相对/storage/app下的路径如:certs/XXX.crt');
|
||||
$form->text('alipay_root_cert_path', '支付宝根证书文件')->help('相对/storage/app下的路径如:certs/XXX.crt');
|
||||
|
||||
$form->embeds('log', '日志配置', function (Form\EmbeddedForm $form) {
|
||||
$form->text('file', '日志文件名');
|
||||
$form->select('level')->options([
|
||||
'info' => 'info',
|
||||
'debug' => 'debug',
|
||||
])->default('info');
|
||||
$form->select('type', '记录方式')->options([
|
||||
'daily' => '按日期',
|
||||
'single' => '单文件',
|
||||
])->default('daily');
|
||||
$form->number('max_file')
|
||||
->default(30)
|
||||
->help('当 【记录方式】 为 【按日期】 时有效');
|
||||
});
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
public function ajax(Request $request)
|
||||
{
|
||||
$q = $request->q;
|
||||
|
||||
return Alipay::where('name', 'like', "%$q%")
|
||||
->orWhere('app_id', 'like', "%$q%")
|
||||
->paginate(null, ['id', 'name as text']);
|
||||
}
|
||||
|
||||
}
|
||||
26
modules/Payment/Http/Controllers/Admin/BillController.php
Normal file
26
modules/Payment/Http/Controllers/Admin/BillController.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Payment\Http\Controllers\Admin;
|
||||
|
||||
use Encore\Admin\Controllers\AdminController;
|
||||
use Encore\Admin\Grid;
|
||||
use Modules\Payment\Models\Bill;
|
||||
|
||||
class BillController extends AdminController
|
||||
{
|
||||
|
||||
protected $title = '日账单';
|
||||
|
||||
public function grid(): Grid
|
||||
{
|
||||
$grid = new Grid(new Bill());
|
||||
|
||||
$grid->column('date', '账单日期');
|
||||
$grid->column('total', '累计收款');
|
||||
$grid->column('refund', '累计退款');
|
||||
$grid->column('created_at', '统计时间');
|
||||
|
||||
return $grid;
|
||||
}
|
||||
|
||||
}
|
||||
60
modules/Payment/Http/Controllers/Admin/IndexController.php
Normal file
60
modules/Payment/Http/Controllers/Admin/IndexController.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Payment\Http\Controllers\Admin;
|
||||
|
||||
use Encore\Admin\Controllers\AdminController;
|
||||
use Encore\Admin\Grid;
|
||||
use Modules\Payment\Models\Payment;
|
||||
|
||||
class IndexController extends AdminController
|
||||
{
|
||||
|
||||
protected $title = '支付订单';
|
||||
|
||||
public function grid(): Grid
|
||||
{
|
||||
$grid = new Grid(new Payment());
|
||||
|
||||
$grid->disableCreateButton();
|
||||
$grid->disableActions();
|
||||
|
||||
$grid->quickSearch('trade_id')->placeholder('交易单号');
|
||||
|
||||
$grid->filter(function (Grid\Filter $filter) {
|
||||
$filter->column(1 / 2, function (Grid\Filter $filter) {
|
||||
$filter->like('trade_id', '交易单号');
|
||||
$filter->like('transaction_id', '交易单号');
|
||||
$filter->like('user.username', '下单用户');
|
||||
$filter->between('created_at', '下单时间')->datetime();
|
||||
|
||||
});
|
||||
$filter->column(1 / 2, function (Grid\Filter $filter) {
|
||||
$filter->equal('driver', '支付通道')->select(Payment::DRIVER_MAP);
|
||||
$filter->equal('state', '支付状态')->select(Payment::STATUS_MAP);
|
||||
$filter->between('paid_at', '支付时间')->datetime();
|
||||
});
|
||||
});
|
||||
|
||||
$grid->column('trade_id', '交易单号');
|
||||
$grid->column('transaction_id', '支付单号');
|
||||
$grid->column('user.username', '下单用户');
|
||||
$grid->column('driver', '支付通道')
|
||||
->using(Payment::DRIVER_MAP)
|
||||
->label(Payment::DRIVER_LABEL_MAP);
|
||||
$grid->column('gateway', '支付渠道');
|
||||
$grid->column('state', '支付状态')
|
||||
->using(Payment::STATUS_MAP)
|
||||
->label(Payment::STATUS_LABEL_MAP);
|
||||
$grid->column('total', '应付金额');
|
||||
$grid->column('paid_at', '支付时间');
|
||||
$grid->column('退款状态')->display(function () {
|
||||
return $this->refund_status_text;
|
||||
})->link(function () {
|
||||
return route('admin.payment.refunds.index', ['payment[trade_id]' => $this->trade_id]);
|
||||
}, '_self');
|
||||
$grid->column('created_at', '创建时间');
|
||||
|
||||
return $grid;
|
||||
}
|
||||
|
||||
}
|
||||
50
modules/Payment/Http/Controllers/Admin/RedpackController.php
Normal file
50
modules/Payment/Http/Controllers/Admin/RedpackController.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Payment\Http\Controllers\Admin;
|
||||
|
||||
use Encore\Admin\Controllers\AdminController;
|
||||
use Encore\Admin\Grid;
|
||||
use Modules\Payment\Models\Payment;
|
||||
use Modules\Payment\Models\Redpack;
|
||||
|
||||
class RedpackController extends AdminController
|
||||
{
|
||||
|
||||
protected $title = '红包管理';
|
||||
|
||||
public function grid(): Grid
|
||||
{
|
||||
$grid = new Grid(new Redpack());
|
||||
|
||||
$grid->disableActions();
|
||||
$grid->disableCreateButton();
|
||||
|
||||
$grid->quickSearch('redpack_no')->placeholder('红包编号');
|
||||
|
||||
$grid->filter(function (Grid\Filter $filter) {
|
||||
$filter->column(1 / 3, function (Grid\Filter $filter) {
|
||||
$filter->like('redpack_no', '红包编号');
|
||||
});
|
||||
$filter->column(1 / 3, function (Grid\Filter $filter) {
|
||||
$filter->equal('driver', '发送渠道')->select(Payment::DRIVER_MAP);
|
||||
});
|
||||
$filter->column(1 / 3, function (Grid\Filter $filter) {
|
||||
$filter->equal('type', '红包类型')->select(Redpack::TYPE_MAP);
|
||||
});
|
||||
});
|
||||
|
||||
$grid->column('redpack_no', '红包编号');
|
||||
$grid->column('driver', '发送渠道')
|
||||
->using(Payment::DRIVER_MAP)
|
||||
->label(Payment::DRIVER_LABEL_MAP);
|
||||
$grid->column('type', '红包类型')
|
||||
->using(Redpack::TYPE_MAP);
|
||||
$grid->column('amount', '红包金额');
|
||||
$grid->column('to', '发送对象');
|
||||
$grid->column('status', '发送状态');
|
||||
$grid->column('created_at', '创建时间');
|
||||
|
||||
return $grid;
|
||||
}
|
||||
|
||||
}
|
||||
47
modules/Payment/Http/Controllers/Admin/RefundController.php
Normal file
47
modules/Payment/Http/Controllers/Admin/RefundController.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Payment\Http\Controllers\Admin;
|
||||
|
||||
use Encore\Admin\Controllers\AdminController;
|
||||
use Encore\Admin\Grid;
|
||||
use Modules\Payment\Models\Refund;
|
||||
|
||||
class RefundController extends AdminController
|
||||
{
|
||||
|
||||
protected $title = '退款订单';
|
||||
|
||||
public function grid(): Grid
|
||||
{
|
||||
$grid = new Grid(new Refund());
|
||||
|
||||
$grid->disableCreateButton();
|
||||
$grid->disableActions();
|
||||
|
||||
$grid->quickSearch('refund_no')->placeholder('退款单号');
|
||||
$grid->filter(function (Grid\Filter $filter) {
|
||||
$filter->column(1 / 2, function (Grid\Filter $filter) {
|
||||
$filter->like('payment.trade_id', '订单编号');
|
||||
});
|
||||
$filter->column(1 / 2, function (Grid\Filter $filter) {
|
||||
$filter->like('refund_no', '退款单号');
|
||||
});
|
||||
});
|
||||
|
||||
$grid->column('payment.trade_id', '订单编号')
|
||||
->link(function () {
|
||||
return route('admin.payment.index', ['trade_id' => $this->payment->trade_id]);
|
||||
}, '_self');
|
||||
$grid->column('payment.total', '订单金额');
|
||||
$grid->column('refund_no', '退款单号');
|
||||
$grid->column('total', '退款金额');
|
||||
$grid->column('退款类型')->display(function () {
|
||||
return $this->total === $this->payment->total ? '全额退款' : '部分退款';
|
||||
});
|
||||
$grid->column('refund_at', '退款时间');
|
||||
$grid->column('created_at', '创建时间');
|
||||
|
||||
return $grid;
|
||||
}
|
||||
|
||||
}
|
||||
73
modules/Payment/Http/Controllers/Admin/SettingController.php
Normal file
73
modules/Payment/Http/Controllers/Admin/SettingController.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Payment\Http\Controllers\Admin;
|
||||
|
||||
use Encore\Admin\Controllers\AdminController;
|
||||
use Encore\Admin\Form;
|
||||
use Encore\Admin\Grid;
|
||||
use Modules\Payment\Models\Alipay;
|
||||
use Modules\Payment\Models\Setting;
|
||||
use Modules\Payment\Models\Wechat;
|
||||
|
||||
class SettingController extends AdminController
|
||||
{
|
||||
|
||||
protected $title = '支付配置';
|
||||
|
||||
public function grid(): Grid
|
||||
{
|
||||
$grid = new Grid(new Setting());
|
||||
$grid->disableFilter();
|
||||
|
||||
$grid->model()->orderByDesc('in_use');
|
||||
|
||||
$grid->column('name', '配置名称');
|
||||
$grid->column('trade_id_counter_length', '计数器位数');
|
||||
$grid->column('in_use', '使用中')->bool();
|
||||
$grid->column('wechat.name', '微信支付');
|
||||
$grid->column('alipay.name', '支付宝');
|
||||
$grid->column('created_at', '创建时间');
|
||||
|
||||
return $grid;
|
||||
}
|
||||
|
||||
public function form(): Form
|
||||
{
|
||||
$form = new Form(new Setting());
|
||||
|
||||
$form->text('name', '配置名称')
|
||||
->required();
|
||||
$form->number('trade_id_counter_length', '计数器位数')
|
||||
->required()
|
||||
->default(4)
|
||||
->rules('integer|max:16', [
|
||||
'max' => '计数器最大不能超过16',
|
||||
]);
|
||||
$form->switch('in_use', '当前配置');
|
||||
|
||||
$form->select('wechat_id', '微信支付')
|
||||
->options(function ($wechatId) {
|
||||
if ($wechatId) {
|
||||
$wechat = Wechat::find($wechatId);
|
||||
if ($wechat) {
|
||||
return [$wechat->id => $wechat->name];
|
||||
}
|
||||
}
|
||||
})
|
||||
->ajax(route('admin.payment.wechats.ajax'));
|
||||
$form->select('alipay_id', '支付宝')
|
||||
->options(function ($alipayId) {
|
||||
if ($alipayId) {
|
||||
$alipay = Alipay::find($alipayId);
|
||||
if ($alipay) {
|
||||
return [$alipay->id => $alipay->name];
|
||||
}
|
||||
}
|
||||
})
|
||||
->ajax(route('admin.payment.alipays.ajax'));
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Payment\Http\Controllers\Admin;
|
||||
|
||||
use Encore\Admin\Controllers\AdminController;
|
||||
use Encore\Admin\Grid;
|
||||
use Modules\Payment\Models\Payment;
|
||||
use Modules\Payment\Models\Transfer;
|
||||
|
||||
class TransferController extends AdminController
|
||||
{
|
||||
|
||||
protected $title = '转账管理';
|
||||
|
||||
public function grid(): Grid
|
||||
{
|
||||
$grid = new Grid(new Transfer());
|
||||
|
||||
$grid->disableActions();
|
||||
$grid->disableCreateButton();
|
||||
|
||||
$grid->quickSearch('transfer_no')->placeholder('转账单号');
|
||||
|
||||
$grid->filter(function (Grid\Filter $filter) {
|
||||
$filter->column(1 / 3, function (Grid\Filter $filter) {
|
||||
$filter->like('transfer_no', '转账单号');
|
||||
});
|
||||
$filter->column(1 / 3, function (Grid\Filter $filter) {
|
||||
$filter->equal('driver', '转账渠道')->select(Payment::DRIVER_MAP);
|
||||
});
|
||||
$filter->column(1 / 3, function (Grid\Filter $filter) {
|
||||
$filter->like('user_name', '收款人姓名');
|
||||
});
|
||||
});
|
||||
|
||||
$grid->column('transfer_no', '转账单号');
|
||||
$grid->column('driver', '转账渠道')
|
||||
->using(Payment::DRIVER_MAP)
|
||||
->label(Payment::DRIVER_LABEL_MAP);;
|
||||
$grid->column('to', '收款用户');
|
||||
$grid->column('check', '实名校验')->bool();
|
||||
$grid->column('user_name', '收款人姓名');
|
||||
$grid->column('desc', '转账附言');
|
||||
$grid->column('status', '转账状态');
|
||||
$grid->column('created_at', '创建时间');
|
||||
|
||||
return $grid;
|
||||
}
|
||||
|
||||
}
|
||||
96
modules/Payment/Http/Controllers/Admin/WechatController.php
Normal file
96
modules/Payment/Http/Controllers/Admin/WechatController.php
Normal file
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Payment\Http\Controllers\Admin;
|
||||
|
||||
use Encore\Admin\Controllers\AdminController;
|
||||
use Encore\Admin\Form;
|
||||
use Encore\Admin\Grid;
|
||||
use Illuminate\Http\Request;
|
||||
use Modules\Payment\Models\Wechat;
|
||||
|
||||
class WechatController extends AdminController
|
||||
{
|
||||
|
||||
protected $title = '微信支付';
|
||||
|
||||
public function grid(): Grid
|
||||
{
|
||||
$grid = new Grid(new Wechat());
|
||||
|
||||
$grid->disableFilter();
|
||||
$grid->column('id', '#ID#');
|
||||
$grid->column('name', '支付名称');
|
||||
$grid->column('appid', 'APP APPID');
|
||||
$grid->column('app_id', '公众号 APPID');
|
||||
$grid->column('miniapp_id', '小程序 APPID');
|
||||
$grid->column('mch_id', '商户号');
|
||||
|
||||
$grid->column('created_at', '创建时间');
|
||||
|
||||
return $grid;
|
||||
}
|
||||
|
||||
public function form(): Form
|
||||
{
|
||||
$form = new Form(new Wechat());
|
||||
|
||||
$form->text('name', '支付名称')
|
||||
->required();
|
||||
|
||||
$form->text('appid', 'APP APPID');
|
||||
$form->text('app_id', '公众号 APPID');
|
||||
$form->text('miniapp_id', '小程序 APPID');
|
||||
$form->text('mch_id', '商户号')
|
||||
->required()
|
||||
->rules('required|size:10', [
|
||||
'required' => '商户号必须填写',
|
||||
'size' => '商户号长度应为:size位',
|
||||
]);
|
||||
$form->text('key', '支付密钥')
|
||||
->required()->rules('required|size:32', [
|
||||
'required' => '支付密钥必须填写',
|
||||
'size' => '支付密钥长度应为:size位',
|
||||
]);
|
||||
$form->url('notify_url', '通知地址');
|
||||
|
||||
// $form->file('cert_client', '公钥证书')
|
||||
// ->disk('local')
|
||||
// ->move('certs')
|
||||
// ->removable();
|
||||
//
|
||||
// $form->file('cert_key', '私钥证书')
|
||||
// ->disk('local')
|
||||
// ->move('certs')
|
||||
// ->removable();
|
||||
|
||||
$form->text('cert_client', '公钥证书')->help('相对/storage/app下的路径如:certs/apiclient_key.pem');
|
||||
$form->text('cert_key', '私钥证书')->help('相对/storage/app下的路径如:certs/apiclient_cert.pem');
|
||||
|
||||
$form->embeds('log', '日志配置', function (Form\EmbeddedForm $form) {
|
||||
$form->text('file', '日志文件名')->default('wechat');
|
||||
$form->select('level')->options([
|
||||
'info' => 'info',
|
||||
'debug' => 'debug',
|
||||
])->default('info');
|
||||
$form->select('type', '记录方式')->options([
|
||||
'daily' => '按日期',
|
||||
'single' => '单文件',
|
||||
])->default('daily');
|
||||
$form->number('max_file')
|
||||
->default(30)
|
||||
->help('当 【记录方式】 为 【按日期】 时有效');
|
||||
});
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
public function ajax(Request $request)
|
||||
{
|
||||
$q = $request->q;
|
||||
|
||||
return Wechat::where('name', 'like', "%$q%")
|
||||
->orWhere('mch_id', 'like', "%$q%")
|
||||
->paginate(null, ['id', 'name as text']);
|
||||
}
|
||||
|
||||
}
|
||||
29
modules/Payment/Http/Controllers/Api/GatewayController.php
Normal file
29
modules/Payment/Http/Controllers/Api/GatewayController.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Payment\Http\Controllers\Api;
|
||||
|
||||
use App\Api\Controllers\Controller;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Modules\Payment\Models\Setting;
|
||||
|
||||
class GatewayController extends Controller
|
||||
{
|
||||
|
||||
/**
|
||||
* Notes : 获取支持的支付渠道
|
||||
*
|
||||
* @Date : 2021/8/26 3:50 下午
|
||||
* @Author : <Jason.C>
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
$setting = Setting::orderByDesc('in_use')->first();
|
||||
|
||||
return $this->success([
|
||||
'alipay' => (bool) $setting->alipay_id,
|
||||
'wechat' => (bool) $setting->wechat_id,
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
63
modules/Payment/Http/Controllers/Api/NotifyController.php
Normal file
63
modules/Payment/Http/Controllers/Api/NotifyController.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Payment\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Modules\Payment\Facades\Pay;
|
||||
use Modules\Payment\Models\Payment;
|
||||
|
||||
class NotifyController extends Controller
|
||||
{
|
||||
|
||||
|
||||
protected Payment $payment;
|
||||
|
||||
public function alipay()
|
||||
{
|
||||
$alipay = Pay::alipay();
|
||||
|
||||
if (config('payment.version', 2) == 2) {
|
||||
$data = $alipay->verify();
|
||||
} else {
|
||||
$data = $alipay->callback();
|
||||
}
|
||||
|
||||
$this->getPaymentByTradeNo($data->out_trade_no);
|
||||
|
||||
//设置支付完成
|
||||
if ($this->payment) {
|
||||
$this->payment->paid();
|
||||
$this->payment->order->pay();
|
||||
}
|
||||
|
||||
return $alipay->success();
|
||||
|
||||
}
|
||||
|
||||
public function wechat()
|
||||
{
|
||||
$wechat = Pay::wechat();
|
||||
|
||||
if (config('payment.version', 2) == 2) {
|
||||
$data = $wechat->verify();
|
||||
$this->getPaymentByTradeNo($data->out_trade_no);
|
||||
} else {
|
||||
$data = $wechat->callback();
|
||||
$this->getPaymentByTradeNo($data->resource['ciphertext']['out_trade_no']);
|
||||
}
|
||||
|
||||
//设置支付完成
|
||||
if ($this->payment) {
|
||||
$this->payment->paid();
|
||||
$this->payment->order->pay();
|
||||
}
|
||||
|
||||
return $wechat->success();
|
||||
}
|
||||
|
||||
public function getPaymentByTradeNo($tradeNo)
|
||||
{
|
||||
$this->payment = Payment::where('trade_id', $tradeNo)->first();
|
||||
}
|
||||
|
||||
}
|
||||
56
modules/Payment/Models/Alipay.php
Normal file
56
modules/Payment/Models/Alipay.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Payment\Models;
|
||||
|
||||
use App\Models\Model;
|
||||
use GeneaLabs\LaravelModelCaching\Traits\Cachable;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class Alipay extends Model
|
||||
{
|
||||
|
||||
use Cachable,
|
||||
SoftDeletes;
|
||||
|
||||
protected $table = 'payment_alipays';
|
||||
|
||||
protected $casts = [
|
||||
'log' => 'json',
|
||||
];
|
||||
|
||||
/**
|
||||
* Notes : 生成配置文件
|
||||
*
|
||||
* @Date : 2021/5/26 2:21 下午
|
||||
* @Author : < Jason.C >
|
||||
* @return int[]
|
||||
*/
|
||||
public function toConfig(): array
|
||||
{
|
||||
$ali_public_key = $this->ali_public_key ?? '';
|
||||
if ($this->alipay_cert_path) {
|
||||
$ali_public_key = storage_path('app/'.$this->alipay_cert_path);
|
||||
}
|
||||
|
||||
return [
|
||||
'app_id' => $this->app_id ?? '',
|
||||
'notify_url' => $this->notify_url ?? '',
|
||||
'return_url' => $this->return_url ?? '',
|
||||
'ali_public_key' => $ali_public_key,
|
||||
'app_secret_cert' => $this->private_key ?? '',
|
||||
//应用公钥证书
|
||||
'app_public_cert_path' => $this->app_cert_path ? storage_path('app/'.$this->app_cert_path) : '',
|
||||
//支付宝公钥证书
|
||||
'alipay_public_cert_path' => $this->alipay_cert_path ? storage_path('app/'.$this->alipay_cert_path) : '',
|
||||
'alipay_root_cert_path' => $this->alipay_root_cert_path ? storage_path('app/'.$this->alipay_root_cert_path) : '',
|
||||
'logger' => [
|
||||
'enable' => true,
|
||||
'file' => storage_path('logs/alipay/'.$this->log['file'].'.log'),
|
||||
'level' => $this->log['level'],
|
||||
'type' => $this->log['type'],
|
||||
'max_file' => (int) $this->log['max_file'],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
12
modules/Payment/Models/Bill.php
Normal file
12
modules/Payment/Models/Bill.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Payment\Models;
|
||||
|
||||
use App\Models\Model;
|
||||
|
||||
class Bill extends Model
|
||||
{
|
||||
|
||||
protected $table = 'payment_bills';
|
||||
|
||||
}
|
||||
256
modules/Payment/Models/Payment.php
Normal file
256
modules/Payment/Models/Payment.php
Normal file
@@ -0,0 +1,256 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Payment\Models;
|
||||
|
||||
use App\Models\Model;
|
||||
use App\Traits\OrderByIdDesc;
|
||||
use Carbon\Carbon;
|
||||
use Exception;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphTo;
|
||||
use Modules\Payment\Events\Paid;
|
||||
use Modules\Payment\Facades\Pay;
|
||||
use Modules\User\Models\User;
|
||||
use Modules\User\Traits\BelongsToUser;
|
||||
|
||||
class Payment extends Model
|
||||
{
|
||||
|
||||
use BelongsToUser,
|
||||
OrderByIdDesc;
|
||||
|
||||
protected $table = 'payments';
|
||||
|
||||
protected $dates = [
|
||||
'paid_at',
|
||||
];
|
||||
|
||||
const STATUS_NOPAY = 'nopay';
|
||||
const STATUS_SUCCESS = 'success';
|
||||
const STATUS_FAIL = 'fail';
|
||||
const STATUS_REFUND = 'refund';
|
||||
const STATUS_CLOSED = 'closed';
|
||||
const STATUS_REVOKED = 'revoked';
|
||||
const STATUS_ERROR = 'error';
|
||||
const STATUS_FINISHED = 'finished';
|
||||
|
||||
const STATUS_MAP = [
|
||||
self::STATUS_NOPAY => '未支付',
|
||||
self::STATUS_SUCCESS => '支付成功',
|
||||
self::STATUS_FAIL => '支付失败',
|
||||
self::STATUS_REFUND => '转入退款',
|
||||
self::STATUS_CLOSED => '支付关闭',
|
||||
self::STATUS_REVOKED => '支付撤销',
|
||||
self::STATUS_ERROR => '支付错误',
|
||||
self::STATUS_FINISHED => '支付完结',
|
||||
];
|
||||
|
||||
const STATUS_LABEL_MAP = [
|
||||
self::STATUS_NOPAY => 'default',
|
||||
self::STATUS_SUCCESS => 'success',
|
||||
self::STATUS_FAIL => 'warning',
|
||||
self::STATUS_REFUND => 'info',
|
||||
self::STATUS_CLOSED => 'primary',
|
||||
self::STATUS_REVOKED => 'primary',
|
||||
self::STATUS_ERROR => 'danger',
|
||||
self::STATUS_FINISHED => 'success',
|
||||
];
|
||||
|
||||
/**
|
||||
* 支付方式
|
||||
*/
|
||||
const DRIVER_ALIPAY = 'alipay';
|
||||
const DRIVER_WECHAT = 'wechat';
|
||||
const DRIVER_SCORE = 'score';
|
||||
|
||||
const DRIVER_MAP = [
|
||||
self::DRIVER_ALIPAY => '支付宝',
|
||||
self::DRIVER_WECHAT => '微信支付',
|
||||
self::DRIVER_SCORE => '水滴支付',
|
||||
];
|
||||
|
||||
const DRIVER_LABEL_MAP = [
|
||||
self::DRIVER_ALIPAY => 'info',
|
||||
self::DRIVER_WECHAT => 'success',
|
||||
];
|
||||
|
||||
protected static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
self::creating(function ($model) {
|
||||
$time = explode(' ', microtime());
|
||||
|
||||
$counter = $model->whereDate('created_at', Carbon::today())->count() + 1;
|
||||
|
||||
$len = Setting::orderByDesc('in_use')->value('trade_id_counter_length');
|
||||
|
||||
$len = $len < 6 ? 6 : $len;
|
||||
$len = $len > 16 ? 16 : $len;
|
||||
|
||||
$model->trade_id = date('YmdHis').
|
||||
sprintf('%06d', $time[0] * 1e6).
|
||||
sprintf('%0'.$len.'d', $counter);
|
||||
|
||||
$model->state = self::STATUS_NOPAY;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Notes: 支付渠道
|
||||
*
|
||||
* @Author: 玄尘
|
||||
* @Date : 2021/5/18 11:14
|
||||
* @return string
|
||||
*/
|
||||
public function getDriverTextAttribute(): string
|
||||
{
|
||||
return self::DRIVER_MAP[$this->driver];
|
||||
}
|
||||
|
||||
/**
|
||||
* Notes: 支付状态
|
||||
*
|
||||
* @Author: 玄尘
|
||||
* @Date : 2021/5/18 11:17
|
||||
* @return string
|
||||
*/
|
||||
public function getStateTextAttribute(): string
|
||||
{
|
||||
return self::STATUS_MAP[$this->state];
|
||||
}
|
||||
|
||||
/**
|
||||
* Notes : 要支付的模型
|
||||
*
|
||||
* @Date : 2021/4/21 1:59 下午
|
||||
* @Author : < Jason.C >
|
||||
* @return \Illuminate\Database\Eloquent\Relations\MorphTo
|
||||
*/
|
||||
public function order(): MorphTo
|
||||
{
|
||||
return $this->morphTo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Notes : 结果通知
|
||||
*
|
||||
* @Date : 2021/4/23 11:49 上午
|
||||
* @Author : < Jason.C >
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
public function notifies(): HasMany
|
||||
{
|
||||
return $this->hasMany(PaymentNotify::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notes : 退款单
|
||||
*
|
||||
* @Date : 2021/6/1 11:13 上午
|
||||
* @Author : < Jason.C >
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
public function refunds(): HasMany
|
||||
{
|
||||
return $this->hasMany(Refund::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notes : 退款状态
|
||||
*
|
||||
* @Date : 2021/6/1 11:40 上午
|
||||
* @Author : < Jason.C >
|
||||
* @return string
|
||||
*/
|
||||
public function getRefundStatusTextAttribute(): string
|
||||
{
|
||||
if ($this->refunds->count()) {
|
||||
return ($this->refunds->sum('total') == $this->total) ? '全额退款' : '部分退款';
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notes : 直接通过 order 来设置关联的支付订单
|
||||
*
|
||||
* @Date : 2021/4/21 12:43 下午
|
||||
* @Author : < Jason.C >
|
||||
* @param \App\Models\Model $order
|
||||
*/
|
||||
public function setOrderAttribute(Model $order)
|
||||
{
|
||||
$this->attributes['order_type'] = get_class($order);
|
||||
$this->attributes['order_id'] = $order->getKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* Notes : 通过模型来设置所属用户
|
||||
*
|
||||
* @Date : 2021/4/21 1:48 下午
|
||||
* @Author : < Jason.C >
|
||||
* @param \Modules\User\Models\User $user
|
||||
*/
|
||||
public function setUserAttribute(User $user)
|
||||
{
|
||||
$this->attributes['user_id'] = $user->getKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* Notes : 支付成功,调用的方法
|
||||
*
|
||||
* @Date : 2021/4/20 5:42 下午
|
||||
* @Author : < Jason.C >
|
||||
*/
|
||||
public function paid(): void
|
||||
{
|
||||
$this->state = self::STATUS_SUCCESS;
|
||||
$this->paid_at = now();
|
||||
$this->save();
|
||||
|
||||
event(new Paid($this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Notes : 根据支付订单,获取支付渠道需要的接口数据
|
||||
*
|
||||
* @Date : 2021/4/21 4:24 下午
|
||||
* @Author : < Jason.C >
|
||||
* @param string $title 订单标题
|
||||
* @param array $extends 扩展数据, 微信公众号 ['openid' => OPENID]
|
||||
* @return mixed
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getPaymentParams(string $title, array $extends = [])
|
||||
{
|
||||
$order['out_trade_no'] = $this->trade_id;
|
||||
|
||||
$driver = $this->driver;
|
||||
$gateway = $this->gateway;
|
||||
|
||||
if ($driver === self::DRIVER_WECHAT) {
|
||||
$order['out_trade_no'] = $this->trade_id;
|
||||
if (config('payment.version', 2) == 2) {
|
||||
$order['body'] = $title;
|
||||
$order['total_fee'] = $this->total * 100;
|
||||
} else {
|
||||
$order['description'] = $title;
|
||||
$order['amount']['total'] = $this->total * 100;
|
||||
}
|
||||
} elseif ($driver === self::DRIVER_ALIPAY) {
|
||||
$order['total_amount'] = $this->total;
|
||||
$order['out_trade_no'] = $this->trade_id;
|
||||
$order['subject'] = $title;
|
||||
} else {
|
||||
throw new Exception('unsupported driver');
|
||||
}
|
||||
|
||||
if (! empty($extends)) {
|
||||
$order = array_merge($order, $extends);
|
||||
}
|
||||
|
||||
return Pay::$driver()->$gateway($order);
|
||||
}
|
||||
|
||||
}
|
||||
27
modules/Payment/Models/PaymentNotify.php
Normal file
27
modules/Payment/Models/PaymentNotify.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Payment\Models;
|
||||
|
||||
use App\Models\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class PaymentNotify extends Model
|
||||
{
|
||||
|
||||
protected $casts = [
|
||||
'payload' => 'json',
|
||||
];
|
||||
|
||||
/**
|
||||
* Notes : 所属支付单
|
||||
*
|
||||
* @Date : 2021/5/26 9:55 上午
|
||||
* @Author : < Jason.C >
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function payment(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Payment::class);
|
||||
}
|
||||
|
||||
}
|
||||
25
modules/Payment/Models/Redpack.php
Normal file
25
modules/Payment/Models/Redpack.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Payment\Models;
|
||||
|
||||
use App\Models\Model;
|
||||
use App\Traits\OrderByIdDesc;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class Redpack extends Model
|
||||
{
|
||||
|
||||
use OrderByIdDesc,
|
||||
SoftDeletes;
|
||||
|
||||
protected $table = 'payment_redpacks';
|
||||
|
||||
const COMMON_REDPACK = 0;
|
||||
const GROUP_REDPACK = 1;
|
||||
|
||||
const TYPE_MAP = [
|
||||
self::COMMON_REDPACK => '普通红包',
|
||||
self::GROUP_REDPACK => '裂变红包',
|
||||
];
|
||||
|
||||
}
|
||||
49
modules/Payment/Models/Refund.php
Normal file
49
modules/Payment/Models/Refund.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Payment\Models;
|
||||
|
||||
use App\Models\Model;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class Refund extends Model
|
||||
{
|
||||
|
||||
protected $table = 'payment_refunds';
|
||||
|
||||
protected $dates = [
|
||||
'refund_at',
|
||||
];
|
||||
|
||||
public static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
self::creating(function ($model) {
|
||||
$time = explode(' ', microtime());
|
||||
$counter = $model->whereDate('created_at', Carbon::today())->count() + 1;
|
||||
$len = config('payment.refund_no_counter_length');
|
||||
$prefix = config('payment.refund_no_counter_prefix');
|
||||
|
||||
$len = $len < 6 ? 6 : $len;
|
||||
$len = $len > 16 ? 16 : $len;
|
||||
|
||||
$model->refund_no = $prefix.date('YmdHis').
|
||||
sprintf('%06d', $time[0] * 1e6).
|
||||
sprintf('%0'.$len.'d', $counter);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Notes : 支付订单
|
||||
*
|
||||
* @Date : 2021/6/1 11:13 上午
|
||||
* @Author : < Jason.C >
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function payment(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Payment::class);
|
||||
}
|
||||
|
||||
}
|
||||
83
modules/Payment/Models/Setting.php
Normal file
83
modules/Payment/Models/Setting.php
Normal file
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Payment\Models;
|
||||
|
||||
use App\Models\Model;
|
||||
use Exception;
|
||||
use GeneaLabs\LaravelModelCaching\Traits\Cachable;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
class Setting extends Model
|
||||
{
|
||||
|
||||
use Cachable,
|
||||
SoftDeletes;
|
||||
|
||||
protected $table = 'payment_settings';
|
||||
|
||||
protected $casts = [
|
||||
'in_use' => 'boolean',
|
||||
];
|
||||
|
||||
public static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
self::saved(function ($model) {
|
||||
if ($model->in_use && $model->id) {
|
||||
self::where('id', '<>', $model->id)
|
||||
->where('in_use', 1)
|
||||
->update(['in_use' => 0]);
|
||||
}
|
||||
|
||||
// Cache::tags('payment_config')->flush();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Notes : 挂载的微信支付
|
||||
*
|
||||
* @Date : 2021/5/26 2:01 下午
|
||||
* @Author : < Jason.C >
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function wechat(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Wechat::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notes : 挂载的支付宝
|
||||
*
|
||||
* @Date : 2021/5/26 2:18 下午
|
||||
* @Author : < Jason.C >
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function alipay(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Alipay::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notes : 获取微信/支付宝的配置
|
||||
*
|
||||
* @Date : 2021/5/26 2:19 下午
|
||||
* @Author : < Jason.C >
|
||||
* @param string $type
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function getDefaultConfig(string $type): array
|
||||
{
|
||||
if (! in_array($type, ['wechat', 'alipay'])) {
|
||||
throw new Exception('不支持的支付渠道');
|
||||
}
|
||||
|
||||
$setting = self::orderByDesc('in_use')->first();
|
||||
|
||||
return $setting->$type->toConfig();
|
||||
}
|
||||
|
||||
}
|
||||
37
modules/Payment/Models/Traits/WithConfig.php
Normal file
37
modules/Payment/Models/Traits/WithConfig.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Payment\Models\Traits;
|
||||
|
||||
use Modules\Payment\Models\Setting;
|
||||
|
||||
trait WithConfig
|
||||
{
|
||||
|
||||
/**
|
||||
* Notes: 获取配置文件
|
||||
* @Author: 玄尘
|
||||
* @Date : 2021/11/2 15:27
|
||||
* @param string $driver
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getPayConfig(string $driver, $gateway = '')
|
||||
{
|
||||
$defaule_config = Setting::getDefaultConfig($driver);
|
||||
|
||||
if (config('payment.version', 2) == 3) {
|
||||
$config = [
|
||||
$driver => [
|
||||
'default' => $defaule_config,
|
||||
],
|
||||
'logger' => array_merge($defaule_config['logger'], [
|
||||
'enable' => config('payment.logger', true),
|
||||
]),
|
||||
];
|
||||
}
|
||||
|
||||
return $config;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
17
modules/Payment/Models/Transfer.php
Normal file
17
modules/Payment/Models/Transfer.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Payment\Models;
|
||||
|
||||
use App\Models\Model;
|
||||
use App\Traits\OrderByIdDesc;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class Transfer extends Model
|
||||
{
|
||||
|
||||
use OrderByIdDesc,
|
||||
SoftDeletes;
|
||||
|
||||
protected $table = 'payment_transfers';
|
||||
|
||||
}
|
||||
50
modules/Payment/Models/Wechat.php
Normal file
50
modules/Payment/Models/Wechat.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Payment\Models;
|
||||
|
||||
use App\Models\Model;
|
||||
use GeneaLabs\LaravelModelCaching\Traits\Cachable;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class Wechat extends Model
|
||||
{
|
||||
|
||||
use Cachable,
|
||||
SoftDeletes;
|
||||
|
||||
protected $table = 'payment_wechats';
|
||||
|
||||
protected $casts = [
|
||||
'log' => 'json',
|
||||
];
|
||||
|
||||
/**
|
||||
* Notes : 生成配置文件
|
||||
*
|
||||
* @Date : 2021/5/26 2:21 下午
|
||||
* @Author : < Jason.C >
|
||||
* @return int[]
|
||||
*/
|
||||
public function toConfig(): array
|
||||
{
|
||||
return [
|
||||
'app_id' => $this->appid,//app
|
||||
'mp_app_id' => $this->app_id,//mp
|
||||
'mini_app_id' => $this->miniapp_id,//mini
|
||||
'mch_id' => $this->mch_id,
|
||||
// 必填-商户秘钥
|
||||
'mch_secret_key' => $this->key,
|
||||
// 必填-商户私钥 字符串或路径
|
||||
'mch_secret_cert' => $this->cert_key ? storage_path('app/'.$this->cert_key) : '',
|
||||
// 必填-商户公钥证书路径
|
||||
'mch_public_cert_path' => $this->cert_client ? storage_path('app/'.$this->cert_client) : '',
|
||||
'logger' => [
|
||||
'file' => storage_path('logs/wechat/'.$this->log['file'].'.log'),
|
||||
'level' => $this->log['level'],
|
||||
'type' => $this->log['type'],
|
||||
'max_file' => (int) $this->log['max_file'],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
123
modules/Payment/Payment.php
Normal file
123
modules/Payment/Payment.php
Normal file
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Payment;
|
||||
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
|
||||
class Payment
|
||||
{
|
||||
|
||||
protected static string $mainTitle = '支付管理';
|
||||
|
||||
/**
|
||||
* Notes : 模块初始化要做的一些操作
|
||||
*
|
||||
* @Date : 2021/3/12 11:34 上午
|
||||
* @Author : < Jason.C >
|
||||
*/
|
||||
public static function install()
|
||||
{
|
||||
Artisan::call('migrate', [
|
||||
'--path' => 'modules/Payment/Database/Migrations',
|
||||
]);
|
||||
|
||||
self::createAdminMenu();
|
||||
}
|
||||
|
||||
/**
|
||||
* Notes : 卸载模块的一些操作
|
||||
*
|
||||
* @Date : 2021/3/12 11:35 上午
|
||||
* @Author : < Jason.C >
|
||||
*/
|
||||
public static function uninstall()
|
||||
{
|
||||
$menu = config('admin.database.menu_model');
|
||||
|
||||
$settingMenu = $menu::where('title', '支付设置')->get();
|
||||
|
||||
foreach ($settingMenu as $main) {
|
||||
$main->delete();
|
||||
}
|
||||
|
||||
$mains = $menu::where('title', self::$mainTitle)->get();
|
||||
|
||||
foreach ($mains as $main) {
|
||||
$main->delete();
|
||||
}
|
||||
}
|
||||
|
||||
protected static function createAdminMenu()
|
||||
{
|
||||
$menu = config('admin.database.menu_model');
|
||||
|
||||
$main = $menu::create([
|
||||
'parent_id' => 0,
|
||||
'order' => 30,
|
||||
'title' => self::$mainTitle,
|
||||
'icon' => 'fa-wordpress',
|
||||
]);
|
||||
|
||||
$main->children()->createMany([
|
||||
[
|
||||
'order' => 1,
|
||||
'title' => '支付订单',
|
||||
'icon' => 'fa-bars',
|
||||
'uri' => 'payments',
|
||||
],
|
||||
[
|
||||
'order' => 2,
|
||||
'title' => '退款订单',
|
||||
'icon' => 'fa-bars',
|
||||
'uri' => 'payments/refunds',
|
||||
],
|
||||
[
|
||||
'order' => 3,
|
||||
'title' => '转账订单',
|
||||
'icon' => 'fa-edit',
|
||||
'uri' => 'payments/transfers',
|
||||
],
|
||||
[
|
||||
'order' => 4,
|
||||
'title' => '现金红包',
|
||||
'icon' => 'fa-folder',
|
||||
'uri' => 'payments/redpacks',
|
||||
],
|
||||
[
|
||||
'order' => 5,
|
||||
'title' => '日账单',
|
||||
'icon' => 'fa-bars',
|
||||
'uri' => 'payments/bills',
|
||||
],
|
||||
]);
|
||||
|
||||
$settingMenu = $main->children()->create([
|
||||
'order' => 99,
|
||||
'title' => '支付设置',
|
||||
'icon' => 'fa-cogs',
|
||||
'uri' => '',
|
||||
]);
|
||||
|
||||
$settingMenu->children()->createMany([
|
||||
[
|
||||
'order' => 13,
|
||||
'title' => '支付配置',
|
||||
'icon' => 'fa-cogs',
|
||||
'uri' => 'payments/settings',
|
||||
],
|
||||
[
|
||||
'order' => 14,
|
||||
'title' => '微信支付',
|
||||
'icon' => 'fa-wechat',
|
||||
'uri' => 'payments/wechats',
|
||||
],
|
||||
[
|
||||
'order' => 15,
|
||||
'title' => '支付宝',
|
||||
'icon' => 'fa-adn',
|
||||
'uri' => 'payments/alipays',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
98
modules/Payment/Providers/PaymentServiceProvider.php
Normal file
98
modules/Payment/Providers/PaymentServiceProvider.php
Normal file
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Payment\Providers;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Modules\Payment\Models\Setting;
|
||||
use Yansongda\Pay\Pay;
|
||||
|
||||
class PaymentServiceProvider extends ServiceProvider
|
||||
{
|
||||
|
||||
/**
|
||||
* @var string $moduleName
|
||||
*/
|
||||
protected string $moduleName = 'Payment';
|
||||
|
||||
/**
|
||||
* @var string $moduleNameLower
|
||||
*/
|
||||
protected string $moduleNameLower = 'payment';
|
||||
|
||||
/**
|
||||
* Boot the application events.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
$this->loadMigrationsFrom(module_path($this->moduleName, 'Database/Migrations'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the service provider.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
$this->registerConfig();
|
||||
$this->app->register(RouteServiceProvider::class);
|
||||
|
||||
|
||||
$this->app->singleton('pay.alipay', function () {
|
||||
$alipay = Setting::getDefaultConfig('alipay');
|
||||
|
||||
return Pay::alipay([
|
||||
'alipay' => [
|
||||
'default' => $alipay,
|
||||
],
|
||||
'logger' => array_merge($alipay['logger'], [
|
||||
'enable' => config('payment.logger', true),
|
||||
]),
|
||||
'notify_url' => route('api.payment.notify.alipay')
|
||||
]);
|
||||
});
|
||||
|
||||
$this->app->singleton('pay.wechat', function () {
|
||||
$wechat = Setting::getDefaultConfig('wechat');
|
||||
return Pay::wechat([
|
||||
'wechat' => [
|
||||
'default' => Setting::getDefaultConfig('wechat'),
|
||||
],
|
||||
'logger' => array_merge($wechat['logger'], [
|
||||
'enable' => config('payment.logger', true),
|
||||
]),
|
||||
'notify_url' => route('api.payment.notify.alipay')
|
||||
]);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the services provided by the provider.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function provides(): array
|
||||
{
|
||||
return ['pay.alipay', 'pay.wechat'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Register config.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function registerConfig()
|
||||
{
|
||||
$this->publishes([
|
||||
module_path($this->moduleName, 'Config/config.php') => config_path($this->moduleNameLower.'.php'),
|
||||
], 'payment-config');
|
||||
|
||||
$this->mergeConfigFrom(
|
||||
module_path($this->moduleName, 'Config/config.php'), $this->moduleNameLower
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
63
modules/Payment/Providers/RouteServiceProvider.php
Normal file
63
modules/Payment/Providers/RouteServiceProvider.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Payment\Providers;
|
||||
|
||||
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
class RouteServiceProvider extends ServiceProvider
|
||||
{
|
||||
|
||||
/**
|
||||
* @var string $moduleName
|
||||
*/
|
||||
protected string $moduleName = 'Payment';
|
||||
|
||||
/**
|
||||
* The module namespace to assume when generating URLs to actions.
|
||||
* @var string
|
||||
*/
|
||||
protected string $moduleNamespace = 'Modules\Payment\Http\Controllers';
|
||||
|
||||
/**
|
||||
* Called before routes are registered.
|
||||
* Register any model bindings or pattern based filters.
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
parent::boot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the routes for the application.
|
||||
* @return void
|
||||
*/
|
||||
public function map()
|
||||
{
|
||||
$this->mapApiRoutes();
|
||||
|
||||
$this->mapAdminRoutes();
|
||||
}
|
||||
|
||||
protected function mapApiRoutes()
|
||||
{
|
||||
Route::as(config('api.route.as'))
|
||||
->domain(config('api.route.domain'))
|
||||
->middleware(config('api.route.middleware'))
|
||||
->namespace($this->moduleNamespace)
|
||||
->prefix(config('api.route.prefix'))
|
||||
->group(module_path($this->moduleName, 'Routes/api.php'));
|
||||
}
|
||||
|
||||
protected function mapAdminRoutes()
|
||||
{
|
||||
Route::as(config('admin.route.as'))
|
||||
->domain(config('admin.route.domain'))
|
||||
->middleware(config('admin.route.middleware'))
|
||||
->namespace($this->moduleNamespace)
|
||||
->prefix(config('admin.route.prefix'))
|
||||
->group(module_path($this->moduleName, 'Routes/admin.php'));
|
||||
}
|
||||
|
||||
}
|
||||
46
modules/Payment/README.md
Normal file
46
modules/Payment/README.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# 支付模块
|
||||
|
||||
## 1. 安装
|
||||
|
||||
```shell
|
||||
composer require yansongda/pay
|
||||
```
|
||||
|
||||
## 2. 文档参考
|
||||
|
||||
> https://github.com/yansongda/pay
|
||||
|
||||
## 3. 生成支付订单
|
||||
```php
|
||||
use Modules\Payment\Traits\WithPayments;
|
||||
|
||||
$order = Order::first();
|
||||
|
||||
$payment = $order->createWechatPayment(User::first(), -1, 'mp');
|
||||
|
||||
$payment->getPaymentParams();
|
||||
```
|
||||
|
||||
|
||||
## 4. 支付渠道 GATEWAY 参考值
|
||||
// 支付宝
|
||||
//web 电脑支付 array $order Response
|
||||
//wap 手机网站支付 array $order Response
|
||||
//app APP 支付 array $order Response
|
||||
//mini 小程序支付 array $order Collection
|
||||
//scan 扫码支付 array $order Collection
|
||||
// 不支持的
|
||||
//pos 刷卡支付 array $order Collection
|
||||
//transfer 账户转账 array $order Collection
|
||||
|
||||
// 微信支付
|
||||
//mp 公众号支付 array $order Collection
|
||||
//wap 手机网站支付 array $order Response
|
||||
//app APP 支付 array $order JsonResponse
|
||||
//miniapp 小程序支付 array $order Collection
|
||||
//scan 扫码支付 array $order Collection
|
||||
// 不支持的
|
||||
//pos 刷卡支付 array $order Collection
|
||||
//transfer 账户转账 array $order Collection
|
||||
//redpack 普通红包 array $order Collection
|
||||
//groupRedpack 裂变红包 array $order Collection
|
||||
23
modules/Payment/Routes/admin.php
Normal file
23
modules/Payment/Routes/admin.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Routing\Router;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
Route::group([
|
||||
'prefix' => 'payments',
|
||||
'namespace' => 'Admin',
|
||||
'as' => 'payment.',
|
||||
], function (Router $router) {
|
||||
$router->get('', 'IndexController@index')->name('index');
|
||||
$router->get('refunds', 'RefundController@index')->name('refunds.index');
|
||||
$router->get('bills', 'BillController@index');
|
||||
$router->resource('settings', 'SettingController');
|
||||
|
||||
$router->get('wechats/ajax', 'WechatController@ajax')->name('wechats.ajax');
|
||||
$router->resource('wechats', 'WechatController');
|
||||
$router->get('alipays/ajax', 'AlipayController@ajax')->name('alipays.ajax');
|
||||
$router->resource('alipays', 'AlipayController');
|
||||
|
||||
$router->get('redpacks', 'RedpackController@index');
|
||||
$router->get('transfers', 'TransferController@index');
|
||||
});
|
||||
14
modules/Payment/Routes/api.php
Normal file
14
modules/Payment/Routes/api.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Routing\Router;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
Route::group([
|
||||
'prefix' => 'payments',
|
||||
'namespace' => 'Api',
|
||||
], function (Router $router) {
|
||||
$router->get('gateways', 'GatewayController@index');
|
||||
|
||||
$router->any('notify/wechat', 'NotifyController@wechat')->name('payment.notify.wechat');
|
||||
$router->any('notify/alipay', 'NotifyController@alipay')->name('payment.notify.alipay');
|
||||
});
|
||||
160
modules/Payment/Traits/WithPayments.php
Normal file
160
modules/Payment/Traits/WithPayments.php
Normal file
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Payment\Traits;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphMany;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphOne;
|
||||
use Modules\Payment\Models\Payment;
|
||||
use Modules\User\Models\User;
|
||||
|
||||
trait WithPayments
|
||||
{
|
||||
|
||||
/**
|
||||
* Notes : 自动获取订单的应付金额
|
||||
* 主模型应设置 protected $amount_field = 'xxx';
|
||||
*
|
||||
* @Date : 2021/4/23 10:32 上午
|
||||
* @Author : < Jason.C >
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function getTotalPayAmount()
|
||||
{
|
||||
if (! in_array('amount_field', array_keys(get_class_vars(__CLASS__))) || empty($this->amount_field)) {
|
||||
throw new Exception('Model need [amount_field] property');
|
||||
}
|
||||
|
||||
return $this->getOriginal($this->amount_field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notes : 关联支付订单
|
||||
*
|
||||
* @Date : 2021/4/21 1:49 下午
|
||||
* @Author : < Jason.C >
|
||||
* @return \Illuminate\Database\Eloquent\Relations\MorphMany
|
||||
*/
|
||||
public function payments(): MorphMany
|
||||
{
|
||||
return $this->morphMany(Payment::class, 'order');
|
||||
}
|
||||
|
||||
/**
|
||||
* Notes: 关联支付信息
|
||||
*
|
||||
* @Author: 玄尘
|
||||
* @Date : 2021/5/17 9:26
|
||||
* @return \Illuminate\Database\Eloquent\Relations\MorphOne
|
||||
*/
|
||||
public function payment(): MorphOne
|
||||
{
|
||||
return $this->morphOne(Payment::class, 'order')->where('state', Payment::STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notes: 是否支付
|
||||
*
|
||||
* @Author: 玄尘
|
||||
* @Date : 2021/5/17 9:04
|
||||
*/
|
||||
public function isPay(): bool
|
||||
{
|
||||
return $this->payments()->where('state', Payment::STATUS_SUCCESS)->exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* Notes : 创建微信支付订单
|
||||
*
|
||||
* @Date : 2021/4/21 1:55 下午
|
||||
* @Author : < Jason.C >
|
||||
* @param \Modules\User\Models\User $user 下单用户
|
||||
* @param float $total 订单金额
|
||||
* @param string $gateway 支付渠道
|
||||
* @return \Illuminate\Database\Eloquent\Model
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function createWechatPayment(User $user, float $total = -1, string $gateway = 'mp'): Model
|
||||
{
|
||||
$gatewayAll = ['mp', 'wap', 'app', 'miniapp', 'scan'];
|
||||
if (config('payment.version', 2) == 3) {
|
||||
$gatewayAll = ['mp', 'wap', 'app', 'mini', 'scan'];
|
||||
if ($gateway == 'miniapp') {
|
||||
$gateway = 'mini';
|
||||
}
|
||||
}
|
||||
|
||||
if (! in_array($gateway, $gatewayAll)) {
|
||||
throw new Exception('Unsupported Wechat gateway');
|
||||
}
|
||||
|
||||
// 自动获取订单金额
|
||||
if ($total < 0) {
|
||||
$total = $this->getTotalPayAmount();
|
||||
}
|
||||
|
||||
return $this->payments()->create([
|
||||
'user' => $user,
|
||||
'total' => $total,
|
||||
'driver' => Payment::DRIVER_WECHAT,
|
||||
'gateway' => $gateway,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notes : 创建支付宝订单
|
||||
*
|
||||
* @Date : 2021/4/23 10:24 上午
|
||||
* @Author : < Jason.C >
|
||||
* @param \Modules\User\Models\User $user 下单用户
|
||||
* @param float $total 订单金额
|
||||
* @param string $gateway 支付渠道
|
||||
* @return \Illuminate\Database\Eloquent\Model
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function createAlipayPayment(User $user, float $total = -1, string $gateway = 'web'): Model
|
||||
{
|
||||
if (! in_array($gateway, ['web', 'wap', 'app', 'mini', 'scan'])) {
|
||||
throw new Exception('Unsupported Alipay gateway');
|
||||
}
|
||||
// 自动获取订单金额
|
||||
if ($total < 0) {
|
||||
$total = $this->getTotalPayAmount();
|
||||
}
|
||||
|
||||
return $this->payments()->create([
|
||||
'user' => $user,
|
||||
'total' => $total,
|
||||
'driver' => Payment::DRIVER_ALIPAY,
|
||||
'gateway' => $gateway,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notes: 水滴支付
|
||||
*
|
||||
* @Author: 玄尘
|
||||
* @Date: 2022/9/5 15:50
|
||||
* @param User $user
|
||||
* @param float $total
|
||||
* @param string $gateway
|
||||
* @return Model
|
||||
* @throws Exception
|
||||
*/
|
||||
public function createScorePayment(User $user, float $total = -1, string $gateway = 'web'): Model
|
||||
{
|
||||
// 自动获取订单金额
|
||||
if ($total < 0) {
|
||||
$total = $this->getTotalPayAmount();
|
||||
}
|
||||
|
||||
return $this->payments()->create([
|
||||
'user' => $user,
|
||||
'total' => $total,
|
||||
'driver' => Payment::DRIVER_SCORE,
|
||||
'gateway' => $gateway,
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
23
modules/Payment/composer.json
Normal file
23
modules/Payment/composer.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "jasonc/payment-module",
|
||||
"description": "支付模块",
|
||||
"type": "laravel-module",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Jason.Chen",
|
||||
"email": "chenjxlg@163.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"genealabs/laravel-model-caching": "^0.11.3",
|
||||
"yansongda/pay": "^v2.10.0"
|
||||
},
|
||||
"extra": {
|
||||
"module-dir": "modules"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Modules\\Payment\\": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
15
modules/Payment/module.json
Normal file
15
modules/Payment/module.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "Payment",
|
||||
"alias": "payment",
|
||||
"description": "支付管理模块",
|
||||
"keywords": [],
|
||||
"priority": 0,
|
||||
"providers": [
|
||||
"Modules\\Payment\\Providers\\PaymentServiceProvider"
|
||||
],
|
||||
"aliases": {},
|
||||
"files": [],
|
||||
"requires": [],
|
||||
"version": "1.0.0",
|
||||
"author": "Jason.Chen"
|
||||
}
|
||||
Reference in New Issue
Block a user