增加 支付宝支付网关

This commit is contained in:
iVampireSP.com 2022-09-01 17:48:29 +08:00
parent c8fcc8c9ed
commit 27e756156f
No known key found for this signature in database
GPG Key ID: 2F7B001CA27A8132
14 changed files with 1237 additions and 20 deletions

View File

@ -56,3 +56,5 @@ VITE_PUSHER_HOST="${PUSHER_HOST}"
VITE_PUSHER_PORT="${PUSHER_PORT}" VITE_PUSHER_PORT="${PUSHER_PORT}"
VITE_PUSHER_SCHEME="${PUSHER_SCHEME}" VITE_PUSHER_SCHEME="${PUSHER_SCHEME}"
VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
ALIPAY_APP_ID=

View File

@ -0,0 +1,10 @@
<?php
namespace App\Exceptions;
use Exception;
class PaymentException extends Exception
{
//
}

View File

@ -2,8 +2,13 @@
namespace App\Http\Controllers\User; namespace App\Http\Controllers\User;
use App\Http\Controllers\Controller; use Exception;
use App\Models\User\Balance;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use App\Http\Controllers\Controller;
use Alipay\EasySDK\Kernel\Util\ResponseChecker;
use Alipay\EasySDK\Kernel\Factory as AlipayFactory;
class BalanceController extends Controller class BalanceController extends Controller
{ {
@ -18,28 +23,102 @@ public function index(Request $request)
public function store(Request $request) public function store(Request $request)
{ {
// 充值 // 充值
$request->validate([ $request->validate([
'amount' => 'required|numeric', 'amount' => 'required|numeric',
]); ]);
$balance = $request->user(); $user = $request->user();
// 启用事物 $balance = new Balance();
\DB::beginTransaction();
try {
$balance->increment('amount', $request->amount); $balance = $balance->create([
\DB::commit(); 'user_id' => $user->id,
} catch (\Exception $e) { 'amount' => $request->amount,
\DB::rollBack(); 'payment' => 'alipay',
return $this->error($e->getMessage()); ]);
}
// 生成 18 位订单号
$order_id = date('YmdHis') . $balance->id . rand(1000, 9999);
$balance->order_id = $order_id;
$balance->save();
$balance = $balance->toArray();
$balance['pay_url'] = route('balances.pay.show', ['balance' => $balance['order_id']]);
return $this->success($balance); return $this->success($balance);
} }
public function show(Request $request, Balance $balance)
{
// dd(AlipayFactory::payment()->common()->query('20220901070430102316'));
// dd(route(''));
if ($balance->paid_at !== null) {
return $this->error('订单已支付');
}
try {
$result = AlipayFactory::payment()->page()->pay("支付", $balance->order_id, $balance->amount, 'http://rcrmqishil.sharedwithexpose.com/api/pay/return');
$responseChecker = new ResponseChecker();
// dd($result);
if ($responseChecker->success($result)) {
$html = $result->body;
return view('pay', compact('html'));
}
} catch (Exception $e) {
dd($e->getMessage());
echo "调用失败," . $e->getMessage() . PHP_EOL;;
}
return $this->success($balance);
}
public function notify(Request $request)
{
// 检测订单是否存在
$balance = Balance::where('order_id', $request->out_trade_no)->with('user')->first();
if (!$balance) {
return $this->notFound('balance not found');
}
// 检测订单是否已支付
if ($balance->paid_at !== null) {
return $this->success('订单已支付');
}
$trade = AlipayFactory::payment()->common()->query($request->out_trade_no);
if ($trade->code == "10000" && $trade->tradeStatus == "TRADE_SUCCESS") {
$balance->paid_at = now();
$balance->save();
DB::beginTransaction();
try {
$balance->user->increment('balance', $trade->totalAmount);
DB::commit();
} catch (\Exception $e) {
DB::rollBack();
AlipayFactory::payment()->common()->refund($request->out_trade_no, $trade->totalAmount);
return $this->error($e->getMessage());
}
return $this->success('订单支付成功');
} else {
return $this->error('订单支付失败');
}
}
// // 转换为 drops // // 转换为 drops
// public function transfer($amount = 1) // public function transfer($amount = 1)
// { // {
@ -49,6 +128,4 @@ public function store(Request $request)
// } // }
} }

View File

@ -0,0 +1,32 @@
<?php
namespace App\Models\User;
use App\Models\User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class Balance extends Model
{
use HasFactory;
protected $fillable = [
'order_id',
'payment',
'amount',
'user_id',
'paid_at',
'trade_id'
];
// route key
public function getRouteKeyName()
{
return 'order_id';
}
public function user()
{
return $this->belongsTo(User::class);
}
}

View File

@ -2,7 +2,10 @@
namespace App\Providers; namespace App\Providers;
use Alipay\EasySDK\Kernel\Config as AlipayConfig;
use Alipay\EasySDK\Kernel\Factory as AlipayFactory;
use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\ServiceProvider; use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider class AppServiceProvider extends ServiceProvider
@ -34,5 +37,33 @@ public function boot()
])->baseUrl($url); ])->baseUrl($url);
}); });
AlipayFactory::setOptions($this->alipayOptions());
}
private function alipayOptions()
{
$options = new AlipayConfig();
$options->protocol = 'https';
$options->gatewayHost = 'openapi.alipaydev.com';
$options->signType = 'RSA2';
$options->appId = config('alipay.app_id');
// 为避免私钥随源码泄露,推荐从文件中读取私钥字符串而不是写入源码中
$options->merchantPrivateKey = trim(Storage::get('alipayAppPriv.key'));
$options->alipayCertPath = storage_path('app/alipayCertPublicKey_RSA2.crt');
$options->alipayRootCertPath = storage_path('app/alipayRootCert.crt');
$options->merchantCertPath = storage_path('app/appCertPublicKey.crt');
//注:如果采用非证书模式,则无需赋值上面的三个证书路径,改为赋值如下的支付宝公钥字符串即可
// $options->alipayPublicKey = Storage::get('alipayCertPublicKey_RSA2.crt');
//可设置异步通知接收服务地址(可选)
$options->notifyUrl = "http://rcrmqishil.sharedwithexpose.com/api/pay/notify";
return $options;
} }
} }

View File

@ -6,6 +6,7 @@
"license": "MIT", "license": "MIT",
"require": { "require": {
"php": "^8.0.2", "php": "^8.0.2",
"alipaysdk/easysdk": "^2.0",
"doctrine/dbal": "^3.3", "doctrine/dbal": "^3.3",
"guzzlehttp/guzzle": "^7.2", "guzzlehttp/guzzle": "^7.2",
"laravel/framework": "^9.19", "laravel/framework": "^9.19",
@ -13,7 +14,8 @@
"laravel/octane": "^1.2", "laravel/octane": "^1.2",
"laravel/sanctum": "^2.14.1", "laravel/sanctum": "^2.14.1",
"laravel/telescope": "^4.9", "laravel/telescope": "^4.9",
"laravel/tinker": "^2.7" "laravel/tinker": "^2.7",
"league/omnipay": "^3"
}, },
"require-dev": { "require-dev": {
"fakerphp/faker": "^1.9.1", "fakerphp/faker": "^1.9.1",

988
composer.lock generated

File diff suppressed because it is too large Load Diff

5
config/alipay.php Normal file
View File

@ -0,0 +1,5 @@
<?php
return [
'app_id' => env('ALIPAY_APP_ID'),
];

View File

@ -0,0 +1,51 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('balances', function (Blueprint $table) {
$table->id();
// order id
$table->string('order_id')->nullable()->index();
// trade id
$table->string('trade_id')->nullable()->index();
// payment
$table->string('payment')->nullable()->index();
// amount
$table->decimal('amount', 10, 2)->default(0);
// paid_at
$table->timestamp('paid_at')->nullable();
// user id
$table->unsignedBigInteger('user_id')->nullable()->index();
$table->foreign('user_id')->references('id')->on('users');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('balances');
}
};

View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>支付订单</title>
</head>
<body>
{!! $html !!}
{{--{{ $html }}--}}
</body>
</html>

View File

@ -1,10 +1,10 @@
<?php <?php
use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Route;
use App\Http\Controllers\User\DropController;
use App\Http\Controllers\User\TaskController; use App\Http\Controllers\User\TaskController;
use App\Http\Controllers\Remote\ModuleController; use App\Http\Controllers\Remote\ModuleController;
use App\Http\Controllers\ServerController; use App\Http\Controllers\ServerController;
use App\Http\Controllers\User\BalanceController;
use App\Http\Controllers\UserController; use App\Http\Controllers\UserController;
use App\Http\Controllers\User\WorkOrder\ReplyController; use App\Http\Controllers\User\WorkOrder\ReplyController;
use App\Http\Controllers\User\WorkOrder\WorkOrderController; use App\Http\Controllers\User\WorkOrder\WorkOrderController;
@ -14,7 +14,8 @@
Route::get('servers', ServerController::class); Route::get('servers', ServerController::class);
Route::apiResource('drops', DropController::class); Route::apiResource('balances', BalanceController::class)->only(['index', 'store']);
// Route::apiResource('drops', DropController::class);
Route::get('tasks', TaskController::class); Route::get('tasks', TaskController::class);
@ -23,5 +24,8 @@
// 调用远程 API // 调用远程 API
Route::any('/modules/{module}', [ModuleController::class, 'call'])->name('module.call'); Route::any('/modules/{module}', [ModuleController::class, 'call'])->name('module.call');
}); });
Route::get('/pay/return', [BalanceController::class, 'notify'])->name('balances.return');
Route::get('/pay/notify', [BalanceController::class, 'notify'])->name('balances.notify');

View File

@ -7,7 +7,7 @@
// Route::apiResource('users', Remote\UserController::class)->only(['show']); // Route::apiResource('users', Remote\UserController::class)->only(['show']);
Route::apiResource('modules', Remote\ModuleController::class)->only(['index']); Route::apiResource('modules', Remote\ModuleController::class)->only(['index']);
Route::apiResource('servers', Remote\ServerController::class); Route::apiResource('servers', \App\Http\Controllers\ServerController::class);
Route::apiResource('hosts', Remote\Host\HostController::class); Route::apiResource('hosts', Remote\Host\HostController::class);
// Route::patch('hosts/{host}', [Remote\Host\DropController::class, 'update']); // Route::patch('hosts/{host}', [Remote\Host\DropController::class, 'update']);
// Route::patch('tasks', Remote\Host\TaskController::class); // Route::patch('tasks', Remote\Host\TaskController::class);

View File

@ -25,3 +25,7 @@
}); });
Route::get('/', [Controllers\AuthController::class, 'index'])->name('index'); Route::get('/', [Controllers\AuthController::class, 'index'])->name('index');
Route::get('/balances/{balance}', [Controllers\User\BalanceController::class, 'show'])->name('balances.pay.show');

BIN
storage/.DS_Store vendored Normal file

Binary file not shown.